fix(riscv64): compressed store offsets, FENCE and branch range checks
Assisted-by: GLM 5.3
This commit is contained in:
+34
-31
@@ -4,6 +4,7 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/ast"
|
||||
@@ -241,33 +242,36 @@ func riscvFitsCAddi(imm int32) bool {
|
||||
}
|
||||
|
||||
// riscvPrologueSpadjPC returns the function-relative byte offset where the
|
||||
// prologue has finished decrementing SP (the delta becomes autosize).
|
||||
// prologue has finished decrementing SP (the delta becomes autosize). It is
|
||||
// computed from the same expansion functions the prologue emits, so the
|
||||
// large-frame X31 materialisations are counted: C.LUI + C.ADD before the SD,
|
||||
// C.LUI + ADDIW + C.ADD for the SP adjust.
|
||||
func riscvPrologueSpadjPC(fi riscvFrameInfo) int {
|
||||
if fi.autosize == 0 {
|
||||
return 0
|
||||
}
|
||||
// SD (4 bytes) + ADDI/C.ADDI (2 or 4 bytes).
|
||||
return 4 + riscvSPAdjustLen(int32(-fi.autosize))
|
||||
adj := int32(-fi.autosize)
|
||||
if fits12(adj) {
|
||||
// SD (4 bytes) + ADDI/C.ADDI (2 or 4 bytes).
|
||||
return 4 + len(riscvSPAdjust(adj))
|
||||
}
|
||||
return len(riscvAddressInX31(adj)) + 4 + len(riscvAddToSP(adj))
|
||||
}
|
||||
|
||||
// riscvReturnEpilogueLen returns the byte length of the RET's epilogue up to
|
||||
// (but not including) the final JALR, the point where SP is restored.
|
||||
// (but not including) the final JALR, the point where SP is restored. The
|
||||
// small frame closes with C.LDSP + ADDI/C.ADDI; the large frame materialises
|
||||
// the adjustment through X31 (C.LUI + ADDIW + C.ADD).
|
||||
func riscvReturnEpilogueLen(fi riscvFrameInfo) int {
|
||||
if fi.autosize == 0 {
|
||||
return 0
|
||||
}
|
||||
// C.LDSP (2 bytes) + ADDI/C.ADDI (2 or 4 bytes).
|
||||
return 2 + riscvSPAdjustLen(int32(fi.autosize))
|
||||
}
|
||||
|
||||
func riscvSPAdjustLen(imm int32) int {
|
||||
if imm != 0 && imm%16 == 0 && imm >= -512 && imm <= 511 {
|
||||
return 2
|
||||
adj := int32(fi.autosize)
|
||||
if fits12(adj) {
|
||||
// C.LDSP (2 bytes) + ADDI/C.ADDI (2 or 4 bytes).
|
||||
return 2 + len(riscvSPAdjust(adj))
|
||||
}
|
||||
if riscvFitsCAddi(imm) {
|
||||
return 2
|
||||
}
|
||||
return 4
|
||||
return 2 + len(riscvAddToSP(adj))
|
||||
}
|
||||
|
||||
// riscvResolvePseudo translates a pseudo-register memory reference into a
|
||||
@@ -293,19 +297,21 @@ func riscvResolvePseudo(sym *ast.Symbol, fi riscvFrameInfo) (base int, off int32
|
||||
// including the inline morestack call (zero when the function needs no
|
||||
// guard). Unlike amd64 and arm64, the toolchain places the morestack call
|
||||
// between the guard and the body: the guard branches forward over it.
|
||||
func riscvGuardLen(fi riscvFrameInfo) int {
|
||||
_, reloc := riscvGuard(fi)
|
||||
_ = reloc
|
||||
return len(riscvGuardBytes(fi))
|
||||
func riscvGuardLen(fi riscvFrameInfo) (int, error) {
|
||||
g, _, err := riscvGuard(fi)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(g), nil
|
||||
}
|
||||
|
||||
// riscvGuard emits the stack-split guard prefix with the inline morestack
|
||||
// call: the branch skips forward over JAL X5 and JAL X0 straight into the
|
||||
// body; the JAL X5 carries the R_RISCV_JAL relocation. All offsets are
|
||||
// relative to the guard itself, which sits at function offset 0.
|
||||
func riscvGuard(fi riscvFrameInfo) ([]byte, Reloc) {
|
||||
func riscvGuard(fi riscvFrameInfo) ([]byte, Reloc, error) {
|
||||
if !fi.needSplit {
|
||||
return nil, Reloc{}
|
||||
return nil, Reloc{}, nil
|
||||
}
|
||||
// MOV 16(g), X6 (g.stackguard0), g = X27.
|
||||
out := wordLE(riscvIType(riscvEnc{0x03, 0x3, 0x00}, 6, 27, 16))
|
||||
@@ -317,14 +323,14 @@ func riscvGuard(fi riscvFrameInfo) ([]byte, Reloc) {
|
||||
var reloc Reloc
|
||||
switch fi.splitClass {
|
||||
case 0:
|
||||
// BLTU X6, SP, done (+8: over the CALL and the JMP back)
|
||||
// BLTU X6, SP, done (+12: over the CALL and the JMP back)
|
||||
out = append(out, wordLE(riscvBType(riscvEnc{0x63, 0x06, 0x00}, 6, 2, 12))...)
|
||||
call := len(out)
|
||||
reloc = Reloc{Off: call, After: call + 4, Name: "runtime\u00b7morestack_noctxt", Kind: RelRISCVJal}
|
||||
out = append(out, wordLE(riscvJType(5, 0))...)
|
||||
out = append(out, jalBack()...)
|
||||
case 1:
|
||||
// ADDI $-(framesize-StackSmall), SP, X7; BLTU X6, X7, done (+8)
|
||||
// ADDI $-(framesize-StackSmall), SP, X7; BLTU X6, X7, done (+12)
|
||||
off := int32(fi.autosize - stackSmall)
|
||||
out = append(out, wordLE(riscvIType(riscvEnc{0x13, 0x0, 0x00}, 7, 2, -off))...)
|
||||
out = append(out, wordLE(riscvBType(riscvEnc{0x63, 0x06, 0x00}, 6, 7, 12))...)
|
||||
@@ -342,7 +348,10 @@ func riscvGuard(fi riscvFrameInfo) ([]byte, Reloc) {
|
||||
out = append(out, wordLE(riscvBType(riscvEnc{0x63, 0x06, 0x00}, 2, 7, int32(addiLen+8)))...)
|
||||
addi, err := encodeRISCVItypeImmediate("ADDI", riscvEnc{0x13, 0x0, 0x00}, 7, 2, -off)
|
||||
if err != nil {
|
||||
addi = nil
|
||||
// The ADDI expansion failed: the SP adjustment this class
|
||||
// depends on is not emittable, and silently dropping it would
|
||||
// corrupt every stack reference in the body.
|
||||
return nil, Reloc{}, fmt.Errorf("stack-split guard: %w", err)
|
||||
}
|
||||
out = append(out, addi...)
|
||||
out = append(out, wordLE(riscvBType(riscvEnc{0x63, 0x06, 0x00}, 6, 7, 12))...)
|
||||
@@ -351,11 +360,5 @@ func riscvGuard(fi riscvFrameInfo) ([]byte, Reloc) {
|
||||
out = append(out, wordLE(riscvJType(5, 0))...)
|
||||
out = append(out, jalBack()...)
|
||||
}
|
||||
return out, reloc
|
||||
}
|
||||
|
||||
// riscvGuardBytes emits the guard prefix bytes alone (sizing helper).
|
||||
func riscvGuardBytes(fi riscvFrameInfo) []byte {
|
||||
g, _ := riscvGuard(fi)
|
||||
return g
|
||||
return out, reloc, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user