Fix Issue #25134 - AssertionProp incorrectly removes cast from uint#25260
Fix Issue #25134 - AssertionProp incorrectly removes cast from uint#25260briansull merged 2 commits intodotnet:masterfrom
Conversation
|
Fixes #25134 |
src/jit/assertionprop.cpp
Outdated
| GenTree* op1 = tree->gtCast.CastOp(); | ||
|
|
||
| // force the fromType to unsigned if GT_UNSIGNED flag is set | ||
| if (tree->gtFlags & GTF_UNSIGNED) |
|
|
||
| var_types toType = tree->gtCast.gtCastType; | ||
| GenTree* op1 = tree->gtCast.CastOp(); | ||
| var_types fromType = tree->CastFromType(); |
There was a problem hiding this comment.
I think it would be better to avoid using CastFromType as it doesn't do anything useful. You already have the cast operand below so this is simply var_types fromType = op1->TypeGet().
There was a problem hiding this comment.
Well eventually I want to push the check for IsUnsigned() into CastFromType()
Currently CastFromType() returns the wrong result when IsUnsigned() is set.
Since this change is going into 3.0 I didn't want to refactor this area more broadly.
There was a problem hiding this comment.
I'm not sure if it's easy to put the IsUnsigned check in CastFromType. In some cases (narrowing casts without overflow check) the signedness of the "from" type is not relevant so it should be ignored, otherwise we may end up with different issues. Unfortunately GT_CAST's semantic is extremely complicated (narrowing cast, sign/zero extending cast, FP<->INT cast, overflow checks) and adding a few "helper" functions like CastFromType won't necessarily help with that.
Add additional check for the GT_UNSIGNED flag + Ran clang-format + Code review feedback, use IsUnsigned()
| // | ||
| if (varTypeIsUnsigned(fromType)) | ||
| { | ||
| if (curAssertion->op2.u2.loBound < 0) |
There was a problem hiding this comment.
Speaking of fromType signedness - it looks like this condition is unnecessarily strong. For a narrowing cast the signedness of fromType doesn't really matter as you actually have a widening cast from toType. Yeah, "from toType", that makes my head spin 😄 Well, at least this is only a CQ issue in the worst case.
There was a problem hiding this comment.
When we have a fromType of TYP_UINT and a toType of TYP_INT it matters.
By the way is this a narrowing or a widening cast?
I look a the casting operation like this:
resulting type <= CastToType <= fromType
Where both the fromType and the resulting type are constrained to be legal IL stack types.
(which are 32-bit or 64-bit types)
The small types only show up in the CastToType.
There was a problem hiding this comment.
When we have a fromType of TYP_UINT and a toType of TYP_INT it matters.
Only if it's an overflow checking cast, otherwise the entire cast is no-op. In general:
- casts from
TYP_(U)INTtoTYP_(U)LONG-GTF_UNSIGNEDdecides whether we're sign extending or zero extending - overflow checking casts -
GTF_UNSIGNEDdecides how the source operand is treated for overflow checking purposes (e.g. when casting unsigned toTYP_SHORTcheck for 0..32767 instead of -32768..32767). - all other integral casts should ignore
GTF_UNSIGNED. Ideally it should not be set at all but I'm not sure the JIT gets it right, it's too easy to change IR in various ways and forget to reset the flag.
By the way is this a narrowing or a widening cast?
Hmm, neither I'd say. If it doesn't check for overflow then it's a no-op. If it checks for overflow it's not really a cast in the narrowing/widening sense, it's just a range check.
Casting to a small type can probably be considered both narrowing and widening (TYP_INT->TYP_SHORT->TYP_INT) but I decided a long time ago that my head will spin less if I classify it based on the generated code. It generates a movsx so it's a widening cast. Unless of course, it checks for overflow when again it's not a cast but a range check.
|
@dotnet/jit-contrib PTAL |
|
After starring a bit more at this issue I'm starting to believe that the problem slightly more complicated and it involves the assertion generation code as well. The test code results in the following assertion being generated: This range doesn't make sense. Of course it's INT_MIN..INT_MAX, it's a This fix stop the incorrect elimination of the cast but the pointless assertion is still generated. The only time the The |
|
I will open a new issue to remove the creation of the useless assertion.
|
|
@AndyAyersMS PTAL |
|
What diffs does this cause? I'm likewise concerned about the potential for mis-interpreting the generated subrange assertions. That's why I thought trying to handle this might be tricky. A minimal fix (perhaps more suitable for this stage of 3.0?) would be to just bail out of Also it seems like more test cases would be advisable -- at least we should check the complementary pattern of going from a negative int to an unsigned. |
|
I didn't see any Asm Diffs in the frameworks. @AndyAyersMS |
AndyAyersMS
left a comment
There was a problem hiding this comment.
Thanks for the extra test case.
|
@briansull Could you please squash and merge your next change? Merging creates non linear history. |
Fix Issue dotnet/coreclr#25134 - AssertionProp incorrectly removes cast from uint Commit migrated from dotnet/coreclr@56e6568
Add additional check for the GT_UNSIGNED flag