834 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			834 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)
 | 
						|
 | 
						|
	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", "-lm")
 | 
						|
			}
 | 
						|
		} 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)
 | 
						|
	}
 | 
						|
}
 |