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.
Этот коммит содержится в:
родитель
c72f9eb08c
коммит
1570adac1c
2 изменённых файлов: 48 добавлений и 52 удалений
|
@ -292,58 +292,40 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
|
|
||||||
methodSet := use.Operand(1).Operand(0) // global variable
|
methodSet := use.Operand(1).Operand(0) // global variable
|
||||||
itf := p.interfaces[methodSet.Name()]
|
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")
|
|
||||||
}
|
|
||||||
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
|
// Delegate calling the right function to a special wrapper function.
|
||||||
// call, after selecting the right concrete type.
|
inttoptrs := getUses(use)
|
||||||
redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
|
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
|
||||||
|
return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod")
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
inttoptr.EraseFromParentAsInstruction()
|
|
||||||
use.EraseFromParentAsInstruction()
|
|
||||||
}
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
inttoptr.EraseFromParentAsInstruction()
|
||||||
|
use.EraseFromParentAsInstruction()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace all typeasserts on interface types with matches on their concrete
|
// Replace all typeasserts on interface types with matches on their concrete
|
||||||
|
|
18
transform/testdata/interface.out.ll
предоставленный
18
transform/testdata/interface.out.ll
предоставленный
|
@ -47,8 +47,8 @@ typeswitch.notUnmatched: ; preds = %0
|
||||||
br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler
|
br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler
|
||||||
|
|
||||||
typeswitch.Doubler: ; preds = %typeswitch.notUnmatched
|
typeswitch.Doubler: ; preds = %typeswitch.notUnmatched
|
||||||
%doubler.result = call i32 @"(Number).Double$invoke"(i8* %value, i8* null)
|
%1 = call i32 @"(Doubler).Double"(i8* %value, i8* null, i32 %typecode, i8* null)
|
||||||
call void @runtime.printint32(i32 %doubler.result)
|
call void @runtime.printint32(i32 %1)
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched
|
typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched
|
||||||
|
@ -76,6 +76,20 @@ define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) {
|
||||||
ret i32 %ret
|
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 {
|
define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
switch i32 %actualType, label %else [
|
switch i32 %actualType, label %else [
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче