Skip to content

Commit cb92866

Browse files
committed
runtime: implement synctest
Add the synctest ABI and testing integration. Track bubble tasks across all schedulers. Add fake time, timer and channel ownership, durable blocking, and sync integration. Keep synctest paths out of programs that do not call Run, and retain the futex fast path for WaitGroups outside bubbles. Run supported upstream synctest tests and the encoding/json regression from the standard library test targets. Update compiler output and binary size baselines for the runtime layout changes.
1 parent 7f1f99b commit cb92866

23 files changed

Lines changed: 869 additions & 99 deletions

builder/testdata/binary-size.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
target package code rodata data bss
22
hifive1b examples/echo 4526 346 0 2268
33
microbit examples/serial 2993 391 8 2264
4-
wioterminal examples/pininterrupt 8309 1739 148 7496
4+
wioterminal examples/pininterrupt 8341 1739 148 7504

compiler/testdata/channel.ll

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ entry:
1515
}
1616

1717
; Function Attrs: nounwind
18-
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
18+
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(40) %ch, ptr %context) unnamed_addr #1 {
1919
entry:
2020
%chan.op = alloca %runtime.channelOp, align 8
2121
%chan.value = alloca i32, align 4
@@ -31,13 +31,13 @@ entry:
3131
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
3232
declare void @llvm.lifetime.start.p0(ptr nocapture) #2
3333

34-
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
34+
declare void @runtime.chanSend(ptr dereferenceable_or_null(40), ptr, ptr dereferenceable_or_null(16), ptr) #0
3535

3636
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
3737
declare void @llvm.lifetime.end.p0(ptr nocapture) #2
3838

3939
; Function Attrs: nounwind
40-
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
40+
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(40) %ch, ptr %context) unnamed_addr #1 {
4141
entry:
4242
%chan.op = alloca %runtime.channelOp, align 8
4343
%chan.value = alloca i32, align 4
@@ -49,10 +49,10 @@ entry:
4949
ret void
5050
}
5151

52-
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
52+
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(40), ptr, ptr dereferenceable_or_null(16), ptr) #0
5353

5454
; Function Attrs: nounwind
55-
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
55+
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(40) %ch, ptr %context) unnamed_addr #1 {
5656
entry:
5757
%chan.op = alloca %runtime.channelOp, align 8
5858
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
@@ -62,7 +62,7 @@ entry:
6262
}
6363

6464
; Function Attrs: nounwind
65-
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
65+
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(40) %ch, ptr %context) unnamed_addr #1 {
6666
entry:
6767
%chan.op = alloca %runtime.channelOp, align 8
6868
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
@@ -72,7 +72,7 @@ entry:
7272
}
7373

7474
; Function Attrs: nounwind
75-
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #1 {
75+
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(40) %ch1, ptr dereferenceable_or_null(40) %ch2, ptr %context) unnamed_addr #1 {
7676
entry:
7777
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
7878
%select.send.value = alloca i32, align 4

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,13 @@ declare i32 @llvm.umin.i32(i32, i32) #7
146146
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
147147

148148
; Function Attrs: nounwind
149-
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #0 {
149+
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(40) %ch, ptr %context) unnamed_addr #0 {
150150
entry:
151151
call void @runtime.chanClose(ptr %ch, ptr undef) #11
152152
ret void
153153
}
154154

155-
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
155+
declare void @runtime.chanClose(ptr dereferenceable_or_null(40), ptr) #1
156156

157157
; Function Attrs: nounwind
158158
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #0 {

compiler/testdata/goroutine-wasm-asyncify.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ declare i32 @llvm.umin.i32(i32, i32) #7
155155
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
156156

157157
; Function Attrs: nounwind
158-
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
158+
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(40) %ch, ptr %context) unnamed_addr #1 {
159159
entry:
160160
call void @runtime.chanClose(ptr %ch, ptr undef) #11
161161
ret void
162162
}
163163

164-
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #0
164+
declare void @runtime.chanClose(ptr dereferenceable_or_null(40), ptr) #0
165165

166166
; Function Attrs: nounwind
167167
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {

compiler/testdata/large.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(52), ptr, ptr
389389
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(52), ptr, ptr, i32, ptr) #0
390390

391391
; Function Attrs: nounwind
392-
define hidden i8 @main.useLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
392+
define hidden i8 @main.useLargeChannel(ptr dereferenceable_or_null(40) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
393393
entry:
394394
%chan.op1 = alloca %runtime.channelOp, align 8
395395
%chan.op = alloca %runtime.channelOp, align 8
@@ -426,15 +426,15 @@ if.then: ; preds = %entry
426426
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
427427
declare void @llvm.lifetime.start.p0(ptr nocapture) #8
428428

429-
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
429+
declare void @runtime.chanSend(ptr dereferenceable_or_null(40), ptr, ptr dereferenceable_or_null(16), ptr) #0
430430

431431
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
432432
declare void @llvm.lifetime.end.p0(ptr nocapture) #8
433433

434-
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
434+
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(40), ptr, ptr dereferenceable_or_null(16), ptr) #0
435435

436436
; Function Attrs: nounwind
437-
define hidden i8 @main.selectLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
437+
define hidden i8 @main.selectLargeChannel(ptr dereferenceable_or_null(40) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
438438
entry:
439439
%select.block.alloca = alloca [2 x %runtime.channelOp], align 8
440440
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8

compiler/testdata/pragma.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ entry:
8484

8585
declare void @main.undefinedFunctionNotInSection(ptr) #0
8686

87-
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #0
87+
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(40), ptr nocapture, ptr) #0
8888

8989
; Function Attrs: nounwind
90-
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #1 {
90+
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(40) %c, ptr %d, ptr %context) unnamed_addr #1 {
9191
entry:
9292
ret void
9393
}

make/test.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ tinygo-test:
234234
ifneq ($(TEST_PACKAGES_SHORT_HOST),)
235235
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -short $(TEST_PACKAGES_SHORT_HOST)
236236
endif
237+
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -skip='^(TestReflectFuncOf|TestChannelMovedOutOfBubble|TestTimerFromInsideBubble|TestWaitGroupMovedIntoBubble|TestWaitGroupMovedOutOfBubble|TestWaitGroupMovedBetweenBubblesWithNonZeroCount)$$' internal/synctest
238+
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -skip='^(TestFatal|TestError|TestVerboseError|TestSkip|TestVerboseSkip|TestHelper|TestHTTPTransport100Continue)$$' testing/synctest
239+
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -run='^TestSynctestMarshal$$' encoding/json
237240
ifeq ($(TEST_ENCODING_XML),true)
238241
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -short -stack-size=16MB encoding/xml
239242
endif

src/internal/task/task.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@ type Task struct {
3030
// since it falls into the padding of the FipsIndicator bit above.
3131
RunState uint8
3232

33+
// SynctestBlocked is set while this task is durably blocked.
34+
SynctestBlocked bool
35+
3336
// DeferFrame stores a pointer to the (stack allocated) defer frame of the
3437
// goroutine that is used for the recover builtin.
3538
DeferFrame unsafe.Pointer
39+
40+
// SynctestBubble identifies the synctest bubble this task belongs to.
41+
SynctestBubble unsafe.Pointer
3642
}
3743

3844
const (
@@ -74,3 +80,34 @@ func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
7480

7581
//go:linkname scheduleTask runtime.scheduleTask
7682
func scheduleTask(*Task)
83+
84+
func inheritSynctest(t *Task) {
85+
if !synctestIsEnabled() {
86+
return
87+
}
88+
parent := Current()
89+
if parent == nil || parent.SynctestBubble == nil {
90+
return
91+
}
92+
t.SynctestBubble = parent.SynctestBubble
93+
synctestTaskCreated(t)
94+
}
95+
96+
func exitSynctest(t *Task) {
97+
if !synctestIsEnabled() {
98+
return
99+
}
100+
if t.SynctestBubble != nil {
101+
synctestTaskExited(t)
102+
t.SynctestBubble = nil
103+
}
104+
}
105+
106+
//go:linkname synctestTaskCreated runtime.synctestTaskCreated
107+
func synctestTaskCreated(*Task)
108+
109+
//go:linkname synctestTaskExited runtime.synctestTaskExited
110+
func synctestTaskExited(*Task)
111+
112+
//go:linkname synctestIsEnabled runtime.synctestIsEnabled
113+
func synctestIsEnabled() bool

src/internal/task/task_asyncify.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type stackState struct {
5656
// The new goroutine is immediately started.
5757
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
5858
t := &Task{}
59+
inheritSynctest(t)
5960
addLiveTask(t)
6061
t.state.initialize(fn, args, stackSize)
6162
scheduleTask(t)

src/internal/task/task_exit.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func Exit() {
2424

2525
func exit(goexit bool) {
2626
t := Current()
27+
exitSynctest(t)
2728
remaining := atomic.AddUint32(&liveTasks, ^uint32(0))
2829
if t == mainTask {
2930
if goexit {

0 commit comments

Comments
 (0)