Skip to content

Commit 4759e86

Browse files
committed
Intrinsify Interlocked.And/Or
1 parent 04afbf7 commit 4759e86

File tree

4 files changed

+67
-11
lines changed

4 files changed

+67
-11
lines changed

src/coreclr/jit/codegenxarch.cpp

+52-5
Original file line numberDiff line numberDiff line change
@@ -2047,12 +2047,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
20472047

20482048
case GT_XCHG:
20492049
case GT_XADD:
2050-
genLockedInstructions(treeNode->AsOp());
2051-
break;
2052-
20532050
case GT_XORR:
20542051
case GT_XAND:
2055-
NYI("Interlocked.Or and Interlocked.And aren't implemented for x86 yet.");
2052+
genLockedInstructions(treeNode->AsOp());
20562053
break;
20572054

20582055
case GT_MEMORYBARRIER:
@@ -4354,7 +4351,7 @@ void CodeGen::genCodeForLockAdd(GenTreeOp* node)
43544351
//
43554352
void CodeGen::genLockedInstructions(GenTreeOp* node)
43564353
{
4357-
assert(node->OperIs(GT_XADD, GT_XCHG));
4354+
assert(node->OperIs(GT_XADD, GT_XCHG, GT_XORR, GT_XAND));
43584355

43594356
GenTree* addr = node->gtGetOp1();
43604357
GenTree* data = node->gtGetOp2();
@@ -4366,6 +4363,56 @@ void CodeGen::genLockedInstructions(GenTreeOp* node)
43664363

43674364
genConsumeOperands(node);
43684365

4366+
if (node->OperIs(GT_XORR, GT_XAND))
4367+
{
4368+
const instruction ins = node->OperIs(GT_XORR) ? INS_or : INS_and;
4369+
4370+
if (node->IsUnusedValue())
4371+
{
4372+
// If value is not used we can emit a short form:
4373+
//
4374+
// lock
4375+
// or/and dword ptr [addrReg], val
4376+
//
4377+
instGen(INS_lock);
4378+
GetEmitter()->emitIns_AR_R(ins, size, data->GetRegNum(), addr->GetRegNum(), 0);
4379+
}
4380+
else
4381+
{
4382+
// When value is used (it's the original value of the memory location)
4383+
// we fallback to cmpxchg-loop idiom.
4384+
4385+
// for cmpxchg we need to keep the original value in RAX
4386+
assert(node->GetRegNum() == REG_RAX);
4387+
4388+
// mov RAX, dword ptr [addrReg]
4389+
//.LOOP:
4390+
// mov tmp, RAX
4391+
// or/and tmp, val
4392+
// lock
4393+
// cmpxchg dword ptr [addrReg], tmp
4394+
// jne .LOOP
4395+
// ret
4396+
4397+
// Extend liveness of addr
4398+
gcInfo.gcMarkRegPtrVal(addr->GetRegNum(), addr->TypeGet());
4399+
4400+
const regNumber tmpReg = node->GetSingleTempReg();
4401+
GetEmitter()->emitIns_R_AR(INS_mov, size, REG_RAX, addr->GetRegNum(), 0);
4402+
BasicBlock* loop = genCreateTempLabel();
4403+
genDefineTempLabel(loop);
4404+
GetEmitter()->emitIns_Mov(INS_mov, size, tmpReg, REG_RAX, false);
4405+
GetEmitter()->emitIns_R_R(ins, size, tmpReg, data->GetRegNum());
4406+
instGen(INS_lock);
4407+
GetEmitter()->emitIns_AR_R(INS_cmpxchg, size, tmpReg, addr->GetRegNum(), 0);
4408+
inst_JMP(EJ_jne, loop);
4409+
4410+
gcInfo.gcMarkRegSetNpt(genRegMask(addr->GetRegNum()));
4411+
genProduceReg(node);
4412+
}
4413+
return;
4414+
}
4415+
43694416
// If the destination register is different from the data register then we need
43704417
// to first move the data to the target register. Make sure we don't overwrite
43714418
// the address, the register allocator should have taken care of this.

src/coreclr/jit/importercalls.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -3239,13 +3239,13 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
32393239
break;
32403240
}
32413241

3242-
#if defined(TARGET_ARM64) || defined(TARGET_RISCV64)
3243-
// Intrinsify Interlocked.Or and Interlocked.And only for arm64-v8.1 (and newer) and for RV64A
3244-
// TODO-CQ: Implement for XArch (https://github.com/dotnet/runtime/issues/32239).
3242+
#if defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_XARCH)
32453243
case NI_System_Threading_Interlocked_Or:
32463244
case NI_System_Threading_Interlocked_And:
32473245
{
3248-
ARM64_ONLY(if (compOpportunisticallyDependsOn(InstructionSet_Atomics)))
3246+
#if defined(TARGET_ARM64)
3247+
if (compOpportunisticallyDependsOn(InstructionSet_Atomics))
3248+
#endif
32493249
{
32503250
assert(sig->numArgs == 2);
32513251
GenTree* op2 = impPopStack().val;

src/coreclr/jit/lower.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -640,8 +640,6 @@ GenTree* Lowering::LowerNode(GenTree* node)
640640
CheckImmedAndMakeContained(node, node->AsOp()->gtOp2);
641641
break;
642642
#elif defined(TARGET_XARCH)
643-
case GT_XORR:
644-
case GT_XAND:
645643
case GT_XADD:
646644
if (node->IsUnusedValue())
647645
{

src/coreclr/jit/lsraxarch.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,17 @@ int LinearScan::BuildNode(GenTree* tree)
436436

437437
case GT_XORR:
438438
case GT_XAND:
439+
if (!tree->IsUnusedValue())
440+
{
441+
// if value is unused, we'll emit a cmpxchg-loop idiom (requires RAX)
442+
BuildUse(tree->gtGetOp1(), availableIntRegs & ~RBM_RAX);
443+
BuildUse(tree->gtGetOp2(), availableIntRegs & ~RBM_RAX);
444+
buildInternalIntRegisterDefForNode(tree, availableIntRegs & ~RBM_RAX);
445+
BuildDef(tree, RBM_RAX);
446+
srcCount = 2;
447+
assert(dstCount == 1);
448+
}
449+
FALLTHROUGH;
439450
case GT_XADD:
440451
case GT_XCHG:
441452
{

0 commit comments

Comments
 (0)