diff --git a/src/ion/merge.rs b/src/ion/merge.rs index dfdbe7d..b32a1b0 100644 --- a/src/ion/merge.rs +++ b/src/ion/merge.rs @@ -99,9 +99,10 @@ impl<'a, F: Function> Env<'a, F> { || self.bundles[to.index()].cached_stack() || self.bundles[to.index()].cached_fixed() { - let req_from = self.compute_requirement(from); - let req_to = self.compute_requirement(to); - let req = self.merge_requirement(req_from, req_to); + let req = self + .compute_requirement(from) + .0 + .merge(self.compute_requirement(to).0); if req == Requirement::Conflict { log::trace!(" -> conflicting requirements; aborting merge"); return false; diff --git a/src/ion/process.rs b/src/ion/process.rs index 9c29156..0afe894 100644 --- a/src/ion/process.rs +++ b/src/ion/process.rs @@ -367,29 +367,6 @@ impl<'a, F: Function> Env<'a, F> { } } - pub fn find_conflict_split_point(&self, bundle: LiveBundleIndex) -> ProgPoint { - // Find the first use whose requirement causes the merge up to - // this point to go to Conflict. - let mut req = Requirement::Unknown; - for entry in &self.bundles[bundle.index()].ranges { - for u in &self.ranges[entry.index.index()].uses { - let this_req = Requirement::from_operand(u.operand); - req = self.merge_requirement(req, this_req); - if req == Requirement::Conflict { - return u.pos; - } - } - } - - // Fallback: start of bundle. - self.bundles[bundle.index()] - .ranges - .first() - .unwrap() - .range - .from - } - pub fn get_or_create_spill_bundle( &mut self, bundle: LiveBundleIndex, @@ -752,7 +729,7 @@ impl<'a, F: Function> Env<'a, F> { reg_hint: PReg, ) -> Result<(), RegAllocError> { let class = self.spillsets[self.bundles[bundle.index()].spillset.index()].class; - let req = self.compute_requirement(bundle); + let (req, split_point) = self.compute_requirement(bundle); // Grab a hint from either the queue or our spillset, if any. let mut hint_reg = if reg_hint != PReg::invalid() { reg_hint @@ -772,7 +749,6 @@ impl<'a, F: Function> Env<'a, F> { !self.minimal_bundle(bundle), "Minimal bundle with conflict!" ); - let split_point = self.find_conflict_split_point(bundle); self.split_and_requeue_bundle( bundle, /* split_at_point = */ split_point, @@ -809,7 +785,7 @@ impl<'a, F: Function> Env<'a, F> { debug_assert!(attempts < 100 * self.func.num_insts()); let fixed_preg = match req { - Requirement::Fixed(preg) => Some(preg), + Requirement::FixedReg(preg) | Requirement::FixedStack(preg) => Some(preg), Requirement::Register => None, Requirement::Stack => { // If we must be on the stack, mark our spillset diff --git a/src/ion/requirement.rs b/src/ion/requirement.rs index e5c9c90..6e7920b 100644 --- a/src/ion/requirement.rs +++ b/src/ion/requirement.rs @@ -13,12 +13,13 @@ //! Requirements computation. use super::{Env, LiveBundleIndex}; -use crate::{Function, Operand, OperandConstraint, PReg}; +use crate::{Function, Operand, OperandConstraint, PReg, ProgPoint}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Requirement { Unknown, - Fixed(PReg), + FixedReg(PReg), + FixedStack(PReg), Register, Stack, Any, @@ -26,47 +27,58 @@ pub enum Requirement { } impl Requirement { #[inline(always)] - pub fn from_operand(op: Operand) -> Requirement { - match op.constraint() { - OperandConstraint::FixedReg(preg) => Requirement::Fixed(preg), - OperandConstraint::Reg | OperandConstraint::Reuse(_) => Requirement::Register, - OperandConstraint::Stack => Requirement::Stack, - _ => Requirement::Any, + pub fn merge(self, other: Requirement) -> Requirement { + match (self, other) { + (Requirement::Unknown, other) | (other, Requirement::Unknown) => other, + (Requirement::Conflict, _) | (_, Requirement::Conflict) => Requirement::Conflict, + (other, Requirement::Any) | (Requirement::Any, other) => other, + (Requirement::Register, Requirement::Register) => self, + (Requirement::Stack, Requirement::Stack) => self, + (Requirement::Register, Requirement::FixedReg(preg)) + | (Requirement::FixedReg(preg), Requirement::Register) => Requirement::FixedReg(preg), + (Requirement::Stack, Requirement::FixedStack(preg)) + | (Requirement::FixedStack(preg), Requirement::Stack) => Requirement::FixedStack(preg), + (Requirement::FixedReg(a), Requirement::FixedReg(b)) if a == b => self, + (Requirement::FixedStack(a), Requirement::FixedStack(b)) if a == b => self, + _ => Requirement::Conflict, } } } impl<'a, F: Function> Env<'a, F> { #[inline(always)] - pub fn merge_requirement(&self, a: Requirement, b: Requirement) -> Requirement { - match (a, b) { - (Requirement::Unknown, other) | (other, Requirement::Unknown) => other, - (Requirement::Conflict, _) | (_, Requirement::Conflict) => Requirement::Conflict, - (other, Requirement::Any) | (Requirement::Any, other) => other, - (Requirement::Stack, Requirement::Stack) => Requirement::Stack, - (Requirement::Register, Requirement::Register) => Requirement::Register, - (Requirement::Register, Requirement::Fixed(preg)) - | (Requirement::Fixed(preg), Requirement::Register) if !self.pregs[preg.index()].is_stack => Requirement::Fixed(preg), - (Requirement::Stack, Requirement::Fixed(preg)) - | (Requirement::Fixed(preg), Requirement::Stack) if self.pregs[preg.index()].is_stack => Requirement::Fixed(preg), - (Requirement::Fixed(a), Requirement::Fixed(b)) if a == b => Requirement::Fixed(a), - _ => Requirement::Conflict, + pub fn requirement_from_operand(&self, op: Operand) -> Requirement { + match op.constraint() { + OperandConstraint::FixedReg(preg) => { + if self.pregs[preg.index()].is_stack { + Requirement::FixedStack(preg) + } else { + Requirement::FixedReg(preg) + } + } + OperandConstraint::Reg | OperandConstraint::Reuse(_) => Requirement::Register, + OperandConstraint::Stack => Requirement::Stack, + _ => Requirement::Any, } } - pub fn compute_requirement(&self, bundle: LiveBundleIndex) -> Requirement { + pub fn compute_requirement(&self, bundle: LiveBundleIndex) -> (Requirement, ProgPoint) { let mut req = Requirement::Unknown; log::trace!("compute_requirement: {:?}", bundle); - for entry in &self.bundles[bundle.index()].ranges { + let ranges = &self.bundles[bundle.index()].ranges; + for entry in ranges { log::trace!(" -> LR {:?}", entry.index); for u in &self.ranges[entry.index.index()].uses { log::trace!(" -> use {:?}", u); - let r = Requirement::from_operand(u.operand); - req = self.merge_requirement(req, r); + let r = self.requirement_from_operand(u.operand); + req = req.merge(r); log::trace!(" -> req {:?}", req); + if req == Requirement::Conflict { + return (req, u.pos); + } } } log::trace!(" -> final: {:?}", req); - req + (req, ranges.first().unwrap().range.from) } }