@@ -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
873983void LiftoffAssembler::AtomicFence () { mfence (); }
0 commit comments