@@ -1314,6 +1314,46 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree)
1314
1314
}
1315
1315
}
1316
1316
1317
+ //------------------------------------------------------------------------
1318
+ // JumpKindToCmov:
1319
+ // Convert an emitJumpKind to the corresponding cmov instruction.
1320
+ //
1321
+ // Arguments:
1322
+ // condition - the condition
1323
+ //
1324
+ // Returns:
1325
+ // A cmov instruction.
1326
+ //
1327
+ instruction CodeGen::JumpKindToCmov(emitJumpKind condition)
1328
+ {
1329
+ static constexpr instruction s_table[EJ_COUNT] = {
1330
+ INS_none, INS_none, INS_cmovo, INS_cmovno, INS_cmovb, INS_cmovae, INS_cmove, INS_cmovne, INS_cmovbe,
1331
+ INS_cmova, INS_cmovs, INS_cmovns, INS_cmovp, INS_cmovnp, INS_cmovl, INS_cmovge, INS_cmovle, INS_cmovg,
1332
+ };
1333
+
1334
+ static_assert_no_msg(s_table[EJ_NONE] == INS_none);
1335
+ static_assert_no_msg(s_table[EJ_jmp] == INS_none);
1336
+ static_assert_no_msg(s_table[EJ_jo] == INS_cmovo);
1337
+ static_assert_no_msg(s_table[EJ_jno] == INS_cmovno);
1338
+ static_assert_no_msg(s_table[EJ_jb] == INS_cmovb);
1339
+ static_assert_no_msg(s_table[EJ_jae] == INS_cmovae);
1340
+ static_assert_no_msg(s_table[EJ_je] == INS_cmove);
1341
+ static_assert_no_msg(s_table[EJ_jne] == INS_cmovne);
1342
+ static_assert_no_msg(s_table[EJ_jbe] == INS_cmovbe);
1343
+ static_assert_no_msg(s_table[EJ_ja] == INS_cmova);
1344
+ static_assert_no_msg(s_table[EJ_js] == INS_cmovs);
1345
+ static_assert_no_msg(s_table[EJ_jns] == INS_cmovns);
1346
+ static_assert_no_msg(s_table[EJ_jp] == INS_cmovp);
1347
+ static_assert_no_msg(s_table[EJ_jnp] == INS_cmovnp);
1348
+ static_assert_no_msg(s_table[EJ_jl] == INS_cmovl);
1349
+ static_assert_no_msg(s_table[EJ_jge] == INS_cmovge);
1350
+ static_assert_no_msg(s_table[EJ_jle] == INS_cmovle);
1351
+ static_assert_no_msg(s_table[EJ_jg] == INS_cmovg);
1352
+
1353
+ assert((condition >= EJ_NONE) && (condition < EJ_COUNT));
1354
+ return s_table[condition];
1355
+ }
1356
+
1317
1357
//------------------------------------------------------------------------
1318
1358
// genCodeForCompare: Produce code for a GT_SELECT/GT_SELECT_HI node.
1319
1359
//
@@ -1328,37 +1368,99 @@ void CodeGen::genCodeForSelect(GenTreeOp* select)
1328
1368
assert(select->OperIs(GT_SELECT));
1329
1369
#endif
1330
1370
1331
- regNumber dstReg = select->GetRegNum();
1332
1371
if (select->OperIs(GT_SELECT))
1333
1372
{
1334
- genConsumeReg (select->AsConditional()->gtCond);
1373
+ genConsumeRegs (select->AsConditional()->gtCond);
1335
1374
}
1336
1375
1337
1376
genConsumeOperands(select);
1338
1377
1339
- instruction cmovKind = INS_cmovne;
1340
- GenTree* trueVal = select->gtOp1;
1341
- GenTree* falseVal = select->gtOp2;
1378
+ regNumber dstReg = select->GetRegNum();
1342
1379
1343
- // If the 'true' operand was allocated the same register as the target
1344
- // register then flip it to the false value so we can skip a reg-reg mov.
1345
- if (trueVal->isUsedFromReg() && (trueVal->GetRegNum() == dstReg))
1380
+ GenTree* trueVal = select->gtOp1;
1381
+ GenTree* falseVal = select->gtOp2;
1382
+
1383
+ GenCondition cc = GenCondition::NE;
1384
+
1385
+ if (select->OperIs(GT_SELECT))
1386
+ {
1387
+ GenTree* cond = select->AsConditional()->gtCond;
1388
+ if (cond->isContained())
1389
+ {
1390
+ assert(cond->OperIsCompare());
1391
+ genCodeForCompare(cond->AsOp());
1392
+ cc = GenCondition::FromRelop(cond);
1393
+
1394
+ if (cc.PreferSwap())
1395
+ {
1396
+ // genCodeForCompare generated the compare with swapped
1397
+ // operands because this swap requires fewer branches/cmovs.
1398
+ cc = GenCondition::Swap(cc);
1399
+ }
1400
+ }
1401
+ else
1402
+ {
1403
+ regNumber condReg = cond->GetRegNum();
1404
+ GetEmitter()->emitIns_R_R(INS_test, EA_4BYTE, condReg, condReg);
1405
+ }
1406
+ }
1407
+
1408
+ // The usual codegen will be
1409
+ // mov targetReg, falseValue
1410
+ // cmovne targetReg, trueValue
1411
+ //
1412
+ // However, if the 'true' operand was allocated the same register as the
1413
+ // target register then prefer to generate
1414
+ //
1415
+ // mov targetReg, trueValue
1416
+ // cmove targetReg, falseValue
1417
+ //
1418
+ // so the first mov is elided.
1419
+ //
1420
+ if (falseVal->isUsedFromReg() && (falseVal->GetRegNum() == dstReg))
1346
1421
{
1347
1422
std::swap(trueVal, falseVal);
1348
- cmovKind = INS_cmove ;
1423
+ cc = GenCondition::Reverse(cc) ;
1349
1424
}
1350
1425
1351
- if (select->OperIs(GT_SELECT))
1426
+ // If there is a conflict then swap the condition anyway. LSRA should have
1427
+ // ensured the other way around has no conflict.
1428
+ if ((trueVal->gtGetContainedRegMask() & genRegMask(dstReg)) != 0)
1352
1429
{
1353
- // TODO-CQ: Support contained relops here.
1354
- assert(select->AsConditional()->gtCond->isUsedFromReg());
1430
+ std::swap(trueVal, falseVal);
1431
+ cc = GenCondition::Reverse(cc);
1432
+ }
1433
+
1434
+ GenConditionDesc desc = GenConditionDesc::Get(cc);
1355
1435
1356
- regNumber condReg = select->AsConditional()->gtCond->GetRegNum();
1357
- GetEmitter()->emitIns_R_R(INS_test, EA_4BYTE, condReg, condReg);
1436
+ // There may also be a conflict with the falseVal in case this is an AND
1437
+ // condition. Once again, after swapping there should be no conflict as
1438
+ // ensured by LSRA.
1439
+ if ((desc.oper == GT_AND) && (falseVal->gtGetContainedRegMask() & genRegMask(dstReg)) != 0)
1440
+ {
1441
+ std::swap(trueVal, falseVal);
1442
+ cc = GenCondition::Reverse(cc);
1443
+ desc = GenConditionDesc::Get(cc);
1358
1444
}
1359
1445
1360
1446
inst_RV_TT(INS_mov, emitTypeSize(select), dstReg, falseVal);
1361
- inst_RV_TT(cmovKind, emitTypeSize(select), dstReg, trueVal);
1447
+
1448
+ assert(!trueVal->isContained() || trueVal->isUsedFromMemory());
1449
+ assert((trueVal->gtGetContainedRegMask() & genRegMask(dstReg)) == 0);
1450
+ inst_RV_TT(JumpKindToCmov(desc.jumpKind1), emitTypeSize(select), dstReg, trueVal);
1451
+
1452
+ if (desc.oper == GT_AND)
1453
+ {
1454
+ assert(falseVal->isUsedFromReg());
1455
+ assert((falseVal->gtGetContainedRegMask() & genRegMask(dstReg)) == 0);
1456
+ inst_RV_TT(JumpKindToCmov(emitter::emitReverseJumpKind(desc.jumpKind2)), emitTypeSize(select), dstReg,
1457
+ falseVal);
1458
+ }
1459
+ else if (desc.oper == GT_OR)
1460
+ {
1461
+ assert(trueVal->isUsedFromReg());
1462
+ inst_RV_TT(JumpKindToCmov(desc.jumpKind2), emitTypeSize(select), dstReg, trueVal);
1463
+ }
1362
1464
1363
1465
genProduceReg(select);
1364
1466
}
@@ -1776,6 +1878,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
1776
1878
case GT_TEST_EQ:
1777
1879
case GT_TEST_NE:
1778
1880
case GT_CMP:
1881
+ genConsumeOperands(treeNode->AsOp());
1779
1882
genCodeForCompare(treeNode->AsOp());
1780
1883
break;
1781
1884
@@ -6490,8 +6593,6 @@ void CodeGen::genCompareFloat(GenTree* treeNode)
6490
6593
var_types op1Type = op1->TypeGet();
6491
6594
var_types op2Type = op2->TypeGet();
6492
6595
6493
- genConsumeOperands(tree);
6494
-
6495
6596
assert(varTypeIsFloating(op1Type));
6496
6597
assert(op1Type == op2Type);
6497
6598
@@ -6565,8 +6666,6 @@ void CodeGen::genCompareInt(GenTree* treeNode)
6565
6666
emitter* emit = GetEmitter();
6566
6667
bool canReuseFlags = false;
6567
6668
6568
- genConsumeOperands(tree);
6569
-
6570
6669
assert(!op1->isContainedIntOrIImmed());
6571
6670
assert(!varTypeIsFloating(op2Type));
6572
6671
0 commit comments