fix(amd64): correct guard displacements, frameless FP offsets and immediate ranges
Assisted-by: GLM 5.3
This commit is contained in:
@@ -149,6 +149,45 @@ func TestPushPop(t *testing.T) {
|
||||
checkSyntax(t, "push rbx", "PUSHQ", BX)
|
||||
checkSyntax(t, "pop r12", "POPQ", Reg{idx: 12, size: 8})
|
||||
checkSyntax(t, "push 0x5", "PUSHQ", Imm(5))
|
||||
// The W spelling carries the 0x66 operand-size prefix, byte for byte
|
||||
// with go tool asm; the L and B spellings are illegal in 64-bit mode
|
||||
// there and rejected here rather than silently widened.
|
||||
cases := []struct {
|
||||
name string
|
||||
mnem string
|
||||
ops []Operand
|
||||
want string
|
||||
}{
|
||||
{"PUSHW AX", "PUSHW", []Operand{AX}, "6650"},
|
||||
{"POPW AX", "POPW", []Operand{AX}, "6658"},
|
||||
{"PUSHW $5", "PUSHW", []Operand{Imm(5)}, "666a05"},
|
||||
{"PUSHW (AX)", "PUSHW", []Operand{Ptr(AX, 0, 2)}, "66ff30"},
|
||||
{"PUSHQ AX", "PUSHQ", []Operand{AX}, "50"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
code, err := Encode(c.mnem, c.ops...)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
if got := fmt.Sprintf("%x", code); got != c.want {
|
||||
t.Errorf("%s: bytes %s, want %s", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
mnem string
|
||||
ops []Operand
|
||||
}{
|
||||
{"PUSHL AX", "PUSHL", []Operand{AX}},
|
||||
{"PUSHL R8", "PUSHL", []Operand{Reg{idx: 8, size: 8}}},
|
||||
{"POPL BX", "POPL", []Operand{BX}},
|
||||
{"PUSHB AX", "PUSHB", []Operand{AX}},
|
||||
} {
|
||||
if _, err := Encode(c.mnem, c.ops...); err == nil {
|
||||
t.Errorf("%s: expected an error, got none", c.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnary(t *testing.T) {
|
||||
@@ -397,6 +436,104 @@ func TestScalarErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestImmediateOutOfRange pins the go-tool-asm parity of the immediate and
|
||||
// displacement spans: a scalar immediate must fit a signed or unsigned 32-bit
|
||||
// word (only MOVQ reg, $imm takes the full int64), a scalar shift count must
|
||||
// be an unsigned byte, and a displacement must fit int32. Every rejected
|
||||
// shape here is rejected by `go tool asm` too; every accepted one encodes the
|
||||
// same bytes.
|
||||
func TestImmediateOutOfRange(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
mnem string
|
||||
ops []Operand
|
||||
}{
|
||||
{"SHLQ count 300", "SHLQ", []Operand{Imm(300), AX}},
|
||||
{"SHLQ count -1", "SHLQ", []Operand{Imm(-1), AX}},
|
||||
{"SHLW count 256", "SHLW", []Operand{Imm(256), DX}},
|
||||
{"SHLB count 300", "SHLB", []Operand{Imm(300), BL}},
|
||||
{"MOVL imm32+", "MOVL", []Operand{Imm(4294967296), AX}},
|
||||
{"MOVL imm32-", "MOVL", []Operand{Imm(-2147483649), AX}},
|
||||
{"MOVW imm32+", "MOVW", []Operand{Imm(4294967296), AX}},
|
||||
{"MOVB imm32+", "MOVB", []Operand{Imm(4294967296), AL}},
|
||||
{"ADDB imm32+", "ADDB", []Operand{Imm(4294967296), AL}},
|
||||
{"ADDL imm32+", "ADDL", []Operand{Imm(4294967296), AX}},
|
||||
{"ADDQ imm32+", "ADDQ", []Operand{Imm(8589934592), AX}},
|
||||
{"CMPQ imm32+", "CMPQ", []Operand{AX, Imm(4294967296)}},
|
||||
{"CMPQ imm32-", "CMPQ", []Operand{AX, Imm(-2147483649)}},
|
||||
{"TESTL imm32+", "TESTL", []Operand{Imm(4294967296), AX}},
|
||||
{"IMUL3L imm32+", "IMUL3L", []Operand{Imm(4294967296), CX, DX}},
|
||||
{"PUSHQ imm32+", "PUSHQ", []Operand{Imm(4294967296)}},
|
||||
{"MOVQ mem imm32+", "MOVQ", []Operand{Imm(4294967296), Ptr(AX, 0, 8)}},
|
||||
{"disp32+", "MOVQ", []Operand{Ptr(AX, 4294967296, 8), BX}},
|
||||
{"disp32+ max", "MOVQ", []Operand{Ptr(AX, 2147483648, 8), BX}},
|
||||
{"disp32-", "MOVQ", []Operand{Ptr(AX, -2147483649, 8), BX}},
|
||||
{"VEX disp32+", "VMOVDQU", []Operand{Ptr(AX, 4294967296, 32), vreg(t, "Y1")}},
|
||||
{"EVEX disp32+", "VMOVDQU32", []Operand{Ptr(AX, 4294967296, 64), vreg(t, "Z1")}},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if _, err := Encode(c.mnem, c.ops...); err == nil {
|
||||
t.Errorf("%s: expected an error, got none", c.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestImmediateTruncation pins the toolchain-matching truncations inside the
|
||||
// accepted 32-bit span: the narrower fields take the low bits silently, byte
|
||||
// for byte with `go tool asm` (which rejects none of these).
|
||||
func TestImmediateTruncation(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
mnem string
|
||||
ops []Operand
|
||||
want string
|
||||
}{
|
||||
{"ADDB $256,BL", "ADDB", []Operand{Imm(256), BL}, "80c300"},
|
||||
{"ADDB $1000,BL", "ADDB", []Operand{Imm(1000), BL}, "80c3e8"},
|
||||
{"MOVB $256,AL", "MOVB", []Operand{Imm(256), AL}, "b000"},
|
||||
{"MOVB $-129,AL", "MOVB", []Operand{Imm(-129), AL}, "b07f"},
|
||||
{"MOVW $65536,AX", "MOVW", []Operand{Imm(65536), AX}, "66b80000"},
|
||||
{"MOVW $65535,AX", "MOVW", []Operand{Imm(65535), AX}, "66b8ffff"},
|
||||
{"MOVW $-32769,AX", "MOVW", []Operand{Imm(-32769), AX}, "66b8ff7f"},
|
||||
{"MOVL $4294967295,AX", "MOVL", []Operand{Imm(4294967295), AX}, "b8ffffffff"},
|
||||
{"ADDQ $4294967295,AX", "ADDQ", []Operand{Imm(4294967295), AX}, "4805ffffffff"},
|
||||
{"CMPB BL,$255", "CMPB", []Operand{BL, Imm(255)}, "80fbff"},
|
||||
{"CMPQ AX,$4294967295", "CMPQ", []Operand{AX, Imm(4294967295)}, "483dffffffff"},
|
||||
{"MOVQ $4294967295,0(AX)", "MOVQ", []Operand{Imm(4294967295), Ptr(AX, 0, 8)}, "48c700ffffffff"},
|
||||
{"SHLQ $255,AX", "SHLQ", []Operand{Imm(255), AX}, "48c1e0ff"},
|
||||
{"SHLQ $0,AX", "SHLQ", []Operand{Imm(0), AX}, "48c1e000"},
|
||||
// The one form beyond the 32-bit span: the imm64 MOVQ register move.
|
||||
{"MOVQ $4294967296,AX", "MOVQ", []Operand{Imm(4294967296), AX}, "48b80000000001000000"},
|
||||
{"MOVQ disp32 max", "MOVQ", []Operand{Ptr(AX, 2147483647, 8), BX}, "488b98ffffff7f"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
code, err := Encode(c.mnem, c.ops...)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
if got := fmt.Sprintf("%x", code); got != c.want {
|
||||
t.Errorf("%s: bytes %s, want %s", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEncodableCmovSize pins the linter contract for CMOVcc: Encodable must
|
||||
// reject the spellings Encode rejects, so a mnemonic like CMOVBGT (no size
|
||||
// letter) is not reported as encodable.
|
||||
func TestEncodableCmovSize(t *testing.T) {
|
||||
for _, m := range []string{"CMOVBGT", "CMOVXEQ", "CMOVB", "CMOV", "CMOVWXX"} {
|
||||
if Encodable(m) {
|
||||
t.Errorf("Encodable(%q) = true, want false", m)
|
||||
}
|
||||
}
|
||||
for _, m := range []string{"CMOVLGT", "CMOVQGT", "CMOVWLS", "CMOVLEQ"} {
|
||||
if !Encodable(m) {
|
||||
t.Errorf("Encodable(%q) = false, want true", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSSEBinGroundTruth checks the legacy packed/scalar binary family
|
||||
// byte for byte (no prefix / 66 / F2 / F3 variants).
|
||||
func TestSSEBinGroundTruth(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user