-
Notifications
You must be signed in to change notification settings - Fork 45
Evict larger limits first using spill weights #249
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
Conversation
Consider an instruction that could potentially overallocate to a limited range of registers: ``` Use: v0i limit(0..=1), Use: v0i limit(0..=1), Use: v1i limit(0..=7) ``` Both uses of `v0` _must_ fit in registers 0-1 and `v1` should find its home somewhere else in registers 2-7. But fuzzing found a case where this failed. If `v1` was allocated first--say to register 0--and one of the uses of `v0` allocated to the remaining register--register 1--the final use of `v0` had nowhere to go _and_ could not evict either of the previous allocations. This caused `ion` to fail with `TooManyLiveRegs` when in fact a solution was possible. (Why are the identical uses of `v0` allocated separately? Can't they use the same register? I'm not entirely sure, but I think `ion` had split things down to minimal bundles). The fix in this change is to alter the default spill weights assigned to a minimal bundle. Previously, minimal bundles with a fixed constraint received the highest weight, followed by other minimal bundles and then non-minimal bundles. This reserves weights for limits into that order: - minimal with `fixed` constraint - minimal with `limit(0..=0)` constraint - minimal with `limit(0..=1)` constraint - ... - minimal with `limit(0..=255)` constraint - minimal, any other constraint - non-minimal Doing this allows `ion` to evict bundles with higher limits (e.g., `v1i limit(0..=7)` once they become minimal, allowing allocation to continue. Co-authored-by: Chris Fallin <chris@cfallin.org> Co-authored-by: Rahul Chaphalkar <rahul.s.chaphalkar@intel.com>
|
No hurry to merge this just yet; I'm running this in my branch that enables fuzzing of limits: |
|
Also, if it is helpful, I can include an extra commit here with a unit test that runs the exact test case that caused the original problem: |
cfallin
left a comment
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.
LGTM (no surprise since we pair-programmed this fix) -- thanks a bunch for working on this!
Regarding the unit test for this case -- I guess it depends on whether the fuzzer would cover this (ie the reason we don't have a test suite otherwise, since the fuzzer coverage is so good). This case was originally discovered via fuzzing and the original fuzzbug is now fixed? If so then I think it's fine; otherwise it might be good to throw in an explicit test. Thanks!
|
It's been a bit and this change still looks right to me, so I'm going to go ahead and take this out of draft mode and merge. Thanks again! |
Includes bytecodealliance#249.
|
@cfallin, apologies for not following up with the fuzzing results. I did run fuzzing for this change using fuzz-limit-constraints-rebased and found a fuzz bug that was unrelated to this change. IIRC, the test case did not even have |
|
After looking around I cannot find the |
Consider an instruction that could potentially overallocate to a limited range of registers:
Both uses of
v0must fit in registers 0-1 andv1should find its home somewhere else in registers 2-7. But fuzzing found a case where this failed. Ifv1was allocated first--say to register 0--and one of the uses ofv0allocated to the remaining register--register 1--the final use ofv0had nowhere to go and could not evict either of the previous allocations. This causedionto fail withTooManyLiveRegswhen in fact a solution was possible.(Why are the identical uses of
v0allocated separately? Can't they use the same register? I'm not entirely sure, but I thinkionhad split things down to minimal bundles).The fix in this change is to alter the default spill weights assigned to a minimal bundle. Previously, minimal bundles with a fixed constraint received the highest weight, followed by other minimal bundles and then non-minimal bundles. This reserves weights for limits into that order:
fixedconstraintlimit(0..=0)constraintlimit(0..=1)constraintlimit(0..=255)constraintDoing this allows
ionto evict bundles with higher limits (e.g.,v1i limit(0..=7)once they become minimal, allowing allocation to continue.