feat(asm): encode the arm64 register-offset addressing forms

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 02:36:24 +02:00
1 parent a344399b81
commit 458cdd2066
4 files changed
+221 -1

No files matched your search

+80 -1
View File
@@ -2008,6 +2008,14 @@ func encodeARM64MemOp(mnem string, mem *ast.Operand, reg int, load bool, fi arm6
lt = a64LoadTable["MOVD"]
}
// Register-offset addressing: (Rn)(Rm), (Rn)(Rm<<k), (Rn)(Rm.UXTW) and
// friends. The toolchain accepts the LSL (UXTX), UXTW, SXTW and SXTX
// options with a shift amount of either zero or the access size's log2,
// rejects every other spelling, and rejects the whole form on FMOVQ.
if mem.Addr.Index != "" {
return encodeARM64RegOffset(mnem, mem, rn, reg, load, lt, wb)
}
scale := a64LSScale(lt)
storeOpc := a64StoreOpc(lt)
var opc int
@@ -2094,7 +2102,78 @@ func arm64PoolAccess(mnem string, lt a64LSType, opc int, off int64, rn, reg, pc
// option the toolchain's register-offset accesses use:
// size<<30 | 0x38<<24 | V<<26 | opc<<22 | 1<<21 | Rm<<16 | 011<<13 | 10<<10 | Rn<<5 | Rt.
func a64LSReg(size, V, opc, rm, rn, rt uint32) uint32 {
return size<<30 | 0x38<<24 | V<<26 | opc<<22 | 1<<21 | rm<<16 | 3<<13 | 2<<10 | rn<<5 | rt
return a64LSRegExt(size, V, opc, rm, rn, rt, 3, 0)
}
// a64LSRegExt encodes the extended-register-offset load/store:
// size<<30 | 0x38<<24 | V<<26 | opc<<22 | 1<<21 | Rm<<16 | option<<13 |
// S<<12 | 10<<10 | Rn<<5 | Rt, with S meaning "scale by the access size".
func a64LSRegExt(size, V, opc, rm, rn, rt, option, s uint32) uint32 {
return size<<30 | 0x38<<24 | V<<26 | opc<<22 | 1<<21 | rm<<16 | option<<13 | s<<12 | 2<<10 | rn<<5 | rt
}
// encodeARM64RegOffset encodes the register-offset addressing forms of the
// MOV family: (Rn)(Rm), (Rn)(Rm*1), (Rn)(Rm<<k) and the extend spellings
// (Rn)(Rm.UXTW<<k), (Rn)(Rm.SXTW), (Rn)(Rm.SXTX<<k). The toolchain's
// contract, probed against `go tool asm`: the four options UXTX/LSL, UXTW,
// SXTW and SXTX exist (the spellings UXTX, UXTB, SXTH and friends are
// rejected), the shift amount is either zero or the access size's log2, FMOVQ
// carries no register-offset form at all, and writeback does not combine with
// an index.
func encodeARM64RegOffset(mnem string, mem *ast.Operand, rn, reg int, load bool, lt a64LSType, wb string) ([]byte, error) {
if wb != "" {
return nil, fmt.Errorf("%s: writeback does not combine with a register offset", mnem)
}
if mnem == "FMOVQ" {
return nil, fmt.Errorf("%s: illegal combination: the register offset form does not exist", mnem)
}
if mem.Addr.Scale > 1 {
return nil, fmt.Errorf("%s: the register offset addressing mode takes no scale factor", mnem)
}
name, ext, _ := strings.Cut(mem.Addr.Index, ".")
var option uint32
switch strings.ToUpper(ext) {
case "":
option = 3 // UXTX / LSL
case "UXTW":
option = 2
case "SXTW":
option = 6
case "SXTX":
option = 7
default:
return nil, fmt.Errorf("%s: invalid shift for the register offset addressing mode: %s", mnem, ext)
}
rm := arm64RegNum(name)
if rm < 0 {
return nil, fmt.Errorf("%s: invalid index register %q", mnem, name)
}
amount := 0
shift := strings.Join(strings.Fields(mem.Addr.Shift), "")
shift = strings.TrimSuffix(shift, ")")
if shift != "" {
if !strings.HasPrefix(shift, "<<") {
return nil, fmt.Errorf("%s: invalid shift for the register offset addressing mode: %s", mnem, shift)
}
v, err := strconv.Atoi(strings.TrimPrefix(shift, "<<"))
if err != nil {
return nil, fmt.Errorf("%s: invalid shift for the register offset addressing mode: %s", mnem, shift)
}
amount = v
}
log2 := bits.TrailingZeros64(uint64(a64LSScale(lt)))
if amount != 0 && amount != log2 {
return nil, fmt.Errorf("%s: invalid index shift amount %d", mnem, amount)
}
s := uint32(0)
if amount == log2 && log2 > 0 {
s = 1
}
opc := lt.opc
if !load {
opc = a64StoreOpc(lt)
}
return a64wordLE(a64LSRegExt(uint32(lt.size), uint32(lt.V), uint32(opc), uint32(rm), uint32(rn), uint32(reg), option, s)), nil
}
// arm64OffsetSplitReach reports whether an offset stays within the band the