Skip to content

Commit 9f8715c

Browse files
niaowdeadprogram
authored andcommitted
runtime (gc): scan callee-saved registers while marking stack
1 parent cbaa58a commit 9f8715c

File tree

8 files changed

+152
-3
lines changed

8 files changed

+152
-3
lines changed

src/internal/task/task_coroutine.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,9 @@ func fake() {
9595
go func() {}()
9696
Pause()
9797
}
98+
99+
// OnSystemStack returns whether the caller is running on the system stack.
100+
func OnSystemStack() bool {
101+
// This scheduler does not do any stack switching.
102+
return true
103+
}

src/internal/task/task_none.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@ type state struct{}
2727
func (t *Task) Resume() {
2828
runtimePanic("scheduler is disabled")
2929
}
30+
31+
// OnSystemStack returns whether the caller is running on the system stack.
32+
func OnSystemStack() bool {
33+
// This scheduler does not do any stack switching.
34+
return true
35+
}

src/internal/task/task_stack.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,9 @@ func start(fn uintptr, args unsafe.Pointer) {
7272
t.state.initialize(fn, args)
7373
runqueuePushBack(t)
7474
}
75+
76+
// OnSystemStack returns whether the caller is running on the system stack.
77+
func OnSystemStack() bool {
78+
// If there is not an active goroutine, then this must be running on the system stack.
79+
return Current() == nil
80+
}

src/runtime/gc_stack_raw.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,38 @@
33

44
package runtime
55

6+
import "internal/task"
7+
68
// markStack marks all root pointers found on the stack.
79
//
810
// This implementation is conservative and relies on the stack top (provided by
911
// the linker) and getting the current stack pointer from a register. Also, it
1012
// assumes a descending stack. Thus, it is not very portable.
1113
func markStack() {
12-
// Mark system stack.
13-
markRoots(getSystemStackPointer(), stackTop)
14+
// Scan the current stack, and all current registers.
15+
scanCurrentStack()
16+
17+
if !task.OnSystemStack() {
18+
// Mark system stack.
19+
markRoots(getSystemStackPointer(), stackTop)
20+
}
21+
}
22+
23+
//go:export tinygo_scanCurrentStack
24+
func scanCurrentStack()
25+
26+
//go:export tinygo_scanstack
27+
func scanstack(sp uintptr) {
28+
// Mark current stack.
29+
// This function is called by scanCurrentStack, after pushing all registers onto the stack.
30+
// Callee-saved registers have been pushed onto stack by tinygo_localscan, so this will scan them too.
31+
if task.OnSystemStack() {
32+
// This is the system stack.
33+
// Scan all words on the stack.
34+
markRoots(sp, stackTop)
35+
} else {
36+
// This is a goroutine stack.
37+
// It is an allocation, so scan it as if it were a value in a global.
38+
markRoot(0, sp)
39+
}
1440
}

src/runtime/scheduler_avr.S

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,55 @@ tinygo_switchToScheduler:
187187

188188
// Return into the scheduler, as if tinygo_switchToTask was a regular call.
189189
ret
190+
191+
.global tinygo_scanCurrentStack
192+
.type tinygo_scanCurrentStack, %function
193+
tinygo_scanCurrentStack:
194+
// Save callee-saved registers.
195+
push r29 // Y
196+
push r28 // Y
197+
push r17
198+
push r16
199+
push r15
200+
push r14
201+
push r13
202+
push r12
203+
push r11
204+
push r10
205+
push r9
206+
push r8
207+
push r7
208+
push r6
209+
push r5
210+
push r4
211+
push r3
212+
push r2
213+
214+
// Scan the stack.
215+
in r24, 0x3d; SPL
216+
in r25, 0x3e; SPH
217+
#if __AVR_ARCH__ == 2 || __AVR_ARCH__ == 25
218+
rcall tinygo_scanstack
219+
#else
220+
call tinygo_scanstack
221+
#endif
222+
223+
// Restore callee-saved registers.
224+
pop r2
225+
pop r3
226+
pop r4
227+
pop r5
228+
pop r6
229+
pop r7
230+
pop r8
231+
pop r9
232+
pop r10
233+
pop r11
234+
pop r12
235+
pop r13
236+
pop r14
237+
pop r15
238+
pop r16
239+
pop r17
240+
pop r28 // Y
241+
pop r29 // Y

src/runtime/scheduler_cortexm.S

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,26 @@ tinygo_swapTask:
111111
mov r11, r3
112112
pop {pc}
113113
#endif
114+
115+
.global tinygo_scanCurrentStack
116+
.type tinygo_scanCurrentStack, %function
117+
tinygo_scanCurrentStack:
118+
// Save callee-saved registers onto the stack.
119+
#if defined(__thumb2__)
120+
push {r4-r11, lr}
121+
#else
122+
mov r0, r8
123+
mov r1, r9
124+
mov r2, r10
125+
mov r3, r11
126+
push {r0-r3, lr}
127+
push {r4-r7}
128+
#endif
129+
130+
// Scan the stack.
131+
mov r0, sp
132+
bl tinygo_scanstack
133+
134+
// Restore stack state and return.
135+
add sp, #32
136+
pop {pc}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.section .text.tinygo_scanCurrentStack
2+
.global tinygo_scanCurrentStack
3+
.type tinygo_scanCurrentStack, %function
4+
tinygo_scanCurrentStack:
5+
// Push callee-saved registers onto the stack.
6+
addi sp, sp, -64
7+
sw ra, 60(sp)
8+
sw s11, 56(sp)
9+
sw s10, 52(sp)
10+
sw s9, 48(sp)
11+
sw s8, 44(sp)
12+
sw s7, 40(sp)
13+
sw s6, 36(sp)
14+
sw s5, 32(sp)
15+
sw s4, 28(sp)
16+
sw s3, 24(sp)
17+
sw s2, 20(sp)
18+
sw s1, 16(sp)
19+
sw s0, 12(sp)
20+
21+
// Scan the stack.
22+
mv a0, sp
23+
call tinygo_scanstack
24+
25+
// Restore stack state.
26+
addi sp, sp, 64
27+
28+
// Return to the caller.
29+
ret

targets/riscv.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"--gc-sections"
2323
],
2424
"extra-files": [
25-
"src/device/riscv/start.S"
25+
"src/device/riscv/start.S",
26+
"src/runtime/scheduler_tinygoriscv.S"
2627
],
2728
"gdb": "riscv64-unknown-elf-gdb"
2829
}

0 commit comments

Comments
 (0)