regalloc2/ion/
requirement.rs1use super::{Env, LiveBundleIndex};
16use crate::{Function, Inst, Operand, OperandConstraint, PReg, ProgPoint};
17
18pub struct RequirementConflict;
19
20#[derive(Clone, Copy, Debug)]
21pub enum RequirementConflictAt {
22 StackToReg(ProgPoint),
26 RegToStack(ProgPoint),
30 Other(ProgPoint),
38}
39
40impl RequirementConflictAt {
41 #[inline(always)]
42 pub fn should_trim_edges_around_split(self) -> bool {
43 match self {
44 RequirementConflictAt::RegToStack(..) | RequirementConflictAt::StackToReg(..) => false,
45 RequirementConflictAt::Other(..) => true,
46 }
47 }
48
49 #[inline(always)]
50 pub fn suggested_split_point(self) -> ProgPoint {
51 match self {
52 RequirementConflictAt::RegToStack(pt)
53 | RequirementConflictAt::StackToReg(pt)
54 | RequirementConflictAt::Other(pt) => pt,
55 }
56 }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub enum Requirement {
61 Any,
62 Register,
63 FixedReg(PReg),
64 Limit(usize),
65 Stack,
66 FixedStack(PReg),
67}
68impl Requirement {
69 #[inline(always)]
70 pub fn merge(self, other: Requirement) -> Result<Requirement, RequirementConflict> {
71 use Requirement::*;
72
73 match (self, other) {
74 (other, Any) | (Any, other) => Ok(other),
76 (Register, Register) => Ok(self),
78 (Stack, Stack) => Ok(self),
79 (Limit(a), Limit(b)) => Ok(Limit(a.min(b))),
80 (FixedReg(a), FixedReg(b)) if a == b => Ok(self),
81 (FixedStack(a), FixedStack(b)) if a == b => Ok(self),
82 (Limit(a), Register) | (Register, Limit(a)) => Ok(Limit(a)),
84 (Limit(a), FixedReg(b)) | (FixedReg(b), Limit(a)) if usize::from(a) > b.hw_enc() => {
85 Ok(FixedReg(b))
86 }
87 (Register, FixedReg(preg)) | (FixedReg(preg), Register) => Ok(FixedReg(preg)),
89 (Stack, FixedStack(preg)) | (FixedStack(preg), Stack) => Ok(FixedStack(preg)),
90 _ => Err(RequirementConflict),
92 }
93 }
94
95 #[inline(always)]
96 pub fn is_stack(self) -> bool {
97 match self {
98 Requirement::Stack | Requirement::FixedStack(..) => true,
99 Requirement::Register | Requirement::FixedReg(..) | Requirement::Limit(..) => false,
100 Requirement::Any => false,
101 }
102 }
103
104 #[inline(always)]
105 pub fn is_reg(self) -> bool {
106 match self {
107 Requirement::Register | Requirement::FixedReg(..) | Requirement::Limit(..) => true,
108 Requirement::Stack | Requirement::FixedStack(..) => false,
109 Requirement::Any => false,
110 }
111 }
112}
113
114impl<'a, F: Function> Env<'a, F> {
115 #[inline(always)]
116 pub fn requirement_from_operand(&self, op: Operand) -> Requirement {
117 match op.constraint() {
118 OperandConstraint::FixedReg(preg) => {
119 if self.pregs[preg.index()].is_stack {
120 Requirement::FixedStack(preg)
121 } else {
122 Requirement::FixedReg(preg)
123 }
124 }
125 OperandConstraint::Reg | OperandConstraint::Reuse(_) => Requirement::Register,
126 OperandConstraint::Limit(max) => Requirement::Limit(max),
127 OperandConstraint::Stack => Requirement::Stack,
128 OperandConstraint::Any => Requirement::Any,
129 }
130 }
131
132 pub fn compute_requirement(
133 &self,
134 bundle: LiveBundleIndex,
135 ) -> Result<Requirement, RequirementConflictAt> {
136 let mut req = Requirement::Any;
137 let mut last_pos = ProgPoint::before(Inst::new(0));
138 trace!("compute_requirement: {:?}", bundle);
139 let ranges = &self.bundles[bundle].ranges;
140 for entry in ranges {
141 trace!(" -> LR {:?}: {:?}", entry.index, entry.range);
142 for u in &self.ranges[entry.index].uses {
143 trace!(" -> use {:?}", u);
144 let r = self.requirement_from_operand(u.operand);
145 req = req.merge(r).map_err(|_| {
146 trace!(" -> conflict");
147 if req.is_stack() && r.is_reg() {
148 RequirementConflictAt::StackToReg(u.pos)
150 } else if req.is_reg() && r.is_stack() {
151 RequirementConflictAt::RegToStack(last_pos)
157 } else {
158 RequirementConflictAt::Other(u.pos)
159 }
160 })?;
161 last_pos = u.pos;
162 trace!(" -> req {:?}", req);
163 }
164 }
165 trace!(" -> final: {:?}", req);
166 Ok(req)
167 }
168
169 pub fn merge_bundle_requirements(
170 &self,
171 a: LiveBundleIndex,
172 b: LiveBundleIndex,
173 ) -> Result<Requirement, RequirementConflict> {
174 let req_a = self
175 .compute_requirement(a)
176 .map_err(|_| RequirementConflict)?;
177 let req_b = self
178 .compute_requirement(b)
179 .map_err(|_| RequirementConflict)?;
180 req_a.merge(req_b)
181 }
182}