compiler: avoid bitcast when replacing a method call with a direct call

A bitcast was inserted when the receiver of the call wasn't a *i8. This
is a pretty common case, and did not play well with goroutines.
Avoid this bitcast by changing each call to a direct call, after
unpacking the receiver type from the *i8 parameter. This might also fix
some undefined behavior in the resulting program, as it is technically
not allowed to call a function with a different signature (even if the
signature is compatible).
Этот коммит содержится в:
Ayke van Laethem 2019-04-29 00:12:34 +02:00 коммит произвёл Ron Evans
родитель 387e1340bf
коммит 99da328453
3 изменённых файлов: 50 добавлений и 4 удалений

Просмотреть файл

@ -527,11 +527,40 @@ func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInf
} }
inttoptr := inttoptrs[0] inttoptr := inttoptrs[0]
function := typ.getMethod(signature).function function := typ.getMethod(signature).function
if inttoptr.Type() != function.Type() { if inttoptr.Type() == function.Type() {
p.builder.SetInsertPointBefore(use) // Easy case: the types are the same. Simply replace the inttoptr
function = p.builder.CreateBitCast(function, inttoptr.Type(), "") // result (which is directly called) with the actual function.
}
inttoptr.ReplaceAllUsesWith(function) inttoptr.ReplaceAllUsesWith(function)
} else {
// Harder case: the type is not actually the same. Go through each call
// (of which there should be only one), extract the receiver params for
// this call and replace the call with a direct call to the target
// function.
for _, call := range getUses(inttoptr) {
if call.IsACallInst().IsNil() || call.CalledValue() != inttoptr {
panic("expected the inttoptr to be called as a method, this is not a method call")
}
operands := make([]llvm.Value, call.OperandsCount()-1)
for i := range operands {
operands[i] = call.Operand(i)
}
paramTypes := function.Type().ElementType().ParamTypes()
receiverParamTypes := paramTypes[:len(paramTypes)-(len(operands)-1)]
methodParamTypes := paramTypes[len(paramTypes)-(len(operands)-1):]
for i, methodParamType := range methodParamTypes {
if methodParamType != operands[i+1].Type() {
panic("expected method call param type and function param type to be the same")
}
}
p.builder.SetInsertPointBefore(call)
receiverParams := p.emitPointerUnpack(operands[0], receiverParamTypes)
result := p.builder.CreateCall(function, append(receiverParams, operands[1:]...), "")
if result.Type().TypeKind() != llvm.VoidTypeKind {
call.ReplaceAllUsesWith(result)
}
call.EraseFromParentAsInstruction()
}
}
inttoptr.EraseFromParentAsInstruction() inttoptr.EraseFromParentAsInstruction()
use.EraseFromParentAsInstruction() use.EraseFromParentAsInstruction()
} }

16
testdata/coroutines.go предоставленный
Просмотреть файл

@ -21,6 +21,10 @@ func main() {
go nowait() go nowait()
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
println("done with non-blocking goroutine") println("done with non-blocking goroutine")
var printer Printer
printer = &myPrinter{}
printer.Print()
} }
func sub() { func sub() {
@ -38,3 +42,15 @@ func wait() {
func nowait() { func nowait() {
println("non-blocking goroutine") println("non-blocking goroutine")
} }
type Printer interface {
Print()
}
type myPrinter struct{
}
func (i *myPrinter) Print() {
time.Sleep(time.Millisecond)
println("async interface method call")
}

1
testdata/coroutines.txt предоставленный
Просмотреть файл

@ -9,3 +9,4 @@ wait:
end waiting end waiting
non-blocking goroutine non-blocking goroutine
done with non-blocking goroutine done with non-blocking goroutine
async interface method call