Coverage Report

Created: 2025-01-06 07:43

/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
}