style: purge em dashes from the produced text
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -166,6 +166,6 @@ recipe.
|
||||
|
||||
## Licence
|
||||
|
||||
BSD-3-Clause — see [LICENSE](LICENSE).
|
||||
BSD-3-Clause; see [LICENSE](LICENSE).
|
||||
|
||||
Copyright © 2026 [Petr Balvín](https://petrbalvin.org)
|
||||
|
||||
@@ -202,8 +202,8 @@ TEXT ·withframe(SB), NOSPLIT, $16-16
|
||||
}
|
||||
|
||||
// TestAssembleVexKernel assembles the horizontal-sum reduction the go-flac
|
||||
// kernels end with — exercising the VEX moves, shuffle and extract forms
|
||||
// through the full parser → encoder path — and checks the output is
|
||||
// kernels end with; exercising the VEX moves, shuffle and extract forms
|
||||
// through the full parser → encoder path; and checks the output is
|
||||
// byte-identical to the Go assembler's.
|
||||
func TestAssembleVexKernel(t *testing.T) {
|
||||
fn := firstText(t, `
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ func elfTestImage(t *testing.T) *Image {
|
||||
}
|
||||
|
||||
// TestAssembleFileExternals checks that a reference to a symbol no GLOBL
|
||||
// defines is recorded as an external relocation instead of failing — the
|
||||
// defines is recorded as an external relocation instead of failing; the
|
||||
// raw image leaves the displacement zero, the object emitters carry it.
|
||||
func TestAssembleFileExternals(t *testing.T) {
|
||||
img := elfTestImage(t)
|
||||
|
||||
+2
-2
@@ -230,7 +230,7 @@ func TestSSEMoveGroundTruth(t *testing.T) {
|
||||
// TestGoFlacScalarTail encodes the scalar tail of an analyze kernel to confirm
|
||||
// the encoder handles a realistic instruction sequence.
|
||||
func TestGoFlacScalarTail(t *testing.T) {
|
||||
// MOVQ swin_base+0(FP), SI — modelled as MOVQ disp(reg), reg.
|
||||
// MOVQ swin_base+0(FP), SI; modelled as MOVQ disp(reg), reg.
|
||||
checkSyntax(t, "mov rsi, qword ptr [rax+0x10]", "MOVQ", Ptr(AX, 0x10, 8), SI)
|
||||
checkSyntax(t, "lea r9, ptr [rsi+4*rbx]", "LEAQ", Idx(SI, BX, 4, 0, 8), Reg{idx: 9, size: 8})
|
||||
checkSyntax(t, "and r10, -0x8", "ANDQ", Imm(-8), Reg{idx: 10, size: 8})
|
||||
@@ -421,7 +421,7 @@ 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
|
||||
// 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 {
|
||||
|
||||
+13
-13
@@ -16,7 +16,7 @@ import (
|
||||
// kernels use: NDS arithmetic, immediate and variable shifts, shuffles with
|
||||
// an immediate, lane extracts, narrowing stores, broadcasts from a GPR or
|
||||
// memory, mask destinations, mask moves, disp8×N compression and the 5-bit
|
||||
// register fields (X/Y 16–31, Z 0–31).
|
||||
// register fields (X/Y 16-31, Z 0-31).
|
||||
func TestEvexGroundTruth(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -58,7 +58,7 @@ func TestEvexGroundTruth(t *testing.T) {
|
||||
{"VMOVDQU32 16(SI)(R15*4),Z4", "VMOVDQU32", []Operand{Idx(SI, vreg(t, "R15"), 4, 16, 64), vreg(t, "Z4")}, "62b17e486fa4be10000000"},
|
||||
{"VMOVDQU32 Z0,4(SI)(AX*1)", "VMOVDQU32", []Operand{vreg(t, "Z0"), Idx(SI, AX, 1, 4, 64)}, "62f17e487f840604000000"},
|
||||
{"VMOVDQU32 Z3,(DI)(R15*4)", "VMOVDQU32", []Operand{vreg(t, "Z3"), Idx(DI, vreg(t, "R15"), 4, 0, 64)}, "62b17e487f1cbf"},
|
||||
// VMOVDQU64 — the W1 qword variant.
|
||||
// VMOVDQU64; the W1 qword variant.
|
||||
{"VMOVDQU64 (SI)(R15*4),Z3", "VMOVDQU64", []Operand{Idx(SI, vreg(t, "R15"), 4, 0, 64), vreg(t, "Z3")}, "62b1fe486f1cbe"},
|
||||
{"VMOVDQU64 Z0,4(SI)(AX*1)", "VMOVDQU64", []Operand{vreg(t, "Z0"), Idx(SI, AX, 1, 4, 64)}, "62f1fe487f840604000000"},
|
||||
{"VMOVDQU64 Z1,Z2", "VMOVDQU64", []Operand{vreg(t, "Z1"), vreg(t, "Z2")}, "62f1fe487fca"},
|
||||
@@ -77,7 +77,7 @@ func TestEvexGroundTruth(t *testing.T) {
|
||||
{"VPSHUFB Z1,Z2,Z3", "VPSHUFB", []Operand{vreg(t, "Z1"), vreg(t, "Z2"), vreg(t, "Z3")}, "62f26d4800d9"},
|
||||
{"VMOVDQU8 Z1,Z2", "VMOVDQU8", []Operand{vreg(t, "Z1"), vreg(t, "Z2")}, "62f17f487fca"},
|
||||
{"VMOVDQU16 Z1,Z2", "VMOVDQU16", []Operand{vreg(t, "Z1"), vreg(t, "Z2")}, "62f1ff487fca"},
|
||||
// Indices 16–31: rm[4] rides in X̄ for register operands.
|
||||
// Indices 16-31: rm[4] rides in X̄ for register operands.
|
||||
{"VPSHUFD $1,X16,X17", "VPSHUFD", []Operand{Imm(1), vreg(t, "X16"), vreg(t, "X17")}, "62a17d0870c801"},
|
||||
{"VMOVUPD (DI),Z14", "VMOVUPD", []Operand{Ptr(DI, 0, 64), vreg(t, "Z14")}, "6271fd481037"},
|
||||
{"VMOVUPD 64(DI),Z14", "VMOVUPD", []Operand{Ptr(DI, 64, 64), vreg(t, "Z14")}, "6271fd48107701"},
|
||||
@@ -96,7 +96,7 @@ func TestEvexGroundTruth(t *testing.T) {
|
||||
{"VPBROADCASTD 4(SI),Z10", "VPBROADCASTD", []Operand{Ptr(SI, 4, 4), vreg(t, "Z10")}, "62727d48585601"},
|
||||
{"VPBROADCASTQ R8,X31", "VPBROADCASTQ", []Operand{vreg(t, "R8"), vreg(t, "X31")}, "6242fd087cf8"},
|
||||
{"VPBROADCASTQ AX,Z9", "VPBROADCASTQ", []Operand{AX, vreg(t, "Z9")}, "6272fd487cc8"},
|
||||
// Register indices 16–31 exist only in EVEX encodings.
|
||||
// Register indices 16-31 exist only in EVEX encodings.
|
||||
{"VPBROADCASTD AX,Y30", "VPBROADCASTD", []Operand{AX, vreg(t, "Y30")}, "62627d287cf0"},
|
||||
// Packed double arithmetic / unpack (EVEX forms carry W=1).
|
||||
{"VSUBPD Z1,Z2,Z3", "VSUBPD", []Operand{vreg(t, "Z1"), vreg(t, "Z2"), vreg(t, "Z3")}, "62f1ed485cd9"},
|
||||
@@ -107,7 +107,7 @@ func TestEvexGroundTruth(t *testing.T) {
|
||||
{"VUNPCKHPD Z1,Z2,Z3", "VUNPCKHPD", []Operand{vreg(t, "Z1"), vreg(t, "Z2"), vreg(t, "Z3")}, "62f1ed4815d9"},
|
||||
{"VSUBPD 64(AX),Z1,Z2", "VSUBPD", []Operand{Ptr(AX, 64, 64), vreg(t, "Z1"), vreg(t, "Z2")}, "62f1f5485c5001"},
|
||||
{"VSUBPD Z17,Z18,Z19", "VSUBPD", []Operand{vreg(t, "Z17"), vreg(t, "Z18"), vreg(t, "Z19")}, "62a1ed405cd9"},
|
||||
// VMOVDDUP — duplicate the low double; disp8×N = 64 at 512 bits, and
|
||||
// VMOVDDUP; duplicate the low double; disp8×N = 64 at 512 bits, and
|
||||
// X16/X17 force EVEX (the mod=11 rm[4] extension rides in X̄).
|
||||
{"VMOVDDUP Z1,Z2", "VMOVDDUP", []Operand{vreg(t, "Z1"), vreg(t, "Z2")}, "62f1ff4812d1"},
|
||||
{"VMOVDDUP 64(AX),Z1", "VMOVDDUP", []Operand{Ptr(AX, 64, 64), vreg(t, "Z1")}, "62f1ff48124801"},
|
||||
@@ -150,7 +150,7 @@ func TestEvexGroundTruth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvexMasking checks the AVX-512 mask operand (K1–K7, placed freely among
|
||||
// TestEvexMasking checks the AVX-512 mask operand (K1-K7, placed freely among
|
||||
// the operands) and the .Z zeroing suffix, byte for byte against the Go
|
||||
// assembler.
|
||||
func TestEvexMasking(t *testing.T) {
|
||||
@@ -241,11 +241,11 @@ func TestEvexMasking(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvexExtendedGroundTruth covers the wider EVEX/AVX-512 set — ternary
|
||||
// TestEvexExtendedGroundTruth covers the wider EVEX/AVX-512 set; ternary
|
||||
// logic, lane shuffles/inserts/extracts, compares with a K destination,
|
||||
// permutes, the wider integer families, expand/compress, broadcasts,
|
||||
// rotates and word shifts, the opmask instructions, the EVEX suffixes
|
||||
// (rounding/SAE/broadcast) and the aligned/scalar moves — byte for byte
|
||||
// (rounding/SAE/broadcast) and the aligned/scalar moves; byte for byte
|
||||
// against the Go assembler.
|
||||
func TestEvexExtendedGroundTruth(t *testing.T) {
|
||||
mem64 := func(base Reg) Operand { return Ptr(base, 0, 64) }
|
||||
@@ -275,7 +275,7 @@ func TestEvexExtendedGroundTruth(t *testing.T) {
|
||||
{"VMULPD.RZ_SAE.Z", "VMULPD.RZ_SAE.Z", []Operand{vreg(t, "Z1"), vreg(t, "Z2"), vreg(t, "K1"), vreg(t, "Z3")}, "62f1edf959d9"},
|
||||
{"VMAXPD.SAE", "VMAXPD.SAE", []Operand{vreg(t, "Z1"), vreg(t, "Z2"), vreg(t, "Z3")}, "62f1ed585fd9"},
|
||||
{"VADDPD.BCST", "VADDPD.BCST", []Operand{mem64(AX), vreg(t, "Z1"), vreg(t, "Z2")}, "62f1f5585810"},
|
||||
// Packed single arithmetic (same opcodes, no mandatory prefix) —
|
||||
// Packed single arithmetic (same opcodes, no mandatory prefix);
|
||||
// ZMM, YMM and XMM widths, rounding and broadcast.
|
||||
{"VADDPS", "VADDPS", []Operand{vreg(t, "Z1"), vreg(t, "Z2"), vreg(t, "Z3")}, "62f16c4858d9"},
|
||||
{"VMULPS", "VMULPS", []Operand{vreg(t, "Y1"), vreg(t, "Y2"), vreg(t, "Y3")}, "c5ec59d9"},
|
||||
@@ -399,8 +399,8 @@ func TestEvexExtendedGroundTruth(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestEvexHelperGroundTruth covers the floating-point helper and conversion
|
||||
// tail of the EVEX set — reciprocals, rsqrt, getexp/getmant, scalef,
|
||||
// rndscale, reduce, fixupimm, range, fpclass, the remaining conversions —
|
||||
// tail of the EVEX set; reciprocals, rsqrt, getexp/getmant, scalef,
|
||||
// rndscale, reduce, fixupimm, range, fpclass, the remaining conversions;
|
||||
// plus gather/scatter with VSIB addressing, byte for byte against the Go
|
||||
// assembler.
|
||||
func TestEvexHelperGroundTruth(t *testing.T) {
|
||||
@@ -502,9 +502,9 @@ func TestEvexHelperGroundTruth(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestEvexGprGroundTruth covers the scalar conversions between vector and
|
||||
// general-purpose registers — the signed and truncated VCVT{,T}S{D,S}2SI
|
||||
// general-purpose registers; the signed and truncated VCVT{,T}S{D,S}2SI
|
||||
// forms (VEX and EVEX), the unsigned EVEX-only forms, and the GPR-to-vector
|
||||
// VCVTSI2*/VCVTUSI2* forms with the preserved vector source in vvvv — byte
|
||||
// VCVTSI2*/VCVTUSI2* forms with the preserved vector source in vvvv; byte
|
||||
// for byte against the Go assembler, including memory sources and extended
|
||||
// GPRs.
|
||||
func TestEvexGprGroundTruth(t *testing.T) {
|
||||
|
||||
+4
-4
@@ -158,8 +158,8 @@ DATA mask<>+8(SB)/8, $0x800f0e0d0c0b0a09
|
||||
t.Errorf("funcinfo bytes %x", fi)
|
||||
}
|
||||
|
||||
// The pc-value tables of addq (non-package indices 0–3, so global
|
||||
// indices 7–10): pcsp a flat zero over the whole function, pcinline a
|
||||
// The pc-value tables of addq (non-package indices 0-3, so global
|
||||
// indices 7-10): pcsp a flat zero over the whole function, pcinline a
|
||||
// flat -1, both with the pc delta in MinLC (1) units.
|
||||
pcsp := data[le.Uint32(didx[4*7:]):]
|
||||
if got := pcsp[:3]; !bytes.Equal(got, []byte{0x02, 19, 0x00}) {
|
||||
@@ -294,7 +294,7 @@ TEXT ·framed(SB), NOSPLIT, $8-0
|
||||
}
|
||||
for i := range wantPCs {
|
||||
if pcs[i] != wantPCs[i] || vals[i] != wantVals[i] {
|
||||
t.Errorf("pcsp[%d] = (%d,%d), want (%d,%d) — all: %v %v", i, pcs[i], vals[i], wantPCs[i], wantVals[i], pcs, vals)
|
||||
t.Errorf("pcsp[%d] = (%d,%d), want (%d,%d); all: %v %v", i, pcs[i], vals[i], wantPCs[i], wantVals[i], pcs, vals)
|
||||
}
|
||||
}
|
||||
// The last two steps unwind the epilogue to zero.
|
||||
@@ -333,7 +333,7 @@ TEXT ·useext(SB), NOSPLIT, $0-8
|
||||
|
||||
// TestGOObjectLinkAndRun is the end-to-end check: assemble the test
|
||||
// functions to a GOOBJ, swap it into a go build in place of the toolchain's
|
||||
// assembly object, link, and run — the output must match the baseline
|
||||
// assembly object, link, and run; the output must match the baseline
|
||||
// binary the Go assembler produced. Skipped when no Go toolchain is
|
||||
// available.
|
||||
func TestGOObjectLinkAndRun(t *testing.T) {
|
||||
|
||||
@@ -19,8 +19,8 @@ import (
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
||||
)
|
||||
|
||||
// TestAssembleGoFlacAVX2Kernel assembles the whole production AVX2 kernel —
|
||||
// all functions plus the file-local mask24 constant — and checks that every
|
||||
// TestAssembleGoFlacAVX2Kernel assembles the whole production AVX2 kernel;
|
||||
// all functions plus the file-local mask24 constant; and checks that every
|
||||
// static-symbol load resolves to the right bytes in the image.
|
||||
func TestAssembleGoFlacAVX2Kernel(t *testing.T) {
|
||||
path := "../../go-libraries/go-flac/avx2_amd64.s"
|
||||
@@ -81,7 +81,7 @@ func TestAssembleGoFlacAVX2Kernel(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestAssembleGoFlacAVX512Kernel assembles the whole production AVX-512
|
||||
// kernel — all functions plus the file-global idx16 constant — and checks
|
||||
// kernel, all functions plus the file-global idx16 constant, and checks
|
||||
// that the static-symbol load resolves to the right bytes in the image.
|
||||
func TestAssembleGoFlacAVX512Kernel(t *testing.T) {
|
||||
path := "../../go-libraries/go-flac/avx512_amd64.s"
|
||||
|
||||
@@ -94,9 +94,9 @@ DATA ·table<>+0(SB)/8, $0x1122334455667788
|
||||
}
|
||||
|
||||
// The debug_line program: LNE_set_address (the R_ADDR relocation
|
||||
// carries the function address), then one row per line change — the
|
||||
// carries the function address), then one row per line change; the
|
||||
// TEXT is on line 4 (a leading blank line precedes the include), the
|
||||
// instructions on lines 5–9 — an advance to the 20-byte end and an
|
||||
// instructions on lines 5-9; an advance to the 20-byte end and an
|
||||
// end-of-sequence.
|
||||
linesOff := le.Uint32(dataIdx[4*2:])
|
||||
lines := dataBlk[linesOff : linesOff+21]
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ import (
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
||||
)
|
||||
|
||||
// TestAssembleFileStaticData checks the whole-image layout — code, padding
|
||||
// and the data section — and that the RIP-relative displacements of static
|
||||
// TestAssembleFileStaticData checks the whole-image layout; code, padding
|
||||
// and the data section; and that the RIP-relative displacements of static
|
||||
// symbol loads resolve to the right bytes.
|
||||
func TestAssembleFileStaticData(t *testing.T) {
|
||||
f, errs := parser.Parse("d_amd64.s", `
|
||||
|
||||
@@ -280,7 +280,7 @@ done:
|
||||
// TestLOONG64_pcsp checks the stack-adjustment table of a framed function:
|
||||
// the prologue raises the SP delta by autosize (in effect from the third
|
||||
// instruction) and the RET's epilogue restores it to zero, with the pc deltas
|
||||
// in MinLC (4) units — byte-identical to `go tool asm`.
|
||||
// in MinLC (4) units; byte-identical to `go tool asm`.
|
||||
func TestLOONG64_pcsp(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
|
||||
@@ -289,7 +289,7 @@ TEXT ·cmp(SB), NOSPLIT, $0
|
||||
}
|
||||
|
||||
func TestRISCV_forwardBranch(t *testing.T) {
|
||||
// Forward label reference — must not fail.
|
||||
// Forward label reference; must not fail.
|
||||
fn := firstTextRISCV(t, `#include "textflag.h"
|
||||
TEXT ·fwd(SB), NOSPLIT, $0
|
||||
ADDI $1, X10, X10
|
||||
|
||||
+5
-5
@@ -173,7 +173,7 @@ func TestVexGroundTruth(t *testing.T) {
|
||||
{"VPMULLD Y1,Y2,Y3", "VPMULLD", []Operand{vreg(t, "Y1"), vreg(t, "Y2"), vreg(t, "Y3")}, "c4e26d40d9", ""},
|
||||
{"VPUNPCKLDQ Y4,Y3,Y5", "VPUNPCKLDQ", []Operand{vreg(t, "Y4"), vreg(t, "Y3"), vreg(t, "Y5")}, "c5e562ec", ""},
|
||||
{"VPERMD Y1,Y2,Y3", "VPERMD", []Operand{vreg(t, "Y1"), vreg(t, "Y2"), vreg(t, "Y3")}, "c4e26d36d9", ""},
|
||||
// Floating point (packed and scalar) and FMA — same NDS form, the pp
|
||||
// Floating point (packed and scalar) and FMA; same NDS form, the pp
|
||||
// bits and map select the operation.
|
||||
{"VADDPD Y9,Y8,Y8", "VADDPD", []Operand{vreg(t, "Y9"), vreg(t, "Y8"), vreg(t, "Y8")}, "c4413d58c1", ""},
|
||||
{"VADDPD X1,X2,X3", "VADDPD", []Operand{vreg(t, "X1"), vreg(t, "X2"), vreg(t, "X3")}, "c5e958d9", ""},
|
||||
@@ -217,7 +217,7 @@ func TestVexGroundTruth(t *testing.T) {
|
||||
{"VEXTRACTI128 $1,Y8,X9", "VEXTRACTI128", []Operand{Imm(1), vreg(t, "Y8"), vreg(t, "X9")}, "c4437d39c101", ""},
|
||||
{"VEXTRACTI128 $1,Y8,(DI)", "VEXTRACTI128", []Operand{Imm(1), vreg(t, "Y8"), Ptr(DI, 0, 16)}, "c4637d390701", ""},
|
||||
{"VEXTRACTF128 $1,Y8,X9", "VEXTRACTF128", []Operand{Imm(1), vreg(t, "Y8"), vreg(t, "X9")}, "c4437d19c101", ""},
|
||||
// Moves — each direction picks its own opcode and VEX.W.
|
||||
// Moves; each direction picks its own opcode and VEX.W.
|
||||
{"VMOVDQU (SI),Y1", "VMOVDQU", []Operand{Ptr(SI, 0, 32), vreg(t, "Y1")}, "c5fe6f0e", ""},
|
||||
{"VMOVDQU Y3,(DI)", "VMOVDQU", []Operand{vreg(t, "Y3"), Ptr(DI, 0, 32)}, "c5fe7f1f", ""},
|
||||
{"VMOVDQU X1,X2", "VMOVDQU", []Operand{vreg(t, "X1"), vreg(t, "X2")}, "c5fa7fca", ""},
|
||||
@@ -234,7 +234,7 @@ func TestVexGroundTruth(t *testing.T) {
|
||||
{"VMOVD AX,X0", "VMOVD", []Operand{AX, vreg(t, "X0")}, "c5f96ec0", ""},
|
||||
{"VMOVSD (SI),X8", "VMOVSD", []Operand{Ptr(SI, 0, 8), vreg(t, "X8")}, "c57b1006", ""},
|
||||
{"VMOVSD X8,(SI)", "VMOVSD", []Operand{vreg(t, "X8"), Ptr(SI, 0, 8)}, "c57b1106", ""},
|
||||
// Packed double arithmetic and unpack — the NDS form, the opcode
|
||||
// Packed double arithmetic and unpack; the NDS form, the opcode
|
||||
// selects the operation.
|
||||
{"VSUBPD Y1,Y2,Y3", "VSUBPD", []Operand{vreg(t, "Y1"), vreg(t, "Y2"), vreg(t, "Y3")}, "c5ed5cd9", ""},
|
||||
{"VDIVPD X1,X2,X3", "VDIVPD", []Operand{vreg(t, "X1"), vreg(t, "X2"), vreg(t, "X3")}, "c5e95ed9", ""},
|
||||
@@ -255,12 +255,12 @@ func TestVexGroundTruth(t *testing.T) {
|
||||
{"VMINSS X6,X7,X8", "VMINSS", []Operand{vreg(t, "X6"), vreg(t, "X7"), vreg(t, "X8")}, "c5425dc6", ""},
|
||||
{"VMAXSS X1,X2,X3", "VMAXSS", []Operand{vreg(t, "X1"), vreg(t, "X2"), vreg(t, "X3")}, "c5ea5fd9", ""},
|
||||
{"VADDSD 8(AX),X1,X2", "VADDSD", []Operand{Ptr(AX, 8, 8), vreg(t, "X1"), vreg(t, "X2")}, "c5f3585008", ""},
|
||||
// VMOVDDUP — duplicate the low double (reg=dst, rm=src, F2 pp).
|
||||
// VMOVDDUP; duplicate the low double (reg=dst, rm=src, F2 pp).
|
||||
{"VMOVDDUP X1,X2", "VMOVDDUP", []Operand{vreg(t, "X1"), vreg(t, "X2")}, "c5fb12d1", ""},
|
||||
{"VMOVDDUP Y1,Y2", "VMOVDDUP", []Operand{vreg(t, "Y1"), vreg(t, "Y2")}, "c5ff12d1", ""},
|
||||
{"VMOVDDUP 8(AX),X1", "VMOVDDUP", []Operand{Ptr(AX, 8, 8), vreg(t, "X1")}, "c5fb124808", ""},
|
||||
// Conversions: DQ→PS (no prefix), PS→PD (Go emits it without the F3
|
||||
// prefix — see the table comment), DQ→PD.
|
||||
// prefix; see the table comment), DQ→PD.
|
||||
{"VCVTDQ2PS X1,X2", "VCVTDQ2PS", []Operand{vreg(t, "X1"), vreg(t, "X2")}, "c5f85bd1", ""},
|
||||
{"VCVTDQ2PS Y3,Y4", "VCVTDQ2PS", []Operand{vreg(t, "Y3"), vreg(t, "Y4")}, "c5fc5be3", ""},
|
||||
{"VCVTPS2PD X1,X2", "VCVTPS2PD", []Operand{vreg(t, "X1"), vreg(t, "X2")}, "c5f85ad1", ""},
|
||||
|
||||
@@ -275,7 +275,7 @@ func TestVerifySmokeCrashIsolation(t *testing.T) {
|
||||
}
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
if ws, ok := exitErr.Sys().(syscall.WaitStatus); ok && ws.Signaled() {
|
||||
t.Fatalf("verify died from %v — the crash was not isolated:\n%s", ws.Signal(), out)
|
||||
t.Fatalf("verify died from %v; the crash was not isolated:\n%s", ws.Signal(), out)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(string(out), "CRASH") {
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestGolden(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestDocCommentIndent checks that a doc comment preceding a TEXT directive
|
||||
// sits at column 0 even when another function (ending in RET) precedes it —
|
||||
// sits at column 0 even when another function (ending in RET) precedes it;
|
||||
// the RET must terminate the previous body for indentation purposes.
|
||||
func TestDocCommentIndent(t *testing.T) {
|
||||
in := "#include \"textflag.h\"\n" +
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
||||
// architecture's syntax (macros, addressing modes, branch aliases) against
|
||||
// production assembly. It is skipped when the toolchain source is absent.
|
||||
//
|
||||
// The bar is zero parse errors and zero error-severity diagnostics — i.e. no
|
||||
// The bar is zero parse errors and zero error-severity diagnostics; i.e. no
|
||||
// false "unknown instruction" / "undefined label" findings on code the real
|
||||
// assembler accepts. Advisory warnings are reported but not fatal, since they
|
||||
// are heuristics that may legitimately differ across Go versions.
|
||||
|
||||
+7
-7
@@ -48,7 +48,7 @@ func TestFixtureIsClean(t *testing.T) {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
}
|
||||
// The fixture mirrors the go-flac kernels, which write the Go ABI0
|
||||
// scratch registers (BX, R13) without saving them — legal under Go's
|
||||
// scratch registers (BX, R13) without saving them; legal under Go's
|
||||
// stack-based ABI, so the register-clobber audit stays silent and the
|
||||
// fixture must lint entirely clean.
|
||||
diags := File(f, Config{Arch: arch.AMD64})
|
||||
@@ -186,8 +186,8 @@ done:
|
||||
}
|
||||
}
|
||||
|
||||
// TestEvexMaskingRecognised checks that masked EVEX forms — the .Z suffix and
|
||||
// an explicit K operand — are recognised and exempt from operand-count
|
||||
// TestEvexMaskingRecognised checks that masked EVEX forms; the .Z suffix and
|
||||
// an explicit K operand; are recognised and exempt from operand-count
|
||||
// checks.
|
||||
func TestEvexMaskingRecognised(t *testing.T) {
|
||||
diags := lintSrc(t, `
|
||||
@@ -284,7 +284,7 @@ TEXT ·f(SB), NOSPLIT|NOFRAME|DUPOK, $0
|
||||
}
|
||||
|
||||
func TestStackImbalance(t *testing.T) {
|
||||
// Function with frame size 16 but only SUB 8, SP — imbalance.
|
||||
// Function with frame size 16 but only SUB 8, SP; imbalance.
|
||||
diags := lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $16-0
|
||||
@@ -297,7 +297,7 @@ TEXT ·f(SB), NOSPLIT, $16-0
|
||||
}
|
||||
|
||||
func TestStackBalanced(t *testing.T) {
|
||||
// Function with frame size 16 and matching SUB/ADD — balanced.
|
||||
// Function with frame size 16 and matching SUB/ADD; balanced.
|
||||
diags := lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $16-0
|
||||
@@ -311,7 +311,7 @@ TEXT ·f(SB), NOSPLIT, $16-0
|
||||
}
|
||||
|
||||
func TestRegisterWidthMismatch(t *testing.T) {
|
||||
// MOVQ with 32-bit register — mismatch.
|
||||
// MOVQ with 32-bit register; mismatch.
|
||||
diags := lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
@@ -324,7 +324,7 @@ TEXT ·f(SB), NOSPLIT, $0
|
||||
}
|
||||
|
||||
func TestRegisterWidthCorrect(t *testing.T) {
|
||||
// MOVQ with 64-bit registers — correct.
|
||||
// MOVQ with 64-bit registers; correct.
|
||||
diags := lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
|
||||
@@ -7,13 +7,13 @@ import "testing"
|
||||
|
||||
// TestRegisterClobber checks the register-clobber audit is calibrated to the
|
||||
// Go ABI (cmd/compile/abi-internal.md), not the platform ABI: Go's
|
||||
// stack-based ABI0 — which hand-written assembly uses — has no System V
|
||||
// stack-based ABI0, which hand-written assembly uses, has no System V
|
||||
// style callee-saved registers, so argument and scratch registers may be
|
||||
// clobbered freely. Only the registers the ABI fixes across calls (the
|
||||
// frame pointer, the goroutine pointer, OS-reserved registers) are audited.
|
||||
func TestRegisterClobber(t *testing.T) {
|
||||
// amd64: BX, R12, R13 and R15 are argument/permanent-scratch registers in
|
||||
// Go ABI0 — writing them unsaved is legal (a System V calibration would
|
||||
// Go ABI0; writing them unsaved is legal (a System V calibration would
|
||||
// report all of these).
|
||||
scratch := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
@@ -27,7 +27,7 @@ func TestRegisterClobber(t *testing.T) {
|
||||
}
|
||||
|
||||
// amd64: R14 (the goroutine pointer) in a NOSPLIT function without calls
|
||||
// is the runtime's own pattern — the ABI0 transition restores it — so it
|
||||
// is the runtime's own pattern (the ABI0 transition restores it), so it
|
||||
// is not flagged.
|
||||
leaf := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
@@ -101,7 +101,7 @@ func TestRegisterClobber(t *testing.T) {
|
||||
t.Fatalf("arm64 R18 write should be flagged: %+v", armReserved)
|
||||
}
|
||||
|
||||
// riscv64: X27 holds the goroutine; X5–X7 are scratch.
|
||||
// riscv64: X27 holds the goroutine; X5-X7 are scratch.
|
||||
riscScratch := lintSrcArch(t, "t_riscv64.s", "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
"\tMOV X5, X6\n"+
|
||||
@@ -117,7 +117,7 @@ func TestRegisterClobber(t *testing.T) {
|
||||
t.Fatalf("unsaved riscv64 X27 write should be flagged: %+v", riscG)
|
||||
}
|
||||
|
||||
// loong64: R22 holds the goroutine; R5–R19 are argument/scratch.
|
||||
// loong64: R22 holds the goroutine; R5-R19 are argument/scratch.
|
||||
loongScratch := lintSrcArch(t, "t_loong64.s", "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
"\tMOVV R5, R6\n"+
|
||||
|
||||
@@ -149,7 +149,7 @@ func TestOperandStructure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// MOVQ swin_base+0(FP), SI — the first MOVQ in the body.
|
||||
// MOVQ swin_base+0(FP), SI; the first MOVQ in the body.
|
||||
var mov *ast.Instr
|
||||
for _, s := range fn.Body {
|
||||
if in, ok := s.(*ast.Instr); ok && in.Mnemonic.Text == "MOVQ" {
|
||||
@@ -202,7 +202,7 @@ func TestAVX512Operands(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// VALIGND $15, Z9, Z0, Z1 — four operands.
|
||||
// VALIGND $15, Z9, Z0, Z1; four operands.
|
||||
val := byMnem["VALIGND"]
|
||||
if val == nil {
|
||||
t.Fatal("VALIGND not found")
|
||||
@@ -224,7 +224,7 @@ func TestAVX512Operands(t *testing.T) {
|
||||
t.Errorf("VMOVDQU32 dst = %+v, want 4(SI)(AX*1)", dst)
|
||||
}
|
||||
|
||||
// KTESTW K1, K1 — mask registers parse as bare names.
|
||||
// KTESTW K1, K1; mask registers parse as bare names.
|
||||
kt := byMnem["KTESTW"]
|
||||
if kt == nil || len(kt.Operands) != 2 {
|
||||
t.Fatalf("KTESTW = %+v, want two operands", kt)
|
||||
|
||||
Vendored
+1
-1
@@ -20,7 +20,7 @@ TEXT ·dirtyBP(SB), NOSPLIT, $0-16
|
||||
RET
|
||||
|
||||
// func dirtyR14(a int64) int64
|
||||
// Deliberately clobbers R14 (the goroutine pointer — a serious ABI violation).
|
||||
// Deliberately clobbers R14 (the goroutine pointer; a serious ABI violation).
|
||||
TEXT ·dirtyR14(SB), NOSPLIT, $0-16
|
||||
MOVQ $0x5678, R14
|
||||
MOVQ a+0(FP), AX
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// The return trampoline (leaveJITCheckedRaw) is a raw TEXT symbol with no
|
||||
// Go function declaration, so the toolchain does NOT interpose an
|
||||
// ABIInternal wrapper — the JIT function RETs directly into the check code,
|
||||
// ABIInternal wrapper; the JIT function RETs directly into the check code,
|
||||
// which sees the registers exactly as the function left them.
|
||||
//
|
||||
// Go ABI0 on amd64 guarantees:
|
||||
@@ -39,7 +39,7 @@ TEXT ·enterJITChecked(SB), NOSPLIT, $0-16
|
||||
JMP AX
|
||||
|
||||
// leaveJITCheckedRaw is the raw return trampoline. It has NO Go function
|
||||
// declaration, so no ABIInternal wrapper is generated — the JIT function's
|
||||
// declaration, so no ABIInternal wrapper is generated; the JIT function's
|
||||
// RET lands here directly, seeing BP and R14 exactly as the function left
|
||||
// them. It checks the sentinels, records violations in abiResult, then
|
||||
// restores the Go stack and returns.
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// The return trampoline (leaveJITCheckedRaw) is a raw TEXT symbol with no
|
||||
// Go function declaration, so the toolchain does NOT interpose an
|
||||
// ABIInternal wrapper — the JIT function RETs directly into the check
|
||||
// ABIInternal wrapper; the JIT function RETs directly into the check
|
||||
// code, which sees the registers exactly as the function left them.
|
||||
//
|
||||
// Go ABI on arm64 guarantees:
|
||||
@@ -48,7 +48,7 @@ TEXT ·enterJITChecked(SB), NOSPLIT, $0-16
|
||||
JMP (R0) // branch to JIT function
|
||||
|
||||
// leaveJITCheckedRaw is the raw return trampoline. It has NO Go function
|
||||
// declaration, so no ABIInternal wrapper is generated — the JIT function's
|
||||
// declaration, so no ABIInternal wrapper is generated; the JIT function's
|
||||
// RET lands here directly, seeing R29 and g exactly as the function left
|
||||
// them. It checks the sentinels, records violations in abiResult, then
|
||||
// restores the Go stack and returns.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// The return trampoline (leaveJITCheckedRaw) is a raw TEXT symbol with no
|
||||
// Go function declaration, so the toolchain does NOT interpose an
|
||||
// ABIInternal wrapper — the JIT function RETs directly into the check
|
||||
// ABIInternal wrapper; the JIT function RETs directly into the check
|
||||
// code, which sees the registers exactly as the function left them.
|
||||
//
|
||||
// Go ABI on loong64 guarantees:
|
||||
@@ -42,7 +42,7 @@ TEXT ·enterJITChecked(SB), NOSPLIT, $0-16
|
||||
JIRL R0, R4, 0 // jump to JIT function
|
||||
|
||||
// leaveJITCheckedRaw is the raw return trampoline. It has NO Go function
|
||||
// declaration, so no ABIInternal wrapper is generated — the JIT function's
|
||||
// declaration, so no ABIInternal wrapper is generated; the JIT function's
|
||||
// RET lands here directly, seeing g exactly as the function left it. It
|
||||
// checks the sentinel, records violations in abiResult, then restores the
|
||||
// Go stack and returns.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// The return trampoline (leaveJITCheckedRaw) is a raw TEXT symbol with no
|
||||
// Go function declaration, so the toolchain does NOT interpose an
|
||||
// ABIInternal wrapper — the JIT function RETs directly into the check
|
||||
// ABIInternal wrapper; the JIT function RETs directly into the check
|
||||
// code, which sees the registers exactly as the function left them.
|
||||
//
|
||||
// Go ABI on riscv64 guarantees:
|
||||
@@ -42,7 +42,7 @@ TEXT ·enterJITChecked(SB), NOSPLIT, $0-16
|
||||
JALR X0, 0(X5) // jump to JIT function
|
||||
|
||||
// leaveJITCheckedRaw is the raw return trampoline. It has NO Go function
|
||||
// declaration, so no ABIInternal wrapper is generated — the JIT function's
|
||||
// declaration, so no ABIInternal wrapper is generated; the JIT function's
|
||||
// RET lands here directly, seeing g exactly as the function left it. It
|
||||
// checks the sentinel, records violations in abiResult, then restores the
|
||||
// Go stack and returns.
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestBlockCount(t *testing.T) {
|
||||
|
||||
func TestFillBuffer(t *testing.T) {
|
||||
t.Run("zero", func(t *testing.T) {
|
||||
// fillBuffer("zero") is a no-op — relies on make already zeroing.
|
||||
// fillBuffer("zero") is a no-op; relies on make already zeroing.
|
||||
buf := make([]byte, 16)
|
||||
fillBuffer(buf, "zero")
|
||||
for _, b := range buf {
|
||||
@@ -159,7 +159,7 @@ func TestFuzzFuncChecked(t *testing.T) {
|
||||
if !result.OK() {
|
||||
t.Errorf("FuzzFuncChecked(sum): %s", result)
|
||||
}
|
||||
// Test with non-existent function — should report failure.
|
||||
// Test with non-existent function; should report failure.
|
||||
result = k.FuzzFuncChecked("nope", sig, 10, 0)
|
||||
if result.OK() {
|
||||
t.Error("FuzzFuncChecked(nope): expected failure")
|
||||
|
||||
@@ -22,7 +22,7 @@ func mustRead(t *testing.T, path string) string {
|
||||
}
|
||||
|
||||
func TestGroundTruthBasic(t *testing.T) {
|
||||
// Use the simple test kernel — it assembles with go tool asm.
|
||||
// Use the simple test kernel; it assembles with go tool asm.
|
||||
gt, err := GroundTruth("../testdata/verify/basic_amd64.s")
|
||||
if err != nil {
|
||||
t.Fatalf("GroundTruth: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user