837 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			837 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
| //===- gllgo.go - gccgo-like driver for llgo ------------------------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // This is llgo's driver. It has a gccgo-like interface in order to easily
 | |
| // interoperate with the "go" command and the libgo build system.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| package main
 | |
| 
 | |
| /*
 | |
| #include "config.h"
 | |
| */
 | |
| import "C"
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"go/scanner"
 | |
| 	"go/token"
 | |
| 	"io/ioutil"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 
 | |
| 	"llvm.org/llgo/debug"
 | |
| 	"llvm.org/llgo/driver"
 | |
| 	"llvm.org/llgo/irgen"
 | |
| 	"llvm.org/llvm/bindings/go/llvm"
 | |
| )
 | |
| 
 | |
| const LibDirSuffix = C.LLVM_LIBDIR_SUFFIX
 | |
| 
 | |
| func report(err error) {
 | |
| 	if list, ok := err.(scanner.ErrorList); ok {
 | |
| 		for _, e := range list {
 | |
| 			fmt.Fprintf(os.Stderr, "%s\n", e)
 | |
| 		}
 | |
| 	} else if err != nil {
 | |
| 		fmt.Fprintf(os.Stderr, "gllgo: error: %s\n", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func llvmVersion() string {
 | |
| 	return strings.Replace(llvm.Version, "svn", "", 1)
 | |
| }
 | |
| 
 | |
| func displayVersion() {
 | |
| 	fmt.Printf("llgo version %s (%s)\n\n", llvmVersion(), irgen.GoVersion())
 | |
| 	os.Exit(0)
 | |
| }
 | |
| 
 | |
| func initCompiler(opts *driverOptions) (*irgen.Compiler, error) {
 | |
| 	importPaths := make([]string, len(opts.importPaths)+len(opts.libPaths))
 | |
| 	copy(importPaths, opts.importPaths)
 | |
| 	copy(importPaths[len(opts.importPaths):], opts.libPaths)
 | |
| 	if opts.prefix != "" {
 | |
| 		importPaths = append(importPaths, filepath.Join(opts.prefix, "lib"+LibDirSuffix, "go", "llgo-"+llvmVersion()))
 | |
| 	}
 | |
| 	copts := irgen.CompilerOptions{
 | |
| 		TargetTriple:       opts.triple,
 | |
| 		GenerateDebug:      opts.generateDebug,
 | |
| 		DebugPrefixMaps:    opts.debugPrefixMaps,
 | |
| 		DumpSSA:            opts.dumpSSA,
 | |
| 		GccgoPath:          opts.gccgoPath,
 | |
| 		GccgoABI:           opts.gccgoPath != "",
 | |
| 		ImportPaths:        importPaths,
 | |
| 		SanitizerAttribute: opts.sanitizer.getAttribute(),
 | |
| 	}
 | |
| 	if opts.dumpTrace {
 | |
| 		copts.Logger = log.New(os.Stderr, "", 0)
 | |
| 	}
 | |
| 	return irgen.NewCompiler(copts)
 | |
| }
 | |
| 
 | |
| type actionKind int
 | |
| 
 | |
| const (
 | |
| 	actionAssemble = actionKind(iota)
 | |
| 	actionCompile
 | |
| 	actionLink
 | |
| 	actionPrint
 | |
| )
 | |
| 
 | |
| type action struct {
 | |
| 	kind   actionKind
 | |
| 	inputs []string
 | |
| }
 | |
| 
 | |
| type sanitizerOptions struct {
 | |
| 	blacklist string
 | |
| 	crtPrefix string
 | |
| 
 | |
| 	address, thread, memory, dataflow bool
 | |
| }
 | |
| 
 | |
| func (san *sanitizerOptions) resourcePath() string {
 | |
| 	return filepath.Join(san.crtPrefix, "lib"+LibDirSuffix, "clang", llvmVersion())
 | |
| }
 | |
| 
 | |
| func (san *sanitizerOptions) isPIEDefault() bool {
 | |
| 	return san.thread || san.memory || san.dataflow
 | |
| }
 | |
| 
 | |
| func (san *sanitizerOptions) addPasses(mpm, fpm llvm.PassManager) {
 | |
| 	switch {
 | |
| 	case san.address:
 | |
| 		mpm.AddAddressSanitizerModulePass()
 | |
| 		fpm.AddAddressSanitizerFunctionPass()
 | |
| 	case san.thread:
 | |
| 		mpm.AddThreadSanitizerPass()
 | |
| 	case san.memory:
 | |
| 		mpm.AddMemorySanitizerPass()
 | |
| 	case san.dataflow:
 | |
| 		blacklist := san.blacklist
 | |
| 		if blacklist == "" {
 | |
| 			blacklist = filepath.Join(san.resourcePath(), "dfsan_abilist.txt")
 | |
| 		}
 | |
| 		mpm.AddDataFlowSanitizerPass([]string{blacklist})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (san *sanitizerOptions) libPath(triple, sanitizerName string) string {
 | |
| 	s := strings.Split(triple, "-")
 | |
| 	return filepath.Join(san.resourcePath(), "lib", s[2], "libclang_rt."+sanitizerName+"-"+s[0]+".a")
 | |
| }
 | |
| 
 | |
| func (san *sanitizerOptions) addLibsForSanitizer(flags []string, triple, sanitizerName string) []string {
 | |
| 	return append(flags, san.libPath(triple, sanitizerName),
 | |
| 		"-Wl,--no-as-needed", "-lpthread", "-lrt", "-lm", "-ldl")
 | |
| }
 | |
| 
 | |
| func (san *sanitizerOptions) addLibs(triple string, flags []string) []string {
 | |
| 	switch {
 | |
| 	case san.address:
 | |
| 		flags = san.addLibsForSanitizer(flags, triple, "asan")
 | |
| 	case san.thread:
 | |
| 		flags = san.addLibsForSanitizer(flags, triple, "tsan")
 | |
| 	case san.memory:
 | |
| 		flags = san.addLibsForSanitizer(flags, triple, "msan")
 | |
| 	case san.dataflow:
 | |
| 		flags = san.addLibsForSanitizer(flags, triple, "dfsan")
 | |
| 	}
 | |
| 
 | |
| 	return flags
 | |
| }
 | |
| 
 | |
| func (san *sanitizerOptions) getAttribute() llvm.Attribute {
 | |
| 	switch {
 | |
| 	case san.address:
 | |
| 		return llvm.SanitizeAddressAttribute
 | |
| 	case san.thread:
 | |
| 		return llvm.SanitizeThreadAttribute
 | |
| 	case san.memory:
 | |
| 		return llvm.SanitizeMemoryAttribute
 | |
| 	default:
 | |
| 		return 0
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type driverOptions struct {
 | |
| 	actions []action
 | |
| 	output  string
 | |
| 
 | |
| 	bprefix         string
 | |
| 	debugPrefixMaps []debug.PrefixMap
 | |
| 	dumpSSA         bool
 | |
| 	dumpTrace       bool
 | |
| 	emitIR          bool
 | |
| 	gccgoPath       string
 | |
| 	generateDebug   bool
 | |
| 	importPaths     []string
 | |
| 	libPaths        []string
 | |
| 	llvmArgs        []string
 | |
| 	lto             bool
 | |
| 	optLevel        int
 | |
| 	pic             bool
 | |
| 	pieLink         bool
 | |
| 	pkgpath         string
 | |
| 	plugins         []string
 | |
| 	prefix          string
 | |
| 	sanitizer       sanitizerOptions
 | |
| 	sizeLevel       int
 | |
| 	staticLibgcc    bool
 | |
| 	staticLibgo     bool
 | |
| 	staticLink      bool
 | |
| 	triple          string
 | |
| }
 | |
| 
 | |
| func getInstPrefix() (string, error) {
 | |
| 	path, err := exec.LookPath(os.Args[0])
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	path, err = filepath.EvalSymlinks(path)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	prefix := filepath.Join(path, "..", "..")
 | |
| 	return prefix, nil
 | |
| }
 | |
| 
 | |
| func parseArguments(args []string) (opts driverOptions, err error) {
 | |
| 	var goInputs, otherInputs []string
 | |
| 	hasOtherNonFlagInputs := false
 | |
| 	noPrefix := false
 | |
| 	actionKind := actionLink
 | |
| 	opts.triple = llvm.DefaultTargetTriple()
 | |
| 
 | |
| 	for len(args) > 0 {
 | |
| 		consumedArgs := 1
 | |
| 
 | |
| 		switch {
 | |
| 		case !strings.HasPrefix(args[0], "-"):
 | |
| 			if strings.HasSuffix(args[0], ".go") {
 | |
| 				goInputs = append(goInputs, args[0])
 | |
| 			} else {
 | |
| 				hasOtherNonFlagInputs = true
 | |
| 				otherInputs = append(otherInputs, args[0])
 | |
| 			}
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-Wl,"), strings.HasPrefix(args[0], "-l"), strings.HasPrefix(args[0], "--sysroot="):
 | |
| 			// TODO(pcc): Handle these correctly.
 | |
| 			otherInputs = append(otherInputs, args[0])
 | |
| 
 | |
| 		case args[0] == "-B":
 | |
| 			if len(args) == 1 {
 | |
| 				return opts, errors.New("missing argument after '-B'")
 | |
| 			}
 | |
| 			opts.bprefix = args[1]
 | |
| 			consumedArgs = 2
 | |
| 
 | |
| 		case args[0] == "-D":
 | |
| 			if len(args) == 1 {
 | |
| 				return opts, errors.New("missing argument after '-D'")
 | |
| 			}
 | |
| 			otherInputs = append(otherInputs, args[0], args[1])
 | |
| 			consumedArgs = 2
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-D"):
 | |
| 			otherInputs = append(otherInputs, args[0])
 | |
| 
 | |
| 		case args[0] == "-I":
 | |
| 			if len(args) == 1 {
 | |
| 				return opts, errors.New("missing argument after '-I'")
 | |
| 			}
 | |
| 			opts.importPaths = append(opts.importPaths, args[1])
 | |
| 			consumedArgs = 2
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-I"):
 | |
| 			opts.importPaths = append(opts.importPaths, args[0][2:])
 | |
| 
 | |
| 		case args[0] == "-isystem":
 | |
| 			if len(args) == 1 {
 | |
| 				return opts, errors.New("missing argument after '-isystem'")
 | |
| 			}
 | |
| 			otherInputs = append(otherInputs, args[0], args[1])
 | |
| 			consumedArgs = 2
 | |
| 
 | |
| 		case args[0] == "-L":
 | |
| 			if len(args) == 1 {
 | |
| 				return opts, errors.New("missing argument after '-L'")
 | |
| 			}
 | |
| 			opts.libPaths = append(opts.libPaths, args[1])
 | |
| 			consumedArgs = 2
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-L"):
 | |
| 			opts.libPaths = append(opts.libPaths, args[0][2:])
 | |
| 
 | |
| 		case args[0] == "-O0":
 | |
| 			opts.optLevel = 0
 | |
| 
 | |
| 		case args[0] == "-O1", args[0] == "-O":
 | |
| 			opts.optLevel = 1
 | |
| 
 | |
| 		case args[0] == "-O2":
 | |
| 			opts.optLevel = 2
 | |
| 
 | |
| 		case args[0] == "-Os":
 | |
| 			opts.optLevel = 2
 | |
| 			opts.sizeLevel = 1
 | |
| 
 | |
| 		case args[0] == "-O3":
 | |
| 			opts.optLevel = 3
 | |
| 
 | |
| 		case args[0] == "-S":
 | |
| 			actionKind = actionAssemble
 | |
| 
 | |
| 		case args[0] == "-c":
 | |
| 			actionKind = actionCompile
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-fcompilerrt-prefix="):
 | |
| 			opts.sanitizer.crtPrefix = args[0][20:]
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-fdebug-prefix-map="):
 | |
| 			split := strings.SplitN(args[0][19:], "=", 2)
 | |
| 			if len(split) < 2 {
 | |
| 				return opts, fmt.Errorf("argument '%s' must be of form '-fdebug-prefix-map=SOURCE=REPLACEMENT'", args[0])
 | |
| 			}
 | |
| 			opts.debugPrefixMaps = append(opts.debugPrefixMaps, debug.PrefixMap{split[0], split[1]})
 | |
| 
 | |
| 		case args[0] == "-fdump-ssa":
 | |
| 			opts.dumpSSA = true
 | |
| 
 | |
| 		case args[0] == "-fdump-trace":
 | |
| 			opts.dumpTrace = true
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-fgccgo-path="):
 | |
| 			opts.gccgoPath = args[0][13:]
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-fgo-pkgpath="):
 | |
| 			opts.pkgpath = args[0][13:]
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-fgo-relative-import-path="):
 | |
| 			// TODO(pcc): Handle this.
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-fstack-protector"):
 | |
| 			// TODO(axw) set ssp function attributes. This can be useful
 | |
| 			// even for Go, if it interfaces with code written in a non-
 | |
| 			// memory safe language (e.g. via cgo).
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-W"):
 | |
| 			// Go doesn't do warnings. Ignore.
 | |
| 
 | |
| 		case args[0] == "-fload-plugin":
 | |
| 			if len(args) == 1 {
 | |
| 				return opts, errors.New("missing argument after '-fload-plugin'")
 | |
| 			}
 | |
| 			opts.plugins = append(opts.plugins, args[1])
 | |
| 			consumedArgs = 2
 | |
| 
 | |
| 		case args[0] == "-fno-toplevel-reorder":
 | |
| 			// This is a GCC-specific code generation option. Ignore.
 | |
| 
 | |
| 		case args[0] == "-emit-llvm":
 | |
| 			opts.emitIR = true
 | |
| 
 | |
| 		case args[0] == "-flto":
 | |
| 			opts.lto = true
 | |
| 
 | |
| 		case args[0] == "-fPIC":
 | |
| 			opts.pic = true
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-fsanitize-blacklist="):
 | |
| 			opts.sanitizer.blacklist = args[0][21:]
 | |
| 
 | |
| 		// TODO(pcc): Enforce mutual exclusion between sanitizers.
 | |
| 
 | |
| 		case args[0] == "-fsanitize=address":
 | |
| 			opts.sanitizer.address = true
 | |
| 
 | |
| 		case args[0] == "-fsanitize=thread":
 | |
| 			opts.sanitizer.thread = true
 | |
| 
 | |
| 		case args[0] == "-fsanitize=memory":
 | |
| 			opts.sanitizer.memory = true
 | |
| 
 | |
| 		case args[0] == "-fsanitize=dataflow":
 | |
| 			opts.sanitizer.dataflow = true
 | |
| 
 | |
| 		case args[0] == "-g":
 | |
| 			opts.generateDebug = true
 | |
| 
 | |
| 		case args[0] == "-mllvm":
 | |
| 			if len(args) == 1 {
 | |
| 				return opts, errors.New("missing argument after '-mllvm'")
 | |
| 			}
 | |
| 			opts.llvmArgs = append(opts.llvmArgs, args[1])
 | |
| 			consumedArgs = 2
 | |
| 
 | |
| 		case strings.HasPrefix(args[0], "-m"), args[0] == "-funsafe-math-optimizations", args[0] == "-ffp-contract=off":
 | |
| 			// TODO(pcc): Handle code generation options.
 | |
| 
 | |
| 		case args[0] == "-no-prefix":
 | |
| 			noPrefix = true
 | |
| 
 | |
| 		case args[0] == "-o":
 | |
| 			if len(args) == 1 {
 | |
| 				return opts, errors.New("missing argument after '-o'")
 | |
| 			}
 | |
| 			opts.output = args[1]
 | |
| 			consumedArgs = 2
 | |
| 
 | |
| 		case args[0] == "-pie":
 | |
| 			opts.pieLink = true
 | |
| 
 | |
| 		case args[0] == "-dumpversion",
 | |
| 			args[0] == "-print-libgcc-file-name",
 | |
| 			args[0] == "-print-multi-os-directory",
 | |
| 			args[0] == "--version":
 | |
| 			actionKind = actionPrint
 | |
| 			opts.output = args[0]
 | |
| 
 | |
| 		case args[0] == "-static":
 | |
| 			opts.staticLink = true
 | |
| 
 | |
| 		case args[0] == "-static-libgcc":
 | |
| 			opts.staticLibgcc = true
 | |
| 
 | |
| 		case args[0] == "-static-libgo":
 | |
| 			opts.staticLibgo = true
 | |
| 
 | |
| 		default:
 | |
| 			return opts, fmt.Errorf("unrecognized command line option '%s'", args[0])
 | |
| 		}
 | |
| 
 | |
| 		args = args[consumedArgs:]
 | |
| 	}
 | |
| 
 | |
| 	if actionKind != actionPrint && len(goInputs) == 0 && !hasOtherNonFlagInputs {
 | |
| 		return opts, errors.New("no input files")
 | |
| 	}
 | |
| 
 | |
| 	if !noPrefix {
 | |
| 		opts.prefix, err = getInstPrefix()
 | |
| 		if err != nil {
 | |
| 			return opts, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if opts.sanitizer.crtPrefix == "" {
 | |
| 		opts.sanitizer.crtPrefix = opts.prefix
 | |
| 	}
 | |
| 
 | |
| 	if opts.sanitizer.isPIEDefault() {
 | |
| 		// This should really only be turning on -fPIE, but this isn't
 | |
| 		// easy to do from Go, and -fPIC is a superset of it anyway.
 | |
| 		opts.pic = true
 | |
| 		opts.pieLink = true
 | |
| 	}
 | |
| 
 | |
| 	switch actionKind {
 | |
| 	case actionLink:
 | |
| 		if len(goInputs) != 0 {
 | |
| 			opts.actions = []action{action{actionCompile, goInputs}}
 | |
| 		}
 | |
| 		opts.actions = append(opts.actions, action{actionLink, otherInputs})
 | |
| 
 | |
| 	case actionCompile, actionAssemble:
 | |
| 		if len(goInputs) != 0 {
 | |
| 			opts.actions = []action{action{actionKind, goInputs}}
 | |
| 		}
 | |
| 
 | |
| 	case actionPrint:
 | |
| 		opts.actions = []action{action{actionKind, nil}}
 | |
| 	}
 | |
| 
 | |
| 	if opts.output == "" && len(opts.actions) != 0 {
 | |
| 		switch actionKind {
 | |
| 		case actionCompile, actionAssemble:
 | |
| 			base := filepath.Base(goInputs[0])
 | |
| 			base = base[0 : len(base)-3]
 | |
| 			if actionKind == actionCompile {
 | |
| 				opts.output = base + ".o"
 | |
| 			} else {
 | |
| 				opts.output = base + ".s"
 | |
| 			}
 | |
| 
 | |
| 		case actionLink:
 | |
| 			opts.output = "a.out"
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return opts, nil
 | |
| }
 | |
| 
 | |
| func runPasses(opts *driverOptions, tm llvm.TargetMachine, m llvm.Module) {
 | |
| 	fpm := llvm.NewFunctionPassManagerForModule(m)
 | |
| 	defer fpm.Dispose()
 | |
| 
 | |
| 	mpm := llvm.NewPassManager()
 | |
| 	defer mpm.Dispose()
 | |
| 
 | |
| 	pmb := llvm.NewPassManagerBuilder()
 | |
| 	defer pmb.Dispose()
 | |
| 
 | |
| 	pmb.SetOptLevel(opts.optLevel)
 | |
| 	pmb.SetSizeLevel(opts.sizeLevel)
 | |
| 
 | |
| 	target := tm.TargetData()
 | |
| 	mpm.Add(target)
 | |
| 	fpm.Add(target)
 | |
| 	tm.AddAnalysisPasses(mpm)
 | |
| 	tm.AddAnalysisPasses(fpm)
 | |
| 
 | |
| 	mpm.AddVerifierPass()
 | |
| 	fpm.AddVerifierPass()
 | |
| 
 | |
| 	pmb.Populate(mpm)
 | |
| 	pmb.PopulateFunc(fpm)
 | |
| 
 | |
| 	if opts.optLevel == 0 {
 | |
| 		// Remove references (via the descriptor) to dead functions,
 | |
| 		// for compatibility with other compilers.
 | |
| 		mpm.AddGlobalDCEPass()
 | |
| 	}
 | |
| 
 | |
| 	opts.sanitizer.addPasses(mpm, fpm)
 | |
| 
 | |
| 	fpm.InitializeFunc()
 | |
| 	for fn := m.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
 | |
| 		fpm.RunFunc(fn)
 | |
| 	}
 | |
| 	fpm.FinalizeFunc()
 | |
| 
 | |
| 	mpm.Run(m)
 | |
| }
 | |
| 
 | |
| func getMetadataSectionInlineAsm(name string) string {
 | |
| 	// ELF: creates a non-allocated excluded section.
 | |
| 	return ".section \"" + name + "\", \"e\"\n"
 | |
| }
 | |
| 
 | |
| func getDataInlineAsm(data []byte) string {
 | |
| 	edata := make([]byte, 0, len(data)*4+10)
 | |
| 
 | |
| 	edata = append(edata, ".ascii \""...)
 | |
| 	for i := range data {
 | |
| 		switch data[i] {
 | |
| 		case '\000':
 | |
| 			edata = append(edata, "\\000"...)
 | |
| 			continue
 | |
| 		case '\n':
 | |
| 			edata = append(edata, "\\n"...)
 | |
| 			continue
 | |
| 		case '"', '\\':
 | |
| 			edata = append(edata, '\\')
 | |
| 		}
 | |
| 		edata = append(edata, data[i])
 | |
| 	}
 | |
| 	edata = append(edata, "\"\n"...)
 | |
| 	return string(edata)
 | |
| }
 | |
| 
 | |
| // Get the lib path to the standard libraries for the given driver options.
 | |
| // This is normally 'lib' but can vary for cross compilation, LTO, sanitizers
 | |
| // etc.
 | |
| func getLibDir(opts *driverOptions) string {
 | |
| 	lib := "lib" + LibDirSuffix
 | |
| 	switch {
 | |
| 	case opts.lto:
 | |
| 		return filepath.Join(lib, "llvm-lto.0")
 | |
| 	case opts.sanitizer.address:
 | |
| 		return filepath.Join(lib, "llvm-asan.0")
 | |
| 	case opts.sanitizer.thread:
 | |
| 		return filepath.Join(lib, "llvm-tsan.0")
 | |
| 	case opts.sanitizer.memory:
 | |
| 		return filepath.Join(lib, "llvm-msan.0")
 | |
| 	case opts.sanitizer.dataflow:
 | |
| 		return filepath.Join(lib, "llvm-dfsan.0")
 | |
| 	default:
 | |
| 		return lib
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func performAction(opts *driverOptions, kind actionKind, inputs []string, output string) error {
 | |
| 	switch kind {
 | |
| 	case actionPrint:
 | |
| 		switch opts.output {
 | |
| 		case "-dumpversion":
 | |
| 			fmt.Println("llgo-" + llvmVersion())
 | |
| 			return nil
 | |
| 		case "-print-libgcc-file-name":
 | |
| 			cmd := exec.Command(opts.bprefix+"gcc", "-print-libgcc-file-name")
 | |
| 			out, err := cmd.CombinedOutput()
 | |
| 			os.Stdout.Write(out)
 | |
| 			return err
 | |
| 		case "-print-multi-os-directory":
 | |
| 			fmt.Println(filepath.Join("..", getLibDir(opts)))
 | |
| 			return nil
 | |
| 		case "--version":
 | |
| 			displayVersion()
 | |
| 			return nil
 | |
| 		default:
 | |
| 			panic("unexpected print command")
 | |
| 		}
 | |
| 
 | |
| 	case actionCompile, actionAssemble:
 | |
| 		compiler, err := initCompiler(opts)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		fset := token.NewFileSet()
 | |
| 		files, err := driver.ParseFiles(fset, inputs)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		module, err := compiler.Compile(fset, files, opts.pkgpath)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		defer module.Dispose()
 | |
| 
 | |
| 		target, err := llvm.GetTargetFromTriple(opts.triple)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		optLevel := [...]llvm.CodeGenOptLevel{
 | |
| 			llvm.CodeGenLevelNone,
 | |
| 			llvm.CodeGenLevelLess,
 | |
| 			llvm.CodeGenLevelDefault,
 | |
| 			llvm.CodeGenLevelAggressive,
 | |
| 		}[opts.optLevel]
 | |
| 
 | |
| 		relocMode := llvm.RelocStatic
 | |
| 		if opts.pic {
 | |
| 			relocMode = llvm.RelocPIC
 | |
| 		}
 | |
| 
 | |
| 		tm := target.CreateTargetMachine(opts.triple, "", "", optLevel,
 | |
| 			relocMode, llvm.CodeModelDefault)
 | |
| 		defer tm.Dispose()
 | |
| 
 | |
| 		runPasses(opts, tm, module.Module)
 | |
| 
 | |
| 		var file *os.File
 | |
| 		if output == "-" {
 | |
| 			file = os.Stdout
 | |
| 		} else {
 | |
| 			file, err = os.Create(output)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			defer file.Close()
 | |
| 		}
 | |
| 
 | |
| 		switch {
 | |
| 		case !opts.lto && !opts.emitIR:
 | |
| 			if module.ExportData != nil {
 | |
| 				asm := getMetadataSectionInlineAsm(".go_export")
 | |
| 				asm += getDataInlineAsm(module.ExportData)
 | |
| 				module.Module.SetInlineAsm(asm)
 | |
| 			}
 | |
| 
 | |
| 			fileType := llvm.AssemblyFile
 | |
| 			if kind == actionCompile {
 | |
| 				fileType = llvm.ObjectFile
 | |
| 			}
 | |
| 			mb, err := tm.EmitToMemoryBuffer(module.Module, fileType)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			defer mb.Dispose()
 | |
| 
 | |
| 			bytes := mb.Bytes()
 | |
| 			_, err = file.Write(bytes)
 | |
| 			return err
 | |
| 
 | |
| 		case opts.lto:
 | |
| 			bcmb := llvm.WriteBitcodeToMemoryBuffer(module.Module)
 | |
| 			defer bcmb.Dispose()
 | |
| 
 | |
| 			// This is a bit of a hack. We just want an object file
 | |
| 			// containing some metadata sections. This might be simpler
 | |
| 			// if we had bindings for the MC library, but for now we create
 | |
| 			// a fresh module containing only inline asm that creates the
 | |
| 			// sections.
 | |
| 			outmodule := llvm.NewModule("")
 | |
| 			defer outmodule.Dispose()
 | |
| 			asm := getMetadataSectionInlineAsm(".llvmbc")
 | |
| 			asm += getDataInlineAsm(bcmb.Bytes())
 | |
| 			if module.ExportData != nil {
 | |
| 				asm += getMetadataSectionInlineAsm(".go_export")
 | |
| 				asm += getDataInlineAsm(module.ExportData)
 | |
| 			}
 | |
| 			outmodule.SetInlineAsm(asm)
 | |
| 
 | |
| 			fileType := llvm.AssemblyFile
 | |
| 			if kind == actionCompile {
 | |
| 				fileType = llvm.ObjectFile
 | |
| 			}
 | |
| 			mb, err := tm.EmitToMemoryBuffer(outmodule, fileType)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			defer mb.Dispose()
 | |
| 
 | |
| 			bytes := mb.Bytes()
 | |
| 			_, err = file.Write(bytes)
 | |
| 			return err
 | |
| 
 | |
| 		case kind == actionCompile:
 | |
| 			err := llvm.WriteBitcodeToFile(module.Module, file)
 | |
| 			return err
 | |
| 
 | |
| 		case kind == actionAssemble:
 | |
| 			_, err := file.WriteString(module.Module.String())
 | |
| 			return err
 | |
| 
 | |
| 		default:
 | |
| 			panic("unexpected action kind")
 | |
| 		}
 | |
| 
 | |
| 	case actionLink:
 | |
| 		// TODO(pcc): Teach this to do LTO.
 | |
| 		args := []string{"-o", output}
 | |
| 		if opts.pic {
 | |
| 			args = append(args, "-fPIC")
 | |
| 		}
 | |
| 		if opts.pieLink {
 | |
| 			args = append(args, "-pie")
 | |
| 		}
 | |
| 		if opts.staticLink {
 | |
| 			args = append(args, "-static")
 | |
| 		}
 | |
| 		if opts.staticLibgcc {
 | |
| 			args = append(args, "-static-libgcc")
 | |
| 		}
 | |
| 		for _, p := range opts.libPaths {
 | |
| 			args = append(args, "-L", p)
 | |
| 		}
 | |
| 		for _, p := range opts.importPaths {
 | |
| 			args = append(args, "-I", p)
 | |
| 		}
 | |
| 		args = append(args, inputs...)
 | |
| 		var linkerPath string
 | |
| 		if opts.gccgoPath == "" {
 | |
| 			// TODO(pcc): See if we can avoid calling gcc here.
 | |
| 			// We currently rely on it to find crt*.o and compile
 | |
| 			// any C source files passed as arguments.
 | |
| 			linkerPath = opts.bprefix + "gcc"
 | |
| 
 | |
| 			if opts.prefix != "" {
 | |
| 				libdir := filepath.Join(opts.prefix, getLibDir(opts))
 | |
| 				args = append(args, "-L", libdir)
 | |
| 				if !opts.staticLibgo {
 | |
| 					args = append(args, "-Wl,-rpath,"+libdir)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			args = append(args, "-lgobegin-llgo")
 | |
| 			if opts.staticLibgo {
 | |
| 				args = append(args, "-Wl,-Bstatic", "-lgo-llgo", "-Wl,-Bdynamic", "-lpthread", "-lm")
 | |
| 			} else {
 | |
| 				args = append(args, "-lgo-llgo")
 | |
| 			}
 | |
| 		} else {
 | |
| 			linkerPath = opts.gccgoPath
 | |
| 			if opts.staticLibgo {
 | |
| 				args = append(args, "-static-libgo")
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		args = opts.sanitizer.addLibs(opts.triple, args)
 | |
| 
 | |
| 		cmd := exec.Command(linkerPath, args...)
 | |
| 		out, err := cmd.CombinedOutput()
 | |
| 		if err != nil {
 | |
| 			os.Stderr.Write(out)
 | |
| 		}
 | |
| 		return err
 | |
| 
 | |
| 	default:
 | |
| 		panic("unexpected action kind")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func performActions(opts *driverOptions) error {
 | |
| 	var extraInput string
 | |
| 
 | |
| 	for _, plugin := range opts.plugins {
 | |
| 		err := llvm.LoadLibraryPermanently(plugin)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	llvm.ParseCommandLineOptions(append([]string{"llgo"}, opts.llvmArgs...), "llgo (LLVM option parsing)\n")
 | |
| 
 | |
| 	for i, action := range opts.actions {
 | |
| 		var output string
 | |
| 		if i == len(opts.actions)-1 {
 | |
| 			output = opts.output
 | |
| 		} else {
 | |
| 			tmpfile, err := ioutil.TempFile("", "llgo")
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			output = tmpfile.Name() + ".o"
 | |
| 			tmpfile.Close()
 | |
| 			err = os.Remove(tmpfile.Name())
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			defer os.Remove(output)
 | |
| 		}
 | |
| 
 | |
| 		inputs := action.inputs
 | |
| 		if extraInput != "" {
 | |
| 			inputs = append([]string{extraInput}, inputs...)
 | |
| 		}
 | |
| 
 | |
| 		err := performAction(opts, action.kind, inputs, output)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		extraInput = output
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func main() {
 | |
| 	llvm.InitializeAllTargets()
 | |
| 	llvm.InitializeAllTargetMCs()
 | |
| 	llvm.InitializeAllTargetInfos()
 | |
| 	llvm.InitializeAllAsmParsers()
 | |
| 	llvm.InitializeAllAsmPrinters()
 | |
| 
 | |
| 	opts, err := parseArguments(os.Args[1:])
 | |
| 	if err != nil {
 | |
| 		report(err)
 | |
| 		os.Exit(1)
 | |
| 	}
 | |
| 
 | |
| 	err = performActions(&opts)
 | |
| 	if err != nil {
 | |
| 		report(err)
 | |
| 		os.Exit(1)
 | |
| 	}
 | |
| }
 |