forked from OSchip/llvm-project
838 lines
20 KiB
Go
838 lines
20 KiB
Go
//===- gllgo.go - gccgo-like driver for llgo ------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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.AddMemorySanitizerLegacyPassPass()
|
|
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 {
|
|
var attrKind uint
|
|
|
|
switch {
|
|
case san.address:
|
|
attrKind = llvm.AttributeKindID("sanitize_address")
|
|
case san.thread:
|
|
attrKind = llvm.AttributeKindID("sanitize_thread")
|
|
case san.memory:
|
|
attrKind = llvm.AttributeKindID("sanitize_memory")
|
|
default:
|
|
attrKind = 0
|
|
}
|
|
|
|
ctx := llvm.GlobalContext()
|
|
return ctx.CreateEnumAttribute(attrKind, 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)
|
|
}
|
|
}
|