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.
Этот коммит содержится в:
родитель
159f0051bb
коммит
9af535bf98
14 изменённых файлов: 75 добавлений и 9 удалений
|
@ -31,7 +31,7 @@ func (b *builder) supportsRecover() bool {
|
|||
// proposal of WebAssembly:
|
||||
// https://github.com/WebAssembly/exception-handling
|
||||
return false
|
||||
case "avr", "riscv64", "xtensa":
|
||||
case "riscv64", "xtensa":
|
||||
// TODO: add support for these architectures
|
||||
return false
|
||||
default:
|
||||
|
@ -116,8 +116,8 @@ func (b *builder) createInvokeCheckpoint() {
|
|||
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
||||
// assembly but set to an unspecified non-zero value when jumping using
|
||||
// a longjmp.
|
||||
asmType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.deferFrame.Type()}, false)
|
||||
var asmString, constraints string
|
||||
resultType := b.uintptrType
|
||||
switch b.archFamily() {
|
||||
case "i386":
|
||||
asmString = `
|
||||
|
@ -163,6 +163,21 @@ mov x0, #0
|
|||
`
|
||||
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}"
|
||||
// TODO: SVE registers, which we don't use in TinyGo at the moment.
|
||||
case "avr":
|
||||
// Note: the Y register (R28:R29) is a fixed register and therefore
|
||||
// needs to be saved manually. TODO: do this only once per function with
|
||||
// a defer frame, not for every call.
|
||||
resultType = b.ctx.Int8Type()
|
||||
asmString = `
|
||||
ldi r24, pm_lo8(1f)
|
||||
ldi r25, pm_hi8(1f)
|
||||
std z+2, r24
|
||||
std z+3, r25
|
||||
std z+4, r28
|
||||
std z+5, r29
|
||||
ldi r24, 0
|
||||
1:`
|
||||
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}"
|
||||
case "riscv32":
|
||||
asmString = `
|
||||
la a2, 1f
|
||||
|
@ -174,10 +189,11 @@ li a0, 0
|
|||
// This case should have been handled by b.supportsRecover().
|
||||
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
|
||||
}
|
||||
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
|
||||
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
||||
result := b.CreateCall(asm, []llvm.Value{b.deferFrame}, "setjmp")
|
||||
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
||||
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(b.uintptrType, 0, false), "setjmp.result")
|
||||
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
b.SetInsertPointAtEnd(continueBB)
|
||||
|
|
2
compiler/testdata/defer-cortex-m-qemu.ll
предоставленный
2
compiler/testdata/defer-cortex-m-qemu.ll
предоставленный
|
@ -4,7 +4,7 @@ target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
|||
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
%runtime._defer = type { i32, %runtime._defer* }
|
||||
%runtime.deferFrame = type { i8*, i8*, %runtime.deferFrame*, i1, %runtime._interface }
|
||||
%runtime.deferFrame = type { i8*, i8*, [0 x i8*], %runtime.deferFrame*, i1, %runtime._interface }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) #0
|
||||
|
|
|
@ -349,6 +349,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
|
||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
|
||||
actual = bytes.Replace(actual, []byte{'.', '.', '\n'}, []byte{'\n'}, -1)
|
||||
actual = bytes.Replace(actual, []byte{'\n', '.', '\n'}, []byte{'\n', '\n'}, -1)
|
||||
}
|
||||
if name == "testing.go" {
|
||||
// Strip actual time.
|
||||
|
|
|
@ -5,6 +5,8 @@ const GOARCH = "386"
|
|||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 15) &^ 15
|
||||
|
|
|
@ -5,6 +5,8 @@ const GOARCH = "amd64"
|
|||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 64
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
// Align a pointer.
|
||||
// Note that some amd64 instructions (like movaps) expect 16-byte aligned
|
||||
// memory, thus the result must be 16-byte aligned.
|
||||
|
|
|
@ -8,6 +8,8 @@ const GOARCH = "arm"
|
|||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 3) &^ 3
|
||||
|
|
|
@ -5,6 +5,8 @@ const GOARCH = "arm64"
|
|||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 64
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 7) &^ 7
|
||||
|
|
|
@ -10,6 +10,8 @@ const GOARCH = "arm" // avr pretends to be arm
|
|||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 8
|
||||
|
||||
const deferExtraRegs = 1 // the frame pointer (Y register) also needs to be stored
|
||||
|
||||
// Align on a word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
// No alignment necessary on the AVR.
|
||||
|
|
|
@ -12,6 +12,8 @@ const GOARCH = "arm"
|
|||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 3) &^ 3
|
||||
|
|
|
@ -5,6 +5,8 @@ package runtime
|
|||
|
||||
import "device/riscv"
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
func getCurrentStackPointer() uintptr {
|
||||
return uintptr(stacksave())
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ const GOARCH = "wasm"
|
|||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
//go:extern __heap_base
|
||||
var heapStartSymbol [0]byte
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ const GOARCH = "arm" // xtensa pretends to be arm
|
|||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 32
|
||||
|
||||
const deferExtraRegs = 0
|
||||
|
||||
// Align on a word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 3) &^ 3
|
||||
|
|
|
@ -50,3 +50,33 @@ tinygo_scanCurrentStack:
|
|||
pop r17
|
||||
pop r28 // Y
|
||||
pop r29 // Y
|
||||
|
||||
|
||||
.section .text.tinygo_longjmp
|
||||
.global tinygo_longjmp
|
||||
tinygo_longjmp:
|
||||
; Move the *DeferFrame pointer to the X register.
|
||||
mov r26, r24
|
||||
mov r27, r25
|
||||
|
||||
; Load the stack pointer
|
||||
ld r24, X+
|
||||
ld r25, X+
|
||||
|
||||
; Switch to the given stack pointer.
|
||||
in r0, 0x3f ; SREG
|
||||
cli ; disable interrupts
|
||||
out 0x3d, r24 ; SPL
|
||||
out 0x3f, r0 ; re-enable interrupts (with one instruction delay)
|
||||
out 0x3e, r25 ; SPH
|
||||
|
||||
; Load the new PC
|
||||
ld r30, X+
|
||||
ld r31, X+
|
||||
|
||||
; Load the new Y register
|
||||
ld r28, X+
|
||||
ld r29, X+
|
||||
|
||||
; Jump to the PC (stored in the Z register)
|
||||
icall
|
||||
|
|
|
@ -25,11 +25,12 @@ func supportsRecover() bool
|
|||
// The compiler knows about the JumpPC struct offset, so it should not be moved
|
||||
// without also updating compiler/defer.go.
|
||||
type deferFrame struct {
|
||||
JumpSP unsafe.Pointer // stack pointer to return to
|
||||
JumpPC unsafe.Pointer // pc to return to
|
||||
Previous *deferFrame // previous recover buffer pointer
|
||||
Panicking bool // true iff this defer frame is panicking
|
||||
PanicValue interface{} // panic value, might be nil for panic(nil) for example
|
||||
JumpSP unsafe.Pointer // stack pointer to return to
|
||||
JumpPC unsafe.Pointer // pc to return to
|
||||
ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
|
||||
Previous *deferFrame // previous recover buffer pointer
|
||||
Panicking bool // true iff this defer frame is panicking
|
||||
PanicValue interface{} // panic value, might be nil for panic(nil) for example
|
||||
}
|
||||
|
||||
// Builtin function panic(msg), used as a compiler intrinsic.
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче