@@ -2047,12 +2047,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
2047
2047
2048
2048
case GT_XCHG:
2049
2049
case GT_XADD:
2050
- genLockedInstructions(treeNode->AsOp());
2051
- break;
2052
-
2053
2050
case GT_XORR:
2054
2051
case GT_XAND:
2055
- NYI("Interlocked.Or and Interlocked.And aren't implemented for x86 yet." );
2052
+ genLockedInstructions(treeNode->AsOp() );
2056
2053
break;
2057
2054
2058
2055
case GT_MEMORYBARRIER:
@@ -4354,7 +4351,7 @@ void CodeGen::genCodeForLockAdd(GenTreeOp* node)
4354
4351
//
4355
4352
void CodeGen::genLockedInstructions(GenTreeOp* node)
4356
4353
{
4357
- assert(node->OperIs(GT_XADD, GT_XCHG));
4354
+ assert(node->OperIs(GT_XADD, GT_XCHG, GT_XORR, GT_XAND ));
4358
4355
4359
4356
GenTree* addr = node->gtGetOp1();
4360
4357
GenTree* data = node->gtGetOp2();
@@ -4366,6 +4363,56 @@ void CodeGen::genLockedInstructions(GenTreeOp* node)
4366
4363
4367
4364
genConsumeOperands(node);
4368
4365
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
+
4369
4416
// If the destination register is different from the data register then we need
4370
4417
// to first move the data to the target register. Make sure we don't overwrite
4371
4418
// the address, the register allocator should have taken care of this.
0 commit comments