Coverage Report

Created: 2021-03-22 08:29

/src/regalloc.rs/lib/src/linear_scan/mod.rs
Line
Count
Source (jump to first uncovered line)
1
//! pub(crate) Implementation of the linear scan allocator algorithm.
2
//!
3
//! This tries to follow the implementation as suggested by:
4
//!   Optimized Interval Splitting in a Linear Scan Register Allocator,
5
//!     by Wimmer et al., 2005
6
7
use log::{info, log_enabled, trace, Level};
8
9
use std::env;
10
use std::fmt;
11
use std::{cmp::Ordering, default};
12
13
use crate::{
14
    checker::CheckerContext, reg_maps::MentionRegUsageMapper, Function, RealRegUniverse,
15
    RegAllocError, RegAllocResult, RegClass, Set, SpillSlot, VirtualReg, NUM_REG_CLASSES,
16
};
17
use crate::{
18
    checker::CheckerStackmapInfo,
19
    inst_stream::{add_spills_reloads_and_moves, InstToInsertAndExtPoint},
20
};
21
use crate::{
22
    data_structures::{BlockIx, InstIx, InstPoint, Point, RealReg, RegVecsAndBounds},
23
    CheckerErrors, StackmapRequestInfo,
24
};
25
26
use analysis::{AnalysisInfo, RangeFrag};
27
use smallvec::SmallVec;
28
29
use self::analysis::{BlockBoundary, BlockPos};
30
31
mod analysis;
32
mod assign_registers;
33
mod resolve_moves;
34
35
#[derive(Default)]
36
pub(crate) struct Statistics {
37
    only_large: bool,
38
39
    num_fixed: usize,
40
    num_vregs: usize,
41
    num_virtual_ranges: usize,
42
43
    peak_active: usize,
44
    peak_inactive: usize,
45
46
    num_try_allocate_reg: usize,
47
    num_try_allocate_reg_success: usize,
48
49
    num_reg_splits: usize,
50
    num_reg_splits_success: usize,
51
}
52
53
impl Drop for Statistics {
54
0
    fn drop(&mut self) {
55
0
        if self.only_large && self.num_vregs < 1000 {
56
0
            return;
57
0
        }
58
0
        println!(
59
0
            "stats: {} fixed; {} vreg; {} vranges; {} peak-active; {} peak-inactive, {} direct-alloc; {} total-alloc; {} partial-splits; {} partial-splits-attempts",
60
0
            self.num_fixed,
61
0
            self.num_vregs,
62
0
            self.num_virtual_ranges,
63
0
            self.peak_active,
64
0
            self.peak_inactive,
65
0
            self.num_try_allocate_reg_success,
66
0
            self.num_try_allocate_reg,
67
0
            self.num_reg_splits_success,
68
0
            self.num_reg_splits,
69
0
        );
70
0
    }
71
}
72
73
/// Which strategy should we use when trying to find the best split position?
74
/// TODO Consider loop depth to avoid splitting in the middle of a loop
75
/// whenever possible.
76
0
#[derive(Copy, Clone, Debug)]
77
enum OptimalSplitStrategy {
78
    From,
79
    To,
80
    NextFrom,
81
    NextNextFrom,
82
    PrevTo,
83
    PrevPrevTo,
84
    Mid,
85
}
86
87
0
#[derive(Clone)]
88
pub struct LinearScanOptions {
89
    split_strategy: OptimalSplitStrategy,
90
    partial_split: bool,
91
    partial_split_near_end: bool,
92
    stats: bool,
93
    large_stats: bool,
94
}
95
96
impl default::Default for LinearScanOptions {
97
0
    fn default() -> Self {
98
        // Useful for debugging.
99
0
        let optimal_split_strategy = match env::var("LSRA_SPLIT") {
100
0
            Ok(s) => match s.as_str() {
101
0
                "t" | "to" => OptimalSplitStrategy::To,
102
0
                "n" => OptimalSplitStrategy::NextFrom,
103
0
                "nn" => OptimalSplitStrategy::NextNextFrom,
104
0
                "p" => OptimalSplitStrategy::PrevTo,
105
0
                "pp" => OptimalSplitStrategy::PrevPrevTo,
106
0
                "m" | "mid" => OptimalSplitStrategy::Mid,
107
0
                _ => OptimalSplitStrategy::From,
108
            },
109
0
            Err(_) => OptimalSplitStrategy::From,
110
        };
111
112
0
        let large_stats = env::var("LSRA_LARGE_STATS").is_ok();
113
0
        let stats = env::var("LSRA_STATS").is_ok() || large_stats;
114
115
0
        let partial_split = env::var("LSRA_PARTIAL").is_ok();
116
0
        let partial_split_near_end = env::var("LSRA_PARTIAL_END").is_ok();
117
0
118
0
        Self {
119
0
            split_strategy: optimal_split_strategy,
120
0
            partial_split,
121
0
            partial_split_near_end,
122
0
            stats,
123
0
            large_stats,
124
0
        }
125
0
    }
126
}
127
128
impl fmt::Debug for LinearScanOptions {
129
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
130
0
        writeln!(fmt, "linear scan")?;
131
0
        write!(fmt, "  split: {:?}", self.split_strategy)
132
0
    }
