aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-09-08 03:14:11 +0200
committerRon Evans <[email protected]>2021-09-08 10:02:57 +0200
commit409688e67ab121efe0bf898dd45e3e4bf52b1b80 (patch)
treed46beb0b88c7da55060e12a9b78b06cd2550d83d
parentd348db4a0d3b865f7b5bf7bfd4c3621de96c041a (diff)
downloadtinygo-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.go33
-rw-r--r--compiler/testdata/basic.go15
-rw-r--r--compiler/testdata/basic.ll14
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
+}