/src/regalloc2/src/ion/requirement.rs
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * This file was initially derived from the files |
3 | | * `js/src/jit/BacktrackingAllocator.h` and |
4 | | * `js/src/jit/BacktrackingAllocator.cpp` in Mozilla Firefox, and was |
5 | | * originally licensed under the Mozilla Public License 2.0. We |
6 | | * subsequently relicensed it to Apache-2.0 WITH LLVM-exception (see |
7 | | * https://github.com/bytecodealliance/regalloc2/issues/7). |
8 | | * |
9 | | * Since the initial port, the design has been substantially evolved |
10 | | * and optimized. |
11 | | */ |
12 | | |
13 | | //! Requirements computation. |
14 | | |
15 | | use super::{Env, LiveBundleIndex}; |
16 | | use crate::{Function, Inst, Operand, OperandConstraint, PReg, ProgPoint}; |
17 | | |
18 | | pub struct RequirementConflict; |
19 | | |
20 | | #[derive(Clone, Copy, Debug)] |
21 | | pub enum RequirementConflictAt { |
22 | | /// A transition from a stack-constrained to a reg-constrained |
23 | | /// segment. The suggested split point is late, to keep the |
24 | | /// intervening region with the stackslot (which is cheaper). |
25 | | StackToReg(ProgPoint), |
26 | | /// A transition from a reg-constraint to a stack-constrained |
27 | | /// segment. Mirror of above: the suggested split point is early |
28 | | /// (just after the last register use). |
29 | | RegToStack(ProgPoint), |
30 | | /// Any other transition. The suggested split point is late (just |
31 | | /// before the conflicting use), but the split will also trim the |
32 | | /// ends and create a split bundle, so the intervening region will |
33 | | /// not appear with either side. This is probably for the best |
34 | | /// when e.g. the two sides of the split are both constrained to |
35 | | /// different physical registers: the part in the middle should be |
36 | | /// constrained to neither. |
37 | | Other(ProgPoint), |
38 | | } |
39 | | |
40 | | impl RequirementConflictAt { |
41 | | #[inline(always)] |
42 | 214k | pub fn should_trim_edges_around_split(self) -> bool { |
43 | 214k | match self { |
44 | 117k | RequirementConflictAt::RegToStack(..) | RequirementConflictAt::StackToReg(..) => false, |
45 | 96.6k | RequirementConflictAt::Other(..) => true, |
46 | | } |
47 | 214k | } |
48 | | |
49 | | #[inline(always)] |
50 | 214k | pub fn suggested_split_point(self) -> ProgPoint { |
51 | 214k | match self { |
52 | 60.5k | RequirementConflictAt::RegToStack(pt) |
53 | 57.0k | | RequirementConflictAt::StackToReg(pt) |
54 | 214k | | RequirementConflictAt::Other(pt) => pt, |
55 | 214k | } |
56 | 214k | } |
57 | | } |
58 | | |
59 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
60 | | pub enum Requirement { |
61 | | FixedReg(PReg), |
62 | | FixedStack(PReg), |
63 | | Register, |
64 | | Any, |
65 | | } |
66 | | impl Requirement { |
67 | | #[inline(always)] |
68 | 11.8M | pub fn merge(self, other: Requirement) -> Result<Requirement, RequirementConflict> { |
69 | 11.8M | match (self, other) { |
70 | 8.05M | (other, Requirement::Any) | (Requirement::Any, other) => Ok(other), |
71 | 2.27M | (Requirement::Register, Requirement::Register) => Ok(self), |
72 | 101k | (Requirement::Register, Requirement::FixedReg(preg)) |
73 | 1.00M | | (Requirement::FixedReg(preg), Requirement::Register) => { |
74 | 1.10M | Ok(Requirement::FixedReg(preg)) |
75 | | } |
76 | 104k | (Requirement::FixedReg(a), Requirement::FixedReg(b)) if a == b => Ok(self), |
77 | 185k | (Requirement::FixedStack(a), Requirement::FixedStack(b)) if a == b => Ok(self), |
78 | 245k | _ => Err(RequirementConflict), |
79 | | } |
80 | 11.8M | } |
81 | | |
82 | | #[inline(always)] |
83 | 322k | pub fn is_stack(self) -> bool { |
84 | 322k | match self { |
85 | 240k | Requirement::FixedStack(..) => true, |
86 | 81.9k | Requirement::Register | Requirement::FixedReg(..) => false, |
87 | 0 | Requirement::Any => false, |
88 | | } |
89 | 322k | } |
90 | | |
91 | | #[inline(always)] |
92 | 343k | pub fn is_reg(self) -> bool { |
93 | 343k | match self { |
94 | 144k | Requirement::Register | Requirement::FixedReg(..) => true, |
95 | 198k | Requirement::FixedStack(..) => false, |
96 | 0 | Requirement::Any => false, |
97 | | } |
98 | 343k | } |
99 | | } |
100 | | |
101 | | impl<'a, F: Function> Env<'a, F> { |
102 | | #[inline(always)] |
103 | 11.8M | pub fn requirement_from_operand(&self, op: Operand) -> Requirement { |
104 | 11.8M | match op.constraint() { |
105 | 1.32M | OperandConstraint::FixedReg(preg) => { |
106 | 1.32M | if self.pregs[preg.index()].is_stack { |
107 | 900k | Requirement::FixedStack(preg) |
108 | | } else { |
109 | 419k | Requirement::FixedReg(preg) |
110 | | } |
111 | | } |
112 | 4.96M | OperandConstraint::Reg | OperandConstraint::Reuse(_) => Requirement::Register, |
113 | 5.54M | OperandConstraint::Any => Requirement::Any, |
114 | | } |
115 | 11.8M | } <regalloc2::ion::data_structures::Env<regalloc2::fuzzing::func::Func>>::requirement_from_operand Line | Count | Source | 103 | 11.8M | pub fn requirement_from_operand(&self, op: Operand) -> Requirement { | 104 | 11.8M | match op.constraint() { | 105 | 1.32M | OperandConstraint::FixedReg(preg) => { | 106 | 1.32M | if self.pregs[preg.index()].is_stack { | 107 | 900k | Requirement::FixedStack(preg) | 108 | | } else { | 109 | 419k | Requirement::FixedReg(preg) | 110 | | } | 111 | | } | 112 | 4.96M | OperandConstraint::Reg | OperandConstraint::Reuse(_) => Requirement::Register, | 113 | 5.54M | OperandConstraint::Any => Requirement::Any, | 114 | | } | 115 | 11.8M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::requirement_from_operand |
116 | | |
117 | 5.67M | pub fn compute_requirement( |
118 | 5.67M | &self, |
119 | 5.67M | bundle: LiveBundleIndex, |
120 | 5.67M | ) -> Result<Requirement, RequirementConflictAt> { |
121 | 5.67M | let mut req = Requirement::Any; |
122 | 5.67M | let mut last_pos = ProgPoint::before(Inst::new(0)); |
123 | 5.67M | trace!("compute_requirement: {:?}", bundle); |
124 | 5.67M | let ranges = &self.bundles[bundle].ranges; |
125 | 14.8M | for entry in ranges { |
126 | 9.44M | trace!(" -> LR {:?}: {:?}", entry.index, entry.range); |
127 | 11.8M | for u in &self.ranges[entry.index].uses { |
128 | 11.8M | trace!(" -> use {:?}", u); |
129 | 11.8M | let r = self.requirement_from_operand(u.operand); |
130 | 11.8M | req = req.merge(r).map_err(|_| { |
131 | 244k | trace!(" -> conflict"); |
132 | 244k | if req.is_stack() && r.is_reg() { |
133 | | // Suggested split point just before the reg (i.e., late split). |
134 | 66.5k | RequirementConflictAt::StackToReg(u.pos) |
135 | 177k | } else if req.is_reg() && r.is_stack() { |
136 | | // Suggested split point just after the stack |
137 | | // (i.e., early split). Note that splitting |
138 | | // with a use *right* at the beginning is |
139 | | // interpreted by `split_and_requeue_bundle` |
140 | | // as splitting off the first use. |
141 | 74.5k | RequirementConflictAt::RegToStack(last_pos) |
142 | | } else { |
143 | 102k | RequirementConflictAt::Other(u.pos) |
144 | | } |
145 | 11.8M | })?; <regalloc2::ion::data_structures::Env<regalloc2::fuzzing::func::Func>>::compute_requirement::{closure#0}Line | Count | Source | 130 | 244k | req = req.merge(r).map_err(|_| { | 131 | 244k | trace!(" -> conflict"); | 132 | 244k | if req.is_stack() && r.is_reg() { | 133 | | // Suggested split point just before the reg (i.e., late split). | 134 | 66.5k | RequirementConflictAt::StackToReg(u.pos) | 135 | 177k | } else if req.is_reg() && r.is_stack() { | 136 | | // Suggested split point just after the stack | 137 | | // (i.e., early split). Note that splitting | 138 | | // with a use *right* at the beginning is | 139 | | // interpreted by `split_and_requeue_bundle` | 140 | | // as splitting off the first use. | 141 | 74.5k | RequirementConflictAt::RegToStack(last_pos) | 142 | | } else { | 143 | 102k | RequirementConflictAt::Other(u.pos) | 144 | | } | 145 | 244k | })?; |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::compute_requirement::{closure#0} |
146 | 11.5M | last_pos = u.pos; |
147 | 11.5M | trace!(" -> req {:?}", req); |
148 | | } |
149 | | } |
150 | 5.43M | trace!(" -> final: {:?}", req); |
151 | 5.43M | Ok(req) |
152 | 5.67M | } <regalloc2::ion::data_structures::Env<regalloc2::fuzzing::func::Func>>::compute_requirement Line | Count | Source | 117 | 5.67M | pub fn compute_requirement( | 118 | 5.67M | &self, | 119 | 5.67M | bundle: LiveBundleIndex, | 120 | 5.67M | ) -> Result<Requirement, RequirementConflictAt> { | 121 | 5.67M | let mut req = Requirement::Any; | 122 | 5.67M | let mut last_pos = ProgPoint::before(Inst::new(0)); | 123 | 5.67M | trace!("compute_requirement: {:?}", bundle); | 124 | 5.67M | let ranges = &self.bundles[bundle].ranges; | 125 | 14.8M | for entry in ranges { | 126 | 9.44M | trace!(" -> LR {:?}: {:?}", entry.index, entry.range); | 127 | 11.8M | for u in &self.ranges[entry.index].uses { | 128 | 11.8M | trace!(" -> use {:?}", u); | 129 | 11.8M | let r = self.requirement_from_operand(u.operand); | 130 | 11.8M | req = req.merge(r).map_err(|_| { | 131 | | trace!(" -> conflict"); | 132 | | if req.is_stack() && r.is_reg() { | 133 | | // Suggested split point just before the reg (i.e., late split). | 134 | | RequirementConflictAt::StackToReg(u.pos) | 135 | | } else if req.is_reg() && r.is_stack() { | 136 | | // Suggested split point just after the stack | 137 | | // (i.e., early split). Note that splitting | 138 | | // with a use *right* at the beginning is | 139 | | // interpreted by `split_and_requeue_bundle` | 140 | | // as splitting off the first use. | 141 | | RequirementConflictAt::RegToStack(last_pos) | 142 | | } else { | 143 | | RequirementConflictAt::Other(u.pos) | 144 | | } | 145 | 11.8M | })?; | 146 | 11.5M | last_pos = u.pos; | 147 | 11.5M | trace!(" -> req {:?}", req); | 148 | | } | 149 | | } | 150 | 5.43M | trace!(" -> final: {:?}", req); | 151 | 5.43M | Ok(req) | 152 | 5.67M | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::compute_requirement |
153 | | |
154 | 67.8k | pub fn merge_bundle_requirements( |
155 | 67.8k | &self, |
156 | 67.8k | a: LiveBundleIndex, |
157 | 67.8k | b: LiveBundleIndex, |
158 | 67.8k | ) -> Result<Requirement, RequirementConflict> { |
159 | 67.8k | let req_a = self |
160 | 67.8k | .compute_requirement(a) |
161 | 67.8k | .map_err(|_| RequirementConflict)?; <regalloc2::ion::data_structures::Env<regalloc2::fuzzing::func::Func>>::merge_bundle_requirements::{closure#0}Line | Count | Source | 161 | 20.4k | .map_err(|_| RequirementConflict)?; |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundle_requirements::{closure#0} |
162 | 47.4k | let req_b = self |
163 | 47.4k | .compute_requirement(b) |
164 | 47.4k | .map_err(|_| RequirementConflict)?; <regalloc2::ion::data_structures::Env<regalloc2::fuzzing::func::Func>>::merge_bundle_requirements::{closure#1}Line | Count | Source | 164 | 9.33k | .map_err(|_| RequirementConflict)?; |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundle_requirements::{closure#1} |
165 | 38.1k | req_a.merge(req_b) |
166 | 67.8k | } <regalloc2::ion::data_structures::Env<regalloc2::fuzzing::func::Func>>::merge_bundle_requirements Line | Count | Source | 154 | 67.8k | pub fn merge_bundle_requirements( | 155 | 67.8k | &self, | 156 | 67.8k | a: LiveBundleIndex, | 157 | 67.8k | b: LiveBundleIndex, | 158 | 67.8k | ) -> Result<Requirement, RequirementConflict> { | 159 | 67.8k | let req_a = self | 160 | 67.8k | .compute_requirement(a) | 161 | 67.8k | .map_err(|_| RequirementConflict)?; | 162 | 47.4k | let req_b = self | 163 | 47.4k | .compute_requirement(b) | 164 | 47.4k | .map_err(|_| RequirementConflict)?; | 165 | 38.1k | req_a.merge(req_b) | 166 | 67.8k | } |
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundle_requirements |
167 | | } |