133
}
134
135
// Local shorthands.
136
type RegUses = RegVecsAndBounds;
137
138
/// A unique identifier for an interval.
139
0
#[derive(Clone, Copy, PartialEq, Eq)]
140
struct IntId(pub(crate) usize);
141
142
impl fmt::Debug for IntId {
143
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
144
        write!(fmt, "int{}", self.0)
145
    }
146
}
147
148
0
#[derive(Clone)]
149
struct FixedInterval {
150
    reg: RealReg,
151
    frags: Vec<RangeFrag>,
152
}
153
154
impl fmt::Display for FixedInterval {
155
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156
0
        write!(f, "fixed {:?} [", self.reg)?;
157
0
        for (i, frag) in self.frags.iter().enumerate() {
158
0
            if i > 0 {
159
0
                write!(f, ", ")?;
160
0
            }
161
0
            if frag.ref_typed {
162
0
                write!(f, "ref ")?;
163
0
            }
164
0
            write!(f, "({:?}, {:?})", frag.first, frag.last)?;
165
        }
166
0
        write!(f, "]")
167
0
    }
168
}
169
170
impl FixedInterval {
171
    /// Find the fragment that contains the given instruction point.
172
    /// May crash if the point doesn't belong to any fragment.
173
0
    pub(crate) fn find_frag(&self, pt: InstPoint) -> usize {
174
0
        self.frags
175
0
            .binary_search_by(|frag| {
176
0
                if pt < frag.first {
177
0
                    Ordering::Greater
178
0
                } else if pt >= frag.first && pt <= frag.last {
179
0
                    Ordering::Equal
180
                } else {
181
0
                    Ordering::Less
182
                }
183
0
            })
184
0
            .unwrap()
185
0
    }
186
}
187
188
type Safepoints = SmallVec<[(InstIx, usize); 8]>;
189
190
0
#[derive(Clone)]
191
pub(crate) struct VirtualInterval {
192
    id: IntId,
193
    vreg: VirtualReg,
194
195
    /// Is this interval used for a reference type?
196
    ref_typed: bool,
197
198
    /// Parent interval in the split tree.
199
    parent: Option<IntId>,
200
    ancestor: Option<IntId>,
201
    /// Child interval, if it has one, in the split tree.
202
    child: Option<IntId>,
203
204
    /// Location assigned to this live interval.
205
    location: Location,
206
207
    mentions: MentionMap,
208
    block_boundaries: Vec<BlockBoundary>,
209
    safepoints: Safepoints,
210
    start: InstPoint,
211
    end: InstPoint,
212
}
213
214
impl fmt::Display for VirtualInterval {
215
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
216
0
        write!(fmt, "virtual {:?}", self.id)?;
217
0
        if self.ref_typed {
218
0
            write!(fmt, " ref")?;
219
0
        }
220
0
        if let Some(ref p) = self.parent {
221
0
            write!(fmt, " (parent={:?})", p)?;
222
0
        }
223
0
        write!(
224
0
            fmt,
225
0
            ": {:?} {} [{:?}; {:?}]",
226
0
            self.vreg, self.location, self.start, self.end
227
0
        )?;
228
0
        write!(
229
0
            fmt,
230
0
            " boundaries=[{}]",
231
0
            self.block_boundaries
232
0
                .iter()
233
0
                .map(|boundary| format!(
234
                    "{:?}{}",
235
                    boundary.bix,
236
                    if boundary.pos == BlockPos::Start {
237
                        "s"
238
                    } else {
239
                        "e"
240
                    }
241
0
                ))
242
0
                .collect::<Vec<_>>()
243
0
                .join(", ")
244
0
        )?;
245
0
        if !self.safepoints.is_empty() {
246
0
            write!(fmt, " safepoints=[")?;
247
0
            for (i, sp) in self.safepoints.iter().enumerate() {
248
0
                if i > 0 {
249
0
                    write!(fmt, ", {:?}", sp.0)?;
250
                } else {
251
0
                    write!(fmt, "{:?}", sp.0)?;
252
                }
253
            }
254
0
            write!(fmt, "]")?;
255
0
        }
256
0
        Ok(())
257
0
    }
