From 03f3ad25d09e47de9413551e1389d1dfc8693469 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Fri, 4 Sep 2026 17:10:33 +0800 Subject: [PATCH 01/11] LATX, fix: Sync rounding after x87 state restores Softfpu mode 1 restores the x87 control word through helpers. These helpers update CPUX86State and softfloat state, but not the host LoongArch FCSR used by translated floating-point operations. Directed rounding can therefore continue to execute as round-to-nearest. Reload the restored control word and update the translated state after FLDCW, FLDENV, FNINIT, FNSAVE, FRSTOR, FXRSTOR, and XRSTOR helper paths. Add an i386 integration test that checks each non-AVX restore path in both softfpu modes. Signed-off-by: Wenqiang Wei --- target/i386/latx/translator/tr-softfpu.c | 17 ++++ .../registrations/process/meson.build | 8 ++ .../softfpu-control-rounding-i386.S | 85 +++++++++++++++++++ .../test-softfpu-control-rounding-i386.sh | 27 ++++++ 4 files changed, 137 insertions(+) create mode 100644 tests/integration/softfpu-control-rounding-i386.S create mode 100755 tests/integration/test-softfpu-control-rounding-i386.sh diff --git a/target/i386/latx/translator/tr-softfpu.c b/target/i386/latx/translator/tr-softfpu.c index 849cb968d0..4a17d6db32 100644 --- a/target/i386/latx/translator/tr-softfpu.c +++ b/target/i386/latx/translator/tr-softfpu.c @@ -377,6 +377,16 @@ static void la_update_fp_status(IR2_OPND cw_opnd) ra_free_temp(tmp_fcsr); } +static void la_update_fp_status_from_env(void) +{ + IR2_OPND cw_opnd = ra_alloc_itemp(); + + la_ld_hu(cw_opnd, env_ir2_opnd, + lsenv_offset_of_control_word(lsenv)); + la_update_fp_status(cw_opnd); + ra_free_temp(cw_opnd); +} + __attribute__((unused)) void gen_softfpu_helper_prologue(IR1_INST *pir1) { @@ -1934,6 +1944,7 @@ static bool translate_fldcw_softfpu(IR1_INST *pir1) } else { gen_softfpu_helper2m_16u((ADDR)helper_fldcw, mem_opnd); + la_update_fp_status_from_env(); } return true; } @@ -2020,6 +2031,7 @@ static bool translate_fldenv_softfpu(IR1_INST *pir1) } else { gen_softfpu_helper3i((ADDR)helper_fldenv, mem_opnd, data32); + la_update_fp_status_from_env(); } return true; } @@ -2325,6 +2337,7 @@ static bool translate_fninit_softfpu(IR1_INST *pir1) ra_free_temp(temp); } else { gen_softfpu_helper1((ADDR)helper_fninit); + la_update_fp_status_from_env(); } return true; } @@ -2474,6 +2487,7 @@ static bool translate_fnsave_softfpu(IR1_INST *pir1) } else { IR2_OPND mem_opnd = convert_mem_no_offset(opnd0); gen_softfpu_helper3i((ADDR)helper_fsave, mem_opnd, data32); + la_update_fp_status_from_env(); } return true; } @@ -2618,6 +2632,7 @@ static bool translate_frstor_softfpu(IR1_INST *pir1) } else { IR2_OPND mem_opnd = convert_mem_no_offset(opnd0); gen_softfpu_helper3i((ADDR)helper_frstor, mem_opnd, data32); + la_update_fp_status_from_env(); } return true; @@ -3131,6 +3146,7 @@ static bool translate_fxrstor_softfpu(IR1_INST *pir1) IR2_OPND mem_opnd = convert_mem_no_offset(opnd0); gen_softfpu_helper2m_ptr((ADDR)helper_fxrstor, mem_opnd); + la_update_fp_status_from_env(); return true; } @@ -3227,6 +3243,7 @@ static bool translate_xrstor_softfpu(IR1_INST *pir1) la_bstrins_d(temp_rfbm, eax_opnd, 31, 0); la_bstrins_d(temp_rfbm, edx_opnd, 63, 32); gen_softfpu_helper3_ll((ADDR)helper_xrstor, mem_opnd, temp_rfbm); + la_update_fp_status_from_env(); return true; } #endif diff --git a/tests/integration/registrations/process/meson.build b/tests/integration/registrations/process/meson.build index bf2f8406bc..67f981acf9 100644 --- a/tests/integration/registrations/process/meson.build +++ b/tests/integration/registrations/process/meson.build @@ -86,6 +86,14 @@ if 'x86_64-linux-user' in target_dirs endif if 'i386-linux-user' in target_dirs + latx_integration_tests += [{ + 'name': 'test-softfpu-control-rounding-i386', + 'runner': find_program('../../test-softfpu-control-rounding-i386.sh'), + 'args': [ + emulators['latx-i386'], + files('../../softfpu-control-rounding-i386.S'), + ], + }] latx_integration_tests += [{ 'name': 'test-softfpu-eflags-region-i386', 'runner': find_program('../../test-softfpu-eflags-region-i386.sh'), diff --git a/tests/integration/softfpu-control-rounding-i386.S b/tests/integration/softfpu-control-rounding-i386.S new file mode 100644 index 0000000000..7eb5487695 --- /dev/null +++ b/tests/integration/softfpu-control-rounding-i386.S @@ -0,0 +1,85 @@ +.equ __NR_exit, 1 + +.macro check_down fail + fldl 8(%esp) + fcos + fstps 4(%esp) + cmpl $0x3f7fffff, 4(%esp) + jne \fail +.endm + +.macro check_nearest fail + fldl 8(%esp) + fcos + fstps 4(%esp) + cmpl $0x3f800000, 4(%esp) + jne \fail +.endm + +.section .text +.global _start +.type _start, @function +_start: + and $-16, %esp + sub $1024, %esp + + movw $0x077f, 0(%esp) + movl $0, 8(%esp) + movl $0x3d700000, 12(%esp) + + /* FLDCW must update both softfloat state and the host FCSR. */ + fninit + fldcw 0(%esp) + check_down .Lfail_fldcw + + /* FNINIT restores round-to-nearest. */ + fldcw 0(%esp) + fninit + check_nearest .Lfail_fninit + + /* FLDENV restores the x87 control word. */ + fldcw 0(%esp) + fnstenv 32(%esp) + fninit + fldenv 32(%esp) + check_down .Lfail_fldenv + + /* FNSAVE resets the x87 control word; FRSTOR restores it. */ + fldcw 0(%esp) + fnsave 64(%esp) + check_nearest .Lfail_fnsave + frstor 64(%esp) + check_down .Lfail_frstor + + /* FXRSTOR also restores the x87 control word. */ + fldcw 0(%esp) + fxsave 256(%esp) + fninit + fxrstor 256(%esp) + check_down .Lfail_fxrstor + + xor %ebx, %ebx + jmp .Lexit +.Lfail_fldcw: + mov $1, %ebx + jmp .Lexit +.Lfail_fninit: + mov $2, %ebx + jmp .Lexit +.Lfail_fldenv: + mov $3, %ebx + jmp .Lexit +.Lfail_fnsave: + mov $4, %ebx + jmp .Lexit +.Lfail_frstor: + mov $5, %ebx + jmp .Lexit +.Lfail_fxrstor: + mov $6, %ebx +.Lexit: + mov $__NR_exit, %eax + int $0x80 +.size _start, .-_start + +.section .note.GNU-stack,"",@progbits diff --git a/tests/integration/test-softfpu-control-rounding-i386.sh b/tests/integration/test-softfpu-control-rounding-i386.sh new file mode 100755 index 0000000000..3c697e66ac --- /dev/null +++ b/tests/integration/test-softfpu-control-rounding-i386.sh @@ -0,0 +1,27 @@ +#!/bin/sh +set -eu + +emulator=$(readlink -f "$1") +source_file=$(readlink -f "$2") +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +if command -v clang-19 >/dev/null 2>&1; then + clang=clang-19 +elif command -v clang >/dev/null 2>&1; then + clang=clang +else + echo "SKIP: clang is required to build the i386 guest" + exit 77 +fi + +"$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none "$source_file" \ + -o "$workdir/softfpu-control-rounding-i386" + +for mode in 1 2; do + LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=$mode LATX_SOFTFPU_FAST=0 \ + "$emulator" "$workdir/softfpu-control-rounding-i386" +done + +echo "PASS: softfpu x87 control restores update rounding state" From 9def682e667d9daa9a57e2017d228136e8bb2792 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Fri, 4 Sep 2026 17:13:08 +0800 Subject: [PATCH 02/11] LATX, opt: Inline softfpu FLDCW state updates FLDCW only loads the x87 control word and derives floating-point rounding and precision state from it. No helper call is required. Use the existing inline mode 2 implementation for both softfpu modes. Select the wrapper without a helper prologue to remove register save and restore work from this common control-state operation. This retains the corrected rounding behavior. Signed-off-by: Wenqiang Wei --- target/i386/latx/translator/tr-softfpu.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/target/i386/latx/translator/tr-softfpu.c b/target/i386/latx/translator/tr-softfpu.c index 4a17d6db32..a691bf78d7 100644 --- a/target/i386/latx/translator/tr-softfpu.c +++ b/target/i386/latx/translator/tr-softfpu.c @@ -1934,18 +1934,12 @@ static bool translate_fldcw_softfpu(IR1_INST *pir1) { IR1_OPND *opnd0 = ir1_get_opnd(pir1, 0); IR2_OPND mem_opnd = convert_mem_no_offset(opnd0); + IR2_OPND new_cw = ra_alloc_itemp(); - if (option_softfpu == 2) { - IR2_OPND new_cw = ra_alloc_itemp(); - la_ld_hu(new_cw, mem_opnd, 0); - la_st_h(new_cw, env_ir2_opnd, lsenv_offset_of_control_word(lsenv)); - la_update_fp_status(new_cw); - ra_free_temp(new_cw); - - } else { - gen_softfpu_helper2m_16u((ADDR)helper_fldcw, mem_opnd); - la_update_fp_status_from_env(); - } + la_ld_hu(new_cw, mem_opnd, 0); + la_st_h(new_cw, env_ir2_opnd, lsenv_offset_of_control_word(lsenv)); + la_update_fp_status(new_cw); + ra_free_temp(new_cw); return true; } @@ -3285,7 +3279,7 @@ TRANS_FPU_WRAP_GEN(fisub); TRANS_FPU_WRAP_GEN(fisubr); TRANS_FPU_WRAP_GEN(fld1); TRANS_FPU_WRAP_GEN(fld); -TRANS_FPU_WRAP_GEN(fldcw); +TRANS_FPU_WRAP_GEN_NO_PROLOGUE(fldcw); TRANS_FPU_WRAP_GEN(fldenv); TRANS_FPU_WRAP_GEN(fldl2e); TRANS_FPU_WRAP_GEN(fldl2t); From 5f0ab6f09dc719b0a0e05c45ed38feaa845c5d0f Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Mon, 7 Sep 2026 10:59:32 +0800 Subject: [PATCH 03/11] LATX, fix: Save MXCSR in softfpu FXSAVE The LATX-specific FXSAVE helper writes the MXCSR mask and XMM state but leaves the MXCSR field in the save area untouched. FXRSTOR can therefore restore stale control and exception state from the buffer. Reuse the common MXCSR save path so FXSAVE records the register value and preserves the existing softfloat exception-flag merge behavior. Tests: - i386 directed-rounding FXSAVE regression in softfpu modes 1 and 2 Signed-off-by: Wenqiang Wei --- target/i386/tcg/fpu_helper.c | 2 +- .../registrations/process/meson.build | 8 +++++ tests/integration/softfpu-fxsave-mxcsr-i386.S | 29 +++++++++++++++++++ .../test-softfpu-fxsave-mxcsr-i386.sh | 27 +++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/integration/softfpu-fxsave-mxcsr-i386.S create mode 100755 tests/integration/test-softfpu-fxsave-mxcsr-i386.sh diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 1efc9d70b7..ff9e0d3f4d 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -2856,7 +2856,7 @@ void helper_fxsave(CPUX86State *env, target_ulong ptr) do_xsave_fpu(env, ptr, ra); if (env->cr[4] & CR4_OSFXSR_MASK) { - cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr_mask), 0x0000ffff, ra); + do_xsave_mxcsr(env, ptr, ra); /* Fast FXSAVE leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) diff --git a/tests/integration/registrations/process/meson.build b/tests/integration/registrations/process/meson.build index 67f981acf9..d5704b43eb 100644 --- a/tests/integration/registrations/process/meson.build +++ b/tests/integration/registrations/process/meson.build @@ -86,6 +86,14 @@ if 'x86_64-linux-user' in target_dirs endif if 'i386-linux-user' in target_dirs + latx_integration_tests += [{ + 'name': 'test-softfpu-fxsave-mxcsr-i386', + 'runner': find_program('../../test-softfpu-fxsave-mxcsr-i386.sh'), + 'args': [ + emulators['latx-i386'], + files('../../softfpu-fxsave-mxcsr-i386.S'), + ], + }] latx_integration_tests += [{ 'name': 'test-softfpu-control-rounding-i386', 'runner': find_program('../../test-softfpu-control-rounding-i386.sh'), diff --git a/tests/integration/softfpu-fxsave-mxcsr-i386.S b/tests/integration/softfpu-fxsave-mxcsr-i386.S new file mode 100644 index 0000000000..1680355268 --- /dev/null +++ b/tests/integration/softfpu-fxsave-mxcsr-i386.S @@ -0,0 +1,29 @@ +.equ __NR_exit, 1 + +.section .text +.global _start +.type _start, @function +_start: + and $-16, %esp + sub $1024, %esp + + movl $0x5f80, 0(%esp) + ldmxcsr 0(%esp) + movl $0, 536(%esp) + fxsave 512(%esp) + + movl 536(%esp), %eax + and $0x6000, %eax + cmp $0x4000, %eax + jne .Lfail + + xor %ebx, %ebx + jmp .Lexit +.Lfail: + mov $1, %ebx +.Lexit: + mov $__NR_exit, %eax + int $0x80 +.size _start, .-_start + +.section .note.GNU-stack,"",@progbits diff --git a/tests/integration/test-softfpu-fxsave-mxcsr-i386.sh b/tests/integration/test-softfpu-fxsave-mxcsr-i386.sh new file mode 100755 index 0000000000..35658caec4 --- /dev/null +++ b/tests/integration/test-softfpu-fxsave-mxcsr-i386.sh @@ -0,0 +1,27 @@ +#!/bin/sh +set -eu + +emulator=$(readlink -f "$1") +source_file=$(readlink -f "$2") +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +if command -v clang-19 >/dev/null 2>&1; then + clang=clang-19 +elif command -v clang >/dev/null 2>&1; then + clang=clang +else + echo "SKIP: clang is required to build the i386 guest" + exit 77 +fi + +"$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none "$source_file" \ + -o "$workdir/softfpu-fxsave-mxcsr-i386" + +for mode in 1 2; do + LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=$mode LATX_SOFTFPU_FAST=0 \ + "$emulator" "$workdir/softfpu-fxsave-mxcsr-i386" +done + +echo "PASS: softfpu FXSAVE records MXCSR" From 84a2feac8f25c66a98b5210d7855a0aba3a07ab5 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Mon, 7 Sep 2026 11:01:08 +0800 Subject: [PATCH 04/11] LATX, fix: Apply softfpu rounding after LDMXCSR Softfpu disables the global rounding shortcut, so update_mxcsr_status() updates only the emulated SSE state after LDMXCSR. Native translated SSE operations can then continue with a stale host FCSR rounding mode. Apply the newly loaded MXCSR rounding control to the host FCSR before the next translated instruction executes. Signed-off-by: Wenqiang Wei --- target/i386/latx/include/translate.h | 1 + target/i386/latx/translator/tr-fctrl.c | 21 +++++++++++++ .../registrations/process/meson.build | 8 +++++ .../softfpu-ldmxcsr-rounding-i386.S | 30 +++++++++++++++++++ .../test-softfpu-ldmxcsr-rounding-i386.sh | 27 +++++++++++++++++ 5 files changed, 87 insertions(+) create mode 100644 tests/integration/softfpu-ldmxcsr-rounding-i386.S create mode 100755 tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh diff --git a/target/i386/latx/include/translate.h b/target/i386/latx/include/translate.h index 38200bc429..d73f17fa11 100644 --- a/target/i386/latx/include/translate.h +++ b/target/i386/latx/include/translate.h @@ -1728,6 +1728,7 @@ void tr_lat_spin_unlock(IR2_OPND lat_lock_addr); void gen_softfpu_helper_prologue(IR1_INST *pir1); void gen_softfpu_helper_epilogue(IR1_INST *pir1); +void gen_softfpu_x87_fcsr_exit(void); void update_fcsr_rm(IR2_OPND control_word, IR2_OPND fcsr); bool ir1_need_reserve_h128(IR1_INST *ir1); diff --git a/target/i386/latx/translator/tr-fctrl.c b/target/i386/latx/translator/tr-fctrl.c index 36773c5f27..d017a2c5ba 100644 --- a/target/i386/latx/translator/tr-fctrl.c +++ b/target/i386/latx/translator/tr-fctrl.c @@ -111,6 +111,24 @@ void update_fcsr_rm(IR2_OPND control_word, IR2_OPND fcsr) ra_free_temp(temp_cw); } +void gen_softfpu_x87_fcsr_exit(void) +{ + IR2_OPND mxcsr_opnd = ra_alloc_itemp(); + IR2_OPND cw_opnd = ra_alloc_itemp(); + IR2_OPND fcsr_opnd = ra_alloc_itemp(); + + la_ld_wu(mxcsr_opnd, env_ir2_opnd, lsenv_offset_of_mxcsr(lsenv)); + la_bstrpick_d(cw_opnd, mxcsr_opnd, 14, 13); + la_slli_d(cw_opnd, cw_opnd, 10); + la_movfcsr2gr(fcsr_opnd, fcsr_ir2_opnd); + update_fcsr_rm(cw_opnd, fcsr_opnd); + la_movgr2fcsr(fcsr_ir2_opnd, fcsr_opnd); + + ra_free_temp(mxcsr_opnd); + ra_free_temp(cw_opnd); + ra_free_temp(fcsr_opnd); +} + void update_fcsr_by_sw(IR2_OPND sw) { IR2_OPND old_fcsr = ra_alloc_itemp(); @@ -234,6 +252,9 @@ bool translate_ldmxcsr(IR1_INST *pir1) tr_gen_call_to_helper1((ADDR)update_mxcsr_status, 1, LOAD_HELPER_UPDATE_MXCSR_STATUS); + if (option_softfpu) { + gen_softfpu_x87_fcsr_exit(); + } return true; } diff --git a/tests/integration/registrations/process/meson.build b/tests/integration/registrations/process/meson.build index d5704b43eb..d63203d3e7 100644 --- a/tests/integration/registrations/process/meson.build +++ b/tests/integration/registrations/process/meson.build @@ -86,6 +86,14 @@ if 'x86_64-linux-user' in target_dirs endif if 'i386-linux-user' in target_dirs + latx_integration_tests += [{ + 'name': 'test-softfpu-ldmxcsr-rounding-i386', + 'runner': find_program('../../test-softfpu-ldmxcsr-rounding-i386.sh'), + 'args': [ + emulators['latx-i386'], + files('../../softfpu-ldmxcsr-rounding-i386.S'), + ], + }] latx_integration_tests += [{ 'name': 'test-softfpu-fxsave-mxcsr-i386', 'runner': find_program('../../test-softfpu-fxsave-mxcsr-i386.sh'), diff --git a/tests/integration/softfpu-ldmxcsr-rounding-i386.S b/tests/integration/softfpu-ldmxcsr-rounding-i386.S new file mode 100644 index 0000000000..4db5726309 --- /dev/null +++ b/tests/integration/softfpu-ldmxcsr-rounding-i386.S @@ -0,0 +1,30 @@ +.equ __NR_exit, 1 + +.section .text +.global _start +.type _start, @function +_start: + and $-16, %esp + sub $32, %esp + + movl $0x3f800000, 0(%esp) + movl $0x33800000, 4(%esp) + movl $0x5f80, 8(%esp) + ldmxcsr 8(%esp) + + movss 0(%esp), %xmm0 + addss 4(%esp), %xmm0 + movss %xmm0, 12(%esp) + cmpl $0x3f800001, 12(%esp) + jne .Lfail + + xor %ebx, %ebx + jmp .Lexit +.Lfail: + mov $1, %ebx +.Lexit: + mov $__NR_exit, %eax + int $0x80 +.size _start, .-_start + +.section .note.GNU-stack,"",@progbits diff --git a/tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh b/tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh new file mode 100755 index 0000000000..28dfe2bdee --- /dev/null +++ b/tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh @@ -0,0 +1,27 @@ +#!/bin/sh +set -eu + +emulator=$(readlink -f "$1") +source_file=$(readlink -f "$2") +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +if command -v clang-19 >/dev/null 2>&1; then + clang=clang-19 +elif command -v clang >/dev/null 2>&1; then + clang=clang +else + echo "SKIP: clang is required to build the i386 guest" + exit 77 +fi + +"$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none "$source_file" \ + -o "$workdir/softfpu-ldmxcsr-rounding-i386" + +for mode in 1 2; do + LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=$mode LATX_SOFTFPU_FAST=0 \ + "$emulator" "$workdir/softfpu-ldmxcsr-rounding-i386" +done + +echo "PASS: softfpu LDMXCSR updates host rounding" From b5f26ec76ce2c9776d83ea922cc47adbad563c91 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Mon, 7 Sep 2026 11:39:09 +0800 Subject: [PATCH 05/11] LATX, fix: Preserve x87 and SSE rounding independently Translated x87 instructions use the host FCSR rounding mode, while SSE instructions require the rounding mode recorded in MXCSR. Leaving one mode active across both instruction classes makes one side observe stale rounding state. Classify x87 instructions explicitly. Load the x87 control-word rounding mode before each x87 instruction and restore the MXCSR rounding mode afterward so the two guest control states remain independent. Tests: - Run the i386 and x86_64 control-rounding regressions in softfpu modes 1 and 2. Signed-off-by: Wenqiang Wei --- target/i386/latx/include/translate.h | 1 + target/i386/latx/translator/tr-softfpu.c | 15 +++ target/i386/latx/translator/translate.c | 112 ++++++++++++++++++ .../registrations/process/meson.build | 8 ++ .../softfpu-control-rounding-i386.S | 53 ++++++++- .../softfpu-control-rounding-x86_64.S | 63 ++++++++++ .../test-softfpu-control-rounding-i386.sh | 2 +- .../test-softfpu-control-rounding-x86_64.sh | 27 +++++ 8 files changed, 276 insertions(+), 5 deletions(-) create mode 100644 tests/integration/softfpu-control-rounding-x86_64.S create mode 100755 tests/integration/test-softfpu-control-rounding-x86_64.sh diff --git a/target/i386/latx/include/translate.h b/target/i386/latx/include/translate.h index d73f17fa11..ff709c4e5b 100644 --- a/target/i386/latx/include/translate.h +++ b/target/i386/latx/include/translate.h @@ -1728,6 +1728,7 @@ void tr_lat_spin_unlock(IR2_OPND lat_lock_addr); void gen_softfpu_helper_prologue(IR1_INST *pir1); void gen_softfpu_helper_epilogue(IR1_INST *pir1); +void gen_softfpu_x87_fcsr_enter(void); void gen_softfpu_x87_fcsr_exit(void); void update_fcsr_rm(IR2_OPND control_word, IR2_OPND fcsr); diff --git a/target/i386/latx/translator/tr-softfpu.c b/target/i386/latx/translator/tr-softfpu.c index a691bf78d7..d718733129 100644 --- a/target/i386/latx/translator/tr-softfpu.c +++ b/target/i386/latx/translator/tr-softfpu.c @@ -387,6 +387,21 @@ static void la_update_fp_status_from_env(void) ra_free_temp(cw_opnd); } +void gen_softfpu_x87_fcsr_enter(void) +{ + IR2_OPND cw_opnd = ra_alloc_itemp(); + IR2_OPND fcsr_opnd = ra_alloc_itemp(); + + la_ld_hu(cw_opnd, env_ir2_opnd, + lsenv_offset_of_control_word(lsenv)); + la_movfcsr2gr(fcsr_opnd, fcsr_ir2_opnd); + update_fcsr_rm(cw_opnd, fcsr_opnd); + la_movgr2fcsr(fcsr_ir2_opnd, fcsr_opnd); + + ra_free_temp(cw_opnd); + ra_free_temp(fcsr_opnd); +} + __attribute__((unused)) void gen_softfpu_helper_prologue(IR1_INST *pir1) { diff --git a/target/i386/latx/translator/translate.c b/target/i386/latx/translator/translate.c index 38266d7bbc..fdd3c1362e 100644 --- a/target/i386/latx/translator/translate.c +++ b/target/i386/latx/translator/translate.c @@ -2101,6 +2101,108 @@ typedef enum SoftFPURegionClass { SOFTFPU_REGION_TRANSPARENT, } SoftFPURegionClass; +static bool is_softfpu_x87_insn(IR1_OPCODE opcode) +{ + switch (opcode) { + case dt_X86_INS_WAIT: + case dt_X86_INS_F2XM1: + case dt_X86_INS_FABS: + case dt_X86_INS_FADD: + case dt_X86_INS_FADDP: + case dt_X86_INS_FBLD: + case dt_X86_INS_FBSTP: + case dt_X86_INS_FCHS: + case dt_X86_INS_FCMOVB: + case dt_X86_INS_FCMOVBE: + case dt_X86_INS_FCMOVE: + case dt_X86_INS_FCMOVNB: + case dt_X86_INS_FCMOVNBE: + case dt_X86_INS_FCMOVNE: + case dt_X86_INS_FCMOVNU: + case dt_X86_INS_FCMOVU: + case dt_X86_INS_FCOM: + case dt_X86_INS_FCOMI: + case dt_X86_INS_FCOMIP: + case dt_X86_INS_FCOMP: + case dt_X86_INS_FCOMPP: + case dt_X86_INS_FCOS: + case dt_X86_INS_FDECSTP: + case dt_X86_INS_FDIV: + case dt_X86_INS_FDIVP: + case dt_X86_INS_FDIVR: + case dt_X86_INS_FDIVRP: + case dt_X86_INS_FFREE: + case dt_X86_INS_FFREEP: + case dt_X86_INS_FIADD: + case dt_X86_INS_FICOM: + case dt_X86_INS_FICOMP: + case dt_X86_INS_FIDIV: + case dt_X86_INS_FIDIVR: + case dt_X86_INS_FILD: + case dt_X86_INS_FIMUL: + case dt_X86_INS_FINCSTP: + case dt_X86_INS_FIST: + case dt_X86_INS_FISTP: + case dt_X86_INS_FISTTP: + case dt_X86_INS_FISUB: + case dt_X86_INS_FISUBR: + case dt_X86_INS_FLD1: + case dt_X86_INS_FLD: + case dt_X86_INS_FLDCW: + case dt_X86_INS_FLDENV: + case dt_X86_INS_FLDL2E: + case dt_X86_INS_FLDL2T: + case dt_X86_INS_FLDLG2: + case dt_X86_INS_FLDLN2: + case dt_X86_INS_FLDPI: + case dt_X86_INS_FLDZ: + case dt_X86_INS_FMUL: + case dt_X86_INS_FMULP: + case dt_X86_INS_FNCLEX: + case dt_X86_INS_FNINIT: + case dt_X86_INS_FNOP: + case dt_X86_INS_FNSAVE: + case dt_X86_INS_FNSTCW: + case dt_X86_INS_FNSTENV: + case dt_X86_INS_FNSTSW: + case dt_X86_INS_FPATAN: + case dt_X86_INS_FPREM1: + case dt_X86_INS_FPREM: + case dt_X86_INS_FPTAN: + case dt_X86_INS_FRNDINT: + case dt_X86_INS_FRSTOR: + case dt_X86_INS_FSCALE: + case dt_X86_INS_FSETPM: + case dt_X86_INS_FSIN: + case dt_X86_INS_FSINCOS: + case dt_X86_INS_FSQRT: + case dt_X86_INS_FST: + case dt_X86_INS_FSTP: + case dt_X86_INS_FSUB: + case dt_X86_INS_FSUBP: + case dt_X86_INS_FSUBR: + case dt_X86_INS_FSUBRP: + case dt_X86_INS_FTST: + case dt_X86_INS_FUCOM: + case dt_X86_INS_FUCOMI: + case dt_X86_INS_FUCOMIP: + case dt_X86_INS_FUCOMP: + case dt_X86_INS_FUCOMPP: + case dt_X86_INS_FXAM: + case dt_X86_INS_FXCH: + case dt_X86_INS_FXRSTOR: + case dt_X86_INS_FXRSTOR64: + case dt_X86_INS_FXSAVE: + case dt_X86_INS_FXSAVE64: + case dt_X86_INS_FXTRACT: + case dt_X86_INS_FYL2X: + case dt_X86_INS_FYL2XP1: + return true; + default: + return false; + } +} + static uint32_t softfpu_fast_mask(IR1_OPCODE opcode) { switch (opcode) { @@ -2419,6 +2521,12 @@ int tr_ir2_generate(struct TranslationBlock *tb) } #endif + bool x87_insn = option_softfpu && + is_softfpu_x87_insn(ir1_opcode(pir1)); + if (x87_insn) { + gen_softfpu_x87_fcsr_enter(); + } + if (option_softfpu == 2 && !reduce_proepo && softfpu_region_class(ir1_opcode(pir1)) == SOFTFPU_REGION_REQUIRED) { @@ -2445,6 +2553,10 @@ int tr_ir2_generate(struct TranslationBlock *tb) gen_softfpu_helper_epilogue(pir1); } + if (x87_insn) { + gen_softfpu_x87_fcsr_exit(); + } + #ifdef CONFIG_LATX_IMM_REG if (option_imm_reg) { imm_cache_update_ir1_usage(imm_cache, pir1, i); diff --git a/tests/integration/registrations/process/meson.build b/tests/integration/registrations/process/meson.build index d63203d3e7..e1390b9bfe 100644 --- a/tests/integration/registrations/process/meson.build +++ b/tests/integration/registrations/process/meson.build @@ -1,4 +1,12 @@ if 'x86_64-linux-user' in target_dirs + latx_integration_tests += [{ + 'name': 'test-softfpu-control-rounding-x86_64', + 'runner': find_program('../../test-softfpu-control-rounding-x86_64.sh'), + 'args': [ + emulators['latx-x86_64'], + files('../../softfpu-control-rounding-x86_64.S'), + ], + }] latx_integration_tests += [{ 'name': 'test-proc-readdir', 'runner': find_program('../../test-proc-readdir.sh'), diff --git a/tests/integration/softfpu-control-rounding-i386.S b/tests/integration/softfpu-control-rounding-i386.S index 7eb5487695..49ee76bef0 100644 --- a/tests/integration/softfpu-control-rounding-i386.S +++ b/tests/integration/softfpu-control-rounding-i386.S @@ -8,6 +8,14 @@ jne \fail .endm +.macro check_sse_up fail + movss 16(%esp), %xmm0 + addss 20(%esp), %xmm0 + movss %xmm0, 4(%esp) + cmpl $0x3f800001, 4(%esp) + jne \fail +.endm + .macro check_nearest fail fldl 8(%esp) fcos @@ -21,11 +29,13 @@ .type _start, @function _start: and $-16, %esp - sub $1024, %esp + sub $2048, %esp movw $0x077f, 0(%esp) movl $0, 8(%esp) movl $0x3d700000, 12(%esp) + movl $0x3f800000, 16(%esp) + movl $0x33800000, 20(%esp) /* FLDCW must update both softfloat state and the host FCSR. */ fninit @@ -51,12 +61,32 @@ _start: frstor 64(%esp) check_down .Lfail_frstor - /* FXRSTOR also restores the x87 control word. */ + /* FXRSTOR restores independent x87 and MXCSR rounding controls. */ fldcw 0(%esp) - fxsave 256(%esp) + movl $0x5f80, 24(%esp) + ldmxcsr 24(%esp) + stmxcsr 28(%esp) + movl 28(%esp), %eax + and $0x6000, %eax + cmp $0x4000, %eax + jne .Lfail_ldmxcsr + fxsave 512(%esp) + movl 536(%esp), %eax + and $0x6000, %eax + cmp $0x4000, %eax + jne .Lfail_fxsave_mxcsr fninit - fxrstor 256(%esp) + movl $0x3f80, 24(%esp) + ldmxcsr 24(%esp) + fxrstor 512(%esp) check_down .Lfail_fxrstor + stmxcsr 28(%esp) + movl 28(%esp), %eax + and $0x6000, %eax + cmp $0x4000, %eax + jne .Lfail_fxrstor_saved_mxcsr + check_sse_up .Lfail_fxrstor_mxcsr + check_down .Lfail_fxrstor_x87_again xor %ebx, %ebx jmp .Lexit @@ -77,6 +107,21 @@ _start: jmp .Lexit .Lfail_fxrstor: mov $6, %ebx + jmp .Lexit +.Lfail_ldmxcsr: + mov $7, %ebx + jmp .Lexit +.Lfail_fxsave_mxcsr: + mov $8, %ebx + jmp .Lexit +.Lfail_fxrstor_saved_mxcsr: + mov $9, %ebx + jmp .Lexit +.Lfail_fxrstor_mxcsr: + mov $10, %ebx + jmp .Lexit +.Lfail_fxrstor_x87_again: + mov $11, %ebx .Lexit: mov $__NR_exit, %eax int $0x80 diff --git a/tests/integration/softfpu-control-rounding-x86_64.S b/tests/integration/softfpu-control-rounding-x86_64.S new file mode 100644 index 0000000000..3d031e6683 --- /dev/null +++ b/tests/integration/softfpu-control-rounding-x86_64.S @@ -0,0 +1,63 @@ +.equ __NR_exit, 60 + +.macro check_down fail + fldl 8(%rsp) + fcos + fstps 4(%rsp) + cmpl $0x3f7fffff, 4(%rsp) + jne \fail +.endm + +.macro check_sse_up fail + movss 16(%rsp), %xmm0 + addss 20(%rsp), %xmm0 + movss %xmm0, 4(%rsp) + cmpl $0x3f800001, 4(%rsp) + jne \fail +.endm + +.section .text +.global _start +.type _start, @function +_start: + and $-64, %rsp + sub $2048, %rsp + + movw $0x077f, 0(%rsp) + movl $0, 8(%rsp) + movl $0x3d700000, 12(%rsp) + movl $0x3f800000, 16(%rsp) + movl $0x33800000, 20(%rsp) + + fldcw 0(%rsp) + movl $0x5f80, 24(%rsp) + ldmxcsr 24(%rsp) + fxsave64 512(%rsp) + movl 536(%rsp), %eax + and $0x6000, %eax + cmp $0x4000, %eax + jne .Lfail_fxsave_mxcsr + + fninit + movl $0x3f80, 24(%rsp) + ldmxcsr 24(%rsp) + fxrstor64 512(%rsp) + check_down .Lfail_fxrstor + check_sse_up .Lfail_fxrstor_mxcsr + + xor %edi, %edi + jmp .Lexit +.Lfail_fxsave_mxcsr: + mov $1, %edi + jmp .Lexit +.Lfail_fxrstor: + mov $2, %edi + jmp .Lexit +.Lfail_fxrstor_mxcsr: + mov $3, %edi +.Lexit: + mov $__NR_exit, %eax + syscall +.size _start, .-_start + +.section .note.GNU-stack,"",@progbits diff --git a/tests/integration/test-softfpu-control-rounding-i386.sh b/tests/integration/test-softfpu-control-rounding-i386.sh index 3c697e66ac..801f8f18ec 100755 --- a/tests/integration/test-softfpu-control-rounding-i386.sh +++ b/tests/integration/test-softfpu-control-rounding-i386.sh @@ -24,4 +24,4 @@ for mode in 1 2; do "$emulator" "$workdir/softfpu-control-rounding-i386" done -echo "PASS: softfpu x87 control restores update rounding state" +echo "PASS: softfpu restores independent x87 and SSE rounding state" diff --git a/tests/integration/test-softfpu-control-rounding-x86_64.sh b/tests/integration/test-softfpu-control-rounding-x86_64.sh new file mode 100755 index 0000000000..f2281d92ca --- /dev/null +++ b/tests/integration/test-softfpu-control-rounding-x86_64.sh @@ -0,0 +1,27 @@ +#!/bin/sh +set -eu + +emulator=$(readlink -f "$1") +source_file=$(readlink -f "$2") +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +if command -v clang-19 >/dev/null 2>&1; then + clang=clang-19 +elif command -v clang >/dev/null 2>&1; then + clang=clang +else + echo "SKIP: clang is required to build the x86_64 guest" + exit 77 +fi + +"$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none "$source_file" \ + -o "$workdir/softfpu-control-rounding-x86_64" + +for mode in 1 2; do + LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=$mode LATX_SOFTFPU_FAST=0 \ + "$emulator" "$workdir/softfpu-control-rounding-x86_64" +done + +echo "PASS: x86_64 softfpu restores independent rounding state" From 9cc99879c5883badda8cafd3b4b5cb7ff25bc340 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Mon, 7 Sep 2026 11:42:00 +0800 Subject: [PATCH 06/11] LATX, opt: Share FCSR switches across x87 runs The correctness path switches the host FCSR before and after every x87 instruction. Consecutive x87 instructions use the same guest control word, so the intermediate restore and reload are redundant. Keep the x87 FCSR mode active across adjacent x87 instructions and restore the MXCSR mode only when leaving the run or ending the translation block. Tests: - Run the i386 and x86_64 control-rounding regressions with softfpu mode 2 and LATX_SOFTFPU_FAST=-1. Signed-off-by: Wenqiang Wei --- target/i386/latx/translator/translate.c | 14 +++++++++----- .../test-softfpu-control-rounding-i386.sh | 2 ++ .../test-softfpu-control-rounding-x86_64.sh | 2 ++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/target/i386/latx/translator/translate.c b/target/i386/latx/translator/translate.c index fdd3c1362e..75a1aa739b 100644 --- a/target/i386/latx/translator/translate.c +++ b/target/i386/latx/translator/translate.c @@ -2412,6 +2412,7 @@ int tr_ir2_generate(struct TranslationBlock *tb) IR1_INST *pir1 = tb_ir1_inst(tb, 0); bool reduce_proepo = false; + bool x87_fcsr_active = false; int softfpu_region_end = -1; lsenv->tr_data->softfpu_region_active = false; @@ -2523,8 +2524,12 @@ int tr_ir2_generate(struct TranslationBlock *tb) bool x87_insn = option_softfpu && is_softfpu_x87_insn(ir1_opcode(pir1)); - if (x87_insn) { + if (x87_insn && !x87_fcsr_active) { gen_softfpu_x87_fcsr_enter(); + x87_fcsr_active = true; + } else if (!x87_insn && x87_fcsr_active) { + gen_softfpu_x87_fcsr_exit(); + x87_fcsr_active = false; } if (option_softfpu == 2 && !reduce_proepo && @@ -2553,10 +2558,6 @@ int tr_ir2_generate(struct TranslationBlock *tb) gen_softfpu_helper_epilogue(pir1); } - if (x87_insn) { - gen_softfpu_x87_fcsr_exit(); - } - #ifdef CONFIG_LATX_IMM_REG if (option_imm_reg) { imm_cache_update_ir1_usage(imm_cache, pir1, i); @@ -2567,6 +2568,9 @@ int tr_ir2_generate(struct TranslationBlock *tb) pir1++; } + if (x87_fcsr_active) { + gen_softfpu_x87_fcsr_exit(); + } #ifdef CONFIG_LATX_DEBUG if (option_dump_ir1) { pir1 = tb_ir1_inst(tb, 0); diff --git a/tests/integration/test-softfpu-control-rounding-i386.sh b/tests/integration/test-softfpu-control-rounding-i386.sh index 801f8f18ec..da745b55b1 100755 --- a/tests/integration/test-softfpu-control-rounding-i386.sh +++ b/tests/integration/test-softfpu-control-rounding-i386.sh @@ -23,5 +23,7 @@ for mode in 1 2; do LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=$mode LATX_SOFTFPU_FAST=0 \ "$emulator" "$workdir/softfpu-control-rounding-i386" done +LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=2 LATX_SOFTFPU_FAST=-1 \ + "$emulator" "$workdir/softfpu-control-rounding-i386" echo "PASS: softfpu restores independent x87 and SSE rounding state" diff --git a/tests/integration/test-softfpu-control-rounding-x86_64.sh b/tests/integration/test-softfpu-control-rounding-x86_64.sh index f2281d92ca..3a823ffdf4 100755 --- a/tests/integration/test-softfpu-control-rounding-x86_64.sh +++ b/tests/integration/test-softfpu-control-rounding-x86_64.sh @@ -23,5 +23,7 @@ for mode in 1 2; do LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=$mode LATX_SOFTFPU_FAST=0 \ "$emulator" "$workdir/softfpu-control-rounding-x86_64" done +LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=2 LATX_SOFTFPU_FAST=-1 \ + "$emulator" "$workdir/softfpu-control-rounding-x86_64" echo "PASS: x86_64 softfpu restores independent rounding state" From b7dd59278a736dde0f04cd75ccf8bd0751389664 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Mon, 7 Sep 2026 11:43:21 +0800 Subject: [PATCH 07/11] LATX, fix: Restore MXCSR rounding after XRSTOR The softfpu XRSTOR path left the host FCSR rounding mode derived from the x87 control word after the helper returned. A following translated SSE instruction could therefore use x87 rounding instead of the restored MXCSR rounding control. Reload the host FCSR rounding mode from MXCSR after XRSTOR. Cover both x87-only and x87-plus-SSE restore masks in an AVX-enabled i386 regression. Tests: - Run the XRSTOR rounding regression in softfpu modes 1 and 2 with LATX_SOFTFPU_FAST=0, and in mode 2 with LATX_SOFTFPU_FAST=-1. Signed-off-by: Wenqiang Wei --- target/i386/latx/translator/tr-softfpu.c | 2 +- .../registrations/process/meson.build | 10 ++ .../softfpu-xrstor-rounding-i386.S | 97 +++++++++++++++++++ .../test-softfpu-xrstor-rounding-i386.sh | 31 ++++++ 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 tests/integration/softfpu-xrstor-rounding-i386.S create mode 100755 tests/integration/test-softfpu-xrstor-rounding-i386.sh diff --git a/target/i386/latx/translator/tr-softfpu.c b/target/i386/latx/translator/tr-softfpu.c index d718733129..4b75486b6f 100644 --- a/target/i386/latx/translator/tr-softfpu.c +++ b/target/i386/latx/translator/tr-softfpu.c @@ -3252,7 +3252,7 @@ static bool translate_xrstor_softfpu(IR1_INST *pir1) la_bstrins_d(temp_rfbm, eax_opnd, 31, 0); la_bstrins_d(temp_rfbm, edx_opnd, 63, 32); gen_softfpu_helper3_ll((ADDR)helper_xrstor, mem_opnd, temp_rfbm); - la_update_fp_status_from_env(); + gen_softfpu_x87_fcsr_exit(); return true; } #endif diff --git a/tests/integration/registrations/process/meson.build b/tests/integration/registrations/process/meson.build index e1390b9bfe..be89d26489 100644 --- a/tests/integration/registrations/process/meson.build +++ b/tests/integration/registrations/process/meson.build @@ -118,6 +118,16 @@ if 'i386-linux-user' in target_dirs files('../../softfpu-control-rounding-i386.S'), ], }] + if 'CONFIG_LATX_AVX_OPT' in config_host + latx_integration_tests += [{ + 'name': 'test-softfpu-xrstor-rounding-i386', + 'runner': find_program('../../test-softfpu-xrstor-rounding-i386.sh'), + 'args': [ + emulators['latx-i386'], + files('../../softfpu-xrstor-rounding-i386.S'), + ], + }] + endif latx_integration_tests += [{ 'name': 'test-softfpu-eflags-region-i386', 'runner': find_program('../../test-softfpu-eflags-region-i386.sh'), diff --git a/tests/integration/softfpu-xrstor-rounding-i386.S b/tests/integration/softfpu-xrstor-rounding-i386.S new file mode 100644 index 0000000000..f65c74fbff --- /dev/null +++ b/tests/integration/softfpu-xrstor-rounding-i386.S @@ -0,0 +1,97 @@ +.equ __NR_exit, 1 + +.macro check_down fail + fldl 8(%esp) + fcos + fstps 4(%esp) + cmpl $0x3f7fffff, 4(%esp) + jne \fail +.endm + +.macro check_sse_nearest fail + movss 16(%esp), %xmm0 + addss 20(%esp), %xmm0 + movss %xmm0, 4(%esp) + cmpl $0x3f800000, 4(%esp) + jne \fail +.endm + +.macro check_sse_up fail + movss 16(%esp), %xmm0 + addss 20(%esp), %xmm0 + movss %xmm0, 4(%esp) + cmpl $0x3f800001, 4(%esp) + jne \fail +.endm + +.section .text +.global _start +.type _start, @function +_start: + and $-64, %esp + sub $2048, %esp + + movw $0x077f, 0(%esp) + movl $0, 8(%esp) + movl $0x3d700000, 12(%esp) + movl $0x3f800000, 16(%esp) + movl $0x33800000, 20(%esp) + + xor %ecx, %ecx + xgetbv + and $3, %eax + cmp $3, %eax + jne .Lfail_xcr0 + + fldcw 0(%esp) + movl $0x5f80, 24(%esp) + ldmxcsr 24(%esp) + xor %ecx, %ecx + xgetbv + xsave 512(%esp) + + movl 536(%esp), %ecx + and $0x6000, %ecx + cmp $0x4000, %ecx + jne .Lfail_xsave_mxcsr + + fninit + movl $0x1f80, 24(%esp) + ldmxcsr 24(%esp) + mov $1, %eax + xor %edx, %edx + xrstor 512(%esp) + check_down .Lfail_xrstor_fp + check_sse_nearest .Lfail_xrstor_mask + + mov $3, %eax + xor %edx, %edx + xrstor 512(%esp) + check_down .Lfail_xrstor_both_x87 + check_sse_up .Lfail_xrstor_both_mxcsr + + xor %ebx, %ebx + jmp .Lexit +.Lfail_xcr0: + mov $1, %ebx + jmp .Lexit +.Lfail_xsave_mxcsr: + mov $2, %ebx + jmp .Lexit +.Lfail_xrstor_fp: + mov $3, %ebx + jmp .Lexit +.Lfail_xrstor_mask: + mov $4, %ebx + jmp .Lexit +.Lfail_xrstor_both_x87: + mov $5, %ebx + jmp .Lexit +.Lfail_xrstor_both_mxcsr: + mov $6, %ebx +.Lexit: + mov $__NR_exit, %eax + int $0x80 +.size _start, .-_start + +.section .note.GNU-stack,"",@progbits diff --git a/tests/integration/test-softfpu-xrstor-rounding-i386.sh b/tests/integration/test-softfpu-xrstor-rounding-i386.sh new file mode 100755 index 0000000000..b20f46af5b --- /dev/null +++ b/tests/integration/test-softfpu-xrstor-rounding-i386.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -eu + +emulator=$(readlink -f "$1") +source_file=$(readlink -f "$2") +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +if command -v clang-19 >/dev/null 2>&1; then + clang=clang-19 +elif command -v clang >/dev/null 2>&1; then + clang=clang +else + echo "SKIP: clang is required to build the i386 guest" + exit 77 +fi + +"$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none "$source_file" \ + -o "$workdir/softfpu-xrstor-rounding-i386" + +for mode in 1 2; do + LATX_AOT=0 LATX_AVX_CPUID=1 LATX_MT=0 LATX_SOFTFPU=$mode \ + LATX_SOFTFPU_FAST=0 \ + "$emulator" "$workdir/softfpu-xrstor-rounding-i386" +done +LATX_AOT=0 LATX_AVX_CPUID=1 LATX_MT=0 LATX_SOFTFPU=2 \ + LATX_SOFTFPU_FAST=-1 \ + "$emulator" "$workdir/softfpu-xrstor-rounding-i386" + +echo "PASS: softfpu XRSTOR preserves independent rounding controls" From e609b293f86a8625483cb67b98c086af2ebf41ef Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Mon, 7 Sep 2026 11:45:35 +0800 Subject: [PATCH 08/11] LATX, fix: Restore softfpu rounding after sigreturn Signal-frame restore updates the guest MXCSR outside translated code. The softfpu host FCSR and its saved env copy could therefore retain the handler's rounding mode when execution resumed. Reloading only the XMM registers did not repair that state. Map the restored MXCSR rounding control to both the host floating-point environment and env->fcsr before returning to translated code. Check SSE before x87 in the regressions so an x87 transition cannot hide a stale FCSR value. Tests: - Run the i386 and x86_64 signal-rounding regressions in softfpu modes 1 and 2 with LATX_SOFTFPU_FAST=0, and in mode 2 with LATX_SOFTFPU_FAST=-1. Signed-off-by: Wenqiang Wei --- linux-user/i386/signal.c | 3 + target/i386/cpu.h | 3 + target/i386/tcg/fpu_helper.c | 47 ++++++-- .../registrations/process/meson.build | 16 +++ .../softfpu-signal-rounding-i386.S | 108 ++++++++++++++++++ .../softfpu-signal-rounding-x86_64.S | 108 ++++++++++++++++++ .../test-softfpu-signal-rounding-i386.sh | 29 +++++ .../test-softfpu-signal-rounding-x86_64.sh | 29 +++++ 8 files changed, 336 insertions(+), 7 deletions(-) create mode 100644 tests/integration/softfpu-signal-rounding-i386.S create mode 100644 tests/integration/softfpu-signal-rounding-x86_64.S create mode 100755 tests/integration/test-softfpu-signal-rounding-i386.sh create mode 100755 tests/integration/test-softfpu-signal-rounding-x86_64.sh diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c index 857a126699..1917fc0a20 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -837,6 +837,9 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) unlock_user_struct(fpstate, fpstate_addr, 0); #ifdef CONFIG_LATX load_xmm_from_env(env); + if (option_softfpu) { + cpu_x86_set_host_rounding_from_mxcsr(env); + } #endif } else { err = 0; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7a3f8aeb89..444cdd80de 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1934,6 +1934,9 @@ void cpu_x86_fxsave(CPUX86State *s, target_ulong ptr); void cpu_x86_fxrstor(CPUX86State *s, target_ulong ptr); void cpu_x86_xsave(CPUX86State *s, target_ulong ptr); void cpu_x86_xrstor(CPUX86State *s, target_ulong ptr); +#ifdef CONFIG_LATX +void cpu_x86_set_host_rounding_from_mxcsr(CPUX86State *s); +#endif /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index ff9e0d3f4d..1af410363b 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -763,6 +763,34 @@ uint32_t helper_fnstcw(CPUX86State *env) return env->fpuc; } +static int x86_rounding_mode_to_host(unsigned mode) +{ + static int host_round_mode[4] = { + FE_TONEAREST, + FE_DOWNWARD, + FE_UPWARD, + FE_TOWARDZERO + }; + + assert(mode < ARRAY_SIZE(host_round_mode)); + return host_round_mode[mode]; +} + +#ifdef CONFIG_LATX +static unsigned x86_rounding_mode_to_fcsr(unsigned mode) +{ + static const unsigned fcsr_round_mode[4] = { + 0, /* round to nearest */ + 3, /* round down */ + 2, /* round up */ + 1, /* round toward zero */ + }; + + assert(mode < ARRAY_SIZE(fcsr_round_mode)); + return fcsr_round_mode[mode]; +} +#endif + static void set_x86_rounding_mode(unsigned mode, float_status *status) { static FloatRoundMode x86_round_mode[4] = { @@ -774,13 +802,7 @@ static void set_x86_rounding_mode(unsigned mode, float_status *status) assert(mode < ARRAY_SIZE(x86_round_mode)); set_float_rounding_mode(x86_round_mode[mode], status); if (option_set_rounding_opt) { - static int round_mode_enum[4] = { - FE_TONEAREST, - FE_DOWNWARD, - FE_UPWARD, - FE_TOWARDZERO - }; - fesetround(round_mode_enum[mode]); + fesetround(x86_rounding_mode_to_host(mode)); } } @@ -3312,6 +3334,17 @@ void update_mxcsr_status(CPUX86State *env) set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->sse_status); } +#ifdef CONFIG_LATX +void cpu_x86_set_host_rounding_from_mxcsr(CPUX86State *env) +{ + unsigned mode = (env->mxcsr & SSE_RC_MASK) >> SSE_RC_SHIFT; + + env->fcsr = (env->fcsr & ~(3U << 8)) | + (x86_rounding_mode_to_fcsr(mode) << 8); + fesetround(x86_rounding_mode_to_host(mode)); +} +#endif + void update_mxcsr_from_sse_status(CPUX86State *env) { uint8_t flags = get_float_exception_flags(&env->sse_status); diff --git a/tests/integration/registrations/process/meson.build b/tests/integration/registrations/process/meson.build index be89d26489..5fee4cf1c9 100644 --- a/tests/integration/registrations/process/meson.build +++ b/tests/integration/registrations/process/meson.build @@ -7,6 +7,14 @@ if 'x86_64-linux-user' in target_dirs files('../../softfpu-control-rounding-x86_64.S'), ], }] + latx_integration_tests += [{ + 'name': 'test-softfpu-signal-rounding-x86_64', + 'runner': find_program('../../test-softfpu-signal-rounding-x86_64.sh'), + 'args': [ + emulators['latx-x86_64'], + files('../../softfpu-signal-rounding-x86_64.S'), + ], + }] latx_integration_tests += [{ 'name': 'test-proc-readdir', 'runner': find_program('../../test-proc-readdir.sh'), @@ -128,6 +136,14 @@ if 'i386-linux-user' in target_dirs ], }] endif + latx_integration_tests += [{ + 'name': 'test-softfpu-signal-rounding-i386', + 'runner': find_program('../../test-softfpu-signal-rounding-i386.sh'), + 'args': [ + emulators['latx-i386'], + files('../../softfpu-signal-rounding-i386.S'), + ], + }] latx_integration_tests += [{ 'name': 'test-softfpu-eflags-region-i386', 'runner': find_program('../../test-softfpu-eflags-region-i386.sh'), diff --git a/tests/integration/softfpu-signal-rounding-i386.S b/tests/integration/softfpu-signal-rounding-i386.S new file mode 100644 index 0000000000..a994652651 --- /dev/null +++ b/tests/integration/softfpu-signal-rounding-i386.S @@ -0,0 +1,108 @@ +.equ __NR_exit, 1 +.equ __NR_getpid, 20 +.equ __NR_kill, 37 +.equ __NR_rt_sigreturn, 173 +.equ __NR_rt_sigaction, 174 + +.equ SIGUSR1, 10 +.equ SA_SIGINFO, 4 +.equ SA_RESTORER, 0x04000000 + +.macro check_down fail + fldl 8(%esp) + fcos + fstps 4(%esp) + cmpl $0x3f7fffff, 4(%esp) + jne \fail +.endm + +.macro check_sse_up fail + movss 16(%esp), %xmm0 + addss 20(%esp), %xmm0 + movss %xmm0, 4(%esp) + cmpl $0x3f800001, 4(%esp) + jne \fail +.endm + +.section .text +.global _start +.type _start, @function +_start: + and $-16, %esp + sub $64, %esp + + movw $0x077f, 0(%esp) + movl $0, 8(%esp) + movl $0x3d700000, 12(%esp) + movl $0x3f800000, 16(%esp) + movl $0x33800000, 20(%esp) + movl $0x5f80, 24(%esp) + + mov $__NR_rt_sigaction, %eax + mov $SIGUSR1, %ebx + mov $signal_action, %ecx + xor %edx, %edx + mov $8, %esi + int $0x80 + test %eax, %eax + js .Lfail_sigaction + + fldcw 0(%esp) + ldmxcsr 24(%esp) + mov $__NR_getpid, %eax + int $0x80 + mov %eax, %ebx + mov $SIGUSR1, %ecx + mov $__NR_kill, %eax + int $0x80 + test %eax, %eax + js .Lfail_signal + + check_sse_up .Lfail_mxcsr + check_down .Lfail_x87 + xor %ebx, %ebx + jmp .Lexit +.Lfail_sigaction: + mov $1, %ebx + jmp .Lexit +.Lfail_signal: + mov $2, %ebx + jmp .Lexit +.Lfail_x87: + mov $3, %ebx + jmp .Lexit +.Lfail_mxcsr: + mov $4, %ebx +.Lexit: + mov $__NR_exit, %eax + int $0x80 +.size _start, .-_start + +.type signal_handler, @function +signal_handler: + fldcw signal_nearest_cw + ldmxcsr signal_down_mxcsr + ret +.size signal_handler, .-signal_handler + +.type signal_restorer, @function +signal_restorer: + mov $__NR_rt_sigreturn, %eax + int $0x80 + ud2 +.size signal_restorer, .-signal_restorer + +.section .data +.align 8 +signal_action: + .long signal_handler + .long SA_SIGINFO | SA_RESTORER + .long signal_restorer + .quad 0 +signal_nearest_cw: + .short 0x037f +.align 4 +signal_down_mxcsr: + .long 0x3f80 + +.section .note.GNU-stack,"",@progbits diff --git a/tests/integration/softfpu-signal-rounding-x86_64.S b/tests/integration/softfpu-signal-rounding-x86_64.S new file mode 100644 index 0000000000..cd6d9db990 --- /dev/null +++ b/tests/integration/softfpu-signal-rounding-x86_64.S @@ -0,0 +1,108 @@ +.equ __NR_rt_sigaction, 13 +.equ __NR_rt_sigreturn, 15 +.equ __NR_getpid, 39 +.equ __NR_kill, 62 +.equ __NR_exit, 60 + +.equ SIGUSR1, 10 +.equ SA_SIGINFO, 4 +.equ SA_RESTORER, 0x04000000 + +.macro check_down fail + fldl 8(%rsp) + fcos + fstps 4(%rsp) + cmpl $0x3f7fffff, 4(%rsp) + jne \fail +.endm + +.macro check_sse_up fail + movss 16(%rsp), %xmm0 + addss 20(%rsp), %xmm0 + movss %xmm0, 4(%rsp) + cmpl $0x3f800001, 4(%rsp) + jne \fail +.endm + +.section .text +.global _start +.type _start, @function +_start: + and $-16, %rsp + sub $64, %rsp + + movw $0x077f, 0(%rsp) + movl $0, 8(%rsp) + movl $0x3d700000, 12(%rsp) + movl $0x3f800000, 16(%rsp) + movl $0x33800000, 20(%rsp) + movl $0x5f80, 24(%rsp) + + mov $__NR_rt_sigaction, %eax + mov $SIGUSR1, %edi + lea signal_action(%rip), %rsi + xor %edx, %edx + mov $8, %r10d + syscall + test %rax, %rax + js .Lfail_sigaction + + fldcw 0(%rsp) + ldmxcsr 24(%rsp) + mov $__NR_getpid, %eax + syscall + mov %eax, %edi + mov $SIGUSR1, %esi + mov $__NR_kill, %eax + syscall + test %rax, %rax + js .Lfail_signal + + check_sse_up .Lfail_mxcsr + check_down .Lfail_x87 + xor %edi, %edi + jmp .Lexit +.Lfail_sigaction: + mov $1, %edi + jmp .Lexit +.Lfail_signal: + mov $2, %edi + jmp .Lexit +.Lfail_x87: + mov $3, %edi + jmp .Lexit +.Lfail_mxcsr: + mov $4, %edi +.Lexit: + mov $__NR_exit, %eax + syscall +.size _start, .-_start + +.type signal_handler, @function +signal_handler: + fldcw signal_nearest_cw(%rip) + ldmxcsr signal_down_mxcsr(%rip) + ret +.size signal_handler, .-signal_handler + +.type signal_restorer, @function +signal_restorer: + mov $__NR_rt_sigreturn, %eax + syscall + ud2 +.size signal_restorer, .-signal_restorer + +.section .data +.align 8 +signal_action: + .quad signal_handler + .quad SA_SIGINFO | SA_RESTORER + .quad signal_restorer + .quad 0 +signal_nearest_cw: + .short 0x037f +.align 4 +signal_down_mxcsr: + .long 0x3f80 + +.section .note.GNU-stack,"",@progbits diff --git a/tests/integration/test-softfpu-signal-rounding-i386.sh b/tests/integration/test-softfpu-signal-rounding-i386.sh new file mode 100755 index 0000000000..9832a38e5c --- /dev/null +++ b/tests/integration/test-softfpu-signal-rounding-i386.sh @@ -0,0 +1,29 @@ +#!/bin/sh +set -eu + +emulator=$(readlink -f "$1") +source_file=$(readlink -f "$2") +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +if command -v clang-19 >/dev/null 2>&1; then + clang=clang-19 +elif command -v clang >/dev/null 2>&1; then + clang=clang +else + echo "SKIP: clang is required to build the i386 guest" + exit 77 +fi + +"$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none "$source_file" \ + -o "$workdir/softfpu-signal-rounding-i386" + +for mode in 1 2; do + LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=$mode LATX_SOFTFPU_FAST=0 \ + "$emulator" "$workdir/softfpu-signal-rounding-i386" +done +LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=2 LATX_SOFTFPU_FAST=-1 \ + "$emulator" "$workdir/softfpu-signal-rounding-i386" + +echo "PASS: i386 sigreturn restores softfpu rounding state" diff --git a/tests/integration/test-softfpu-signal-rounding-x86_64.sh b/tests/integration/test-softfpu-signal-rounding-x86_64.sh new file mode 100755 index 0000000000..c30f72f641 --- /dev/null +++ b/tests/integration/test-softfpu-signal-rounding-x86_64.sh @@ -0,0 +1,29 @@ +#!/bin/sh +set -eu + +emulator=$(readlink -f "$1") +source_file=$(readlink -f "$2") +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT HUP INT TERM + +if command -v clang-19 >/dev/null 2>&1; then + clang=clang-19 +elif command -v clang >/dev/null 2>&1; then + clang=clang +else + echo "SKIP: clang is required to build the x86_64 guest" + exit 77 +fi + +"$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none "$source_file" \ + -o "$workdir/softfpu-signal-rounding-x86_64" + +for mode in 1 2; do + LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=$mode LATX_SOFTFPU_FAST=0 \ + "$emulator" "$workdir/softfpu-signal-rounding-x86_64" +done +LATX_AOT=0 LATX_MT=0 LATX_SOFTFPU=2 LATX_SOFTFPU_FAST=-1 \ + "$emulator" "$workdir/softfpu-signal-rounding-x86_64" + +echo "PASS: x86_64 sigreturn restores softfpu rounding state" From 00ca91568e9fecc02ba884ed5d15a45ec01d6d31 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Tue, 8 Sep 2026 09:50:59 +0800 Subject: [PATCH 09/11] LATX, fix: Preserve reserved x87 precision semantics QEMU treats the reserved x87 precision-control value 01 as extended precision. The inline softfpu state update instead mapped it to single precision through an arithmetic transform. Use explicit precision-control branches that match update_fp_status(). Extend both control-rounding guests to distinguish extended from single precision for the reserved encoding. Tests: - Run the i386 and x86_64 control-rounding regressions in softfpu modes 1 and 2 with LATX_SOFTFPU_FAST=0, and in mode 2 with LATX_SOFTFPU_FAST=-1. Signed-off-by: Wenqiang Wei --- target/i386/latx/translator/tr-softfpu.c | 24 ++++++++++++++----- .../softfpu-control-rounding-i386.S | 15 ++++++++++++ .../softfpu-control-rounding-x86_64.S | 15 ++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/target/i386/latx/translator/tr-softfpu.c b/target/i386/latx/translator/tr-softfpu.c index 4b75486b6f..f63ffbd95c 100644 --- a/target/i386/latx/translator/tr-softfpu.c +++ b/target/i386/latx/translator/tr-softfpu.c @@ -338,7 +338,9 @@ static void la_update_fp_status(IR2_OPND cw_opnd) IR2_OPND itemp = ra_alloc_itemp(); IR2_OPND tmp_fcsr = ra_alloc_itemp(); - IR2_OPND label_float64_float80 = ra_alloc_label(); + IR2_OPND label_float32 = ra_alloc_label(); + IR2_OPND label_float64 = ra_alloc_label(); + IR2_OPND label_precision_done = ra_alloc_label(); int fp_status_offset = lsenv_offset_of_fp_status(lsenv); int round_mode_offset = fp_status_offset + @@ -359,18 +361,28 @@ static void la_update_fp_status(IR2_OPND cw_opnd) * x86 env * * 32 00 10 + * reserved 01 00 * 64 10 01 - * 80 11 10 + * 80 11 00 * */ /* Precision Control (9, 8)*/ la_bstrpick_d(itemp, cw_opnd, 9, 8); - la_bne(itemp, zero_ir2_opnd, label_float64_float80); - li_wu(itemp, 1); + la_beq(itemp, zero_ir2_opnd, label_float32); + li_wu(tmp_fcsr, 2); + la_beq(itemp, tmp_fcsr, label_float64); + li_wu(itemp, floatx80_precision_x); + la_b(label_precision_done); - la_label(label_float64_float80); - la_xori(itemp, itemp, 3); + la_label(label_float32); + li_wu(itemp, floatx80_precision_s); + la_b(label_precision_done); + + la_label(label_float64); + li_wu(itemp, floatx80_precision_d); + + la_label(label_precision_done); la_st_b(itemp, env_ir2_opnd, round_precision_offset); ra_free_temp(itemp); diff --git a/tests/integration/softfpu-control-rounding-i386.S b/tests/integration/softfpu-control-rounding-i386.S index 49ee76bef0..8b6dc948f3 100644 --- a/tests/integration/softfpu-control-rounding-i386.S +++ b/tests/integration/softfpu-control-rounding-i386.S @@ -37,6 +37,18 @@ _start: movl $0x3f800000, 16(%esp) movl $0x33800000, 20(%esp) + /* Reserved PC=01 follows QEMU's default extended-precision behavior. */ + movw $0x017f, 128(%esp) + fldcw 128(%esp) + fld1 + fldl 8(%esp) + faddp + fstpl 136(%esp) + cmpl $0x00001000, 136(%esp) + jne .Lfail_reserved_pc + cmpl $0x3ff00000, 140(%esp) + jne .Lfail_reserved_pc + /* FLDCW must update both softfloat state and the host FCSR. */ fninit fldcw 0(%esp) @@ -122,6 +134,9 @@ _start: jmp .Lexit .Lfail_fxrstor_x87_again: mov $11, %ebx + jmp .Lexit +.Lfail_reserved_pc: + mov $12, %ebx .Lexit: mov $__NR_exit, %eax int $0x80 diff --git a/tests/integration/softfpu-control-rounding-x86_64.S b/tests/integration/softfpu-control-rounding-x86_64.S index 3d031e6683..6907b6ca49 100644 --- a/tests/integration/softfpu-control-rounding-x86_64.S +++ b/tests/integration/softfpu-control-rounding-x86_64.S @@ -29,6 +29,18 @@ _start: movl $0x3f800000, 16(%rsp) movl $0x33800000, 20(%rsp) + /* Reserved PC=01 follows QEMU's default extended-precision behavior. */ + movw $0x017f, 128(%rsp) + fldcw 128(%rsp) + fld1 + fldl 8(%rsp) + faddp + fstpl 136(%rsp) + cmpl $0x00001000, 136(%rsp) + jne .Lfail_reserved_pc + cmpl $0x3ff00000, 140(%rsp) + jne .Lfail_reserved_pc + fldcw 0(%rsp) movl $0x5f80, 24(%rsp) ldmxcsr 24(%rsp) @@ -55,6 +67,9 @@ _start: jmp .Lexit .Lfail_fxrstor_mxcsr: mov $3, %edi + jmp .Lexit +.Lfail_reserved_pc: + mov $4, %edi .Lexit: mov $__NR_exit, %eax syscall From 1f0b6f8da12c4787bbc511ddae8b9e72b73a9ba7 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Tue, 8 Sep 2026 09:55:11 +0800 Subject: [PATCH 10/11] tests: skip softfpu guests without a working LLD linker The softfpu integration runners only checked for clang before selecting LLD unconditionally. Hosts with clang but no usable ld.lld therefore reported test failures instead of an unavailable optional dependency. Probe the exact target-specific link command before building each guest and return the Meson skip status when that capability is unavailable. Tests: - Verify all 12 softfpu runners return status 77 when clang exists but ld.lld does not. Signed-off-by: Wenqiang Wei --- tests/integration/test-softfpu-control-rounding-i386.sh | 6 ++++++ tests/integration/test-softfpu-control-rounding-x86_64.sh | 6 ++++++ tests/integration/test-softfpu-eflags-region-i386.sh | 6 ++++++ tests/integration/test-softfpu-fxsave-mxcsr-i386.sh | 6 ++++++ tests/integration/test-softfpu-helper-store-gpr-x86_64.sh | 6 ++++++ tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh | 6 ++++++ tests/integration/test-softfpu-region-planner-x86_64.sh | 6 ++++++ tests/integration/test-softfpu-shared-region-gpr-x86_64.sh | 6 ++++++ tests/integration/test-softfpu-signal-rounding-i386.sh | 6 ++++++ tests/integration/test-softfpu-signal-rounding-x86_64.sh | 6 ++++++ tests/integration/test-softfpu-xgetbv-gpr-x86_64.sh | 6 ++++++ tests/integration/test-softfpu-xrstor-rounding-i386.sh | 6 ++++++ 12 files changed, 72 insertions(+) diff --git a/tests/integration/test-softfpu-control-rounding-i386.sh b/tests/integration/test-softfpu-control-rounding-i386.sh index da745b55b1..75dec8bb6f 100755 --- a/tests/integration/test-softfpu-control-rounding-i386.sh +++ b/tests/integration/test-softfpu-control-rounding-i386.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the i386 guest" exit 77 fi +if ! "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the i386 guest" + exit 77 +fi "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-control-rounding-x86_64.sh b/tests/integration/test-softfpu-control-rounding-x86_64.sh index 3a823ffdf4..173b779306 100755 --- a/tests/integration/test-softfpu-control-rounding-x86_64.sh +++ b/tests/integration/test-softfpu-control-rounding-x86_64.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the x86_64 guest" exit 77 fi +if ! "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the x86_64 guest" + exit 77 +fi "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-eflags-region-i386.sh b/tests/integration/test-softfpu-eflags-region-i386.sh index 534f279d1d..25de724a75 100755 --- a/tests/integration/test-softfpu-eflags-region-i386.sh +++ b/tests/integration/test-softfpu-eflags-region-i386.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the i386 guest" exit 77 fi +if ! "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the i386 guest" + exit 77 +fi "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-fxsave-mxcsr-i386.sh b/tests/integration/test-softfpu-fxsave-mxcsr-i386.sh index 35658caec4..44f4c8f0bf 100755 --- a/tests/integration/test-softfpu-fxsave-mxcsr-i386.sh +++ b/tests/integration/test-softfpu-fxsave-mxcsr-i386.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the i386 guest" exit 77 fi +if ! "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the i386 guest" + exit 77 +fi "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-helper-store-gpr-x86_64.sh b/tests/integration/test-softfpu-helper-store-gpr-x86_64.sh index 1a5b0872d0..6052b89401 100755 --- a/tests/integration/test-softfpu-helper-store-gpr-x86_64.sh +++ b/tests/integration/test-softfpu-helper-store-gpr-x86_64.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the x86_64 guest" exit 77 fi +if ! "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the x86_64 guest" + exit 77 +fi "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh b/tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh index 28dfe2bdee..fc731811af 100755 --- a/tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh +++ b/tests/integration/test-softfpu-ldmxcsr-rounding-i386.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the i386 guest" exit 77 fi +if ! "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the i386 guest" + exit 77 +fi "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-region-planner-x86_64.sh b/tests/integration/test-softfpu-region-planner-x86_64.sh index a6d1b3ac6c..eaa716caf6 100755 --- a/tests/integration/test-softfpu-region-planner-x86_64.sh +++ b/tests/integration/test-softfpu-region-planner-x86_64.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the x86_64 guest" exit 77 fi +if ! "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the x86_64 guest" + exit 77 +fi "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-shared-region-gpr-x86_64.sh b/tests/integration/test-softfpu-shared-region-gpr-x86_64.sh index 636c43169a..b9d98fecc2 100755 --- a/tests/integration/test-softfpu-shared-region-gpr-x86_64.sh +++ b/tests/integration/test-softfpu-shared-region-gpr-x86_64.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the x86_64 guest" exit 77 fi +if ! "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the x86_64 guest" + exit 77 +fi "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-signal-rounding-i386.sh b/tests/integration/test-softfpu-signal-rounding-i386.sh index 9832a38e5c..fc7cb9f138 100755 --- a/tests/integration/test-softfpu-signal-rounding-i386.sh +++ b/tests/integration/test-softfpu-signal-rounding-i386.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the i386 guest" exit 77 fi +if ! "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the i386 guest" + exit 77 +fi "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-signal-rounding-x86_64.sh b/tests/integration/test-softfpu-signal-rounding-x86_64.sh index c30f72f641..a3fe0310a5 100755 --- a/tests/integration/test-softfpu-signal-rounding-x86_64.sh +++ b/tests/integration/test-softfpu-signal-rounding-x86_64.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the x86_64 guest" exit 77 fi +if ! "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the x86_64 guest" + exit 77 +fi "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-xgetbv-gpr-x86_64.sh b/tests/integration/test-softfpu-xgetbv-gpr-x86_64.sh index a763c05d2d..e8d3da9ce5 100755 --- a/tests/integration/test-softfpu-xgetbv-gpr-x86_64.sh +++ b/tests/integration/test-softfpu-xgetbv-gpr-x86_64.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the x86_64 guest" exit 77 fi +if ! "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the x86_64 guest" + exit 77 +fi "$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ diff --git a/tests/integration/test-softfpu-xrstor-rounding-i386.sh b/tests/integration/test-softfpu-xrstor-rounding-i386.sh index b20f46af5b..06d0be445b 100755 --- a/tests/integration/test-softfpu-xrstor-rounding-i386.sh +++ b/tests/integration/test-softfpu-xrstor-rounding-i386.sh @@ -14,6 +14,12 @@ else echo "SKIP: clang is required to build the i386 guest" exit 77 fi +if ! "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ + -Wl,--build-id=none -x assembler /dev/null \ + -o "$workdir/linker-check" >/dev/null 2>&1; then + echo "SKIP: clang with ld.lld is required to build the i386 guest" + exit 77 +fi "$clang" --target=i386-linux-gnu -fuse-ld=lld -nostdlib -static \ -Wl,--build-id=none "$source_file" \ From c0f173956706132cf1e9b3718b24fdf40a463867 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Tue, 8 Sep 2026 15:34:51 +0800 Subject: [PATCH 11/11] tests: check SSE immediately after softfpu XRSTOR The x87 check exits its FCSR region before the SSE assertion and can repair the stale host rounding state left by a broken XRSTOR path. Check SSE first so the regression observes XRSTOR's result directly. Tests: - Restoring the old XRSTOR implementation fails with status 6 in modes 1 and 2. The fixed implementation passes in both modes and mode 2 with LATX_SOFTFPU_FAST=-1 on the AVX-enabled i386 build. Signed-off-by: Wenqiang Wei --- tests/integration/softfpu-xrstor-rounding-i386.S | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration/softfpu-xrstor-rounding-i386.S b/tests/integration/softfpu-xrstor-rounding-i386.S index f65c74fbff..bca0883f4d 100644 --- a/tests/integration/softfpu-xrstor-rounding-i386.S +++ b/tests/integration/softfpu-xrstor-rounding-i386.S @@ -61,14 +61,15 @@ _start: mov $1, %eax xor %edx, %edx xrstor 512(%esp) - check_down .Lfail_xrstor_fp + /* Check SSE before an x87 exit can repair a stale host FCSR. */ check_sse_nearest .Lfail_xrstor_mask + check_down .Lfail_xrstor_fp mov $3, %eax xor %edx, %edx xrstor 512(%esp) - check_down .Lfail_xrstor_both_x87 check_sse_up .Lfail_xrstor_both_mxcsr + check_down .Lfail_xrstor_both_x87 xor %ebx, %ebx jmp .Lexit