Skip to content

Commit 15c7d93

Browse files
aykevldeadprogram
authored andcommitted
avr: use a garbage collector
This might sound crazy, but I think it's better to enable the GC by default to avoid surprises. It costs 1130 bytes of flash and 16 bytes of RAM (plus heap overhead) so it's not exactly free, but if needed it can easily be disabled with `-gc=leaking`. On the Uno (32kB flash, 2kB RAM) that's not massive, on the DigiSpark (8kB flash, 0.5kB RAM) that may be too much depending on the application.
1 parent 6e26728 commit 15c7d93

File tree

6 files changed

+24
-4
lines changed

6 files changed

+24
-4
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ ifneq ($(AVR), 0)
269269
@$(MD5SUM) test.hex
270270
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
271271
@$(MD5SUM) test.hex
272+
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
273+
@$(MD5SUM) test.hex
272274
endif
273275
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
274276
@$(MD5SUM) test.hex

src/runtime/gc_conservative.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ func markRoots(start, end uintptr) {
318318
}
319319
}
320320

321-
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
321+
for addr := start; addr != end; addr += unsafe.Alignof(addr) {
322322
root := *(*uintptr)(unsafe.Pointer(addr))
323323
markRoot(addr, root)
324324
}

targets/avr.S

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,12 @@ __vector_WDT:
6060

6161
pop r16
6262
reti
63+
64+
; This is necessary for the garbage collector.
65+
; It returns the stack pointer as an uintptr.
66+
.section .text.runtime.getCurrentStackPointer
67+
.global runtime.getCurrentStackPointer
68+
runtime.getCurrentStackPointer:
69+
in r24, 0x3d; SPL
70+
in r25, 0x3e; SPH
71+
ret

targets/avr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"goos": "linux",
44
"goarch": "arm",
55
"compiler": "avr-gcc",
6-
"gc": "leaking",
6+
"gc": "conservative",
77
"linker": "avr-gcc",
88
"ldflags": [
99
"-T", "targets/avr.ld",

targets/avr.ld

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,5 @@ SECTIONS
4646
/* For the memory allocator. */
4747
_heap_start = _ebss;
4848
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
49+
_globals_start = _sdata;
50+
_globals_end = _ebss;

testdata/gc.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ var scalarSlices [4][]byte
2323
var randSeeds [4]uint32
2424

2525
func testNonPointerHeap() {
26+
maxSliceSize := uint32(1024)
27+
if ^uintptr(0) <= 0xffff {
28+
// 16-bit and lower devices, such as AVR.
29+
// Heap size is a real issue there, while it is still useful to run
30+
// these tests. Therefore, lower the max slice size.
31+
maxSliceSize = 64
32+
}
2633
// Allocate roughly 0.5MB of memory.
2734
for i := 0; i < 1000; i++ {
2835
// Pick a random index that the optimizer can't predict.
@@ -38,9 +45,9 @@ func testNonPointerHeap() {
3845
}
3946

4047
// Allocate a randomly-sized slice, randomly sliced to be smaller.
41-
sliceLen := randuint32() % 1024
48+
sliceLen := randuint32() % maxSliceSize
4249
slice := make([]byte, sliceLen)
43-
cutLen := randuint32() % 1024
50+
cutLen := randuint32() % maxSliceSize
4451
if cutLen < sliceLen {
4552
slice = slice[cutLen:]
4653
}

0 commit comments

Comments
 (0)