-
Notifications
You must be signed in to change notification settings - Fork 237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SSA] Buggy SSA output with to_ssa.py
and a modified if-ssa.bril
#108
Comments
Aha, that's interesting! You're quite right, of course, that the latter example, should not prune the Anyway, dealing with undefined variables is extremely tricky here! I'd be interested in any straightforward fixes that don't make things any more complicated (it's fine if they generate more useless Maybe there's a way to fix the pruning too. Not sure. |
I’ll put up a PR that fixes the bug, so to_ssa.py generates correct code. Then I’ll think about a different pruning approach. |
Fixes Issue sampsyo#108. The optimization in prune_phis() is not sound. phi nodes that are "partially undefined" cannot be removed. It is only illegal to read from the result if the second to last executed label corresponds to an undefined argument. `to_ssa.py` generated incorrect code for the two tests added, before the fix. In most cases, these phi nodes aren't read from, and will be removed by DCE. However, in the case where the phi nodes are read, a correct optimization would be: 1. Detect partially undefined phi nodes whose value is always used. Let `undefined_labels` be the set of labels whose argument is undefined. 2. Leverage the undefined behavior to say that `undefined_labels` are in fact not predecessors to the basic block. `preds = preds - undefined_labels`. Though, I'm not sure how useful that optimization would be in practice.
Fixes Issue sampsyo#108. The optimization in prune_phis() is not sound. phi nodes that are "partially undefined" cannot be removed. It is only illegal to read from the result if the second to last executed label corresponds to an undefined argument. `to_ssa.py` generated incorrect code for the two tests added, before the fix. In most cases, these phi nodes aren't read from, and will be removed by DCE. However, in the case where the phi nodes are read, a correct optimization would be: 1. Detect partially undefined phi nodes whose value is always used. Let `undefined_labels` be the set of labels whose argument is undefined. 2. Leverage the undefined behavior to say that `undefined_labels` are in fact not predecessors to the basic block. `preds = preds - undefined_labels`. Though, I'm not sure how useful that optimization would be in practice.
One thing that pops out now is that the resulting code after To demonstrate on the second example above,
Maybe there's a reason why this is not a good idea? Edit: It also looks like there is a bug, since we have the
I think this is along the lines of what you're trying to fix? Edit2: To build on this a little more, We don't want The fix I propose is on the following line: Line 75 in dbbd8e8
We append the following condition:
When we pipe this through
I need to do a bit more thinking / some tests, since I'm sure there's some bits I'm missing, such as definitions being in predecessors, but not the immediate block. |
I think you're on the right track, although I don't know 100% that this will fix the problem. (Maybe it will?) In short, just removing the copies from |
I know this issue is old, but I want to write up a summary of where things stand on this sad state of affairs. Basically, writing an SSA conversion in a way that deals elegantly with undefined values turns out to be pretty frustrating. To re-illustrate the basic problem, consider this simple program (called
The thing to notice about this program is that it is only safe to run because the branch happens to always go to the If we follow our SSA conversion algorithm to the letter, we'd end up first renaming the assignment to
And generating a phi-node that must look something like this in
But what should the value of Our current "solution" is to just replace that
…the idea being that this edge will never actually be traversed, so it should be fine to refer to a variable that doesn't actually exist here. Easy peasy, right? Not so fast—this works until we try to generate the phi-node for
Oh no! The CFG edge from To make this bad scenario more concrete (because running programs directly in SSA form is kinda weird), consider converting this program back out of SSA form, which means inserting assignments into the predecessor blocks. Namely, here, we'll put this into the end of the
The interpreter will, understandably, not like this line. Again, we have a clever "solution": let's just run dead code elimination! This The only problem is that trivial dead code elimination is trivial. It can't identify more sophisticated situations where these needless generated assignments are inserted. The most dastardly case is where a cycle exists between two generated SSA variables, both of which are dead, but neither of which looks trivially dead in isolation. This comes up if you write a loop that assigns to a variable (that isn't assigned outside of the loop), like in this code from
In this code,
The problem here is that, if you insert all the copies to reflect these phi-nodes, the In #119, @terrelln had the bright idea to just define all these variables. SSA conversion would just insert actual variables like I like this solution well enough, but it has a couple of drawbacks:
So there it currently stands. I would love to find a solution that avoids the above problems while not adding too much complexity to the rest of Bril or to the SSA conversion algorithm itself. (The example is structured this way to closely mirror the idealized pseudocode; crapping it up too much just to deal with this weird edge case of undefined values would be a shame.) Something in particular that I like about our current (flawed) approach is that it is possible to generate working SSA code with the "naive" SSA conversion algorithm; you don't need to do some sort of fancy "minimal SSA" conversion just to get correct code. It would be awesome to preserve that property in the face of undefined values. One option I'm considering is redefining the |
Just one small addendum on the history here: our previous "solution" (before the whole
But rather like this:
That is, we could omit the The problem with that approach was again "transitive deadness." When translating back out of SSA form, the (lone) copy that implements this phi-node for |
This input, which is exactly if-ssa.bril except the
.exit
label is renamed to.xit
causes buggy SSA to be generated byto_ssa.py
:The generated SSA is:
The buggy line is
a.4.0: int = phi a.2.1 a.3.1 .left .right;
. When called withcond = false
the variablea.3.1
is not defined, causinga.4.0
to be undefined.This is caused by the function
prune_phis()
:bril/examples/to_ssa.py
Lines 91 to 126 in d65455d
This bug is triggered with the new input, and not triggered by
if-ssa.bril
because of the sorting done on this line:bril/examples/to_ssa.py
Line 79 in d65455d
I believe that this optimization is not sound, even in the case when the input does not have
phi
instructions. Imagine the input:Because
cond
is alwaystrue
,a
is always defined. But,to_ssa.py
generates incorrect code similar to before:You could also argue that variables MUST be defined on all paths for the bril program to be well-formed. But, I don't think that is a reasonable requirement. And instead it should instead be undefined behavior to read from an undefined variable. That leaves the
prune_phis
optimization as it is currently implemented unsound.Instead,
prune_phis()
should probably insert undefined variables like__undefined
, or read from the output variable.The text was updated successfully, but these errors were encountered: