target/i386: fix cmpxchg with 32-bit register destination

Unlike the memory case, where "the destination operand receives a write
cycle without regard to the result of the comparison", rm must not be
touched altogether if the write fails, including not zero-extending
it on 64-bit processors.  This is not how the movcond currently works,
because it is always followed by a gen_op_mov_reg_v to rm.

To fix it, introduce a new function that is similar to gen_op_mov_reg_v
but writes to a TCG temporary.

Considering that gen_extu(ot, oldv) is not needed in the memory case
either, the two cases for register and memory destinations are different
enough that one might as well fuse the two "if (mod == 3)" into one.
So do that too.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/508
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
[rth: Add a test case ]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Paolo Bonzini 2022-09-11 14:04:36 +02:00 committed by Richard Henderson
parent 98f10f0e26
commit d1bb978ba1
3 changed files with 99 additions and 26 deletions

View file

@ -11,6 +11,7 @@ include $(SRC_PATH)/tests/tcg/i386/Makefile.target
ifeq ($(filter %-linux-user, $(TARGET)),$(TARGET))
X86_64_TESTS += vsyscall
X86_64_TESTS += noexec
X86_64_TESTS += cmpxchg
TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64
else
TESTS=$(MULTIARCH_TESTS)

View file

@ -0,0 +1,42 @@
#include <assert.h>
static int mem;
static unsigned long test_cmpxchgb(unsigned long orig)
{
unsigned long ret;
mem = orig;
asm("cmpxchgb %b[cmp],%[mem]"
: [ mem ] "+m"(mem), [ rax ] "=a"(ret)
: [ cmp ] "r"(0x77), "a"(orig));
return ret;
}
static unsigned long test_cmpxchgw(unsigned long orig)
{
unsigned long ret;
mem = orig;
asm("cmpxchgw %w[cmp],%[mem]"
: [ mem ] "+m"(mem), [ rax ] "=a"(ret)
: [ cmp ] "r"(0x7777), "a"(orig));
return ret;
}
static unsigned long test_cmpxchgl(unsigned long orig)
{
unsigned long ret;
mem = orig;
asm("cmpxchgl %[cmp],%[mem]"
: [ mem ] "+m"(mem), [ rax ] "=a"(ret)
: [ cmp ] "r"(0x77777777u), "a"(orig));
return ret;
}
int main()
{
unsigned long test = 0xdeadbeef12345678ull;
assert(test == test_cmpxchgb(test));
assert(test == test_cmpxchgw(test));
assert(test == test_cmpxchgl(test));
return 0;
}