From 74fca2e5e8a0d262b6e334a4dfff55bb2549a9c3 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 7 Jun 2019 21:04:56 +0200 Subject: [PATCH 1/2] compiler: refactor named types to create them lazily This commit refactors named types to be created lazily. Instead of defining all types in advance, do it only when necessary. --- compiler/channel.go | 2 +- compiler/compiler.go | 66 ++++++++++++++++++---------------- compiler/defer.go | 8 ++--- compiler/func-lowering.go | 2 +- compiler/func.go | 4 +-- compiler/goroutine-lowering.go | 2 +- compiler/interface-lowering.go | 4 +-- compiler/interface.go | 10 +++--- ir/ir.go | 16 --------- 9 files changed, 51 insertions(+), 63 deletions(-) diff --git a/compiler/channel.go b/compiler/channel.go index b5bcd95b99..8098ea99a5 100644 --- a/compiler/channel.go +++ b/compiler/channel.go @@ -12,7 +12,7 @@ import ( // emitMakeChan returns a new channel value for the given channel type. func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) { - chanType := c.mod.GetTypeByName("runtime.channel") + chanType := c.getLLVMType(c.getRuntimeType("channel")) size := c.targetData.TypeAllocSize(chanType) sizeValue := llvm.ConstInt(c.uintptrType, size, false) ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc") diff --git a/compiler/compiler.go b/compiler/compiler.go index 4364b03acf..dcd299db00 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -275,25 +275,6 @@ func (c *Compiler) Compile(mainPath string) []error { var frames []*Frame - // Declare all named struct types. - for _, t := range c.ir.NamedTypes { - if named, ok := t.Type.Type().(*types.Named); ok { - if _, ok := named.Underlying().(*types.Struct); ok { - t.LLVMType = c.ctx.StructCreateNamed(named.Obj().Pkg().Path() + "." + named.Obj().Name()) - } - } - } - - // Define all named struct types. - for _, t := range c.ir.NamedTypes { - if named, ok := t.Type.Type().(*types.Named); ok { - if st, ok := named.Underlying().(*types.Struct); ok { - llvmType := c.getLLVMType(st) - t.LLVMType.StructSetBody(llvmType.StructElementTypes(), false) - } - } - } - // Declare all globals. for _, g := range c.ir.Globals { typ := g.Type().(*types.Pointer).Elem() @@ -413,6 +394,22 @@ func (c *Compiler) Compile(mainPath string) []error { return c.diagnostics } +// getRuntimeType obtains a named type from the runtime package and returns it +// as a Go type. +func (c *Compiler) getRuntimeType(name string) types.Type { + return c.ir.Program.ImportedPackage("runtime").Type(name).Type() +} + +// getLLVMRuntimeType obtains a named type from the runtime package and returns +// it as a LLVM type, creating it if necessary. It is a shorthand for +// getLLVMType(getRuntimeType(name)). +func (c *Compiler) getLLVMRuntimeType(name string) llvm.Type { + return c.getLLVMType(c.getRuntimeType(name)) +} + +// getLLVMType creates and returns a LLVM type for a Go type. In the case of +// named struct types (or Go types implemented as named LLVM structs such as +// strings) it also creates it first if necessary. func (c *Compiler) getLLVMType(goType types.Type) llvm.Type { switch typ := goType.(type) { case *types.Array: @@ -441,7 +438,7 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type { case types.Complex128: return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false) case types.String, types.UntypedString: - return c.mod.GetTypeByName("runtime._string") + return c.getLLVMRuntimeType("_string") case types.Uintptr: return c.uintptrType case types.UnsafePointer: @@ -450,16 +447,23 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type { panic("unknown basic type: " + typ.String()) } case *types.Chan: - return llvm.PointerType(c.mod.GetTypeByName("runtime.channel"), 0) + return llvm.PointerType(c.getLLVMRuntimeType("channel"), 0) case *types.Interface: - return c.mod.GetTypeByName("runtime._interface") + return c.getLLVMRuntimeType("_interface") case *types.Map: - return llvm.PointerType(c.mod.GetTypeByName("runtime.hashmap"), 0) + return llvm.PointerType(c.getLLVMRuntimeType("hashmap"), 0) case *types.Named: - if _, ok := typ.Underlying().(*types.Struct); ok { - llvmType := c.mod.GetTypeByName(typ.Obj().Pkg().Path() + "." + typ.Obj().Name()) + if st, ok := typ.Underlying().(*types.Struct); ok { + // Structs are a special case. While other named types are ignored + // in LLVM IR, named structs are implemented as named structs in + // LLVM. This is because it is otherwise impossible to create + // self-referencing types such as linked lists. + llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name() + llvmType := c.mod.GetTypeByName(llvmName) if llvmType.IsNil() { - panic("underlying type not found: " + typ.Obj().Pkg().Path() + "." + typ.Obj().Name()) + llvmType = c.ctx.StructCreateNamed(llvmName) + underlying := c.getLLVMType(st) + llvmType.StructSetBody(underlying.StructElementTypes(), false) } return llvmType } @@ -1696,9 +1700,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { var iteratorType llvm.Type switch typ := expr.X.Type().Underlying().(type) { case *types.Basic: // string - iteratorType = c.mod.GetTypeByName("runtime.stringIterator") + iteratorType = c.getLLVMRuntimeType("stringIterator") case *types.Map: - iteratorType = c.mod.GetTypeByName("runtime.hashmapIterator") + iteratorType = c.getLLVMRuntimeType("hashmapIterator") default: panic("unknown type in range: " + typ.String()) } @@ -1858,7 +1862,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "") newLen := c.builder.CreateSub(high, low, "") - str := llvm.Undef(c.mod.GetTypeByName("runtime._string")) + str := llvm.Undef(c.getLLVMRuntimeType("_string")) str = c.builder.CreateInsertValue(str, newPtr, 0, "") str = c.builder.CreateInsertValue(str, newLen, 1, "") return str, nil @@ -2237,7 +2241,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value { global.SetUnnamedAddr(true) zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) strPtr := c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "") - strObj := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._string"), []llvm.Value{strPtr, strLen}) + strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen}) return strObj } else if typ.Kind() == types.UnsafePointer { if !expr.IsNil() { @@ -2290,7 +2294,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value { llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstPointerNull(c.i8ptrType), } - return llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), fields) + return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields) case *types.Pointer: if expr.Value != nil { panic("expected nil pointer constant") diff --git a/compiler/defer.go b/compiler/defer.go index 1246f03a0d..21fff3aad5 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -29,7 +29,7 @@ func (c *Compiler) deferInitFunc(frame *Frame) { frame.deferClosureFuncs = make(map[*ir.Function]int) // Create defer list pointer. - deferType := llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0) + deferType := llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0) frame.deferPtr = c.builder.CreateAlloca(deferType, "deferPtr") c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr) } @@ -200,7 +200,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) { } // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0), c.i8ptrType} + valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0), c.i8ptrType} for _, arg := range callback.Args { valueTypes = append(valueTypes, c.getLLVMType(arg.Type())) } @@ -231,7 +231,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) { // Direct call. // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)} + valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)} for _, param := range callback.Params { valueTypes = append(valueTypes, c.getLLVMType(param.Type())) } @@ -260,7 +260,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) { case *ssa.MakeClosure: // Get the real defer struct type and cast to it. fn := c.ir.GetFunction(callback.Fn.(*ssa.Function)) - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)} + valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)} params := fn.Signature.Params() for i := 0; i < params.Len(); i++ { valueTypes = append(valueTypes, c.getLLVMType(params.At(i).Type())) diff --git a/compiler/func-lowering.go b/compiler/func-lowering.go index a774b23ac1..2d67e2a5f2 100644 --- a/compiler/func-lowering.go +++ b/compiler/func-lowering.go @@ -51,7 +51,7 @@ func (c *Compiler) LowerFuncValues() { } // Find all func values used in the program with their signatures. - funcValueWithSignaturePtr := llvm.PointerType(c.mod.GetTypeByName("runtime.funcValueWithSignature"), 0) + funcValueWithSignaturePtr := llvm.PointerType(c.getLLVMRuntimeType("funcValueWithSignature"), 0) signatures := map[string]*funcSignatureInfo{} for global := c.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) { if global.Type() != funcValueWithSignaturePtr { diff --git a/compiler/func.go b/compiler/func.go index 2a2af95429..5c66006188 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -52,7 +52,7 @@ func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signa funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature" funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName) if funcValueWithSignatureGlobal.IsNil() { - funcValueWithSignatureType := c.mod.GetTypeByName("runtime.funcValueWithSignature") + funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature") funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{ llvm.ConstPtrToInt(funcPtr, c.uintptrType), sigGlobal, @@ -126,7 +126,7 @@ func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type { rawPtr := c.getRawFuncType(typ) return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false) case funcValueSwitch: - return c.mod.GetTypeByName("runtime.funcValue") + return c.getLLVMRuntimeType("funcValue") default: panic("unimplemented func value variant") } diff --git a/compiler/goroutine-lowering.go b/compiler/goroutine-lowering.go index c493bf48ca..3aa72e938c 100644 --- a/compiler/goroutine-lowering.go +++ b/compiler/goroutine-lowering.go @@ -321,7 +321,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { // Coroutine setup. c.builder.SetInsertPointBefore(f.EntryBasicBlock().FirstInstruction()) - taskState := c.builder.CreateAlloca(c.mod.GetTypeByName("runtime.taskState"), "task.state") + taskState := c.builder.CreateAlloca(c.getLLVMRuntimeType("taskState"), "task.state") stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8") id := c.builder.CreateCall(coroIdFunc, []llvm.Value{ llvm.ConstInt(c.ctx.Int32Type(), 0, false), diff --git a/compiler/interface-lowering.go b/compiler/interface-lowering.go index 3df1c64430..6a756410c5 100644 --- a/compiler/interface-lowering.go +++ b/compiler/interface-lowering.go @@ -163,8 +163,8 @@ func (c *Compiler) LowerInterfaces() { // run runs the pass itself. func (p *lowerInterfacesPass) run() { // Collect all type codes. - typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0) - typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0) + typecodeIDPtr := llvm.PointerType(p.getLLVMRuntimeType("typecodeID"), 0) + typeInInterfacePtr := llvm.PointerType(p.getLLVMRuntimeType("typeInInterface"), 0) var typesInInterfaces []llvm.Value for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) { switch global.Type() { diff --git a/compiler/interface.go b/compiler/interface.go index 3d2014f317..d91b8063f6 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -28,14 +28,14 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token. itfMethodSetGlobal := c.getTypeMethodSet(typ) itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name()) if itfConcreteTypeGlobal.IsNil() { - typeInInterface := c.mod.GetTypeByName("runtime.typeInInterface") + typeInInterface := c.getLLVMRuntimeType("typeInInterface") itfConcreteTypeGlobal = llvm.AddGlobal(c.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name()) itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal})) itfConcreteTypeGlobal.SetGlobalConstant(true) itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage) } itfTypeCode := c.builder.CreatePtrToInt(itfConcreteTypeGlobal, c.uintptrType, "") - itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface")) + itf := llvm.Undef(c.getLLVMRuntimeType("_interface")) itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "") itf = c.builder.CreateInsertValue(itf, itfValue, 1, "") return itf @@ -48,7 +48,7 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value { globalName := "type:" + getTypeCodeName(typ) global := c.mod.NamedGlobal(globalName) if global.IsNil() { - global = llvm.AddGlobal(c.mod, c.mod.GetTypeByName("runtime.typecodeID"), globalName) + global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName) global.SetGlobalConstant(true) } return global @@ -163,11 +163,11 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value { ms := c.ir.Program.MethodSets.MethodSet(typ) if ms.Len() == 0 { // no methods, so can leave that one out - return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0)) + return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0)) } methods := make([]llvm.Value, ms.Len()) - interfaceMethodInfoType := c.mod.GetTypeByName("runtime.interfaceMethodInfo") + interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo") for i := 0; i < ms.Len(); i++ { method := ms.At(i) signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func)) diff --git a/ir/ir.go b/ir/ir.go index 1556330ef3..5f73b90e19 100644 --- a/ir/ir.go +++ b/ir/ir.go @@ -26,7 +26,6 @@ type Program struct { Globals []*Global globalMap map[*ssa.Global]*Global comments map[string]*ast.CommentGroup - NamedTypes []*NamedType } // Function or method. @@ -50,19 +49,6 @@ type Global struct { extern bool // go:extern } -// Type with a name and possibly methods. -type NamedType struct { - *ssa.Type - LLVMType llvm.Type -} - -// Type that is at some point put in an interface. -type TypeWithMethods struct { - t types.Type - Num int - Methods map[string]*types.Selection -} - // Interface type that is at some point used in a type assert (to check whether // it implements another interface). type Interface struct { @@ -210,8 +196,6 @@ func (p *Program) AddPackage(pkg *ssa.Package) { case *ssa.Function: p.addFunction(member) case *ssa.Type: - t := &NamedType{Type: member} - p.NamedTypes = append(p.NamedTypes, t) methods := getAllMethods(pkg.Prog, member.Type()) if !types.IsInterface(member.Type()) { // named type From 7c07f85fb8856c8b5d332ce200ad01070d11cb70 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 7 Jun 2019 22:16:09 +0200 Subject: [PATCH 2/2] compiler: move global handling from ir to compiler package This is part of a larger rafactor that tries to shrink the ir package and in general tries to shrink the amount of state that is kept around in the compiler. The end goal is being able to compile packages independent of each other, linking them together in a later stage. Along the way, it cleans up lots of old cruft that has accumulated over the months. This refactor also results in globals being loaded lazily. This may be a problem for some specific programs but will probably change back in a commit in the near future. --- compiler/compiler.go | 25 +++------- compiler/symbol.go | 107 +++++++++++++++++++++++++++++++++++++++++++ ir/ir.go | 96 +------------------------------------- 3 files changed, 115 insertions(+), 113 deletions(-) create mode 100644 compiler/symbol.go diff --git a/compiler/compiler.go b/compiler/compiler.go index dcd299db00..73efc8d6b8 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -3,6 +3,7 @@ package compiler import ( "errors" "fmt" + "go/ast" "go/build" "go/constant" "go/token" @@ -66,6 +67,7 @@ type Compiler struct { interfaceInvokeWrappers []interfaceInvokeWrapper ir *ir.Program diagnostics []error + astComments map[string]*ast.CommentGroup } type Frame struct { @@ -275,20 +277,7 @@ func (c *Compiler) Compile(mainPath string) []error { var frames []*Frame - // Declare all globals. - for _, g := range c.ir.Globals { - typ := g.Type().(*types.Pointer).Elem() - llvmType := c.getLLVMType(typ) - global := c.mod.NamedGlobal(g.LinkName()) - if global.IsNil() { - global = llvm.AddGlobal(c.mod, llvmType, g.LinkName()) - } - g.LLVMGlobal = global - if !g.IsExtern() { - global.SetLinkage(llvm.InternalLinkage) - global.SetInitializer(c.getZeroValue(llvmType)) - } - } + c.loadASTComments(lprogram) // Declare all functions. for _, f := range c.ir.Functions { @@ -1360,9 +1349,9 @@ func (c *Compiler) getValue(frame *Frame, expr ssa.Value) llvm.Value { } return c.createFuncValue(fn.LLVMFn, llvm.Undef(c.i8ptrType), fn.Signature) case *ssa.Global: - value := c.ir.GetGlobal(expr).LLVMGlobal + value := c.getGlobal(expr) if value.IsNil() { - c.addError(expr.Pos(), "global not found: "+c.ir.GetGlobal(expr).LinkName()) + c.addError(expr.Pos(), "global not found: "+expr.RelString(nil)) return llvm.Undef(c.getLLVMType(expr.Type())) } return value @@ -2514,8 +2503,8 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { // var C.add unsafe.Pointer // Instead of a load from the global, create a bitcast of the // function pointer itself. - global := c.ir.GetGlobal(unop.X.(*ssa.Global)) - name := global.LinkName()[:len(global.LinkName())-len("$funcaddr")] + globalName := c.getGlobalInfo(unop.X.(*ssa.Global)).linkName + name := globalName[:len(globalName)-len("$funcaddr")] fn := c.mod.NamedFunction(name) if fn.IsNil() { return llvm.Value{}, c.makeError(unop.Pos(), "cgo function not found: "+name) diff --git a/compiler/symbol.go b/compiler/symbol.go new file mode 100644 index 0000000000..0df2ab6e42 --- /dev/null +++ b/compiler/symbol.go @@ -0,0 +1,107 @@ +package compiler + +// This file manages symbols, that is, functions and globals. It reads their +// pragmas, determines the link name, etc. + +import ( + "go/ast" + "go/token" + "go/types" + "strings" + + "github.com/tinygo-org/tinygo/loader" + "golang.org/x/tools/go/ssa" + "tinygo.org/x/go-llvm" +) + +// globalInfo contains some information about a specific global. By default, +// linkName is equal to .RelString(nil) on a global and extern is false, but for +// some symbols this is different (due to //go:extern for example). +type globalInfo struct { + linkName string // go:extern + extern bool // go:extern +} + +// loadASTComments loads comments on globals from the AST, for use later in the +// program. In particular, they are required for //go:extern pragmas on globals. +func (c *Compiler) loadASTComments(lprogram *loader.Program) { + c.astComments = map[string]*ast.CommentGroup{} + for _, pkgInfo := range lprogram.Sorted() { + for _, file := range pkgInfo.Files { + for _, decl := range file.Decls { + switch decl := decl.(type) { + case *ast.GenDecl: + switch decl.Tok { + case token.VAR: + if len(decl.Specs) != 1 { + continue + } + for _, spec := range decl.Specs { + switch spec := spec.(type) { + case *ast.ValueSpec: // decl.Tok == token.VAR + for _, name := range spec.Names { + id := pkgInfo.Pkg.Path() + "." + name.Name + c.astComments[id] = decl.Doc + } + } + } + } + } + } + } + } +} + +// getGlobal returns a LLVM IR global value for a Go SSA global. It is added to +// the LLVM IR if it has not been added already. +func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value { + info := c.getGlobalInfo(g) + llvmGlobal := c.mod.NamedGlobal(info.linkName) + if llvmGlobal.IsNil() { + llvmType := c.getLLVMType(g.Type().(*types.Pointer).Elem()) + llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName) + if !info.extern { + llvmGlobal.SetInitializer(c.getZeroValue(llvmType)) + llvmGlobal.SetLinkage(llvm.InternalLinkage) + } + } + return llvmGlobal +} + +// getGlobalInfo returns some information about a specific global. +func (c *Compiler) getGlobalInfo(g *ssa.Global) globalInfo { + info := globalInfo{} + if strings.HasPrefix(g.Name(), "C.") { + // Created by CGo: such a name cannot be created by regular C code. + info.linkName = g.Name()[2:] + info.extern = true + } else { + // Pick the default linkName. + info.linkName = g.RelString(nil) + // Check for //go: pragmas, which may change the link name (among + // others). + doc := c.astComments[info.linkName] + if doc != nil { + info.parsePragmas(doc) + } + } + return info +} + +// Parse //go: pragma comments from the source. In particular, it parses the +// //go:extern pragma on globals. +func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) { + for _, comment := range doc.List { + if !strings.HasPrefix(comment.Text, "//go:") { + continue + } + parts := strings.Fields(comment.Text) + switch parts[0] { + case "//go:extern": + info.extern = true + if len(parts) == 2 { + info.linkName = parts[1] + } + } + } +} diff --git a/ir/ir.go b/ir/ir.go index 5f73b90e19..d9d5764ee3 100644 --- a/ir/ir.go +++ b/ir/ir.go @@ -2,7 +2,6 @@ package ir import ( "go/ast" - "go/token" "go/types" "sort" "strings" @@ -23,9 +22,6 @@ type Program struct { mainPkg *ssa.Package Functions []*Function functionMap map[*ssa.Function]*Function - Globals []*Global - globalMap map[*ssa.Global]*Global - comments map[string]*ast.CommentGroup } // Function or method. @@ -40,15 +36,6 @@ type Function struct { inline InlineType // go:inline } -// Global variable, possibly constant. -type Global struct { - *ssa.Global - program *Program - LLVMGlobal llvm.Value - linkName string // go:extern - extern bool // go:extern -} - // Interface type that is at some point used in a type assert (to check whether // it implements another interface). type Interface struct { @@ -73,32 +60,6 @@ const ( // Create and initialize a new *Program from a *ssa.Program. func NewProgram(lprogram *loader.Program, mainPath string) *Program { - comments := map[string]*ast.CommentGroup{} - for _, pkgInfo := range lprogram.Sorted() { - for _, file := range pkgInfo.Files { - for _, decl := range file.Decls { - switch decl := decl.(type) { - case *ast.GenDecl: - switch decl.Tok { - case token.TYPE, token.VAR: - if len(decl.Specs) != 1 { - continue - } - for _, spec := range decl.Specs { - switch spec := spec.(type) { - case *ast.ValueSpec: // decl.Tok == token.VAR - for _, name := range spec.Names { - id := pkgInfo.Pkg.Path() + "." + name.Name - comments[id] = decl.Doc - } - } - } - } - } - } - } - } - program := lprogram.LoadSSA() program.Build() @@ -170,8 +131,6 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program { LoaderProgram: lprogram, mainPkg: mainPkg, functionMap: make(map[*ssa.Function]*Function), - globalMap: make(map[*ssa.Global]*Global), - comments: comments, } for _, pkg := range packageList { @@ -204,13 +163,7 @@ func (p *Program) AddPackage(pkg *ssa.Package) { } } case *ssa.Global: - g := &Global{program: p, Global: member} - doc := p.comments[g.RelString(nil)] - if doc != nil { - g.parsePragmas(doc) - } - p.Globals = append(p.Globals, g) - p.globalMap[member] = g + // Ignore. Globals are not handled here. case *ssa.NamedConst: // Ignore: these are already resolved. default: @@ -244,10 +197,6 @@ func (p *Program) GetFunction(ssaFn *ssa.Function) *Function { return p.functionMap[ssaFn] } -func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global { - return p.globalMap[ssaGlobal] -} - func (p *Program) MainPkg() *ssa.Package { return p.mainPkg } @@ -370,49 +319,6 @@ func (f *Function) CName() string { return "" } -// Parse //go: pragma comments from the source. -func (g *Global) parsePragmas(doc *ast.CommentGroup) { - for _, comment := range doc.List { - if !strings.HasPrefix(comment.Text, "//go:") { - continue - } - parts := strings.Fields(comment.Text) - switch parts[0] { - case "//go:extern": - g.extern = true - if len(parts) == 2 { - g.linkName = parts[1] - } - } - } -} - -// Return the link name for this global. -func (g *Global) LinkName() string { - if g.linkName != "" { - return g.linkName - } - if name := g.CName(); name != "" { - return name - } - return g.RelString(nil) -} - -func (g *Global) IsExtern() bool { - return g.extern || g.CName() != "" -} - -// Return the name of the C global if this is a CGo wrapper. Otherwise, return a -// zero-length string. -func (g *Global) CName() string { - name := g.Name() - if strings.HasPrefix(name, "C.") { - // created by ../loader/cgo.go - return name[2:] - } - return "" -} - // Get all methods of a type. func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection { ms := prog.MethodSets.MethodSet(typ)