-
Notifications
You must be signed in to change notification settings - Fork 50
Some fixes to allow for call instructions to name args, returns, and clobbers with constraints. #74
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,9 @@ use super::{ | |
| Env, LiveBundleIndex, LiveRangeIndex, LiveRangeKey, SpillSet, SpillSetIndex, SpillSlotIndex, | ||
| VRegIndex, | ||
| }; | ||
| use crate::{ion::data_structures::BlockparamOut, Function, Inst, OperandConstraint, PReg}; | ||
| use crate::{ | ||
| ion::data_structures::BlockparamOut, Function, Inst, OperandConstraint, OperandKind, PReg, | ||
| }; | ||
| use smallvec::smallvec; | ||
|
|
||
| impl<'a, F: Function> Env<'a, F> { | ||
|
|
@@ -93,6 +95,17 @@ impl<'a, F: Function> Env<'a, F> { | |
| } | ||
| } | ||
|
|
||
| // Avoid merging if either side has a fixed-reg def: this can | ||
| // result in an impossible-to-solve allocation problem if | ||
| // there is a fixed-reg use in the same reg on the same | ||
| // instruction. | ||
| if self.bundles[from.index()].cached_fixed_def() | ||
| || self.bundles[to.index()].cached_fixed_def() | ||
| { | ||
| trace!(" -> one bundle has a fixed def; aborting merge"); | ||
| return false; | ||
| } | ||
|
|
||
| // Check for a requirements conflict. | ||
| if self.bundles[from.index()].cached_stack() | ||
| || self.bundles[from.index()].cached_fixed() | ||
|
|
@@ -258,23 +271,30 @@ impl<'a, F: Function> Env<'a, F> { | |
| } | ||
|
|
||
| let mut fixed = false; | ||
| let mut fixed_def = false; | ||
| let mut stack = false; | ||
| for entry in &self.bundles[bundle.index()].ranges { | ||
| for u in &self.ranges[entry.index.index()].uses { | ||
| if let OperandConstraint::FixedReg(_) = u.operand.constraint() { | ||
| fixed = true; | ||
| if u.operand.kind() == OperandKind::Def { | ||
| fixed_def = true; | ||
| } | ||
| } | ||
| if let OperandConstraint::Stack = u.operand.constraint() { | ||
| stack = true; | ||
| } | ||
| if fixed && stack { | ||
| if fixed && stack && fixed_def { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| if fixed { | ||
| self.bundles[bundle.index()].set_cached_fixed(); | ||
| } | ||
| if fixed_def { | ||
| self.bundles[bundle.index()].set_cached_fixed_def(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this flag ever get cleared, or does it follow the parts of the bundle if it's split up?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's recomputed along with the other flags when the bundle is split, if it ever is (it actually is a flag on the whole bundle!).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| if stack { | ||
| self.bundles[bundle.index()].set_cached_stack(); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you knew that the fixed def constraints were for the same register, would that change whether or not you needed to bail out here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, in fact the fuzzbug was exactly this case! The issue is that the way that we do the constraint rewrite adds an anonymous reservation; so this isn't a case of "different fixed def, can't merge" (that is captured by the incompatible-requirement check) but rather "corner case results in impossible problem".
The interesting case is when we have a fixed-use bound to preg P with liverange up to the early point on an inst, a fixed-def bound to preg P (same preg) at the late point on an inst, and that def'd vreg flows into a blockparam arg and over a backedge to the use's vreg. Then we try to merge the two vregs into one bundle (because we try this for every blockparam arg - blockparam def pair). But we rewrote the use to be an "any" use and put an anonymous reservation on preg P at the early-point only. So rejoining the first vreg (the use-side) to the whole bundle and thus requiring it to have the fixed-preg constraint again causes an impossible-to-solve problem.
There may be an even more complex way to rewrite this but I was several levels deep into "introduce fix, get deeper bug" and wanted to bail out of the rabbithole altogether...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is very conservative and is hurting our compiler quite a bit since it makes heavy use of fixed operands. Removing this check gives a 4% code size reduction (I enabled the checker to ensure the result is still valid).
What are the exact requirements that cause the impossible to resolve constraint? Could this check be made more specific?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really have details paged in anymore, beyond the paragraph describing a fuzzing-discovered example I wrote above, nor the cycles at the moment to think through it deeply. You're welcome to remove it and fuzz to (re)discover it, and propose a better fix, of course!