feat(asm): encode the GETCALLERPC, REM, DWORD and half-FCVT shapes
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
df7a5091e0
commit
4066396226
3 files changed
+217
No files matched your search
@@ -439,6 +439,15 @@ func arm64InstrSize(instr *ast.Instr, fi arm64FrameInfo, pos int) int {
|
||||
if mnem == "BYTE" {
|
||||
return len(ops)
|
||||
}
|
||||
if mnem == "DWORD" {
|
||||
// Eight little-endian bytes per immediate.
|
||||
return 8 * len(ops)
|
||||
}
|
||||
// The remainder family: the division into REGTMP plus the MSUB tail.
|
||||
switch mnem {
|
||||
case "REM", "REMW", "UREM", "UREMW":
|
||||
return 8
|
||||
}
|
||||
if mnem == "NOP" {
|
||||
return 0 // the zero-size pseudo-instruction, operand or not
|
||||
}
|
||||
@@ -610,6 +619,41 @@ func encodeARM64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi arm64
|
||||
return nil, fmt.Errorf("WORD: immediate %d does not fit a 32-bit word", w)
|
||||
}
|
||||
return a64wordLE(uint32(w)), nil
|
||||
case "DWORD":
|
||||
// The toolchain's ADWORD (asm7.go case 11): eight little-endian
|
||||
// bytes per immediate. A symbol operand would need the data
|
||||
// section's relocation plumbing, which the code path does not
|
||||
// reach, so the immediate form alone is supported.
|
||||
if len(ops) == 0 {
|
||||
return nil, fmt.Errorf("DWORD expects an operand")
|
||||
}
|
||||
var out []byte
|
||||
for _, op := range ops {
|
||||
if !isImmOperand(op) || op.Imm.Sym != nil {
|
||||
return nil, fmt.Errorf("DWORD: only an immediate operand is supported")
|
||||
}
|
||||
v := arm64Imm64(op)
|
||||
out = append(out, byte(v), byte(v>>8), byte(v>>16), byte(v>>24),
|
||||
byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56))
|
||||
}
|
||||
return out, nil
|
||||
case "GETCALLERPC":
|
||||
// The toolchain's rewrite (obj7.go AGETCALLERPC): a leaf reads the
|
||||
// link register, a function with a frame reads the saved LR at
|
||||
// 0(SP), where both prologue shapes leave it.
|
||||
if len(ops) != 1 {
|
||||
return nil, fmt.Errorf("GETCALLERPC expects 1 operand, got %d", len(ops))
|
||||
}
|
||||
rd := arm64RegNum(operandRegName(ops[0]))
|
||||
if rd < 0 {
|
||||
return nil, fmt.Errorf("invalid register operand in GETCALLERPC")
|
||||
}
|
||||
if fi.leaf {
|
||||
return a64wordLE(0xaa000000 | 30<<16 | 31<<5 | uint32(rd)), nil // MOVD R30, Rd
|
||||
}
|
||||
return a64wordLE(a64LSU(3, 0, 1, 0, 31, uint32(rd))), nil // MOVD (RSP), Rd
|
||||
case "REM", "REMW", "UREM", "UREMW":
|
||||
return encodeARM64Rem(mnem, ops)
|
||||
case "B", "JMP":
|
||||
return encodeARM64Branch(mnem, ops, pc, offsets, false, relocs, resolve)
|
||||
case "BL", "CALL":
|
||||
@@ -2059,6 +2103,43 @@ func encodeARM64Mov(instr *ast.Instr, mnem string, wb string, fi arm64FrameInfo,
|
||||
return encodeARM64RegMove(mnem, src, dst)
|
||||
}
|
||||
|
||||
// encodeARM64Rem encodes the remainder family the toolchain's optab case
|
||||
// 16 does: the quotient rides REGTMP (R27) through SDIV/UDIV and the
|
||||
// remainder is the minuend less REGTMP times the divisor, an MSUB whose W
|
||||
// forms keep the 32-bit size. The two-operand spelling divides into the
|
||||
// destination (asm7.go: r defaults to rt), and an RSP destination is the
|
||||
// toolchain's illegal combination.
|
||||
func encodeARM64Rem(mnem string, ops []*ast.Operand) ([]byte, error) {
|
||||
if len(ops) != 2 && len(ops) != 3 {
|
||||
return nil, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
rd := arm64RegNum(operandRegName(ops[len(ops)-1]))
|
||||
if strings.EqualFold(operandRegName(ops[len(ops)-1]), "RSP") {
|
||||
return nil, fmt.Errorf("%s: illegal combination: the destination cannot be RSP", mnem)
|
||||
}
|
||||
rm := arm64RegNum(operandRegName(ops[0]))
|
||||
rn := rd
|
||||
if len(ops) == 3 {
|
||||
rn = arm64RegNum(operandRegName(ops[1]))
|
||||
}
|
||||
if rm < 0 || rn < 0 || rd < 0 {
|
||||
return nil, fmt.Errorf("invalid register operand in %s", mnem)
|
||||
}
|
||||
sf := uint32(1) // 64-bit
|
||||
if strings.HasSuffix(mnem, "W") {
|
||||
sf = 0
|
||||
}
|
||||
div := sf<<31 | 0xd6<<21 | 3<<10 // SDIV / SDIVW
|
||||
if strings.HasPrefix(mnem, "U") {
|
||||
div = sf<<31 | 0xd6<<21 | 2<<10 // UDIV / UDIVW
|
||||
}
|
||||
msub := sf<<31 | 0x1b<<24 | 1<<15 // MSUB / MSUBW
|
||||
return a64WordsLE(
|
||||
div|uint32(rm)<<16|uint32(rn)<<5|27,
|
||||
msub|uint32(rm)<<16|uint32(rn)<<10|27<<5|uint32(rd),
|
||||
), nil
|
||||
}
|
||||
|
||||
// arm64PairSize returns the encoded size of a load/store pair instruction,
|
||||
// mirroring the encoder's branch order exactly: the label offsets of pass 1
|
||||
// must match the bytes pass 2 lays down.
|
||||
|
||||
Reference in new issue
Block a user