compiler: add support for the go
keyword on interface methods
This is a feature that was long missing, but because of the previous refactor, it is now trivial to implement.
Этот коммит содержится в:
родитель
a4afc3b4b0
коммит
9e1b4de999
6 изменённых файлов: 115 добавлений и 4 удалений
|
@ -79,7 +79,15 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||
}
|
||||
b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos())
|
||||
return
|
||||
} else if !instr.Call.IsInvoke() {
|
||||
} else if instr.Call.IsInvoke() {
|
||||
// This is a method call on an interface value.
|
||||
itf := b.getValue(instr.Call.Value)
|
||||
itfTypeCode := b.CreateExtractValue(itf, 0, "")
|
||||
itfValue := b.CreateExtractValue(itf, 1, "")
|
||||
funcPtr = b.getInvokeFunction(&instr.Call)
|
||||
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
||||
params = append(params, itfTypeCode) // end with typecode
|
||||
} else {
|
||||
// This is a function pointer.
|
||||
// At the moment, two extra params are passed to the newly started
|
||||
// goroutine:
|
||||
|
@ -99,9 +107,6 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||
panic("unknown scheduler type")
|
||||
}
|
||||
prefix = b.fn.RelString(nil)
|
||||
} else {
|
||||
b.addError(instr.Pos(), "todo: go on interface call")
|
||||
return
|
||||
}
|
||||
|
||||
paramBundle := b.emitPointerPack(params)
|
||||
|
|
44
compiler/testdata/goroutine-cortex-m-qemu.ll
предоставленный
44
compiler/testdata/goroutine-cortex-m-qemu.ll
предоставленный
|
@ -9,6 +9,8 @@ target triple = "armv7m-unknown-unknown-eabi"
|
|||
%"internal/task.state" = type { i32, i32* }
|
||||
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||
|
||||
@"main.startInterfaceMethod$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
|
@ -158,8 +160,50 @@ entry:
|
|||
|
||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* undef, i8* null) #0
|
||||
%1 = bitcast i8* %0 to i8**
|
||||
store i8* %itf.value, i8** %1, align 4
|
||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
%.repack = bitcast i8* %2 to i8**
|
||||
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main.startInterfaceMethod$string", i32 0, i32 0), i8** %.repack, align 4
|
||||
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%3 = bitcast i8* %.repack1 to i32*
|
||||
store i32 4, i32* %3, align 4
|
||||
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||
%5 = bitcast i8* %4 to i32*
|
||||
store i32 %itf.typecode, i32* %5, align 4
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* undef, i8* undef) #0
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 %stacksize, i8* undef, i8* null) #0
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*, i8*) #5
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
|
||||
entry:
|
||||
%1 = bitcast i8* %0 to i8**
|
||||
%2 = load i8*, i8** %1, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
%4 = bitcast i8* %3 to i8**
|
||||
%5 = load i8*, i8** %4, align 4
|
||||
%6 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%7 = bitcast i8* %6 to i32*
|
||||
%8 = load i32, i32* %7, align 4
|
||||
%9 = getelementptr inbounds i8, i8* %0, i32 12
|
||||
%10 = bitcast i8* %9 to i32*
|
||||
%11 = load i32, i32* %10, align 4
|
||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef, i8* undef) #0
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
|
||||
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||
attributes #4 = { nounwind "tinygo-gowrapper" }
|
||||
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||
|
|
23
compiler/testdata/goroutine-wasm.ll
предоставленный
23
compiler/testdata/goroutine-wasm.ll
предоставленный
|
@ -14,6 +14,7 @@ target triple = "wasm32-unknown-wasi"
|
|||
@"main.inlineFunctionGoroutine$pack" = private unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
|
||||
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
||||
@"main.closureFunctionGoroutine$1$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main.closureFunctionGoroutine$1" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
||||
@"main.startInterfaceMethod$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
@ -115,4 +116,26 @@ entry:
|
|||
|
||||
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||
entry:
|
||||
%0 = call i8* @runtime.alloc(i32 16, i8* undef, i8* null) #0
|
||||
%1 = bitcast i8* %0 to i8**
|
||||
store i8* %itf.value, i8** %1, align 4
|
||||
%2 = getelementptr inbounds i8, i8* %0, i32 4
|
||||
%.repack = bitcast i8* %2 to i8**
|
||||
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main.startInterfaceMethod$string", i32 0, i32 0), i8** %.repack, align 4
|
||||
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
|
||||
%3 = bitcast i8* %.repack1 to i32*
|
||||
store i32 4, i32* %3, align 4
|
||||
%4 = getelementptr inbounds i8, i8* %0, i32 12
|
||||
%5 = bitcast i8* %4 to i32*
|
||||
store i32 %itf.typecode, i32* %5, align 4
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*, i8*, i32, i32, i8*, i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke" to i32), i8* nonnull %0, i32 undef, i8* undef, i8* null) #0
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*, i8*) #1
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||
|
|
8
compiler/testdata/goroutine.go
предоставленный
8
compiler/testdata/goroutine.go
предоставленный
|
@ -41,3 +41,11 @@ func closeBuiltinGoroutine(ch chan int) {
|
|||
}
|
||||
|
||||
func regularFunction(x int)
|
||||
|
||||
type simpleInterface interface {
|
||||
Print(string)
|
||||
}
|
||||
|
||||
func startInterfaceMethod(itf simpleInterface) {
|
||||
go itf.Print("test")
|
||||
}
|
||||
|
|
27
testdata/goroutines.go
предоставленный
27
testdata/goroutines.go
предоставленный
|
@ -75,6 +75,8 @@ func main() {
|
|||
|
||||
testGoOnBuiltins()
|
||||
|
||||
testGoOnInterface(Foo(0))
|
||||
|
||||
testCond()
|
||||
|
||||
testIssue1790()
|
||||
|
@ -203,6 +205,14 @@ func testCond() {
|
|||
|
||||
var once sync.Once
|
||||
|
||||
func testGoOnInterface(f Itf) {
|
||||
go f.Nowait()
|
||||
time.Sleep(time.Millisecond)
|
||||
go f.Wait()
|
||||
time.Sleep(time.Millisecond * 2)
|
||||
println("done with 'go on interface'")
|
||||
}
|
||||
|
||||
// This tests a fix for issue 1790:
|
||||
// https://github.com/tinygo-org/tinygo/issues/1790
|
||||
func testIssue1790() *int {
|
||||
|
@ -210,3 +220,20 @@ func testIssue1790() *int {
|
|||
i := 0
|
||||
return &i
|
||||
}
|
||||
|
||||
type Itf interface {
|
||||
Nowait()
|
||||
Wait()
|
||||
}
|
||||
|
||||
type Foo string
|
||||
|
||||
func (f Foo) Nowait() {
|
||||
println("called: Foo.Nowait")
|
||||
}
|
||||
|
||||
func (f Foo) Wait() {
|
||||
println("called: Foo.Wait")
|
||||
time.Sleep(time.Microsecond)
|
||||
println(" ...waited")
|
||||
}
|
||||
|
|
4
testdata/goroutines.txt
предоставленный
4
testdata/goroutines.txt
предоставленный
|
@ -20,3 +20,7 @@ acquired mutex from goroutine
|
|||
released mutex from goroutine
|
||||
re-acquired mutex
|
||||
done
|
||||
called: Foo.Nowait
|
||||
called: Foo.Wait
|
||||
...waited
|
||||
done with 'go on interface'
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче