github.com/axw/llgo@v0.0.0-20160805011314-95b5fe4dca20/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  	tm.AddAnalysisPasses(mpm)
   491  	tm.AddAnalysisPasses(fpm)
   492  
   493  	mpm.AddVerifierPass()
   494  	fpm.AddVerifierPass()
   495  
   496  	pmb.Populate(mpm)
   497  	pmb.PopulateFunc(fpm)
   498  
   499  	if opts.optLevel == 0 {
   500  		// Remove references (via the descriptor) to dead functions,
   501  		// for compatibility with other compilers.
   502  		mpm.AddGlobalDCEPass()
   503  	}
   504  
   505  	opts.sanitizer.addPasses(mpm, fpm)
   506  
   507  	fpm.InitializeFunc()
   508  	for fn := m.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
   509  		fpm.RunFunc(fn)
   510  	}
   511  	fpm.FinalizeFunc()
   512  
   513  	mpm.Run(m)
   514  }
   515  
   516  func getMetadataSectionInlineAsm(name string) string {
   517  	// ELF: creates a non-allocated excluded section.
   518  	return ".section \"" + name + "\", \"e\"\n"
   519  }
   520  
   521  func getDataInlineAsm(data []byte) string {
   522  	edata := make([]byte, 0, len(data)*4+10)
   523  
   524  	edata = append(edata, ".ascii \""...)
   525  	for i := range data {
   526  		switch data[i] {
   527  		case '\000':
   528  			edata = append(edata, "\\000"...)
   529  			continue
   530  		case '\n':
   531  			edata = append(edata, "\\n"...)
   532  			continue
   533  		case '"', '\\':
   534  			edata = append(edata, '\\')
   535  		}
   536  		edata = append(edata, data[i])
   537  	}
   538  	edata = append(edata, "\"\n"...)
   539  	return string(edata)
   540  }
   541  
   542  // Get the lib path to the standard libraries for the given driver options.
   543  // This is normally 'lib' but can vary for cross compilation, LTO, sanitizers
   544  // etc.
   545  func getLibDir(opts *driverOptions) string {
   546  	lib := "lib" + LibDirSuffix
   547  	switch {
   548  	case opts.lto:
   549  		return filepath.Join(lib, "llvm-lto.0")
   550  	case opts.sanitizer.address:
   551  		return filepath.Join(lib, "llvm-asan.0")
   552  	case opts.sanitizer.thread:
   553  		return filepath.Join(lib, "llvm-tsan.0")
   554  	case opts.sanitizer.memory:
   555  		return filepath.Join(lib, "llvm-msan.0")
   556  	case opts.sanitizer.dataflow:
   557  		return filepath.Join(lib, "llvm-dfsan.0")
   558  	default:
   559  		return lib
   560  	}
   561  }
   562  
   563  func performAction(opts *driverOptions, kind actionKind, inputs []string, output string) error {
   564  	switch kind {
   565  	case actionPrint:
   566  		switch opts.output {
   567  		case "-dumpversion":
   568  			fmt.Println("llgo-" + llvmVersion())
   569  			return nil
   570  		case "-print-libgcc-file-name":
   571  			cmd := exec.Command(opts.bprefix+"gcc", "-print-libgcc-file-name")
   572  			out, err := cmd.CombinedOutput()
   573  			os.Stdout.Write(out)
   574  			return err
   575  		case "-print-multi-os-directory":
   576  			fmt.Println(filepath.Join("..", getLibDir(opts)))
   577  			return nil
   578  		case "--version":
   579  			displayVersion()
   580  			return nil
   581  		default:
   582  			panic("unexpected print command")
   583  		}
   584  
   585  	case actionCompile, actionAssemble:
   586  		compiler, err := initCompiler(opts)
   587  		if err != nil {
   588  			return err
   589  		}
   590  
   591  		fset := token.NewFileSet()
   592  		files, err := driver.ParseFiles(fset, inputs)
   593  		if err != nil {
   594  			return err
   595  		}
   596  
   597  		module, err := compiler.Compile(fset, files, opts.pkgpath)
   598  		if err != nil {
   599  			return err
   600  		}
   601  
   602  		defer module.Dispose()
   603  
   604  		target, err := llvm.GetTargetFromTriple(opts.triple)
   605  		if err != nil {
   606  			return err
   607  		}
   608  
   609  		optLevel := [...]llvm.CodeGenOptLevel{
   610  			llvm.CodeGenLevelNone,
   611  			llvm.CodeGenLevelLess,
   612  			llvm.CodeGenLevelDefault,
   613  			llvm.CodeGenLevelAggressive,
   614  		}[opts.optLevel]
   615  
   616  		relocMode := llvm.RelocStatic
   617  		if opts.pic {
   618  			relocMode = llvm.RelocPIC
   619  		}
   620  
   621  		tm := target.CreateTargetMachine(opts.triple, "", "", optLevel,
   622  			relocMode, llvm.CodeModelDefault)
   623  		defer tm.Dispose()
   624  
   625  		runPasses(opts, tm, module.Module)
   626  
   627  		var file *os.File
   628  		if output == "-" {
   629  			file = os.Stdout
   630  		} else {
   631  			file, err = os.Create(output)
   632  			if err != nil {
   633  				return err
   634  			}
   635  			defer file.Close()
   636  		}
   637  
   638  		switch {
   639  		case !opts.lto && !opts.emitIR:
   640  			if module.ExportData != nil {
   641  				asm := getMetadataSectionInlineAsm(".go_export")
   642  				asm += getDataInlineAsm(module.ExportData)
   643  				module.Module.SetInlineAsm(asm)
   644  			}
   645  
   646  			fileType := llvm.AssemblyFile
   647  			if kind == actionCompile {
   648  				fileType = llvm.ObjectFile
   649  			}
   650  			mb, err := tm.EmitToMemoryBuffer(module.Module, fileType)
   651  			if err != nil {
   652  				return err
   653  			}
   654  			defer mb.Dispose()
   655  
   656  			bytes := mb.Bytes()
   657  			_, err = file.Write(bytes)
   658  			return err
   659  
   660  		case opts.lto:
   661  			bcmb := llvm.WriteBitcodeToMemoryBuffer(module.Module)
   662  			defer bcmb.Dispose()
   663  
   664  			// This is a bit of a hack. We just want an object file
   665  			// containing some metadata sections. This might be simpler
   666  			// if we had bindings for the MC library, but for now we create
   667  			// a fresh module containing only inline asm that creates the
   668  			// sections.
   669  			outmodule := llvm.NewModule("")
   670  			defer outmodule.Dispose()
   671  			asm := getMetadataSectionInlineAsm(".llvmbc")
   672  			asm += getDataInlineAsm(bcmb.Bytes())
   673  			if module.ExportData != nil {
   674  				asm += getMetadataSectionInlineAsm(".go_export")
   675  				asm += getDataInlineAsm(module.ExportData)
   676  			}
   677  			outmodule.SetInlineAsm(asm)
   678  
   679  			fileType := llvm.AssemblyFile
   680  			if kind == actionCompile {
   681  				fileType = llvm.ObjectFile
   682  			}
   683  			mb, err := tm.EmitToMemoryBuffer(outmodule, fileType)
   684  			if err != nil {
   685  				return err
   686  			}
   687  			defer mb.Dispose()
   688  
   689  			bytes := mb.Bytes()
   690  			_, err = file.Write(bytes)
   691  			return err
   692  
   693  		case kind == actionCompile:
   694  			err := llvm.WriteBitcodeToFile(module.Module, file)
   695  			return err
   696  
   697  		case kind == actionAssemble:
   698  			_, err := file.WriteString(module.Module.String())
   699  			return err
   700  
   701  		default:
   702  			panic("unexpected action kind")
   703  		}
   704  
   705  	case actionLink:
   706  		// TODO(pcc): Teach this to do LTO.
   707  		args := []string{"-o", output}
   708  		if opts.pic {
   709  			args = append(args, "-fPIC")
   710  		}
   711  		if opts.pieLink {
   712  			args = append(args, "-pie")
   713  		}
   714  		if opts.staticLink {
   715  			args = append(args, "-static")
   716  		}
   717  		if opts.staticLibgcc {
   718  			args = append(args, "-static-libgcc")
   719  		}
   720  		for _, p := range opts.libPaths {
   721  			args = append(args, "-L", p)
   722  		}
   723  		for _, p := range opts.importPaths {
   724  			args = append(args, "-I", p)
   725  		}
   726  		args = append(args, inputs...)
   727  		var linkerPath string
   728  		if opts.gccgoPath == "" {
   729  			// TODO(pcc): See if we can avoid calling gcc here.
   730  			// We currently rely on it to find crt*.o and compile
   731  			// any C source files passed as arguments.
   732  			linkerPath = opts.bprefix + "gcc"
   733  
   734  			if opts.prefix != "" {
   735  				libdir := filepath.Join(opts.prefix, getLibDir(opts))
   736  				args = append(args, "-L", libdir)
   737  				if !opts.staticLibgo {
   738  					args = append(args, "-Wl,-rpath,"+libdir)
   739  				}
   740  			}
   741  
   742  			args = append(args, "-lgobegin-llgo")
   743  			if opts.staticLibgo {
   744  				args = append(args, "-Wl,-Bstatic", "-lgo-llgo", "-Wl,-Bdynamic", "-lpthread", "-lm")
   745  			} else {
   746  				args = append(args, "-lgo-llgo", "-lm")
   747  			}
   748  		} else {
   749  			linkerPath = opts.gccgoPath
   750  			if opts.staticLibgo {
   751  				args = append(args, "-static-libgo")
   752  			}
   753  		}
   754  
   755  		args = opts.sanitizer.addLibs(opts.triple, args)
   756  
   757  		cmd := exec.Command(linkerPath, args...)
   758  		out, err := cmd.CombinedOutput()
   759  		if err != nil {
   760  			os.Stderr.Write(out)
   761  		}
   762  		return err
   763  
   764  	default:
   765  		panic("unexpected action kind")
   766  	}
   767  }
   768  
   769  func performActions(opts *driverOptions) error {
   770  	var extraInput string
   771  
   772  	for _, plugin := range opts.plugins {
   773  		err := llvm.LoadLibraryPermanently(plugin)
   774  		if err != nil {
   775  			return err
   776  		}
   777  	}
   778  
   779  	llvm.ParseCommandLineOptions(append([]string{"llgo"}, opts.llvmArgs...), "llgo (LLVM option parsing)\n")
   780  
   781  	for i, action := range opts.actions {
   782  		var output string
   783  		if i == len(opts.actions)-1 {
   784  			output = opts.output
   785  		} else {
   786  			tmpfile, err := ioutil.TempFile("", "llgo")
   787  			if err != nil {
   788  				return err
   789  			}
   790  			output = tmpfile.Name() + ".o"
   791  			tmpfile.Close()
   792  			err = os.Remove(tmpfile.Name())
   793  			if err != nil {
   794  				return err
   795  			}
   796  			defer os.Remove(output)
   797  		}
   798  
   799  		inputs := action.inputs
   800  		if extraInput != "" {
   801  			inputs = append([]string{extraInput}, inputs...)
   802  		}
   803  
   804  		err := performAction(opts, action.kind, inputs, output)
   805  		if err != nil {
   806  			return err
   807  		}
   808  
   809  		extraInput = output
   810  	}
   811  
   812  	return nil
   813  }
   814  
   815  func main() {
   816  	llvm.InitializeAllTargets()
   817  	llvm.InitializeAllTargetMCs()
   818  	llvm.InitializeAllTargetInfos()
   819  	llvm.InitializeAllAsmParsers()
   820  	llvm.InitializeAllAsmPrinters()
   821  
   822  	opts, err := parseArguments(os.Args[1:])
   823  	if err != nil {
   824  		report(err)
   825  		os.Exit(1)
   826  	}
   827  
   828  	err = performActions(&opts)
   829  	if err != nil {
   830  		report(err)
   831  		os.Exit(1)
   832  	}
   833  }