258
}
259
260
impl VirtualInterval {
261
    fn new(
262
        id: IntId,
263
        vreg: VirtualReg,
264
        start: InstPoint,
265
        end: InstPoint,
266
        mentions: MentionMap,
267
        block_boundaries: Vec<BlockBoundary>,
268
        ref_typed: bool,
269
        safepoints: Safepoints,
270
    ) -> Self {
271
        Self {
272
            id,
273
            vreg,
274
            parent: None,
275
            ancestor: None,
276
            child: None,
277
            location: Location::None,
278
            mentions,
279
            block_boundaries,
280
            safepoints,
281
            start,
282
            end,
283
            ref_typed,
284
        }
285
    }
286
    fn safepoints(&self) -> &Safepoints {
287
        &self.safepoints
288
    }
289
    fn safepoints_mut(&mut self) -> &mut Safepoints {
290
        &mut self.safepoints
291
    }
292
    fn mentions(&self) -> &MentionMap {
293
        &self.mentions
294
    }
295
    fn mentions_mut(&mut self) -> &mut MentionMap {
296
        &mut self.mentions
297
    }
298
    fn block_boundaries(&self) -> &[BlockBoundary] {
299
        &self.block_boundaries
300
    }
301
    fn block_boundaries_mut(&mut self) -> &mut Vec<BlockBoundary> {
302
        &mut self.block_boundaries
303
    }
304
    fn covers(&self, pos: InstPoint) -> bool {
305
        self.start <= pos && pos <= self.end
306
    }
307
}
308
309
/// This data structure tracks the mentions of a register (virtual or real) at a precise
310
/// instruction point. It's a set encoded as three flags, one for each of use/mod/def.
311
0
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
312
pub struct Mention(u8);
313
314
impl fmt::Debug for Mention {
315
0
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
316
0
        let mut comma = false;
317
0
        if self.0 & 1 == 1 {
318
0
            write!(fmt, "use")?;
319
0
            comma = true;
320
0
        }
321
0
        if (self.0 >> 1) & 1 == 1 {
322
0
            if comma {
323
0
                write!(fmt, ",")?;
324
0
            }
325
0
            write!(fmt, "mod")?;
326
0
            comma = true;
327
0
        }
328
0
        if (self.0 >> 2) & 1 == 1 {
329
0
            if comma {
330
0
                write!(fmt, ",")?;
331
0
            }
332
0
            write!(fmt, "def")?;
333
0
        }
334
0
        Ok(())
335
0
    }
336
}
337
338
impl Mention {
339
    fn new() -> Self {
340
        Self(0)
341
    }
342
343
    // Setters.
344
    fn add_use(&mut self) {
345
        self.0 |= 1 << 0;
346
    }
347
    fn add_mod(&mut self) {
348
        self.0 |= 1 << 1;
349
    }
350
    fn add_def(&mut self) {
351
        self.0 |= 1 << 2;
352
    }
353
354
    // Getters.
355
    fn is_use(&self) -> bool {
356
        (self.0 & 0b001) != 0
357
    }
358
    fn is_mod(&self) -> bool {
359
        (self.0 & 0b010) != 0
360
    }
361
    fn is_def(&self) -> bool {
362
        (self.0 & 0b100) != 0
363
    }
364
    fn is_use_or_mod(&self) -> bool {
365
        (self.0 & 0b011) != 0
366
    }
367
    fn is_mod_or_def(&self) -> bool {
368
        (self.0 & 0b110) != 0
369
    }
370
}
371
372
pub type MentionMap = SmallVec<[(InstIx, Mention); 2]>;
373
374
0
#[derive(Debug, Clone, Copy)]
375
pub(crate) enum Location {
376
    None,
377
    Reg(RealReg),
378
    Stack(SpillSlot),
379
}
380
381
impl Location {
382
0
    pub(crate) fn reg(&self) -> Option<RealReg> {
383
0
        match self {
384
0
            Location::Reg(reg) => Some(*reg),
385
0
            _ => None,
386
        }
387
0
    }
388
0
    pub(crate) fn spill(&self) -> Option<SpillSlot> {
389
0
        match self {
390
0
            Location::Stack(slot) => Some(*slot),
391
0
            _ => None,
392
        }
393
0
    }
394
0
    pub(crate) fn is_none(&self) -> bool {
395
0
        match self {
396
0
            Location::None => true,
397
0
            _ => false,
398
        }
399
0
    }
400
}
401
402
impl fmt::Display for Location {
403
0
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
404
0
        match self {
405
0
            Location::None => write!(fmt, "none"),
406
0
            Location::Reg(reg) => write!(fmt, "{:?}", reg),
407
0
            Location::Stack(slot) => write!(fmt, "{:?}", slot),
408
        }
409
0
    }
