Repository navigation
ion: preserve register limits through reuse and fixups #267
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
base: main
Are you sure you want to change the base?
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 |
|---|---|---|
|
|
@@ -7,6 +7,88 @@ use crate::{ | |
| use alloc::vec; | ||
| use alloc::vec::Vec; | ||
|
|
||
| #[test] | ||
| fn ion_limited_operands_and_reuse() { | ||
| let env = mach_env(32); | ||
| let function = RealFunction::new(vec![BlockBuildInfo { | ||
| insts: vec![ | ||
| vec![op(Def, 0, FixedReg(p(31)))], | ||
| vec![op(Def, 1, Limit(16))], | ||
| vec![op(Use, 0, Limit(16)), op(Use, 1, Reg), op(Def, 2, Reuse(0))], | ||
| vec![op(Use, 2, FixedReg(p(30))), op(Use, 1, Limit(8))], | ||
| vec![op(Use, 0, Limit(1)), op(Use, 2, Limit(16))], | ||
| ], | ||
| }]); | ||
| let options = RegallocOptions { | ||
| algorithm: Algorithm::Ion, | ||
| validate_ssa: true, | ||
| ..RegallocOptions::default() | ||
| }; | ||
| let output = run(&function, &env, &options).unwrap(); | ||
| for index in 0..function.num_insts() { | ||
| for (operand, allocation) in function | ||
| .inst_operands(i(index)) | ||
| .iter() | ||
| .zip(output.inst_allocs(i(index))) | ||
| { | ||
| if let Limit(limit) = operand.constraint() { | ||
| assert!( | ||
| allocation.as_reg().unwrap().hw_enc() < limit, | ||
| "instruction {}, {:?} -> {:?}", | ||
| index, | ||
| operand, | ||
| allocation | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| assert_eq!(output.inst_allocs(i(2))[0], output.inst_allocs(i(2))[2]); | ||
| #[cfg(feature = "checker")] | ||
| { | ||
| let mut checker = crate::checker::Checker::new(&function, &env); | ||
| checker.prepare(&output); | ||
| checker.run().unwrap(); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn ion_limited_operand_with_multiple_fixed_uses() { | ||
| let mut env = mach_env(32); | ||
| env.fixed_stack_slots = vec![p(38), p(53)]; | ||
| let mut uses = vec![ | ||
| op(Use, 0, FixedReg(p(16))), | ||
| op(Use, 0, FixedReg(p(53))), | ||
| op(Use, 0, Limit(16)), | ||
| op(Use, 0, FixedReg(p(38))), | ||
| op(Use, 0, Any), | ||
| ]; | ||
| for _ in 0..uses.len() { | ||
| uses.rotate_left(1); | ||
| let function = RealFunction::new(vec![BlockBuildInfo { | ||
| insts: vec![vec![op(Def, 0, FixedReg(p(31)))], uses.clone()], | ||
| }]); | ||
| let options = RegallocOptions { | ||
| algorithm: Algorithm::Ion, | ||
|
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. Ah, I see -- you put a test for Rather than add this test, can you update the fuzzing function generator to emit functions that hit the newly problematic cases? |
||
| validate_ssa: true, | ||
| ..RegallocOptions::default() | ||
| }; | ||
| let output = run(&function, &env, &options).unwrap(); | ||
| for (operand, allocation) in uses.iter().zip(output.inst_allocs(i(1))) { | ||
| match operand.constraint() { | ||
| FixedReg(preg) => assert_eq!(*allocation, alloc(preg)), | ||
| Limit(limit) => assert!(allocation.as_reg().unwrap().hw_enc() < limit), | ||
| _ => {} | ||
| } | ||
| } | ||
| #[cfg(feature = "checker")] | ||
| { | ||
| let mut checker = crate::checker::Checker::new(&function, &env); | ||
| checker.prepare(&output); | ||
| checker.run().unwrap(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_debug_locations1() { | ||
| let mach_env = mach_env(10); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -817,7 +817,7 @@ impl<'a, F: Function> Env<'a, F> { | |
| requires_reg = true; | ||
| } | ||
| OperandConstraint::Limit(max) => { | ||
| first_reg_slot.get_or_insert(u.slot); | ||
| first_reg_slot = Some(u.slot); | ||
|
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. Can you explain (in a comment ideally) why we overwrite If there is some lattice meet semantics we want here, e.g. |
||
| min_limit = min_limit.min(max); | ||
| requires_reg = true; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -287,7 +287,7 @@ impl<'a, F: Function> Env<'a, F> { | |
| for entry in &self.bundles[bundle].ranges { | ||
| for u in &self.ranges[entry.index].uses { | ||
| use OperandConstraint::*; | ||
| match u.operand.constraint() { | ||
| match self.operand_constraint(u.operand, u.pos.inst()) { | ||
|
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. I'm not sure I understand why we need to "look through" Why can't we propagate any constraints after we merge, if we merge? |
||
| FixedReg(_) => { | ||
| fixed = true; | ||
| if u.operand.kind() == OperandKind::Def { | ||
|
|
@@ -402,7 +402,7 @@ impl<'a, F: Function> Env<'a, F> { | |
| for entry in &self.bundles[bundle].ranges { | ||
| for u in &self.ranges[entry.index].uses { | ||
| use OperandConstraint::*; | ||
| match u.operand.constraint() { | ||
| match self.operand_constraint(u.operand, u.pos.inst()) { | ||
| Limit(current) => { | ||
| let current = u8::try_from(current) | ||
| .expect("the current limit is too large to fit in a u8"); | ||
|
|
||
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.
Why is the test in
fastallocnamed withion?