From 11815400cdb413ed77e4f2a1efd168b9c9967077 Mon Sep 17 00:00:00 2001 From: "Herman S." <429230+has207@users.noreply.github.com> Date: Thu, 12 Feb 2026 09:59:58 +0900 Subject: [PATCH] [CPU] Fix f16 pack rounding and SHORT_2 test input Add round-to-nearest-even to the fast float16 pack path by folding a 0xFFF rounding bias into XMMF16PackLCPI0 and extracting bit 13 of the source as the tie-breaker, matching the software fallback behavior. Fix PACK_SHORT_2 test to use pre-biased float input (0x40400000 = 3.0) as the hardware expects, rather than raw 0.0f which is out of range. --- src/xenia/cpu/backend/x64/x64_emitter.cc | 4 ++-- src/xenia/cpu/backend/x64/x64_seq_vector.cc | 12 +++++++++--- src/xenia/cpu/testing/pack_test.cc | 14 +++++++++----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index d2dfa9e2c..df5c1b40d 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -1152,8 +1152,8 @@ static const vec128_t xmm_consts[] = { */ vec128q(0x7fe000007fe000ULL), - /* XMMF16PackLCPI0*/ - vec128i(0x8000000), + /* XMMF16PackLCPI0 (bias + round-half-down) */ + vec128i(0x8000FFF), /*XMMF16PackLCPI2*/ vec128i(0x47ffe000), /*XMMF16PackLCPI3*/ diff --git a/src/xenia/cpu/backend/x64/x64_seq_vector.cc b/src/xenia/cpu/backend/x64/x64_seq_vector.cc index d72cd903a..81ddb5363 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_vector.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_vector.cc @@ -2600,7 +2600,16 @@ static void emit_fast_f16_unpack(X64Emitter& e, const Inst& i, template static void emit_fast_f16_pack(X64Emitter& e, const Inst& i, XmmConst final_shuffle) { + // XMMF16PackLCPI0 includes bias (0x08000000) + round-half-down (0xFFF). + // For round-to-nearest-even, also add the tie-breaker: bit 13 of src. + // Bit 13 of (src + bias) == bit 13 of src since the bias doesn't affect + // bits 0-26. e.vpaddd(e.xmm1, i.src1, e.GetXmmConstPtr(XMMF16PackLCPI0)); + e.vpsrld(e.xmm0, i.src1, 13); + e.vpslld(e.xmm0, e.xmm0, 31); + e.vpsrld(e.xmm0, e.xmm0, 31); + e.vpaddd(e.xmm1, e.xmm1, e.xmm0); + e.vpand(e.xmm2, i.src1, e.GetXmmConstPtr(XMMAbsMaskPS)); e.vmovdqa(e.xmm3, e.GetXmmConstPtr(XMMF16PackLCPI2)); @@ -2769,9 +2778,6 @@ struct PACK : Sequence> { static void EmitFLOAT16_4(X64Emitter& e, const EmitArgType& i) { if (!i.src1.is_constant) { #if XE_ARCH_AMD64 - // TODO(has207): this code has an off-by-1 in rounding - // compared to the fallback which is accurate, but - // keeping it as default. Should be fixed. emit_fast_f16_pack(e, i, XMMPackFLOAT16_4); #else auto src1 = GetInputRegOrConstant(e, i.src1, e.xmm3); diff --git a/src/xenia/cpu/testing/pack_test.cc b/src/xenia/cpu/testing/pack_test.cc index 915272128..5bd31d3aa 100644 --- a/src/xenia/cpu/testing/pack_test.cc +++ b/src/xenia/cpu/testing/pack_test.cc @@ -89,11 +89,15 @@ TEST_CASE("PACK_SHORT_2", "[instr]") { StoreVR(b, 3, b.Pack(LoadVR(b, 4), PACK_TYPE_SHORT_2)); b.Return(); }); - test.Run([](PPCContext* ctx) { ctx->v[4] = vec128i(0); }, - [](PPCContext* ctx) { - auto result = ctx->v[3]; - REQUIRE(result == vec128i(0)); - }); + // SHORT_2 operates on pre-biased floats near 3.0 (0x40400000 = short 0) + test.Run( + [](PPCContext* ctx) { + ctx->v[4] = vec128i(0x40400000, 0x40400000, 0, 0); + }, + [](PPCContext* ctx) { + auto result = ctx->v[3]; + REQUIRE(result == vec128i(0)); + }); test.Run( [](PPCContext* ctx) { ctx->v[4] = vec128i(0x43817E00, 0xC37CFC00, 0, 0);