aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-11-13 20:14:39 +0100
committerAyke <[email protected]>2019-11-15 23:37:17 +0100
commite20af665fadb62cfda7c5b174ffed4d7b73c94b3 (patch)
treef51aa99049bbdbc9e7dd2b79e4b2c906a0f30d86
parent36d119811534752904dad8a369e5587d5750442f (diff)
downloadtinygo-e20af665fadb62cfda7c5b174ffed4d7b73c94b3.tar.gz
tinygo-e20af665fadb62cfda7c5b174ffed4d7b73c94b3.zip
compiler,transform: move interface lowering to transform package
-rw-r--r--compiler/llvm.go39
-rw-r--r--compiler/llvmutil/wordpack.go12
-rw-r--r--compiler/optimizer.go4
-rw-r--r--transform/interface-lowering.go (renamed from compiler/interface-lowering.go)43
-rw-r--r--transform/interface-lowering_test.go10
-rw-r--r--transform/llvm.go50
-rw-r--r--transform/reflect.go (renamed from compiler/reflect.go)24
-rw-r--r--transform/testdata/interface.ll80
-rw-r--r--transform/testdata/interface.out.ll102
9 files changed, 295 insertions, 69 deletions
diff --git a/compiler/llvm.go b/compiler/llvm.go
index 3bd9eeee1..078956657 100644
--- a/compiler/llvm.go
+++ b/compiler/llvm.go
@@ -1,8 +1,6 @@
package compiler
import (
- "reflect"
-
"github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm"
)
@@ -130,43 +128,14 @@ func (c *Compiler) splitBasicBlock(afterInst llvm.Value, insertAfter llvm.BasicB
// contents, and returns the global.
// Note that it is left with the default linkage etc., you should set
// linkage/constant/etc properties yourself.
-func (c *Compiler) makeGlobalArray(bufItf interface{}, name string, elementType llvm.Type) llvm.Value {
- buf := reflect.ValueOf(bufItf)
- globalType := llvm.ArrayType(elementType, buf.Len())
+func (c *Compiler) makeGlobalArray(buf []byte, name string, elementType llvm.Type) llvm.Value {
+ globalType := llvm.ArrayType(elementType, len(buf))
global := llvm.AddGlobal(c.mod, globalType, name)
value := llvm.Undef(globalType)
- for i := 0; i < buf.Len(); i++ {
- ch := buf.Index(i).Uint()
+ for i := 0; i < len(buf); i++ {
+ ch := uint64(buf[i])
value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
}
global.SetInitializer(value)
return global
}
-
-// getGlobalBytes returns the slice contained in the array of the provided
-// global. It can recover the bytes originally created using makeGlobalArray, if
-// makeGlobalArray was given a byte slice.
-func getGlobalBytes(global llvm.Value) []byte {
- value := global.Initializer()
- buf := make([]byte, value.Type().ArrayLength())
- for i := range buf {
- buf[i] = byte(llvm.ConstExtractValue(value, []uint32{uint32(i)}).ZExtValue())
- }
- return buf
-}
-
-// replaceGlobalByteWithArray replaces a global integer type in the module with
-// an integer array, using a GEP to make the types match. It is a convenience
-// function used for creating reflection sidetables, for example.
-func (c *Compiler) replaceGlobalIntWithArray(name string, buf interface{}) llvm.Value {
- oldGlobal := c.mod.NamedGlobal(name)
- global := c.makeGlobalArray(buf, name+".tmp", oldGlobal.Type().ElementType())
- gep := llvm.ConstGEP(global, []llvm.Value{
- llvm.ConstInt(c.ctx.Int32Type(), 0, false),
- llvm.ConstInt(c.ctx.Int32Type(), 0, false),
- })
- oldGlobal.ReplaceAllUsesWith(gep)
- oldGlobal.EraseFromParentAsGlobal()
- global.SetName(name)
- return global
-}
diff --git a/compiler/llvmutil/wordpack.go b/compiler/llvmutil/wordpack.go
index 1cbada7ee..0c1c951f8 100644
--- a/compiler/llvmutil/wordpack.go
+++ b/compiler/llvmutil/wordpack.go
@@ -45,10 +45,18 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
// Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(uintptrType, size, false)
alloc := mod.NamedFunction("runtime.alloc")
- packedHeapAlloc = builder.CreateCall(alloc, []llvm.Value{sizeValue}, "")
+ packedHeapAlloc = builder.CreateCall(alloc, []llvm.Value{
+ sizeValue,
+ llvm.Undef(i8ptrType), // unused context parameter
+ llvm.ConstPointerNull(i8ptrType), // coroutine handle
+ }, "")
if config.NeedsStackObjects() {
trackPointer := mod.NamedFunction("runtime.trackPointer")
- builder.CreateCall(trackPointer, []llvm.Value{packedHeapAlloc}, "")
+ builder.CreateCall(trackPointer, []llvm.Value{
+ packedHeapAlloc,
+ llvm.Undef(i8ptrType), // unused context parameter
+ llvm.ConstPointerNull(i8ptrType), // coroutine handle
+ }, "")
}
packedAlloc = builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
}
diff --git a/compiler/optimizer.go b/compiler/optimizer.go
index c7fd0ba3a..0b319e3da 100644
--- a/compiler/optimizer.go
+++ b/compiler/optimizer.go
@@ -55,7 +55,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
transform.OptimizeMaps(c.mod)
transform.OptimizeStringToBytes(c.mod)
transform.OptimizeAllocs(c.mod)
- c.LowerInterfaces()
+ transform.LowerInterfaces(c.mod)
c.LowerFuncValues()
// After interfaces are lowered, there are many more opportunities for
@@ -89,7 +89,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
}
} else {
// Must be run at any optimization level.
- c.LowerInterfaces()
+ transform.LowerInterfaces(c.mod)
c.LowerFuncValues()
err := c.LowerGoroutines()
if err != nil {
diff --git a/compiler/interface-lowering.go b/transform/interface-lowering.go
index 1aa9862f1..b75054aac 100644
--- a/compiler/interface-lowering.go
+++ b/transform/interface-lowering.go
@@ -1,4 +1,4 @@
-package compiler
+package transform
// This file provides function to lower interface intrinsics to their final LLVM
// form, optimizing them in the process.
@@ -38,6 +38,7 @@ import (
"sort"
"strings"
+ "github.com/tinygo-org/tinygo/compiler/llvmutil"
"tinygo.org/x/go-llvm"
)
@@ -133,22 +134,28 @@ func (itf *interfaceInfo) id() string {
// pass has been implemented as an object type because of its complexity, but
// should be seen as a regular function call (see LowerInterfaces).
type lowerInterfacesPass struct {
- *Compiler
- types map[string]*typeInfo
- signatures map[string]*signatureInfo
- interfaces map[string]*interfaceInfo
+ mod llvm.Module
+ builder llvm.Builder
+ ctx llvm.Context
+ uintptrType llvm.Type
+ types map[string]*typeInfo
+ signatures map[string]*signatureInfo
+ interfaces map[string]*interfaceInfo
}
-// Lower all interface functions. They are emitted by the compiler as
-// higher-level intrinsics that need some lowering before LLVM can work on them.
-// This is done so that a few cleanup passes can run before assigning the final
-// type codes.
-func (c *Compiler) LowerInterfaces() {
+// LowerInterfaces lowers all intermediate interface calls and globals that are
+// emitted by the compiler as higher-level intrinsics. They need some lowering
+// before LLVM can work on them. This is done so that a few cleanup passes can
+// run before assigning the final type codes.
+func LowerInterfaces(mod llvm.Module) {
p := &lowerInterfacesPass{
- Compiler: c,
- types: make(map[string]*typeInfo),
- signatures: make(map[string]*signatureInfo),
- interfaces: make(map[string]*interfaceInfo),
+ mod: mod,
+ builder: mod.Context().NewBuilder(),
+ ctx: mod.Context(),
+ uintptrType: mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8),
+ types: make(map[string]*typeInfo),
+ signatures: make(map[string]*signatureInfo),
+ interfaces: make(map[string]*interfaceInfo),
}
p.run()
}
@@ -156,8 +163,8 @@ func (c *Compiler) LowerInterfaces() {
// run runs the pass itself.
func (p *lowerInterfacesPass) run() {
// Collect all type codes.
- typecodeIDPtr := llvm.PointerType(p.getLLVMRuntimeType("typecodeID"), 0)
- typeInInterfacePtr := llvm.PointerType(p.getLLVMRuntimeType("typeInInterface"), 0)
+ typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0)
+ typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0)
var typesInInterfaces []llvm.Value
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
switch global.Type() {
@@ -368,7 +375,7 @@ func (p *lowerInterfacesPass) run() {
}
// Assign a type code for each type.
- p.assignTypeCodes(typeSlice)
+ assignTypeCodes(p.mod, typeSlice)
// Replace each use of a runtime.typeInInterface with the constant type
// code.
@@ -519,7 +526,7 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
}
}
p.builder.SetInsertPointBefore(call)
- receiverParams := p.emitPointerUnpack(operands[0], receiverParamTypes)
+ receiverParams := llvmutil.EmitPointerUnpack(p.builder, p.mod, operands[0], receiverParamTypes)
result := p.builder.CreateCall(function, append(receiverParams, operands[1:]...), "")
if result.Type().TypeKind() != llvm.VoidTypeKind {
call.ReplaceAllUsesWith(result)
diff --git a/transform/interface-lowering_test.go b/transform/interface-lowering_test.go
new file mode 100644
index 000000000..2bc8ed643
--- /dev/null
+++ b/transform/interface-lowering_test.go
@@ -0,0 +1,10 @@
+package transform
+
+import (
+ "testing"
+)
+
+func TestInterfaceLowering(t *testing.T) {
+ t.Parallel()
+ testTransform(t, "testdata/interface", LowerInterfaces)
+}
diff --git a/transform/llvm.go b/transform/llvm.go
index 54e5aad58..f41d2f9c3 100644
--- a/transform/llvm.go
+++ b/transform/llvm.go
@@ -1,12 +1,17 @@
package transform
import (
+ "reflect"
+
"tinygo.org/x/go-llvm"
)
// Return a list of values (actually, instructions) where this value is used as
// an operand.
func getUses(value llvm.Value) []llvm.Value {
+ if value.IsNil() {
+ return nil
+ }
var uses []llvm.Value
use := value.FirstUse()
for !use.IsNil() {
@@ -15,3 +20,48 @@ func getUses(value llvm.Value) []llvm.Value {
}
return uses
}
+
+// makeGlobalArray creates a new LLVM global with the given name and integers as
+// contents, and returns the global.
+// Note that it is left with the default linkage etc., you should set
+// linkage/constant/etc properties yourself.
+func makeGlobalArray(mod llvm.Module, bufItf interface{}, name string, elementType llvm.Type) llvm.Value {
+ buf := reflect.ValueOf(bufItf)
+ globalType := llvm.ArrayType(elementType, buf.Len())
+ global := llvm.AddGlobal(mod, globalType, name)
+ value := llvm.Undef(globalType)
+ for i := 0; i < buf.Len(); i++ {
+ ch := buf.Index(i).Uint()
+ value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
+ }
+ global.SetInitializer(value)
+ return global
+}
+
+// getGlobalBytes returns the slice contained in the array of the provided
+// global. It can recover the bytes originally created using makeGlobalArray, if
+// makeGlobalArray was given a byte slice.
+func getGlobalBytes(global llvm.Value) []byte {
+ value := global.Initializer()
+ buf := make([]byte, value.Type().ArrayLength())
+ for i := range buf {
+ buf[i] = byte(llvm.ConstExtractValue(value, []uint32{uint32(i)}).ZExtValue())
+ }
+ return buf
+}
+
+// replaceGlobalByteWithArray replaces a global integer type in the module with
+// an integer array, using a GEP to make the types match. It is a convenience
+// function used for creating reflection sidetables, for example.
+func replaceGlobalIntWithArray(mod llvm.Module, name string, buf interface{}) llvm.Value {
+ oldGlobal := mod.NamedGlobal(name)
+ global := makeGlobalArray(mod, buf, name+".tmp", oldGlobal.Type().ElementType())
+ gep := llvm.ConstGEP(global, []llvm.Value{
+ llvm.ConstInt(mod.Context().Int32Type(), 0, false),
+ llvm.ConstInt(mod.Context().Int32Type(), 0, false),
+ })
+ oldGlobal.ReplaceAllUsesWith(gep)
+ oldGlobal.EraseFromParentAsGlobal()
+ global.SetName(name)
+ return global
+}
diff --git a/compiler/reflect.go b/transform/reflect.go
index 8c0c82cec..9bfd16904 100644
--- a/compiler/reflect.go
+++ b/transform/reflect.go
@@ -1,4 +1,4 @@
-package compiler
+package transform
// This file has some compiler support for run-time reflection using the reflect
// package. In particular, it encodes type information in type codes in such a
@@ -125,27 +125,27 @@ type typeCodeAssignmentState struct {
// assignTypeCodes is used to assign a type code to each type in the program
// that is ever stored in an interface. It tries to use the smallest possible
// numbers to make the code that works with interfaces as small as possible.
-func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
+func assignTypeCodes(mod llvm.Module, typeSlice typeInfoSlice) {
// if reflect were not used, we could skip generating the sidetable
// this does not help in practice, and is difficult to do correctly
// Assign typecodes the way the reflect package expects.
state := typeCodeAssignmentState{
fallbackIndex: 1,
- uintptrLen: c.uintptrType.IntTypeWidth(),
+ uintptrLen: llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8,
namedBasicTypes: make(map[string]int),
namedNonBasicTypes: make(map[string]int),
arrayTypes: make(map[string]int),
structTypes: make(map[string]int),
structNames: make(map[string]int),
- needsNamedNonBasicTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0,
- needsStructTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.structTypesSidetable"))) != 0,
- needsStructNamesSidetable: len(getUses(c.mod.NamedGlobal("reflect.structNamesSidetable"))) != 0,
- needsArrayTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.arrayTypesSidetable"))) != 0,
+ needsNamedNonBasicTypesSidetable: len(getUses(mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0,
+ needsStructTypesSidetable: len(getUses(mod.NamedGlobal("reflect.structTypesSidetable"))) != 0,
+ needsStructNamesSidetable: len(getUses(mod.NamedGlobal("reflect.structNamesSidetable"))) != 0,
+ needsArrayTypesSidetable: len(getUses(mod.NamedGlobal("reflect.arrayTypesSidetable"))) != 0,
}
for _, t := range typeSlice {
num := state.getTypeCodeNum(t.typecode)
- if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() {
+ if num.BitLen() > state.uintptrLen || !num.IsUint64() {
// TODO: support this in some way, using a side table for example.
// That's less efficient but better than not working at all.
// Particularly important on systems with 16-bit pointers (e.g.
@@ -157,22 +157,22 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
// Only create this sidetable when it is necessary.
if state.needsNamedNonBasicTypesSidetable {
- global := c.replaceGlobalIntWithArray("reflect.namedNonBasicTypesSidetable", state.namedNonBasicTypesSidetable)
+ global := replaceGlobalIntWithArray(mod, "reflect.namedNonBasicTypesSidetable", state.namedNonBasicTypesSidetable)
global.SetLinkage(llvm.InternalLinkage)
global.SetUnnamedAddr(true)
}
if state.needsArrayTypesSidetable {
- global := c.replaceGlobalIntWithArray("reflect.arrayTypesSidetable", state.arrayTypesSidetable)
+ global := replaceGlobalIntWithArray(mod, "reflect.arrayTypesSidetable", state.arrayTypesSidetable)
global.SetLinkage(llvm.InternalLinkage)
global.SetUnnamedAddr(true)
}
if state.needsStructTypesSidetable {
- global := c.replaceGlobalIntWithArray("reflect.structTypesSidetable", state.structTypesSidetable)
+ global := replaceGlobalIntWithArray(mod, "reflect.structTypesSidetable", state.structTypesSidetable)
global.SetLinkage(llvm.InternalLinkage)
global.SetUnnamedAddr(true)
}
if state.needsStructNamesSidetable {
- global := c.replaceGlobalIntWithArray("reflect.structNamesSidetable", state.structNamesSidetable)
+ global := replaceGlobalIntWithArray(mod, "reflect.structNamesSidetable", state.structNamesSidetable)
global.SetLinkage(llvm.InternalLinkage)
global.SetUnnamedAddr(true)
}
diff --git a/transform/testdata/interface.ll b/transform/testdata/interface.ll
new file mode 100644
index 000000000..dd00670df
--- /dev/null
+++ b/transform/testdata/interface.ll
@@ -0,0 +1,80 @@
+target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "armv7m-none-eabi"
+
+%runtime.typecodeID = type { %runtime.typecodeID*, i32 }
+%runtime.typeInInterface = type { %runtime.typecodeID*, %runtime.interfaceMethodInfo* }
+%runtime.interfaceMethodInfo = type { i8*, i32 }
+
+@"reflect/types.type:basic:uint8" = external constant %runtime.typecodeID
+@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
+@"typeInInterface:reflect/types.type:basic:uint8" = private constant %runtime.typeInInterface { %runtime.typecodeID* @"reflect/types.type:basic:uint8", %runtime.interfaceMethodInfo* null }
+@"typeInInterface:reflect/types.type:basic:int" = private constant %runtime.typeInInterface { %runtime.typecodeID* @"reflect/types.type:basic:int", %runtime.interfaceMethodInfo* null }
+@"func NeverImplementedMethod()" = external constant i8
+@"Unmatched$interface" = private constant [1 x i8*] [i8* @"func NeverImplementedMethod()"]
+@"func Double() int" = external constant i8
+@"Doubler$interface" = private constant [1 x i8*] [i8* @"func Double() int"]
+@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"func Double() int", i32 ptrtoint (i32 (i8*)* @"(Number).Double$invoke" to i32) }]
+@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0 }
+@"typeInInterface:reflect/types.type:named:Number" = private constant %runtime.typeInInterface { %runtime.typecodeID* @"reflect/types.type:named:Number", %runtime.interfaceMethodInfo* getelementptr inbounds ([1 x %runtime.interfaceMethodInfo], [1 x %runtime.interfaceMethodInfo]* @"Number$methodset", i32 0, i32 0) }
+
+declare i1 @runtime.interfaceImplements(i32, i8**)
+declare i1 @runtime.typeAssert(i32, %runtime.typecodeID*)
+declare i32 @runtime.interfaceMethod(i32, i8**, i8*)
+declare void @runtime.printuint8(i8)
+declare void @runtime.printint32(i32)
+declare void @runtime.printptr(i32)
+declare void @runtime.printnl()
+
+define void @printInterfaces() {
+ call void @printInterface(i32 ptrtoint (%runtime.typeInInterface* @"typeInInterface:reflect/types.type:basic:int" to i32), i8* inttoptr (i32 5 to i8*))
+ call void @printInterface(i32 ptrtoint (%runtime.typeInInterface* @"typeInInterface:reflect/types.type:basic:uint8" to i32), i8* inttoptr (i8 120 to i8*))
+ call void @printInterface(i32 ptrtoint (%runtime.typeInInterface* @"typeInInterface:reflect/types.type:named:Number" to i32), i8* inttoptr (i32 3 to i8*))
+
+ ret void
+}
+
+define void @printInterface(i32 %typecode, i8* %value) {
+ %isUnmatched = call i1 @runtime.interfaceImplements(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Unmatched$interface", i32 0, i32 0))
+ br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
+
+typeswitch.Unmatched:
+ %unmatched = ptrtoint i8* %value to i32
+ call void @runtime.printptr(i32 %unmatched)
+ call void @runtime.printnl()
+ ret void
+
+typeswitch.notUnmatched:
+ %isDoubler = call i1 @runtime.interfaceImplements(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Doubler$interface", i32 0, i32 0))
+ br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
+
+typeswitch.Doubler:
+ %doubler.func = call i32 @runtime.interfaceMethod(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Doubler$interface", i32 0, i32 0), i8* nonnull @"func Double() int")
+ %doubler.func.cast = inttoptr i32 %doubler.func to i32 (i8*)*
+ %doubler.result = call i32 %doubler.func.cast(i8* %value)
+ call void @runtime.printint32(i32 %doubler.result)
+ ret void
+
+typeswitch.notDoubler:
+ %isByte = call i1 @runtime.typeAssert(i32 %typecode, %runtime.typecodeID* nonnull @"reflect/types.type:basic:uint8")
+ br i1 %isByte, label %typeswitch.byte, label %typeswitch.notByte
+
+typeswitch.byte:
+ %byte = ptrtoint i8* %value to i8
+ call void @runtime.printuint8(i8 %byte)
+ call void @runtime.printnl()
+ ret void
+
+typeswitch.notByte:
+ ret void
+}
+
+define i32 @"(Number).Double"(i32 %receiver) {
+ %ret = mul i32 %receiver, 2
+ ret i32 %ret
+}
+
+define i32 @"(Number).Double$invoke"(i8* %receiverPtr) {
+ %receiver = ptrtoint i8* %receiverPtr to i32
+ %ret = call i32 @"(Number).Double"(i32 %receiver)
+ ret i32 %ret
+}
diff --git a/transform/testdata/interface.out.ll b/transform/testdata/interface.out.ll
new file mode 100644
index 000000000..d4828cf57
--- /dev/null
+++ b/transform/testdata/interface.out.ll
@@ -0,0 +1,102 @@
+target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "armv7m-none-eabi"
+
+%runtime.typecodeID = type { %runtime.typecodeID*, i32 }
+
+@"reflect/types.type:basic:uint8" = external constant %runtime.typecodeID
+@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
+@"func NeverImplementedMethod()" = external constant i8
+@"Unmatched$interface" = private constant [1 x i8*] [i8* @"func NeverImplementedMethod()"]
+@"func Double() int" = external constant i8
+@"Doubler$interface" = private constant [1 x i8*] [i8* @"func Double() int"]
+@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0 }
+
+declare i1 @runtime.interfaceImplements(i32, i8**)
+
+declare i1 @runtime.typeAssert(i32, %runtime.typecodeID*)
+
+declare i32 @runtime.interfaceMethod(i32, i8**, i8*)
+
+declare void @runtime.printuint8(i8)
+
+declare void @runtime.printint32(i32)
+
+declare void @runtime.printptr(i32)
+
+declare void @runtime.printnl()
+
+define void @printInterfaces() {
+ call void @printInterface(i32 4, i8* inttoptr (i32 5 to i8*))
+ call void @printInterface(i32 16, i8* inttoptr (i8 120 to i8*))
+ call void @printInterface(i32 68, i8* inttoptr (i32 3 to i8*))
+ ret void
+}
+
+define void @printInterface(i32 %typecode, i8* %value) {
+ %typeassert.ok1 = call i1 @"Unmatched$typeassert"(i32 %typecode)
+ br i1 %typeassert.ok1, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
+
+typeswitch.Unmatched:
+ %unmatched = ptrtoint i8* %value to i32
+ call void @runtime.printptr(i32 %unmatched)
+ call void @runtime.printnl()
+ ret void
+
+typeswitch.notUnmatched:
+ %typeassert.ok = call i1 @"Doubler$typeassert"(i32 %typecode)
+ br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler
+
+typeswitch.Doubler:
+ %doubler.result = call i32 @"(Number).Double$invoke"(i8* %value)
+ call void @runtime.printint32(i32 %doubler.result)
+ ret void
+
+typeswitch.notDoubler:
+ %typeassert.ok2 = icmp eq i32 16, %typecode
+ br i1 %typeassert.ok2, label %typeswitch.byte, label %typeswitch.notByte
+
+typeswitch.byte:
+ %byte = ptrtoint i8* %value to i8
+ call void @runtime.printuint8(i8 %byte)
+ call void @runtime.printnl()
+ ret void
+
+typeswitch.notByte:
+ ret void
+}
+
+define i32 @"(Number).Double"(i32 %receiver) {
+ %ret = mul i32 %receiver, 2
+ ret i32 %ret
+}
+
+define i32 @"(Number).Double$invoke"(i8* %receiverPtr) {
+ %receiver = ptrtoint i8* %receiverPtr to i32
+ %ret = call i32 @"(Number).Double"(i32 %receiver)
+ ret i32 %ret
+}
+
+define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr {
+entry:
+ switch i32 %actualType, label %else [
+ i32 68, label %then
+ ]
+
+then:
+ ret i1 true
+
+else:
+ ret i1 false
+}
+
+define internal i1 @"Unmatched$typeassert"(i32 %actualType) unnamed_addr {
+entry:
+ switch i32 %actualType, label %else [
+ ]
+
+then:
+ ret i1 true
+
+else:
+ ret i1 false
+}