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