transform: don't rely on struct name of runtime.typecodeID
Sometimes, LLVM may rename named structs when merging modules. Therefore, we can't rely on typecodeID structs to retain their struct names. This commit changes the interface lowering pass to not rely on these names. The interp package does however still rely on this name, but I hope to fix that in the future.
Этот коммит содержится в:
родитель
49ec3eb58e
коммит
61243f6c57
8 изменённых файлов: 21 добавлений и 27 удалений
|
@ -23,7 +23,7 @@ import (
|
||||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||||
// package changes in a way that affects the generated LLVM module.
|
// package changes in a way that affects the generated LLVM module.
|
||||||
// This version is independent of the TinyGo version number.
|
// This version is independent of the TinyGo version number.
|
||||||
const Version = 6 // last change: fix issue 1304
|
const Version = 7 // last change: don't rely on runtime.typecodeID struct type name
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
|
|
|
@ -341,7 +341,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
globalName := "reflect/types.type:" + getTypeCodeName(expr.AssertedType) + "$id"
|
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||||
if assertedTypeCodeGlobal.IsNil() {
|
if assertedTypeCodeGlobal.IsNil() {
|
||||||
// Create a new typecode global.
|
// Create a new typecode global.
|
||||||
|
|
4
compiler/testdata/interface.ll
предоставленный
4
compiler/testdata/interface.ll
предоставленный
|
@ -19,7 +19,7 @@ target triple = "i686--linux"
|
||||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||||
@"func String() string" = external constant i8
|
@"func String() string" = external constant i8
|
||||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
||||||
@"reflect/types.type:basic:int$id" = external constant i8
|
@"reflect/types.typeid:basic:int" = external constant i8
|
||||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
@ -51,7 +51,7 @@ entry:
|
||||||
|
|
||||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.type:basic:int$id", i8* undef, i8* null)
|
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef, i8* null)
|
||||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
typeassert.ok: ; preds = %entry
|
||||||
|
|
|
@ -328,7 +328,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
actualType := actualTypePtrToInt.Operand(0)
|
actualType := actualTypePtrToInt.Operand(0)
|
||||||
if actualType.Name()+"$id" == assertedType.Name() {
|
if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") {
|
||||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||||
} else {
|
} else {
|
||||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||||
|
|
4
interp/testdata/interface.ll
предоставленный
4
interp/testdata/interface.ll
предоставленный
|
@ -6,7 +6,7 @@ target triple = "x86_64--linux"
|
||||||
|
|
||||||
@main.v1 = global i1 0
|
@main.v1 = global i1 0
|
||||||
@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0, %runtime.interfaceMethodInfo* null }
|
@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0, %runtime.interfaceMethodInfo* null }
|
||||||
@"reflect/types.type:named:main.foo$id" = external constant i8
|
@"reflect/types.typeid:named:main.foo" = external constant i8
|
||||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ entry:
|
||||||
define internal void @main.init() unnamed_addr {
|
define internal void @main.init() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
; Test type asserts.
|
; Test type asserts.
|
||||||
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.type:named:main.foo$id", i8* undef, i8* null)
|
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null)
|
||||||
store i1 %typecode, i1* @main.v1
|
store i1 %typecode, i1* @main.v1
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,16 +163,13 @@ func LowerInterfaces(mod llvm.Module, sizeLevel int) error {
|
||||||
// run runs the pass itself.
|
// run runs the pass itself.
|
||||||
func (p *lowerInterfacesPass) run() error {
|
func (p *lowerInterfacesPass) run() error {
|
||||||
// Collect all type codes.
|
// Collect all type codes.
|
||||||
typecodeID := p.mod.GetTypeByName("runtime.typecodeID")
|
|
||||||
typecodeIDPtr := llvm.PointerType(typecodeID, 0)
|
|
||||||
var typecodeIDs []llvm.Value
|
var typecodeIDs []llvm.Value
|
||||||
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||||
switch global.Type() {
|
if strings.HasPrefix(global.Name(), "reflect/types.type:") {
|
||||||
case typecodeIDPtr:
|
|
||||||
// Retrieve Go type information based on an opaque global variable.
|
// Retrieve Go type information based on an opaque global variable.
|
||||||
// Only the name of the global is relevant, the object itself is
|
// Only the name of the global is relevant, the object itself is
|
||||||
// discarded afterwards.
|
// discarded afterwards.
|
||||||
name := global.Name()
|
name := strings.TrimPrefix(global.Name(), "reflect/types.type:")
|
||||||
if _, ok := p.types[name]; !ok {
|
if _, ok := p.types[name]; !ok {
|
||||||
typecodeIDs = append(typecodeIDs, global)
|
typecodeIDs = append(typecodeIDs, global)
|
||||||
t := &typeInfo{
|
t := &typeInfo{
|
||||||
|
@ -196,8 +193,7 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
typeAssertUses := getUses(typeAssert)
|
typeAssertUses := getUses(typeAssert)
|
||||||
for _, use := range typeAssertUses {
|
for _, use := range typeAssertUses {
|
||||||
typecode := use.Operand(1)
|
typecode := use.Operand(1)
|
||||||
name := typecode.Name() // name with $id suffix
|
name := strings.TrimPrefix(typecode.Name(), "reflect/types.typeid:")
|
||||||
name = name[:len(name)-len("$id")] // remove $id suffix
|
|
||||||
if t, ok := p.types[name]; ok {
|
if t, ok := p.types[name]; ok {
|
||||||
t.countTypeAsserts++
|
t.countTypeAsserts++
|
||||||
}
|
}
|
||||||
|
@ -360,7 +356,7 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
if use.IsAConstantExpr().IsNil() {
|
if use.IsAConstantExpr().IsNil() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t := p.types[global.Name()]
|
t := p.types[strings.TrimPrefix(global.Name(), "reflect/types.type:")]
|
||||||
typecode := llvm.ConstInt(p.uintptrType, t.num, false)
|
typecode := llvm.ConstInt(p.uintptrType, t.num, false)
|
||||||
switch use.Opcode() {
|
switch use.Opcode() {
|
||||||
case llvm.PtrToInt:
|
case llvm.PtrToInt:
|
||||||
|
@ -381,8 +377,7 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
llvmFalse := llvm.ConstInt(p.ctx.Int1Type(), 0, false)
|
llvmFalse := llvm.ConstInt(p.ctx.Int1Type(), 0, false)
|
||||||
for _, use := range typeAssertUses {
|
for _, use := range typeAssertUses {
|
||||||
actualType := use.Operand(0)
|
actualType := use.Operand(0)
|
||||||
name := use.Operand(1).Name() // name with $id suffix
|
name := strings.TrimPrefix(use.Operand(1).Name(), "reflect/types.typeid:")
|
||||||
name = name[:len(name)-len("$id")] // remove $id suffix
|
|
||||||
if t, ok := p.types[name]; ok {
|
if t, ok := p.types[name]; ok {
|
||||||
// The type exists in the program, so lower to a regular integer
|
// The type exists in the program, so lower to a regular integer
|
||||||
// comparison.
|
// comparison.
|
||||||
|
@ -423,13 +418,12 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
|
|
||||||
// Remove most objects created for interface and reflect lowering.
|
// Remove most objects created for interface and reflect lowering.
|
||||||
// Unnecessary, but cleans up the IR for inspection and testing.
|
// Unnecessary, but cleans up the IR for inspection and testing.
|
||||||
zeroTypeCode := llvm.ConstNull(typecodeID)
|
|
||||||
for _, typ := range p.types {
|
for _, typ := range p.types {
|
||||||
// Only some typecodes have an initializer.
|
// Only some typecodes have an initializer.
|
||||||
initializer := typ.typecode.Initializer()
|
initializer := typ.typecode.Initializer()
|
||||||
if !initializer.IsNil() {
|
if !initializer.IsNil() {
|
||||||
references := llvm.ConstExtractValue(initializer, []uint32{0})
|
references := llvm.ConstExtractValue(initializer, []uint32{0})
|
||||||
typ.typecode.SetInitializer(zeroTypeCode)
|
typ.typecode.SetInitializer(llvm.ConstNull(initializer.Type()))
|
||||||
if strings.HasPrefix(typ.name, "reflect/types.type:struct:") {
|
if strings.HasPrefix(typ.name, "reflect/types.type:struct:") {
|
||||||
// Structs have a 'references' field that is not a typecode but
|
// Structs have a 'references' field that is not a typecode but
|
||||||
// a pointer to a runtime.structField array and therefore a
|
// a pointer to a runtime.structField array and therefore a
|
||||||
|
|
8
transform/testdata/interface.ll
предоставленный
8
transform/testdata/interface.ll
предоставленный
|
@ -5,8 +5,8 @@ target triple = "armv7m-none-eabi"
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
|
|
||||||
@"reflect/types.type:basic:uint8" = external constant %runtime.typecodeID
|
@"reflect/types.type:basic:uint8" = external constant %runtime.typecodeID
|
||||||
@"reflect/types.type:basic:uint8$id" = external constant i8
|
@"reflect/types.typeid:basic:uint8" = external constant i8
|
||||||
@"reflect/types.type:basic:int16$id" = external constant i8
|
@"reflect/types.typeid:basic:int16" = external constant i8
|
||||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
||||||
@"func NeverImplementedMethod()" = external constant i8
|
@"func NeverImplementedMethod()" = external constant i8
|
||||||
@"Unmatched$interface" = private constant [1 x i8*] [i8* @"func NeverImplementedMethod()"]
|
@"Unmatched$interface" = private constant [1 x i8*] [i8* @"func NeverImplementedMethod()"]
|
||||||
|
@ -55,7 +55,7 @@ typeswitch.Doubler:
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
typeswitch.notDoubler:
|
typeswitch.notDoubler:
|
||||||
%isByte = call i1 @runtime.typeAssert(i32 %typecode, i8* nonnull @"reflect/types.type:basic:uint8$id")
|
%isByte = call i1 @runtime.typeAssert(i32 %typecode, i8* nonnull @"reflect/types.typeid:basic:uint8")
|
||||||
br i1 %isByte, label %typeswitch.byte, label %typeswitch.notByte
|
br i1 %isByte, label %typeswitch.byte, label %typeswitch.notByte
|
||||||
|
|
||||||
typeswitch.byte:
|
typeswitch.byte:
|
||||||
|
@ -66,7 +66,7 @@ typeswitch.byte:
|
||||||
|
|
||||||
typeswitch.notByte:
|
typeswitch.notByte:
|
||||||
; this is a type assert that always fails
|
; this is a type assert that always fails
|
||||||
%isInt16 = call i1 @runtime.typeAssert(i32 %typecode, i8* nonnull @"reflect/types.type:basic:int16$id")
|
%isInt16 = call i1 @runtime.typeAssert(i32 %typecode, i8* nonnull @"reflect/types.typeid:basic:int16")
|
||||||
br i1 %isInt16, label %typeswitch.int16, label %typeswitch.notInt16
|
br i1 %isInt16, label %typeswitch.int16, label %typeswitch.notInt16
|
||||||
|
|
||||||
typeswitch.int16:
|
typeswitch.int16:
|
||||||
|
|
8
transform/testdata/interface.out.ll
предоставленный
8
transform/testdata/interface.out.ll
предоставленный
|
@ -5,8 +5,8 @@ target triple = "armv7m-none-eabi"
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
|
|
||||||
@"reflect/types.type:basic:uint8" = external constant %runtime.typecodeID
|
@"reflect/types.type:basic:uint8" = external constant %runtime.typecodeID
|
||||||
@"reflect/types.type:basic:uint8$id" = external constant i8
|
@"reflect/types.typeid:basic:uint8" = external constant i8
|
||||||
@"reflect/types.type:basic:int16$id" = external constant i8
|
@"reflect/types.typeid:basic:int16" = external constant i8
|
||||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
||||||
@"func NeverImplementedMethod()" = external constant i8
|
@"func NeverImplementedMethod()" = external constant i8
|
||||||
@"func Double() int" = external constant i8
|
@"func Double() int" = external constant i8
|
||||||
|
@ -93,14 +93,14 @@ define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) {
|
||||||
define internal i32 @"(Doubler).Double"(i8* %0, i8* %1, i32 %actualType, i8* %parentHandle) unnamed_addr {
|
define internal i32 @"(Doubler).Double"(i8* %0, i8* %1, i32 %actualType, i8* %parentHandle) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
switch i32 %actualType, label %default [
|
switch i32 %actualType, label %default [
|
||||||
i32 68, label %"reflect/types.type:named:Number"
|
i32 68, label %"named:Number"
|
||||||
]
|
]
|
||||||
|
|
||||||
default: ; preds = %entry
|
default: ; preds = %entry
|
||||||
call void @runtime.nilPanic(i8* undef, i8* undef)
|
call void @runtime.nilPanic(i8* undef, i8* undef)
|
||||||
unreachable
|
unreachable
|
||||||
|
|
||||||
"reflect/types.type:named:Number": ; preds = %entry
|
"named:Number": ; preds = %entry
|
||||||
%2 = call i32 @"(Number).Double$invoke"(i8* %0, i8* %1)
|
%2 = call i32 @"(Number).Double$invoke"(i8* %0, i8* %1)
|
||||||
ret i32 %2
|
ret i32 %2
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче