Commit f1ad3744 authored by bjaideep's avatar bjaideep Committed by Commit bot

PPC: [Atomics] Make Atomics.exchange a builtin using TF

Implemented l[w|h|b]arx and st[w|h|b]cx instructions which are
needed to perform atomic exchange. Also added synchronization
primitives similar to arm to simulate those instructions.

R=joransiu@ca.ibm.com, jyan@ca.ibm.com, binji@chromium.org, aseemgarg@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2754263004
Cr-Commit-Position: refs/heads/master@{#44257}
parent 872accf9
......@@ -268,8 +268,7 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
Node* value_integer = ToInteger(context, value);
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
V8_TARGET_ARCH_PPC
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_integer,
value_integer));
#else
......@@ -318,8 +317,7 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
// This shouldn't happen, we've already validated the type.
Bind(&other);
Unreachable();
#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
// || V8_TARGET_ARCH_PPC
#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
}
TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
......
......@@ -792,6 +792,16 @@ Condition FlagsConditionToCondition(FlagsCondition condition, ArchOpcode op) {
__ sync(); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(load_instr, store_instr) \
do { \
Label exchange; \
__ bind(&exchange); \
__ load_instr(i.OutputRegister(0), \
MemOperand(i.InputRegister(0), i.InputRegister(1))); \
__ store_instr(i.InputRegister(2), \
MemOperand(i.InputRegister(0), i.InputRegister(1))); \
__ bne(&exchange, cr0); \
} while (0)
void CodeGenerator::AssembleDeconstructFrame() {
__ LeaveFrame(StackFrame::MANUAL);
......@@ -1979,10 +1989,22 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
ASSEMBLE_ATOMIC_STORE_INTEGER(stw, stwx);
break;
case kAtomicExchangeInt8:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lbarx, stbcx);
__ extsb(i.OutputRegister(0), i.OutputRegister(0));
break;
case kAtomicExchangeUint8:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lbarx, stbcx);
break;
case kAtomicExchangeInt16:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lharx, sthcx);
__ extsh(i.OutputRegister(0), i.OutputRegister(0));
break;
case kAtomicExchangeUint16:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lharx, sthcx);
break;
case kAtomicExchangeWord32:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lwarx, stwcx);
break;
case kAtomicCompareExchangeInt8:
case kAtomicCompareExchangeUint8:
case kAtomicCompareExchangeInt16:
......
......@@ -2117,7 +2117,39 @@ void InstructionSelector::VisitAtomicStore(Node* node) {
0, nullptr, input_count, inputs);
}
void InstructionSelector::VisitAtomicExchange(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitAtomicExchange(Node* node) {
PPCOperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
Node* value = node->InputAt(2);
ArchOpcode opcode = kArchNop;
MachineType type = AtomicExchangeRepresentationOf(node->op());
if (type == MachineType::Int8()) {
opcode = kAtomicExchangeInt8;
} else if (type == MachineType::Uint8()) {
opcode = kAtomicExchangeUint8;
} else if (type == MachineType::Int16()) {
opcode = kAtomicExchangeInt16;
} else if (type == MachineType::Uint16()) {
opcode = kAtomicExchangeUint16;
} else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
opcode = kAtomicExchangeWord32;
} else {
UNREACHABLE();
return;
}
AddressingMode addressing_mode = kMode_MRR;
InstructionOperand inputs[3];
size_t input_count = 0;
inputs[input_count++] = g.UseUniqueRegister(base);
inputs[input_count++] = g.UseUniqueRegister(index);
inputs[input_count++] = g.UseUniqueRegister(value);
InstructionOperand outputs[1];
outputs[0] = g.UseUniqueRegister(node);
InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
Emit(code, 1, outputs, input_count, inputs);
}
void InstructionSelector::VisitAtomicCompareExchange(Node* node) {
UNIMPLEMENTED();
......
......@@ -641,7 +641,6 @@ void Assembler::d_form(Instr instr, Register rt, Register ra,
emit(instr | rt.code() * B21 | ra.code() * B16 | (kImm16Mask & val));
}
void Assembler::xo_form(Instr instr, Register rt, Register ra, Register rb,
OEBit o, RCBit r) {
emit(instr | rt.code() * B21 | ra.code() * B16 | rb.code() * B11 | o | r);
......@@ -1061,7 +1060,6 @@ void Assembler::lwa(Register dst, const MemOperand& src) {
#endif
}
void Assembler::stb(Register dst, const MemOperand& src) {
DCHECK(!src.ra_.is(r0));
d_form(STB, dst, src.ra(), src.offset(), true);
......
......@@ -598,6 +598,16 @@ class Assembler : public AssemblerBase {
x_form(instr_name, cr.code() * B2, src1.code(), src2.code(), LeaveRC); \
}
#define DECLARE_PPC_X_INSTRUCTIONS_EH_S_FORM(name, instr_name, instr_value) \
inline void name(const Register dst, const MemOperand& src) { \
x_form(instr_name, src.ra(), dst, src.rb(), SetEH); \
}
#define DECLARE_PPC_X_INSTRUCTIONS_EH_L_FORM(name, instr_name, instr_value) \
inline void name(const Register dst, const MemOperand& src) { \
DCHECK(!src.ra_.is(r0)); \
x_form(instr_name, src.ra(), dst, src.rb(), SetEH); \
}
inline void x_form(Instr instr, int f1, int f2, int f3, int rc) {
emit(instr | f1 * B21 | f2 * B16 | f3 * B11 | rc);
}
......@@ -605,6 +615,10 @@ class Assembler : public AssemblerBase {
RCBit rc) {
emit(instr | rs.code() * B21 | ra.code() * B16 | rb.code() * B11 | rc);
}
inline void x_form(Instr instr, Register ra, Register rs, Register rb,
EHBit eh = SetEH) {
emit(instr | rs.code() * B21 | ra.code() * B16 | rb.code() * B11 | eh);
}
inline void x_form(Instr instr, CRegister cr, Register s1, Register s2,
RCBit rc) {
#if V8_TARGET_ARCH_PPC64
......@@ -622,6 +636,8 @@ class Assembler : public AssemblerBase {
PPC_X_OPCODE_D_FORM_LIST(DECLARE_PPC_X_INSTRUCTIONS_D_FORM)
PPC_X_OPCODE_E_FORM_LIST(DECLARE_PPC_X_INSTRUCTIONS_E_FORM)
PPC_X_OPCODE_F_FORM_LIST(DECLARE_PPC_X_INSTRUCTIONS_F_FORM)
PPC_X_OPCODE_EH_S_FORM_LIST(DECLARE_PPC_X_INSTRUCTIONS_EH_S_FORM)
PPC_X_OPCODE_EH_L_FORM_LIST(DECLARE_PPC_X_INSTRUCTIONS_EH_L_FORM)
inline void notx(Register dst, Register src, RCBit rc = LeaveRC) {
nor(dst, src, src, rc);
......@@ -651,6 +667,8 @@ class Assembler : public AssemblerBase {
#undef DECLARE_PPC_X_INSTRUCTIONS_D_FORM
#undef DECLARE_PPC_X_INSTRUCTIONS_E_FORM
#undef DECLARE_PPC_X_INSTRUCTIONS_F_FORM
#undef DECLARE_PPC_X_INSTRUCTIONS_EH_S_FORM
#undef DECLARE_PPC_X_INSTRUCTIONS_EH_L_FORM
#define DECLARE_PPC_XX3_INSTRUCTIONS(name, instr_name, instr_value) \
inline void name(const DoubleRegister rt, const DoubleRegister ra, \
......@@ -978,7 +996,6 @@ class Assembler : public AssemblerBase {
void sth(Register dst, const MemOperand& src);
void stw(Register dst, const MemOperand& src);
void stwu(Register dst, const MemOperand& src);
void neg(Register rt, Register ra, OEBit o = LeaveOE, RCBit c = LeaveRC);
#if V8_TARGET_ARCH_PPC64
......
This diff is collapsed.
......@@ -562,6 +562,10 @@ void Decoder::DecodeExt2(Instruction* instr) {
return;
}
#endif
case SYNC: {
Format(instr, "sync");
return;
}
case MODSW: {
Format(instr, "modsw 'rt, 'ra, 'rb");
return;
......@@ -649,6 +653,21 @@ void Decoder::DecodeExt2(Instruction* instr) {
}
}
switch (EXT2 | (instr->BitField(10, 0))) {
case STBCX: {
Format(instr, "stbcx 'rs, 'ra, 'rb");
return;
}
case STHCX: {
Format(instr, "sthcx 'rs, 'ra, 'rb");
return;
}
case STWCX: {
Format(instr, "stwcx 'rs, 'ra, 'rb");
return;
}
}
// ?? are all of these xo_form?
switch (EXT2 | (instr->BitField(9, 1))) {
case CMP: {
......@@ -859,6 +878,18 @@ void Decoder::DecodeExt2(Instruction* instr) {
Format(instr, "lhax 'rt, 'ra, 'rb");
return;
}
case LBARX: {
Format(instr, "lbarx 'rt, 'ra, 'rb");
return;
}
case LHARX: {
Format(instr, "lharx 'rt, 'ra, 'rb");
return;
}
case LWARX: {
Format(instr, "lwarx 'rt, 'ra, 'rb");
return;
}
#if V8_TARGET_ARCH_PPC64
case LDX: {
Format(instr, "ldx 'rt, 'ra, 'rb");
......
This diff is collapsed.
......@@ -289,19 +289,25 @@ class Simulator {
// Read and write memory.
inline uint8_t ReadBU(intptr_t addr);
inline uint8_t ReadExBU(intptr_t addr);
inline int8_t ReadB(intptr_t addr);
inline void WriteB(intptr_t addr, uint8_t value);
inline int WriteExB(intptr_t addr, uint8_t value);
inline void WriteB(intptr_t addr, int8_t value);
inline uint16_t ReadHU(intptr_t addr, Instruction* instr);
inline uint16_t ReadExHU(intptr_t addr, Instruction* instr);
inline int16_t ReadH(intptr_t addr, Instruction* instr);
// Note: Overloaded on the sign of the value.
inline void WriteH(intptr_t addr, uint16_t value, Instruction* instr);
inline int WriteExH(intptr_t addr, uint16_t value, Instruction* instr);
inline void WriteH(intptr_t addr, int16_t value, Instruction* instr);
inline uint32_t ReadWU(intptr_t addr, Instruction* instr);
inline uint32_t ReadExWU(intptr_t addr, Instruction* instr);
inline int32_t ReadW(intptr_t addr, Instruction* instr);
inline void WriteW(intptr_t addr, uint32_t value, Instruction* instr);
inline int WriteExW(intptr_t addr, uint32_t value, Instruction* instr);
inline void WriteW(intptr_t addr, int32_t value, Instruction* instr);
intptr_t* ReadDW(intptr_t addr);
......@@ -311,7 +317,8 @@ class Simulator {
void SetCR0(intptr_t result, bool setSO = false);
void ExecuteBranchConditional(Instruction* instr, BCType type);
void ExecuteExt1(Instruction* instr);
bool ExecuteExt2_10bit(Instruction* instr);
bool ExecuteExt2_10bit_part1(Instruction* instr);
bool ExecuteExt2_10bit_part2(Instruction* instr);
bool ExecuteExt2_9bit_part1(Instruction* instr);
bool ExecuteExt2_9bit_part2(Instruction* instr);
void ExecuteExt2_5bit(Instruction* instr);
......@@ -398,6 +405,84 @@ class Simulator {
char* desc;
};
StopCountAndDesc watched_stops_[kNumOfWatchedStops];
// Syncronization primitives. See ARM DDI 0406C.b, A2.9.
enum class MonitorAccess {
Open,
Exclusive,
};
enum class TransactionSize {
None = 0,
Byte = 1,
HalfWord = 2,
Word = 4,
};
class LocalMonitor {
public:
LocalMonitor();
// These functions manage the state machine for the local monitor, but do
// not actually perform loads and stores. NotifyStoreExcl only returns
// true if the exclusive store is allowed; the global monitor will still
// have to be checked to see whether the memory should be updated.
void NotifyLoad(int32_t addr);
void NotifyLoadExcl(int32_t addr, TransactionSize size);
void NotifyStore(int32_t addr);
bool NotifyStoreExcl(int32_t addr, TransactionSize size);
private:
void Clear();
MonitorAccess access_state_;
int32_t tagged_addr_;
TransactionSize size_;
};
class GlobalMonitor {
public:
GlobalMonitor();
class Processor {
public:
Processor();
private:
friend class GlobalMonitor;
// These functions manage the state machine for the global monitor, but do
// not actually perform loads and stores.
void Clear_Locked();
void NotifyLoadExcl_Locked(int32_t addr);
void NotifyStore_Locked(int32_t addr, bool is_requesting_processor);
bool NotifyStoreExcl_Locked(int32_t addr, bool is_requesting_processor);
MonitorAccess access_state_;
int32_t tagged_addr_;
Processor* next_;
Processor* prev_;
};
// Exposed so it can be accessed by Simulator::{Read,Write}Ex*.
base::Mutex mutex;
void NotifyLoadExcl_Locked(int32_t addr, Processor* processor);
void NotifyStore_Locked(int32_t addr, Processor* processor);
bool NotifyStoreExcl_Locked(int32_t addr, Processor* processor);
// Called when the simulator is destroyed.
void RemoveProcessor(Processor* processor);
private:
bool IsProcessorInLinkedList_Locked(Processor* processor) const;
void PrependProcessor_Locked(Processor* processor);
Processor* head_;
};
LocalMonitor local_monitor_;
GlobalMonitor::Processor global_monitor_processor_;
static base::LazyInstance<GlobalMonitor>::type global_monitor_;
};
......
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