From 819dcee3ad1812de2055dc5c4858b430970874ae Mon Sep 17 00:00:00 2001 From: Simon Scherer Date: Fri, 24 Apr 2026 08:45:49 +0200 Subject: [PATCH 1/4] FEXCore: Fix wrong shift value to extract NZCV in CmpPairZ --- FEXCore/Source/Interface/Core/JIT/ALUOps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FEXCore/Source/Interface/Core/JIT/ALUOps.cpp b/FEXCore/Source/Interface/Core/JIT/ALUOps.cpp index 93974b2d7..ddb29e56c 100644 --- a/FEXCore/Source/Interface/Core/JIT/ALUOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/ALUOps.cpp @@ -274,7 +274,7 @@ DEF_OP(CmpPairZ) { // Restore NzCV if (CTX->HostFeatures.SupportsFlagM) { - rmif(TMP1, 0, 0xb /* NzCV */); + rmif(TMP1, 28, 0xb /* NzCV */); } else { cset(ARMEmitter::Size::i32Bit, TMP2, ARMEmitter::Condition::CC_EQ); bfi(ARMEmitter::Size::i32Bit, TMP1, TMP2, 30 /* lsb: Z */, 1); From fd1378f77892d943155fe5f0e432dc5f2b9d8d18 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 25 Apr 2026 00:43:50 -0700 Subject: [PATCH 2/4] InstcountCI: Fix incorrect instruction --- unittests/InstructionCountCI/Primary.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/unittests/InstructionCountCI/Primary.json b/unittests/InstructionCountCI/Primary.json index 747f92407..c98c8ada8 100644 --- a/unittests/InstructionCountCI/Primary.json +++ b/unittests/InstructionCountCI/Primary.json @@ -1826,14 +1826,6 @@ "strh w20, [x8, #-2]!" ] }, - "push dword -1": { - "ExpectedInstructionCount": 2, - "Comment": "0x6a", - "ExpectedArm64ASM": [ - "mov x20, #0xffffffffffffffff", - "str x20, [x8, #-8]!" - ] - }, "push qword -1": { "ExpectedInstructionCount": 2, "Comment": "0x6a", From dbf2761fb7363739cca9c9812966a15bb90cf2d2 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 25 Apr 2026 00:45:05 -0700 Subject: [PATCH 3/4] InstcountCI: Update --- unittests/InstructionCountCI/FlagM/FlagOpts.json | 2 +- unittests/InstructionCountCI/FlagM/HotBlocks_32Bit.json | 2 +- unittests/InstructionCountCI/FlagM/SecondaryGroup.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/unittests/InstructionCountCI/FlagM/FlagOpts.json b/unittests/InstructionCountCI/FlagM/FlagOpts.json index 38995d18b..150b820e8 100644 --- a/unittests/InstructionCountCI/FlagM/FlagOpts.json +++ b/unittests/InstructionCountCI/FlagM/FlagOpts.json @@ -377,7 +377,7 @@ "mrs x0, nzcv", "cmp w20, w4", "ccmp w21, w5, #nzcv, eq", - "rmif x0, #0, #NzCV", + "rmif x0, #28, #NzCV", "csel x4, x20, x4, ne", "csel x5, x21, x5, ne", "subs x26, x4, #0x0 (0)" diff --git a/unittests/InstructionCountCI/FlagM/HotBlocks_32Bit.json b/unittests/InstructionCountCI/FlagM/HotBlocks_32Bit.json index b056cc630..18ce26b1a 100644 --- a/unittests/InstructionCountCI/FlagM/HotBlocks_32Bit.json +++ b/unittests/InstructionCountCI/FlagM/HotBlocks_32Bit.json @@ -126,7 +126,7 @@ "mrs x0, nzcv", "cmp w22, w4", "ccmp w23, w5, #nzcv, eq", - "rmif x0, #0, #NzCV", + "rmif x0, #28, #NzCV", "csel x4, x22, x4, ne", "csel x5, x23, x5, ne", "cfinv" diff --git a/unittests/InstructionCountCI/FlagM/SecondaryGroup.json b/unittests/InstructionCountCI/FlagM/SecondaryGroup.json index c43d0501d..e791f31e1 100644 --- a/unittests/InstructionCountCI/FlagM/SecondaryGroup.json +++ b/unittests/InstructionCountCI/FlagM/SecondaryGroup.json @@ -713,7 +713,7 @@ "mrs x0, nzcv", "cmp w20, w4", "ccmp w21, w5, #nzcv, eq", - "rmif x0, #0, #NzCV", + "rmif x0, #28, #NzCV", "csel x4, x20, x4, ne", "csel x5, x21, x5, ne" ] @@ -728,7 +728,7 @@ "mrs x0, nzcv", "cmp x20, x4", "ccmp x21, x5, #nzcv, eq", - "rmif x0, #0, #NzCV", + "rmif x0, #28, #NzCV", "csel x4, x20, x4, ne", "csel x5, x21, x5, ne" ] From c09fb03eda2e0617c5742b9fe701a22bd7bf2cf6 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 25 Apr 2026 00:46:40 -0700 Subject: [PATCH 4/4] unittests/ASM: Add unittests to ensure correct cmpxchg8b/16b flag setting --- unittests/ASM/FEX_bugs/cmpxchg16b.asm | 147 ++++++++++++++++++++++++++ unittests/ASM/FEX_bugs/cmpxchg8b.asm | 144 +++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 unittests/ASM/FEX_bugs/cmpxchg16b.asm create mode 100644 unittests/ASM/FEX_bugs/cmpxchg8b.asm diff --git a/unittests/ASM/FEX_bugs/cmpxchg16b.asm b/unittests/ASM/FEX_bugs/cmpxchg16b.asm new file mode 100644 index 000000000..38feffa13 --- /dev/null +++ b/unittests/ASM/FEX_bugs/cmpxchg16b.asm @@ -0,0 +1,147 @@ +%ifdef CONFIG +{ + "RegData": { + "R13": "0x20", + "R14": "0x0000000000010dd4", + "R15": "1" + } +} +%endif + +; FEX-Emu had a bug where cmpxchg8b/cmpxchg16b wasn't setting z flag correctly on flagm supporting CPUs. +; This runs through all the configurations to both ensure that the z flag is set correctly, and that the other flags aren't affected. +; There is an additional cmpxchg8b test in another file. + +global temp_data + +%define FULL_FLAGS ((1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 7)) +%define EMPTY_FLAGS + +%define DATA_LOW 0x4142434445464748 +%define DATA_HIGH 0x5152535455565758 + +%define DATA_INCORRECT_LOW 0x4142433445464748 +%define DATA_INCORRECT_HIGH 0x5152533455565758 + +; Clobbers: RAX +%macro reset_data 0 + mov rax, DATA_LOW + mov [rel temp_data], rax + mov rax, DATA_HIGH + mov [rel temp_data + 8], rax +%endmacro + +; Clobbers: RAX +; Args: +%macro reset_flag 1 + mov rax, %1 + push rax + popfq +%endmacro + +; Clobbers: RAX, RBX, RCX, RDX +; Compares incoming values against memory, and replaces +; Args: <64-bit immediate>, <64-bit immediate> +; Return: Compare result in ZF (ZF==1 == compare_success) +%macro cas_u64x2 2 + mov rax, %1 + mov rdx, %2 + + mov rbx, 0 + mov rcx, 0 + cmpxchg16b [rel temp_data] +%endmacro + +; Clobbers: RAX, RBX +; Compares flags with correct result +; Args: , +%macro check_flags 2 + pushfq + pop rbx + + ; Remove IF and Reserved + and rbx, ~(0x202) + + ; just compare ZF + mov rax, rbx + and rax, (1 << 6) +%if %1 == 0 + jz %%.no_error +%else + jnz %%.no_error +%endif + + hlt +%%.no_error: + + ; compare full flags + cmp rbx, %2 + + je %%.second_no_error + hlt + +%%.second_no_error: + ; Continue forward +%endmacro + +; Args: , , , +%macro do_test 4 + reset_flag %2 + reset_data + cas_u64x2 %3, %4 + + ; Check flag adding reserved for comparison + check_flags %1, %2 +%endmacro + +mov r15, 0 +mov r14, 0 +mov r13, 0 +mov rsp, 0xe000_1000 + +; %define FULL_FLAGS ((1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 7)) +%assign eq_val 0 + +; EQ +%rep 2 + %assign flag_0 0 + %rep 2 + %assign flag_2 0 + %rep 2 + %assign flag_4 0 + %rep 2 + %assign flag_7 0 + %rep 2 + ; if eq_val == 0 then zf is expected to be zero, aka non-match + %if eq_val == 0 + %assign data_low DATA_LOW + %assign data_high DATA_INCORRECT_HIGH + %else + %assign data_low DATA_LOW + %assign data_high DATA_HIGH + %endif + + %assign expected_flags ((flag_0 << 0) | (flag_2 << 2) | (flag_4 << 4) | (flag_7 << 7) | (eq_val << 6)) + inc r13 + do_test eq_val, expected_flags, data_low, data_high + %assign flag_7 flag_7+1 + %endrep + %assign flag_4 flag_4+1 + %endrep + %assign flag_2 flag_2+1 + %endrep + %assign flag_0 flag_0+1 + %endrep + + %assign eq_val eq_val+1 +%endrep + +mov r15, 1 +lea r14, [rel .end] +.end: +hlt + +align 4096 +temp_data: +dq 0 +dq 0 diff --git a/unittests/ASM/FEX_bugs/cmpxchg8b.asm b/unittests/ASM/FEX_bugs/cmpxchg8b.asm new file mode 100644 index 000000000..6b9570c33 --- /dev/null +++ b/unittests/ASM/FEX_bugs/cmpxchg8b.asm @@ -0,0 +1,144 @@ +%ifdef CONFIG +{ + "RegData": { + "R13": "0x20", + "R14": "0x0000000000010c74", + "R15": "1" + } +} +%endif + +; FEX-Emu had a bug where cmpxchg8b/cmpxchg16b wasn't setting z flag correctly on flagm supporting CPUs. +; This runs through all the configurations to both ensure that the z flag is set correctly, and that the other flags aren't affected. +; There is an additional cmpxchg16b test in another file. +global temp_data + +%define FULL_FLAGS ((1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 7)) +%define EMPTY_FLAGS + +%define DATA_LOW 0x4142434445464748 +%define DATA_HIGH 0x5152535455565758 + +%define DATA_INCORRECT_LOW 0x4142433445464748 +%define DATA_INCORRECT_HIGH 0x5152533455565758 + +; Clobbers: RAX +%macro reset_data 0 + mov rax, DATA_LOW + mov [rel temp_data], rax + mov rax, DATA_HIGH + mov [rel temp_data + 8], rax +%endmacro + +; Clobbers: RAX +; Args: +%macro reset_flag 1 + mov rax, %1 + push rax + popfq +%endmacro + +; Clobbers: RAX, RBX, RCX, RDX +; Compares incoming values against memory, and replaces +; Args: <64-bit immediate> +; Return: Compare result in ZF (ZF==1 == compare_success) +%macro cas_u32x2 1 + mov eax, (%1 & 0xFFFF_FFFF) + mov edx, %1 >> 32 + + mov ebx, 0 + mov ecx, 0 + cmpxchg8b [rel temp_data] +%endmacro + +; Clobbers: RAX, RBX +; Compares flags with correct result +; Args: , +%macro check_flags 2 + pushfq + pop rbx + + ; Remove IF and Reserved + and rbx, ~(0x202) + + ; just compare ZF + mov rax, rbx + and rax, (1 << 6) +%if %1 == 0 + jz %%.no_error +%else + jnz %%.no_error +%endif + + hlt +%%.no_error: + + ; compare full flags + cmp rbx, %2 + + je %%.second_no_error + hlt + +%%.second_no_error: + ; Continue forward +%endmacro + +; Args: , , +%macro do_test 3 + reset_flag %2 + reset_data + cas_u32x2 %3 + + ; Check flag adding reserved for comparison + check_flags %1, %2 +%endmacro + +mov r15, 0 +mov r14, 0 +mov r13, 0 +mov rsp, 0xe000_1000 + +; %define FULL_FLAGS ((1 << 0) | (1 << 2) | (1 << 4) | (1 << 6) | (1 << 7)) +%assign eq_val 0 + +; EQ +%rep 2 + %assign flag_0 0 + %rep 2 + %assign flag_2 0 + %rep 2 + %assign flag_4 0 + %rep 2 + %assign flag_7 0 + %rep 2 + ; if eq_val == 0 then zf is expected to be zero, aka non-match + %if eq_val == 0 + %assign data DATA_INCORRECT_LOW + %else + %assign data DATA_LOW + %endif + + %assign expected_flags ((flag_0 << 0) | (flag_2 << 2) | (flag_4 << 4) | (flag_7 << 7) | (eq_val << 6)) + inc r13 + do_test eq_val, expected_flags, data + %assign flag_7 flag_7+1 + %endrep + %assign flag_4 flag_4+1 + %endrep + %assign flag_2 flag_2+1 + %endrep + %assign flag_0 flag_0+1 + %endrep + + %assign eq_val eq_val+1 +%endrep + +mov r15, 1 +lea r14, [rel .end] +.end: +hlt + +align 4096 +temp_data: +dq 0 +dq 0