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