Skip to content

Commit de00deb

Browse files
Use IntegralRange in fgOptimizeCast (#59897)
* Set call return type in fgMorphIntoHelperCall It was just forgotten. * Introduce IntegralRange::ForNode * Use Range::ForNode in assertion propagation Couple good diffs from the handling of GT_CALL. * Fix IntegralRange::ForCastInput The comments are right, the code is wrong... Fortunately, the bug was a pessimizing one, not a correctness issue. * Use IntegralRange in fgOptimizeCast And reap the benefits.
1 parent edc5a41 commit de00deb

File tree

4 files changed

+136
-228
lines changed

4 files changed

+136
-228
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 68 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,59 @@ bool IntegralRange::Contains(int64_t value) const
118118
}
119119
}
120120

121+
//------------------------------------------------------------------------
122+
// ForNode: Compute the integral range for a node.
123+
//
124+
// Arguments:
125+
// node - the node, of an integral type, in question
126+
// compiler - the Compiler, used to retrieve additional info
127+
//
128+
// Return Value:
129+
// The integral range this node produces.
130+
//
131+
/* static */ IntegralRange IntegralRange::ForNode(GenTree* node, Compiler* compiler)
132+
{
133+
assert(varTypeIsIntegral(node));
134+
135+
var_types rangeType = node->TypeGet();
136+
137+
switch (node->OperGet())
138+
{
139+
case GT_EQ:
140+
case GT_NE:
141+
case GT_LT:
142+
case GT_LE:
143+
case GT_GE:
144+
case GT_GT:
145+
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One};
146+
147+
case GT_ARR_LENGTH:
148+
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::IntMax};
149+
150+
case GT_CALL:
151+
if (node->AsCall()->NormalizesSmallTypesOnReturn())
152+
{
153+
rangeType = static_cast<var_types>(node->AsCall()->gtReturnType);
154+
}
155+
break;
156+
157+
case GT_LCL_VAR:
158+
if (compiler->lvaGetDesc(node->AsLclVar())->lvNormalizeOnStore())
159+
{
160+
rangeType = compiler->lvaGetDesc(node->AsLclVar())->TypeGet();
161+
}
162+
break;
163+
164+
case GT_CAST:
165+
return ForCastOutput(node->AsCast());
166+
167+
default:
168+
break;
169+
}
170+
171+
return ForType(rangeType);
172+
}
173+
121174
//------------------------------------------------------------------------
122175
// ForCastInput: Get the non-overflowing input range for a cast.
123176
//
@@ -214,7 +267,14 @@ bool IntegralRange::Contains(int64_t value) const
214267
// CAST_OVF(long <- ulong) - [0..LONG_MAX]
215268
// CAST_OVF(long <- long) - [LONG_MIN..LONG_MAX]
216269
case TYP_LONG:
217-
lowerBound = fromUnsigned ? SymbolicIntegerValue::Zero : LowerBoundForType(fromType);
270+
if (fromUnsigned && (fromType == TYP_LONG))
271+
{
272+
lowerBound = SymbolicIntegerValue::Zero;
273+
}
274+
else
275+
{
276+
lowerBound = LowerBoundForType(fromType);
277+
}
218278
upperBound = UpperBoundForType(fromType);
219279
break;
220280

@@ -1545,12 +1605,17 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
15451605
}
15461606

15471607
// Try and see if we can make a subrange assertion.
1548-
if (((assertionKind == OAK_SUBRANGE) || (assertionKind == OAK_EQUAL)))
1608+
if (((assertionKind == OAK_SUBRANGE) || (assertionKind == OAK_EQUAL)) && varTypeIsIntegral(op2))
15491609
{
1550-
if (optTryExtractSubrangeAssertion(op2, &assertion.op2.u2))
1610+
IntegralRange nodeRange = IntegralRange::ForNode(op2, this);
1611+
IntegralRange typeRange = IntegralRange::ForType(genActualType(op2));
1612+
assert(typeRange.Contains(nodeRange));
1613+
1614+
if (!typeRange.Equals(nodeRange))
15511615
{
15521616
assertion.op2.kind = O2K_SUBRANGE;
15531617
assertion.assertionKind = OAK_SUBRANGE;
1618+
assertion.op2.u2 = nodeRange;
15541619
}
15551620
}
15561621
}
@@ -1689,73 +1754,6 @@ AssertionIndex Compiler::optFinalizeCreatingAssertion(AssertionDsc* assertion)
16891754
return optAddAssertion(assertion);
16901755
}
16911756

