compiler,transform: move interface lowering to transform package
Этот коммит содержится в:
родитель
36d1198115
коммит
e20af665fa
9 изменённых файлов: 295 добавлений и 69 удалений
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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), "")
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
10
transform/interface-lowering_test.go
Обычный файл
10
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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
80
transform/testdata/interface.ll
предоставленный
Обычный файл
80
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
|
||||
}
|
102
transform/testdata/interface.out.ll
предоставленный
Обычный файл
102
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
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче