Skip to content

Commit d53ac90

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 bdc4a21 commit d53ac90

23 files changed

Lines changed: 870 additions & 100 deletions

builder/testdata/binary-size.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
target package code rodata data bss
22
hifive1b examples/echo 4321 323 0 2268
3-
microbit examples/serial 2842 382 8 2264
4-
wioterminal examples/pininterrupt 8039 1665 132 7496
3+
microbit examples/serial 2846 382 8 2264
4+
wioterminal examples/pininterrupt 8075 1665 132 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
@@ -388,7 +388,7 @@ declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(48), ptr, ptr
388388
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(48), ptr, ptr, i32, ptr) #0
389389

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

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

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

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

435435
; Function Attrs: nounwind
436-
define hidden i8 @main.selectLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
436+
define hidden i8 @main.selectLargeChannel(ptr dereferenceable_or_null(40) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
437437
entry:
438438
%select.block.alloca = alloca [2 x %runtime.channelOp], align 8
439439
%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
@@ -223,6 +223,9 @@ tinygo-test:
223223
@# TestUnmarshalNestingLimit{Slice,Struct}: encoding/asn1 nesting limit added in
224224
@# https://github.com/golang/go/commit/6a6d115f9a7422b2fa081ba6f567eefb4a099462
225225
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(filter-out encoding/xml,$(TEST_PACKAGES_HOST)) $(TEST_PACKAGES_SLOW)
226+
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -skip='^(TestReflectFuncOf|TestChannelMovedOutOfBubble|TestTimerFromInsideBubble|TestWaitGroupMovedIntoBubble|TestWaitGroupMovedOutOfBubble|TestWaitGroupMovedBetweenBubblesWithNonZeroCount)$$' internal/synctest
227+
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -skip='^(TestFatal|TestError|TestVerboseError|TestSkip|TestVerboseSkip|TestHelper|TestHTTPTransport100Continue)$$' testing/synctest
228+
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) -run='^TestSynctestMarshal$$' encoding/json
226229
ifeq ($(TEST_ENCODING_XML),true)
227230
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -stack-size=16MB encoding/xml
228231
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
@@ -55,6 +55,7 @@ type stackState struct {
5555
// The new goroutine is immediately started.
5656
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
5757
t := &Task{}
58+
inheritSynctest(t)
5859
addLiveTask(t)
5960
t.state.initialize(fn, args, stackSize)
6061
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)