feat(arch): add the {1toN} broadcast to the packed amd64 memory sources

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 02:06:11 +02:00
1 parent 9c1392dfc3
commit ccb155437e
4 files changed
+218 -32

No files matched your search

+53
View File
@@ -76,6 +76,8 @@ func TestAmd64ExtMemoryRejects(t *testing.T) {
"arrangement"},
{"predicate qualifier on the memory operand", ExtOperand{Kind: ExtMem, Reg: 8, Qual: ExtQualZeroing},
"predicate qualifier"},
{"broadcast on an entry that takes none", ExtBroadcast(8, 0),
"takes none"},
} {
_, _, err := in.amd64Memory(tt.op, 1)
if err == nil {
@@ -86,6 +88,57 @@ func TestAmd64ExtMemoryRejects(t *testing.T) {
t.Errorf("%s: error %q lacks %q", tt.name, err, tt.quote)
}
}
// The broadcast spelling passes the flag gate on an entry that carries
// Bcast and then meets the same base and displacement checks.
bcast := ExtInstr{Name: "TEST", Bcast: true}
for _, tt := range []struct {
name string
op ExtOperand
quote string
}{
{"broadcast base beyond r15", ExtOperand{Kind: ExtMem, Reg: 16, Imm: 0, Broadcast: true},
"outside 0-15"},
{"broadcast displacement past the signed 32-bit ceiling", ExtBroadcast(8, 1<<31),
"outside the signed 32-bit range"},
{"broadcast base under r0", ExtBroadcast(-1, 0),
"outside 0-15"},
} {
if _, _, err := bcast.amd64Memory(tt.op, 1); err == nil {
t.Errorf("%s: validation succeeded, want an error", tt.name)
} else if !strings.Contains(err.Error(), tt.quote) {
t.Errorf("%s: error %q lacks %q", tt.name, err, tt.quote)
}
}
}
// TestAmd64ExtBroadcastEncoding pins the broadcast layer over the memory
// encoding: EVEX.b, bit 4 of byte three, set on the VADDPH 512-bit template
// while the ModR/M mod bits, the SIB byte and the displacement choices keep
// the plain semantics amd64EncodeMemory chooses.
func TestAmd64ExtBroadcastEncoding(t *testing.T) {
add := []byte{0x62, 0x05, 0x04, 0x40, 0x58, 0xC0}
for _, tt := range []struct {
name string
base int
disp int64
want string
}{
{"zero displacement keeps the mod-00 form under the broadcast bit", 9, 0,
"624514505831"},
{"disp8 semantics unchanged", 1, 127,
"6265145058717f"},
{"disp32 semantics unchanged", 2, 8128,
"6265145058b2c01f0000"},
{"RBP keeps the forced displacement", 5, 0,
"62651450587500"},
{"RSP keeps the SIB byte", 12, 0,
"62451450583424"},
} {
got := amd64EncodeBroadcast(add, 30, 29, tt.base, tt.disp)
if hex.EncodeToString(got) != tt.want {
t.Errorf("%s:\n got %x\n want %s", tt.name, got, tt.want)
}
}
}
// TestAmd64ExtMemoryVocabulary pins the names the shared layer gives the