Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame^] | 1 | //===- 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 | |
| 14 | package irgen |
| 15 | |
| 16 | import ( |
| 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 | |
| 36 | type Module struct { |
| 37 | llvm.Module |
| 38 | Path string |
| 39 | ExportData []byte |
| 40 | disposed bool |
| 41 | } |
| 42 | |
| 43 | func (m *Module) Dispose() { |
| 44 | if m.disposed { |
| 45 | return |
| 46 | } |
| 47 | m.Module.Dispose() |
| 48 | m.disposed = true |
| 49 | } |
| 50 | |
| 51 | /////////////////////////////////////////////////////////////////////////////// |
| 52 | |
| 53 | type 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 | |
| 85 | type Compiler struct { |
| 86 | opts CompilerOptions |
| 87 | dataLayout string |
| 88 | pnacl bool |
| 89 | } |
| 90 | |
| 91 | func 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 | |
| 105 | func (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 | |
| 117 | type 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 | |
| 142 | func (c *compiler) logf(format string, v ...interface{}) { |
| 143 | if c.Logger != nil { |
| 144 | c.Logger.Printf(format, v...) |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | func (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 | |
| 156 | func (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 | |
| 256 | type byPriorityThenFunc []gccgoimporter.PackageInit |
| 257 | |
| 258 | func (a byPriorityThenFunc) Len() int { return len(a) } |
| 259 | func (a byPriorityThenFunc) Swap(i, j int) { a[i], a[j] = a[j], a[i] } |
| 260 | func (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 | |
| 273 | func (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 | |
| 312 | func (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 | |
| 336 | func (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 : |