feat(asm): give riscv64 the END and GETCALLERPC the toolchain accepts
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
0211d6672d
commit
76f8ba6403
3 files changed
+195
-6
No files matched your search
+99
-2
@@ -380,10 +380,14 @@ func riscvInstrSize(instr *ast.Instr, fi riscvFrameInfo, tlsSyms map[string]bool
|
||||
mnem := instr.Mnemonic.Text
|
||||
ops := instr.Operands
|
||||
mnem = riscvNormalisePseudo(mnem)
|
||||
if mnem == "FUNCDATA" || mnem == "PCDATA" {
|
||||
// The bookkeeping statements contribute no bytes.
|
||||
if mnem == "FUNCDATA" || mnem == "PCDATA" || mnem == "END" {
|
||||
// The bookkeeping statements and the function-end marker contribute
|
||||
// no bytes.
|
||||
return 0
|
||||
}
|
||||
if mnem == "GETCALLERPC" {
|
||||
return riscvGetCallerPCSize(ops, fi)
|
||||
}
|
||||
var immNeg bool
|
||||
mnem, immNeg = riscvNormaliseImmAlias(mnem, ops)
|
||||
if mnem == "RET" {
|
||||
@@ -1136,6 +1140,12 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
return nil, fmt.Errorf("PCDATA expects $n, $m")
|
||||
}
|
||||
return nil, nil
|
||||
case "END":
|
||||
// The function-end marker: the toolchain accepts it anywhere in a
|
||||
// body, ignores whatever operands follow it, and emits nothing.
|
||||
return nil, nil
|
||||
case "GETCALLERPC":
|
||||
return encodeRISCVGetCallerPC(ops, fi, relocs)
|
||||
case "WORD":
|
||||
// WORD $w lays down a raw 32-bit little-endian word, in the range
|
||||
// [0, 0xffffffff] exactly as the toolchain's validation bounds it.
|
||||
@@ -2429,6 +2439,65 @@ func riscvFrameMemSize(op *ast.Operand, fi riscvFrameInfo) int {
|
||||
return len(riscvAddressInX31WithBase(off, rs1)) + 4
|
||||
}
|
||||
|
||||
// encodeRISCVGetCallerPC encodes the toolchain's GETCALLERPC rewrite: on
|
||||
// entry the caller's address sits in the link register, so a leaf reads it
|
||||
// straight from X1 (MOV X1, rd, which the compress pass turns into C.MV);
|
||||
// a body that calls out has clobbered X1, and reads the prologue's save at
|
||||
// 0(SP) instead (LD rd, 0(SP), compressed to C.LDSP). A memory destination
|
||||
// stores the address there through the MOV store paths, which the leaf form
|
||||
// shares with every width and offset; the framed form is the toolchain's
|
||||
// own rejection, its rewrite reading (SP) into a MOV with two memory ends.
|
||||
func encodeRISCVGetCallerPC(ops []*ast.Operand, fi riscvFrameInfo, relocs *[]Reloc) ([]byte, error) {
|
||||
if len(ops) == 0 {
|
||||
return nil, fmt.Errorf("GETCALLERPC: unsupported MOV")
|
||||
}
|
||||
dst := ops[len(ops)-1]
|
||||
if isMemOperand(dst) {
|
||||
if !fi.leaf {
|
||||
return nil, fmt.Errorf("GETCALLERPC: unsupported MOV")
|
||||
}
|
||||
if dst.Addr.Sym != nil && dst.Addr.Sym.Pseudo == "SB" {
|
||||
// A static destination rides the external store path, exactly
|
||||
// as MOV X1, sym(SB) does.
|
||||
return encodeRISCVSBStore(dst.Addr.Sym, 1, relocs), nil
|
||||
}
|
||||
if err := riscvWantMemBase("GETCALLERPC", "rs1", dst); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rs1, off, err := riscvAccessMem("GETCALLERPC", dst, fi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return riscvFrameMemOp(riscvEnc{0x23, 0x3, 0x00}, true, 1, rs1, off), nil
|
||||
}
|
||||
n, bank := riscvBankedRegNum(operandRegName(dst))
|
||||
if bank != riscvBankInt {
|
||||
return nil, fmt.Errorf("GETCALLERPC: expected integer register in rd position but got non-integer register %s", operandRegName(dst))
|
||||
}
|
||||
if fi.leaf {
|
||||
return wordLE(riscvIType(riscvInstrTable["ADDI"], n, 1, 0)), nil
|
||||
}
|
||||
return wordLE(riscvIType(riscvInstrTable["LD"], n, 2, 0)), nil
|
||||
}
|
||||
|
||||
// riscvGetCallerPCSize returns the encoded size of a GETCALLERPC for the
|
||||
// layout pass: one word for the register reads, the store's own size
|
||||
// (expanded where the offset leaves imm12) for a memory destination.
|
||||
func riscvGetCallerPCSize(ops []*ast.Operand, fi riscvFrameInfo) int {
|
||||
if len(ops) == 0 {
|
||||
return 4
|
||||
}
|
||||
dst := ops[len(ops)-1]
|
||||
if isMemOperand(dst) && (dst.Addr.Sym == nil || dst.Addr.Sym.Pseudo != "SB") {
|
||||
if err := riscvWantMemOffset("GETCALLERPC", dst, fi); err == nil {
|
||||
if rs1, off := memFromOperandWithFrame(dst, fi); rs1 >= 0 && !fits12(off) {
|
||||
return len(riscvAddressInX31WithBase(off, rs1)) + 4
|
||||
}
|
||||
}
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
// riscvMemInstrSize returns the encoded size of a plain load or store
|
||||
// (integer and FP widths, one register end and one memory end) for the layout
|
||||
// pass: 4 bytes when the offset fits the signed 12-bit span, otherwise the
|
||||
@@ -3124,6 +3193,34 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
return rvcCS(0x7, rvcReg3(rs2), rvcReg3(rs1), uint32(imm)), true
|
||||
}
|
||||
|
||||
case "GETCALLERPC":
|
||||
// The rewrite lands on MOV X1, rd (leaf), LD rd, 0(SP) (framed) or
|
||||
// SD X1, off(base) (leaf, memory destination); each compresses the
|
||||
// way its own shape does, the toolchain's compress() rules verbatim.
|
||||
if len(ops) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
dst := ops[len(ops)-1]
|
||||
if !isMemOperand(dst) {
|
||||
rd := regFromOperand(dst)
|
||||
if fi.leaf {
|
||||
if rd > 0 {
|
||||
return rvcCR(0x8, uint32(rd), 1), true // C.MV rd, X1
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
// LD rd, 0(SP): the SP form takes every destination but X0.
|
||||
if rd > 0 {
|
||||
return rvcLSP(0x3, uint32(rd), 0), true // C.LDSP rd, 0(SP)
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
if fi.leaf {
|
||||
if rs1, off := memFromOperandWithFrame(dst, fi); rs1 == 2 && off >= 0 && off < 512 && off%8 == 0 {
|
||||
return rvcSSP(0x7, 1, uint32(off)), true // C.SDSP X1, off
|
||||
}
|
||||
}
|
||||
|
||||
case "LW":
|
||||
rd, rs1, imm := extractLDParams(instr, fi)
|
||||
if rs1 == 2 && rd != 0 && rd != -1 && imm >= 0 && imm < 256 && imm%4 == 0 {
|
||||
|
||||
Reference in new issue
Block a user