feat(arch): add the scaled index to the amd64 memory operands
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
ccb155437e
commit
11cac26508
4 files changed
+183
-44
No files matched your search
+106
-44
@@ -22,9 +22,9 @@
|
||||
// memory forms beside them, the scalar ones the manual spells m16 and the
|
||||
// packed ones with the {1toN} broadcast, base-relative operands with the
|
||||
// ModR/M disp8 and disp32 choices and the SIB byte RSP and R12 demand, the
|
||||
// broadcast laying EVEX.b over the same displacement semantics. A scaled
|
||||
// index, write masking ({k1}{z}) and embedded rounding still arrive with a
|
||||
// later slice.
|
||||
// scaled index and the broadcast laying the SIB byte and EVEX.b over the
|
||||
// same displacement semantics. Write masking ({k1}{z}) and embedded
|
||||
// rounding still arrive with a later slice.
|
||||
|
||||
package arch
|
||||
|
||||
@@ -198,6 +198,94 @@ func amd64EncodeBroadcast(b []byte, dest, vvvv, base int, disp int64) []byte {
|
||||
return out
|
||||
}
|
||||
|
||||
// amd64EncodeScaledMemory returns the register-form template with a
|
||||
// base-plus-scaled-index memory operand filled in: the SIB byte follows the
|
||||
// ModR/M and carries the scale field, the index and the base, whose number
|
||||
// rides the r/m field as 100. In a SIB form EVEX.B keeps carrying base bit
|
||||
// three, as amd64Encode laid it from the base, and EVEX.X changes meaning
|
||||
// from the register's bit four to the index's bit three, so it clears when
|
||||
// the index sits above 7. The ModR/M and displacement choices stay the
|
||||
// canonical ones amd64EncodeMemory makes, with the RBP and R13 bases
|
||||
// keeping their forced displacement: with a SIB byte present, mod 00 with
|
||||
// base 101 addresses baseless disp32, never through the base.
|
||||
func amd64EncodeScaledMemory(b []byte, dest, vvvv, base, index, scale int, disp int64) []byte {
|
||||
out := amd64Encode(b, dest, vvvv, base)
|
||||
if index&8 != 0 {
|
||||
out[1] &^= 0x40
|
||||
}
|
||||
rm := base & 7
|
||||
var tail []byte
|
||||
mod := byte(0)
|
||||
switch {
|
||||
case rm == 5 || disp != 0:
|
||||
if disp >= -128 && disp <= 127 {
|
||||
mod = 1
|
||||
tail = []byte{byte(disp)}
|
||||
} else {
|
||||
mod = 2
|
||||
tail = []byte{byte(disp), byte(disp >> 8), byte(disp >> 16), byte(disp >> 24)}
|
||||
}
|
||||
}
|
||||
sib := byte(rm)
|
||||
sib |= byte(index&7) << 3
|
||||
switch scale {
|
||||
case 2:
|
||||
sib |= 1 << 6
|
||||
case 4:
|
||||
sib |= 2 << 6
|
||||
case 8:
|
||||
sib |= 3 << 6
|
||||
}
|
||||
tail = append([]byte{sib}, tail...)
|
||||
out[5] = out[5]&0x38 | mod<<6 | 4
|
||||
return append(out, tail...)
|
||||
}
|
||||
|
||||
// amd64Index validates the scaled index of a memory operand: a general
|
||||
// register inside 0-15 and never RSP, whose SIB encoding 100 means no index,
|
||||
// and a scale the byte multipliers carry.
|
||||
func (in ExtInstr) amd64Index(op ExtOperand, pos int) (index, scale int, err error) {
|
||||
if op.Index < 0 || op.Index > 15 {
|
||||
return 0, 0, fmt.Errorf("%s: operand %d names index register %d, outside 0-15", in.Name, pos, op.Index)
|
||||
}
|
||||
if op.Index == 4 {
|
||||
return 0, 0, fmt.Errorf("%s: operand %d names RSP as the index, which the SIB byte cannot encode", in.Name, pos)
|
||||
}
|
||||
switch op.Scale {
|
||||
case 1, 2, 4, 8:
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("%s: operand %d carries a scale of %d, outside the byte multipliers 1, 2, 4 and 8", in.Name, pos, op.Scale)
|
||||
}
|
||||
return op.Index, op.Scale, nil
|
||||
}
|
||||
|
||||
// amd64MemBytes encodes one validated memory position: the plain
|
||||
// base-plus-displacement form, the scaled index over it, and the broadcast
|
||||
// bit over either, each an additive layer on the same displacement
|
||||
// semantics. The operand must have passed amd64Memory's kind gate, which
|
||||
// the encode paths reach only at the entry's Mem position.
|
||||
func (in ExtInstr) amd64MemBytes(b []byte, dest, vvvv int, op ExtOperand, pos int) ([]byte, error) {
|
||||
base, disp, err := in.amd64Memory(op, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if op.HasIndex {
|
||||
index, scale, err := in.amd64Index(op, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := amd64EncodeScaledMemory(b, dest, vvvv, base, index, scale, disp)
|
||||
if op.Broadcast {
|
||||
out[3] |= 0x10
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
if op.Broadcast {
|
||||
return amd64EncodeBroadcast(b, dest, vvvv, base, disp), nil
|
||||
}
|
||||
return amd64EncodeMemory(b, dest, vvvv, base, disp), nil
|
||||
}
|
||||
|
||||
// amd64PlainReg checks the invariants every amd64 register operand carries:
|
||||
// no arm64 arrangement, no predicate qualifier, and a register number inside
|
||||
// the class the instruction encodes. A broadcast spelling names a memory
|
||||
@@ -294,17 +382,10 @@ func (in ExtInstr) encodeAmdVec3(ops []ExtOperand) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
if in.Mem == 2 && ops[1].Kind == ExtMem {
|
||||
base, disp, err := in.amd64Memory(ops[1], 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := in.amd64Vector(ops[2], class, 3); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ops[1].Broadcast {
|
||||
return amd64EncodeBroadcast(in.Bytes, ops[2].Reg, ops[0].Reg, base, disp), nil
|
||||
}
|
||||
return amd64EncodeMemory(in.Bytes, ops[2].Reg, ops[0].Reg, base, disp), nil
|
||||
return in.amd64MemBytes(in.Bytes, ops[2].Reg, ops[0].Reg, ops[1], 2)
|
||||
}
|
||||
for i, op := range ops[1:] {
|
||||
if err := in.amd64Vector(op, class, i+2); err != nil {
|
||||
@@ -320,14 +401,10 @@ func (in ExtInstr) encodeAmdVec3(ops []ExtOperand) ([]byte, error) {
|
||||
// unused, which the encoding spells as vvvv 1111.
|
||||
func (in ExtInstr) encodeAmdMemVec(ops []ExtOperand) ([]byte, error) {
|
||||
class := amd64LengthClass(in.Bytes)
|
||||
base, disp, err := in.amd64Memory(ops[0], 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := in.amd64Vector(ops[1], class, 2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return amd64EncodeMemory(in.Bytes, ops[1].Reg, -1, base, disp), nil
|
||||
return in.amd64MemBytes(in.Bytes, ops[1].Reg, -1, ops[0], 1)
|
||||
}
|
||||
|
||||
// encodeAmdVecMem fills the memory-store form: src, mem. VMOVSH 4660(R9),
|
||||
@@ -339,11 +416,7 @@ func (in ExtInstr) encodeAmdVecMem(ops []ExtOperand) ([]byte, error) {
|
||||
if err := in.amd64Vector(ops[0], class, 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base, disp, err := in.amd64Memory(ops[1], 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return amd64EncodeMemory(in.Bytes, ops[0].Reg, -1, base, disp), nil
|
||||
return in.amd64MemBytes(in.Bytes, ops[0].Reg, -1, ops[1], 2)
|
||||
}
|
||||
|
||||
// encodeAmdVec2 fills the two-vector form: src, dest. The half form narrows
|
||||
@@ -360,27 +433,22 @@ func (in ExtInstr) encodeAmdVec2(ops []ExtOperand) ([]byte, error) {
|
||||
destClass = amd64HalfClass(class)
|
||||
}
|
||||
if in.Mem == 1 && ops[0].Kind == ExtMem {
|
||||
base, disp, err := in.amd64Memory(ops[0], 1)
|
||||
if err != nil {
|
||||
// The memory spelling is validated before the destination: on the
|
||||
// half form the destination's narrowed class is the likelier
|
||||
// rejection, but a miswritten source spelling names itself first.
|
||||
if _, _, err := in.amd64Memory(ops[0], 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := in.amd64Vector(ops[1], destClass, 2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ops[0].Broadcast {
|
||||
return amd64EncodeBroadcast(in.Bytes, ops[1].Reg, -1, base, disp), nil
|
||||
}
|
||||
return amd64EncodeMemory(in.Bytes, ops[1].Reg, -1, base, disp), nil
|
||||
return in.amd64MemBytes(in.Bytes, ops[1].Reg, -1, ops[0], 1)
|
||||
}
|
||||
if in.Mem == 2 && ops[1].Kind == ExtMem {
|
||||
if err := in.amd64Vector(ops[0], class, 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base, disp, err := in.amd64Memory(ops[1], 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return amd64EncodeMemory(in.Bytes, ops[0].Reg, -1, base, disp), nil
|
||||
return in.amd64MemBytes(in.Bytes, ops[0].Reg, -1, ops[1], 2)
|
||||
}
|
||||
if err := in.amd64Vector(ops[0], class, 1); err != nil {
|
||||
return nil, err
|
||||
@@ -449,14 +517,13 @@ func (in ExtInstr) encodeAmdVec3Imm(ops []ExtOperand) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
if in.Mem == 3 && ops[2].Kind == ExtMem {
|
||||
base, disp, err := in.amd64Memory(ops[2], 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := in.amd64Vector(ops[3], class, 4); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := amd64EncodeMemory(in.Bytes, ops[3].Reg, ops[1].Reg, base, disp)
|
||||
out, err := in.amd64MemBytes(in.Bytes, ops[3].Reg, ops[1].Reg, ops[2], 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(out, imm), nil
|
||||
}
|
||||
if err := in.amd64Vector(ops[2], class, 3); err != nil {
|
||||
@@ -489,11 +556,10 @@ func (in ExtInstr) encodeAmdMask2Imm(ops []ExtOperand) ([]byte, error) {
|
||||
}
|
||||
var out []byte
|
||||
if in.Mem == 3 && ops[2].Kind == ExtMem {
|
||||
base, disp, err := in.amd64Memory(ops[2], 3)
|
||||
out, err := in.amd64MemBytes(in.Bytes, ops[3].Reg, ops[1].Reg, ops[2], 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = amd64EncodeMemory(in.Bytes, ops[3].Reg, ops[1].Reg, base, disp)
|
||||
return append(out, imm), nil
|
||||
}
|
||||
if err := in.amd64Vector(ops[2], class, 3); err != nil {
|
||||
@@ -543,14 +609,10 @@ func (in ExtInstr) encodeAmdVecGprVec(ops []ExtOperand) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
if in.Mem == 2 && ops[1].Kind == ExtMem {
|
||||
base, disp, err := in.amd64Memory(ops[1], 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := in.amd64Vector(ops[2], class, 3); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return amd64EncodeMemory(in.Bytes, ops[2].Reg, ops[0].Reg, base, disp), nil
|
||||
return in.amd64MemBytes(in.Bytes, ops[2].Reg, ops[0].Reg, ops[1], 2)
|
||||
}
|
||||
if err := in.amd64Gpr(ops[1], 2); err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in new issue
Block a user