Skip to content

Commit d3043c7

Browse files
committed
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.
1 parent df3fecc commit d3043c7

File tree

6 files changed

+115
-4
lines changed

6 files changed

+115
-4
lines changed

compiler/goroutine.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,15 @@ func (b *builder) createGo(instr *ssa.Go) {
7979
}
8080
b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos())
8181
return
82-
} else if !instr.Call.IsInvoke() {
82+
} else if instr.Call.IsInvoke() {
83+
// This is a method call on an interface value.
84+
itf := b.getValue(instr.Call.Value)
85+
itfTypeCode := b.CreateExtractValue(itf, 0, "")
86+
itfValue := b.CreateExtractValue(itf, 1, "")
87+
funcPtr = b.getInvokeFunction(&instr.Call)
88+
params = append([]llvm.Value{itfValue}, params...) // start with receiver
89+
params = append(params, itfTypeCode) // end with typecode
90+
} else {
8391
// This is a function pointer.
8492
// At the moment, two extra params are passed to the newly started
8593
// goroutine:
@@ -99,9 +107,6 @@ func (b *builder) createGo(instr *ssa.Go) {
99107
panic("unknown scheduler type")
100108
}
101109
prefix = b.fn.RelString(nil)
102-
} else {
103-
b.addError(instr.Pos(), "todo: go on interface call")
104-
return
105110
}
106111

107112
paramBundle := b.emitPointerPack(params)

compiler/testdata/goroutine-cortex-m-qemu.ll

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ target triple = "armv7m-unknown-unknown-eabi"
99
%"internal/task.state" = type { i32, i32* }
1010
%runtime.chanSelectState = type { %runtime.channel*, i8* }
1111

12+
@"main.startInterfaceMethod$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
13+
1214
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
1315

1416
; Function Attrs: nounwind
@@ -158,8 +160,50 @@ entry:
158160

159161
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
160162

163+
; Function Attrs: nounwind
164+
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
165+
entry:
166+
%0 = call i8* @runtime.alloc(i32 16, i8* undef, i8* null) #0
167+
%1 = bitcast i8* %0 to i8**
168+
store i8* %itf.value, i8** %1, align 4
169+
%2 = getelementptr inbounds i8, i8* %0, i32 4
170+
%.repack = bitcast i8* %2 to i8**
171+
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main.startInterfaceMethod$string", i32 0, i32 0), i8** %.repack, align 4
172+
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
173+
%3 = bitcast i8* %.repack1 to i32*
174+
store i32 4, i32* %3, align 4
175+
%4 = getelementptr inbounds i8, i8* %0, i32 12
176+
%5 = bitcast i8* %4 to i32*
177+
store i32 %itf.typecode, i32* %5, align 4
178+
%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
179+
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
180+
ret void
181+
}
182+
183+
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*, i8*) #5
184+
185+
; Function Attrs: nounwind
186+
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
187+
entry:
188+
%1 = bitcast i8* %0 to i8**
189+
%2 = load i8*, i8** %1, align 4
190+
%3 = getelementptr inbounds i8, i8* %0, i32 4
191+
%4 = bitcast i8* %3 to i8**
192+
%5 = load i8*, i8** %4, align 4
193+
%6 = getelementptr inbounds i8, i8* %0, i32 8
194+
%7 = bitcast i8* %6 to i32*
195+
%8 = load i32, i32* %7, align 4
196+
%9 = getelementptr inbounds i8, i8* %0, i32 12
197+
%10 = bitcast i8* %9 to i32*
198+
%11 = load i32, i32* %10, align 4
199+
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef, i8* undef) #0
200+
ret void
201+
}
202+
161203
attributes #0 = { nounwind }
162204
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
163205
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
164206
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
165207
attributes #4 = { nounwind "tinygo-gowrapper" }
208+
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
209+
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }

compiler/testdata/goroutine-wasm.ll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ target triple = "wasm32-unknown-wasi"
1414
@"main.inlineFunctionGoroutine$pack" = private unnamed_addr constant { i32, i8* } { i32 5, i8* undef }
1515
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
1616
@"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}{}" }
17+
@"main.startInterfaceMethod$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
1718

1819
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
1920

@@ -115,4 +116,26 @@ entry:
115116

116117
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
117118

119+
; Function Attrs: nounwind
120+
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
121+
entry:
122+
%0 = call i8* @runtime.alloc(i32 16, i8* undef, i8* null) #0
123+
%1 = bitcast i8* %0 to i8**
124+
store i8* %itf.value, i8** %1, align 4
125+
%2 = getelementptr inbounds i8, i8* %0, i32 4
126+
%.repack = bitcast i8* %2 to i8**
127+
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main.startInterfaceMethod$string", i32 0, i32 0), i8** %.repack, align 4
128+
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
129+
%3 = bitcast i8* %.repack1 to i32*
130+
store i32 4, i32* %3, align 4
131+
%4 = getelementptr inbounds i8, i8* %0, i32 12
132+
%5 = bitcast i8* %4 to i32*
133+
store i32 %itf.typecode, i32* %5, align 4
134+
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
135+
ret void
136+
}
137+
138+
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*, i8*) #1
139+
118140
attributes #0 = { nounwind }
141+
attributes #1 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }

compiler/testdata/goroutine.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,11 @@ func closeBuiltinGoroutine(ch chan int) {
4141
}
4242

4343
func regularFunction(x int)
44+
45+
type simpleInterface interface {
46+
Print(string)
47+
}
48+
49+
func startInterfaceMethod(itf simpleInterface) {
50+
go itf.Print("test")
51+
}

testdata/goroutines.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ func main() {
7575

7676
testGoOnBuiltins()
7777

78+
testGoOnInterface(Foo(0))
79+
7880
testCond()
7981

8082
testIssue1790()
@@ -203,10 +205,35 @@ func testCond() {
203205

204206
var once sync.Once
205207

208+
func testGoOnInterface(f Itf) {
209+
go f.Nowait()
210+
time.Sleep(time.Millisecond)
211+
go f.Wait()
212+
time.Sleep(time.Millisecond * 2)
213+
println("done with 'go on interface'")
214+
}
215+
206216
// This tests a fix for issue 1790:
207217
// https://github.com/tinygo-org/tinygo/issues/1790
208218
func testIssue1790() *int {
209219
once.Do(func() {})
210220
i := 0
211221
return &i
212222
}
223+
224+
type Itf interface {
225+
Nowait()
226+
Wait()
227+
}
228+
229+
type Foo string
230+
231+
func (f Foo) Nowait() {
232+
println("called: Foo.Nowait")
233+
}
234+
235+
func (f Foo) Wait() {
236+
println("called: Foo.Wait")
237+
time.Sleep(time.Microsecond)
238+
println(" ...waited")
239+
}

testdata/goroutines.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ acquired mutex from goroutine
2020
released mutex from goroutine
2121
re-acquired mutex
2222
done
23+
called: Foo.Nowait
24+
called: Foo.Wait
25+
...waited
26+
done with 'go on interface'

0 commit comments

Comments
 (0)