fix(amd64): correct guard displacements, frameless FP offsets and immediate ranges
Assisted-by: GLM 5.3
This commit is contained in:
+37
-20
@@ -509,40 +509,43 @@ var evexBcastTable = map[string]evexBcastSpec{
|
||||
}
|
||||
|
||||
// evexMoveSpec describes an EVEX move (load and store opcodes, like the VEX
|
||||
// move table).
|
||||
// move table). vecOK and xmmOnly mirror the VEX twin's operand rules: a
|
||||
// scalar move (vecOK false, xmmOnly true) takes XMM↔memory operands only.
|
||||
type evexMoveSpec struct {
|
||||
mapSel int
|
||||
pp int
|
||||
load byte // r/m → vector
|
||||
store byte // vector → r/m
|
||||
w int
|
||||
n [3]int
|
||||
mapSel int
|
||||
pp int
|
||||
load byte // r/m → vector
|
||||
store byte // vector → r/m
|
||||
w int
|
||||
n [3]int
|
||||
vecOK bool // the non-memory operand may be a vector register
|
||||
xmmOnly bool // wider than XMM registers are rejected
|
||||
}
|
||||
|
||||
// evexMoveTable maps an upper-case EVEX move mnemonic to its encoding.
|
||||
var evexMoveTable = map[string]evexMoveSpec{
|
||||
// EVEX.128/256/512.F3.0F.W0, unaligned integer move.
|
||||
"VMOVDQU32": {1, 2, 0x6F, 0x7F, 0, [3]int{16, 32, 64}},
|
||||
"VMOVDQU32": {1, 2, 0x6F, 0x7F, 0, [3]int{16, 32, 64}, true, false},
|
||||
// EVEX.128/256/512.F3.0F.W1, unaligned qword move.
|
||||
"VMOVDQU64": {1, 2, 0x6F, 0x7F, 1, [3]int{16, 32, 64}},
|
||||
"VMOVDQU64": {1, 2, 0x6F, 0x7F, 1, [3]int{16, 32, 64}, true, false},
|
||||
// EVEX.128/256/512.F2.0F.W0, unaligned byte move (byte/word moves use the
|
||||
// F2 prefix, dword/qword moves F3; the element size only changes the tuple
|
||||
// semantics).
|
||||
"VMOVDQU8": {1, 3, 0x6F, 0x7F, 0, [3]int{16, 32, 64}},
|
||||
"VMOVDQU8": {1, 3, 0x6F, 0x7F, 0, [3]int{16, 32, 64}, true, false},
|
||||
// EVEX.128/256/512.F2.0F.W1, unaligned word move (shares the qword
|
||||
// encoding).
|
||||
"VMOVDQU16": {1, 3, 0x6F, 0x7F, 1, [3]int{16, 32, 64}},
|
||||
"VMOVDQU16": {1, 3, 0x6F, 0x7F, 1, [3]int{16, 32, 64}, true, false},
|
||||
// EVEX.128/256/512.66.0F.W1, unaligned packed double move.
|
||||
"VMOVUPD": {1, 1, 0x10, 0x11, 1, [3]int{16, 32, 64}},
|
||||
"VMOVUPD": {1, 1, 0x10, 0x11, 1, [3]int{16, 32, 64}, true, false},
|
||||
// EVEX.128/256/512, aligned packed moves.
|
||||
"VMOVAPS": {1, 0, 0x28, 0x29, 0, [3]int{16, 32, 64}},
|
||||
"VMOVAPD": {1, 1, 0x28, 0x29, 1, [3]int{16, 32, 64}},
|
||||
"VMOVAPS": {1, 0, 0x28, 0x29, 0, [3]int{16, 32, 64}, true, false},
|
||||
"VMOVAPD": {1, 1, 0x28, 0x29, 1, [3]int{16, 32, 64}, true, false},
|
||||
// EVEX.128/256/512.66.0F, aligned integer moves.
|
||||
"VMOVDQA32": {1, 1, 0x6F, 0x7F, 0, [3]int{16, 32, 64}},
|
||||
"VMOVDQA64": {1, 1, 0x6F, 0x7F, 1, [3]int{16, 32, 64}},
|
||||
"VMOVDQA32": {1, 1, 0x6F, 0x7F, 0, [3]int{16, 32, 64}, true, false},
|
||||
"VMOVDQA64": {1, 1, 0x6F, 0x7F, 1, [3]int{16, 32, 64}, true, false},
|
||||
// EVEX.128.F3.0F.W0, scalar single move, memory operands (the
|
||||
// three-operand register form is not supported).
|
||||
"VMOVSS": {1, 2, 0x10, 0x11, 0, [3]int{4, 4, 4}},
|
||||
"VMOVSS": {1, 2, 0x10, 0x11, 0, [3]int{4, 4, 4}, false, true},
|
||||
}
|
||||
|
||||
// isEvex reports whether the mnemonic has an EVEX encoding we handle.
|
||||
@@ -1022,6 +1025,12 @@ func (e *enc) encodeEvexMove(mnem string, ms evexMoveSpec, ops []Operand, mask i
|
||||
var rm Operand
|
||||
switch {
|
||||
case srcIsVec && dstIsVec:
|
||||
// A store-form reg-reg move, the layout the Go assembler uses; a
|
||||
// scalar move has no two-register form at all (the register form
|
||||
// takes three operands), matching the VEX twin's vecOK rule.
|
||||
if !ms.vecOK {
|
||||
return fmt.Errorf("%s does not take two vector registers", mnem)
|
||||
}
|
||||
reg, rm = srcReg, dst
|
||||
case srcIsVec:
|
||||
if !memOperand(dst) {
|
||||
@@ -1037,6 +1046,12 @@ func (e *enc) encodeEvexMove(mnem string, ms evexMoveSpec, ops []Operand, mask i
|
||||
default:
|
||||
return fmt.Errorf("%s needs a vector register operand", mnem)
|
||||
}
|
||||
// The scalar move is 128-bit only, so the register the length follows
|
||||
// must be an XMM (the VEX twin's xmmOnly rule; EVEX also reaches ZMM,
|
||||
// hence the inequality rather than a YMM test).
|
||||
if ms.xmmOnly && reg.size != 16 {
|
||||
return fmt.Errorf("%s operates on XMM registers only", mnem)
|
||||
}
|
||||
spec := evexSpec{mapSel: ms.mapSel, opcode: op, w: ms.w, pp: ms.pp, opdigit: -1, n: ms.n}
|
||||
return e.emitEvexFields(spec, reg.vecLenBit(), reg.idx, -1, rm, mask, sfx)
|
||||
}
|
||||
@@ -1171,9 +1186,6 @@ func (e *enc) emitEvexFields(spec evexSpec, ll, regIdx, vvvvIdx int, rm Operand,
|
||||
if r.idx&16 != 0 {
|
||||
xBar = 0
|
||||
}
|
||||
if r.idx&16 != 0 {
|
||||
xBar = 0
|
||||
}
|
||||
case Mem:
|
||||
var err error
|
||||
modrm, sib, disp, xBar, bBar, err = memComponentsEvex(regIdx&7, r, spec.n[ll])
|
||||
@@ -1232,6 +1244,11 @@ func (e *enc) emitEvexFields(spec evexSpec, ll, regIdx, vvvvIdx int, rm Operand,
|
||||
func memComponentsEvex(regField int, m Mem, n int) (modrm, sib int, disp []byte, xBar, bBar int, err error) {
|
||||
sib = -1
|
||||
xBar, bBar = 1, 1 // inverted bits: 1 = no extension
|
||||
// The disp32 fallback bounds the displacement by int32, and the
|
||||
// compressed disp8 form reaches at most ±127×64, well inside it.
|
||||
if m.Disp < -(1<<31) || m.Disp > (1<<31)-1 {
|
||||
return 0, -1, nil, 0, 0, fmt.Errorf("displacement %d does not fit in 32 bits", m.Disp)
|
||||
}
|
||||
if !m.HasBase && !m.HasIndex {
|
||||
return regField<<3 | 0x05, -1, le32(m.Disp), 1, 1, nil // RIP-relative
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user