410
}
411
412
/// A group of live intervals.
413
pub struct Intervals {
414
    virtuals: Vec<VirtualInterval>,
415
    fixeds: Vec<FixedInterval>,
416
}
417
418
impl Intervals {
419
    fn get(&self, int_id: IntId) -> &VirtualInterval {
420
        &self.virtuals[int_id.0]
421
    }
422
    fn get_mut(&mut self, int_id: IntId) -> &mut VirtualInterval {
423
        &mut self.virtuals[int_id.0]
424
    }
425
    fn num_virtual_intervals(&self) -> usize {
426
        self.virtuals.len()
427
    }
428
429
    // Mutators.
430
    fn set_reg(&mut self, int_id: IntId, reg: RealReg) {
431
        let int = self.get_mut(int_id);
432
        debug_assert!(int.location.is_none());
433
        int.location = Location::Reg(reg);
434
    }
435
    fn set_spill(&mut self, int_id: IntId, slot: SpillSlot) {
436
        let int = self.get_mut(int_id);
437
        debug_assert!(int.location.spill().is_none());
438
        int.location = Location::Stack(slot);
439
    }
440
    fn push_interval(&mut self, int: VirtualInterval) {
441
        debug_assert!(int.id.0 == self.virtuals.len());
442
        self.virtuals.push(int);
443
    }
444
0
    fn set_child(&mut self, int_id: IntId, child_id: IntId) {
445
0
        if let Some(prev_child) = self.virtuals[int_id.0].child.clone() {
446
0
            self.virtuals[child_id.0].child = Some(prev_child);
447
0
            self.virtuals[prev_child.0].parent = Some(child_id);
448
0
        }
449
0
        self.virtuals[int_id.0].child = Some(child_id);
450
0
    }
451
}
452
453
/// Finds the first use for the current interval that's located after the given
454
/// `pos` (included), in a broad sense of use (any of use, def or mod).
455
///
456
/// Extends to the left, that is, "modified" means "used".
457
#[inline(never)]
458
fn next_use(interval: &VirtualInterval, pos: InstPoint, _reg_uses: &RegUses) -> Option<InstPoint> {
459
0
    if log_enabled!(Level::Trace) {
460
0
        trace!("find next use of {} after {:?}", interval, pos);
461
0
    }
462
463
0
    let mentions = interval.mentions();
464
0
    let target = InstPoint::max(pos, interval.start);
465
466
0
    let ret = match mentions.binary_search_by_key(&target.iix(), |mention| mention.0) {
467
0
        Ok(index) => {
468
0
            // Either the selected index is a perfect match, or the next mention is
469
0
            // the correct answer.
470
0
            let mention = &mentions[index];
471
0
            if target.pt() == Point::Use {
472
0
                if mention.1.is_use_or_mod() {
473
0
                    Some(InstPoint::new_use(mention.0))
474
                } else {
475
0
                    Some(InstPoint::new_def(mention.0))
476
                }
477
0
            } else if target.pt() == Point::Def && mention.1.is_mod_or_def() {
478
0
                Some(target)
479
0
            } else if index == mentions.len() - 1 {
480
0
                None
481
            } else {
482
0
                let mention = &mentions[index + 1];
483
0
                if mention.1.is_use_or_mod() {
484
0
                    Some(InstPoint::new_use(mention.0))
485
                } else {
486
0
                    Some(InstPoint::new_def(mention.0))
487
                }
488
            }
489
        }
490
491
0
        Err(index) => {
492
0
            if index == mentions.len() {
493
0
                None
494
            } else {
495
0
                let mention = &mentions[index];
496
0
                if mention.1.is_use_or_mod() {
497
0
                    Some(InstPoint::new_use(mention.0))
498
                } else {
499
0
                    Some(InstPoint::new_def(mention.0))
500
                }
501
            }
502
        }
503
    };
504
505
    // TODO once the mentions are properly split, this could be removed, in
506
    // theory.
507
0
    let ret = match ret {
508
0
        Some(pos) => {
509
0
            if pos <= interval.end {
510
0
                Some(pos)
511
            } else {
512
0
                None
513
            }
514
        }
515
0
        None => None,
516
    };
517
518
0
    ret
519
0
}
520
521
/// Finds the last use of a vreg before a given target, including it in possible
522
/// return values.
523
/// Extends to the right, that is, modified means "def".
524
#[inline(never)]
525
fn last_use(interval: &VirtualInterval, pos: InstPoint, _reg_uses: &RegUses) -> Option<InstPoint> {
526
0
    if log_enabled!(Level::Trace) {
527
0
        trace!("searching last use of {} before {:?}", interval, pos,);
528
0
    }
529
530
0
    let mentions = interval.mentions();
531
0
532
0
    let target = InstPoint::min(pos, interval.end);
533
534
0
    let ret = match mentions.binary_search_by_key(&target.iix(), |mention| mention.0) {
535
0
        Ok(index) => {
536
0
            // Either the selected index is a perfect match, or the previous mention
537
0
            // is the correct answer.
538
0
            let mention = &mentions[index];
539
0
            if target.pt() == Point::Def {
540
0
                if mention.1.is_mod_or_def() {
541
0
                    Some(InstPoint::new_def(mention.0))
542
                } else {
543
0
                    Some(InstPoint::new_use(mention.0))
544
                }
545
0
            } else if target.pt() == Point::Use && mention.1.is_use() {
546
0
                Some(target)
547
0
            } else if index == 0 {
548
0
                None
549
            } else {
550
0
                let mention = &mentions[index - 1];
551
0
                if mention.1.is_mod_or_def() {
552
0
                    Some(InstPoint::new_def(mention.0))
553
                } else {
554
0
                    Some(InstPoint::new_use(mention.0))
555
                }
556
            }
557
        }
558
559
0
        Err(index) => {
560
0
            if index == 0 {
561
0
                None
562
            } else {
563
0
                let mention = &mentions[index - 1];
564
0
                if mention.1.is_mod_or_def() {
565
0
                    Some(InstPoint::new_def(mention.0))
566
                } else {
567
0
                    Some(InstPoint::new_use(mention.0))
568
                }
569
            }
570
        }
571
    };
572
573
    // TODO once the mentions are properly split, this could be removed, in
574
    // theory.
575
0
    let ret = match ret {
576
0
        Some(pos) => {
577
0
            if pos >= interval.start {
578
0
                Some(pos)
579
            } else {
580
0
                None
581
            }
582
        }
583
0
        None => None,
584
    };
585
586
0
    trace!("mentions: {:?}", mentions);
587
0
    trace!("found: {:?}", ret);
588
589
0
    ret
590
0
}
591
592
/// Checks that each register class has its own scratch register in addition to one available
593
/// register, and creates a mapping of register class -> scratch register.
594
0
fn compute_scratches(
595
0
    reg_universe: &RealRegUniverse,
596
0
) -> Result<Vec<Option<RealReg>>, RegAllocError> {
597
0
    let mut scratches_by_rc = vec![None; NUM_REG_CLASSES];
598
0
    for i in 0..NUM_REG_CLASSES {
599
0
        if let Some(info) = &reg_universe.allocable_by_class[i] {
600
0
            if info.first == info.last {
601
0
                return Err(RegAllocError::Other(
602
0
                    "at least 2 registers required for linear scan".into(),
603
0
                ));
604
0
            }
605
0
            let scratch = if let Some(suggested_reg) = info.suggested_scratch {
606
0
                reg_universe.regs[suggested_reg].0
607
            } else {
608
0
                return Err(RegAllocError::MissingSuggestedScratchReg(
609
0
                    RegClass::rc_from_u32(i as u32),
610
0
                ));
611
            };
612
0
            scratches_by_rc[i] = Some(scratch);
613
0
        }
614
    }
615
0
    Ok(scratches_by_rc)
616
0
}
617
618
/// Allocator top level.
619
///
620
/// `func` is modified so that, when this function returns, it will contain no VirtualReg uses.
621
///
622
/// Allocation can fail if there are insufficient registers to even generate spill/reload code, or
623
/// if the function appears to have any undefined VirtualReg/RealReg uses.
624
#[inline(never)]
625
0
pub(crate) fn run<F: Function>(
626
0
    func: &mut F,
627
0
    reg_universe: &RealRegUniverse,
628
0
    stackmap_request: Option<&StackmapRequestInfo>,
629
0
    use_checker: bool,
630
0
    opts: &LinearScanOptions,
631
0
) -> Result<RegAllocResult<F>, RegAllocError> {
632
    let AnalysisInfo {
633
0
        reg_vecs_and_bounds: reg_uses,
634
0
        intervals,
635
0
        liveins,
636
0
        liveouts,
637
0
        cfg,
638
        ..
639
0
    } = analysis::run(func, reg_universe, stackmap_request)
640
0
        .map_err(|err| RegAllocError::Analysis(err))?;
641
642
0
    let scratches_by_rc = compute_scratches(reg_universe)?;
643
644
0
    let stats = if opts.stats {
645
0
        let mut stats = Statistics::default();
646
0
        stats.num_fixed = intervals.fixeds.len();
647
0
        stats.num_virtual_ranges = intervals.virtuals.len();
648
0
        stats.num_vregs = intervals
649
0
            .virtuals
650
0
            .iter()
651
0
            .map(|virt| virt.vreg.get_index())
652
0
            .fold(0, |a, b| usize::max(a, b));
653
0
        stats.only_large = opts.large_stats;
654
0
        Some(stats)
655
    } else {
656
0
        None
657
    };
658
659
0
    if log_enabled!(Level::Trace) {
660
0
        trace!("fixed intervals:");
661
0
        for int in &intervals.fixeds {
662
0
            trace!("{}", int);
663
        }
664
0
        trace!("");
665
0
        trace!("unassigned intervals:");
666
0
        for int in &intervals.virtuals {
667
0
            trace!("{}", int);
668
0
            for mention in &int.mentions {
669
0
                trace!("  mention @ {:?}: {:?}", mention.0, mention.1);
670
            }
671
        }
672
0
        trace!("");
673
0
    }
674
675
0
    let (intervals, mut num_spill_slots) = assign_registers::run(
676
0
        opts,
677
0
        func,
678
0
        &reg_uses,
679
0
        reg_universe,
680
0
        &scratches_by_rc,
681
0
        intervals,
682
0
        stats,
683
0
    )?;
684
685
0
    let virtuals = &intervals.virtuals;
686
0
687
0
    let memory_moves = resolve_moves::run(
688
0
        func,
689
0
        &cfg,
690
0
        &reg_uses,
691
0
        virtuals,
692
0
        &liveins,
693
0
        &liveouts,
694
0
        &mut num_spill_slots,
695
0
        &scratches_by_rc,
696
0
    );
697
0
698
0
    apply_registers(
699
0
        func,
700
0
        virtuals,
701
0
        memory_moves,
702
0
        reg_universe,
703
0
        num_spill_slots,
704
0
        use_checker,
705
0
        stackmap_request,
706
0
    )
707
0
}
Unexecuted instantiation: regalloc::linear_scan::run::<minira::test_framework::Func>
Unexecuted instantiation: regalloc::linear_scan::run::<regalloc::snapshot::IRFunction>
708
709
#[inline(never)]
710
0
fn set_registers<F: Function>(
711
0
    func: &mut F,
712
0
    virtual_intervals: &Vec<VirtualInterval>,
713
0
    reg_universe: &RealRegUniverse,
714
0
    use_checker: bool,
715
0
    memory_moves: &Vec<InstToInsertAndExtPoint>,
716
0
    stackmap_request: Option<&StackmapRequestInfo>,
717
0
    stackmaps: &[Vec<SpillSlot>],
718
0
) -> Result<Set<RealReg>, CheckerErrors> {
719
0
    info!("set_registers");
720
721
0
    let mut clobbered_registers = Set::empty();
722
0
723
0
    // Collect all the regs per instruction and mention set.
724
0
    let capacity = virtual_intervals
725
0
        .iter()
726
0
        .map(|int| int.mentions.len())
727
0
        .fold(0, |a, b| a + b);
728
0
729
0
    if capacity == 0 {
730
        // No virtual registers have been allocated, exit early.
731
0
        return Ok(clobbered_registers);
732
0
    }
733
0
734
0
    let mut mention_map = Vec::with_capacity(capacity);
735
736
0
    for int in virtual_intervals {
737
0
        let rreg = match int.location.reg() {
738
0
            Some(rreg) => rreg,
739
            _ => continue,
740
        };
741
0
        trace!("int: {}", int);
742
0
        trace!("  {:?}", int.mentions);
743
0
        for &mention in &int.mentions {
744
0
            mention_map.push((mention.0, mention.1, int.vreg, rreg));
745
0
        }
746
    }
747
748
    // Sort by instruction index.
749
0
    mention_map.sort_unstable_by_key(|quad| quad.0);
750
0
751
0
    // Iterate over all the mentions.
752
0
    let mut mapper = MentionRegUsageMapper::new();
753
0
754
0
    // Set up checker state, if indicated by our configuration.
755
0
    let mut checker: Option<CheckerContext> = None;
756
0
    let mut insn_blocks: Vec<BlockIx> = vec![];
757
0
    if use_checker {
758
0
        let stackmap_info =
759
0
            stackmap_request.map(|request| CheckerStackmapInfo { request, stackmaps });
760
0
        checker = Some(CheckerContext::new(
761
0
            func,
762
0
            reg_universe,
763
0
            memory_moves,
764
0
            stackmap_info,
765
0
        ));
766
0
        insn_blocks.resize(func.insns().len(), BlockIx::new(0));
767
0
        for block_ix in func.blocks() {
768
0
            for insn_ix in func.block_insns(block_ix) {
769
0
                insn_blocks[insn_ix.get() as usize] = block_ix;
770
0
            }
771
        }
772
0
    }
773
774
0
    let mut cur_quad_ix = 0;
775
0
    for func_inst_ix in func.insn_indices() {
776
        // Several items in the mention_map array may refer to the same instruction index, so
777
        // iterate over all of them that are related to the current instruction index.
778
0
        while let Some((iix, mention_set, vreg, rreg)) = mention_map.get(cur_quad_ix) {
779
0
            if func_inst_ix != *iix {
780
0
                break;
781
0
            }
782
783
            trace!(
784
0
                "{:?}: {:?} is in {:?} at {:?}",
785
0
                iix,
786
0
                vreg,
787
0
                rreg,
788
0
                mention_set
789
            );
790
791
            // Fill in new information at the given index.
792
0
            if mention_set.is_use() {
793
0
                if let Some(prev_rreg) = mapper.lookup_use(*vreg) {
794
                    debug_assert_eq!(prev_rreg, *rreg, "different use allocs for {:?}", vreg);
795
0
                }
796
0
                mapper.set_use(*vreg, *rreg);
797
0
            }
798
799
0
            let included_in_clobbers = func.is_included_in_clobbers(func.get_insn(*iix));
800
0
            if mention_set.is_mod() {
801
0
                if let Some(prev_rreg) = mapper.lookup_use(*vreg) {
802
                    debug_assert_eq!(prev_rreg, *rreg, "different use allocs for {:?}", vreg);
803
0
                }
804
0
                if let Some(prev_rreg) = mapper.lookup_def(*vreg) {
805
                    debug_assert_eq!(prev_rreg, *rreg, "different def allocs for {:?}", vreg);
806
0
                }
807
808
0
                mapper.set_use(*vreg, *rreg);
809
0
                mapper.set_def(*vreg, *rreg);
810
0
                if included_in_clobbers {
811
0
                    clobbered_registers.insert(*rreg);
812
0
                }
813
0
            }
814
815
0
            if mention_set.is_def() {
816
0
                if let Some(prev_rreg) = mapper.lookup_def(*vreg) {
817
                    debug_assert_eq!(prev_rreg, *rreg, "different def allocs for {:?}", *vreg);
818
0
                }
819
820
0
                mapper.set_def(*vreg, *rreg);
821
0
                if included_in_clobbers {
822
0
                    clobbered_registers.insert(*rreg);
823
0
                }
824
0
            }
825
826
0
            cur_quad_ix += 1;
827
        }
828
829
        // At this point we've correctly filled the mapper; actually map the virtual registers to
830
        // the real ones in the Function.
831
0
        trace!("map_regs for {:?}", func_inst_ix);
832
833
        // If available, make sure to update the checker's state *before* actually mapping the
834
        // register; the checker must see the function with virtual registers, not real ones.
835
0
        if let Some(ref mut checker) = checker {
836
0
            let block_ix = insn_blocks[func_inst_ix.get() as usize];
837
0
            checker
838
0
                .handle_insn(reg_universe, func, block_ix, func_inst_ix, &mapper)
839
0
                .unwrap();
840
0
        }
841
842
0
        let mut inst = func.get_insn_mut(func_inst_ix);
843
0
        F::map_regs(&mut inst, &mapper);
844
0
845
0
        mapper.clear();
846
    }
847
848
0
    if let Some(checker) = checker {
849
0
        checker.run()?;
850
0
    }
851
852
0
    Ok(clobbered_registers)
853
0
}
Unexecuted instantiation: regalloc::linear_scan::set_registers::<minira::test_framework::Func>
Unexecuted instantiation: regalloc::linear_scan::set_registers::<regalloc::snapshot::IRFunction>
854
855
fn compute_stackmaps(
856
    intervals: &[VirtualInterval],
857
    stackmap_request: Option<&StackmapRequestInfo>,
858
) -> Vec<Vec<SpillSlot>> {
859
0
    if let Some(request) = stackmap_request {
860
0
        let mut stackmaps = vec![Vec::new(); request.safepoint_insns.len()];
861
0
        for int in intervals {
862
0
            if !int.ref_typed {
863
                continue;
864
0
            }
865
0
            if let Some(slot) = int.location.spill() {
866
0
                for &(_sp_iix, sp_ix) in &int.safepoints {
867
0
                    stackmaps[sp_ix].push(slot);
868
0
                }
869
0
            }
870
        }
871
0
        stackmaps
872
    } else {
873
0
        vec![]
874
    }
875
0
}
876
877
/// Fills in the register assignments into instructions.
878
#[inline(never)]
879
fn apply_registers<F: Function>(
880
    func: &mut F,
881
    virtual_intervals: &Vec<VirtualInterval>,
882
    memory_moves: Vec<InstToInsertAndExtPoint>,
883
    reg_universe: &RealRegUniverse,
884
    num_spill_slots: u32,
885
    use_checker: bool,
886
    stackmap_request: Option<&StackmapRequestInfo>,
887
) -> Result<RegAllocResult<F>, RegAllocError> {
888
0
    info!("apply_registers");
889
890
0
    let stackmaps = compute_stackmaps(virtual_intervals, stackmap_request.clone());
891
892
0
    let clobbered_registers = set_registers(
893
0
        func,
894
0
        virtual_intervals,
895
0
        reg_universe,
896
0
        use_checker,
897
0
        &memory_moves,
898
0
        stackmap_request,
899
0
        &stackmaps,
900
0
    )
901
0
    .map_err(|err| RegAllocError::RegChecker(err))?;
902
903
0
    let (final_insns, target_map, new_to_old_insn_map, new_safepoint_insns) =
904
0
        add_spills_reloads_and_moves(
905
0
            func,
906
0
            stackmap_request.map(|request| request.safepoint_insns.as_slice()),
907
0
            memory_moves,
908
0
        )
909
0
        .map_err(|e| RegAllocError::Other(e))?;
910
911
    // And now remove from the clobbered registers set, all those not available to the allocator.
912
    // But not removing the reserved regs, since we might have modified those.
913
0
    clobbered_registers.filter_map(|&reg| {
914
0
        if reg.get_index() >= reg_universe.allocable {
915
0
            None
916
        } else {
917
0
            Some(reg)
918
        }
919
0
    });
Unexecuted instantiation: regalloc::linear_scan::apply_registers::<minira::test_framework::Func>::{closure#3}
Unexecuted instantiation: regalloc::linear_scan::apply_registers::<regalloc::snapshot::IRFunction>::{closure#3}
920
0
921
0
    Ok(RegAllocResult {
922
0
        insns: final_insns,
923
0
        target_map,
924
0
        orig_insn_map: new_to_old_insn_map,
925
0
        clobbered_registers,
926
0
        num_spill_slots,
927
0
        block_annotations: None,
928
0
        stackmaps,
929
0
        new_safepoint_insns,
930
0
    })
931
0
}
Unexecuted instantiation: regalloc::linear_scan::apply_registers::<minira::test_framework::Func>
Unexecuted instantiation: regalloc::linear_scan::apply_registers::<regalloc::snapshot::IRFunction>