Includes generalized regression test that puts icmp and cond br in distinct
blocks, and an update to an existing test to reflect correction to LSR
transformation.
Details
- Reviewers
- None
Diff Detail
- Repository
- rG LLVM Github Monorepo
Unit Tests
Event Timeline
llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp | ||
---|---|---|
2401 | thanks lint! (old habits die hard.) will fix. |
The approach here doesn't look right to me. Poison flags have no relation to the position where a certain instruction is executed. What's relevant is where the instruction is used.
This transform adds a new poison-is-UB user to the post-inc IV. Whereever that code is, should be the place that drops poison flags. The code is pretty convoluted though, so I didn't immediately spot where that happens.
The main instance I know of of a new poison-is-UB user is the TermBr itself that will immediately succeed Cond after the transformation. Based on what the transformation is doing, I guess *all* users of the Cond are potentially poison-is-UB users.
That is, for the specific bug that I'm recreating: (1.) the addition now happens before the Cond instead of after (and still overflows; it always overflowed), (2.) the poison value flows into the Cond, yielding poison, and (3.) the poison from the Cond flows into the TermBr, yielding UB.
I considered starting from the TermBr, and tracing backwards to find every operation that flows into its computation. But I was concerned that would be overly conservative and potentially introduce regressions. I considered the change I posted to be the simplest change I could make that would still fix the bug.
I suppose I could try to trace the value-flow backwards from the TermBr, but only remove the nowrap flags from the instructions that are *dominated* by the Cond... would that be preferable to you, @nikic ?
That is, for the specific bug that I'm recreating: (1.) the addition now happens before the Cond instead of after (and still overflows; it always overflowed), (2.) the poison value flows into the Cond, yielding poison, and (3.) the poison from the Cond flows into the TermBr, yielding UB.
To be explicit here: The issue is not whether the addition happens before or after the comparison, in fact you can freely interchange these instructions, as they have no sequencing constraint. I expect that if you swap x.3 and x.4 in your test case, then your fix will no longer work, even though dropping nuw is equally necessary in that case. The problematic part is the new user of the postinc IV.
I would expect the patch for this to look closer to something like this: https://gist.github.com/nikic/7e1301a81542d95953a44243c8ee5b5e That is, drop the poison flags when SCEV expansion reuses a postinc value in some form, on the assumption that the new user might have UB-on-poison semantics.
The programUndefinedIfPoison() check there is supposed to avoid dropping poison flags if there is an existing UB-on-poison user, in particular if the loop is already in postinc form. Unfortunately this doesn't actually work right now, because getGuaranteedNonPoisonOps() doesn't take into account branch instructions :/
clang-tidy: warning: invalid case style for variable 'add_successors' [readability-identifier-naming]
not useful