Skip to content

Commit 67de8b4

Browse files
aykevldeadprogram
authored andcommitted
gc: use raw stack access whenever possible
The only architecture that actually needs special support for scanning the stack is WebAssembly. All others allow raw access to the stack with a small bit of assembly. Therefore, don't manually keep track of all these objects on the stack manually and instead just use conservative stack scanning. This results in a massive code size decrease in the affected targets (only tested linux/amd64 for code size) - sometimes around 33%. It also allows for future improvements such as using proper stackful goroutines.
1 parent bfa29f1 commit 67de8b4

File tree

13 files changed

+125
-11
lines changed

13 files changed

+125
-11
lines changed

compileopts/config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ func (c *Config) NeedsStackObjects() bool {
118118
switch c.GC() {
119119
case "conservative", "extalloc":
120120
for _, tag := range c.BuildTags() {
121-
if tag == "baremetal" {
122-
return false
121+
if tag == "wasm" {
122+
return true
123123
}
124124
}
125125

126-
return true
126+
return false
127127
default:
128128
return false
129129
}

compileopts/target.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
246246
} else {
247247
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
248248
}
249+
if goarch != "wasm" {
250+
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S")
251+
}
249252
if goarch != runtime.GOARCH {
250253
// Some educated guesses as to how to invoke helper programs.
251254
spec.GDB = "gdb-multiarch"

src/runtime/arch_386.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package runtime
22

3+
import "device"
4+
35
const GOARCH = "386"
46

57
// The bitness of the CPU (e.g. 8, 32, 64).
@@ -10,4 +12,6 @@ func align(ptr uintptr) uintptr {
1012
return (ptr + 3) &^ 3
1113
}
1214

13-
func getCurrentStackPointer() uintptr
15+
func getCurrentStackPointer() uintptr {
16+
return device.AsmFull("movl %esp, {}", nil)
17+
}

src/runtime/arch_amd64.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package runtime
22

3+
import "device"
4+
35
const GOARCH = "amd64"
46

57
// The bitness of the CPU (e.g. 8, 32, 64).
@@ -12,4 +14,6 @@ func align(ptr uintptr) uintptr {
1214
return (ptr + 15) &^ 15
1315
}
1416

15-
func getCurrentStackPointer() uintptr
17+
func getCurrentStackPointer() uintptr {
18+
return device.AsmFull("movq %rsp, {}", nil)
19+
}

src/runtime/arch_arm64.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package runtime
22

3+
import "device/arm"
4+
35
const GOARCH = "arm64"
46

57
// The bitness of the CPU (e.g. 8, 32, 64).
@@ -9,5 +11,6 @@ const TargetBits = 64
911
func align(ptr uintptr) uintptr {
1012
return (ptr + 7) &^ 7
1113
}
12-
13-
func getCurrentStackPointer() uintptr
14+
func getCurrentStackPointer() uintptr {
15+
return arm.AsmFull("mov {}, sp", nil)
16+
}

src/runtime/gc_386.S

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.section .text.tinygo_scanCurrentStack
2+
.global tinygo_scanCurrentStack
3+
.type tinygo_scanCurrentStack, %function
4+
tinygo_scanCurrentStack:
5+
// Sources:
6+
// * https://stackoverflow.com/questions/18024672/what-registers-are-preserved-through-a-linux-x86-64-function-call
7+
// * https://godbolt.org/z/q7e8dn
8+
9+
// Save callee-saved registers.
10+
pushl %ebx
11+
pushl %esi
12+
pushl %edi
13+
pushl %ebp
14+
15+
// Scan the stack.
16+
pushl %esp
17+
calll tinygo_scanstack
18+
19+
// Restore the stack pointer. Registers do not need to be restored as they
20+
// were only pushed to be discoverable by the GC.
21+
addl $20, %esp
22+
retl

src/runtime/gc_amd64.S

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifdef __ELF__
2+
.section .text.tinygo_scanCurrentStack
3+
.global tinygo_scanCurrentStack
4+
tinygo_scanCurrentStack:
5+
#else // Darwin
6+
.global _tinygo_scanCurrentStack
7+
_tinygo_scanCurrentStack:
8+
#endif
9+
// Save callee-saved registers.
10+
pushq %rbx
11+
pushq %rbp
12+
pushq %r12
13+
pushq %r13
14+
pushq %r14
15+
pushq %r15
16+
17+
// Scan the stack.
18+
subq $8, %rsp // adjust the stack before the call to maintain 16-byte alignment
19+
movq %rsp, %rdi
20+
#ifdef __ELF__
21+
callq tinygo_scanstack
22+
#else
23+
callq _tinygo_scanstack // Darwin
24+
#endif
25+
26+
// Restore the stack pointer. Registers do not need to be restored as they
27+
// were only pushed to be discoverable by the GC.
28+
addq $56, %rsp
29+
retq

src/runtime/gc_arm64.S

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.section .text.tinygo_scanCurrentStack
2+
.global tinygo_scanCurrentStack
3+
.type tinygo_scanCurrentStack, %function
4+
tinygo_scanCurrentStack:
5+
// Sources:
6+
// * https://developer.arm.com/architectures/learn-the-architecture/armv8-a-instruction-set-architecture/procedure-call-standard
7+
// * https://godbolt.org/z/qrvrEh
8+
9+
// Save callee-saved registers.
10+
stp x29, x30, [sp, #-96]!
11+
stp x28, x27, [sp, #16]
12+
stp x26, x25, [sp, #32]
13+
stp x24, x23, [sp, #48]
14+
stp x22, x21, [sp, #64]
15+
stp x20, x19, [sp, #80]
16+
17+
// Scan the stack.
18+
mov x0, sp
19+
bl tinygo_scanstack
20+
21+
// Restore stack state and return.
22+
ldp x29, x30, [sp], #96
23+
ret

src/runtime/gc_stack_portable.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// +build gc.conservative gc.extalloc
2-
// +build !baremetal
2+
// +build wasm
33

44
package runtime
55

src/runtime/gc_stack_raw.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// +build gc.conservative gc.extalloc
2-
// +build baremetal
2+
// +build !wasm
33

44
package runtime
55

0 commit comments

Comments
 (0)