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