diff options
author | Ayke van Laethem <[email protected]> | 2021-09-08 03:14:11 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-09-08 10:02:57 +0200 |
commit | 409688e67ab121efe0bf898dd45e3e4bf52b1b80 (patch) | |
tree | d46beb0b88c7da55060e12a9b78b06cd2550d83d | |
parent | d348db4a0d3b865f7b5bf7bfd4c3621de96c041a (diff) | |
download | tinygo-409688e67ab121efe0bf898dd45e3e4bf52b1b80.tar.gz tinygo-409688e67ab121efe0bf898dd45e3e4bf52b1b80.zip |
compiler: fix equally named structs in different scopes
For example, in this code:
type kv struct {
v float32
}
func foo(a *kv) {
type kv struct {
v byte
}
}
Both 'kv' types would be given the same LLVM type, even though they are
different types! This is fixed by only creating a LLVM type once per Go
type (types.Type).
As an added bonus, this change gives a performance improvement of about
0.4%. Not that much, but certainly not nothing for such a small change.
-rw-r--r-- | compiler/compiler.go | 33 | ||||
-rw-r--r-- | compiler/testdata/basic.go | 15 | ||||
-rw-r--r-- | compiler/testdata/basic.ll | 14 |
3 files changed, 52 insertions, 10 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go index 185859dec..c0ad39d46 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -23,7 +23,7 @@ import ( // Version of the compiler pacakge. Must be incremented each time the compiler // package changes in a way that affects the generated LLVM module. // This version is independent of the TinyGo version number. -const Version = 17 // last change: add math.arch* aliases +const Version = 18 // last change: fix duplicated named structs func init() { llvm.InitializeAllTargets() @@ -74,6 +74,7 @@ type compilerContext struct { cu llvm.Metadata difiles map[string]llvm.Metadata ditypes map[types.Type]llvm.Metadata + llvmTypes map[types.Type]llvm.Type machine llvm.TargetMachine targetData llvm.TargetData intType llvm.Type @@ -94,6 +95,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C DumpSSA: dumpSSA, difiles: make(map[string]llvm.Metadata), ditypes: make(map[types.Type]llvm.Metadata), + llvmTypes: make(map[types.Type]llvm.Type), machine: machine, targetData: machine.CreateTargetData(), astComments: map[string]*ast.CommentGroup{}, @@ -315,10 +317,23 @@ func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type { return c.getLLVMType(typ) } -// 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. +// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already +// created types. This is somewhat important for performance, but especially +// important for named struct types (which should only be created once). func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type { + // Try to load the LLVM type from the cache. + if t, ok := c.llvmTypes[goType]; ok { + return t + } + // Not already created, so adding this type to the cache. + llvmType := c.makeLLVMType(goType) + c.llvmTypes[goType] = llvmType + return llvmType +} + +// makeLLVMType creates a LLVM type for a Go type. Don't call this, use +// getLLVMType instead. +func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type { switch typ := goType.(type) { case *types.Array: elemType := c.getLLVMType(typ.Elem()) @@ -367,12 +382,10 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type { // 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() { - llvmType = c.ctx.StructCreateNamed(llvmName) - underlying := c.getLLVMType(st) - llvmType.StructSetBody(underlying.StructElementTypes(), false) - } + llvmType := c.ctx.StructCreateNamed(llvmName) + c.llvmTypes[goType] = llvmType // avoid infinite recursion + underlying := c.getLLVMType(st) + llvmType.StructSetBody(underlying.StructElementTypes(), false) return llvmType } return c.getLLVMType(typ.Underlying()) diff --git a/compiler/testdata/basic.go b/compiler/testdata/basic.go index 3a6367043..ab8b5986a 100644 --- a/compiler/testdata/basic.go +++ b/compiler/testdata/basic.go @@ -55,3 +55,18 @@ func complexMul(x, y complex64) complex64 { } // TODO: complexDiv (requires runtime call) + +// A type 'kv' also exists in function foo. Test that these two types don't +// conflict with each other. +type kv struct { + v float32 +} + +func foo(a *kv) { + // Define a new 'kv' type. + type kv struct { + v byte + } + // Use this type. + func(b *kv) {}(nil) +} diff --git a/compiler/testdata/basic.ll b/compiler/testdata/basic.ll index cdafea0f1..aca2ece0a 100644 --- a/compiler/testdata/basic.ll +++ b/compiler/testdata/basic.ll @@ -3,6 +3,9 @@ source_filename = "basic.go" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32--wasi" +%main.kv = type { float } +%main.kv.0 = type { i8 } + declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr { @@ -98,3 +101,14 @@ entry: %7 = insertvalue { float, float } %6, float %5, 1 ret { float, float } %7 } + +define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + call void @"main.foo$1"(%main.kv.0* null, i8* undef, i8* undef) + ret void +} + +define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context, i8* %parentHandle) unnamed_addr { +entry: + ret void +} |