Skip to content

Commit 84a8f67

Browse files
authored
[RISC-V] Atomics (#92102)
* [RISC-V] Add atomics to JIT * [RISC-V] Intrinsify Interlocked.ExchangeAdd|Exchange|Or|And with RV64 atomics * [RISC-V] Intrinsify CompareExchange with load-reserved/store-conditional loop * [ARM64] Fix assertion as it was always passing due to precedence misevaluation
1 parent 37c10f8 commit 84a8f67

File tree

7 files changed

+398
-139
lines changed

7 files changed

+398
-139
lines changed

src/coreclr/jit/codegenriscv64.cpp

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,14 +2609,55 @@ void CodeGen::genJumpTable(GenTree* treeNode)
26092609
}
26102610

26112611
//------------------------------------------------------------------------
2612-
// genLockedInstructions: Generate code for a GT_XADD or GT_XCHG node.
2612+
// genLockedInstructions: Generate code for a GT_XADD, GT_XAND, GT_XORR or GT_XCHG node.
26132613
//
26142614
// Arguments:
2615-
// treeNode - the GT_XADD/XCHG node
2615+
// treeNode - the GT_XADD/XAND/XORR/XCHG node
26162616
//
26172617
void CodeGen::genLockedInstructions(GenTreeOp* treeNode)
26182618
{
2619-
NYI_RISCV64("genLockedInstructions-----unimplemented/unused on RISCV64 yet----");
2619+
GenTree* data = treeNode->AsOp()->gtOp2;
2620+
GenTree* addr = treeNode->AsOp()->gtOp1;
2621+
regNumber dataReg = data->GetRegNum();
2622+
regNumber addrReg = addr->GetRegNum();
2623+
regNumber targetReg = treeNode->GetRegNum();
2624+
if (targetReg == REG_NA)
2625+
{
2626+
targetReg = REG_R0;
2627+
}
2628+
2629+
genConsumeAddress(addr);
2630+
genConsumeRegs(data);
2631+
2632+
emitAttr dataSize = emitActualTypeSize(data);
2633+
bool is4 = (dataSize == EA_4BYTE);
2634+
2635+
assert(!data->isContainedIntOrIImmed());
2636+
2637+
instruction ins = INS_none;
2638+
switch (treeNode->gtOper)
2639+
{
2640+
case GT_XORR:
2641+
ins = is4 ? INS_amoor_w : INS_amoor_d;
2642+
break;
2643+
case GT_XAND:
2644+
ins = is4 ? INS_amoand_w : INS_amoand_d;
2645+
break;
2646+
case GT_XCHG:
2647+
ins = is4 ? INS_amoswap_w : INS_amoswap_d;
2648+
break;
2649+
case GT_XADD:
2650+
ins = is4 ? INS_amoadd_w : INS_amoadd_d;
2651+
break;
2652+
default:
2653+
noway_assert(!"Unexpected treeNode->gtOper");
2654+
}
2655+
GetEmitter()->emitIns_R_R_R(ins, dataSize, targetReg, addrReg, dataReg);
2656+
2657+
if (targetReg != REG_R0)
2658+
{
2659+
genProduceReg(treeNode);
2660+
}
26202661
}
26212662

26222663
//------------------------------------------------------------------------
@@ -2627,7 +2668,62 @@ void CodeGen::genLockedInstructions(GenTreeOp* treeNode)
26272668
//
26282669
void CodeGen::genCodeForCmpXchg(GenTreeCmpXchg* treeNode)
26292670
{
2630-
NYI_RISCV64("genCodeForCmpXchg-----unimplemented/unused on RISCV64 yet----");
2671+
assert(treeNode->OperIs(GT_CMPXCHG));
2672+
2673+
GenTree* locOp = treeNode->gtOpLocation;
2674+
GenTree* valOp = treeNode->gtOpValue;
2675+
GenTree* comparandOp = treeNode->gtOpComparand;
2676+
2677+
regNumber target = treeNode->GetRegNum();
2678+
regNumber loc = locOp->GetRegNum();
2679+
regNumber val = valOp->GetRegNum();
2680+
regNumber comparand = comparandOp->GetRegNum();
2681+
regNumber storeErr = treeNode->ExtractTempReg(RBM_ALLINT);
2682+
2683+
// Register allocator should have extended the lifetimes of all input and internal registers
2684+
// They should all be different
2685+
noway_assert(target != loc);
2686+
noway_assert(target != val);
2687+
noway_assert(target != comparand);
2688+
noway_assert(target != storeErr);
2689+
noway_assert(loc != val);
2690+
noway_assert(loc != comparand);
2691+
noway_assert(loc != storeErr);
2692+
noway_assert(val != comparand);
2693+
noway_assert(val != storeErr);
2694+
noway_assert(comparand != storeErr);
2695+
noway_assert(target != REG_NA);
2696+
noway_assert(storeErr != REG_NA);
2697+
2698+
assert(locOp->isUsedFromReg());
2699+
assert(valOp->isUsedFromReg());
2700+
assert(!comparandOp->isUsedFromMemory());
2701+
2702+
genConsumeAddress(locOp);
2703+
genConsumeRegs(valOp);
2704+
genConsumeRegs(comparandOp);
2705+
2706+
// NOTE: `genConsumeAddress` marks consumed register as not a GC pointer, assuming the input
2707+
// registers die at the first generated instruction. However, here the input registers are reused,
2708+
// so mark the location register as a GC pointer until code generation for this node is finished.
2709+
gcInfo.gcMarkRegPtrVal(loc, locOp->TypeGet());
2710+
2711+
BasicBlock* retry = genCreateTempLabel();
2712+
BasicBlock* fail = genCreateTempLabel();
2713+
2714+
emitter* e = GetEmitter();
2715+
emitAttr size = emitActualTypeSize(valOp);
2716+
bool is4 = (size == EA_4BYTE);
2717+
2718+
genDefineTempLabel(retry);
2719+
e->emitIns_R_R_R(is4 ? INS_lr_w : INS_lr_d, size, target, loc, REG_R0); // load original value
2720+
e->emitIns_J_cond_la(INS_bne, fail, target, comparand); // fail if doesn’t match
2721+
e->emitIns_R_R_R(is4 ? INS_sc_w : INS_sc_d, size, storeErr, loc, val); // try to update
2722+
e->emitIns_J(INS_bnez, retry, storeErr); // retry if update failed
2723+
genDefineTempLabel(fail);
2724+
2725+
gcInfo.gcMarkRegSetNpt(locOp->gtGetRegMask());
2726+
genProduceReg(treeNode);
26312727
}
26322728

26332729
static inline bool isImmed(GenTree* treeNode)
@@ -4643,6 +4739,8 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
46434739

46444740
case GT_XCHG:
46454741
case GT_XADD:
4742+
case GT_XORR:
4743+
case GT_XAND:
46464744
genLockedInstructions(treeNode->AsOp());
46474745
break;
46484746

0 commit comments

Comments
 (0)