compiler: pass interface typecode through defer frames
Previously, the typecode was passed via a direct reference, which results in invalid IR when the defer is not reached in all return paths. It also results in incorrect behavior if the defer is in a loop, causing all defers to use the typecode of the last iteration.
Этот коммит содержится в:
родитель
e077e35386
коммит
5308e8903e
3 изменённых файлов: 57 добавлений и 13 удалений
|
@ -92,9 +92,10 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||||
// Collect all values to be put in the struct (starting with
|
// Collect all values to be put in the struct (starting with
|
||||||
// runtime._defer fields, followed by the call parameters).
|
// runtime._defer fields, followed by the call parameters).
|
||||||
itf := b.getValue(instr.Call.Value) // interface
|
itf := b.getValue(instr.Call.Value) // interface
|
||||||
|
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||||
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||||
values = []llvm.Value{callback, next, receiverValue}
|
values = []llvm.Value{callback, next, typecode, receiverValue}
|
||||||
valueTypes = append(valueTypes, b.i8ptrType)
|
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
|
||||||
for _, arg := range instr.Call.Args {
|
for _, arg := range instr.Call.Args {
|
||||||
val := b.getValue(arg)
|
val := b.getValue(arg)
|
||||||
values = append(values, val)
|
values = append(values, val)
|
||||||
|
@ -248,7 +249,7 @@ func (b *builder) createRunDefers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0), b.i8ptrType}
|
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0), b.uintptrType, b.i8ptrType}
|
||||||
for _, arg := range callback.Args {
|
for _, arg := range callback.Args {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
||||||
}
|
}
|
||||||
|
@ -264,6 +265,9 @@ func (b *builder) createRunDefers() {
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Isolate the typecode.
|
||||||
|
typecode, forwardParams := forwardParams[0], forwardParams[1:]
|
||||||
|
|
||||||
// Add the context parameter. An interface call cannot also be a
|
// Add the context parameter. An interface call cannot also be a
|
||||||
// closure but we have to supply the parameter anyway for platforms
|
// closure but we have to supply the parameter anyway for platforms
|
||||||
// with a strict calling convention.
|
// with a strict calling convention.
|
||||||
|
@ -272,7 +276,7 @@ func (b *builder) createRunDefers() {
|
||||||
// Parent coroutine handle.
|
// Parent coroutine handle.
|
||||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||||
|
|
||||||
fnPtr, _ := b.getInvokeCall(callback)
|
fnPtr := b.getInvokePtr(callback, typecode)
|
||||||
b.createCall(fnPtr, forwardParams, "")
|
b.createCall(fnPtr, forwardParams, "")
|
||||||
|
|
||||||
case *ir.Function:
|
case *ir.Function:
|
||||||
|
|
|
@ -396,22 +396,26 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInvokeCall creates and returns the function pointer and parameters of an
|
// getInvokePtr creates an interface function pointer lookup for the specified invoke instruction, using a specified typecode.
|
||||||
// interface call. It can be used in a call or defer instruction.
|
func (b *builder) getInvokePtr(instr *ssa.CallCommon, typecode llvm.Value) llvm.Value {
|
||||||
func (b *builder) getInvokeCall(instr *ssa.CallCommon) (llvm.Value, []llvm.Value) {
|
|
||||||
// Call an interface method with dynamic dispatch.
|
|
||||||
itf := b.getValue(instr.Value) // interface
|
|
||||||
|
|
||||||
llvmFnType := b.getRawFuncType(instr.Method.Type().(*types.Signature))
|
llvmFnType := b.getRawFuncType(instr.Method.Type().(*types.Signature))
|
||||||
|
|
||||||
typecode := b.CreateExtractValue(itf, 0, "invoke.typecode")
|
|
||||||
values := []llvm.Value{
|
values := []llvm.Value{
|
||||||
typecode,
|
typecode,
|
||||||
b.getInterfaceMethodSet(instr.Value.Type()),
|
b.getInterfaceMethodSet(instr.Value.Type()),
|
||||||
b.getMethodSignature(instr.Method),
|
b.getMethodSignature(instr.Method),
|
||||||
}
|
}
|
||||||
fn := b.createRuntimeCall("interfaceMethod", values, "invoke.func")
|
fn := b.createRuntimeCall("interfaceMethod", values, "invoke.func")
|
||||||
fnCast := b.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast")
|
return b.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInvokeCall creates and returns the function pointer and parameters of an
|
||||||
|
// interface call.
|
||||||
|
func (b *builder) getInvokeCall(instr *ssa.CallCommon) (llvm.Value, []llvm.Value) {
|
||||||
|
// Call an interface method with dynamic dispatch.
|
||||||
|
itf := b.getValue(instr.Value) // interface
|
||||||
|
|
||||||
|
typecode := b.CreateExtractValue(itf, 0, "invoke.typecode")
|
||||||
|
fnCast := b.getInvokePtr(instr, typecode)
|
||||||
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||||
|
|
||||||
args := []llvm.Value{receiverValue}
|
args := []llvm.Value{receiverValue}
|
||||||
|
|
36
testdata/calls.go
предоставленный
36
testdata/calls.go
предоставленный
|
@ -61,6 +61,9 @@ func main() {
|
||||||
thingFunctionalArgs1.Print("functional args 1")
|
thingFunctionalArgs1.Print("functional args 1")
|
||||||
thingFunctionalArgs2 := NewThing(WithName("named thing"))
|
thingFunctionalArgs2 := NewThing(WithName("named thing"))
|
||||||
thingFunctionalArgs2.Print("functional args 2")
|
thingFunctionalArgs2.Print("functional args 2")
|
||||||
|
|
||||||
|
// regression testing
|
||||||
|
regression1033()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFunc(f func(int), arg int) {
|
func runFunc(f func(int), arg int) {
|
||||||
|
@ -108,3 +111,36 @@ func exportedDefer() {
|
||||||
func testBound(f func() string) {
|
func testBound(f func() string) {
|
||||||
println("bound method:", f())
|
println("bound method:", f())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// regression1033 is a regression test for https://github.com/tinygo-org/tinygo/issues/1033.
|
||||||
|
// In previous versions of the compiler, a deferred call to an interface would create an instruction that did not dominate its uses.
|
||||||
|
func regression1033() {
|
||||||
|
foo(&Bar{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
empty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bar) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Closer interface {
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo(bar *Bar) error {
|
||||||
|
var a int
|
||||||
|
if !bar.empty {
|
||||||
|
a = 10
|
||||||
|
if a != 5 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var c Closer = bar
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче