blob: 11038a33749eb9f360766670769b0bcbb04893d0 [file] [log] [blame]
Peter Collingbournead9841e2014-11-27 00:06:42 +00001//===- 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
15package main
16
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +000017/*
18#include "config.h"
19*/
20import "C"
21
Peter Collingbournead9841e2014-11-27 00:06:42 +000022import (
23 "errors"
24 "fmt"
25 "go/scanner"
Peter Collingbourne93509422014-12-31 00:25:32 +000026 "go/token"
Peter Collingbournead9841e2014-11-27 00:06:42 +000027 "io/ioutil"
28 "log"
29 "os"
30 "os/exec"
31 "path/filepath"
32 "strings"
33
34 "llvm.org/llgo/debug"
Peter Collingbourne93509422014-12-31 00:25:32 +000035 "llvm.org/llgo/driver"
Peter Collingbournead9841e2014-11-27 00:06:42 +000036 "llvm.org/llgo/irgen"
37 "llvm.org/llvm/bindings/go/llvm"
38)
39
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +000040const LibDirSuffix = C.LLVM_LIBDIR_SUFFIX
41
Peter Collingbournead9841e2014-11-27 00:06:42 +000042func 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
52func llvmVersion() string {
53 return strings.Replace(llvm.Version, "svn", "", 1)
54}
55
56func displayVersion() {
57 fmt.Printf("llgo version %s (%s)\n\n", llvmVersion(), irgen.GoVersion())
58 os.Exit(0)
59}
60
61func 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 != "" {
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +000066 importPaths = append(importPaths, filepath.Join(opts.prefix, "lib"+LibDirSuffix, "go", "llgo-"+llvmVersion()))
Peter Collingbournead9841e2014-11-27 00:06:42 +000067 }
68 copts := irgen.CompilerOptions{
69 TargetTriple: opts.triple,
70 GenerateDebug: opts.generateDebug,
71 DebugPrefixMaps: opts.debugPrefixMaps,
72 DumpSSA: opts.dumpSSA,
73 GccgoPath: opts.gccgoPath,
Peter Collingbourned34d92f2014-12-31 00:25:39 +000074 GccgoABI: opts.gccgoPath != "",
Peter Collingbournead9841e2014-11-27 00:06:42 +000075 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
84type actionKind int
85
86const (
87 actionAssemble = actionKind(iota)
88 actionCompile
89 actionLink
90 actionPrint
91)
92
93type action struct {
94 kind actionKind
95 inputs []string
96}
97
98type sanitizerOptions struct {
99 blacklist string
100 crtPrefix string
101
102 address, thread, memory, dataflow bool
103}
104
105func (san *sanitizerOptions) resourcePath() string {
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000106 return filepath.Join(san.crtPrefix, "lib"+LibDirSuffix, "clang", llvmVersion())
Peter Collingbournead9841e2014-11-27 00:06:42 +0000107}
108
109func (san *sanitizerOptions) isPIEDefault() bool {
110 return san.thread || san.memory || san.dataflow
111}
112
113func (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 }
Peter Collingbournec969b1b2015-02-13 22:58:36 +0000127 mpm.AddDataFlowSanitizerPass([]string{blacklist})
Peter Collingbournead9841e2014-11-27 00:06:42 +0000128 }
129}
130
131func (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
136func (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
141func (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
156func (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
169type 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
198func 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
213func 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
Andrew Wilkins7a94f982015-07-17 08:15:01 +0000327 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
Peter Collingbournead9841e2014-11-27 00:06:42 +0000335 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
477func 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
519func getMetadataSectionInlineAsm(name string) string {
520 // ELF: creates a non-allocated excluded section.
521 return ".section \"" + name + "\", \"e\"\n"
522}
523
524func 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
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000545// 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.
548func getLibDir(opts *driverOptions) string {
549 lib := "lib" + LibDirSuffix
Peter Collingbournead9841e2014-11-27 00:06:42 +0000550 switch {
551 case opts.lto:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000552 return filepath.Join(lib, "llvm-lto.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000553 case opts.sanitizer.address:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000554 return filepath.Join(lib, "llvm-asan.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000555 case opts.sanitizer.thread:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000556 return filepath.Join(lib, "llvm-tsan.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000557 case opts.sanitizer.memory:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000558 return filepath.Join(lib, "llvm-msan.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000559 case opts.sanitizer.dataflow:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000560 return filepath.Join(lib, "llvm-dfsan.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000561 default:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000562 return lib
Peter Collingbournead9841e2014-11-27 00:06:42 +0000563 }
564}
565
566func performAction(opts *driverOptions, kind actionKind, inputs []string, output string) error {
567 switch kind {
568 case actionPrint:
569 switch opts.output {
570 case "-dumpversion":
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000571 fmt.Println("llgo-" + llvmVersion())
Peter Collingbournead9841e2014-11-27 00:06:42 +0000572 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":
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000579 fmt.Println(filepath.Join("..", getLibDir(opts)))
Peter Collingbournead9841e2014-11-27 00:06:42 +0000580 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
Peter Collingbourne93509422014-12-31 00:25:32 +0000594 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)
Peter Collingbournead9841e2014-11-27 00:06:42 +0000601 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 != "" {
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000738 libdir := filepath.Join(opts.prefix, getLibDir(opts))
Peter Collingbournead9841e2014-11-27 00:06:42 +0000739 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
772func 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
818func 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}