1use super::liveranges::SpillWeight;
16use crate::cfg::{CFGInfo, CFGInfoCtx};
17use crate::index::ContainerComparator;
18use crate::indexset::IndexSet;
19use crate::Vec2;
20use crate::{
21 define_index, Allocation, Block, Bump, Edit, Function, FxHashMap, FxHashSet, MachineEnv,
22 Operand, Output, PReg, ProgPoint, RegClass, VReg,
23};
24use alloc::collections::BTreeMap;
25use alloc::collections::VecDeque;
26use alloc::string::String;
27use alloc::vec::Vec;
28use core::cmp::Ordering;
29use core::fmt::Debug;
30use core::ops::{Deref, DerefMut};
31use smallvec::SmallVec;
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub struct CodeRange {
36 pub from: ProgPoint,
37 pub to: ProgPoint,
38}
39
40impl CodeRange {
41 #[inline(always)]
42 pub fn is_empty(&self) -> bool {
43 self.from >= self.to
44 }
45 #[inline(always)]
46 pub fn contains(&self, other: &Self) -> bool {
47 other.from >= self.from && other.to <= self.to
48 }
49 #[inline(always)]
50 pub fn contains_point(&self, other: ProgPoint) -> bool {
51 other >= self.from && other < self.to
52 }
53 #[inline(always)]
54 pub fn overlaps(&self, other: &Self) -> bool {
55 other.to > self.from && other.from < self.to
56 }
57 #[inline(always)]
58 pub fn len(&self) -> usize {
59 self.to.inst().index() - self.from.inst().index()
60 }
61 #[inline(always)]
63 pub fn singleton(pos: ProgPoint) -> CodeRange {
64 CodeRange {
65 from: pos,
66 to: pos.next(),
67 }
68 }
69
70 #[inline(always)]
72 pub fn join(&self, other: CodeRange) -> Self {
73 CodeRange {
74 from: self.from.min(other.from),
75 to: self.to.max(other.to),
76 }
77 }
78}
79
80impl core::cmp::PartialOrd for CodeRange {
81 #[inline(always)]
82 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
83 Some(self.cmp(other))
84 }
85}
86impl core::cmp::Ord for CodeRange {
87 #[inline(always)]
88 fn cmp(&self, other: &Self) -> Ordering {
89 if self.to <= other.from {
90 Ordering::Less
91 } else if self.from >= other.to {
92 Ordering::Greater
93 } else {
94 Ordering::Equal
95 }
96 }
97}
98
99define_index!(LiveBundleIndex, LiveBundles, LiveBundle);
100define_index!(LiveRangeIndex, LiveRanges, LiveRange);
101define_index!(SpillSetIndex, SpillSets, SpillSet);
102define_index!(UseIndex);
103define_index!(VRegIndex, VRegs, VRegData);
104define_index!(PRegIndex);
105define_index!(SpillSlotIndex);
106
107pub type LiveBundleVec = Vec<LiveBundleIndex>;
109
110#[derive(Clone, Copy, Debug)]
111pub struct LiveRangeListEntry {
112 pub range: CodeRange,
113 pub index: LiveRangeIndex,
114}
115
116pub type LiveRangeList = Vec2<LiveRangeListEntry, Bump>;
117pub type UseList = Vec2<Use, Bump>;
118
119#[derive(Clone, Debug)]
120pub struct LiveRange {
121 pub range: CodeRange,
122
123 pub vreg: VRegIndex,
124 pub bundle: LiveBundleIndex,
125 pub uses_spill_weight_and_flags: u32,
126 pub(crate) uses: UseList,
127}
128
129#[derive(Clone, Copy, Debug, PartialEq, Eq)]
130#[repr(u32)]
131pub enum LiveRangeFlag {
132 StartsAtDef = 1,
133}
134
135impl LiveRange {
136 #[inline(always)]
137 pub fn set_flag(&mut self, flag: LiveRangeFlag) {
138 self.uses_spill_weight_and_flags |= (flag as u32) << 29;
139 }
140 #[inline(always)]
141 pub fn clear_flag(&mut self, flag: LiveRangeFlag) {
142 self.uses_spill_weight_and_flags &= !((flag as u32) << 29);
143 }
144 #[inline(always)]
145 pub fn assign_flag(&mut self, flag: LiveRangeFlag, val: bool) {
146 let bit = if val { (flag as u32) << 29 } else { 0 };
147 self.uses_spill_weight_and_flags &= 0xe000_0000;
148 self.uses_spill_weight_and_flags |= bit;
149 }
150 #[inline(always)]
151 pub fn has_flag(&self, flag: LiveRangeFlag) -> bool {
152 self.uses_spill_weight_and_flags & ((flag as u32) << 29) != 0
153 }
154 #[inline(always)]
155 pub fn flag_word(&self) -> u32 {
156 self.uses_spill_weight_and_flags & 0xe000_0000
157 }
158 #[inline(always)]
159 pub fn merge_flags(&mut self, flag_word: u32) {
160 self.uses_spill_weight_and_flags |= flag_word;
161 }
162 #[inline(always)]
163 pub fn uses_spill_weight(&self) -> SpillWeight {
164 let bits = (self.uses_spill_weight_and_flags & 0x1fff_ffff) << 2;
168 SpillWeight::from_f32(f32::from_bits(bits))
169 }
170 #[inline(always)]
171 pub fn set_uses_spill_weight(&mut self, weight: SpillWeight) {
172 let weight_bits = (weight.to_f32().to_bits() >> 2) & 0x1fff_ffff;
173 self.uses_spill_weight_and_flags =
174 (self.uses_spill_weight_and_flags & 0xe000_0000) | weight_bits;
175 }
176}
177
178#[derive(Clone, Copy, Debug)]
179pub struct Use {
180 pub operand: Operand,
181 pub pos: ProgPoint,
182 pub slot: u16,
183 pub weight: u16,
184}
185
186impl Use {
187 #[inline(always)]
188 pub fn new(operand: Operand, pos: ProgPoint, slot: u16) -> Self {
189 Self {
190 operand,
191 pos,
192 slot,
193 weight: 0,
195 }
196 }
197}
198
199#[derive(Clone, Debug)]
200pub struct LiveBundle {
201 pub(crate) ranges: LiveRangeList,
202 pub spillset: SpillSetIndex,
203 pub allocation: Allocation,
204 pub prio: u32, pub spill_weight_and_props: u32,
206}
207
208pub const BUNDLE_MAX_SPILL_WEIGHT: u32 = (1 << 29) - 1;
209pub const MINIMAL_FIXED_BUNDLE_SPILL_WEIGHT: u32 = BUNDLE_MAX_SPILL_WEIGHT;
210pub const MINIMAL_BUNDLE_SPILL_WEIGHT: u32 = BUNDLE_MAX_SPILL_WEIGHT - 1;
211pub const BUNDLE_MAX_NORMAL_SPILL_WEIGHT: u32 = BUNDLE_MAX_SPILL_WEIGHT - 2;
212
213impl LiveBundle {
214 #[inline(always)]
215 pub fn set_cached_spill_weight_and_props(
216 &mut self,
217 spill_weight: u32,
218 minimal: bool,
219 fixed: bool,
220 fixed_def: bool,
221 ) {
222 debug_assert!(spill_weight <= BUNDLE_MAX_SPILL_WEIGHT);
223 self.spill_weight_and_props = spill_weight
224 | (if minimal { 1 << 31 } else { 0 })
225 | (if fixed { 1 << 30 } else { 0 })
226 | (if fixed_def { 1 << 29 } else { 0 });
227 }
228
229 #[inline(always)]
230 pub fn cached_minimal(&self) -> bool {
231 self.spill_weight_and_props & (1 << 31) != 0
232 }
233
234 #[inline(always)]
235 pub fn cached_fixed(&self) -> bool {
236 self.spill_weight_and_props & (1 << 30) != 0
237 }
238
239 #[inline(always)]
240 pub fn cached_fixed_def(&self) -> bool {
241 self.spill_weight_and_props & (1 << 29) != 0
242 }
243
244 #[inline(always)]
245 pub fn set_cached_fixed(&mut self) {
246 self.spill_weight_and_props |= 1 << 30;
247 }
248
249 #[inline(always)]
250 pub fn set_cached_fixed_def(&mut self) {
251 self.spill_weight_and_props |= 1 << 29;
252 }
253
254 #[inline(always)]
255 pub fn cached_spill_weight(&self) -> u32 {
256 self.spill_weight_and_props & BUNDLE_MAX_SPILL_WEIGHT
257 }
258}
259
260#[derive(Clone, Copy, Debug, PartialEq, Eq)]
261pub struct BundleProperties {
262 pub minimal: bool,
263 pub fixed: bool,
264}
265
266const fn no_bloat_capacity<T>() -> usize {
269 core::mem::size_of::<usize>() * 2 / core::mem::size_of::<T>()
281}
282
283#[derive(Clone, Debug)]
284pub struct SpillSet {
285 pub slot: SpillSlotIndex,
286 pub reg_hint: PReg,
287 pub class: RegClass,
288 pub spill_bundle: LiveBundleIndex,
289 pub required: bool,
290 pub splits: u8,
291
292 pub range: CodeRange,
297}
298
299pub(crate) const MAX_SPLITS_PER_SPILLSET: u8 = 2;
300
301#[derive(Clone, Debug)]
302pub struct VRegData {
303 pub(crate) ranges: LiveRangeList,
304 pub blockparam: Block,
305 pub class: Option<RegClass>,
307}
308
309#[derive(Clone, Debug)]
310pub struct PRegData {
311 pub allocations: LiveRangeSet,
312 pub is_stack: bool,
313}
314
315#[derive(Clone, Debug)]
316pub struct MultiFixedRegFixup {
317 pub pos: ProgPoint,
318 pub from_slot: u16,
319 pub to_slot: u16,
320 pub level: FixedRegFixupLevel,
321 pub to_preg: PRegIndex,
322 pub vreg: VRegIndex,
323}
324
325#[derive(Clone, Debug, PartialEq, Eq)]
326pub enum FixedRegFixupLevel {
327 Initial,
329 Secondary,
332}
333
334#[derive(Clone, Copy, Debug, PartialEq, Eq)]
342#[repr(C)]
343pub struct BlockparamOut {
344 pub to_vreg: VRegIndex,
345 pub to_block: Block,
346 pub from_block: Block,
347 pub from_vreg: VRegIndex,
348}
349impl BlockparamOut {
350 #[inline(always)]
351 pub fn key(&self) -> u128 {
352 u128_key(
353 self.from_vreg.raw_u32(),
354 self.from_block.raw_u32(),
355 self.to_block.raw_u32(),
356 self.to_vreg.raw_u32(),
357 )
358 }
359}
360
361#[derive(Clone, Debug)]
366#[repr(C)]
367pub struct BlockparamIn {
368 pub from_block: Block,
369 pub to_block: Block,
370 pub to_vreg: VRegIndex,
371}
372impl BlockparamIn {
373 #[inline(always)]
374 pub fn key(&self) -> u128 {
375 u128_key(
376 self.to_vreg.raw_u32(),
377 self.to_block.raw_u32(),
378 self.from_block.raw_u32(),
379 0,
380 )
381 }
382}
383
384impl LiveRanges {
385 pub(crate) fn add(&mut self, range: CodeRange, bump: Bump) -> LiveRangeIndex {
386 self.push(LiveRange {
387 range,
388 vreg: VRegIndex::invalid(),
389 bundle: LiveBundleIndex::invalid(),
390 uses_spill_weight_and_flags: 0,
391 uses: UseList::new_in(bump),
392 })
393 }
394}
395
396impl LiveBundles {
397 pub(crate) fn add(&mut self, bump: Bump) -> LiveBundleIndex {
398 self.push(LiveBundle {
399 allocation: Allocation::none(),
400 ranges: LiveRangeList::new_in(bump),
401 spillset: SpillSetIndex::invalid(),
402 prio: 0,
403 spill_weight_and_props: 0,
404 })
405 }
406}
407
408impl VRegs {
409 pub fn add(&mut self, reg: VReg, data: VRegData) -> VRegIndex {
410 let idx = self.push(data);
411 debug_assert_eq!(reg.vreg(), idx.index());
412 idx
413 }
414}
415
416impl core::ops::Index<VReg> for VRegs {
417 type Output = VRegData;
418
419 #[inline(always)]
420 fn index(&self, idx: VReg) -> &Self::Output {
421 &self.storage[idx.vreg()]
422 }
423}
424
425impl core::ops::IndexMut<VReg> for VRegs {
426 #[inline(always)]
427 fn index_mut(&mut self, idx: VReg) -> &mut Self::Output {
428 &mut self.storage[idx.vreg()]
429 }
430}
431
432#[derive(Default)]
433pub struct Ctx {
434 pub(crate) cfginfo: CFGInfo,
435 pub(crate) cfginfo_ctx: CFGInfoCtx,
436 pub(crate) liveins: Vec<IndexSet>,
437 pub(crate) liveouts: Vec<IndexSet>,
438 pub(crate) blockparam_outs: Vec<BlockparamOut>,
439 pub(crate) blockparam_ins: Vec<BlockparamIn>,
440
441 pub(crate) ranges: LiveRanges,
442 pub(crate) bundles: LiveBundles,
443 pub(crate) spillsets: SpillSets,
444 pub(crate) vregs: VRegs,
445 pub(crate) pregs: Vec<PRegData>,
446 pub(crate) allocation_queue: PrioQueue,
447
448 pub(crate) spilled_bundles: Vec<LiveBundleIndex>,
449 pub(crate) spillslots: Vec<SpillSlotData>,
450 pub(crate) slots_by_class: [SpillSlotList; 3],
451
452 pub(crate) extra_spillslots_by_class: [SmallVec<[Allocation; 2]>; 3],
453 pub(crate) preferred_victim_by_class: [PReg; 3],
454
455 pub(crate) multi_fixed_reg_fixups: Vec<MultiFixedRegFixup>,
464
465 pub(crate) allocated_bundle_count: usize,
466
467 pub(crate) debug_annotations: FxHashMap<ProgPoint, Vec<String>>,
470 pub(crate) annotations_enabled: bool,
471
472 pub(crate) conflict_set: FxHashSet<LiveBundleIndex>,
475
476 pub output: Output,
478
479 pub(crate) scratch_conflicts: LiveBundleVec,
480 pub(crate) scratch_bundle: LiveBundleVec,
481 pub(crate) scratch_vreg_ranges: Vec<LiveRangeIndex>,
482 pub(crate) scratch_spillset_pool: Vec<SpillSetRanges>,
483
484 pub(crate) scratch_workqueue: VecDeque<Block>,
485
486 pub(crate) scratch_operand_rewrites: FxHashMap<usize, Operand>,
487 pub(crate) scratch_removed_lrs: FxHashSet<LiveRangeIndex>,
488 pub(crate) scratch_removed_lrs_vregs: FxHashSet<VRegIndex>,
489 pub(crate) scratch_workqueue_set: FxHashSet<Block>,
490
491 pub(crate) scratch_bump: Bump,
492}
493
494impl Ctx {
495 pub(crate) fn bump(&self) -> Bump {
496 self.scratch_bump.clone()
497 }
498}
499
500pub struct Env<'a, F: Function> {
501 pub func: &'a F,
502 pub env: &'a MachineEnv,
503 pub ctx: &'a mut Ctx,
504}
505
506impl<'a, F: Function> Deref for Env<'a, F> {
507 type Target = Ctx;
508
509 fn deref(&self) -> &Self::Target {
510 self.ctx
511 }
512}
513
514impl<'a, F: Function> DerefMut for Env<'a, F> {
515 fn deref_mut(&mut self) -> &mut Self::Target {
516 self.ctx
517 }
518}
519
520impl<'a, F: Function> Env<'a, F> {
521 #[inline]
523 pub fn vreg(&self, index: VRegIndex) -> VReg {
524 let class = self.vregs[index]
525 .class
526 .expect("trying to get a VReg before observing its class");
527 VReg::new(index.index(), class)
528 }
529
530 pub fn observe_vreg_class(&mut self, vreg: VReg) {
533 let old_class = self.vregs[vreg].class.replace(vreg.class());
534 debug_assert!(old_class == None || old_class == Some(vreg.class()));
537 }
538
539 pub fn is_vreg_used(&self, index: VRegIndex) -> bool {
541 self.vregs[index].class.is_some()
542 }
543}
544
545#[derive(Clone, Debug, Default)]
546pub struct SpillSetRanges {
547 pub btree: BTreeMap<LiveRangeKey, SpillSetIndex>,
548}
549
550#[derive(Clone, Debug)]
551pub struct SpillSlotData {
552 pub ranges: SpillSetRanges,
553 pub slots: u32,
554 pub alloc: Allocation,
555}
556
557#[derive(Clone, Debug, Default)]
558pub struct SpillSlotList {
559 pub slots: SmallVec<[SpillSlotIndex; 32]>,
560 pub probe_start: usize,
561}
562
563impl SpillSlotList {
564 pub(crate) fn next_index(&self, index: usize) -> usize {
567 debug_assert!(index < self.slots.len());
568 if index == self.slots.len() - 1 {
569 0
570 } else {
571 index + 1
572 }
573 }
574}
575
576#[derive(Clone, Debug, Default)]
577pub struct PrioQueue {
578 pub heap: alloc::collections::BinaryHeap<PrioQueueEntry>,
579}
580
581#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
582pub struct PrioQueueEntry {
583 pub prio: u32,
584 pub bundle: LiveBundleIndex,
585 pub reg_hint: PReg,
586}
587
588#[derive(Clone, Debug)]
589pub struct LiveRangeSet {
590 pub btree: BTreeMap<LiveRangeKey, LiveRangeIndex>,
591}
592
593#[derive(Clone, Copy, Debug)]
594pub struct LiveRangeKey {
595 pub from: u32,
596 pub to: u32,
597}
598
599impl LiveRangeKey {
600 #[inline(always)]
601 pub fn from_range(range: &CodeRange) -> Self {
602 Self {
603 from: range.from.to_index(),
604 to: range.to.to_index(),
605 }
606 }
607
608 #[inline(always)]
609 pub fn to_range(&self) -> CodeRange {
610 CodeRange {
611 from: ProgPoint::from_index(self.from),
612 to: ProgPoint::from_index(self.to),
613 }
614 }
615}
616
617impl core::cmp::PartialEq for LiveRangeKey {
618 #[inline(always)]
619 fn eq(&self, other: &Self) -> bool {
620 self.to > other.from && self.from < other.to
621 }
622}
623impl core::cmp::Eq for LiveRangeKey {}
624impl core::cmp::PartialOrd for LiveRangeKey {
625 #[inline(always)]
626 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
627 Some(self.cmp(other))
628 }
629}
630impl core::cmp::Ord for LiveRangeKey {
631 #[inline(always)]
632 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
633 if self.to <= other.from {
634 core::cmp::Ordering::Less
635 } else if self.from >= other.to {
636 core::cmp::Ordering::Greater
637 } else {
638 core::cmp::Ordering::Equal
639 }
640 }
641}
642
643pub struct PrioQueueComparator<'a> {
644 pub prios: &'a [usize],
645}
646impl<'a> ContainerComparator for PrioQueueComparator<'a> {
647 type Ix = LiveBundleIndex;
648 fn compare(&self, a: Self::Ix, b: Self::Ix) -> core::cmp::Ordering {
649 self.prios[a.index()].cmp(&self.prios[b.index()])
650 }
651}
652
653impl PrioQueue {
654 #[inline(always)]
655 pub fn insert(&mut self, bundle: LiveBundleIndex, prio: usize, reg_hint: PReg) {
656 self.heap.push(PrioQueueEntry {
657 prio: prio as u32,
658 bundle,
659 reg_hint,
660 });
661 }
662
663 #[inline(always)]
664 pub fn is_empty(self) -> bool {
665 self.heap.is_empty()
666 }
667
668 #[inline(always)]
669 pub fn pop(&mut self) -> Option<(LiveBundleIndex, PReg)> {
670 self.heap.pop().map(|entry| (entry.bundle, entry.reg_hint))
671 }
672}
673
674impl LiveRangeSet {
675 pub(crate) fn new() -> Self {
676 Self {
677 btree: BTreeMap::default(),
678 }
679 }
680}
681
682#[derive(Clone, Debug)]
683pub struct InsertedMove {
684 pub pos_prio: PosWithPrio,
685 pub from_alloc: Allocation,
686 pub to_alloc: Allocation,
687 pub to_vreg: VReg,
688}
689
690#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
691pub enum InsertMovePrio {
692 InEdgeMoves,
693 Regular,
694 MultiFixedRegInitial,
695 MultiFixedRegSecondary,
696 ReusedInput,
697 OutEdgeMoves,
698}
699
700#[derive(Debug, Default)]
701pub struct InsertedMoves {
702 pub moves: Vec<InsertedMove>,
703}
704
705impl InsertedMoves {
706 pub fn push(
707 &mut self,
708 pos: ProgPoint,
709 prio: InsertMovePrio,
710 from_alloc: Allocation,
711 to_alloc: Allocation,
712 to_vreg: VReg,
713 ) {
714 trace!(
715 "insert_move: pos {:?} prio {:?} from_alloc {:?} to_alloc {:?} to_vreg {:?}",
716 pos,
717 prio,
718 from_alloc,
719 to_alloc,
720 to_vreg
721 );
722 if from_alloc == to_alloc {
723 trace!(" -> skipping move with same source and dest");
724 return;
725 }
726 if let Some(from) = from_alloc.as_reg() {
727 debug_assert_eq!(from.class(), to_vreg.class());
728 }
729 if let Some(to) = to_alloc.as_reg() {
730 debug_assert_eq!(to.class(), to_vreg.class());
731 }
732 self.moves.push(InsertedMove {
733 pos_prio: PosWithPrio {
734 pos,
735 prio: prio as u32,
736 },
737 from_alloc,
738 to_alloc,
739 to_vreg,
740 });
741 }
742}
743
744#[derive(Clone, Debug, Default)]
745pub struct Edits {
746 edits: Vec<(PosWithPrio, Edit)>,
747}
748
749impl Edits {
750 #[inline(always)]
751 pub fn with_capacity(n: usize) -> Self {
752 Self {
753 edits: Vec::with_capacity(n),
754 }
755 }
756
757 #[inline(always)]
758 pub fn len(&self) -> usize {
759 self.edits.len()
760 }
761
762 #[inline(always)]
763 pub fn iter(&self) -> impl Iterator<Item = &(PosWithPrio, Edit)> {
764 self.edits.iter()
765 }
766
767 #[inline(always)]
768 pub fn drain_edits(&mut self) -> impl Iterator<Item = (ProgPoint, Edit)> + '_ {
769 self.edits.drain(..).map(|(pos, edit)| (pos.pos, edit))
770 }
771
772 #[inline(always)]
775 pub fn sort(&mut self) {
776 self.edits.sort_by_key(|&(pos_prio, _)| pos_prio.key());
777 }
778
779 pub fn add(&mut self, pos_prio: PosWithPrio, from: Allocation, to: Allocation) {
780 if from != to {
781 if from.is_reg() && to.is_reg() {
782 debug_assert_eq!(from.as_reg().unwrap().class(), to.as_reg().unwrap().class());
783 }
784 self.edits.push((pos_prio, Edit::Move { from, to }));
785 }
786 }
787}
788
789#[derive(Clone, Copy, Debug, PartialEq, Eq)]
792#[repr(C)]
793pub struct PosWithPrio {
794 pub prio: u32,
795 pub pos: ProgPoint,
796}
797
798impl PosWithPrio {
799 #[inline]
800 pub fn key(self) -> u64 {
801 u64_key(self.pos.to_index(), self.prio)
802 }
803}
804
805#[derive(Clone, Copy, Debug, Default)]
806#[cfg_attr(feature = "enable-serde", derive(serde::Serialize, serde::Deserialize))]
807pub struct Stats {
808 pub livein_blocks: usize,
809 pub livein_iterations: usize,
810 pub initial_liverange_count: usize,
811 pub merged_bundle_count: usize,
812 pub process_bundle_count: usize,
813 pub process_bundle_reg_probes_fixed: usize,
814 pub process_bundle_reg_success_fixed: usize,
815 pub process_bundle_bounding_range_probe_start_any: usize,
816 pub process_bundle_bounding_range_probes_any: usize,
817 pub process_bundle_bounding_range_success_any: usize,
818 pub process_bundle_reg_probe_start_any: usize,
819 pub process_bundle_reg_probes_any: usize,
820 pub process_bundle_reg_success_any: usize,
821 pub evict_bundle_event: usize,
822 pub evict_bundle_count: usize,
823 pub splits: usize,
824 pub splits_clobbers: usize,
825 pub splits_hot: usize,
826 pub splits_conflicts: usize,
827 pub splits_defs: usize,
828 pub splits_all: usize,
829 pub final_liverange_count: usize,
830 pub final_bundle_count: usize,
831 pub spill_bundle_count: usize,
832 pub spill_bundle_reg_probes: usize,
833 pub spill_bundle_reg_success: usize,
834 pub blockparam_ins_count: usize,
835 pub blockparam_outs_count: usize,
836 pub halfmoves_count: usize,
837 pub edits_count: usize,
838}
839
840#[inline(always)]
846pub fn u64_key(b: u32, a: u32) -> u64 {
847 a as u64 | (b as u64) << 32
848}
849#[inline(always)]
850pub fn u128_key(d: u32, c: u32, b: u32, a: u32) -> u128 {
851 a as u128 | (b as u128) << 32 | (c as u128) << 64 | (d as u128) << 96
852}