This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Liberate assert in InstCombiner::visitZExt
ClosedPublic

Authored by bjope on Mar 15 2017, 11:43 AM.

Details

Summary

The call to canEvaluateZExtd in InstCombiner::visitZExt may
return with BitsToClear == SrcTy->getScalarSizeInBits(), but
there is an assert that BitsToClear should be smaller than
SrcTy->getScalarSizeInBits().

I have a test case that triggers the assert, but it only happens
for my downstream target. I've not been able to trigger it for
any upstream target.

The assert triggered for a piece of code such as this

%shr1 = lshr i16 undef, 15
...
%shr2 = lshr i16 %shr1, 1
%conv = zext i16 %shr2 to i32

Normally the lshr instructions are constant folded before we
visit the zext (that is why it is so hard to reproduce).
The original pattern, before instcombine, is of course a lot more
complicated in my test case. The shift count in the second lshr
is for example determined by the outcome of a PHI instruction.
It seems like other rewrites by instcombine leads up to
the pattern above. And then the zext is pulled from the
worklist, and visited (hitting the assert), before we detect
that the lshr instrucions can be constant folded.

Anyway, since the canEvaluateZExtd may return with BitsToClear
equal to SrcTy->getScalarSizeInBits(), and since the rewrite
that converts the expression type to avoid a zero extend works
also for the case where SrcBitsKept ends up being zero, then
it should be OK to liberate the assert to

assert(BitsToClear <= SrcTy->getScalarSizeInBits() &&
       "Unreasonable BitsToClear");

Diff Detail

Repository
rL LLVM

Event Timeline

bjope created this revision.Mar 15 2017, 11:43 AM

Should we just abort the transformation in this case? Otherwise we just create an and with 0?

lib/Transforms/InstCombine/InstCombineCasts.cpp
908 ↗(On Diff #91910)

This assert message is not very useful. More useful might be:

"Can't clear more bits than in SrcTy"
bjope added a comment.Mar 16 2017, 2:41 AM

Should we just abort the transformation in this case? Otherwise we just create an and with 0?

As I see it there are at least four options:

  1. Just handle this case as the general case. I belive that CreateAnd with 0 will be folded directly into a constant 0, so we never get the 'and'. The possible drawback is that EvaluateInDifferentType also rewrites the source expressions. The instructions added by EvaluateInDifferentType is later removed ("IC: DCE:")) since they are dead due to the and being folded away.
  2. Add special handling of the situation when SrcBitsKept==0, to let make the constant fold more explicit in the code. Here we should avoid EvaluateInDifferentType, and explicitly replace the zext with a zero instead of using CreateAnd.
  3. Bail out (return nullptr) if BitsToClear==SrcTy->getScalarSizeInBits().
  4. Exit the if-statement when BitsToClear==SrcTy->getScalarSizeInBits() and continue looking at the other tranforms done by visitZExt.

I basically went for option 1 as it was least intrusive.
Since I haven't been able to write a simple test case that triggers the BitsToClear==SrcTy->getScalarSizeInBits() situation, and since it seems to happen very rarely (or otherwise someone else would have fixed this assert already), I do not want to go for option 2. If I could test this easily (adding a regression test for an in-tree-target), then I think option 2 would have been the obvious choice.

bjope updated this revision to Diff 91981.Mar 16 2017, 2:52 AM

Updated the assert message (as suggested by hfinkel).
Minor updates to the summary.

bjope marked an inline comment as done.Mar 16 2017, 2:52 AM
hfinkel accepted this revision.Mar 16 2017, 5:43 AM

Okay, this does seem like the safest fix. LGTM.

This revision is now accepted and ready to land.Mar 16 2017, 5:43 AM
This revision was automatically updated to reflect the committed changes.