Skip to content

Commit 9af535b

Browse files
aykevldeadprogram
authored andcommitted
avr: add support for recover()
You can see that it works with the following command: tinygo run -target=simavr ./testdata/recover.go This also gets the following tests to pass again: go test -run=Build -target=simavr -v Adding support for AVR was a bit more compliated because it's also necessary to save and restore the Y register.
1 parent 159f005 commit 9af535b

14 files changed

+75
-9
lines changed

compiler/defer.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (b *builder) supportsRecover() bool {
3131
// proposal of WebAssembly:
3232
// https://github.com/WebAssembly/exception-handling
3333
return false
34-
case "avr", "riscv64", "xtensa":
34+
case "riscv64", "xtensa":
3535
// TODO: add support for these architectures
3636
return false
3737
default:
@@ -116,8 +116,8 @@ func (b *builder) createInvokeCheckpoint() {
116116
// * The return value (eax, rax, r0, etc) is set to zero in the inline
117117
// assembly but set to an unspecified non-zero value when jumping using
118118
// a longjmp.
119-
asmType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.deferFrame.Type()}, false)
120119
var asmString, constraints string
120+
resultType := b.uintptrType
121121
switch b.archFamily() {
122122
case "i386":
123123
asmString = `
@@ -163,6 +163,21 @@ mov x0, #0
163163
`
164164
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{fp},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
165165
// TODO: SVE registers, which we don't use in TinyGo at the moment.
166+
case "avr":
167+
// Note: the Y register (R28:R29) is a fixed register and therefore
168+
// needs to be saved manually. TODO: do this only once per function with
169+
// a defer frame, not for every call.
170+
resultType = b.ctx.Int8Type()
171+
asmString = `
172+
ldi r24, pm_lo8(1f)
173+
ldi r25, pm_hi8(1f)
174+
std z+2, r24
175+
std z+3, r25
176+
std z+4, r28
177+
std z+5, r29
178+
ldi r24, 0
179+
1:`
180+
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
166181
case "riscv32":
167182
asmString = `
168183
la a2, 1f
@@ -174,10 +189,11 @@ li a0, 0
174189
// This case should have been handled by b.supportsRecover().
175190
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
176191
}
192+
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
177193
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
178194
result := b.CreateCall(asm, []llvm.Value{b.deferFrame}, "setjmp")
179195
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
180-
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(b.uintptrType, 0, false), "setjmp.result")
196+
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
181197
continueBB := b.insertBasicBlock("")
182198
b.CreateCondBr(isZero, continueBB, b.landingpad)
183199
b.SetInsertPointAtEnd(continueBB)

compiler/testdata/defer-cortex-m-qemu.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
44
target triple = "thumbv7m-unknown-unknown-eabi"
55

66
%runtime._defer = type { i32, %runtime._defer* }
7-
%runtime.deferFrame = type { i8*, i8*, %runtime.deferFrame*, i1, %runtime._interface }
7+
%runtime.deferFrame = type { i8*, i8*, [0 x i8*], %runtime.deferFrame*, i1, %runtime._interface }
88
%runtime._interface = type { i32, i8* }
99

1010
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0

main_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
349349
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
350350
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
351351
actual = bytes.Replace(actual, []byte{'.', '.', '\n'}, []byte{'\n'}, -1)
352+
actual = bytes.Replace(actual, []byte{'\n', '.', '\n'}, []byte{'\n', '\n'}, -1)
352353
}
353354
if name == "testing.go" {
354355
// Strip actual time.

src/runtime/arch_386.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const GOARCH = "386"
55
// The bitness of the CPU (e.g. 8, 32, 64).
66
const TargetBits = 32
77

8+
const deferExtraRegs = 0
9+
810
// Align on word boundary.
911
func align(ptr uintptr) uintptr {
1012
return (ptr + 15) &^ 15

src/runtime/arch_amd64.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const GOARCH = "amd64"
55
// The bitness of the CPU (e.g. 8, 32, 64).
66
const TargetBits = 64
77

8+
const deferExtraRegs = 0
9+
810
// Align a pointer.
911
// Note that some amd64 instructions (like movaps) expect 16-byte aligned
1012
// memory, thus the result must be 16-byte aligned.

src/runtime/arch_arm.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const GOARCH = "arm"
88
// The bitness of the CPU (e.g. 8, 32, 64).
99
const TargetBits = 32
1010

11+
const deferExtraRegs = 0
12+
1113
// Align on word boundary.
1214
func align(ptr uintptr) uintptr {
1315
return (ptr + 3) &^ 3

src/runtime/arch_arm64.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const GOARCH = "arm64"
55
// The bitness of the CPU (e.g. 8, 32, 64).
66
const TargetBits = 64
77

8+
const deferExtraRegs = 0
9+
810
// Align on word boundary.
911
func align(ptr uintptr) uintptr {
1012
return (ptr + 7) &^ 7

src/runtime/arch_avr.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const GOARCH = "arm" // avr pretends to be arm
1010
// The bitness of the CPU (e.g. 8, 32, 64).
1111
const TargetBits = 8
1212

13+
const deferExtraRegs = 1 // the frame pointer (Y register) also needs to be stored
14+
1315
// Align on a word boundary.
1416
func align(ptr uintptr) uintptr {
1517
// No alignment necessary on the AVR.

src/runtime/arch_cortexm.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const GOARCH = "arm"
1212
// The bitness of the CPU (e.g. 8, 32, 64).
1313
const TargetBits = 32
1414

15+
const deferExtraRegs = 0
16+
1517
// Align on word boundary.
1618
func align(ptr uintptr) uintptr {
1719
return (ptr + 3) &^ 3

src/runtime/arch_tinygoriscv.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ package runtime
55

66
import "device/riscv"
77

8+
const deferExtraRegs = 0
9+
810
func getCurrentStackPointer() uintptr {
911
return uintptr(stacksave())
1012
}

src/runtime/arch_tinygowasm.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const GOARCH = "wasm"
1212
// The bitness of the CPU (e.g. 8, 32, 64).
1313
const TargetBits = 32
1414

15+
const deferExtraRegs = 0
16+
1517
//go:extern __heap_base
1618
var heapStartSymbol [0]byte
1719

src/runtime/arch_xtensa.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const GOARCH = "arm" // xtensa pretends to be arm
88
// The bitness of the CPU (e.g. 8, 32, 64).
99
const TargetBits = 32
1010

11+
const deferExtraRegs = 0
12+
1113
// Align on a word boundary.
1214
func align(ptr uintptr) uintptr {
1315
return (ptr + 3) &^ 3

src/runtime/asm_avr.S

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,33 @@ tinygo_scanCurrentStack:
5050
pop r17
5151
pop r28 // Y
5252
pop r29 // Y
53+
54+
55+
.section .text.tinygo_longjmp
56+
.global tinygo_longjmp
57+
tinygo_longjmp:
58+
; Move the *DeferFrame pointer to the X register.
59+
mov r26, r24
60+
mov r27, r25
61+
62+
; Load the stack pointer
63+
ld r24, X+
64+
ld r25, X+
65+
66+
; Switch to the given stack pointer.
67+
in r0, 0x3f ; SREG
68+
cli ; disable interrupts
69+
out 0x3d, r24 ; SPL
70+
out 0x3f, r0 ; re-enable interrupts (with one instruction delay)
71+
out 0x3e, r25 ; SPH
72+
73+
; Load the new PC
74+
ld r30, X+
75+
ld r31, X+
76+
77+
; Load the new Y register
78+
ld r28, X+
79+
ld r29, X+
80+
81+
; Jump to the PC (stored in the Z register)
82+
icall

src/runtime/panic.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ func supportsRecover() bool
2525
// The compiler knows about the JumpPC struct offset, so it should not be moved
2626
// without also updating compiler/defer.go.
2727
type deferFrame struct {
28-
JumpSP unsafe.Pointer // stack pointer to return to
29-
JumpPC unsafe.Pointer // pc to return to
30-
Previous *deferFrame // previous recover buffer pointer
31-
Panicking bool // true iff this defer frame is panicking
32-
PanicValue interface{} // panic value, might be nil for panic(nil) for example
28+
JumpSP unsafe.Pointer // stack pointer to return to
29+
JumpPC unsafe.Pointer // pc to return to
30+
ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
31+
Previous *deferFrame // previous recover buffer pointer
32+
Panicking bool // true iff this defer frame is panicking
33+
PanicValue interface{} // panic value, might be nil for panic(nil) for example
3334
}
3435

3536
// Builtin function panic(msg), used as a compiler intrinsic.

0 commit comments

Comments
 (0)