github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/irgen/compiler.go (about) 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 "go/ast" 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/gotools/go/gccgoimporter" 30 "llvm.org/llgo/third_party/gotools/go/importer" 31 "llvm.org/llgo/third_party/gotools/go/loader" 32 "llvm.org/llgo/third_party/gotools/go/ssa" 33 "llvm.org/llgo/third_party/gotools/go/types" 34 ) 35 36 type Module struct { 37 llvm.Module 38 Path string 39 ExportData []byte 40 Package *types.Package 41 disposed bool 42 } 43 44 func (m *Module) Dispose() { 45 if m.disposed { 46 return 47 } 48 m.Module.Dispose() 49 m.disposed = true 50 } 51 52 /////////////////////////////////////////////////////////////////////////////// 53 54 type 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 // Whether to use the gccgo ABI. 79 GccgoABI bool 80 81 // ImportPaths is the list of additional import paths 82 ImportPaths []string 83 84 // SanitizerAttribute is an attribute to apply to functions to enable 85 // dynamic instrumentation using a sanitizer. 86 SanitizerAttribute llvm.Attribute 87 88 // Importer is the importer. If nil, the compiler will set this field 89 // automatically using MakeImporter(). 90 Importer types.Importer 91 92 // InitMap is the init map used by Importer. If Importer is nil, the 93 // compiler will set this field automatically using MakeImporter(). 94 // If Importer is non-nil, InitMap must be non-nil also. 95 InitMap map[*types.Package]gccgoimporter.InitData 96 97 // PackageCreated is a hook passed to the go/loader package via 98 // loader.Config, see the documentation for that package for more 99 // information. 100 PackageCreated func(*types.Package) 101 102 // DisableUnusedImportCheck disables the unused import check performed 103 // by go/types if set to true. 104 DisableUnusedImportCheck bool 105 106 // Packages is used by go/types as the imported package map if non-nil. 107 Packages map[string]*types.Package 108 } 109 110 type Compiler struct { 111 opts CompilerOptions 112 dataLayout string 113 pnacl bool 114 } 115 116 func NewCompiler(opts CompilerOptions) (*Compiler, error) { 117 compiler := &Compiler{opts: opts} 118 if strings.ToLower(compiler.opts.TargetTriple) == "pnacl" { 119 compiler.opts.TargetTriple = PNaClTriple 120 compiler.pnacl = true 121 } 122 dataLayout, err := llvmDataLayout(compiler.opts.TargetTriple) 123 if err != nil { 124 return nil, err 125 } 126 compiler.dataLayout = dataLayout 127 return compiler, nil 128 } 129 130 func (c *Compiler) Compile(fset *token.FileSet, astFiles []*ast.File, importpath string) (m *Module, err error) { 131 target := llvm.NewTargetData(c.dataLayout) 132 compiler := &compiler{ 133 CompilerOptions: c.opts, 134 dataLayout: c.dataLayout, 135 target: target, 136 pnacl: c.pnacl, 137 llvmtypes: NewLLVMTypeMap(llvm.GlobalContext(), target), 138 } 139 return compiler.compile(fset, astFiles, importpath) 140 } 141 142 type compiler struct { 143 CompilerOptions 144 145 module *Module 146 dataLayout string 147 target llvm.TargetData 148 fileset *token.FileSet 149 150 runtime *runtimeInterface 151 llvmtypes *llvmTypeMap 152 types *TypeMap 153 154 // pnacl is set to true if the target triple was originally 155 // specified as "pnacl". This is necessary, as the TargetTriple 156 // field will have been updated to the true triple used to 157 // compile PNaCl modules. 158 pnacl bool 159 160 debug *debug.DIBuilder 161 } 162 163 func (c *compiler) logf(format string, v ...interface{}) { 164 if c.Logger != nil { 165 c.Logger.Printf(format, v...) 166 } 167 } 168 169 func (c *compiler) addCommonFunctionAttrs(fn llvm.Value) { 170 fn.AddTargetDependentFunctionAttr("disable-tail-calls", "true") 171 fn.AddTargetDependentFunctionAttr("split-stack", "") 172 if attr := c.SanitizerAttribute; attr != 0 { 173 fn.AddFunctionAttr(attr) 174 } 175 } 176 177 // MakeImporter sets CompilerOptions.Importer to an appropriate importer 178 // for the search paths given in CompilerOptions.ImportPaths, and sets 179 // CompilerOptions.InitMap to an init map belonging to that importer. 180 // If CompilerOptions.GccgoPath is non-empty, the importer will also use 181 // the search paths for that gccgo installation. 182 func (opts *CompilerOptions) MakeImporter() error { 183 opts.InitMap = make(map[*types.Package]gccgoimporter.InitData) 184 if opts.GccgoPath == "" { 185 paths := append(append([]string{}, opts.ImportPaths...), ".") 186 opts.Importer = gccgoimporter.GetImporter(paths, opts.InitMap) 187 } else { 188 var inst gccgoimporter.GccgoInstallation 189 err := inst.InitFromDriver(opts.GccgoPath) 190 if err != nil { 191 return err 192 } 193 opts.Importer = inst.GetImporter(opts.ImportPaths, opts.InitMap) 194 } 195 return nil 196 } 197 198 func (compiler *compiler) compile(fset *token.FileSet, astFiles []*ast.File, importpath string) (m *Module, err error) { 199 buildctx, err := llgobuild.ContextFromTriple(compiler.TargetTriple) 200 if err != nil { 201 return nil, err 202 } 203 204 if compiler.Importer == nil { 205 err = compiler.MakeImporter() 206 if err != nil { 207 return nil, err 208 } 209 } 210 211 impcfg := &loader.Config{ 212 Fset: fset, 213 TypeChecker: types.Config{ 214 Packages: compiler.Packages, 215 Import: compiler.Importer, 216 Sizes: compiler.llvmtypes, 217 DisableUnusedImportCheck: compiler.DisableUnusedImportCheck, 218 }, 219 ImportFromBinary: true, 220 Build: &buildctx.Context, 221 PackageCreated: compiler.PackageCreated, 222 } 223 // If no import path is specified, then set the import 224 // path to be the same as the package's name. 225 if importpath == "" { 226 importpath = astFiles[0].Name.String() 227 } 228 impcfg.CreateFromFiles(importpath, astFiles...) 229 iprog, err := impcfg.Load() 230 if err != nil { 231 return nil, err 232 } 233 program := ssa.Create(iprog, ssa.BareInits) 234 mainPkginfo := iprog.InitialPackages()[0] 235 mainPkg := program.CreatePackage(mainPkginfo) 236 237 // Create a Module, which contains the LLVM module. 238 modulename := importpath 239 compiler.module = &Module{Module: llvm.NewModule(modulename), Path: modulename, Package: mainPkg.Object} 240 compiler.module.SetTarget(compiler.TargetTriple) 241 compiler.module.SetDataLayout(compiler.dataLayout) 242 243 // Create a new translation unit. 244 unit := newUnit(compiler, mainPkg) 245 246 // Create the runtime interface. 247 compiler.runtime, err = newRuntimeInterface(compiler.module.Module, compiler.llvmtypes) 248 if err != nil { 249 return nil, err 250 } 251 252 mainPkg.Build() 253 254 // Create a struct responsible for mapping static types to LLVM types, 255 // and to runtime/dynamic type values. 256 compiler.types = NewTypeMap( 257 mainPkg, 258 compiler.llvmtypes, 259 compiler.module.Module, 260 compiler.runtime, 261 MethodResolver(unit), 262 ) 263 264 if compiler.GenerateDebug { 265 compiler.debug = debug.NewDIBuilder( 266 types.Sizes(compiler.llvmtypes), 267 compiler.module.Module, 268 impcfg.Fset, 269 compiler.DebugPrefixMaps, 270 ) 271 defer compiler.debug.Destroy() 272 defer compiler.debug.Finalize() 273 } 274 275 unit.translatePackage(mainPkg) 276 compiler.processAnnotations(unit, mainPkginfo) 277 278 if importpath == "main" { 279 compiler.createInitMainFunction(mainPkg) 280 } else { 281 compiler.module.ExportData = compiler.buildExportData(mainPkg) 282 } 283 284 return compiler.module, nil 285 } 286 287 type byPriorityThenFunc []gccgoimporter.PackageInit 288 289 func (a byPriorityThenFunc) Len() int { return len(a) } 290 func (a byPriorityThenFunc) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 291 func (a byPriorityThenFunc) Less(i, j int) bool { 292 switch { 293 case a[i].Priority < a[j].Priority: 294 return true 295 case a[i].Priority > a[j].Priority: 296 return false 297 case a[i].InitFunc < a[j].InitFunc: 298 return true 299 default: 300 return false 301 } 302 } 303 304 func (c *compiler) buildPackageInitData(mainPkg *ssa.Package) gccgoimporter.InitData { 305 var inits []gccgoimporter.PackageInit 306 for _, imp := range mainPkg.Object.Imports() { 307 inits = append(inits, c.InitMap[imp].Inits...) 308 } 309 sort.Sort(byPriorityThenFunc(inits)) 310 311 // Deduplicate init entries. We want to preserve the entry with the highest priority. 312 // Normally a package's priorities will be consistent among its dependencies, but it is 313 // possible for them to be different. For example, if a standard library test augments a 314 // package which is a dependency of 'regexp' (which is imported by every test main package) 315 // with additional dependencies, those dependencies may cause the package under test to 316 // receive a higher priority than indicated by its init clause in 'regexp'. 317 uniqinits := make([]gccgoimporter.PackageInit, len(inits)) 318 uniqinitpos := len(inits) 319 uniqinitnames := make(map[string]struct{}) 320 for i, _ := range inits { 321 init := inits[len(inits)-1-i] 322 if _, ok := uniqinitnames[init.InitFunc]; !ok { 323 uniqinitnames[init.InitFunc] = struct{}{} 324 uniqinitpos-- 325 uniqinits[uniqinitpos] = init 326 } 327 } 328 uniqinits = uniqinits[uniqinitpos:] 329 330 ourprio := 1 331 if len(uniqinits) != 0 { 332 ourprio = uniqinits[len(uniqinits)-1].Priority + 1 333 } 334 335 if imp := mainPkg.Func("init"); imp != nil { 336 impname := c.types.mc.mangleFunctionName(imp) 337 uniqinits = append(uniqinits, gccgoimporter.PackageInit{mainPkg.Object.Name(), impname, ourprio}) 338 } 339 340 return gccgoimporter.InitData{ourprio, uniqinits} 341 } 342 343 func (c *compiler) createInitMainFunction(mainPkg *ssa.Package) { 344 int8ptr := llvm.PointerType(c.types.ctx.Int8Type(), 0) 345 ftyp := llvm.FunctionType(llvm.VoidType(), []llvm.Type{int8ptr}, false) 346 initMain := llvm.AddFunction(c.module.Module, "__go_init_main", ftyp) 347 c.addCommonFunctionAttrs(initMain) 348 entry := llvm.AddBasicBlock(initMain, "entry") 349 350 builder := llvm.GlobalContext().NewBuilder() 351 defer builder.Dispose() 352 builder.SetInsertPointAtEnd(entry) 353 354 args := []llvm.Value{llvm.Undef(int8ptr)} 355 356 if !c.GccgoABI { 357 initfn := c.module.Module.NamedFunction("main..import") 358 if !initfn.IsNil() { 359 builder.CreateCall(initfn, args, "") 360 } 361 builder.CreateRetVoid() 362 return 363 } 364 365 initdata := c.buildPackageInitData(mainPkg) 366 367 for _, init := range initdata.Inits { 368 initfn := c.module.Module.NamedFunction(init.InitFunc) 369 if initfn.IsNil() { 370 initfn = llvm.AddFunction(c.module.Module, init.InitFunc, ftyp) 371 } 372 builder.CreateCall(initfn, args, "") 373 } 374 375 builder.CreateRetVoid() 376 } 377 378 func (c *compiler) buildExportData(mainPkg *ssa.Package) []byte { 379 exportData := importer.ExportData(mainPkg.Object) 380 b := bytes.NewBuffer(exportData) 381 382 b.WriteString("v1;\n") 383 if !c.GccgoABI { 384 return b.Bytes() 385 } 386 387 initdata := c.buildPackageInitData(mainPkg) 388 b.WriteString("priority ") 389 b.WriteString(strconv.Itoa(initdata.Priority)) 390 b.WriteString(";\n") 391 392 if len(initdata.Inits) != 0 { 393 b.WriteString("init") 394 for _, init := range initdata.Inits { 395 b.WriteRune(' ') 396 b.WriteString(init.Name) 397 b.WriteRune(' ') 398 b.WriteString(init.InitFunc) 399 b.WriteRune(' ') 400 b.WriteString(strconv.Itoa(init.Priority)) 401 } 402 b.WriteString(";\n") 403 } 404 405 return b.Bytes() 406 } 407 408 // vim: set ft=go :