Skip to content

Commit 1570ada

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent c72f9eb commit 1570ada

File tree

2 files changed

+47
-51
lines changed

2 files changed

+47
-51
lines changed

transform/interface-lowering.go

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -292,58 +292,40 @@ func (p *lowerInterfacesPass) run() error {
292292

293293
methodSet := use.Operand(1).Operand(0) // global variable
294294
itf := p.interfaces[methodSet.Name()]
295-
if len(itf.types) == 0 {
296-
// This method call is impossible: no type implements this
297-
// interface. In fact, the previous type assert that got this
298-
// interface value should already have returned false.
299-
// Replace the function pointer with undef (which will then be
300-
// called), indicating to the optimizer this code is unreachable.
301-
use.ReplaceAllUsesWith(llvm.Undef(p.uintptrType))
302-
use.EraseFromParentAsInstruction()
303-
} else if len(itf.types) == 1 {
304-
// There is only one implementation of the given type.
305-
// Call that function directly.
306-
err := p.replaceInvokeWithCall(use, itf.types[0], signature)
307-
if err != nil {
308-
return err
309-
}
310-
} else {
311-
// There are multiple types implementing this interface, thus there
312-
// are multiple possible functions to call. Delegate calling the
313-
// right function to a special wrapper function.
314-
inttoptrs := getUses(use)
315-
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
316-
return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod")
295+
296+
// Delegate calling the right function to a special wrapper function.
297+
inttoptrs := getUses(use)
298+
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
299+
return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod")
300+
}
301+
inttoptr := inttoptrs[0]
302+
calls := getUses(inttoptr)
303+
for _, call := range calls {
304+
// Set up parameters for the call. First copy the regular params...
305+
params := make([]llvm.Value, call.OperandsCount())
306+
paramTypes := make([]llvm.Type, len(params))
307+
for i := 0; i < len(params)-1; i++ {
308+
params[i] = call.Operand(i)
309+
paramTypes[i] = params[i].Type()
317310
}
318-
inttoptr := inttoptrs[0]
319-
calls := getUses(inttoptr)
320-
for _, call := range calls {
321-
// Set up parameters for the call. First copy the regular params...
322-
params := make([]llvm.Value, call.OperandsCount())
323-
paramTypes := make([]llvm.Type, len(params))
324-
for i := 0; i < len(params)-1; i++ {
325-
params[i] = call.Operand(i)
326-
paramTypes[i] = params[i].Type()
327-
}
328-
// then add the typecode to the end of the list.
329-
params[len(params)-1] = typecode
330-
paramTypes[len(params)-1] = p.uintptrType
331-
332-
// Create a function that redirects the call to the destination
333-
// call, after selecting the right concrete type.
334-
redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
335-
336-
// Replace the old lookup/inttoptr/call with the new call.
337-
p.builder.SetInsertPointBefore(call)
338-
retval := p.builder.CreateCall(redirector, append(params, llvm.ConstNull(llvm.PointerType(p.ctx.Int8Type(), 0))), "")
339-
if retval.Type().TypeKind() != llvm.VoidTypeKind {
340-
call.ReplaceAllUsesWith(retval)
341-
}
342-
call.EraseFromParentAsInstruction()
311+
// then add the typecode to the end of the list.
312+
params[len(params)-1] = typecode
313+
paramTypes[len(params)-1] = p.uintptrType
314+
315+
// Create a function that redirects the call to the destination
316+
// call, after selecting the right concrete type.
317+
redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
318+
319+
// Replace the old lookup/inttoptr/call with the new call.
320+
p.builder.SetInsertPointBefore(call)
321+
retval := p.builder.CreateCall(redirector, append(params, llvm.ConstNull(llvm.PointerType(p.ctx.Int8Type(), 0))), "")
322+
if retval.Type().TypeKind() != llvm.VoidTypeKind {
323+
call.ReplaceAllUsesWith(retval)
343324
}
344-
inttoptr.EraseFromParentAsInstruction()
345-
use.EraseFromParentAsInstruction()
325+
call.EraseFromParentAsInstruction()
346326
}
327+
inttoptr.EraseFromParentAsInstruction()
328+
use.EraseFromParentAsInstruction()
347329
}
348330

349331
// Replace all typeasserts on interface types with matches on their concrete

transform/testdata/interface.out.ll

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ typeswitch.notUnmatched: ; preds = %0
4747
br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler
4848

4949
typeswitch.Doubler: ; preds = %typeswitch.notUnmatched
50-
%doubler.result = call i32 @"(Number).Double$invoke"(i8* %value, i8* null)
51-
call void @runtime.printint32(i32 %doubler.result)
50+
%1 = call i32 @"(Doubler).Double"(i8* %value, i8* null, i32 %typecode, i8* null)
51+
call void @runtime.printint32(i32 %1)
5252
ret void
5353

5454
typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched
@@ -76,6 +76,20 @@ define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) {
7676
ret i32 %ret
7777
}
7878

79+
define internal i32 @"(Doubler).Double"(i8* %0, i8* %1, i32 %actualType, i8* %parentHandle) unnamed_addr {
80+
entry:
81+
switch i32 %actualType, label %default [
82+
i32 68, label %"reflect/types.type:named:Number"
83+
]
84+
85+
default: ; preds = %entry
86+
unreachable
87+
88+
"reflect/types.type:named:Number": ; preds = %entry
89+
%2 = call i32 @"(Number).Double$invoke"(i8* %0, i8* %1)
90+
ret i32 %2
91+
}
92+
7993
define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr {
8094
entry:
8195
switch i32 %actualType, label %else [

0 commit comments

Comments
 (0)