From 28f8bc7eb95cc2c11e30e6ea041b05a22dbff1f3 Mon Sep 17 00:00:00 2001 From: Zhaoyang Xie Date: Mon, 26 Oct 2026 15:37:15 +0800 Subject: [PATCH 1/2] LATX-X64, fix: Release TEST paired source temps Issue #524 exhausts itemp registers when a TEST memory operand is paired with CMOVcc while memory permission checks are enabled. The two TEST sources are dead after Eflags and the AND condition are emitted, so release them before translating CMOVcc. Add a static x86_64 guest covering LATX_MT=0/1/2 on 16K host pages. Signed-off-by: Zhaoyang Xie --- target/i386/latx/translator/tr-pattern.c | 2 ++ tests/integration/latx-mt-test-cmov-mem.asm | 24 +++++++++++++ .../registrations/process/meson.build | 8 +++++ .../integration/test-latx-mt-test-cmov-mem.sh | 36 +++++++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 tests/integration/latx-mt-test-cmov-mem.asm create mode 100755 tests/integration/test-latx-mt-test-cmov-mem.sh diff --git a/target/i386/latx/translator/tr-pattern.c b/target/i386/latx/translator/tr-pattern.c index 55f139ae90..ee15720703 100644 --- a/target/i386/latx/translator/tr-pattern.c +++ b/target/i386/latx/translator/tr-pattern.c @@ -1247,6 +1247,8 @@ static bool translate_test_xxcc(IR1_INST *pir1) generate_eflag_calculation(src0, src0, src1, curr, true); temp = ra_alloc_itemp(); la_and(temp, src0, src1); + ra_free_temp_auto(src0); + ra_free_temp_auto(src1); } lsenv->tr_data->curr_ir1_inst = next; diff --git a/tests/integration/latx-mt-test-cmov-mem.asm b/tests/integration/latx-mt-test-cmov-mem.asm new file mode 100644 index 0000000000..e05f82779a --- /dev/null +++ b/tests/integration/latx-mt-test-cmov-mem.asm @@ -0,0 +1,24 @@ +default rel + +section .text +global _start +_start: + test byte [test_value], 0x20 + cmovne rax, [selected_value] + cmp al, 1 + jne failed + + xor edi, edi + mov eax, 60 + syscall + +failed: + mov edi, 1 + mov eax, 60 + syscall + +section .data +test_value: + db 0x20 +selected_value: + dq 1 diff --git a/tests/integration/registrations/process/meson.build b/tests/integration/registrations/process/meson.build index 1a09752481..46c3379202 100644 --- a/tests/integration/registrations/process/meson.build +++ b/tests/integration/registrations/process/meson.build @@ -99,6 +99,14 @@ if 'x86_64-linux-user' in target_dirs ], 'timeout': 90, }] + latx_integration_tests += [{ + 'name': 'test-latx-mt-test-cmov-mem', + 'runner': find_program('../../test-latx-mt-test-cmov-mem.sh'), + 'args': [ + emulators['latx-x86_64'], + files('../../latx-mt-test-cmov-mem.asm'), + ], + }] latx_integration_tests += [{ 'name': 'test-cmdline-other-task', 'runner': find_program('../../test-cmdline-other-task.sh'), diff --git a/tests/integration/test-latx-mt-test-cmov-mem.sh b/tests/integration/test-latx-mt-test-cmov-mem.sh new file mode 100755 index 0000000000..10359a6fa0 --- /dev/null +++ b/tests/integration/test-latx-mt-test-cmov-mem.sh @@ -0,0 +1,36 @@ +#!/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 [ "$(getconf PAGESIZE)" != 16384 ]; then + echo "SKIP: LATX_MT requires a 16K host page" + exit 77 +fi + +if ! command -v nasm >/dev/null 2>&1; then + echo "SKIP: nasm is required to build the x86_64 guest" + exit 77 +fi + +if ! command -v ld.lld >/dev/null 2>&1; then + echo "SKIP: ld.lld is required to build the x86_64 guest" + exit 77 +fi + +guest="$workdir/latx-mt-test-cmov-mem" +nasm -f elf64 "$source_file" -o "$workdir/guest.o" +ld.lld -m elf_x86_64 -static --build-id=none "$workdir/guest.o" -o "$guest" + +for mode in 0 1 2; do + if ! LATX_AOT=0 LATX_KZT=0 LATX_MT="$mode" \ + timeout 30 "$emulator" "$guest"; then + echo "FAIL: LATX_MT=$mode did not execute TEST+CMOVcc" >&2 + exit 1 + fi +done + +echo "PASS: TEST+CMOVcc memory pair runs with LATX_MT=0/1/2" From 8e191aeafd951b6cc6e4af84de919654a997eb4d Mon Sep 17 00:00:00 2001 From: Zhaoyang Xie Date: Mon, 26 Oct 2026 16:04:08 +0800 Subject: [PATCH 2/2] LATX, fix: Release memory address temps Signed-off-by: Zhaoyang Xie --- target/i386/latx/include/reg-alloc.h | 1 + target/i386/latx/translator/mem-interface.c | 9 ++++++++- target/i386/latx/translator/reg-alloc.c | 7 +++++++ target/i386/latx/translator/tr-opnd-process.c | 16 ++++++++++------ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/target/i386/latx/include/reg-alloc.h b/target/i386/latx/include/reg-alloc.h index b94e905f40..4df72f831f 100644 --- a/target/i386/latx/include/reg-alloc.h +++ b/target/i386/latx/include/reg-alloc.h @@ -167,6 +167,7 @@ bool ld_imm_to_imm_reg(uint64); #define ra_free_ftemp(phy_id) RA_FREE_CODE(ftemp, phy_id) void ra_free_all(void); bool itemp_is_free(int itemp_num); +void ra_free_mem_temp(IR2_OPND opnd); IR2_OPND ra_alloc_num_4095(void); void ra_free_num_4095(IR2_OPND); void free_imm_reg(int itemp_num); diff --git a/target/i386/latx/translator/mem-interface.c b/target/i386/latx/translator/mem-interface.c index 0aca12bd6e..a1b6731759 100644 --- a/target/i386/latx/translator/mem-interface.c +++ b/target/i386/latx/translator/mem-interface.c @@ -579,7 +579,14 @@ static IR2_OPND convert_mem_helper(IR1_OPND *opnd1, IR2_OPND *arg_dest_op, } addr_size = ir1_addr_size(pir1); - return adjust_dest(dest_op, arg_dest_op, dest_size, addr_size); + IR2_OPND ret_opnd = adjust_dest(dest_op, arg_dest_op, + dest_size, addr_size); + + if (arg_dest_op && dest_need_itmp) { + ra_free_mem_temp(dest_op); + } + + return ret_opnd; } void convert_mem_to_specific_gpr(IR1_OPND *opnd, IR2_OPND dest, int dest_size) diff --git a/target/i386/latx/translator/reg-alloc.c b/target/i386/latx/translator/reg-alloc.c index c3181760ff..3c60376bd9 100644 --- a/target/i386/latx/translator/reg-alloc.c +++ b/target/i386/latx/translator/reg-alloc.c @@ -381,6 +381,13 @@ void imm_cache_free_temp_helper(IR2_OPND dest_op) imm_log("[free_temp_helper]call but not free\n"); } +void ra_free_mem_temp(IR2_OPND opnd) +{ + if (ir2_opnd_is_itemp(&opnd) && !ir2_opnd_is_imm_reg(&opnd)) { + ra_free_temp(opnd); + } +} + IR2_OPND ra_alloc_num_4095(void) { IR2_OPND num = ra_alloc_itemp(); diff --git a/target/i386/latx/translator/tr-opnd-process.c b/target/i386/latx/translator/tr-opnd-process.c index bca7bc8134..b5cbc3104f 100644 --- a/target/i386/latx/translator/tr-opnd-process.c +++ b/target/i386/latx/translator/tr-opnd-process.c @@ -248,9 +248,7 @@ void load_ireg_from_ir1_mem(IR2_OPND opnd2, IR1_OPND *opnd1, lsassert(0); } } - if (ir2_opnd_is_itemp(&mem_opnd) && !ir2_opnd_is_imm_reg(&mem_opnd)) { - ra_free_temp(mem_opnd); - } + ra_free_mem_temp(mem_opnd); } IR2_OPND load_ireg_from_ir2_mem(IR2_OPND mem_opnd, int mem_imm, int mem_size, @@ -617,9 +615,7 @@ static void store_ireg_to_ir1_mem(IR2_OPND value_opnd, IR1_OPND *opnd1, lsassert(0); } } - if (ir2_opnd_is_itemp(&mem_opnd) && !ir2_opnd_is_imm_reg(&mem_opnd)) { - ra_free_temp(mem_opnd); - } + ra_free_mem_temp(mem_opnd); } void store_ireg_to_ir1_seg(IR2_OPND seg_value_opnd, IR1_OPND *opnd1) @@ -949,6 +945,8 @@ static void load_freg_from_ir1_mem(IR2_OPND opnd2, IR1_OPND *opnd1, } else { load_64_bit_freg_from_ir1_80_bit_mem(opnd2, mem_opnd, mem_imm); } + + ra_free_mem_temp(mem_opnd); } /* Clear lsenv->mode_trans_mmx_fpu to transfer to MMX mode */ @@ -1185,6 +1183,8 @@ static void store_freg_to_ir1_mem(IR2_OPND opnd2, IR1_OPND *opnd1, } else { store_64_bit_freg_to_ir1_80_bit_mem(opnd2, mem_opnd, mem_imm); } + + ra_free_mem_temp(mem_opnd); } static void store_freg_to_ir1_gpr(IR2_OPND opnd2, IR1_OPND *opnd1) @@ -1323,6 +1323,7 @@ void load_freg128_from_ir1_mem(IR2_OPND opnd2, IR1_OPND *opnd1){ IR2_OPND mem_opnd = convert_mem(opnd1, &little_disp); gen_test_page_flag(mem_opnd, little_disp, PAGE_READ, 16); la_vld(opnd2, mem_opnd, little_disp); + ra_free_mem_temp(mem_opnd); return; } @@ -1336,6 +1337,7 @@ void store_freg128_to_ir1_mem(IR2_OPND opnd2, IR1_OPND *opnd1){ gen_test_page_flag(mem_opnd, little_disp, PAGE_WRITE | PAGE_WRITE_ORG, 16); la_vst(opnd2, mem_opnd, little_disp); + ra_free_mem_temp(mem_opnd); return; } @@ -1373,6 +1375,7 @@ void store_freg256_to_ir1_mem(IR2_OPND opnd2, IR1_OPND * opnd1) { gen_test_page_flag(mem_opnd, little_disp, PAGE_WRITE | PAGE_WRITE_ORG, 32); la_xvst(opnd2, mem_opnd, little_disp); + ra_free_mem_temp(mem_opnd); } void load_freg256_from_ir1_mem(IR2_OPND opnd2, IR1_OPND * opnd1) { @@ -1383,6 +1386,7 @@ void load_freg256_from_ir1_mem(IR2_OPND opnd2, IR1_OPND * opnd1) { IR2_OPND mem_opnd = convert_mem(opnd1, & little_disp); gen_test_page_flag(mem_opnd, little_disp, PAGE_READ, 32); la_xvld(opnd2, mem_opnd, little_disp); + ra_free_mem_temp(mem_opnd); } IR2_OPND load_freg256_from_ir1(IR1_OPND * opnd1) {