diff --git a/src/xenia/cpu/backend/a64/a64_seq_vector.cc b/src/xenia/cpu/backend/a64/a64_seq_vector.cc index 3b0876654..55925cadc 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_vector.cc +++ b/src/xenia/cpu/backend/a64/a64_seq_vector.cc @@ -1702,18 +1702,31 @@ struct PACK : Sequence> { 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> { 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> { } 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> { 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> { 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> { 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();