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).
Этот коммит содержится в:
родитель
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)
|
||||||
|
} 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.ReplaceAllUsesWith(function)
|
|
||||||
inttoptr.EraseFromParentAsInstruction()
|
inttoptr.EraseFromParentAsInstruction()
|
||||||
use.EraseFromParentAsInstruction()
|
use.EraseFromParentAsInstruction()
|
||||||
}
|
}
|
||||||
|
|
16
testdata/coroutines.go
предоставленный
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
предоставленный
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
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче