[A64/Vector] Gate pack ordering on Windows ARM64

- Swap src ordering for XT(N)/UQXTN/SQXTN/SQXTUN on _WIN32

- Add UMIN saturation before 32->16 unsigned packing

- Keep non-Windows path matching existing macOS/Linux ordering
This commit is contained in:
Will Martin
2026-01-21 11:54:00 +09:00
parent 3afa85c4c5
commit 129462f326
+68 -9
View File
@@ -1702,18 +1702,31 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
if (i.src2.is_constant) {
e.LoadConstantV(src2, i.src2.constant());
}
// Windows ARM64 requires src2->src1 ordering with EXT swap
// macOS works with src1->src2 without EXT
#ifdef _WIN32
e.UQXTN(i.dest.reg().toD().B8(), src2.H8());
e.UQXTN2(i.dest.reg().B16(), src1.H8());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
e.EXT(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16(), 8);
#else
e.UQXTN(i.dest.reg().toD().B8(), src1.H8());
e.UQXTN2(i.dest.reg().B16(), src2.H8());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
#endif
} else {
// unsigned -> unsigned
// Windows ARM64 requires src2->src1 ordering with EXT swap
#ifdef _WIN32
e.XTN(i.dest.reg().toD().B8(), i.src2.reg().H8());
e.XTN2(i.dest.reg().B16(), i.src1.reg().H8());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
e.EXT(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16(), 8);
#else
e.XTN(i.dest.reg().toD().B8(), i.src1.reg().H8());
e.XTN2(i.dest.reg().B16(), i.src2.reg().H8());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
#endif
}
} else {
if (IsPackOutSaturate(flags)) {
@@ -1738,11 +1751,17 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
e.LoadConstantV(src2, i.src2.constant());
}
// Windows ARM64 requires src2->src1 ordering with EXT swap
#ifdef _WIN32
e.SQXTUN(i.dest.reg().toD().B8(), src2.H8());
e.SQXTUN2(i.dest.reg().B16(), src1.H8());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
e.EXT(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16(), 8);
#else
e.SQXTUN(i.dest.reg().toD().B8(), src1.H8());
e.SQXTUN2(i.dest.reg().B16(), src2.H8());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
#endif
} else {
// signed -> unsigned
assert_always();
@@ -1750,11 +1769,17 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
} else {
if (IsPackOutSaturate(flags)) {
// signed -> signed + saturate
// Windows ARM64 requires src2->src1 ordering with EXT swap
#ifdef _WIN32
e.SQXTN(i.dest.reg().toD().B8(), i.src2.reg().H8());
e.SQXTN2(i.dest.reg().B16(), i.src1.reg().H8());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
e.EXT(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16(), 8);
#else
e.SQXTN(i.dest.reg().toD().B8(), i.src1.reg().H8());
e.SQXTN2(i.dest.reg().B16(), i.src2.reg().H8());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
#endif
} else {
// signed -> signed
assert_always();
@@ -1780,18 +1805,40 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
e.LoadConstantV(src2, i.src2.constant());
}
e.UQXTN(i.dest.reg().toD().H4(), src2.S4());
e.UQXTN2(i.dest.reg().H8(), src1.S4());
// Create saturation limit: 0xFFFF in all lanes
e.MOV(W0, 0xFFFF);
e.DUP(Q2.S4(), W0);
// Saturate both sources
e.UMIN(Q0.S4(), src1.S4(), Q2.S4()); // Saturate src1 (v3)
e.UMIN(Q1.S4(), src2.S4(), Q2.S4()); // Saturate src2 (v4)
// Windows ARM64 requires src2->src1 ordering with EXT swap
#ifdef _WIN32
e.UQXTN(i.dest.reg().toD().H4(),
Q1.S4()); // Pack src2 to lower 64 bits
e.UQXTN2(i.dest.reg().H8(), Q0.S4()); // Pack src1 to upper 64 bits
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
e.EXT(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16(), 8);
#else
e.UQXTN(i.dest.reg().toD().H4(),
Q0.S4()); // Pack src1 to lower 64 bits
e.UQXTN2(i.dest.reg().H8(), Q1.S4()); // Pack src2 to upper 64 bits
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
#endif
} else {
// unsigned -> unsigned
// Windows ARM64 requires src2->src1 ordering with EXT swap
#ifdef _WIN32
e.XTN(i.dest.reg().toD().H4(), i.src2.reg().S4());
e.XTN2(i.dest.reg().H8(), i.src1.reg().S4());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
e.EXT(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16(), 8);
#else
e.XTN(i.dest.reg().toD().H4(), i.src1.reg().S4());
e.XTN2(i.dest.reg().H8(), i.src2.reg().S4());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
#endif
}
} else {
if (IsPackOutSaturate(flags)) {
@@ -1806,11 +1853,17 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
if (IsPackOutUnsigned(flags)) {
if (IsPackOutSaturate(flags)) {
// signed -> unsigned + saturate
// Windows ARM64 requires src2->src1 ordering with EXT swap
#ifdef _WIN32
e.SQXTUN(i.dest.reg().toD().H4(), i.src2.reg().S4());
e.SQXTUN2(i.dest.reg().H8(), i.src1.reg().S4());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
e.EXT(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16(), 8);
#else
e.SQXTUN(i.dest.reg().toD().H4(), i.src1.reg().S4());
e.SQXTUN2(i.dest.reg().H8(), i.src2.reg().S4());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
#endif
} else {
// signed -> unsigned
assert_always();
@@ -1827,11 +1880,17 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
if (i.src2.is_constant) {
e.LoadConstantV(src2, i.src2.constant());
}
// Windows ARM64 requires src2->src1 ordering with EXT swap
#ifdef _WIN32
e.SQXTN(i.dest.reg().toD().H4(), src2.S4());
e.SQXTN2(i.dest.reg().H8(), src1.S4());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
e.EXT(i.dest.reg().B16(), i.dest.reg().B16(), i.dest.reg().B16(), 8);
#else
e.SQXTN(i.dest.reg().toD().H4(), src1.S4());
e.SQXTN2(i.dest.reg().H8(), src2.S4());
e.REV32(i.dest.reg().H8(), i.dest.reg().H8());
#endif
} else {
// signed -> signed
assert_always();