feat(asm): emit the loong64 stack-split guard for big frames
This commit is contained in:
+22
-20
@@ -18,12 +18,10 @@ import (
|
||||
// then encoding with resolved branch targets).
|
||||
//
|
||||
// The emitted bytes match the Go toolchain's loong64 assembler, which is the
|
||||
// ground-truth oracle: prologue/epilogue, FP/SP frame mapping, branch
|
||||
// encodings and the MOV immediate expansions all follow cmd/internal/obj/
|
||||
// loong64's asmout cases. One deliberate difference: the stack-growth guard
|
||||
// (the morestack check in the prologue and the call back into the runtime in
|
||||
// the epilogue) is not emitted, so the bytes match only for NOSPLIT functions
|
||||
// or zero-frame leaves, where the toolchain emits no guard either.
|
||||
// ground-truth oracle: prologue/epilogue (including the large-frame R30
|
||||
// materialisations), FP/SP frame mapping, the stack-split guard classes, and
|
||||
// branch encodings all follow cmd/internal/obj/loong64. The morestack block
|
||||
// at the end of split functions carries the runtime.morestack_noctxt call.
|
||||
func assembleLOONG64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, []SpadjStep, error) {
|
||||
fi := loong64ComputeFrame(t)
|
||||
prologue := loong64Prologue(fi)
|
||||
@@ -39,11 +37,12 @@ func assembleLOONG64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry,
|
||||
var relocs []Reloc
|
||||
var spadj []SpadjStep
|
||||
|
||||
// The prologue (3 instructions when a frame is present) raises the SP
|
||||
// delta by autosize; the boundary is reported at the third instruction's
|
||||
// pc, exactly as the toolchain's pctospadj does.
|
||||
// The prologue raises the SP delta by autosize; the boundary is reported
|
||||
// after the SP adjust instruction, exactly as the toolchain's pctospadj
|
||||
// does. The prologue (3 instructions when a frame is present) may
|
||||
// materialise its store or adjust through R30, which widens it.
|
||||
if fi.autosize != 0 {
|
||||
spadj = append(spadj, SpadjStep{PC: guardLen + 8, Value: fi.autosize})
|
||||
spadj = append(spadj, SpadjStep{PC: guardLen + (loong64StoreWords(fi.autosize)+loong64AdjustWords(-int64(fi.autosize)))*4, Value: fi.autosize})
|
||||
}
|
||||
|
||||
// Pass 1: label offsets from the instruction sizes.
|
||||
@@ -99,14 +98,9 @@ func assembleLOONG64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry,
|
||||
preCount = len(relocs)
|
||||
lines = append(lines, LineEntry{Offset: pc, Line: in.Pos().Line})
|
||||
// The RET's epilogue closes the frame: the SP delta returns to zero
|
||||
// after the addi.d (one instruction for a leaf, two for a non-leaf
|
||||
// with the LR restore).
|
||||
// after the frame-deallocating ADDV.
|
||||
if strings.ToUpper(in.Mnemonic.Text) == "RET" && fi.autosize != 0 {
|
||||
epi := 4
|
||||
if !fi.leaf {
|
||||
epi = 8
|
||||
}
|
||||
spadj = append(spadj, SpadjStep{PC: pc + epi, Value: 0})
|
||||
spadj = append(spadj, SpadjStep{PC: pc + loong64EpilogueWords(fi)*4, Value: 0})
|
||||
}
|
||||
out = append(out, code...)
|
||||
pc += len(code)
|
||||
@@ -255,9 +249,9 @@ func encodeLOONG64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi loo
|
||||
}
|
||||
return l64wordLE(uint32(immFromOperand(ops[0]))), nil
|
||||
case "JMP", "B":
|
||||
return encodeLOONG64Branch(instr, mnem, pc, offsets, false, resolve)
|
||||
return encodeLOONG64Branch(instr, mnem, pc, offsets, false, resolve, relocs)
|
||||
case "JAL", "CALL", "BL":
|
||||
return encodeLOONG64Branch(instr, mnem, pc, offsets, true, resolve)
|
||||
return encodeLOONG64Branch(instr, mnem, pc, offsets, true, resolve, relocs)
|
||||
case "MOV", "MOVB", "MOVH", "MOVW", "MOVV", "MOVBU", "MOVHU", "MOVWU", "MOVF", "MOVD":
|
||||
return encodeLOONG64Mov(instr, mnem, fi, relocs)
|
||||
}
|
||||
@@ -516,7 +510,7 @@ func encodeLOONG64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi loo
|
||||
//
|
||||
// JMP/B label → b label JMP/B (rj) → jirl r0, rj, 0
|
||||
// JAL/CALL/BL label → bl label JAL/CALL/BL (rj) → jirl r1, rj, 0
|
||||
func encodeLOONG64Branch(instr *ast.Instr, mnem string, pc int, offsets map[string]int, link bool, resolve func(string) string) ([]byte, error) {
|
||||
func encodeLOONG64Branch(instr *ast.Instr, mnem string, pc int, offsets map[string]int, link bool, resolve func(string) string, relocs *[]Reloc) ([]byte, error) {
|
||||
if len(instr.Operands) != 1 {
|
||||
return nil, fmt.Errorf("%s expects 1 operand, got %d", mnem, len(instr.Operands))
|
||||
}
|
||||
@@ -533,6 +527,14 @@ func encodeLOONG64Branch(instr *ast.Instr, mnem string, pc int, offsets map[stri
|
||||
}
|
||||
return l64wordLE(l64irr16(l64branchTable["JIRL"], 0, rj, rd)), nil
|
||||
}
|
||||
// Direct symbol: sym+off(SB) → bl with an R_CALLLOONG64 relocation (the
|
||||
// linker fills the offset), as the toolchain does for CALL/BL/JAL.
|
||||
if op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "SB" && link {
|
||||
if relocs != nil {
|
||||
*relocs = append(*relocs, Reloc{Off: 0, After: 4, Name: op.Addr.Sym.Name, Kind: RelLoong64Branch, Addend: op.Addr.Sym.Offset})
|
||||
}
|
||||
return l64wordLE(l64bbl(l64jumpTable["BL"], 0)), nil
|
||||
}
|
||||
// Direct: label → b/bl.
|
||||
target := resolve(l64Label(op))
|
||||
targetOff, ok := offsets[target]
|
||||
|
||||
Reference in New Issue
Block a user