Skip to content

Commit 93b2105

Browse files
gahaasCommit Bot
authored andcommitted
[wasm][ia32][liftoff] Implement AtomicCompareExchange
As there are not enough registers on ia32 to execute the platform- independent code, the CL also adds ia32-specific code to liftoff-compiler.cc. For this we first retrieve the memory index from the stack, do a bounds check, and calculate the final address. Only afterwards we pop all other values from the stack and pass them to the platform-dependent code. [email protected] Bug: v8:10108 Change-Id: I741266a9523c8b5c46acc0b29817fd143a75752e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2316305 Reviewed-by: Clemens Backes <[email protected]> Commit-Queue: Andreas Haas <[email protected]> Cr-Commit-Position: refs/heads/master@{#69047}
1 parent 5e39557 commit 93b2105

File tree

2 files changed

+143
-4
lines changed

2 files changed

+143
-4
lines changed

src/wasm/baseline/ia32/liftoff-assembler-ia32.h

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,117 @@ void LiftoffAssembler::AtomicCompareExchange(
867867
Register dst_addr, Register offset_reg, uint32_t offset_imm,
868868
LiftoffRegister expected, LiftoffRegister new_value, LiftoffRegister result,
869869
StoreType type) {
870-
bailout(kAtomics, "AtomicCompareExchange");
870+
// We expect that the offset has already been added to {dst_addr}, and no
871+
// {offset_reg} is provided. This is to save registers.
872+
DCHECK_EQ(offset_reg, no_reg);
873+
874+
DCHECK_EQ(result, expected);
875+
876+
if (type.value() != StoreType::kI64Store) {
877+
bool is_64_bit_op = type.value_type() == kWasmI64;
878+
879+
Register value_reg = is_64_bit_op ? new_value.low_gp() : new_value.gp();
880+
Register expected_reg = is_64_bit_op ? expected.low_gp() : expected.gp();
881+
Register result_reg = expected_reg;
882+
883+
bool is_byte_store = type.size() == 1;
884+
LiftoffRegList pinned =
885+
LiftoffRegList::ForRegs(dst_addr, value_reg, expected_reg);
886+
887+
// Ensure that {value_reg} is a valid register.
888+
if (is_byte_store && !liftoff::kByteRegs.has(value_reg)) {
889+
Register safe_value_reg =
890+
pinned.set(GetUnusedRegister(liftoff::kByteRegs, pinned)).gp();
891+
mov(safe_value_reg, value_reg);
892+
value_reg = safe_value_reg;
893+
pinned.clear(LiftoffRegister(value_reg));
894+
}
895+
896+
// The cmpxchg instruction uses eax to store the old value of the
897+
// compare-exchange primitive. Therefore we have to spill the register and
898+
// move any use to another register.
899+
ClearRegister(eax, {&dst_addr, &value_reg}, pinned);
900+
if (expected_reg != eax) {
901+
mov(eax, expected_reg);
902+
}
903+
904+
Operand dst_op = Operand(dst_addr, offset_imm);
905+
906+
lock();
907+
switch (type.value()) {
908+
case StoreType::kI32Store8:
909+
case StoreType::kI64Store8: {
910+
cmpxchg_b(dst_op, value_reg);
911+
movzx_b(result_reg, eax);
912+
break;
913+
}
914+
case StoreType::kI32Store16:
915+
case StoreType::kI64Store16: {
916+
cmpxchg_w(dst_op, value_reg);
917+
movzx_w(result_reg, eax);
918+
break;
919+
}
920+
case StoreType::kI32Store:
921+
case StoreType::kI64Store32: {
922+
cmpxchg(dst_op, value_reg);
923+
if (result_reg != eax) {
924+
mov(result_reg, eax);
925+
}
926+
break;
927+
}
928+
default:
929+
UNREACHABLE();
930+
}
931+
if (is_64_bit_op) {
932+
xor_(result.high_gp(), result.high_gp());
933+
}
934+
return;
935+
}
936+
937+
// The following code handles kExprI64AtomicCompareExchange.
938+
939+
// We need {ebx} here, which is the root register. The root register it
940+
// needs special treatment. As we use {ebx} directly in the code below, we
941+
// have to make sure here that the root register is actually {ebx}.
942+
static_assert(kRootRegister == ebx,
943+
"The following code assumes that kRootRegister == ebx");
944+
push(kRootRegister);
945+
946+
// The compare-exchange instruction uses registers as follows:
947+
// old-value = EDX:EAX; new-value = ECX:EBX.
948+
Register expected_hi = edx;
949+
Register expected_lo = eax;
950+
Register new_hi = ecx;
951+
Register new_lo = ebx;
952+
// The address needs a separate registers that does not alias with the
953+
// ones above.
954+
Register address = esi;
955+
956+
// Spill all these registers if they are still holding other values.
957+
liftoff::SpillRegisters(this, expected_hi, expected_lo, new_hi, address);
958+
959+
// We have to set new_lo specially, because it's the root register. We do it
960+
// before setting all other registers so that the original value does not get
961+
// overwritten.
962+
mov(new_lo, new_value.low_gp());
963+
964+
// Move all other values into the right register.
965+
ParallelRegisterMove(
966+
{{LiftoffRegister(address), LiftoffRegister(dst_addr), kWasmI32},
967+
{LiftoffRegister::ForPair(expected_lo, expected_hi), expected, kWasmI64},
968+
{LiftoffRegister(new_hi), new_value.high(), kWasmI32}});
969+
970+
Operand dst_op = Operand(address, offset_imm);
971+
972+
lock();
973+
cmpxchg8b(dst_op);
974+
975+
// Restore the root register, and we are done.
976+
pop(kRootRegister);
977+
978+
// Move the result into the correct registers.
979+
ParallelRegisterMove(
980+
{{result, LiftoffRegister::ForPair(expected_lo, expected_hi), kWasmI64}});
871981
}
872982

873983
void LiftoffAssembler::AtomicFence() { mfence(); }

src/wasm/baseline/liftoff-compiler.cc

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3082,9 +3082,38 @@ class LiftoffCompiler {
30823082
void AtomicCompareExchange(FullDecoder* decoder, StoreType type,
30833083
const MemoryAccessImmediate<validate>& imm) {
30843084
#ifdef V8_TARGET_ARCH_IA32
3085-
// With the current implementation we do not have enough registers on ia32
3086-
// to even get to the platform-specific code. Therefore we bailout early.
3087-
unsupported(decoder, kAtomics, "AtomicCompareExchange");
3085+
// On ia32 we don't have enough registers to first pop all the values off
3086+
// the stack and then start with the code generation. Instead we do the
3087+
// complete address calculation first, so that the address only needs a
3088+
// single register. Afterwards we load all remaining values into the
3089+
// other registers.
3090+
LiftoffRegList pinned;
3091+
Register index_reg = pinned.set(__ PeekToRegister(2, pinned)).gp();
3092+
if (BoundsCheckMem(decoder, type.size(), imm.offset, index_reg, pinned,
3093+
kDoForceCheck)) {
3094+
return;
3095+
}
3096+
AlignmentCheckMem(decoder, type.size(), imm.offset, index_reg, pinned);
3097+
3098+
uint32_t offset = imm.offset;
3099+
index_reg = AddMemoryMasking(index_reg, &offset, &pinned);
3100+
Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3101+
LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
3102+
__ emit_i32_add(addr, addr, index_reg);
3103+
pinned.clear(LiftoffRegister(index_reg));
3104+
LiftoffRegister new_value = pinned.set(__ PopToRegister(pinned));
3105+
LiftoffRegister expected = pinned.set(__ PopToRegister(pinned));
3106+
3107+
// Pop the index from the stack.
3108+
__ cache_state()->stack_state.pop_back(1);
3109+
3110+
LiftoffRegister result = expected;
3111+
3112+
// We already added the index to addr, so we can just pass no_reg to the
3113+
// assembler now.
3114+
__ AtomicCompareExchange(addr, no_reg, offset, expected, new_value, result,
3115+
type);
3116+
__ PushRegister(type.value_type(), result);
30883117
return;
30893118
#else
30903119
ValueType result_type = type.value_type();

0 commit comments

Comments
 (0)