Skip to content

ion: preserve register limits through reuse and fixups - #267

Open
rahulchaphalkar wants to merge 1 commit into
bytecodealliance:mainfrom
rahulchaphalkar:ion-register-limits
Open

rahulchaphalkar wants to merge 1 commit into
bytecodealliance:mainfrom
rahulchaphalkar:ion-register-limits

Conversation

@rahulchaphalkar

Copy link
Copy Markdown
Contributor

Fix two Ion correctness issues discovered while testing register constraints for Cranelift’s APX/extended-register support. Both affect generic OperandConstraint::Limit handling.

Preserve limits through reuse. When an output reuses a limited input, propagate that input’s constraint into requirement and bundle calculations.
E.g. Previously, Ion could assign p30 to an input constrained by Limit(16).
Keep multi-fixed fixup sources stable. When a value has both limited and fixed uses, select the limited operand as the fixup source. Otherwise, another fixup can overwrite the selected source allocation, causing subsequent moves to read the wrong location.

@cfallin cfallin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. However I think this needs a lot more explanation, and/or exploration. Rather than write a test for one case, we need to update the fuzzer to generate the relevant constraint cases, and then we need to fuzz extensively before merging; otherwise, seemingly innocent point-fixes like this can often break other things (as I worry might be the case here -- see below).

For the specific problem of Reuse constraints that refer to other Limit-constrained vregs: we generally don't propagate constraints on a Reuse otherwise and things work fine -- I think we should be able to either rely on the usual constraint meet-pass on actual bundle merge, if the two halves are merged, or if not, we should change the way that the fixup-move emission + preprocessing logic on reuse constraints works. The fix here is brittle and I worry incorrect in some cases.

Comment thread src/fastalloc/tests.rs
}

#[test]
fn ion_limited_operand_with_multiple_fixed_uses() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the test in fastalloc named with ion?

Comment thread src/fastalloc/tests.rs
insts: vec![vec![op(Def, 0, FixedReg(p(31)))], uses.clone()],
}]);
let options = RegallocOptions {
algorithm: Algorithm::Ion,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see -- you put a test for Ion in fastalloc. Although it seems weird, the Ion backend actually doesn't have unit tests -- they're verbose and hard to write and maintain, and instead, we update the fuzzer to generate the cases we want to accept (and the crate is continuously fuzzed so this will quickly find issues).

Rather than add this test, can you update the fuzzing function generator to emit functions that hit the newly problematic cases?

Comment thread src/ion/liveranges.rs
}
OperandConstraint::Limit(max) => {
first_reg_slot.get_or_insert(u.slot);
first_reg_slot = Some(u.slot);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain (in a comment ideally) why we overwrite first_reg_slot each time we see a Limit, rather than save the first one?

If there is some lattice meet semantics we want here, e.g. Limit overrides Reg, we should do that explicitly rather than rely on visit order, which can be brittle...

Comment thread src/ion/merge.rs
for u in &self.ranges[entry.index].uses {
use OperandConstraint::*;
match u.operand.constraint() {
match self.operand_constraint(u.operand, u.pos.inst()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand why we need to "look through" Reuse constraints here -- we don't do that anywhere else, and it's likely to break things wrt minimal bundles, I suspect.

Why can't we propagate any constraints after we merge, if we merge?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants