blob: de496fffe7cb346969c6ebf18edb0d5b56e7c9a4 [file] [log] [blame]
Peter Collingbournead9841e2014-11-27 00:06:42 +00001//===- compiler.go - IR generator entry point -----------------------------===//
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 file implements the main IR generator entry point, (*Compiler).Compile.
11//
12//===----------------------------------------------------------------------===//
13
14package irgen
15
16import (
17 "bytes"
18 "fmt"
Peter Collingbourne93509422014-12-31 00:25:32 +000019 "go/ast"
Peter Collingbournead9841e2014-11-27 00:06:42 +000020 "go/token"
21 "log"
22 "sort"
23 "strconv"
24 "strings"
25
26 llgobuild "llvm.org/llgo/build"
27 "llvm.org/llgo/debug"
28 "llvm.org/llvm/bindings/go/llvm"
29
30 "llvm.org/llgo/third_party/go.tools/go/gccgoimporter"
31 "llvm.org/llgo/third_party/go.tools/go/importer"
32 "llvm.org/llgo/third_party/go.tools/go/loader"
33 "llvm.org/llgo/third_party/go.tools/go/ssa"
34 "llvm.org/llgo/third_party/go.tools/go/types"
35)
36
37type Module struct {
38 llvm.Module
39 Path string
40 ExportData []byte
41 disposed bool
42}
43
44func (m *Module) Dispose() {
45 if m.disposed {
46 return
47 }
48 m.Module.Dispose()
49 m.disposed = true
50}
51
52///////////////////////////////////////////////////////////////////////////////
53
54type CompilerOptions struct {
55 // TargetTriple is the LLVM triple for the target.
56 TargetTriple string
57
58 // GenerateDebug decides whether debug data is
59 // generated in the output module.
60 GenerateDebug bool
61
62 // DebugPrefixMaps is a list of mappings from source prefixes to
63 // replacement prefixes, to be applied in debug info.
64 DebugPrefixMaps []debug.PrefixMap
65
66 // Logger is a logger used for tracing compilation.
67 Logger *log.Logger
68
69 // DumpSSA is a debugging option that dumps each SSA function
70 // to stderr before generating code for it.
71 DumpSSA bool
72
73 // GccgoPath is the path to the gccgo binary whose libgo we read import
74 // data from. If blank, the caller is expected to supply an import
75 // path in ImportPaths.
76 GccgoPath string
77
78 // ImportPaths is the list of additional import paths
79 ImportPaths []string
80
81 // SanitizerAttribute is an attribute to apply to functions to enable
82 // dynamic instrumentation using a sanitizer.
83 SanitizerAttribute llvm.Attribute
84}
85
86type Compiler struct {
87 opts CompilerOptions
88 dataLayout string
89 pnacl bool
90}
91
92func NewCompiler(opts CompilerOptions) (*Compiler, error) {
93 compiler := &Compiler{opts: opts}
94 if strings.ToLower(compiler.opts.TargetTriple) == "pnacl" {
95 compiler.opts.TargetTriple = PNaClTriple
96 compiler.pnacl = true
97 }
98 dataLayout, err := llvmDataLayout(compiler.opts.TargetTriple)
99 if err != nil {
100 return nil, err
101 }
102 compiler.dataLayout = dataLayout
103 return compiler, nil
104}
105
Peter Collingbourne93509422014-12-31 00:25:32 +0000106func (c *Compiler) Compile(fset *token.FileSet, astFiles []*ast.File, importpath string) (m *Module, err error) {
Peter Collingbournead9841e2014-11-27 00:06:42 +0000107 target := llvm.NewTargetData(c.dataLayout)
108 compiler := &compiler{
109 CompilerOptions: c.opts,
110 dataLayout: c.dataLayout,
111 target: target,
112 pnacl: c.pnacl,
113 llvmtypes: NewLLVMTypeMap(llvm.GlobalContext(), target),
114 }
Peter Collingbourne93509422014-12-31 00:25:32 +0000115 return compiler.compile(fset, astFiles, importpath)
Peter Collingbournead9841e2014-11-27 00:06:42 +0000116}
117
118type compiler struct {
119 CompilerOptions
120
121 module *Module
122 dataLayout string
123 target llvm.TargetData
124 fileset *token.FileSet
125
126 runtime *runtimeInterface
127 llvmtypes *llvmTypeMap
128 types *TypeMap
129
Peter Collingbournead9841e2014-11-27 00:06:42 +0000130 // pnacl is set to true if the target triple was originally
131 // specified as "pnacl". This is necessary, as the TargetTriple
132 // field will have been updated to the true triple used to
133 // compile PNaCl modules.
134 pnacl bool
135
136 debug *debug.DIBuilder
137}
138
139func (c *compiler) logf(format string, v ...interface{}) {
140 if c.Logger != nil {
141 c.Logger.Printf(format, v...)
142 }
143}
144
145func (c *compiler) addCommonFunctionAttrs(fn llvm.Value) {
146 fn.AddTargetDependentFunctionAttr("disable-tail-calls", "true")
147 fn.AddTargetDependentFunctionAttr("split-stack", "")
148 if attr := c.SanitizerAttribute; attr != 0 {
149 fn.AddFunctionAttr(attr)
150 }
151}
152
Peter Collingbourne93509422014-12-31 00:25:32 +0000153func (compiler *compiler) compile(fset *token.FileSet, astFiles []*ast.File, importpath string) (m *Module, err error) {
Peter Collingbournead9841e2014-11-27 00:06:42 +0000154 buildctx, err := llgobuild.ContextFromTriple(compiler.TargetTriple)
155 if err != nil {
156 return nil, err
157 }
158
159 initmap := make(map[*types.Package]gccgoimporter.InitData)
160 var importer types.Importer
161 if compiler.GccgoPath == "" {
162 paths := append(append([]string{}, compiler.ImportPaths...), ".")
163 importer = gccgoimporter.GetImporter(paths, initmap)
164 } else {
165 var inst gccgoimporter.GccgoInstallation
166 err = inst.InitFromDriver(compiler.GccgoPath)
167 if err != nil {
168 return nil, err
169 }
170 importer = inst.GetImporter(compiler.ImportPaths, initmap)
171 }
172
173 impcfg := &loader.Config{
Peter Collingbourne93509422014-12-31 00:25:32 +0000174 Fset: fset,
Peter Collingbournead9841e2014-11-27 00:06:42 +0000175 TypeChecker: types.Config{
176 Import: importer,
177 Sizes: compiler.llvmtypes,
178 },
179 Build: &buildctx.Context,
180 }
Peter Collingbournead9841e2014-11-27 00:06:42 +0000181 // If no import path is specified, then set the import
182 // path to be the same as the package's name.
183 if importpath == "" {
184 importpath = astFiles[0].Name.String()
185 }
186 impcfg.CreateFromFiles(importpath, astFiles...)
187 iprog, err := impcfg.Load()
188 if err != nil {
189 return nil, err
190 }
191 program := ssa.Create(iprog, ssa.BareInits)
192 mainPkginfo := iprog.InitialPackages()[0]
193 mainPkg := program.CreatePackage(mainPkginfo)
194
195 // Create a Module, which contains the LLVM module.
196 modulename := importpath
197 compiler.module = &Module{Module: llvm.NewModule(modulename), Path: modulename}
198 compiler.module.SetTarget(compiler.TargetTriple)
199 compiler.module.SetDataLayout(compiler.dataLayout)
200
201 // Create a new translation unit.
202 unit := newUnit(compiler, mainPkg)
203
204 // Create the runtime interface.
205 compiler.runtime, err = newRuntimeInterface(compiler.module.Module, compiler.llvmtypes)
206 if err != nil {
207 return nil, err
208 }
209
210 mainPkg.Build()
211
212 // Create a struct responsible for mapping static types to LLVM types,
213 // and to runtime/dynamic type values.
214 compiler.types = NewTypeMap(
215 mainPkg,
216 compiler.llvmtypes,
217 compiler.module.Module,
218 compiler.runtime,
219 MethodResolver(unit),
220 )
221
222 if compiler.GenerateDebug {
223 compiler.debug = debug.NewDIBuilder(
224 types.Sizes(compiler.llvmtypes),
225 compiler.module.Module,
226 impcfg.Fset,
227 compiler.DebugPrefixMaps,
228 )
229 defer compiler.debug.Destroy()
230 defer compiler.debug.Finalize()
231 }
232
233 unit.translatePackage(mainPkg)
234 compiler.processAnnotations(unit, mainPkginfo)
235
236 if importpath == "main" {
237 if err = compiler.createInitMainFunction(mainPkg, initmap); err != nil {
238 return nil, fmt.Errorf("failed to create __go_init_main: %v", err)
239 }
240 } else {
241 compiler.module.ExportData = compiler.buildExportData(mainPkg, initmap)
242 }
243
244 return compiler.module, nil
245}
246
247type byPriorityThenFunc []gccgoimporter.PackageInit
248
249func (a byPriorityThenFunc) Len() int { return len(a) }
250func (a byPriorityThenFunc) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
251func (a byPriorityThenFunc) Less(i, j int) bool {
252 switch {
253 case a[i].Priority < a[j].Priority:
254 return true
255 case a[i].Priority > a[j].Priority:
256 return false
257 case a[i].InitFunc < a[j].InitFunc:
258 return true
259 default:
260 return false
261 }
262}
263
264func (c *compiler) buildPackageInitData(mainPkg *ssa.Package, initmap map[*types.Package]gccgoimporter.InitData) gccgoimporter.InitData {
265 var inits []gccgoimporter.PackageInit
266 for _, imp := range mainPkg.Object.Imports() {
267 inits = append(inits, initmap[imp].Inits...)
268 }
269 sort.Sort(byPriorityThenFunc(inits))
270
271 // Deduplicate init entries. We want to preserve the entry with the highest priority.
272 // Normally a package's priorities will be consistent among its dependencies, but it is
273 // possible for them to be different. For example, if a standard library test augments a
274 // package which is a dependency of 'regexp' (which is imported by every test main package)
275 // with additional dependencies, those dependencies may cause the package under test to
276 // receive a higher priority than indicated by its init clause in 'regexp'.
277 uniqinits := make([]gccgoimporter.PackageInit, len(inits))
278 uniqinitpos := len(inits)
279 uniqinitnames := make(map[string]struct{})
280 for i, _ := range inits {
281 init := inits[len(inits)-1-i]
282 if _, ok := uniqinitnames[init.InitFunc]; !ok {
283 uniqinitnames[init.InitFunc] = struct{}{}
284 uniqinitpos--
285 uniqinits[uniqinitpos] = init
286 }
287 }
288 uniqinits = uniqinits[uniqinitpos:]
289
290 ourprio := 1
291 if len(uniqinits) != 0 {
292 ourprio = uniqinits[len(uniqinits)-1].Priority + 1
293 }
294
295 if imp := mainPkg.Func("init"); imp != nil {
296 impname := c.types.mc.mangleFunctionName(imp)
297 uniqinits = append(uniqinits, gccgoimporter.PackageInit{mainPkg.Object.Name(), impname, ourprio})
298 }
299
300 return gccgoimporter.InitData{ourprio, uniqinits}
301}
302
303func (c *compiler) createInitMainFunction(mainPkg *ssa.Package, initmap map[*types.Package]gccgoimporter.InitData) error {
304 initdata := c.buildPackageInitData(mainPkg, initmap)
305
306 ftyp := llvm.FunctionType(llvm.VoidType(), nil, false)
307 initMain := llvm.AddFunction(c.module.Module, "__go_init_main", ftyp)
308 c.addCommonFunctionAttrs(initMain)
309 entry := llvm.AddBasicBlock(initMain, "entry")
310
311 builder := llvm.GlobalContext().NewBuilder()
312 defer builder.Dispose()
313 builder.SetInsertPointAtEnd(entry)
314
315 for _, init := range initdata.Inits {
316 initfn := c.module.Module.NamedFunction(init.InitFunc)
317 if initfn.IsNil() {
318 initfn = llvm.AddFunction(c.module.Module, init.InitFunc, ftyp)
319 }
320 builder.CreateCall(initfn, nil, "")
321 }
322
323 builder.CreateRetVoid()
324 return nil
325}
326
327func (c *compiler) buildExportData(mainPkg *ssa.Package, initmap map[*types.Package]gccgoimporter.InitData) []byte {
328 exportData := importer.ExportData(mainPkg.Object)
329 b := bytes.NewBuffer(exportData)
330
331 initdata := c.buildPackageInitData(mainPkg, initmap)
332 b.WriteString("v1;\npriority ")
333 b.WriteString(strconv.Itoa(initdata.Priority))
334 b.WriteString(";\n")
335
336 if len(initdata.Inits) != 0 {
337 b.WriteString("init")
338 for _, init := range initdata.Inits {
339 b.WriteRune(' ')
340 b.WriteString(init.Name)
341 b.WriteRune(' ')
342 b.WriteString(init.InitFunc)
343 b.WriteRune(' ')
344 b.WriteString(strconv.Itoa(init.Priority))
345 }
346 b.WriteString(";\n")
347 }
348
349 return b.Bytes()
350}
351
352// vim: set ft=go :