From 98eee7c22ab8f790a1a712619b105a1f9a45e271 Mon Sep 17 00:00:00 2001 From: Jaden Weiss Date: Sun, 17 Nov 2019 10:53:26 -0500 Subject: [PATCH] compiler: add support for async interface calls --- compiler/interface.go | 3 +++ testdata/interface.go | 40 +++++++++++++++++++++++++++++ testdata/interface.txt | 3 +++ transform/interface-lowering.go | 11 ++++---- transform/testdata/interface.ll | 12 ++++----- transform/testdata/interface.out.ll | 8 +++--- 6 files changed, 62 insertions(+), 15 deletions(-) diff --git a/compiler/interface.go b/compiler/interface.go index 25b62163..a0c911ab 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -461,6 +461,9 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value { paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...) wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false) wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType) + if f.LLVMFn.LastParam().Name() == "parentHandle" { + wrapper.LastParam().SetName("parentHandle") + } c.interfaceInvokeWrappers = append(c.interfaceInvokeWrappers, interfaceInvokeWrapper{ fn: f, wrapper: wrapper, diff --git a/testdata/interface.go b/testdata/interface.go index 51180a41..fa0a99c6 100644 --- a/testdata/interface.go +++ b/testdata/interface.go @@ -1,5 +1,7 @@ package main +import "time" + func main() { thing := &Thing{"foo"} println("thing:", thing.String()) @@ -87,6 +89,14 @@ func main() { println("test", i, "of interfaceEqualTests failed") } } + + // test interface blocking + blockDynamic(NonBlocker{}) + println("non-blocking call on sometimes-blocking interface") + blockDynamic(SleepBlocker(time.Millisecond)) + println("slept 1ms") + blockStatic(SleepBlocker(time.Millisecond)) + println("slept 1ms") } func printItf(val interface{}) { @@ -134,6 +144,14 @@ func nestedSwitch(verb rune, arg interface{}) bool { return false } +func blockDynamic(blocker DynamicBlocker) { + blocker.Block() +} + +func blockStatic(blocker StaticBlocker) { + blocker.Sleep() +} + type Thing struct { name string } @@ -211,3 +229,25 @@ type Unmatched interface { type linkedList struct { addr *linkedList } + +type DynamicBlocker interface { + Block() +} + +type NonBlocker struct{} + +func (b NonBlocker) Block() {} + +type SleepBlocker time.Duration + +func (s SleepBlocker) Block() { + time.Sleep(time.Duration(s)) +} + +func (s SleepBlocker) Sleep() { + s.Block() +} + +type StaticBlocker interface { + Sleep() +} diff --git a/testdata/interface.txt b/testdata/interface.txt index 83ee80b0..cc9b7011 100644 --- a/testdata/interface.txt +++ b/testdata/interface.txt @@ -19,3 +19,6 @@ SmallPair.Print: 3 5 Stringer.String(): foo Stringer.(*Thing).String(): foo nested switch: true +non-blocking call on sometimes-blocking interface +slept 1ms +slept 1ms diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index b75054aa..5c017dd9 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -336,7 +336,7 @@ func (p *lowerInterfacesPass) run() { // Replace the old lookup/inttoptr/call with the new call. p.builder.SetInsertPointBefore(call) - retval := p.builder.CreateCall(redirector, params, "") + retval := p.builder.CreateCall(redirector, append(params, llvm.ConstNull(llvm.PointerType(p.ctx.Int8Type(), 0))), "") if retval.Type().TypeKind() != llvm.VoidTypeKind { call.ReplaceAllUsesWith(retval) } @@ -613,9 +613,10 @@ func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signatu // Construct the function name, which is of the form: // (main.Stringer).String fnName := "(" + itf.id() + ")." + signature.methodName() - fnType := llvm.FunctionType(returnType, params, false) + fnType := llvm.FunctionType(returnType, append(params, llvm.PointerType(p.ctx.Int8Type(), 0)), false) fn := llvm.AddFunction(p.mod, fnName, fnType) - fn.LastParam().SetName("actualType") + llvm.PrevParam(fn.LastParam()).SetName("actualType") + fn.LastParam().SetName("parentHandle") itf.methodFuncs[signature] = fn return fn } @@ -644,13 +645,13 @@ func (p *lowerInterfacesPass) createInterfaceMethodFunc(itf *interfaceInfo, sign // Create type switch in entry block. p.builder.SetInsertPointAtEnd(entry) - actualType := fn.LastParam() + actualType := llvm.PrevParam(fn.LastParam()) sw := p.builder.CreateSwitch(actualType, defaultBlock, len(itf.types)) // Collect the params that will be passed to the functions to call. // These params exclude the receiver (which may actually consist of multiple // parts). - params := make([]llvm.Value, fn.ParamsCount()-2) + params := make([]llvm.Value, fn.ParamsCount()-3) for i := range params { params[i] = fn.Param(i + 1) } diff --git a/transform/testdata/interface.ll b/transform/testdata/interface.ll index dd00670d..ee059c95 100644 --- a/transform/testdata/interface.ll +++ b/transform/testdata/interface.ll @@ -13,7 +13,7 @@ target triple = "armv7m-none-eabi" @"Unmatched$interface" = private constant [1 x i8*] [i8* @"func NeverImplementedMethod()"] @"func Double() int" = external constant i8 @"Doubler$interface" = private constant [1 x i8*] [i8* @"func Double() int"] -@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"func Double() int", i32 ptrtoint (i32 (i8*)* @"(Number).Double$invoke" to i32) }] +@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"func Double() int", i32 ptrtoint (i32 (i8*, i8*)* @"(Number).Double$invoke" to i32) }] @"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0 } @"typeInInterface:reflect/types.type:named:Number" = private constant %runtime.typeInInterface { %runtime.typecodeID* @"reflect/types.type:named:Number", %runtime.interfaceMethodInfo* getelementptr inbounds ([1 x %runtime.interfaceMethodInfo], [1 x %runtime.interfaceMethodInfo]* @"Number$methodset", i32 0, i32 0) } @@ -49,8 +49,8 @@ typeswitch.notUnmatched: typeswitch.Doubler: %doubler.func = call i32 @runtime.interfaceMethod(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Doubler$interface", i32 0, i32 0), i8* nonnull @"func Double() int") - %doubler.func.cast = inttoptr i32 %doubler.func to i32 (i8*)* - %doubler.result = call i32 %doubler.func.cast(i8* %value) + %doubler.func.cast = inttoptr i32 %doubler.func to i32 (i8*, i8*)* + %doubler.result = call i32 %doubler.func.cast(i8* %value, i8* null) call void @runtime.printint32(i32 %doubler.result) ret void @@ -68,13 +68,13 @@ typeswitch.notByte: ret void } -define i32 @"(Number).Double"(i32 %receiver) { +define i32 @"(Number).Double"(i32 %receiver, i8* %parentHandle) { %ret = mul i32 %receiver, 2 ret i32 %ret } -define i32 @"(Number).Double$invoke"(i8* %receiverPtr) { +define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) { %receiver = ptrtoint i8* %receiverPtr to i32 - %ret = call i32 @"(Number).Double"(i32 %receiver) + %ret = call i32 @"(Number).Double"(i32 %receiver, i8* null) ret i32 %ret } diff --git a/transform/testdata/interface.out.ll b/transform/testdata/interface.out.ll index d4828cf5..25f47d0c 100644 --- a/transform/testdata/interface.out.ll +++ b/transform/testdata/interface.out.ll @@ -47,7 +47,7 @@ typeswitch.notUnmatched: br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler typeswitch.Doubler: - %doubler.result = call i32 @"(Number).Double$invoke"(i8* %value) + %doubler.result = call i32 @"(Number).Double$invoke"(i8* %value, i8* null) call void @runtime.printint32(i32 %doubler.result) ret void @@ -65,14 +65,14 @@ typeswitch.notByte: ret void } -define i32 @"(Number).Double"(i32 %receiver) { +define i32 @"(Number).Double"(i32 %receiver, i8* %parentHandle) { %ret = mul i32 %receiver, 2 ret i32 %ret } -define i32 @"(Number).Double$invoke"(i8* %receiverPtr) { +define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) { %receiver = ptrtoint i8* %receiverPtr to i32 - %ret = call i32 @"(Number).Double"(i32 %receiver) + %ret = call i32 @"(Number).Double"(i32 %receiver, i8* null) ret i32 %ret }