Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions linux-user/i386/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ static void setup_sigcontext(struct target_sigcontext *sc,
CPUState *cs = env_cpu(env);
#ifdef CONFIG_LATX
save_xmm_to_env(env);
cpu_x86_canonicalize_latx_mmx_state(env);
#endif
#ifndef TARGET_X86_64
uint16_t magic;
Expand Down Expand Up @@ -638,6 +639,8 @@ void setup_frame(int sig, struct target_sigaction *ka,

unlock_user_struct(frame, frame_addr, 1);

cpu_x86_init_user_x87(env);

return;

give_sigsegv:
Expand Down Expand Up @@ -728,6 +731,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,

unlock_user_struct(frame, frame_addr, 1);

cpu_x86_init_user_x87(env);

return;

give_sigsegv:
Expand Down Expand Up @@ -836,6 +841,10 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc)
#endif
unlock_user_struct(fpstate, fpstate_addr, 0);
#ifdef CONFIG_LATX
if (!err) {
cpu_x86_sync_latx_fcsr(env);
cpu_x86_sync_latx_fpu_mode(env);
}
load_xmm_from_env(env);
#endif
} else {
Expand Down
17 changes: 16 additions & 1 deletion target/i386/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,15 @@ typedef union {
MMXReg mmx;
} FPReg;

#ifdef CONFIG_LATX
enum {
LATX_FPU_MODE_MMX,
LATX_FPU_MODE_X87,
/* Reload the MMX view on reentry; the restored env stays authoritative. */
LATX_FPU_MODE_RESTORED,
};
#endif

typedef struct {
uint64_t base;
uint64_t mask;
Expand Down Expand Up @@ -1424,7 +1433,7 @@ typedef struct CPUX86State {
int func_index;
int last_func_index;
#endif
bool mode_fpu;
uint8_t mode_fpu;
bool fpu_clobber;
#endif
/* standard registers */
Expand Down Expand Up @@ -1934,6 +1943,12 @@ 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);
void cpu_x86_init_user_x87(CPUX86State *s);
#ifdef CONFIG_LATX
void cpu_x86_canonicalize_latx_mmx_state(CPUX86State *s);
void cpu_x86_sync_latx_fcsr(CPUX86State *s);
void cpu_x86_sync_latx_fpu_mode(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
Expand Down
14 changes: 12 additions & 2 deletions target/i386/latx/translator/translate.c
Original file line number Diff line number Diff line change
Expand Up @@ -4068,8 +4068,14 @@ void tr_load_registers_from_env(uint8 gpr_to_load, uint8 fpr_to_load,

/* check current mode(mmx/fpu) */
if (option_softfpu == 2) {
la_ld_wu(mode_fpu, env_ir2_opnd, lsenv_offset_of_mode_fpu(lsenv));
la_bne(mode_fpu, zero_ir2_opnd, label_fpu);
la_ld_bu(mode_fpu, env_ir2_opnd, lsenv_offset_of_mode_fpu(lsenv));
if (fpr_to_load == 0xff) {
/* A signal restore must initialize MMX without selecting it. */
la_xori(mode_fpu, mode_fpu, LATX_FPU_MODE_X87);
la_beq(mode_fpu, zero_ir2_opnd, label_fpu);
} else {
la_bne(mode_fpu, zero_ir2_opnd, label_fpu);
}
}

for (i = 0; i < 8; i++) {
Expand Down Expand Up @@ -4320,7 +4326,9 @@ static inline void helper_restore_reg(IR2_OPND opnd)
void gen_test_page_flag(IR2_OPND mem_opnd, int mem_imm, uint32_t flag,
unsigned int mem_size)
{
#if TARGET_ABI_BITS == 32
uint32_t required_flag = flag & PAGE_WRITE ? PAGE_WRITE : PAGE_READ;
#endif

if (!option_mem_test) {
#if TARGET_ABI_BITS == 32
Expand Down Expand Up @@ -4351,7 +4359,9 @@ void gen_test_page_flag(IR2_OPND mem_opnd, int mem_imm, uint32_t flag,
IR2_OPND label1 = ra_alloc_label();
IR2_OPND label2 = ra_alloc_label();
IR2_OPND label_fault = ra_alloc_label();
#if TARGET_ABI_BITS == 32
IR2_OPND label_check_end = ra_alloc_label();
#endif
bool need_restore0 = false;
bool need_restore1 = false;
bool need_restore2 = false;
Expand Down
89 changes: 89 additions & 0 deletions target/i386/tcg/fpu_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -3222,6 +3222,95 @@ void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr)
{
do_xrstor(env, ptr, -1, 0);
}

#ifdef CONFIG_LATX
void cpu_x86_canonicalize_latx_mmx_state(CPUX86State *env)
{
int i;

if (env->mode_fpu) {
return;
}

env->fpstt = 0;
memset(env->fptags, 0, sizeof(env->fptags));
for (i = 0; i < 8; i++) {
CPU_LDoubleU reg = { .d = env->fpregs[i].d };

reg.l.upper = 0xffff;
env->fpregs[i].d = reg.d;
}
}

static bool latx_x87_state_is_mmx(const CPUX86State *env)
{
int i;

/* MMX sets every x87 tag valid and every physical exponent to 0xffff. */
for (i = 0; i < 8; i++) {
CPU_LDoubleU reg = { .d = env->fpregs[i].d };

if (env->fptags[i] || reg.l.upper != 0xffff) {
return false;
}
}
return true;
}

void cpu_x86_sync_latx_fcsr(CPUX86State *env)
{
static const uint8_t rounding_map[4] = { 0, 3, 2, 1 };
static const uint8_t exception_map[5] = { 5, 4, 3, 2, 0 };
uint32_t fpuc = env->fpuc;
uint32_t fpus = env->fpus;
uint32_t rc = (fpuc & FPU_RC_MASK) >> FPU_RC_SHIFT;
uint32_t fcsr = rounding_map[rc] << 8;
int i;

/* Keep these exception mappings in sync with tr-fctrl.c. */
for (i = 0; i < 5; i++) {
uint32_t x87_mask = 1 << exception_map[i];

if ((!option_enable_fcsr_exc || i != 0) && !(fpuc & x87_mask)) {
fcsr |= 1 << i;
}
if (fpus & x87_mask) {
fcsr |= 1 << (16 + i);
}
}

env->fcsr = fcsr;
}

void cpu_x86_sync_latx_fpu_mode(CPUX86State *env)
{
/*
* MMX and a full x87 stack of negative NaNs/infinities have identical
* tag/exponent encodings. In softfpu=2 the architectural state lives
* in env, whereas MMX uses host registers. Load the latter on reentry
* without allowing their stale contents to overwrite subsequent x87
* writes. Keep this state until MMX/EMMS explicitly selects a mode:
* translation/dispatch may clobber host MMX before the first guest use.
* Softfpu=1 already reloads MMX on every reentry, but must also avoid
* canonicalizing a negative-NaN x87 stack on a subsequent signal.
*/
if (option_softfpu) {
env->mode_fpu = LATX_FPU_MODE_RESTORED;
} else {
env->mode_fpu = !latx_x87_state_is_mmx(env);
}
}
#endif

void cpu_x86_init_user_x87(CPUX86State *env)
{
do_fninit(env);

#ifdef CONFIG_LATX
env->fcsr = 0;
env->mode_fpu = 1;
#endif
}
#endif

uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx)
Expand Down
27 changes: 27 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,33 @@ and then run tests with the bundled `meson/meson.py`, or vice versa.
To run the integration suite, replace `lat-pr-fast` with
`latx-integration`.

The `test-x87-signal-mode` integration test builds five static x86-64 guests
with Clang/LLD only when explicitly run. On a target without that compiler,
build the same source on an x86-64 Linux host:

```sh
mkdir -p x87-guests
for case_id in 0 1 2 3 4; do
gcc -nostdlib -static -no-pie -DCASE=$case_id \
tests/integration/x87-signal-mode.S \
-o x87-guests/x87-signal-mode-$case_id
done
```

Copy `x87-guests` to the LoongArch target, then run:

```sh
LATX_X87_SIGNAL_GUEST_DIR=/absolute/path/to/x87-guests \
meson test -C build64-tests --suite latx-integration \
test-x87-signal-mode --print-errorlogs
```

The cases cover full x87 stacks of negative NaNs and infinities, a second
signal after an x87 write, MMX restoration, and x87 handler initialization
and rounding restoration. Each runs in hard-float and softfpu=1/2, with
both TB and TU translation. Compile the fixtures from the same checkout
being tested; a supplied directory with a missing executable is a failure.

Before submitting a new test target, verify both of these:

1. A normal product build without `--enable-tests` does not build the test.
Expand Down
9 changes: 9 additions & 0 deletions tests/integration/registrations/process/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
if 'x86_64-linux-user' in target_dirs
latx_integration_tests += [{
'name': 'test-x87-signal-mode',
'runner': find_program('../../test-x87-signal-mode.sh'),
'args': [
emulators['latx-x86_64'],
files('../../x87-signal-mode.S'),
],
'timeout': 120,
}]
latx_integration_tests += [{
'name': 'test-proc-readdir',
'runner': find_program('../../test-proc-readdir.sh'),
Expand Down
47 changes: 47 additions & 0 deletions tests/integration/test-x87-signal-mode.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
set -eu

emulator=$(readlink -f "$1")
source_file=$(readlink -f "$2")
workdir=$(mktemp -d)
trap 'rm -rf "$workdir"' EXIT HUP INT TERM

guest_dir=${LATX_X87_SIGNAL_GUEST_DIR:-$workdir}
if [ -n "${LATX_X87_SIGNAL_GUEST_DIR:-}" ]; then
: # Allow guests built from this source on an x86 build host.
elif 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

for case_id in 0 1 2 3 4; do
guest="$guest_dir/x87-signal-mode-$case_id"
if [ -z "${LATX_X87_SIGNAL_GUEST_DIR:-}" ]; then
"$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \
-Wl,--build-id=none -DCASE=$case_id "$source_file" -o "$guest"
fi
if [ ! -x "$guest" ]; then
echo "FAIL: guest executable missing: $guest" >&2
exit 1
fi
for softfpu in 0 1 2; do
for tu in 0 1; do
if LATX_AOT=0 LATX_MT=0 LATX_TU=$tu LATX_SOFTFPU=$softfpu \
timeout -s KILL 10 "$emulator" "$guest" \
> "$workdir/result"; then
echo "PASS: x87 signal case=$case_id softfpu=$softfpu tu=$tu"
else
status=$?
printf 'FAIL: case=%s softfpu=%s tu=%s exit=%s\n' \
"$case_id" "$softfpu" "$tu" "$status" >&2
od -An -tx1 "$workdir/result" >&2
exit "$status"
fi
done
done
done
Loading