From 0d62818db7f16bb47692e916dfb9115d63ee77d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Thu, 27 Aug 2026 17:35:25 +0200 Subject: [PATCH] feat(amd64): encode legacy SSE binaries, imm8 shuffles and MOVQ xmm moves --- .idea/.gitignore | 10 ++++++++++ .idea/gasm-devkit.iml | 9 +++++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ asm/encode_test.go | 26 ++++++++++++++++++++++++++ asm/instrs.go | 25 +++++++++++++++++++++++++ 6 files changed, 84 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/gasm-devkit.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..30cf57e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/gasm-devkit.iml b/.idea/gasm-devkit.iml new file mode 100644 index 0000000..7c421e3 --- /dev/null +++ b/.idea/gasm-devkit.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..fa14643 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/asm/encode_test.go b/asm/encode_test.go index cec3aa2..c041a75 100644 --- a/asm/encode_test.go +++ b/asm/encode_test.go @@ -369,3 +369,29 @@ func TestSSEShuffleGroundTruth(t *testing.T) { } } } + +// TestMOVQXMMGroundTruth pins the SSE2 packed-quadword move encodings: +// loads and register moves on F3 0F 7E, stores on 66 0F D6 — the forms +// the GPR-move fallback silently corrupted. +func TestMOVQXMMGroundTruth(t *testing.T) { + cases := []struct { + name string + mnem string + ops []Operand + want string + }{ + {"MOVQ (DI),X0", "MOVQ", []Operand{Ptr(DI, 0, 8), vreg(t, "X0")}, "f30f7e07"}, + {"MOVQ X1,X2", "MOVQ", []Operand{vreg(t, "X1"), vreg(t, "X2")}, "f30f7ed1"}, + {"MOVQ X0,(DI)", "MOVQ", []Operand{vreg(t, "X0"), Ptr(DI, 0, 8)}, "660fd607"}, + } + 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 = %s, want %s", c.name, got, c.want) + } + } +} diff --git a/asm/instrs.go b/asm/instrs.go index 6237f38..b963ddf 100644 --- a/asm/instrs.go +++ b/asm/instrs.go @@ -48,6 +48,31 @@ func (e *enc) encodeMov(ops []Operand, size int) error { } src, dst := ops[0], ops[1] + // MOVQ with an XMM operand is the SSE2 packed-quadword move, NOT a + // GPR move: mem→xmm and xmm↔xmm encode as F3 0F 7E (reg = dst), + // xmm→mem as 66 0F D6 (rm = xmm). A GPR-move fallback would + // silently emit REX.W 8B with the wrong operand meaning. + _, srcVec := vecReg(src) + dstReg, dstVec := vecReg(dst) + if srcVec || dstVec { + if dstVec { + i := &instr{prefix: 0xF3, opcode: []byte{0x0F, 0x7E}, modrm: -1, sib: -1} + if err := setRM(i, dstReg, src, 8); err != nil { + return err + } + return e.emit(i) + } + srcXMM, srcIsXMM := src.(Reg) + if !srcIsXMM || !srcXMM.isVec() { + return fmt.Errorf("MOVQ: store needs an XMM source") + } + i := &instr{prefix: 0x66, opcode: []byte{0x0F, 0xD6}, modrm: -1, sib: -1} + if err := setRM(i, srcXMM, dst, 8); err != nil { + return err + } + return e.emit(i) + } + dstReg, dstIsReg := dst.(Reg) switch src := src.(type) { case Reg: