Commit 01f7ff54 authored by Ivica Bogosavljevic's avatar Ivica Bogosavljevic Committed by Commit Bot

MIPS32: Implement AtomicPair operators through runtime

This CL implements AtomicPair operators: Load, Store,
Add, Sub, Or, Xor, And, Exchange and CompareExchange using
runtime on MIPS32R2 and older. MIPS32R6 includes instructions
for 64-bit atomic access so they are implemented using those.

Change-Id: I1309c1ea4771480516ec5a92f7592533bdcb205c
Reviewed-on: https://chromium-review.googlesource.com/c/1326466Reviewed-by: 's avatarSreten Kovacevic <skovacevic@wavecomp.com>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Ivica Bogosavljevic <ibogosavljevic@wavecomp.com>
Cr-Commit-Position: refs/heads/master@{#57496}
parent bfb57282
...@@ -235,29 +235,29 @@ static void VisitPairAtomicBinop(InstructionSelector* selector, Node* node, ...@@ -235,29 +235,29 @@ static void VisitPairAtomicBinop(InstructionSelector* selector, Node* node,
Node* value = node->InputAt(2); Node* value = node->InputAt(2);
Node* value_high = node->InputAt(3); Node* value_high = node->InputAt(3);
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
g.UseFixed(value, a1),
selector->Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg, g.UseFixed(value_high, a2)};
g.UseRegister(index), g.UseRegister(base));
InstructionOperand inputs[] = {g.UseRegister(value),
g.UseRegister(value_high), addr_reg};
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister(),
g.TempRegister(), g.TempRegister()};
Node* projection0 = NodeProperties::FindProjection(node, 0); Node* projection0 = NodeProperties::FindProjection(node, 0);
Node* projection1 = NodeProperties::FindProjection(node, 1); Node* projection1 = NodeProperties::FindProjection(node, 1);
if (projection1) { if (projection1) {
InstructionOperand outputs[] = {g.DefineAsRegister(projection0), InstructionOperand outputs[] = {g.DefineAsFixed(projection0, v0),
g.DefineAsRegister(projection1)}; g.DefineAsFixed(projection1, v1)};
InstructionOperand temps[] = {g.TempRegister(a0), g.TempRegister(),
g.TempRegister()};
selector->Emit(opcode | AddressingModeField::encode(kMode_None), selector->Emit(opcode | AddressingModeField::encode(kMode_None),
arraysize(outputs), outputs, arraysize(inputs), inputs, arraysize(outputs), outputs, arraysize(inputs), inputs,
arraysize(temps), temps); arraysize(temps), temps);
} else if (projection0) { } else if (projection0) {
InstructionOperand outputs[] = {g.DefineAsRegister(projection0)}; InstructionOperand outputs[] = {g.DefineAsFixed(projection0, v0)};
InstructionOperand temps[] = {g.TempRegister(a0), g.TempRegister(v1),
g.TempRegister()};
selector->Emit(opcode | AddressingModeField::encode(kMode_None), selector->Emit(opcode | AddressingModeField::encode(kMode_None),
arraysize(outputs), outputs, arraysize(inputs), inputs, arraysize(outputs), outputs, arraysize(inputs), inputs,
arraysize(temps), temps); arraysize(temps), temps);
} else { } else {
InstructionOperand temps[] = {g.TempRegister(a0), g.TempRegister(v0),
g.TempRegister(v1)};
selector->Emit(opcode | AddressingModeField::encode(kMode_None), 0, nullptr, selector->Emit(opcode | AddressingModeField::encode(kMode_None), 0, nullptr,
arraysize(inputs), inputs, arraysize(temps), temps); arraysize(inputs), inputs, arraysize(temps), temps);
} }
...@@ -688,24 +688,24 @@ void InstructionSelector::VisitWord32AtomicPairLoad(Node* node) { ...@@ -688,24 +688,24 @@ void InstructionSelector::VisitWord32AtomicPairLoad(Node* node) {
Node* projection0 = NodeProperties::FindProjection(node, 0); Node* projection0 = NodeProperties::FindProjection(node, 0);
Node* projection1 = NodeProperties::FindProjection(node, 1); Node* projection1 = NodeProperties::FindProjection(node, 1);
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index)};
Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base));
InstructionOperand inputs[] = {addr_reg};
InstructionOperand temps[] = {g.TempRegister()};
if (projection1) { if (projection1) {
InstructionOperand outputs[] = {g.DefineAsRegister(projection0), InstructionOperand outputs[] = {g.DefineAsFixed(projection0, v0),
g.DefineAsRegister(projection1)}; g.DefineAsFixed(projection1, v1)};
InstructionOperand temps[] = {g.TempRegister(a0)};
Emit(opcode | AddressingModeField::encode(kMode_MRI), arraysize(outputs), Emit(opcode | AddressingModeField::encode(kMode_MRI), arraysize(outputs),
outputs, arraysize(inputs), inputs, 1, temps); outputs, arraysize(inputs), inputs, arraysize(temps), temps);
} else if (projection0) { } else if (projection0) {
InstructionOperand outputs[] = {g.DefineAsRegister(projection0)}; InstructionOperand outputs[] = {g.DefineAsFixed(projection0, v0)};
InstructionOperand temps[] = {g.TempRegister(a0), g.TempRegister(v1)};
Emit(opcode | AddressingModeField::encode(kMode_MRI), arraysize(outputs), Emit(opcode | AddressingModeField::encode(kMode_MRI), arraysize(outputs),
outputs, arraysize(inputs), inputs, 1, temps); outputs, arraysize(inputs), inputs, arraysize(temps), temps);
} else { } else {
InstructionOperand temps[] = {g.TempRegister(a0), g.TempRegister(v0),
g.TempRegister(v1)};
Emit(opcode | AddressingModeField::encode(kMode_MRI), 0, nullptr, Emit(opcode | AddressingModeField::encode(kMode_MRI), 0, nullptr,
arraysize(inputs), inputs, 1, temps); arraysize(inputs), inputs, arraysize(temps), temps);
} }
} }
...@@ -716,13 +716,11 @@ void InstructionSelector::VisitWord32AtomicPairStore(Node* node) { ...@@ -716,13 +716,11 @@ void InstructionSelector::VisitWord32AtomicPairStore(Node* node) {
Node* value_low = node->InputAt(2); Node* value_low = node->InputAt(2);
Node* value_high = node->InputAt(3); Node* value_high = node->InputAt(3);
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand inputs[] = {g.UseRegister(base), g.UseRegister(index),
Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg, g.UseFixed(value_low, a1),
g.UseRegister(index), g.UseRegister(base)); g.UseFixed(value_high, a2)};
InstructionOperand temps[] = {g.TempRegister(a0), g.TempRegister(),
InstructionOperand inputs[] = {addr_reg, g.UseRegister(value_low), g.TempRegister()};
g.UseRegister(value_high)};
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
Emit(kMipsWord32AtomicPairStore | AddressingModeField::encode(kMode_MRI), 0, Emit(kMipsWord32AtomicPairStore | AddressingModeField::encode(kMode_MRI), 0,
nullptr, arraysize(inputs), inputs, arraysize(temps), temps); nullptr, arraysize(inputs), inputs, arraysize(temps), temps);
} }
......
...@@ -991,6 +991,95 @@ ExternalReference ExternalReference::fixed_typed_array_base_data_offset() { ...@@ -991,6 +991,95 @@ ExternalReference ExternalReference::fixed_typed_array_base_data_offset() {
FixedTypedArrayBase::kDataOffset - kHeapObjectTag)); FixedTypedArrayBase::kDataOffset - kHeapObjectTag));
} }
static int64_t atomic_pair_load(intptr_t address) {
return std::atomic_load(reinterpret_cast<std::atomic<int64_t>*>(address));
}
ExternalReference ExternalReference::atomic_pair_load_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_load)));
}
static void atomic_pair_store(intptr_t address, int value_low, int value_high) {
int64_t value =
static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
std::atomic_store(reinterpret_cast<std::atomic<int64_t>*>(address), value);
}
ExternalReference ExternalReference::atomic_pair_store_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_store)));
}
static int64_t atomic_pair_add(intptr_t address, int value_low,
int value_high) {
int64_t value =
static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
return std::atomic_fetch_add(reinterpret_cast<std::atomic<int64_t>*>(address),
value);
}
ExternalReference ExternalReference::atomic_pair_add_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_add)));
}
static int64_t atomic_pair_sub(intptr_t address, int value_low,
int value_high) {
int64_t value =
static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
return std::atomic_fetch_sub(reinterpret_cast<std::atomic<int64_t>*>(address),
value);
}
ExternalReference ExternalReference::atomic_pair_sub_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_sub)));
}
static int64_t atomic_pair_and(intptr_t address, int value_low,
int value_high) {
int64_t value =
static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
return std::atomic_fetch_and(reinterpret_cast<std::atomic<int64_t>*>(address),
value);
}
ExternalReference ExternalReference::atomic_pair_and_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_and)));
}
static int64_t atomic_pair_or(intptr_t address, int value_low, int value_high) {
int64_t value =
static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
return std::atomic_fetch_or(reinterpret_cast<std::atomic<int64_t>*>(address),
value);
}
ExternalReference ExternalReference::atomic_pair_or_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_or)));
}
static int64_t atomic_pair_xor(intptr_t address, int value_low,
int value_high) {
int64_t value =
static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
return std::atomic_fetch_xor(reinterpret_cast<std::atomic<int64_t>*>(address),
value);
}
ExternalReference ExternalReference::atomic_pair_xor_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_xor)));
}
static int64_t atomic_pair_exchange(intptr_t address, int value_low,
int value_high) {
int64_t value =
static_cast<int64_t>(value_high) << 32 | (value_low & 0xFFFFFFFF);
return std::atomic_exchange(reinterpret_cast<std::atomic<int64_t>*>(address),
value);
}
ExternalReference ExternalReference::atomic_pair_exchange_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(atomic_pair_exchange)));
}
static uint64_t atomic_pair_compare_exchange(intptr_t address, static uint64_t atomic_pair_compare_exchange(intptr_t address,
int old_value_low, int old_value_low,
int old_value_high, int old_value_high,
......
...@@ -177,6 +177,14 @@ class StatsCounter; ...@@ -177,6 +177,14 @@ class StatsCounter;
V(wasm_word32_ror, "wasm::word32_ror") \ V(wasm_word32_ror, "wasm::word32_ror") \
V(wasm_word64_ctz, "wasm::word64_ctz") \ V(wasm_word64_ctz, "wasm::word64_ctz") \
V(wasm_word64_popcnt, "wasm::word64_popcnt") \ V(wasm_word64_popcnt, "wasm::word64_popcnt") \
V(atomic_pair_load_function, "atomic_pair_load_function") \
V(atomic_pair_store_function, "atomic_pair_store_function") \
V(atomic_pair_add_function, "atomic_pair_add_function") \
V(atomic_pair_sub_function, "atomic_pair_sub_function") \
V(atomic_pair_and_function, "atomic_pair_and_function") \
V(atomic_pair_or_function, "atomic_pair_or_function") \
V(atomic_pair_xor_function, "atomic_pair_xor_function") \
V(atomic_pair_exchange_function, "atomic_pair_exchange_function") \
V(atomic_pair_compare_exchange_function, \ V(atomic_pair_compare_exchange_function, \
"atomic_pair_compare_exchange_function") \ "atomic_pair_compare_exchange_function") \
EXTERNAL_REFERENCE_LIST_INTL(V) EXTERNAL_REFERENCE_LIST_INTL(V)
......
...@@ -2358,14 +2358,16 @@ void Assembler::sc(Register rd, const MemOperand& rs) { ...@@ -2358,14 +2358,16 @@ void Assembler::sc(Register rd, const MemOperand& rs) {
} }
} }
void Assembler::llwp(Register rd, Register rt, Register base) { void Assembler::llx(Register rd, const MemOperand& rs) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL3, base, rt, rd, 1, LL_R6); DCHECK(is_int9(rs.offset_));
GenInstrImmediate(SPECIAL3, rs.rm(), rd, rs.offset_, 1, LL_R6);
} }
void Assembler::scwp(Register rd, Register rt, Register base) { void Assembler::scx(Register rd, const MemOperand& rs) {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL3, base, rt, rd, 1, SC_R6); DCHECK(is_int9(rs.offset_));
GenInstrImmediate(SPECIAL3, rs.rm(), rd, rs.offset_, 1, SC_R6);
} }
void Assembler::lui(Register rd, int32_t j) { void Assembler::lui(Register rd, int32_t j) {
......
...@@ -890,8 +890,8 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { ...@@ -890,8 +890,8 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
void ll(Register rd, const MemOperand& rs); void ll(Register rd, const MemOperand& rs);
void sc(Register rd, const MemOperand& rs); void sc(Register rd, const MemOperand& rs);
void llwp(Register rd, Register rt, Register base); void llx(Register rd, const MemOperand& rs);
void scwp(Register rd, Register rt, Register base); void scx(Register rd, const MemOperand& rs);
// ---------PC-Relative-instructions----------- // ---------PC-Relative-instructions-----------
......
...@@ -1686,7 +1686,11 @@ void Decoder::DecodeTypeImmediateSPECIAL3(Instruction* instr) { ...@@ -1686,7 +1686,11 @@ void Decoder::DecodeTypeImmediateSPECIAL3(Instruction* instr) {
switch (instr->FunctionFieldRaw()) { switch (instr->FunctionFieldRaw()) {
case LL_R6: { case LL_R6: {
if (IsMipsArchVariant(kMips32r6)) { if (IsMipsArchVariant(kMips32r6)) {
Format(instr, "ll 'rt, 'imm9s('rs)"); if (instr->Bit(6)) {
Format(instr, "llx 'rt, 'imm9s('rs)");
} else {
Format(instr, "ll 'rt, 'imm9s('rs)");
}
} else { } else {
Unknown(instr); Unknown(instr);
} }
...@@ -1694,7 +1698,11 @@ void Decoder::DecodeTypeImmediateSPECIAL3(Instruction* instr) { ...@@ -1694,7 +1698,11 @@ void Decoder::DecodeTypeImmediateSPECIAL3(Instruction* instr) {
} }
case SC_R6: { case SC_R6: {
if (IsMipsArchVariant(kMips32r6)) { if (IsMipsArchVariant(kMips32r6)) {
Format(instr, "sc 'rt, 'imm9s('rs)"); if (instr->Bit(6)) {
Format(instr, "scx 'rt, 'imm9s('rs)");
} else {
Format(instr, "sc 'rt, 'imm9s('rs)");
}
} else { } else {
Unknown(instr); Unknown(instr);
} }
...@@ -1962,14 +1970,14 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) { ...@@ -1962,14 +1970,14 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
if (IsMipsArchVariant(kMips32r6)) { if (IsMipsArchVariant(kMips32r6)) {
Unknown(instr); Unknown(instr);
} else { } else {
Format(instr, "ll 'rt, 'imm16s('rs)"); Format(instr, "ll 'rt, 'imm16s('rs)");
} }
break; break;
case SC: case SC:
if (IsMipsArchVariant(kMips32r6)) { if (IsMipsArchVariant(kMips32r6)) {
Unknown(instr); Unknown(instr);
} else { } else {
Format(instr, "sc 'rt, 'imm16s('rs)"); Format(instr, "sc 'rt, 'imm16s('rs)");
} }
break; break;
case LWC1: case LWC1:
......
...@@ -4240,21 +4240,6 @@ void Simulator::DecodeTypeRegisterSPECIAL3() { ...@@ -4240,21 +4240,6 @@ void Simulator::DecodeTypeRegisterSPECIAL3() {
SetResult(rd_reg(), alu_out); SetResult(rd_reg(), alu_out);
break; break;
} }
case LL_R6: {
// LLWP/SCWP sequence cannot be simulated properly
DCHECK(IsMipsArchVariant(kMips32r6));
set_register(rd_reg(), ReadW(rs() + 4, instr_.instr()));
set_register(rt(), ReadW(rs(), instr_.instr()));
break;
}
case SC_R6: {
// LLWP/SCWP sequence cannot be simulated properly
DCHECK(IsMipsArchVariant(kMips32r6));
WriteW(rs() + 4, rd_reg(), instr_.instr());
WriteW(rs(), rt(), instr_.instr());
set_register(rt(), 1);
break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
} }
...@@ -6833,8 +6818,12 @@ void Simulator::DecodeTypeImmediate() { ...@@ -6833,8 +6818,12 @@ void Simulator::DecodeTypeImmediate() {
DCHECK(IsMipsArchVariant(kMips32r6)); DCHECK(IsMipsArchVariant(kMips32r6));
int32_t base = get_register(instr_.BaseValue()); int32_t base = get_register(instr_.BaseValue());
int32_t offset9 = instr_.Imm9Value(); int32_t offset9 = instr_.Imm9Value();
int32_t bit6 = instr_.Bit(6);
WriteW(base + offset9, rt, instr_.instr()); WriteW(base + offset9, rt, instr_.instr());
set_register(rt_reg, 1); // Only SC (and not SCX) instruction modifies rt_reg
if (bit6 == 0) {
set_register(rt_reg, 1);
}
break; break;
} }
default: default:
......
...@@ -356,7 +356,7 @@ ...@@ -356,7 +356,7 @@
}], # '(arch == mipsel or arch == mips64el or arch == mips or arch == mips64) and not simd_mips' }], # '(arch == mipsel or arch == mips64el or arch == mips or arch == mips64) and not simd_mips'
############################################################################## ##############################################################################
['arch == mipsel or arch == mips64el or arch == mips or arch == mips64', { ['arch == mips or arch == mips64', {
# TODO(mips-team): Implement I64Atomic operations on MIPS # TODO(mips-team): Implement I64Atomic operations on MIPS
'test-run-wasm-atomics64/*': [SKIP], 'test-run-wasm-atomics64/*': [SKIP],
}], # 'arch == mipsel or arch == mips64el or arch == mips or arch == mips64' }], # 'arch == mipsel or arch == mips64el or arch == mips or arch == mips64'
......
...@@ -1123,11 +1123,11 @@ TEST(madd_msub_maddf_msubf) { ...@@ -1123,11 +1123,11 @@ TEST(madd_msub_maddf_msubf) {
TEST(atomic_load_store) { TEST(atomic_load_store) {
SET_UP(); SET_UP();
if (IsMipsArchVariant(kMips32r6)) { if (IsMipsArchVariant(kMips32r6)) {
COMPARE(ll(v0, MemOperand(v1, -1)), "7c62ffb6 ll v0, -1(v1)"); COMPARE(ll(v0, MemOperand(v1, -1)), "7c62ffb6 ll v0, -1(v1)");
COMPARE(sc(v0, MemOperand(v1, 1)), "7c6200a6 sc v0, 1(v1)"); COMPARE(sc(v0, MemOperand(v1, 1)), "7c6200a6 sc v0, 1(v1)");
} else { } else {
COMPARE(ll(v0, MemOperand(v1, -1)), "c062ffff ll v0, -1(v1)"); COMPARE(ll(v0, MemOperand(v1, -1)), "c062ffff ll v0, -1(v1)");
COMPARE(sc(v0, MemOperand(v1, 1)), "e0620001 sc v0, 1(v1)"); COMPARE(sc(v0, MemOperand(v1, 1)), "e0620001 sc v0, 1(v1)");
} }
VERIFY_RUN(); VERIFY_RUN();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment