aboutsummaryrefslogtreecommitdiffhomepage
path: root/transform
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2020-05-28 01:15:13 +0200
committerRon Evans <[email protected]>2020-05-28 13:42:36 +0200
commit1570adac1c28cf0f492e35fe62af7da6b57e42dc (patch)
treedbb11944ece8872f2dea0ac8a74fb75011b15c55 /transform
parentc72f9eb08ca4a51d33f068820109f9bb9da81338 (diff)
downloadtinygo-1570adac1c28cf0f492e35fe62af7da6b57e42dc.tar.gz
tinygo-1570adac1c28cf0f492e35fe62af7da6b57e42dc.zip
transform: do not special-case zero or one implementations of a method call
This is a common case, but it also complicates the code. Removing this special case does have a negative effect on code size in rare cases, but I don't think it's worth keeping around (and possibly causing bugs) for such uncommon cases. This should not result in functional changes, although the output (as stated above) sometimes changes a little bit.
Diffstat (limited to 'transform')
-rw-r--r--transform/interface-lowering.go80
-rw-r--r--transform/testdata/interface.out.ll18
2 files changed, 47 insertions, 51 deletions
diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go
index 53e248d3a..d64f2731d 100644
--- a/transform/interface-lowering.go
+++ b/transform/interface-lowering.go
@@ -292,58 +292,40 @@ func (p *lowerInterfacesPass) run() error {
methodSet := use.Operand(1).Operand(0) // global variable
itf := p.interfaces[methodSet.Name()]
- if len(itf.types) == 0 {
- // This method call is impossible: no type implements this
- // interface. In fact, the previous type assert that got this
- // interface value should already have returned false.
- // Replace the function pointer with undef (which will then be
- // called), indicating to the optimizer this code is unreachable.
- use.ReplaceAllUsesWith(llvm.Undef(p.uintptrType))
- use.EraseFromParentAsInstruction()
- } else if len(itf.types) == 1 {
- // There is only one implementation of the given type.
- // Call that function directly.
- err := p.replaceInvokeWithCall(use, itf.types[0], signature)
- if err != nil {
- return err
- }
- } else {
- // There are multiple types implementing this interface, thus there
- // are multiple possible functions to call. Delegate calling the
- // right function to a special wrapper function.
- inttoptrs := getUses(use)
- if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
- return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod")
+
+ // Delegate calling the right function to a special wrapper function.
+ inttoptrs := getUses(use)
+ if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
+ return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod")
+ }
+ inttoptr := inttoptrs[0]
+ calls := getUses(inttoptr)
+ for _, call := range calls {
+ // Set up parameters for the call. First copy the regular params...
+ params := make([]llvm.Value, call.OperandsCount())
+ paramTypes := make([]llvm.Type, len(params))
+ for i := 0; i < len(params)-1; i++ {
+ params[i] = call.Operand(i)
+ paramTypes[i] = params[i].Type()
}
- inttoptr := inttoptrs[0]
- calls := getUses(inttoptr)
- for _, call := range calls {
- // Set up parameters for the call. First copy the regular params...
- params := make([]llvm.Value, call.OperandsCount())
- paramTypes := make([]llvm.Type, len(params))
- for i := 0; i < len(params)-1; i++ {
- params[i] = call.Operand(i)
- paramTypes[i] = params[i].Type()
- }
- // then add the typecode to the end of the list.
- params[len(params)-1] = typecode
- paramTypes[len(params)-1] = p.uintptrType
-
- // Create a function that redirects the call to the destination
- // call, after selecting the right concrete type.
- redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
-
- // Replace the old lookup/inttoptr/call with the new call.
- p.builder.SetInsertPointBefore(call)
- retval := p.builder.CreateCall(redirector, append(params, llvm.ConstNull(llvm.PointerType(p.ctx.Int8Type(), 0))), "")
- if retval.Type().TypeKind() != llvm.VoidTypeKind {
- call.ReplaceAllUsesWith(retval)
- }
- call.EraseFromParentAsInstruction()
+ // then add the typecode to the end of the list.
+ params[len(params)-1] = typecode
+ paramTypes[len(params)-1] = p.uintptrType
+
+ // Create a function that redirects the call to the destination
+ // call, after selecting the right concrete type.
+ redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
+
+ // Replace the old lookup/inttoptr/call with the new call.
+ p.builder.SetInsertPointBefore(call)
+ retval := p.builder.CreateCall(redirector, append(params, llvm.ConstNull(llvm.PointerType(p.ctx.Int8Type(), 0))), "")
+ if retval.Type().TypeKind() != llvm.VoidTypeKind {
+ call.ReplaceAllUsesWith(retval)
}
- inttoptr.EraseFromParentAsInstruction()
- use.EraseFromParentAsInstruction()
+ call.EraseFromParentAsInstruction()
}
+ inttoptr.EraseFromParentAsInstruction()
+ use.EraseFromParentAsInstruction()
}
// Replace all typeasserts on interface types with matches on their concrete
diff --git a/transform/testdata/interface.out.ll b/transform/testdata/interface.out.ll
index 9ae8b4886..80f71ee4e 100644
--- a/transform/testdata/interface.out.ll
+++ b/transform/testdata/interface.out.ll
@@ -47,8 +47,8 @@ typeswitch.notUnmatched: ; preds = %0
br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler
typeswitch.Doubler: ; preds = %typeswitch.notUnmatched
- %doubler.result = call i32 @"(Number).Double$invoke"(i8* %value, i8* null)
- call void @runtime.printint32(i32 %doubler.result)
+ %1 = call i32 @"(Doubler).Double"(i8* %value, i8* null, i32 %typecode, i8* null)
+ call void @runtime.printint32(i32 %1)
ret void
typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched
@@ -76,6 +76,20 @@ define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) {
ret i32 %ret
}
+define internal i32 @"(Doubler).Double"(i8* %0, i8* %1, i32 %actualType, i8* %parentHandle) unnamed_addr {
+entry:
+ switch i32 %actualType, label %default [
+ i32 68, label %"reflect/types.type:named:Number"
+ ]
+
+default: ; preds = %entry
+ unreachable
+
+"reflect/types.type:named:Number": ; preds = %entry
+ %2 = call i32 @"(Number).Double$invoke"(i8* %0, i8* %1)
+ ret i32 %2
+}
+
define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr {
entry:
switch i32 %actualType, label %else [