github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/cmd/gllgo/gllgo.go (about)

     1  //===- gllgo.go - gccgo-like driver for llgo ------------------------------===//
     2  //
     3  //                     The LLVM Compiler Infrastructure
     4  //
     5  // This file is distributed under the University of Illinois Open Source
     6  // License. See LICENSE.TXT for details.
     7  //
     8  //===----------------------------------------------------------------------===//
     9  //
    10  // This is llgo's driver. It has a gccgo-like interface in order to easily
    11  // interoperate with the "go" command and the libgo build system.
    12  //
    13  //===----------------------------------------------------------------------===//
    14  
    15  package main
    16  
    17  /*
    18  #include "config.h"
    19  */
    20  import "C"
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"go/scanner"
    26  	"go/token"
    27  	"io/ioutil"
    28  	"log"
    29  	"os"
    30  	"os/exec"
    31  	"path/filepath"
    32  	"strings"
    33  
    34  	"llvm.org/llgo/debug"
    35  	"llvm.org/llgo/driver"
    36  	"llvm.org/llgo/irgen"
    37  	"llvm.org/llvm/bindings/go/llvm"
    38  )
    39  
    40  const LibDirSuffix = C.LLVM_LIBDIR_SUFFIX
    41  
    42  func report(err error) {
    43  	if list, ok := err.(scanner.ErrorList); ok {
    44  		for _, e := range list {
    45  			fmt.Fprintf(os.Stderr, "%s\n", e)
    46  		}
    47  	} else if err != nil {
    48  		fmt.Fprintf(os.Stderr, "gllgo: error: %s\n", err)
    49  	}
    50  }
    51  
    52  func llvmVersion() string {
    53  	return strings.Replace(llvm.Version, "svn", "", 1)
    54  }
    55  
    56  func displayVersion() {
    57  	fmt.Printf("llgo version %s (%s)\n\n", llvmVersion(), irgen.GoVersion())
    58  	os.Exit(0)
    59  }
    60  
    61  func initCompiler(opts *driverOptions) (*irgen.Compiler, error) {
    62  	importPaths := make([]string, len(opts.importPaths)+len(opts.libPaths))
    63  	copy(importPaths, opts.importPaths)
    64  	copy(importPaths[len(opts.importPaths):], opts.libPaths)
    65  	if opts.prefix != "" {
    66  		importPaths = append(importPaths, filepath.Join(opts.prefix, "lib"+LibDirSuffix, "go", "llgo-"+llvmVersion()))
    67  	}
    68  	copts := irgen.CompilerOptions{
    69  		TargetTriple:       opts.triple,
    70  		GenerateDebug:      opts.generateDebug,
    71  		DebugPrefixMaps:    opts.debugPrefixMaps,
    72  		DumpSSA:            opts.dumpSSA,
    73  		GccgoPath:          opts.gccgoPath,
    74  		GccgoABI:           opts.gccgoPath != "",
    75  		ImportPaths:        importPaths,
    76  		SanitizerAttribute: opts.sanitizer.getAttribute(),
    77  	}
    78  	if opts.dumpTrace {
    79  		copts.Logger = log.New(os.Stderr, "", 0)
    80  	}
    81  	return irgen.NewCompiler(copts)
    82  }
    83  
    84  type actionKind int
    85  
    86  const (
    87  	actionAssemble = actionKind(iota)
    88  	actionCompile
    89  	actionLink
    90  	actionPrint
    91  )
    92  
    93  type action struct {
    94  	kind   actionKind
    95  	inputs []string
    96  }
    97  
    98  type sanitizerOptions struct {
    99  	blacklist string
   100  	crtPrefix string
   101  
   102  	address, thread, memory, dataflow bool
   103  }
   104  
   105  func (san *sanitizerOptions) resourcePath() string {
   106  	return filepath.Join(san.crtPrefix, "lib"+LibDirSuffix, "clang", llvmVersion())
   107  }
   108  
   109  func (san *sanitizerOptions) isPIEDefault() bool {
   110  	return san.thread || san.memory || san.dataflow
   111  }
   112  
   113  func (san *sanitizerOptions) addPasses(mpm, fpm llvm.PassManager) {
   114  	switch {
   115  	case san.address:
   116  		mpm.AddAddressSanitizerModulePass()
   117  		fpm.AddAddressSanitizerFunctionPass()
   118  	case san.thread:
   119  		mpm.AddThreadSanitizerPass()
   120  	case san.memory:
   121  		mpm.AddMemorySanitizerPass()
   122  	case san.dataflow:
   123  		blacklist := san.blacklist
   124  		if blacklist == "" {
   125  			blacklist = filepath.Join(san.resourcePath(), "dfsan_abilist.txt")
   126  		}
   127  		mpm.AddDataFlowSanitizerPass([]string{blacklist})
   128  	}
   129  }
   130  
   131  func (san *sanitizerOptions) libPath(triple, sanitizerName string) string {
   132  	s := strings.Split(triple, "-")
   133  	return filepath.Join(san.resourcePath(), "lib", s[2], "libclang_rt."+sanitizerName+"-"+s[0]+".a")
   134  }
   135  
   136  func (san *sanitizerOptions) addLibsForSanitizer(flags []string, triple, sanitizerName string) []string {
   137  	return append(flags, san.libPath(triple, sanitizerName),
   138  		"-Wl,--no-as-needed", "-lpthread", "-lrt", "-lm", "-ldl")
   139  }
   140  
   141  func (san *sanitizerOptions) addLibs(triple string, flags []string) []string {
   142  	switch {
   143  	case san.address:
   144  		flags = san.addLibsForSanitizer(flags, triple, "asan")
   145  	case san.thread:
   146  		flags = san.addLibsForSanitizer(flags, triple, "tsan")
   147  	case san.memory:
   148  		flags = san.addLibsForSanitizer(flags, triple, "msan")
   149  	case san.dataflow:
   150  		flags = san.addLibsForSanitizer(flags, triple, "dfsan")
   151  	}
   152  
   153  	return flags
   154  }
   155  
   156  func (san *sanitizerOptions) getAttribute() llvm.Attribute {
   157  	switch {
   158  	case san.address:
   159  		return llvm.SanitizeAddressAttribute
   160  	case san.thread:
   161  		return llvm.SanitizeThreadAttribute
   162  	case san.memory:
   163  		return llvm.SanitizeMemoryAttribute
   164  	default:
   165  		return 0
   166  	}
   167  }
   168  
   169  type driverOptions struct {
   170  	actions []action
   171  	output  string
   172  
   173  	bprefix         string
   174  	debugPrefixMaps []debug.PrefixMap
   175  	dumpSSA         bool
   176  	dumpTrace       bool
   177  	emitIR          bool
   178  	gccgoPath       string
   179  	generateDebug   bool
   180  	importPaths     []string
   181  	libPaths        []string
   182  	llvmArgs        []string
   183  	lto             bool
   184  	optLevel        int
   185  	pic             bool
   186  	pieLink         bool
   187  	pkgpath         string
   188  	plugins         []string
   189  	prefix          string
   190  	sanitizer       sanitizerOptions
   191  	sizeLevel       int
   192  	staticLibgcc    bool
   193  	staticLibgo     bool
   194  	staticLink      bool
   195  	triple          string
   196  }
   197  
   198  func getInstPrefix() (string, error) {
   199  	path, err := exec.LookPath(os.Args[0])
   200  	if err != nil {
   201  		return "", err
   202  	}
   203  
   204  	path, err = filepath.EvalSymlinks(path)
   205  	if err != nil {
   206  		return "", err
   207  	}
   208  
   209  	prefix := filepath.Join(path, "..", "..")
   210  	return prefix, nil
   211  }
   212  
   213  func parseArguments(args []string) (opts driverOptions, err error) {
   214  	var goInputs, otherInputs []string
   215  	hasOtherNonFlagInputs := false
   216  	noPrefix := false
   217  	actionKind := actionLink
   218  	opts.triple = llvm.DefaultTargetTriple()
   219  
   220  	for len(args) > 0 {
   221  		consumedArgs := 1
   222  
   223  		switch {
   224  		case !strings.HasPrefix(args[0], "-"):
   225  			if strings.HasSuffix(args[0], ".go") {
   226  				goInputs = append(goInputs, args[0])
   227  			} else {
   228  				hasOtherNonFlagInputs = true
   229  				otherInputs = append(otherInputs, args[0])
   230  			}
   231  
   232  		case strings.HasPrefix(args[0], "-Wl,"), strings.HasPrefix(args[0], "-l"), strings.HasPrefix(args[0], "--sysroot="):
   233  			// TODO(pcc): Handle these correctly.
   234  			otherInputs = append(otherInputs, args[0])
   235  
   236  		case args[0] == "-B":
   237  			if len(args) == 1 {
   238  				return opts, errors.New("missing argument after '-B'")
   239  			}
   240  			opts.bprefix = args[1]
   241  			consumedArgs = 2
   242  
   243  		case args[0] == "-D":
   244  			if len(args) == 1 {
   245  				return opts, errors.New("missing argument after '-D'")
   246  			}
   247  			otherInputs = append(otherInputs, args[0], args[1])
   248  			consumedArgs = 2
   249  
   250  		case strings.HasPrefix(args[0], "-D"):
   251  			otherInputs = append(otherInputs, args[0])
   252  
   253  		case args[0] == "-I":
   254  			if len(args) == 1 {
   255  				return opts, errors.New("missing argument after '-I'")
   256  			}
   257  			opts.importPaths = append(opts.importPaths, args[1])
   258  			consumedArgs = 2
   259  
   260  		case strings.HasPrefix(args[0], "-I"):
   261  			opts.importPaths = append(opts.importPaths, args[0][2:])
   262  
   263  		case args[0] == "-isystem":
   264  			if len(args) == 1 {
   265  				return opts, errors.New("missing argument after '-isystem'")
   266  			}
   267  			otherInputs = append(otherInputs, args[0], args[1])
   268  			consumedArgs = 2
   269  
   270  		case args[0] == "-L":
   271  			if len(args) == 1 {
   272  				return opts, errors.New("missing argument after '-L'")
   273  			}
   274  			opts.libPaths = append(opts.libPaths, args[1])
   275  			consumedArgs = 2
   276  
   277  		case strings.HasPrefix(args[0], "-L"):
   278  			opts.libPaths = append(opts.libPaths, args[0][2:])
   279  
   280  		case args[0] == "-O0":
   281  			opts.optLevel = 0
   282  
   283  		case args[0] == "-O1", args[0] == "-O":
   284  			opts.optLevel = 1
   285  
   286  		case args[0] == "-O2":
   287  			opts.optLevel = 2
   288  
   289  		case args[0] == "-Os":
   290  			opts.optLevel = 2
   291  			opts.sizeLevel = 1
   292  
   293  		case args[0] == "-O3":
   294  			opts.optLevel = 3
   295  
   296  		case args[0] == "-S":
   297  			actionKind = actionAssemble
   298  
   299  		case args[0] == "-c":
   300  			actionKind = actionCompile
   301  
   302  		case strings.HasPrefix(args[0], "-fcompilerrt-prefix="):
   303  			opts.sanitizer.crtPrefix = args[0][20:]
   304  
   305  		case strings.HasPrefix(args[0], "-fdebug-prefix-map="):
   306  			split := strings.SplitN(args[0][19:], "=", 2)
   307  			if len(split) < 2 {
   308  				return opts, fmt.Errorf("argument '%s' must be of form '-fdebug-prefix-map=SOURCE=REPLACEMENT'", args[0])
   309  			}
   310  			opts.debugPrefixMaps = append(opts.debugPrefixMaps, debug.PrefixMap{split[0], split[1]})
   311  
   312  		case args[0] == "-fdump-ssa":
   313  			opts.dumpSSA = true
   314  
   315  		case args[0] == "-fdump-trace":
   316  			opts.dumpTrace = true
   317  
   318  		case strings.HasPrefix(args[0], "-fgccgo-path="):
   319  			opts.gccgoPath = args[0][13:]
   320  
   321  		case strings.HasPrefix(args[0], "-fgo-pkgpath="):
   322  			opts.pkgpath = args[0][13:]
   323  
   324  		case strings.HasPrefix(args[0], "-fgo-relative-import-path="):
   325  			// TODO(pcc): Handle this.
   326  
   327  		case strings.HasPrefix(args[0], "-fstack-protector"):
   328  			// TODO(axw) set ssp function attributes. This can be useful
   329  			// even for Go, if it interfaces with code written in a non-
   330  			// memory safe language (e.g. via cgo).
   331  
   332  		case strings.HasPrefix(args[0], "-W"):
   333  			// Go doesn't do warnings. Ignore.
   334  
   335  		case args[0] == "-fload-plugin":
   336  			if len(args) == 1 {
   337  				return opts, errors.New("missing argument after '-fload-plugin'")
   338  			}
   339  			opts.plugins = append(opts.plugins, args[1])
   340  			consumedArgs = 2
   341  
   342  		case args[0] == "-fno-toplevel-reorder":
   343  			// This is a GCC-specific code generation option. Ignore.
   344  
   345  		case args[0] == "-emit-llvm":
   346  			opts.emitIR = true
   347  
   348  		case args[0] == "-flto":
   349  			opts.lto = true
   350  
   351  		case args[0] == "-fPIC":
   352  			opts.pic = true
   353  
   354  		case strings.HasPrefix(args[0], "-fsanitize-blacklist="):
   355  			opts.sanitizer.blacklist = args[0][21:]
   356  
   357  		// TODO(pcc): Enforce mutual exclusion between sanitizers.
   358  
   359  		case args[0] == "-fsanitize=address":
   360  			opts.sanitizer.address = true
   361  
   362  		case args[0] == "-fsanitize=thread":
   363  			opts.sanitizer.thread = true
   364  
   365  		case args[0] == "-fsanitize=memory":
   366  			opts.sanitizer.memory = true
   367  
   368  		case args[0] == "-fsanitize=dataflow":
   369  			opts.sanitizer.dataflow = true
   370  
   371  		case args[0] == "-g":
   372  			opts.generateDebug = true
   373  
   374  		case args[0] == "-mllvm":
   375  			if len(args) == 1 {
   376  				return opts, errors.New("missing argument after '-mllvm'")
   377  			}
   378  			opts.llvmArgs = append(opts.llvmArgs, args[1])
   379  			consumedArgs = 2
   380  
   381  		case strings.HasPrefix(args[0], "-m"), args[0] == "-funsafe-math-optimizations", args[0] == "-ffp-contract=off":
   382  			// TODO(pcc): Handle code generation options.
   383  
   384  		case args[0] == "-no-prefix":
   385  			noPrefix = true
   386  
   387  		case args[0] == "-o":
   388  			if len(args) == 1 {
   389  				return opts, errors.New("missing argument after '-o'")
   390  			}
   391  			opts.output = args[1]
   392  			consumedArgs = 2
   393  
   394  		case args[0] == "-pie":
   395  			opts.pieLink = true
   396  
   397  		case args[0] == "-dumpversion",
   398  			args[0] == "-print-libgcc-file-name",
   399  			args[0] == "-print-multi-os-directory",
   400  			args[0] == "--version":
   401  			actionKind = actionPrint
   402  			opts.output = args[0]
   403  
   404  		case args[0] == "-static":
   405  			opts.staticLink = true
   406  
   407  		case args[0] == "-static-libgcc":
   408  			opts.staticLibgcc = true
   409  
   410  		case args[0] == "-static-libgo":
   411  			opts.staticLibgo = true
   412  
   413  		default:
   414  			return opts, fmt.Errorf("unrecognized command line option '%s'", args[0])
   415  		}
   416  
   417  		args = args[consumedArgs:]
   418  	}
   419  
   420  	if actionKind != actionPrint && len(goInputs) == 0 && !hasOtherNonFlagInputs {
   421  		return opts, errors.New("no input files")
   422  	}
   423  
   424  	if !noPrefix {
   425  		opts.prefix, err = getInstPrefix()
   426  		if err != nil {
   427  			return opts, err
   428  		}
   429  	}
   430  
   431  	if opts.sanitizer.crtPrefix == "" {
   432  		opts.sanitizer.crtPrefix = opts.prefix
   433  	}
   434  
   435  	if opts.sanitizer.isPIEDefault() {
   436  		// This should really only be turning on -fPIE, but this isn't
   437  		// easy to do from Go, and -fPIC is a superset of it anyway.
   438  		opts.pic = true
   439  		opts.pieLink = true
   440  	}
   441  
   442  	switch actionKind {
   443  	case actionLink:
   444  		if len(goInputs) != 0 {
   445  			opts.actions = []action{action{actionCompile, goInputs}}
   446  		}
   447  		opts.actions = append(opts.actions, action{actionLink, otherInputs})
   448  
   449  	case actionCompile, actionAssemble:
   450  		if len(goInputs) != 0 {
   451  			opts.actions = []action{action{actionKind, goInputs}}
   452  		}
   453  
   454  	case actionPrint:
   455  		opts.actions = []action{action{actionKind, nil}}
   456  	}
   457  
   458  	if opts.output == "" && len(opts.actions) != 0 {
   459  		switch actionKind {
   460  		case actionCompile, actionAssemble:
   461  			base := filepath.Base(goInputs[0])
   462  			base = base[0 : len(base)-3]
   463  			if actionKind == actionCompile {
   464  				opts.output = base + ".o"
   465  			} else {
   466  				opts.output = base + ".s"
   467  			}
   468  
   469  		case actionLink:
   470  			opts.output = "a.out"
   471  		}
   472  	}
   473  
   474  	return opts, nil
   475  }
   476  
   477  func runPasses(opts *driverOptions, tm llvm.TargetMachine, m llvm.Module) {
   478  	fpm := llvm.NewFunctionPassManagerForModule(m)
   479  	defer fpm.Dispose()
   480  
   481  	mpm := llvm.NewPassManager()
   482  	defer mpm.Dispose()
   483  
   484  	pmb := llvm.NewPassManagerBuilder()
   485  	defer pmb.Dispose()
   486  
   487  	pmb.SetOptLevel(opts.optLevel)
   488  	pmb.SetSizeLevel(opts.sizeLevel)
   489  
   490  	target := tm.TargetData()
   491  	mpm.Add(target)
   492  	fpm.Add(target)
   493  	tm.AddAnalysisPasses(mpm)
   494  	tm.AddAnalysisPasses(fpm)
   495  
   496  	mpm.AddVerifierPass()
   497  	fpm.AddVerifierPass()
   498  
   499  	pmb.Populate(mpm)
   500  	pmb.PopulateFunc(fpm)
   501  
   502  	if opts.optLevel == 0 {
   503  		// Remove references (via the descriptor) to dead functions,
   504  		// for compatibility with other compilers.
   505  		mpm.AddGlobalDCEPass()
   506  	}
   507  
   508  	opts.sanitizer.addPasses(mpm, fpm)
   509  
   510  	fpm.InitializeFunc()
   511  	for fn := m.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
   512  		fpm.RunFunc(fn)
   513  	}
   514  	fpm.FinalizeFunc()
   515  
   516  	mpm.Run(m)
   517  }
   518  
   519  func getMetadataSectionInlineAsm(name string) string {
   520  	// ELF: creates a non-allocated excluded section.
   521  	return ".section \"" + name + "\", \"e\"\n"
   522  }
   523  
   524  func getDataInlineAsm(data []byte) string {
   525  	edata := make([]byte, 0, len(data)*4+10)
   526  
   527  	edata = append(edata, ".ascii \""...)
   528  	for i := range data {
   529  		switch data[i] {
   530  		case '\000':
   531  			edata = append(edata, "\\000"...)
   532  			continue
   533  		case '\n':
   534  			edata = append(edata, "\\n"...)
   535  			continue
   536  		case '"', '\\':
   537  			edata = append(edata, '\\')
   538  		}
   539  		edata = append(edata, data[i])
   540  	}
   541  	edata = append(edata, "\"\n"...)
   542  	return string(edata)
   543  }
   544  
   545  // Get the lib path to the standard libraries for the given driver options.
   546  // This is normally 'lib' but can vary for cross compilation, LTO, sanitizers
   547  // etc.
   548  func getLibDir(opts *driverOptions) string {
   549  	lib := "lib" + LibDirSuffix
   550  	switch {
   551  	case opts.lto:
   552  		return filepath.Join(lib, "llvm-lto.0")
   553  	case opts.sanitizer.address:
   554  		return filepath.Join(lib, "llvm-asan.0")
   555  	case opts.sanitizer.thread:
   556  		return filepath.Join(lib, "llvm-tsan.0")
   557  	case opts.sanitizer.memory:
   558  		return filepath.Join(lib, "llvm-msan.0")
   559  	case opts.sanitizer.dataflow:
   560  		return filepath.Join(lib, "llvm-dfsan.0")
   561  	default:
   562  		return lib
   563  	}
   564  }
   565  
   566  func performAction(opts *driverOptions, kind actionKind, inputs []string, output string) error {
   567  	switch kind {
   568  	case actionPrint:
   569  		switch opts.output {
   570  		case "-dumpversion":
   571  			fmt.Println("llgo-" + llvmVersion())
   572  			return nil
   573  		case "-print-libgcc-file-name":
   574  			cmd := exec.Command(opts.bprefix+"gcc", "-print-libgcc-file-name")
   575  			out, err := cmd.CombinedOutput()
   576  			os.Stdout.Write(out)
   577  			return err
   578  		case "-print-multi-os-directory":
   579  			fmt.Println(filepath.Join("..", getLibDir(opts)))
   580  			return nil
   581  		case "--version":
   582  			displayVersion()
   583  			return nil
   584  		default:
   585  			panic("unexpected print command")
   586  		}
   587  
   588  	case actionCompile, actionAssemble:
   589  		compiler, err := initCompiler(opts)
   590  		if err != nil {
   591  			return err
   592  		}
   593  
   594  		fset := token.NewFileSet()
   595  		files, err := driver.ParseFiles(fset, inputs)
   596  		if err != nil {
   597  			return err
   598  		}
   599  
   600  		module, err := compiler.Compile(fset, files, opts.pkgpath)
   601  		if err != nil {
   602  			return err
   603  		}
   604  
   605  		defer module.Dispose()
   606  
   607  		target, err := llvm.GetTargetFromTriple(opts.triple)
   608  		if err != nil {
   609  			return err
   610  		}
   611  
   612  		optLevel := [...]llvm.CodeGenOptLevel{
   613  			llvm.CodeGenLevelNone,
   614  			llvm.CodeGenLevelLess,
   615  			llvm.CodeGenLevelDefault,
   616  			llvm.CodeGenLevelAggressive,
   617  		}[opts.optLevel]
   618  
   619  		relocMode := llvm.RelocStatic
   620  		if opts.pic {
   621  			relocMode = llvm.RelocPIC
   622  		}
   623  
   624  		tm := target.CreateTargetMachine(opts.triple, "", "", optLevel,
   625  			relocMode, llvm.CodeModelDefault)
   626  		defer tm.Dispose()
   627  
   628  		runPasses(opts, tm, module.Module)
   629  
   630  		var file *os.File
   631  		if output == "-" {
   632  			file = os.Stdout
   633  		} else {
   634  			file, err = os.Create(output)
   635  			if err != nil {
   636  				return err
   637  			}
   638  			defer file.Close()
   639  		}
   640  
   641  		switch {
   642  		case !opts.lto && !opts.emitIR:
   643  			if module.ExportData != nil {
   644  				asm := getMetadataSectionInlineAsm(".go_export")
   645  				asm += getDataInlineAsm(module.ExportData)
   646  				module.Module.SetInlineAsm(asm)
   647  			}
   648  
   649  			fileType := llvm.AssemblyFile
   650  			if kind == actionCompile {
   651  				fileType = llvm.ObjectFile
   652  			}
   653  			mb, err := tm.EmitToMemoryBuffer(module.Module, fileType)
   654  			if err != nil {
   655  				return err
   656  			}
   657  			defer mb.Dispose()
   658  
   659  			bytes := mb.Bytes()
   660  			_, err = file.Write(bytes)
   661  			return err
   662  
   663  		case opts.lto:
   664  			bcmb := llvm.WriteBitcodeToMemoryBuffer(module.Module)
   665  			defer bcmb.Dispose()
   666  
   667  			// This is a bit of a hack. We just want an object file
   668  			// containing some metadata sections. This might be simpler
   669  			// if we had bindings for the MC library, but for now we create
   670  			// a fresh module containing only inline asm that creates the
   671  			// sections.
   672  			outmodule := llvm.NewModule("")
   673  			defer outmodule.Dispose()
   674  			asm := getMetadataSectionInlineAsm(".llvmbc")
   675  			asm += getDataInlineAsm(bcmb.Bytes())
   676  			if module.ExportData != nil {
   677  				asm += getMetadataSectionInlineAsm(".go_export")
   678  				asm += getDataInlineAsm(module.ExportData)
   679  			}
   680  			outmodule.SetInlineAsm(asm)
   681  
   682  			fileType := llvm.AssemblyFile
   683  			if kind == actionCompile {
   684  				fileType = llvm.ObjectFile
   685  			}
   686  			mb, err := tm.EmitToMemoryBuffer(outmodule, fileType)
   687  			if err != nil {
   688  				return err
   689  			}
   690  			defer mb.Dispose()
   691  
   692  			bytes := mb.Bytes()
   693  			_, err = file.Write(bytes)
   694  			return err
   695  
   696  		case kind == actionCompile:
   697  			err := llvm.WriteBitcodeToFile(module.Module, file)
   698  			return err
   699  
   700  		case kind == actionAssemble:
   701  			_, err := file.WriteString(module.Module.String())
   702  			return err
   703  
   704  		default:
   705  			panic("unexpected action kind")
   706  		}
   707  
   708  	case actionLink:
   709  		// TODO(pcc): Teach this to do LTO.
   710  		args := []string{"-o", output}
   711  		if opts.pic {
   712  			args = append(args, "-fPIC")
   713  		}
   714  		if opts.pieLink {
   715  			args = append(args, "-pie")
   716  		}
   717  		if opts.staticLink {
   718  			args = append(args, "-static")
   719  		}
   720  		if opts.staticLibgcc {
   721  			args = append(args, "-static-libgcc")
   722  		}
   723  		for _, p := range opts.libPaths {
   724  			args = append(args, "-L", p)
   725  		}
   726  		for _, p := range opts.importPaths {
   727  			args = append(args, "-I", p)
   728  		}
   729  		args = append(args, inputs...)
   730  		var linkerPath string
   731  		if opts.gccgoPath == "" {
   732  			// TODO(pcc): See if we can avoid calling gcc here.
   733  			// We currently rely on it to find crt*.o and compile
   734  			// any C source files passed as arguments.
   735  			linkerPath = opts.bprefix + "gcc"
   736  
   737  			if opts.prefix != "" {
   738  				libdir := filepath.Join(opts.prefix, getLibDir(opts))
   739  				args = append(args, "-L", libdir)
   740  				if !opts.staticLibgo {
   741  					args = append(args, "-Wl,-rpath,"+libdir)
   742  				}
   743  			}
   744  
   745  			args = append(args, "-lgobegin-llgo")
   746  			if opts.staticLibgo {
   747  				args = append(args, "-Wl,-Bstatic", "-lgo-llgo", "-Wl,-Bdynamic", "-lpthread", "-lm")
   748  			} else {
   749  				args = append(args, "-lgo-llgo")
   750  			}
   751  		} else {
   752  			linkerPath = opts.gccgoPath
   753  			if opts.staticLibgo {
   754  				args = append(args, "-static-libgo")
   755  			}
   756  		}
   757  
   758  		args = opts.sanitizer.addLibs(opts.triple, args)
   759  
   760  		cmd := exec.Command(linkerPath, args...)
   761  		out, err := cmd.CombinedOutput()
   762  		if err != nil {
   763  			os.Stderr.Write(out)
   764  		}
   765  		return err
   766  
   767  	default:
   768  		panic("unexpected action kind")
   769  	}
   770  }
   771  
   772  func performActions(opts *driverOptions) error {
   773  	var extraInput string
   774  
   775  	for _, plugin := range opts.plugins {
   776  		err := llvm.LoadLibraryPermanently(plugin)
   777  		if err != nil {
   778  			return err
   779  		}
   780  	}
   781  
   782  	llvm.ParseCommandLineOptions(append([]string{"llgo"}, opts.llvmArgs...), "llgo (LLVM option parsing)\n")
   783  
   784  	for i, action := range opts.actions {
   785  		var output string
   786  		if i == len(opts.actions)-1 {
   787  			output = opts.output
   788  		} else {
   789  			tmpfile, err := ioutil.TempFile("", "llgo")
   790  			if err != nil {
   791  				return err
   792  			}
   793  			output = tmpfile.Name() + ".o"
   794  			tmpfile.Close()
   795  			err = os.Remove(tmpfile.Name())
   796  			if err != nil {
   797  				return err
   798  			}
   799  			defer os.Remove(output)
   800  		}
   801  
   802  		inputs := action.inputs
   803  		if extraInput != "" {
   804  			inputs = append([]string{extraInput}, inputs...)
   805  		}
   806  
   807  		err := performAction(opts, action.kind, inputs, output)
   808  		if err != nil {
   809  			return err
   810  		}
   811  
   812  		extraInput = output
   813  	}
   814  
   815  	return nil
   816  }
   817  
   818  func main() {
   819  	llvm.InitializeAllTargets()
   820  	llvm.InitializeAllTargetMCs()
   821  	llvm.InitializeAllTargetInfos()
   822  	llvm.InitializeAllAsmParsers()
   823  	llvm.InitializeAllAsmPrinters()
   824  
   825  	opts, err := parseArguments(os.Args[1:])
   826  	if err != nil {
   827  		report(err)
   828  		os.Exit(1)
   829  	}
   830  
   831  	err = performActions(&opts)
   832  	if err != nil {
   833  		report(err)
   834  		os.Exit(1)
   835  	}
   836  }