Skip to content

Commit e52cfdb

Browse files
Emit cmp (extended register) on ARM64 to simplify cast-then-compare expressions (#112411)
* Emit `cmp (extended register)` on ARM64 to simplify cast-then-compare expressions Working towards #68028 * Move mutating logic into callers
1 parent e575397 commit e52cfdb

File tree

5 files changed

+509
-71
lines changed

5 files changed

+509
-71
lines changed

src/coreclr/jit/codegenarm64.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -4834,6 +4834,45 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree)
48344834
op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(oper));
48354835
break;
48364836

4837+
case GT_CAST:
4838+
{
4839+
assert(ins == INS_cmp);
4840+
assert(cmpSize >= genTypeSize(op2->CastToType()));
4841+
assert(cmpSize == EA_4BYTE || cmpSize == EA_8BYTE);
4842+
assert(op1->gtHasReg(compiler));
4843+
assert(op2->gtGetOp1()->gtHasReg(compiler));
4844+
4845+
GenTreeCast* cast = op2->AsCast();
4846+
4847+
GenIntCastDesc desc(cast);
4848+
4849+
// These casts should not lead to an overflow check.
4850+
assert(desc.CheckKind() == GenIntCastDesc::CHECK_NONE);
4851+
4852+
insOpts extOpts = INS_OPTS_NONE;
4853+
switch (desc.ExtendKind())
4854+
{
4855+
case GenIntCastDesc::ZERO_EXTEND_SMALL_INT:
4856+
extOpts = (desc.ExtendSrcSize() == 1) ? INS_OPTS_UXTB : INS_OPTS_UXTH;
4857+
break;
4858+
case GenIntCastDesc::SIGN_EXTEND_SMALL_INT:
4859+
extOpts = (desc.ExtendSrcSize() == 1) ? INS_OPTS_SXTB : INS_OPTS_SXTH;
4860+
break;
4861+
case GenIntCastDesc::ZERO_EXTEND_INT:
4862+
extOpts = INS_OPTS_UXTW;
4863+
break;
4864+
case GenIntCastDesc::SIGN_EXTEND_INT:
4865+
extOpts = INS_OPTS_SXTW;
4866+
break;
4867+
default:
4868+
// Other casts should not lead here as they will not pass the
4869+
// IsContainableUnaryOrBinaryOp check.
4870+
unreached();
4871+
}
4872+
4873+
emit->emitIns_R_R(INS_cmp, cmpSize, op1->GetRegNum(), cast->gtGetOp1()->GetRegNum(), extOpts);
4874+
break;
4875+
}
48374876
default:
48384877
unreached();
48394878
}

src/coreclr/jit/lowerarmarch.cpp

+39-12
Original file line numberDiff line numberDiff line change
@@ -448,14 +448,15 @@ bool Lowering::IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childN
448448
return false;
449449
}
450450

451+
if (!IsInvariantInRange(childNode, parentNode))
452+
{
453+
return false;
454+
}
455+
451456
if (parentNode->OperIs(GT_ADD, GT_SUB))
452457
{
453458
// These operations can still report flags
454-
455-
if (IsInvariantInRange(childNode, parentNode))
456-
{
457-
return true;
458-
}
459+
return true;
459460
}
460461

461462
if ((parentNode->gtFlags & GTF_SET_FLAGS) != 0)
@@ -466,10 +467,27 @@ bool Lowering::IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childN
466467

467468
if (parentNode->OperIs(GT_CMP))
468469
{
469-
if (IsInvariantInRange(childNode, parentNode))
470+
return true;
471+
}
472+
473+
if (parentNode->OperIsCmpCompare())
474+
{
475+
if (castOp->isContained())
470476
{
471-
return true;
477+
return false;
472478
}
479+
480+
if (IsContainableMemoryOp(castOp))
481+
{
482+
// The cast node will contain a memory operation which will perform
483+
// the cast on load/store, so we don't need to contain it here.
484+
// This check won't catch spills, so if register pressure is high
485+
// this can result in cmp (extended-register) taking higher priority
486+
// over a load/store with extension.
487+
return false;
488+
}
489+
490+
return true;
473491
}
474492

475493
return false;
@@ -2846,25 +2864,24 @@ void Lowering::ContainCheckBinary(GenTreeOp* node)
28462864
{
28472865
if (IsContainableUnaryOrBinaryOp(node, op2))
28482866
{
2849-
if (op2->OperIs(GT_CAST))
2867+
if (node->OperIs(GT_ADD, GT_SUB, GT_CMP) && op2->OperIs(GT_CAST))
28502868
{
28512869
// We want to prefer the combined op here over containment of the cast op
28522870
op2->AsCast()->CastOp()->ClearContained();
28532871
}
2854-
MakeSrcContained(node, op2);
28552872

2873+
MakeSrcContained(node, op2);
28562874
return;
28572875
}
2858-
28592876
if (node->OperIsCommutative() && IsContainableUnaryOrBinaryOp(node, op1))
28602877
{
2861-
if (op1->OperIs(GT_CAST))
2878+
if (node->OperIs(GT_ADD, GT_SUB, GT_CMP) && op1->OperIs(GT_CAST))
28622879
{
28632880
// We want to prefer the combined op here over containment of the cast op
28642881
op1->AsCast()->CastOp()->ClearContained();
28652882
}
2866-
MakeSrcContained(node, op1);
28672883

2884+
MakeSrcContained(node, op1);
28682885
std::swap(node->gtOp1, node->gtOp2);
28692886
return;
28702887
}
@@ -3080,12 +3097,22 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
30803097
{
30813098
if (IsContainableUnaryOrBinaryOp(cmp, op2))
30823099
{
3100+
if (cmp->OperIsCmpCompare() && op2->OperIs(GT_CAST))
3101+
{
3102+
op2->AsCast()->CastOp()->ClearRegOptional();
3103+
}
3104+
30833105
MakeSrcContained(cmp, op2);
30843106
return;
30853107
}
30863108

30873109
if (IsContainableUnaryOrBinaryOp(cmp, op1))
30883110
{
3111+
if (cmp->OperIsCmpCompare() && op1->OperIs(GT_CAST))
3112+
{
3113+
op1->AsCast()->CastOp()->ClearRegOptional();
3114+
}
3115+
30893116
MakeSrcContained(cmp, op1);
30903117
std::swap(cmp->gtOp1, cmp->gtOp2);
30913118
if (cmp->OperIsCompare())

0 commit comments

Comments
 (0)