Coverage Report

Created: 2026-02-14 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/regalloc2-0.13.5/src/ion/merge.rs
Line
Count
Source
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
//! Bundle merging.
14
15
use crate::ion::data_structures::{
16
    BlockparamOut, CodeRange, Env, LiveBundleIndex, LiveRangeList, SpillSet, SpillSlotIndex,
17
    VRegIndex,
18
};
19
use crate::{Function, Inst, OperandConstraint, OperandKind, PReg, ProgPoint};
20
use alloc::format;
21
use core::convert::TryFrom;
22
23
impl<'a, F: Function> Env<'a, F> {
24
958k
    fn merge_bundle_properties(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) {
25
958k
        if self.bundles[from].cached_fixed() {
26
81.2k
            self.bundles[to].set_cached_fixed();
27
877k
        }
28
958k
        if self.bundles[from].cached_fixed_def() {
29
78.7k
            self.bundles[to].set_cached_fixed_def();
30
880k
        }
31
958k
        if self.bundles[from].cached_stack() {
32
0
            self.bundles[to].set_cached_stack();
33
958k
        }
34
958k
        if let Some(theirs) = self.bundles[from].limit {
35
0
            match self.bundles[to].limit {
36
0
                Some(ours) => self.bundles[to].limit = Some(ours.min(theirs)),
37
0
                None => self.bundles[to].limit = Some(theirs),
38
            }
39
958k
        }
40
958k
    }
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundle_properties
Line
Count
Source
24
106k
    fn merge_bundle_properties(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) {
25
106k
        if self.bundles[from].cached_fixed() {
26
9.58k
            self.bundles[to].set_cached_fixed();
27
96.9k
        }
28
106k
        if self.bundles[from].cached_fixed_def() {
29
9.54k
            self.bundles[to].set_cached_fixed_def();
30
97.0k
        }
31
106k
        if self.bundles[from].cached_stack() {
32
0
            self.bundles[to].set_cached_stack();
33
106k
        }
34
106k
        if let Some(theirs) = self.bundles[from].limit {
35
0
            match self.bundles[to].limit {
36
0
                Some(ours) => self.bundles[to].limit = Some(ours.min(theirs)),
37
0
                None => self.bundles[to].limit = Some(theirs),
38
            }
39
106k
        }
40
106k
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundle_properties
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundle_properties
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundle_properties
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundle_properties
Line
Count
Source
24
852k
    fn merge_bundle_properties(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) {
25
852k
        if self.bundles[from].cached_fixed() {
26
71.6k
            self.bundles[to].set_cached_fixed();
27
780k
        }
28
852k
        if self.bundles[from].cached_fixed_def() {
29
69.1k
            self.bundles[to].set_cached_fixed_def();
30
783k
        }
31
852k
        if self.bundles[from].cached_stack() {
32
0
            self.bundles[to].set_cached_stack();
33
852k
        }
34
852k
        if let Some(theirs) = self.bundles[from].limit {
35
0
            match self.bundles[to].limit {
36
0
                Some(ours) => self.bundles[to].limit = Some(ours.min(theirs)),
37
0
                None => self.bundles[to].limit = Some(theirs),
38
            }
39
852k
        }
40
852k
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundle_properties
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundle_properties
41
42
1.14M
    pub fn merge_bundles(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) -> bool {
43
1.14M
        if from == to {
44
            // Merge bundle into self -- trivial merge.
45
800
            return true;
46
1.13M
        }
47
1.13M
        trace!(
48
            "merging from bundle{} to bundle{}",
49
0
            from.index(),
50
0
            to.index()
51
        );
52
53
        // Both bundles must deal with the same RegClass.
54
1.13M
        let from_rc = self.spillsets[self.bundles[from].spillset].class;
55
1.13M
        let to_rc = self.spillsets[self.bundles[to].spillset].class;
56
1.13M
        if from_rc != to_rc {
57
0
            trace!(" -> mismatching reg classes");
58
0
            return false;
59
1.13M
        }
60
61
        // If either bundle is already assigned (due to a pinned vreg), don't merge.
62
1.13M
        if self.bundles[from].allocation.is_some() || self.bundles[to].allocation.is_some() {
63
0
            trace!("one of the bundles is already assigned (pinned)");
64
0
            return false;
65
1.13M
        }
66
67
        #[cfg(debug_assertions)]
68
        {
69
            // Sanity check: both bundles should contain only ranges with appropriate VReg classes.
70
            for entry in &self.bundles[from].ranges {
71
                let vreg = self.ranges[entry.index].vreg;
72
                debug_assert_eq!(from_rc, self.vreg(vreg).class());
73
            }
74
            for entry in &self.bundles[to].ranges {
75
                let vreg = self.ranges[entry.index].vreg;
76
                debug_assert_eq!(to_rc, self.vreg(vreg).class());
77
            }
78
        }
79
80
        // If a bundle has a fixed-reg def then we need to be careful to not
81
        // extend the bundle to include another use in the same instruction.
82
        // This could result in a minimal bundle that is impossible to split.
83
        //
84
        // This can only happen with an early use and a late def, so we round
85
        // the start of each range containing a fixed def up to the start of
86
        // its instruction to detect overlaps.
87
3.36M
        let adjust_range_start = |bundle_idx, range: CodeRange| {
88
3.36M
            if self.bundles[bundle_idx].cached_fixed_def() {
89
141k
                ProgPoint::before(range.from.inst())
90
            } else {
91
3.22M
                range.from
92
            }
93
3.36M
        };
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}
Line
Count
Source
87
274k
        let adjust_range_start = |bundle_idx, range: CodeRange| {
88
274k
            if self.bundles[bundle_idx].cached_fixed_def() {
89
10.6k
                ProgPoint::before(range.from.inst())
90
            } else {
91
264k
                range.from
92
            }
93
274k
        };
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundles::{closure#0}
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}
Line
Count
Source
87
3.09M
        let adjust_range_start = |bundle_idx, range: CodeRange| {
88
3.09M
            if self.bundles[bundle_idx].cached_fixed_def() {
89
130k
                ProgPoint::before(range.from.inst())
90
            } else {
91
2.96M
                range.from
92
            }
93
3.09M
        };
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundles::{closure#0}
94
95
        // Check for overlap in LiveRanges and for conflicting
96
        // requirements.
97
1.13M
        let ranges_from = &self.bundles[from].ranges[..];
98
1.13M
        let ranges_to = &self.bundles[to].ranges[..];
99
1.13M
        let mut idx_from = 0;
100
1.13M
        let mut idx_to = 0;
101
1.13M
        let mut range_count = 0;
102
2.70M
        while idx_from < ranges_from.len() && idx_to < ranges_to.len() {
103
1.74M
            range_count += 1;
104
1.74M
            if range_count > 200 {
105
113
                trace!(
106
                    "reached merge complexity (range_count = {}); exiting",
107
                    range_count
108
                );
109
                // Limit merge complexity.
110
113
                return false;
111
1.74M
            }
112
113
1.74M
            if adjust_range_start(from, ranges_from[idx_from].range) >= ranges_to[idx_to].range.to {
114
112k
                idx_to += 1;
115
1.62M
            } else if adjust_range_start(to, ranges_to[idx_to].range)
116
1.62M
                >= ranges_from[idx_from].range.to
117
1.45M
            {
118
1.45M
                idx_from += 1;
119
1.45M
            } else {
120
                // Overlap -- cannot merge.
121
175k
                trace!(
122
                    " -> overlap between {:?} and {:?}, exiting",
123
0
                    ranges_from[idx_from].index,
124
0
                    ranges_to[idx_to].index
125
                );
126
175k
                return false;
127
            }
128
        }
129
130
        // Check for a requirements conflict.
131
963k
        if self.bundles[from].cached_stack()
132
963k
            || self.bundles[from].cached_fixed()
133
877k
            || self.bundles[from].limit.is_some()
134
877k
            || self.bundles[to].cached_stack()
135
877k
            || self.bundles[to].cached_fixed()
136
693k
            || self.bundles[to].limit.is_some()
137
        {
138
270k
            if self.merge_bundle_requirements(from, to).is_err() {
139
4.52k
                trace!(" -> conflicting requirements; aborting merge");
140
4.52k
                return false;
141
265k
            }
142
693k
        }
143
144
958k
        trace!(" -> committing to merge");
145
146
        // If we reach here, then the bundles do not overlap -- merge
147
        // them!  We do this with a merge-sort-like scan over both
148
        // lists, building a new range list and replacing the list on
149
        // `to` when we're done.
150
958k
        if ranges_from.is_empty() {
151
            // `from` bundle is empty -- trivial merge.
152
0
            trace!(" -> from bundle{} is empty; trivial merge", from.index());
153
0
            return true;
154
958k
        }
155
958k
        if ranges_to.is_empty() {
156
            // `to` bundle is empty -- just move the list over from
157
            // `from` and set `bundle` up-link on all ranges.
158
0
            trace!(" -> to bundle{} is empty; trivial merge", to.index());
159
0
            let empty_vec = LiveRangeList::new_in(self.ctx.bump());
160
0
            let list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
161
0
            for entry in &list {
162
0
                self.ranges[entry.index].bundle = to;
163
164
0
                if self.annotations_enabled {
165
0
                    self.annotate(
166
0
                        entry.range.from,
167
0
                        format!(
168
0
                            " MERGE range{} v{} from bundle{} to bundle{}",
169
0
                            entry.index.index(),
170
0
                            self.ranges[entry.index].vreg.index(),
171
0
                            from.index(),
172
0
                            to.index(),
173
0
                        ),
174
0
                    );
175
0
                }
176
            }
177
0
            self.bundles[to].ranges = list;
178
0
            self.merge_bundle_properties(from, to);
179
0
            return true;
180
958k
        }
181
182
958k
        trace!(
183
            "merging: ranges_from = {:?} ranges_to = {:?}",
184
            ranges_from,
185
            ranges_to
186
        );
187
188
958k
        let empty_vec = LiveRangeList::new_in(self.ctx.bump());
189
958k
        let mut from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
190
1.33M
        for entry in &from_list {
191
1.33M
            self.ranges[entry.index].bundle = to;
192
1.33M
        }
193
194
958k
        if from_list.len() == 1 {
195
            // Optimize for the common case where `from_list` contains a single
196
            // item. Using a binary search to find the insertion point and then
197
            // calling `insert` is more efficient than re-sorting the entire
198
            // list, specially after the changes in sorting algorithms introduced
199
            // in rustc 1.81.
200
            // See: https://github.com/bytecodealliance/regalloc2/issues/203
201
766k
            let single_entry = from_list.pop().unwrap();
202
766k
            let pos = self.bundles[to]
203
766k
                .ranges
204
766k
                .binary_search_by_key(&single_entry.range.from, |entry| entry.range.from)
205
766k
                .unwrap_or_else(|pos| pos);
206
766k
            self.bundles[to].ranges.insert(pos, single_entry);
207
        } else {
208
            // Two non-empty lists of LiveRanges: concatenate and sort. This is
209
            // faster than a mergesort-like merge into a new list, empirically.
210
192k
            self.bundles[to].ranges.extend_from_slice(&from_list[..]);
211
192k
            self.bundles[to]
212
192k
                .ranges
213
192k
                .sort_unstable_by_key(|entry| entry.range.from);
214
        }
215
216
958k
        if self.annotations_enabled {
217
0
            trace!("merging: merged = {:?}", self.bundles[to].ranges);
218
0
            let mut last_range = None;
219
0
            for i in 0..self.bundles[to].ranges.len() {
220
0
                let entry = self.bundles[to].ranges[i];
221
0
                if last_range.is_some() {
222
0
                    debug_assert!(last_range.unwrap() < entry.range);
223
0
                }
224
0
                last_range = Some(entry.range);
225
226
0
                if self.ranges[entry.index].bundle == from {
227
0
                    self.annotate(
228
0
                        entry.range.from,
229
0
                        format!(
230
0
                            " MERGE range{} v{} from bundle{} to bundle{}",
231
0
                            entry.index.index(),
232
0
                            self.ranges[entry.index].vreg.index(),
233
0
                            from.index(),
234
0
                            to.index(),
235
0
                        ),
236
0
                    );
237
0
                }
238
239
0
                trace!(
240
                    " -> merged result for bundle{}: range{}",
241
0
                    to.index(),
242
0
                    entry.index.index(),
243
                );
244
            }
245
958k
        }
246
247
958k
        if self.bundles[from].spillset != self.bundles[to].spillset {
248
958k
            // Widen the range for the target spillset to include the one being merged in.
249
958k
            let from_range = self.spillsets[self.bundles[from].spillset].range;
250
958k
            let to_range = &mut self.ctx.spillsets[self.ctx.bundles[to].spillset].range;
251
958k
            *to_range = to_range.join(from_range);
252
958k
        }
253
254
958k
        self.merge_bundle_properties(from, to);
255
256
958k
        true
257
1.14M
    }
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundles
Line
Count
Source
42
113k
    pub fn merge_bundles(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) -> bool {
43
113k
        if from == to {
44
            // Merge bundle into self -- trivial merge.
45
8
            return true;
46
113k
        }
47
113k
        trace!(
48
            "merging from bundle{} to bundle{}",
49
0
            from.index(),
50
0
            to.index()
51
        );
52
53
        // Both bundles must deal with the same RegClass.
54
113k
        let from_rc = self.spillsets[self.bundles[from].spillset].class;
55
113k
        let to_rc = self.spillsets[self.bundles[to].spillset].class;
56
113k
        if from_rc != to_rc {
57
0
            trace!(" -> mismatching reg classes");
58
0
            return false;
59
113k
        }
60
61
        // If either bundle is already assigned (due to a pinned vreg), don't merge.
62
113k
        if self.bundles[from].allocation.is_some() || self.bundles[to].allocation.is_some() {
63
0
            trace!("one of the bundles is already assigned (pinned)");
64
0
            return false;
65
113k
        }
66
67
        #[cfg(debug_assertions)]
68
        {
69
            // Sanity check: both bundles should contain only ranges with appropriate VReg classes.
70
            for entry in &self.bundles[from].ranges {
71
                let vreg = self.ranges[entry.index].vreg;
72
                debug_assert_eq!(from_rc, self.vreg(vreg).class());
73
            }
74
            for entry in &self.bundles[to].ranges {
75
                let vreg = self.ranges[entry.index].vreg;
76
                debug_assert_eq!(to_rc, self.vreg(vreg).class());
77
            }
78
        }
79
80
        // If a bundle has a fixed-reg def then we need to be careful to not
81
        // extend the bundle to include another use in the same instruction.
82
        // This could result in a minimal bundle that is impossible to split.
83
        //
84
        // This can only happen with an early use and a late def, so we round
85
        // the start of each range containing a fixed def up to the start of
86
        // its instruction to detect overlaps.
87
113k
        let adjust_range_start = |bundle_idx, range: CodeRange| {
88
            if self.bundles[bundle_idx].cached_fixed_def() {
89
                ProgPoint::before(range.from.inst())
90
            } else {
91
                range.from
92
            }
93
        };
94
95
        // Check for overlap in LiveRanges and for conflicting
96
        // requirements.
97
113k
        let ranges_from = &self.bundles[from].ranges[..];
98
113k
        let ranges_to = &self.bundles[to].ranges[..];
99
113k
        let mut idx_from = 0;
100
113k
        let mut idx_to = 0;
101
113k
        let mut range_count = 0;
102
248k
        while idx_from < ranges_from.len() && idx_to < ranges_to.len() {
103
141k
            range_count += 1;
104
141k
            if range_count > 200 {
105
0
                trace!(
106
                    "reached merge complexity (range_count = {}); exiting",
107
                    range_count
108
                );
109
                // Limit merge complexity.
110
0
                return false;
111
141k
            }
112
113
141k
            if adjust_range_start(from, ranges_from[idx_from].range) >= ranges_to[idx_to].range.to {
114
8.65k
                idx_to += 1;
115
133k
            } else if adjust_range_start(to, ranges_to[idx_to].range)
116
133k
                >= ranges_from[idx_from].range.to
117
125k
            {
118
125k
                idx_from += 1;
119
125k
            } else {
120
                // Overlap -- cannot merge.
121
7.32k
                trace!(
122
                    " -> overlap between {:?} and {:?}, exiting",
123
0
                    ranges_from[idx_from].index,
124
0
                    ranges_to[idx_to].index
125
                );
126
7.32k
                return false;
127
            }
128
        }
129
130
        // Check for a requirements conflict.
131
106k
        if self.bundles[from].cached_stack()
132
106k
            || self.bundles[from].cached_fixed()
133
96.9k
            || self.bundles[from].limit.is_some()
134
96.9k
            || self.bundles[to].cached_stack()
135
96.9k
            || self.bundles[to].cached_fixed()
136
70.6k
            || self.bundles[to].limit.is_some()
137
        {
138
35.8k
            if self.merge_bundle_requirements(from, to).is_err() {
139
26
                trace!(" -> conflicting requirements; aborting merge");
140
26
                return false;
141
35.8k
            }
142
70.6k
        }
143
144
106k
        trace!(" -> committing to merge");
145
146
        // If we reach here, then the bundles do not overlap -- merge
147
        // them!  We do this with a merge-sort-like scan over both
148
        // lists, building a new range list and replacing the list on
149
        // `to` when we're done.
150
106k
        if ranges_from.is_empty() {
151
            // `from` bundle is empty -- trivial merge.
152
0
            trace!(" -> from bundle{} is empty; trivial merge", from.index());
153
0
            return true;
154
106k
        }
155
106k
        if ranges_to.is_empty() {
156
            // `to` bundle is empty -- just move the list over from
157
            // `from` and set `bundle` up-link on all ranges.
158
0
            trace!(" -> to bundle{} is empty; trivial merge", to.index());
159
0
            let empty_vec = LiveRangeList::new_in(self.ctx.bump());
160
0
            let list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
161
0
            for entry in &list {
162
0
                self.ranges[entry.index].bundle = to;
163
164
0
                if self.annotations_enabled {
165
0
                    self.annotate(
166
0
                        entry.range.from,
167
0
                        format!(
168
0
                            " MERGE range{} v{} from bundle{} to bundle{}",
169
0
                            entry.index.index(),
170
0
                            self.ranges[entry.index].vreg.index(),
171
0
                            from.index(),
172
0
                            to.index(),
173
0
                        ),
174
0
                    );
175
0
                }
176
            }
177
0
            self.bundles[to].ranges = list;
178
0
            self.merge_bundle_properties(from, to);
179
0
            return true;
180
106k
        }
181
182
106k
        trace!(
183
            "merging: ranges_from = {:?} ranges_to = {:?}",
184
            ranges_from,
185
            ranges_to
186
        );
187
188
106k
        let empty_vec = LiveRangeList::new_in(self.ctx.bump());
189
106k
        let mut from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
190
125k
        for entry in &from_list {
191
125k
            self.ranges[entry.index].bundle = to;
192
125k
        }
193
194
106k
        if from_list.len() == 1 {
195
            // Optimize for the common case where `from_list` contains a single
196
            // item. Using a binary search to find the insertion point and then
197
            // calling `insert` is more efficient than re-sorting the entire
198
            // list, specially after the changes in sorting algorithms introduced
199
            // in rustc 1.81.
200
            // See: https://github.com/bytecodealliance/regalloc2/issues/203
201
88.9k
            let single_entry = from_list.pop().unwrap();
202
88.9k
            let pos = self.bundles[to]
203
88.9k
                .ranges
204
88.9k
                .binary_search_by_key(&single_entry.range.from, |entry| entry.range.from)
205
88.9k
                .unwrap_or_else(|pos| pos);
206
88.9k
            self.bundles[to].ranges.insert(pos, single_entry);
207
        } else {
208
            // Two non-empty lists of LiveRanges: concatenate and sort. This is
209
            // faster than a mergesort-like merge into a new list, empirically.
210
17.6k
            self.bundles[to].ranges.extend_from_slice(&from_list[..]);
211
17.6k
            self.bundles[to]
212
17.6k
                .ranges
213
17.6k
                .sort_unstable_by_key(|entry| entry.range.from);
214
        }
215
216
106k
        if self.annotations_enabled {
217
0
            trace!("merging: merged = {:?}", self.bundles[to].ranges);
218
0
            let mut last_range = None;
219
0
            for i in 0..self.bundles[to].ranges.len() {
220
0
                let entry = self.bundles[to].ranges[i];
221
0
                if last_range.is_some() {
222
0
                    debug_assert!(last_range.unwrap() < entry.range);
223
0
                }
224
0
                last_range = Some(entry.range);
225
226
0
                if self.ranges[entry.index].bundle == from {
227
0
                    self.annotate(
228
0
                        entry.range.from,
229
0
                        format!(
230
0
                            " MERGE range{} v{} from bundle{} to bundle{}",
231
0
                            entry.index.index(),
232
0
                            self.ranges[entry.index].vreg.index(),
233
0
                            from.index(),
234
0
                            to.index(),
235
0
                        ),
236
0
                    );
237
0
                }
238
239
0
                trace!(
240
                    " -> merged result for bundle{}: range{}",
241
0
                    to.index(),
242
0
                    entry.index.index(),
243
                );
244
            }
245
106k
        }
246
247
106k
        if self.bundles[from].spillset != self.bundles[to].spillset {
248
106k
            // Widen the range for the target spillset to include the one being merged in.
249
106k
            let from_range = self.spillsets[self.bundles[from].spillset].range;
250
106k
            let to_range = &mut self.ctx.spillsets[self.ctx.bundles[to].spillset].range;
251
106k
            *to_range = to_range.join(from_range);
252
106k
        }
253
254
106k
        self.merge_bundle_properties(from, to);
255
256
106k
        true
257
113k
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_bundles
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_bundles
Line
Count
Source
42
1.02M
    pub fn merge_bundles(&mut self, from: LiveBundleIndex, to: LiveBundleIndex) -> bool {
43
1.02M
        if from == to {
44
            // Merge bundle into self -- trivial merge.
45
792
            return true;
46
1.02M
        }
47
1.02M
        trace!(
48
            "merging from bundle{} to bundle{}",
49
0
            from.index(),
50
0
            to.index()
51
        );
52
53
        // Both bundles must deal with the same RegClass.
54
1.02M
        let from_rc = self.spillsets[self.bundles[from].spillset].class;
55
1.02M
        let to_rc = self.spillsets[self.bundles[to].spillset].class;
56
1.02M
        if from_rc != to_rc {
57
0
            trace!(" -> mismatching reg classes");
58
0
            return false;
59
1.02M
        }
60
61
        // If either bundle is already assigned (due to a pinned vreg), don't merge.
62
1.02M
        if self.bundles[from].allocation.is_some() || self.bundles[to].allocation.is_some() {
63
0
            trace!("one of the bundles is already assigned (pinned)");
64
0
            return false;
65
1.02M
        }
66
67
        #[cfg(debug_assertions)]
68
        {
69
            // Sanity check: both bundles should contain only ranges with appropriate VReg classes.
70
            for entry in &self.bundles[from].ranges {
71
                let vreg = self.ranges[entry.index].vreg;
72
                debug_assert_eq!(from_rc, self.vreg(vreg).class());
73
            }
74
            for entry in &self.bundles[to].ranges {
75
                let vreg = self.ranges[entry.index].vreg;
76
                debug_assert_eq!(to_rc, self.vreg(vreg).class());
77
            }
78
        }
79
80
        // If a bundle has a fixed-reg def then we need to be careful to not
81
        // extend the bundle to include another use in the same instruction.
82
        // This could result in a minimal bundle that is impossible to split.
83
        //
84
        // This can only happen with an early use and a late def, so we round
85
        // the start of each range containing a fixed def up to the start of
86
        // its instruction to detect overlaps.
87
1.02M
        let adjust_range_start = |bundle_idx, range: CodeRange| {
88
            if self.bundles[bundle_idx].cached_fixed_def() {
89
                ProgPoint::before(range.from.inst())
90
            } else {
91
                range.from
92
            }
93
        };
94
95
        // Check for overlap in LiveRanges and for conflicting
96
        // requirements.
97
1.02M
        let ranges_from = &self.bundles[from].ranges[..];
98
1.02M
        let ranges_to = &self.bundles[to].ranges[..];
99
1.02M
        let mut idx_from = 0;
100
1.02M
        let mut idx_to = 0;
101
1.02M
        let mut range_count = 0;
102
2.45M
        while idx_from < ranges_from.len() && idx_to < ranges_to.len() {
103
1.59M
            range_count += 1;
104
1.59M
            if range_count > 200 {
105
113
                trace!(
106
                    "reached merge complexity (range_count = {}); exiting",
107
                    range_count
108
                );
109
                // Limit merge complexity.
110
113
                return false;
111
1.59M
            }
112
113
1.59M
            if adjust_range_start(from, ranges_from[idx_from].range) >= ranges_to[idx_to].range.to {
114
103k
                idx_to += 1;
115
1.49M
            } else if adjust_range_start(to, ranges_to[idx_to].range)
116
1.49M
                >= ranges_from[idx_from].range.to
117
1.32M
            {
118
1.32M
                idx_from += 1;
119
1.32M
            } else {
120
                // Overlap -- cannot merge.
121
168k
                trace!(
122
                    " -> overlap between {:?} and {:?}, exiting",
123
0
                    ranges_from[idx_from].index,
124
0
                    ranges_to[idx_to].index
125
                );
126
168k
                return false;
127
            }
128
        }
129
130
        // Check for a requirements conflict.
131
856k
        if self.bundles[from].cached_stack()
132
856k
            || self.bundles[from].cached_fixed()
133
780k
            || self.bundles[from].limit.is_some()
134
780k
            || self.bundles[to].cached_stack()
135
780k
            || self.bundles[to].cached_fixed()
136
622k
            || self.bundles[to].limit.is_some()
137
        {
138
234k
            if self.merge_bundle_requirements(from, to).is_err() {
139
4.49k
                trace!(" -> conflicting requirements; aborting merge");
140
4.49k
                return false;
141
229k
            }
142
622k
        }
143
144
852k
        trace!(" -> committing to merge");
145
146
        // If we reach here, then the bundles do not overlap -- merge
147
        // them!  We do this with a merge-sort-like scan over both
148
        // lists, building a new range list and replacing the list on
149
        // `to` when we're done.
150
852k
        if ranges_from.is_empty() {
151
            // `from` bundle is empty -- trivial merge.
152
0
            trace!(" -> from bundle{} is empty; trivial merge", from.index());
153
0
            return true;
154
852k
        }
155
852k
        if ranges_to.is_empty() {
156
            // `to` bundle is empty -- just move the list over from
157
            // `from` and set `bundle` up-link on all ranges.
158
0
            trace!(" -> to bundle{} is empty; trivial merge", to.index());
159
0
            let empty_vec = LiveRangeList::new_in(self.ctx.bump());
160
0
            let list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
161
0
            for entry in &list {
162
0
                self.ranges[entry.index].bundle = to;
163
164
0
                if self.annotations_enabled {
165
0
                    self.annotate(
166
0
                        entry.range.from,
167
0
                        format!(
168
0
                            " MERGE range{} v{} from bundle{} to bundle{}",
169
0
                            entry.index.index(),
170
0
                            self.ranges[entry.index].vreg.index(),
171
0
                            from.index(),
172
0
                            to.index(),
173
0
                        ),
174
0
                    );
175
0
                }
176
            }
177
0
            self.bundles[to].ranges = list;
178
0
            self.merge_bundle_properties(from, to);
179
0
            return true;
180
852k
        }
181
182
852k
        trace!(
183
            "merging: ranges_from = {:?} ranges_to = {:?}",
184
            ranges_from,
185
            ranges_to
186
        );
187
188
852k
        let empty_vec = LiveRangeList::new_in(self.ctx.bump());
189
852k
        let mut from_list = core::mem::replace(&mut self.bundles[from].ranges, empty_vec);
190
1.20M
        for entry in &from_list {
191
1.20M
            self.ranges[entry.index].bundle = to;
192
1.20M
        }
193
194
852k
        if from_list.len() == 1 {
195
            // Optimize for the common case where `from_list` contains a single
196
            // item. Using a binary search to find the insertion point and then
197
            // calling `insert` is more efficient than re-sorting the entire
198
            // list, specially after the changes in sorting algorithms introduced
199
            // in rustc 1.81.
200
            // See: https://github.com/bytecodealliance/regalloc2/issues/203
201
677k
            let single_entry = from_list.pop().unwrap();
202
677k
            let pos = self.bundles[to]
203
677k
                .ranges
204
677k
                .binary_search_by_key(&single_entry.range.from, |entry| entry.range.from)
205
677k
                .unwrap_or_else(|pos| pos);
206
677k
            self.bundles[to].ranges.insert(pos, single_entry);
207
        } else {
208
            // Two non-empty lists of LiveRanges: concatenate and sort. This is
209
            // faster than a mergesort-like merge into a new list, empirically.
210
174k
            self.bundles[to].ranges.extend_from_slice(&from_list[..]);
211
174k
            self.bundles[to]
212
174k
                .ranges
213
174k
                .sort_unstable_by_key(|entry| entry.range.from);
214
        }
215
216
852k
        if self.annotations_enabled {
217
0
            trace!("merging: merged = {:?}", self.bundles[to].ranges);
218
0
            let mut last_range = None;
219
0
            for i in 0..self.bundles[to].ranges.len() {
220
0
                let entry = self.bundles[to].ranges[i];
221
0
                if last_range.is_some() {
222
0
                    debug_assert!(last_range.unwrap() < entry.range);
223
0
                }
224
0
                last_range = Some(entry.range);
225
226
0
                if self.ranges[entry.index].bundle == from {
227
0
                    self.annotate(
228
0
                        entry.range.from,
229
0
                        format!(
230
0
                            " MERGE range{} v{} from bundle{} to bundle{}",
231
0
                            entry.index.index(),
232
0
                            self.ranges[entry.index].vreg.index(),
233
0
                            from.index(),
234
0
                            to.index(),
235
0
                        ),
236
0
                    );
237
0
                }
238
239
0
                trace!(
240
                    " -> merged result for bundle{}: range{}",
241
0
                    to.index(),
242
0
                    entry.index.index(),
243
                );
244
            }
245
852k
        }
246
247
852k
        if self.bundles[from].spillset != self.bundles[to].spillset {
248
852k
            // Widen the range for the target spillset to include the one being merged in.
249
852k
            let from_range = self.spillsets[self.bundles[from].spillset].range;
250
852k
            let to_range = &mut self.ctx.spillsets[self.ctx.bundles[to].spillset].range;
251
852k
            *to_range = to_range.join(from_range);
252
852k
        }
253
254
852k
        self.merge_bundle_properties(from, to);
255
256
852k
        true
257
1.02M
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_bundles
258
259
430k
    pub fn merge_vreg_bundles(&mut self) {
260
        // Create a bundle for every vreg, initially.
261
430k
        trace!("merge_vreg_bundles: creating vreg bundles");
262
105M
        for vreg in 0..self.vregs.len() {
263
105M
            let vreg = VRegIndex::new(vreg);
264
105M
            if self.vregs[vreg].ranges.is_empty() {
265
94.8M
                continue;
266
10.3M
            }
267
268
10.3M
            let bundle = self.ctx.bundles.add(self.ctx.bump());
269
10.3M
            let mut range = self.vregs[vreg].ranges.first().unwrap().range;
270
271
10.3M
            self.bundles[bundle].ranges = self.vregs[vreg].ranges.clone();
272
10.3M
            trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index());
273
10.7M
            for entry in &self.ctx.bundles[bundle].ranges {
274
10.7M
                trace!(
275
                    " -> with LR range{}: {:?}",
276
0
                    entry.index.index(),
277
                    entry.range
278
                );
279
10.7M
                range = range.join(entry.range);
280
10.7M
                self.ctx.ranges[entry.index].bundle = bundle;
281
            }
282
283
10.3M
            let mut fixed = false;
284
10.3M
            let mut fixed_def = false;
285
10.3M
            let mut stack = false;
286
10.3M
            let mut limit: Option<u8> = None;
287
10.7M
            for entry in &self.bundles[bundle].ranges {
288
29.6M
                for u in &self.ranges[entry.index].uses {
289
                    use OperandConstraint::*;
290
29.6M
                    match u.operand.constraint() {
291
                        FixedReg(_) => {
292
2.55M
                            fixed = true;
293
2.55M
                            if u.operand.kind() == OperandKind::Def {
294
1.31M
                                fixed_def = true;
295
1.31M
                            }
296
                        }
297
0
                        Stack => stack = true,
298
0
                        Limit(current) => {
299
0
                            let current = u8::try_from(current)
300
0
                                .expect("the current limit is too large to fit in a u8");
301
0
                            match limit {
302
0
                                Some(prev) => limit = Some(prev.min(current)),
303
0
                                None => limit = Some(current),
304
                            }
305
                        }
306
                        Any | Reg | Reuse(_) => {
307
27.1M
                            continue;
308
                        }
309
                    }
310
2.55M
                    if fixed && stack && fixed_def {
311
0
                        break;
312
2.55M
                    }
313
                }
314
            }
315
10.3M
            if fixed {
316
2.30M
                self.bundles[bundle].set_cached_fixed();
317
7.99M
            }
318
10.3M
            if fixed_def {
319
1.31M
                self.bundles[bundle].set_cached_fixed_def();
320
8.98M
            }
321
10.3M
            if stack {
322
0
                self.bundles[bundle].set_cached_stack();
323
10.3M
            }
324
10.3M
            self.bundles[bundle].limit = limit;
325
326
            // Create a spillslot for this bundle.
327
10.3M
            let reg = self.vreg(vreg);
328
10.3M
            let ssidx = self.spillsets.push(SpillSet {
329
10.3M
                slot: SpillSlotIndex::invalid(),
330
10.3M
                required: false,
331
10.3M
                class: reg.class(),
332
10.3M
                hint: PReg::invalid(),
333
10.3M
                spill_bundle: LiveBundleIndex::invalid(),
334
10.3M
                splits: 0,
335
10.3M
                range,
336
10.3M
            });
337
10.3M
            self.bundles[bundle].spillset = ssidx;
338
        }
339
340
15.7M
        for inst in 0..self.func.num_insts() {
341
15.7M
            let inst = Inst::new(inst);
342
343
            // Attempt to merge Reuse-constraint operand outputs with the
344
            // corresponding inputs.
345
29.6M
            for op in self.func.inst_operands(inst) {
346
29.6M
                if let OperandConstraint::Reuse(reuse_idx) = op.constraint() {
347
774k
                    let src_vreg = op.vreg();
348
774k
                    let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
349
350
774k
                    trace!(
351
                        "trying to merge reused-input def: src {} to dst {}",
352
                        src_vreg,
353
                        dst_vreg
354
                    );
355
774k
                    let src_bundle = self.ranges[self.vregs[src_vreg].ranges[0].index].bundle;
356
774k
                    debug_assert!(src_bundle.is_valid());
357
774k
                    let dest_bundle = self.ranges[self.vregs[dst_vreg].ranges[0].index].bundle;
358
774k
                    debug_assert!(dest_bundle.is_valid());
359
774k
                    self.merge_bundles(/* from */ dest_bundle, /* to */ src_bundle);
360
28.8M
                }
361
            }
362
        }
363
364
        // Attempt to merge blockparams with their inputs.
365
430k
        for i in 0..self.blockparam_outs.len() {
366
            let BlockparamOut {
367
365k
                from_vreg, to_vreg, ..
368
365k
            } = self.blockparam_outs[i];
369
365k
            trace!(
370
                "trying to merge blockparam v{} with input v{}",
371
0
                to_vreg.index(),
372
0
                from_vreg.index()
373
            );
374
365k
            let to_bundle = self.ranges[self.vregs[to_vreg].ranges[0].index].bundle;
375
365k
            debug_assert!(to_bundle.is_valid());
376
365k
            let from_bundle = self.ranges[self.vregs[from_vreg].ranges[0].index].bundle;
377
365k
            debug_assert!(from_bundle.is_valid());
378
365k
            trace!(
379
                " -> from bundle{} to bundle{}",
380
0
                from_bundle.index(),
381
0
                to_bundle.index()
382
            );
383
365k
            self.merge_bundles(from_bundle, to_bundle);
384
        }
385
386
430k
        trace!("done merging bundles");
387
430k
    }
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles
Line
Count
Source
259
96.6k
    pub fn merge_vreg_bundles(&mut self) {
260
        // Create a bundle for every vreg, initially.
261
96.6k
        trace!("merge_vreg_bundles: creating vreg bundles");
262
22.3M
        for vreg in 0..self.vregs.len() {
263
22.3M
            let vreg = VRegIndex::new(vreg);
264
22.3M
            if self.vregs[vreg].ranges.is_empty() {
265
20.4M
                continue;
266
1.90M
            }
267
268
1.90M
            let bundle = self.ctx.bundles.add(self.ctx.bump());
269
1.90M
            let mut range = self.vregs[vreg].ranges.first().unwrap().range;
270
271
1.90M
            self.bundles[bundle].ranges = self.vregs[vreg].ranges.clone();
272
1.90M
            trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index());
273
1.92M
            for entry in &self.ctx.bundles[bundle].ranges {
274
1.92M
                trace!(
275
                    " -> with LR range{}: {:?}",
276
0
                    entry.index.index(),
277
                    entry.range
278
                );
279
1.92M
                range = range.join(entry.range);
280
1.92M
                self.ctx.ranges[entry.index].bundle = bundle;
281
            }
282
283
1.90M
            let mut fixed = false;
284
1.90M
            let mut fixed_def = false;
285
1.90M
            let mut stack = false;
286
1.90M
            let mut limit: Option<u8> = None;
287
1.92M
            for entry in &self.bundles[bundle].ranges {
288
5.23M
                for u in &self.ranges[entry.index].uses {
289
                    use OperandConstraint::*;
290
5.23M
                    match u.operand.constraint() {
291
                        FixedReg(_) => {
292
618k
                            fixed = true;
293
618k
                            if u.operand.kind() == OperandKind::Def {
294
306k
                                fixed_def = true;
295
312k
                            }
296
                        }
297
0
                        Stack => stack = true,
298
0
                        Limit(current) => {
299
0
                            let current = u8::try_from(current)
300
0
                                .expect("the current limit is too large to fit in a u8");
301
0
                            match limit {
302
0
                                Some(prev) => limit = Some(prev.min(current)),
303
0
                                None => limit = Some(current),
304
                            }
305
                        }
306
                        Any | Reg | Reuse(_) => {
307
4.61M
                            continue;
308
                        }
309
                    }
310
618k
                    if fixed && stack && fixed_def {
311
0
                        break;
312
618k
                    }
313
                }
314
            }
315
1.90M
            if fixed {
316
553k
                self.bundles[bundle].set_cached_fixed();
317
1.35M
            }
318
1.90M
            if fixed_def {
319
306k
                self.bundles[bundle].set_cached_fixed_def();
320
1.59M
            }
321
1.90M
            if stack {
322
0
                self.bundles[bundle].set_cached_stack();
323
1.90M
            }
324
1.90M
            self.bundles[bundle].limit = limit;
325
326
            // Create a spillslot for this bundle.
327
1.90M
            let reg = self.vreg(vreg);
328
1.90M
            let ssidx = self.spillsets.push(SpillSet {
329
1.90M
                slot: SpillSlotIndex::invalid(),
330
1.90M
                required: false,
331
1.90M
                class: reg.class(),
332
1.90M
                hint: PReg::invalid(),
333
1.90M
                spill_bundle: LiveBundleIndex::invalid(),
334
1.90M
                splits: 0,
335
1.90M
                range,
336
1.90M
            });
337
1.90M
            self.bundles[bundle].spillset = ssidx;
338
        }
339
340
2.55M
        for inst in 0..self.func.num_insts() {
341
2.55M
            let inst = Inst::new(inst);
342
343
            // Attempt to merge Reuse-constraint operand outputs with the
344
            // corresponding inputs.
345
5.23M
            for op in self.func.inst_operands(inst) {
346
5.23M
                if let OperandConstraint::Reuse(reuse_idx) = op.constraint() {
347
76.3k
                    let src_vreg = op.vreg();
348
76.3k
                    let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
349
350
76.3k
                    trace!(
351
                        "trying to merge reused-input def: src {} to dst {}",
352
                        src_vreg,
353
                        dst_vreg
354
                    );
355
76.3k
                    let src_bundle = self.ranges[self.vregs[src_vreg].ranges[0].index].bundle;
356
76.3k
                    debug_assert!(src_bundle.is_valid());
357
76.3k
                    let dest_bundle = self.ranges[self.vregs[dst_vreg].ranges[0].index].bundle;
358
76.3k
                    debug_assert!(dest_bundle.is_valid());
359
76.3k
                    self.merge_bundles(/* from */ dest_bundle, /* to */ src_bundle);
360
5.15M
                }
361
            }
362
        }
363
364
        // Attempt to merge blockparams with their inputs.
365
96.6k
        for i in 0..self.blockparam_outs.len() {
366
            let BlockparamOut {
367
37.5k
                from_vreg, to_vreg, ..
368
37.5k
            } = self.blockparam_outs[i];
369
37.5k
            trace!(
370
                "trying to merge blockparam v{} with input v{}",
371
0
                to_vreg.index(),
372
0
                from_vreg.index()
373
            );
374
37.5k
            let to_bundle = self.ranges[self.vregs[to_vreg].ranges[0].index].bundle;
375
37.5k
            debug_assert!(to_bundle.is_valid());
376
37.5k
            let from_bundle = self.ranges[self.vregs[from_vreg].ranges[0].index].bundle;
377
37.5k
            debug_assert!(from_bundle.is_valid());
378
37.5k
            trace!(
379
                " -> from bundle{} to bundle{}",
380
0
                from_bundle.index(),
381
0
                to_bundle.index()
382
            );
383
37.5k
            self.merge_bundles(from_bundle, to_bundle);
384
        }
385
386
96.6k
        trace!("done merging bundles");
387
96.6k
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::merge_vreg_bundles
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles
Line
Count
Source
259
333k
    pub fn merge_vreg_bundles(&mut self) {
260
        // Create a bundle for every vreg, initially.
261
333k
        trace!("merge_vreg_bundles: creating vreg bundles");
262
82.8M
        for vreg in 0..self.vregs.len() {
263
82.8M
            let vreg = VRegIndex::new(vreg);
264
82.8M
            if self.vregs[vreg].ranges.is_empty() {
265
74.4M
                continue;
266
8.40M
            }
267
268
8.40M
            let bundle = self.ctx.bundles.add(self.ctx.bump());
269
8.40M
            let mut range = self.vregs[vreg].ranges.first().unwrap().range;
270
271
8.40M
            self.bundles[bundle].ranges = self.vregs[vreg].ranges.clone();
272
8.40M
            trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index());
273
8.81M
            for entry in &self.ctx.bundles[bundle].ranges {
274
8.81M
                trace!(
275
                    " -> with LR range{}: {:?}",
276
0
                    entry.index.index(),
277
                    entry.range
278
                );
279
8.81M
                range = range.join(entry.range);
280
8.81M
                self.ctx.ranges[entry.index].bundle = bundle;
281
            }
282
283
8.40M
            let mut fixed = false;
284
8.40M
            let mut fixed_def = false;
285
8.40M
            let mut stack = false;
286
8.40M
            let mut limit: Option<u8> = None;
287
8.81M
            for entry in &self.bundles[bundle].ranges {
288
24.4M
                for u in &self.ranges[entry.index].uses {
289
                    use OperandConstraint::*;
290
24.4M
                    match u.operand.constraint() {
291
                        FixedReg(_) => {
292
1.94M
                            fixed = true;
293
1.94M
                            if u.operand.kind() == OperandKind::Def {
294
1.01M
                                fixed_def = true;
295
1.01M
                            }
296
                        }
297
0
                        Stack => stack = true,
298
0
                        Limit(current) => {
299
0
                            let current = u8::try_from(current)
300
0
                                .expect("the current limit is too large to fit in a u8");
301
0
                            match limit {
302
0
                                Some(prev) => limit = Some(prev.min(current)),
303
0
                                None => limit = Some(current),
304
                            }
305
                        }
306
                        Any | Reg | Reuse(_) => {
307
22.4M
                            continue;
308
                        }
309
                    }
310
1.94M
                    if fixed && stack && fixed_def {
311
0
                        break;
312
1.94M
                    }
313
                }
314
            }
315
8.40M
            if fixed {
316
1.75M
                self.bundles[bundle].set_cached_fixed();
317
6.64M
            }
318
8.40M
            if fixed_def {
319
1.01M
                self.bundles[bundle].set_cached_fixed_def();
320
7.38M
            }
321
8.40M
            if stack {
322
0
                self.bundles[bundle].set_cached_stack();
323
8.40M
            }
324
8.40M
            self.bundles[bundle].limit = limit;
325
326
            // Create a spillslot for this bundle.
327
8.40M
            let reg = self.vreg(vreg);
328
8.40M
            let ssidx = self.spillsets.push(SpillSet {
329
8.40M
                slot: SpillSlotIndex::invalid(),
330
8.40M
                required: false,
331
8.40M
                class: reg.class(),
332
8.40M
                hint: PReg::invalid(),
333
8.40M
                spill_bundle: LiveBundleIndex::invalid(),
334
8.40M
                splits: 0,
335
8.40M
                range,
336
8.40M
            });
337
8.40M
            self.bundles[bundle].spillset = ssidx;
338
        }
339
340
13.2M
        for inst in 0..self.func.num_insts() {
341
13.2M
            let inst = Inst::new(inst);
342
343
            // Attempt to merge Reuse-constraint operand outputs with the
344
            // corresponding inputs.
345
24.4M
            for op in self.func.inst_operands(inst) {
346
24.4M
                if let OperandConstraint::Reuse(reuse_idx) = op.constraint() {
347
698k
                    let src_vreg = op.vreg();
348
698k
                    let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
349
350
698k
                    trace!(
351
                        "trying to merge reused-input def: src {} to dst {}",
352
                        src_vreg,
353
                        dst_vreg
354
                    );
355
698k
                    let src_bundle = self.ranges[self.vregs[src_vreg].ranges[0].index].bundle;
356
698k
                    debug_assert!(src_bundle.is_valid());
357
698k
                    let dest_bundle = self.ranges[self.vregs[dst_vreg].ranges[0].index].bundle;
358
698k
                    debug_assert!(dest_bundle.is_valid());
359
698k
                    self.merge_bundles(/* from */ dest_bundle, /* to */ src_bundle);
360
23.7M
                }
361
            }
362
        }
363
364
        // Attempt to merge blockparams with their inputs.
365
333k
        for i in 0..self.blockparam_outs.len() {
366
            let BlockparamOut {
367
328k
                from_vreg, to_vreg, ..
368
328k
            } = self.blockparam_outs[i];
369
328k
            trace!(
370
                "trying to merge blockparam v{} with input v{}",
371
0
                to_vreg.index(),
372
0
                from_vreg.index()
373
            );
374
328k
            let to_bundle = self.ranges[self.vregs[to_vreg].ranges[0].index].bundle;
375
328k
            debug_assert!(to_bundle.is_valid());
376
328k
            let from_bundle = self.ranges[self.vregs[from_vreg].ranges[0].index].bundle;
377
328k
            debug_assert!(from_bundle.is_valid());
378
328k
            trace!(
379
                " -> from bundle{} to bundle{}",
380
0
                from_bundle.index(),
381
0
                to_bundle.index()
382
            );
383
328k
            self.merge_bundles(from_bundle, to_bundle);
384
        }
385
386
333k
        trace!("done merging bundles");
387
333k
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::merge_vreg_bundles
388
389
13.0M
    pub fn compute_bundle_prio(&self, bundle: LiveBundleIndex) -> u32 {
390
        // The priority is simply the total "length" -- the number of
391
        // instructions covered by all LiveRanges.
392
13.0M
        let mut total = 0;
393
14.6M
        for entry in &self.bundles[bundle].ranges {
394
14.6M
            total += entry.range.len() as u32;
395
14.6M
        }
396
13.0M
        trace!(" -> prio {total}");
397
13.0M
        total
398
13.0M
    }
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_bundle_prio
Line
Count
Source
389
2.91M
    pub fn compute_bundle_prio(&self, bundle: LiveBundleIndex) -> u32 {
390
        // The priority is simply the total "length" -- the number of
391
        // instructions covered by all LiveRanges.
392
2.91M
        let mut total = 0;
393
3.06M
        for entry in &self.bundles[bundle].ranges {
394
3.06M
            total += entry.range.len() as u32;
395
3.06M
        }
396
2.91M
        trace!(" -> prio {total}");
397
2.91M
        total
398
2.91M
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_bundle_prio
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_bundle_prio
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::compute_bundle_prio
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_bundle_prio
Line
Count
Source
389
10.1M
    pub fn compute_bundle_prio(&self, bundle: LiveBundleIndex) -> u32 {
390
        // The priority is simply the total "length" -- the number of
391
        // instructions covered by all LiveRanges.
392
10.1M
        let mut total = 0;
393
11.6M
        for entry in &self.bundles[bundle].ranges {
394
11.6M
            total += entry.range.len() as u32;
395
11.6M
        }
396
10.1M
        trace!(" -> prio {total}");
397
10.1M
        total
398
10.1M
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_bundle_prio
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_bundle_prio
399
400
13.0M
    pub fn compute_bundle_limit(&self, bundle: LiveBundleIndex) -> Option<u8> {
401
13.0M
        let mut limit: Option<u8> = None;
402
14.6M
        for entry in &self.bundles[bundle].ranges {
403
30.5M
            for u in &self.ranges[entry.index].uses {
404
                use OperandConstraint::*;
405
30.5M
                match u.operand.constraint() {
406
0
                    Limit(current) => {
407
0
                        let current = u8::try_from(current)
408
0
                            .expect("the current limit is too large to fit in a u8");
409
0
                        match limit {
410
0
                            Some(prev) => limit = Some(prev.min(current)),
411
0
                            None => limit = Some(current),
412
                        }
413
                    }
414
                    FixedReg(_) | Stack => {
415
3.31M
                        break;
416
                    }
417
                    Any | Reg | Reuse(_) => {
418
27.1M
                        continue;
419
                    }
420
                }
421
            }
422
        }
423
13.0M
        limit
424
13.0M
    }
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_bundle_limit
Line
Count
Source
400
2.91M
    pub fn compute_bundle_limit(&self, bundle: LiveBundleIndex) -> Option<u8> {
401
2.91M
        let mut limit: Option<u8> = None;
402
3.06M
        for entry in &self.bundles[bundle].ranges {
403
5.63M
            for u in &self.ranges[entry.index].uses {
404
                use OperandConstraint::*;
405
5.63M
                match u.operand.constraint() {
406
0
                    Limit(current) => {
407
0
                        let current = u8::try_from(current)
408
0
                            .expect("the current limit is too large to fit in a u8");
409
0
                        match limit {
410
0
                            Some(prev) => limit = Some(prev.min(current)),
411
0
                            None => limit = Some(current),
412
                        }
413
                    }
414
                    FixedReg(_) | Stack => {
415
842k
                        break;
416
                    }
417
                    Any | Reg | Reuse(_) => {
418
4.79M
                        continue;
419
                    }
420
                }
421
            }
422
        }
423
2.91M
        limit
424
2.91M
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_bundle_limit
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_bundle_limit
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::compute_bundle_limit
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::compute_bundle_limit
Line
Count
Source
400
10.1M
    pub fn compute_bundle_limit(&self, bundle: LiveBundleIndex) -> Option<u8> {
401
10.1M
        let mut limit: Option<u8> = None;
402
11.6M
        for entry in &self.bundles[bundle].ranges {
403
24.8M
            for u in &self.ranges[entry.index].uses {
404
                use OperandConstraint::*;
405
24.8M
                match u.operand.constraint() {
406
0
                    Limit(current) => {
407
0
                        let current = u8::try_from(current)
408
0
                            .expect("the current limit is too large to fit in a u8");
409
0
                        match limit {
410
0
                            Some(prev) => limit = Some(prev.min(current)),
411
0
                            None => limit = Some(current),
412
                        }
413
                    }
414
                    FixedReg(_) | Stack => {
415
2.47M
                        break;
416
                    }
417
                    Any | Reg | Reuse(_) => {
418
22.4M
                        continue;
419
                    }
420
                }
421
            }
422
        }
423
10.1M
        limit
424
10.1M
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::compute_bundle_limit
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::compute_bundle_limit
425
426
430k
    pub fn queue_bundles(&mut self) {
427
10.3M
        for bundle in 0..self.bundles.len() {
428
10.3M
            trace!("enqueueing bundle{}", bundle);
429
10.3M
            let bundle = LiveBundleIndex::new(bundle);
430
10.3M
            if self.bundles[bundle].ranges.is_empty() {
431
958k
                trace!(" -> no ranges; skipping");
432
958k
                continue;
433
9.34M
            }
434
9.34M
            self.recompute_bundle_properties(bundle);
435
9.34M
            let prio = self.bundles[bundle].prio as usize;
436
9.34M
            self.allocation_queue.insert(bundle, prio, PReg::invalid());
437
        }
438
430k
        self.output.stats.merged_bundle_count = self.allocation_queue.heap.len();
439
430k
    }
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::queue_bundles
Line
Count
Source
426
96.6k
    pub fn queue_bundles(&mut self) {
427
1.90M
        for bundle in 0..self.bundles.len() {
428
1.90M
            trace!("enqueueing bundle{}", bundle);
429
1.90M
            let bundle = LiveBundleIndex::new(bundle);
430
1.90M
            if self.bundles[bundle].ranges.is_empty() {
431
106k
                trace!(" -> no ranges; skipping");
432
106k
                continue;
433
1.79M
            }
434
1.79M
            self.recompute_bundle_properties(bundle);
435
1.79M
            let prio = self.bundles[bundle].prio as usize;
436
1.79M
            self.allocation_queue.insert(bundle, prio, PReg::invalid());
437
        }
438
96.6k
        self.output.stats.merged_bundle_count = self.allocation_queue.heap.len();
439
96.6k
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::queue_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::queue_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<_>>::queue_bundles
<regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::queue_bundles
Line
Count
Source
426
333k
    pub fn queue_bundles(&mut self) {
427
8.40M
        for bundle in 0..self.bundles.len() {
428
8.40M
            trace!("enqueueing bundle{}", bundle);
429
8.40M
            let bundle = LiveBundleIndex::new(bundle);
430
8.40M
            if self.bundles[bundle].ranges.is_empty() {
431
852k
                trace!(" -> no ranges; skipping");
432
852k
                continue;
433
7.55M
            }
434
7.55M
            self.recompute_bundle_properties(bundle);
435
7.55M
            let prio = self.bundles[bundle].prio as usize;
436
7.55M
            self.allocation_queue.insert(bundle, prio, PReg::invalid());
437
        }
438
333k
        self.output.stats.merged_bundle_count = self.allocation_queue.heap.len();
439
333k
    }
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::queue_bundles
Unexecuted instantiation: <regalloc2::ion::data_structures::Env<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::queue_bundles
440
}