1692-
//------------------------------------------------------------------------
1693-
// optTryExtractSubrangeAssertion: Extract the bounds of the value a tree produces.
1694-
//
1695-
// Generates [0..1] ranges for relops, [T_MIN..T_MAX] for small-typed indirections
1696-
// and casts to small types. Generates various ranges for casts to and from "large"
1697-
// types - see "IntegralRange::ForCastOutput".
1698-
//
1699-
// Arguments:
1700-
// source - tree producing the value
1701-
// pRange - [out] parameter for the range
1702-
//
1703-
// Return Value:
1704-
// "true" if the "source" computes a value that could be used for a subrange
1705-
// assertion, i. e. narrower than the range of the node's type.
1706-
//
1707-
// Notes:
1708-
// The "pRange" parameter is only written to if the function returns "true".
1709-
//
1710-
bool Compiler::optTryExtractSubrangeAssertion(GenTree* source, IntegralRange* pRange)
1711-
{
1712-
var_types sourceType = TYP_UNDEF;
1713-
1714-
switch (source->OperGet())
1715-
{
1716-
case GT_EQ:
1717-
case GT_NE:
1718-
case GT_LT:
1719-
case GT_LE:
1720-
case GT_GT:
1721-
case GT_GE:
1722-
*pRange = {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One};
1723-
return true;
1724-
1725-
case GT_CLS_VAR:
1726-
case GT_LCL_FLD:
1727-
case GT_IND:
1728-
sourceType = source->TypeGet();
1729-
break;
1730-
1731-
case GT_CAST:
1732-
if (varTypeIsIntegral(source))
1733-
{
1734-
IntegralRange castRange = IntegralRange::ForCastOutput(source->AsCast());
1735-
IntegralRange nodeRange = IntegralRange::ForType(source->TypeGet());
1736-
assert(nodeRange.Contains(castRange));
1737-
1738-
if (!castRange.Equals(nodeRange))
1739-
{
1740-
*pRange = castRange;
1741-
return true;
1742-
}
1743-
}
1744-
return false;
1745-
1746-
default:
1747-
return false;
1748-
}
1749-
1750-
if (varTypeIsSmall(sourceType))
1751-
{
1752-
*pRange = IntegralRange::ForType(sourceType);
1753-
return true;
1754-
}
1755-
1756-
return false;
1757-
}
1758-
17591757
/*****************************************************************************
17601758
*
17611759
* If tree is a constant node holding an integral value, retrieve the value in

src/coreclr/jit/compiler.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,11 @@ class IntegralRange
12161216
return (m_lowerBound <= other.m_lowerBound) && (other.m_upperBound <= m_upperBound);
12171217
}
12181218

1219+
bool IsPositive()
1220+
{
1221+
return m_lowerBound >= SymbolicIntegerValue::Zero;
1222+
}
1223+
12191224
bool Equals(IntegralRange other) const
12201225
{
12211226
return (m_lowerBound == other.m_lowerBound) && (m_upperBound == other.m_upperBound);
@@ -1230,6 +1235,7 @@ class IntegralRange
12301235
return {LowerBoundForType(type), UpperBoundForType(type)};
12311236
}
12321237

1238+
static IntegralRange ForNode(GenTree* node, Compiler* compiler);
12331239
static IntegralRange ForCastInput(GenTreeCast* cast);
12341240
static IntegralRange ForCastOutput(GenTreeCast* cast);
12351241

@@ -6363,7 +6369,7 @@ class Compiler
63636369
GenTree* fgMorphCopyBlock(GenTree* tree);
63646370
GenTree* fgMorphForRegisterFP(GenTree* tree);
63656371
GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac = nullptr);
6366-
GenTree* fgOptimizeCast(GenTree* tree);
6372+
GenTree* fgOptimizeCast(GenTreeCast* cast);
63676373
GenTree* fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp);
63686374
GenTree* fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp);
63696375
GenTree* fgPropagateCommaThrow(GenTree* parent, GenTreeOp* commaThrow, GenTreeFlags precedingSideEffects);

src/coreclr/jit/gentree.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4636,6 +4636,11 @@ struct GenTreeCall final : public GenTree
46364636
}
46374637
#endif // !FEATURE_TAILCALL_OPT
46384638

4639+
bool NormalizesSmallTypesOnReturn()
4640+
{
4641+
return GetUnmanagedCallConv() == CorInfoCallConvExtension::Managed;
4642+
}
4643+
46394644
bool IsSameThis() const
46404645
{
46414646
return (gtCallMoreFlags & GTF_CALL_M_NONVIRT_SAME_THIS) != 0;

0 commit comments

Comments
 (0)