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" |
Peter Collingbourne | 9350942 | 2014-12-31 00:25:32 +0000 | [diff] [blame] | 18 | "go/ast" |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 19 | "go/token" |
| 20 | "log" |
| 21 | "sort" |
| 22 | "strconv" |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 23 | |
| 24 | llgobuild "llvm.org/llgo/build" |
| 25 | "llvm.org/llgo/debug" |
| 26 | "llvm.org/llvm/bindings/go/llvm" |
| 27 | |
Peter Collingbourne | 56109b7 | 2015-01-13 20:45:08 +0000 | [diff] [blame] | 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" |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 33 | ) |
| 34 | |
| 35 | type Module struct { |
| 36 | llvm.Module |
| 37 | Path string |
| 38 | ExportData []byte |
Peter Collingbourne | 76c1d4e | 2014-12-31 00:25:35 +0000 | [diff] [blame] | 39 | Package *types.Package |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 | |
Peter Collingbourne | d34d92f | 2014-12-31 00:25:39 +0000 | [diff] [blame] | 77 | // Whether to use the gccgo ABI. |
| 78 | GccgoABI bool |
| 79 | |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 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 |
Peter Collingbourne | c253ebc | 2015-01-14 05:17:41 +0000 | [diff] [blame] | 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) |
Peter Collingbourne | 5ab8061 | 2015-01-14 05:18:16 +0000 | [diff] [blame] | 100 | |
| 101 | // DisableUnusedImportCheck disables the unused import check performed |
| 102 | // by go/types if set to true. |
| 103 | DisableUnusedImportCheck bool |
Peter Collingbourne | 38a7dde | 2015-03-18 08:34:40 +0000 | [diff] [blame] | 104 | |
| 105 | // Packages is used by go/types as the imported package map if non-nil. |
| 106 | Packages map[string]*types.Package |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | type Compiler struct { |
| 110 | opts CompilerOptions |
| 111 | dataLayout string |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | func NewCompiler(opts CompilerOptions) (*Compiler, error) { |
| 115 | compiler := &Compiler{opts: opts} |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 | |
Peter Collingbourne | 9350942 | 2014-12-31 00:25:32 +0000 | [diff] [blame] | 124 | func (c *Compiler) Compile(fset *token.FileSet, astFiles []*ast.File, importpath string) (m *Module, err error) { |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 125 | target := llvm.NewTargetData(c.dataLayout) |
| 126 | compiler := &compiler{ |
| 127 | CompilerOptions: c.opts, |
| 128 | dataLayout: c.dataLayout, |
| 129 | target: target, |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 130 | llvmtypes: NewLLVMTypeMap(llvm.GlobalContext(), target), |
| 131 | } |
Peter Collingbourne | 9350942 | 2014-12-31 00:25:32 +0000 | [diff] [blame] | 132 | return compiler.compile(fset, astFiles, importpath) |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 | |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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", "") |
Meador Inge | 21e5350 | 2016-12-06 04:01:11 +0000 | [diff] [blame] | 159 | if c.SanitizerAttribute.GetEnumKind() != 0 { |
| 160 | fn.AddFunctionAttr(c.SanitizerAttribute) |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 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 | |
Peter Collingbourne | 9350942 | 2014-12-31 00:25:32 +0000 | [diff] [blame] | 185 | func (compiler *compiler) compile(fset *token.FileSet, astFiles []*ast.File, importpath string) (m *Module, err error) { |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 186 | buildctx, err := llgobuild.ContextFromTriple(compiler.TargetTriple) |
| 187 | if err != nil { |
| 188 | return nil, err |
| 189 | } |
| 190 | |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 191 | if compiler.Importer == nil { |
| 192 | err = compiler.MakeImporter() |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 193 | if err != nil { |
| 194 | return nil, err |
| 195 | } |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | impcfg := &loader.Config{ |
Peter Collingbourne | 9350942 | 2014-12-31 00:25:32 +0000 | [diff] [blame] | 199 | Fset: fset, |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 200 | TypeChecker: types.Config{ |
Peter Collingbourne | 38a7dde | 2015-03-18 08:34:40 +0000 | [diff] [blame] | 201 | Packages: compiler.Packages, |
| 202 | Import: compiler.Importer, |
| 203 | Sizes: compiler.llvmtypes, |
Peter Collingbourne | 5ab8061 | 2015-01-14 05:18:16 +0000 | [diff] [blame] | 204 | DisableUnusedImportCheck: compiler.DisableUnusedImportCheck, |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 205 | }, |
Peter Collingbourne | 7d39641 | 2015-04-05 23:28:18 +0000 | [diff] [blame] | 206 | ImportFromBinary: true, |
| 207 | Build: &buildctx.Context, |
| 208 | PackageCreated: compiler.PackageCreated, |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 209 | } |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 |
Peter Collingbourne | 76c1d4e | 2014-12-31 00:25:35 +0000 | [diff] [blame] | 226 | compiler.module = &Module{Module: llvm.NewModule(modulename), Path: modulename, Package: mainPkg.Object} |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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" { |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 266 | compiler.createInitMainFunction(mainPkg) |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 267 | } else { |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 268 | compiler.module.ExportData = compiler.buildExportData(mainPkg) |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 | |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 291 | func (c *compiler) buildPackageInitData(mainPkg *ssa.Package) gccgoimporter.InitData { |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 292 | var inits []gccgoimporter.PackageInit |
| 293 | for _, imp := range mainPkg.Object.Imports() { |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 294 | inits = append(inits, c.InitMap[imp].Inits...) |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 | |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 330 | func (c *compiler) createInitMainFunction(mainPkg *ssa.Package) { |
Peter Collingbourne | cac3259 | 2015-04-05 23:31:49 +0000 | [diff] [blame] | 331 | int8ptr := llvm.PointerType(c.types.ctx.Int8Type(), 0) |
| 332 | ftyp := llvm.FunctionType(llvm.VoidType(), []llvm.Type{int8ptr}, false) |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 | |
Peter Collingbourne | cac3259 | 2015-04-05 23:31:49 +0000 | [diff] [blame] | 341 | args := []llvm.Value{llvm.Undef(int8ptr)} |
| 342 | |
Peter Collingbourne | d34d92f | 2014-12-31 00:25:39 +0000 | [diff] [blame] | 343 | if !c.GccgoABI { |
| 344 | initfn := c.module.Module.NamedFunction("main..import") |
| 345 | if !initfn.IsNil() { |
Peter Collingbourne | cac3259 | 2015-04-05 23:31:49 +0000 | [diff] [blame] | 346 | builder.CreateCall(initfn, args, "") |
Peter Collingbourne | d34d92f | 2014-12-31 00:25:39 +0000 | [diff] [blame] | 347 | } |
| 348 | builder.CreateRetVoid() |
| 349 | return |
| 350 | } |
| 351 | |
| 352 | initdata := c.buildPackageInitData(mainPkg) |
| 353 | |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 | } |
Peter Collingbourne | cac3259 | 2015-04-05 23:31:49 +0000 | [diff] [blame] | 359 | builder.CreateCall(initfn, args, "") |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 360 | } |
| 361 | |
| 362 | builder.CreateRetVoid() |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 363 | } |
| 364 | |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 365 | func (c *compiler) buildExportData(mainPkg *ssa.Package) []byte { |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 366 | exportData := importer.ExportData(mainPkg.Object) |
| 367 | b := bytes.NewBuffer(exportData) |
| 368 | |
Peter Collingbourne | d34d92f | 2014-12-31 00:25:39 +0000 | [diff] [blame] | 369 | b.WriteString("v1;\n") |
| 370 | if !c.GccgoABI { |
| 371 | return b.Bytes() |
| 372 | } |
| 373 | |
Peter Collingbourne | a0a5308 | 2014-12-31 00:25:36 +0000 | [diff] [blame] | 374 | initdata := c.buildPackageInitData(mainPkg) |
Peter Collingbourne | d34d92f | 2014-12-31 00:25:39 +0000 | [diff] [blame] | 375 | b.WriteString("priority ") |
Peter Collingbourne | ad9841e | 2014-11-27 00:06:42 +0000 | [diff] [blame] | 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 : |