cranelift_frontend/frontend/
safepoints.rs

1//! Support for safepoints and stack maps.
2
3use super::*;
4use crate::{HashMap, HashSet};
5use core::ops::{Index, IndexMut};
6
7#[derive(Clone, Copy)]
8#[repr(u8)]
9enum SlotSize {
10    Size8 = 0,
11    Size16 = 1,
12    Size32 = 2,
13    Size64 = 3,
14    Size128 = 4,
15    // If adding support for more slot sizes, update `SLOT_SIZE_LEN` below.
16}
17const SLOT_SIZE_LEN: usize = 5;
18
19impl TryFrom<ir::Type> for SlotSize {
20    type Error = &'static str;
21
22    fn try_from(ty: ir::Type) -> Result<Self, Self::Error> {
23        Self::new(ty.bytes()).ok_or("type is not supported in stack maps")
24    }
25}
26
27impl SlotSize {
28    fn new(bytes: u32) -> Option<Self> {
29        match bytes {
30            1 => Some(Self::Size8),
31            2 => Some(Self::Size16),
32            4 => Some(Self::Size32),
33            8 => Some(Self::Size64),
34            16 => Some(Self::Size128),
35            _ => None,
36        }
37    }
38
39    fn unwrap_new(bytes: u32) -> Self {
40        Self::new(bytes).unwrap_or_else(|| panic!("cannot create a `SlotSize` for {bytes} bytes"))
41    }
42}
43
44/// A map from every `SlotSize` to a `T`.
45struct SlotSizeMap<T>([T; SLOT_SIZE_LEN]);
46
47impl<T> Default for SlotSizeMap<T>
48where
49    T: Default,
50{
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56impl<T> Index<SlotSize> for SlotSizeMap<T> {
57    type Output = T;
58    fn index(&self, index: SlotSize) -> &Self::Output {
59        self.get(index)
60    }
61}
62
63impl<T> IndexMut<SlotSize> for SlotSizeMap<T> {
64    fn index_mut(&mut self, index: SlotSize) -> &mut Self::Output {
65        self.get_mut(index)
66    }
67}
68
69impl<T> SlotSizeMap<T> {
70    fn new() -> Self
71    where
72        T: Default,
73    {
74        Self([
75            T::default(),
76            T::default(),
77            T::default(),
78            T::default(),
79            T::default(),
80        ])
81    }
82
83    fn clear(&mut self)
84    where
85        T: Default,
86    {
87        *self = Self::new();
88    }
89
90    fn get(&self, size: SlotSize) -> &T {
91        let index = size as u8 as usize;
92        &self.0[index]
93    }
94
95    fn get_mut(&mut self, size: SlotSize) -> &mut T {
96        let index = size as u8 as usize;
97        &mut self.0[index]
98    }
99}
100
101/// A set of live values.
102///
103/// Make sure to copy to a vec and sort, or something, before iterating over the
104/// values to ensure deterministic output.
105type LiveSet = HashSet<ir::Value>;
106
107/// A worklist of blocks' post-order indices that we need to process.
108#[derive(Default)]
109struct Worklist {
110    /// Stack of blocks to process.
111    stack: Vec<u32>,
112
113    /// The set of blocks that need to be updated.
114    ///
115    /// This is a subset of the elements present in `self.stack`, *not* the
116    /// exact same elements. `self.stack` is allowed to have duplicates, and
117    /// once we pop the first occurrence of a duplicate, we remove it from this
118    /// set, since it no longer needs updates at that point. This potentially
119    /// uses more stack space than necessary, but prefers processing immediate
120    /// predecessors, and therefore inner loop bodies before continuing to
121    /// process outer loop bodies. This ultimately results in fewer iterations
122    /// required to reach a fixed point.
123    need_updates: HashSet<u32>,
124}
125
126impl Extend<u32> for Worklist {
127    fn extend<T>(&mut self, iter: T)
128    where
129        T: IntoIterator<Item = u32>,
130    {
131        for block_index in iter {
132            self.push(block_index);
133        }
134    }
135}
136
137impl Worklist {
138    fn clear(&mut self) {
139        let Worklist {
140            stack,
141            need_updates,
142        } = self;
143        stack.clear();
144        need_updates.clear();
145    }
146
147    fn reserve(&mut self, capacity: usize) {
148        let Worklist {
149            stack,
150            need_updates,
151        } = self;
152        stack.reserve(capacity);
153        need_updates.reserve(capacity);
154    }
155
156    fn push(&mut self, block_index: u32) {
157        // Mark this block as needing an update. If it wasn't in `self.stack`,
158        // now it is and it needs an update. If it was already in `self.stack`,
159        // then pushing this copy logically hoists it to the top of the
160        // stack. See the above note about processing inner-most loops first.
161        self.need_updates.insert(block_index);
162        self.stack.push(block_index);
163    }
164
165    fn pop(&mut self) -> Option<u32> {
166        while let Some(block_index) = self.stack.pop() {
167            // If this block was pushed multiple times, we only need to update
168            // it once, so remove it from the need-updates set. In other words
169            // it was logically hoisted up to the top of the stack, while this
170            // entry was left behind, and we already popped the hoisted
171            // copy. See the above note about processing inner-most loops first.
172            if self.need_updates.remove(&block_index) {
173                return Some(block_index);
174            }
175        }
176        None
177    }
178}
179
180/// A simple liveness analysis.
181///
182/// This analysis is used to determine which needs-stack-map values are live
183/// across safepoint instructions.
184///
185/// This is a backwards analysis, from uses (which mark values live) to defs
186/// (which remove values from the live set) and from successor blocks to
187/// predecessor blocks.
188///
189/// We compute two live sets for each block:
190///
191/// 1. The live-in set, which is the set of values that are live when control
192///    enters the block.
193///
194/// 2. The live-out set, which is the set of values that are live when control
195///    exits the block.
196///
197/// A block's live-out set is the union of its successors' live-in sets. A
198/// block's live-in set is the set of values that are still live after the
199/// block's instructions have been processed.
200///
201/// ```text
202/// live_in(block) = union(live_out(s) for s in successors(block))
203/// live_out(block) = live_in(block) - defs(block) + uses(block)
204/// ```
205///
206/// Whenever we update a block's live-in set, we must reprocess all of its
207/// predecessors, because those predecessors' live-out sets depend on this
208/// block's live-in set. Processing continues until the live sets stop changing
209/// and we've reached a fixed-point. Each time we process a block, its live sets
210/// can only grow monotonically, and therefore we know that the computation will
211/// reach its fixed-point and terminate. This fixed-point is implemented with a
212/// classic worklist algorithm.
213///
214/// The worklist is seeded such that we initially process blocks in post-order,
215/// which ensures that, when we have a loop-free control-flow graph, we only
216/// process each block once. We pop a block off the worklist for
217/// processing. Whenever a block's live-in set is updated during processing, we
218/// push its predecessors onto the worklist so that their live-in sets can be
219/// updated. Once the worklist is empty, there are no more blocks needing
220/// updates, and we've reached the fixed-point.
221///
222/// Note: For simplicity, we do not flow liveness from block parameters back to
223/// branch arguments, and instead always consider branch arguments live.
224///
225/// Furthermore, we do not differentiate between uses of a needs-stack-map value
226/// that ultimately flow into a side-effecting operation versus uses that
227/// themselves are not live. This could be tightened up in the future, but we're
228/// starting with the easiest, simplest thing. It also means that we do not need
229/// `O(all values)` space, only `O(needs-stack-map values)`. Finally, none of
230/// our mid-end optimization passes have run at this point in time yet, so there
231/// probably isn't much, if any, dead code.
232///
233/// After we've computed the live-in and -out sets for each block, we pass once
234/// more over each block, processing its instructions again. This time, we
235/// record the precise set of needs-stack-map values that are live across each
236/// safepoint instruction inside the block, which is the final output of this
237/// analysis.
238pub(crate) struct LivenessAnalysis {
239    /// Reusable depth-first search state for traversing a function's blocks.
240    dfs: Dfs,
241
242    /// The cached post-order traversal of the function's blocks.
243    post_order: Vec<ir::Block>,
244
245    /// A secondary map from each block to its index in `post_order`.
246    block_to_index: SecondaryMap<ir::Block, u32>,
247
248    /// A mapping from each block's post-order index to the post-order indices
249    /// of its direct (non-transitive) predecessors.
250    predecessors: Vec<SmallVec<[u32; 4]>>,
251
252    /// A worklist of blocks to process. Used to determine which blocks need
253    /// updates cascaded to them and when we reach a fixed-point.
254    worklist: Worklist,
255
256    /// A map from a block's post-order index to its live-in set.
257    live_ins: Vec<LiveSet>,
258
259    /// A map from a block's post-order index to its live-out set.
260    live_outs: Vec<LiveSet>,
261
262    /// The set of each needs-stack-map value that is currently live while
263    /// processing a block.
264    currently_live: LiveSet,
265
266    /// A mapping from each safepoint instruction to the set of needs-stack-map
267    /// values that are live across it.
268    safepoints: HashMap<ir::Inst, SmallVec<[ir::Value; 4]>>,
269
270    /// The set of values that are live across *any* safepoint in the function,
271    /// i.e. the union of all the values in the `safepoints` map.
272    live_across_any_safepoint: EntitySet<ir::Value>,
273}
274
275impl Default for LivenessAnalysis {
276    fn default() -> Self {
277        Self {
278            dfs: Default::default(),
279            post_order: Default::default(),
280            block_to_index: SecondaryMap::with_default(u32::MAX),
281            predecessors: Default::default(),
282            worklist: Default::default(),
283            live_ins: Default::default(),
284            live_outs: Default::default(),
285            currently_live: Default::default(),
286            safepoints: Default::default(),
287            live_across_any_safepoint: Default::default(),
288        }
289    }
290}
291
292#[derive(Clone, Copy, PartialEq, Eq)]
293enum RecordSafepoints {
294    Yes,
295    No,
296}
297
298impl LivenessAnalysis {
299    /// Clear and reset all internal state such that this analysis is ready for
300    /// reuse with a new function.
301    pub fn clear(&mut self) {
302        let LivenessAnalysis {
303            dfs,
304            post_order,
305            block_to_index,
306            predecessors,
307            worklist,
308            live_ins,
309            live_outs,
310            currently_live,
311            safepoints,
312            live_across_any_safepoint,
313        } = self;
314        dfs.clear();
315        post_order.clear();
316        block_to_index.clear();
317        predecessors.clear();
318        worklist.clear();
319        live_ins.clear();
320        live_outs.clear();
321        currently_live.clear();
322        safepoints.clear();
323        live_across_any_safepoint.clear();
324    }
325
326    /// Given that we've initialized `self.post_order`, reserve capacity for the
327    /// various data structures we use during our analysis.
328    fn reserve_capacity(&mut self, func: &Function) {
329        let LivenessAnalysis {
330            dfs: _,
331            post_order,
332            block_to_index,
333            predecessors,
334            worklist,
335            live_ins,
336            live_outs,
337            currently_live: _,
338            safepoints: _,
339            live_across_any_safepoint: _,
340        } = self;
341
342        block_to_index.resize(func.dfg.num_blocks());
343
344        let capacity = post_order.len();
345        worklist.reserve(capacity);
346        predecessors.resize(capacity, Default::default());
347        live_ins.resize(capacity, Default::default());
348        live_outs.resize(capacity, Default::default());
349    }
350
351    fn initialize_block_to_index_map(&mut self) {
352        for (block_index, block) in self.post_order.iter().enumerate() {
353            self.block_to_index[*block] = u32::try_from(block_index).unwrap();
354        }
355    }
356
357    fn initialize_predecessors_map(&mut self, func: &Function) {
358        for (block_index, block) in self.post_order.iter().enumerate() {
359            let block_index = u32::try_from(block_index).unwrap();
360            for succ in func.block_successors(*block) {
361                let succ_index = self.block_to_index[succ];
362                debug_assert_ne!(succ_index, u32::MAX);
363                let succ_index = usize::try_from(succ_index).unwrap();
364                self.predecessors[succ_index].push(block_index);
365            }
366        }
367    }
368
369    /// Process a value's definition, removing it from the currently-live set.
370    fn process_def(&mut self, val: ir::Value) {
371        if self.currently_live.remove(&val) {
372            log::trace!("liveness:   defining {val:?}, removing it from the live set");
373        }
374    }
375
376    /// Record the live set of needs-stack-map values at the given safepoint.
377    fn record_safepoint(&mut self, func: &Function, inst: Inst) {
378        log::trace!(
379            "liveness:   found safepoint: {inst:?}: {}",
380            func.dfg.display_inst(inst)
381        );
382        log::trace!("liveness:     live set = {:?}", self.currently_live);
383
384        let mut live = self.currently_live.iter().copied().collect::<SmallVec<_>>();
385        // Keep order deterministic since we add stack map entries in this
386        // order.
387        live.sort();
388
389        self.live_across_any_safepoint.extend(live.iter().copied());
390        self.safepoints.insert(inst, live);
391    }
392
393    /// Process a use of a needs-stack-map value, inserting it into the
394    /// currently-live set.
395    fn process_use(&mut self, func: &Function, inst: Inst, val: Value) {
396        if self.currently_live.insert(val) {
397            log::trace!(
398                "liveness:   found use of {val:?}, marking it live: {inst:?}: {}",
399                func.dfg.display_inst(inst)
400            );
401        }
402    }
403
404    /// Process all the instructions in a block in reverse order.
405    fn process_block(
406        &mut self,
407        func: &mut Function,
408        stack_map_values: &EntitySet<ir::Value>,
409        block_index: usize,
410        record_safepoints: RecordSafepoints,
411    ) {
412        let block = self.post_order[block_index];
413        log::trace!("liveness: traversing {block:?}");
414
415        // Reset the currently-live set to this block's live-out set.
416        self.currently_live.clear();
417        self.currently_live
418            .extend(self.live_outs[block_index].iter().copied());
419
420        // Now process this block's instructions, incrementally building its
421        // live-in set inside the currently-live set.
422        let mut option_inst = func.layout.last_inst(block);
423        while let Some(inst) = option_inst {
424            // Process any needs-stack-map values defined by this instruction.
425            for val in func.dfg.inst_results(inst) {
426                self.process_def(*val);
427            }
428
429            // If this instruction is a safepoint and we've been asked to record
430            // safepoints, then do so.
431            let opcode = func.dfg.insts[inst].opcode();
432            if record_safepoints == RecordSafepoints::Yes && opcode.is_safepoint() {
433                self.record_safepoint(func, inst);
434            }
435
436            // Process any needs-stack-map values used by this instruction.
437            for val in func.dfg.inst_values(inst) {
438                let val = func.dfg.resolve_aliases(val);
439                if stack_map_values.contains(val) {
440                    self.process_use(func, inst, val);
441                }
442            }
443
444            option_inst = func.layout.prev_inst(inst);
445        }
446
447        // After we've processed this block's instructions, remove its
448        // parameters from the live set. This is part of step (1).
449        for val in func.dfg.block_params(block) {
450            self.process_def(*val);
451        }
452    }
453
454    /// Run the liveness analysis on the given function.
455    pub fn run(&mut self, func: &mut Function, stack_map_values: &EntitySet<ir::Value>) {
456        self.clear();
457        self.post_order.extend(self.dfs.post_order_iter(func));
458        self.reserve_capacity(func);
459        self.initialize_block_to_index_map();
460        self.initialize_predecessors_map(func);
461
462        // Initially enqueue all blocks for processing. We push them in reverse
463        // post-order (which yields them in post-order when popped) because if
464        // there are no back-edges in the control-flow graph, post-order will
465        // result in only a single pass over the blocks.
466        self.worklist
467            .extend((0..u32::try_from(self.post_order.len()).unwrap()).rev());
468
469        // Pump the worklist until we reach a fixed-point.
470        while let Some(block_index) = self.worklist.pop() {
471            let block_index = usize::try_from(block_index).unwrap();
472
473            // Because our live sets grow monotonically, we just need to see if
474            // the size changed to determine whether the whole set changed.
475            let initial_live_in_len = self.live_ins[block_index].len();
476
477            // The live-out set for a block is the union of the live-in sets of
478            // its successors.
479            for successor in func.block_successors(self.post_order[block_index]) {
480                let successor_index = self.block_to_index[successor];
481                debug_assert_ne!(successor_index, u32::MAX);
482                let successor_index = usize::try_from(successor_index).unwrap();
483                self.live_outs[block_index].extend(self.live_ins[successor_index].iter().copied());
484            }
485
486            // Process the block to compute its live-in set, but do not record
487            // safepoints yet, as we haven't yet processed loop back edges (see
488            // below).
489            self.process_block(func, stack_map_values, block_index, RecordSafepoints::No);
490
491            // The live-in set for a block is the set of values that are still
492            // live after the block's instructions have been processed.
493            self.live_ins[block_index].extend(self.currently_live.iter().copied());
494
495            // If the live-in set changed, then we need to revisit all this
496            // block's predecessors.
497            if self.live_ins[block_index].len() != initial_live_in_len {
498                self.worklist
499                    .extend(self.predecessors[block_index].iter().copied());
500            }
501        }
502
503        // Once we've reached a fixed-point, compute the actual live set for
504        // each safepoint instruction in each block, backwards from the block's
505        // live-out set.
506        for block_index in 0..self.post_order.len() {
507            self.process_block(func, stack_map_values, block_index, RecordSafepoints::Yes);
508
509            debug_assert_eq!(
510                self.currently_live, self.live_ins[block_index],
511                "when we recompute the live-in set for a block as part of \
512                 computing live sets at each safepoint, we should get the same \
513                 result we computed in the fixed-point"
514            );
515        }
516    }
517}
518
519/// A mapping from each needs-stack-map value to its associated stack slot.
520///
521/// Internally maintains free lists for stack slots that won't be used again, so
522/// that we can reuse them and minimize the number of stack slots we need to
523/// allocate.
524#[derive(Default)]
525struct StackSlots {
526    /// A mapping from each needs-stack-map value that is live across some
527    /// safepoint to the stack slot that it resides within. Note that if a
528    /// needs-stack-map value is never live across a safepoint, then we won't
529    /// ever add it to this map, it can remain in a virtual register for the
530    /// duration of its lifetime, and we won't replace all its uses with reloads
531    /// and all that stuff.
532    stack_slots: HashMap<ir::Value, ir::StackSlot>,
533
534    /// A map from slot size to free stack slots that are not being used
535    /// anymore. This allows us to reuse stack slots across multiple values
536    /// helps cut down on the ultimate size of our stack frames.
537    free_stack_slots: SlotSizeMap<SmallVec<[ir::StackSlot; 4]>>,
538}
539
540impl StackSlots {
541    fn clear(&mut self) {
542        let StackSlots {
543            stack_slots,
544            free_stack_slots,
545        } = self;
546        stack_slots.clear();
547        free_stack_slots.clear();
548    }
549
550    fn get(&self, val: ir::Value) -> Option<ir::StackSlot> {
551        self.stack_slots.get(&val).copied()
552    }
553
554    fn get_or_create_stack_slot(&mut self, func: &mut Function, val: ir::Value) -> ir::StackSlot {
555        *self.stack_slots.entry(val).or_insert_with(|| {
556            log::trace!("rewriting:     {val:?} needs a stack slot");
557            let ty = func.dfg.value_type(val);
558            let size = ty.bytes();
559            match self.free_stack_slots[SlotSize::unwrap_new(size)].pop() {
560                Some(slot) => {
561                    log::trace!("rewriting:       reusing free stack slot {slot:?} for {val:?}");
562                    slot
563                }
564                None => {
565                    debug_assert!(size.is_power_of_two());
566                    let log2_size = size.ilog2();
567                    let slot = func.create_sized_stack_slot(ir::StackSlotData::new(
568                        ir::StackSlotKind::ExplicitSlot,
569                        size,
570                        log2_size.try_into().unwrap(),
571                    ));
572                    log::trace!("rewriting:       created new stack slot {slot:?} for {val:?}");
573                    slot
574                }
575            }
576        })
577    }
578
579    fn free_stack_slot(&mut self, size: SlotSize, slot: ir::StackSlot) {
580        log::trace!("rewriting:     returning {slot:?} to the free list");
581        self.free_stack_slots[size].push(slot);
582    }
583}
584
585/// A pass to rewrite a function's instructions to spill and reload values that
586/// are live across safepoints.
587///
588/// A single `SafepointSpiller` instance may be reused to rewrite many
589/// functions, amortizing the cost of its internal allocations and avoiding
590/// repeated `malloc` and `free` calls.
591#[derive(Default)]
592pub(super) struct SafepointSpiller {
593    liveness: LivenessAnalysis,
594    stack_slots: StackSlots,
595}
596
597impl SafepointSpiller {
598    /// Clear and reset all internal state such that this pass is ready to run
599    /// on a new function.
600    pub fn clear(&mut self) {
601        let SafepointSpiller {
602            liveness,
603            stack_slots,
604        } = self;
605        liveness.clear();
606        stack_slots.clear();
607    }
608
609    /// Identify needs-stack-map values that are live across safepoints, and
610    /// rewrite the function's instructions to spill and reload them as
611    /// necessary.
612    pub fn run(&mut self, func: &mut Function, stack_map_values: &EntitySet<ir::Value>) {
613        log::trace!(
614            "values needing inclusion in stack maps: {:?}",
615            stack_map_values
616        );
617        log::trace!(
618            "before inserting safepoint spills and reloads:\n{}",
619            func.display()
620        );
621
622        self.clear();
623        self.liveness.run(func, stack_map_values);
624        self.rewrite(func);
625
626        log::trace!(
627            "after inserting safepoint spills and reloads:\n{}",
628            func.display()
629        );
630    }
631
632    /// Spill this value to a stack slot if it has been declared that it must be
633    /// included in stack maps and is live across any safepoints.
634    ///
635    /// The given cursor must point just after this value's definition.
636    fn rewrite_def(&mut self, pos: &mut FuncCursor<'_>, val: ir::Value) {
637        if let Some(slot) = self.stack_slots.get(val) {
638            let i = pos.ins().stack_store(val, slot, 0);
639            log::trace!(
640                "rewriting:   spilling {val:?} to {slot:?}: {}",
641                pos.func.dfg.display_inst(i)
642            );
643
644            // Now that we've defined this value, there cannot be any more uses
645            // of it, and therefore this stack slot is now available for reuse.
646            let ty = pos.func.dfg.value_type(val);
647            let size = SlotSize::try_from(ty).unwrap();
648            self.stack_slots.free_stack_slot(size, slot);
649        }
650    }
651
652    /// Add a stack map entry for each needs-stack-map value that is live across
653    /// the given safepoint instruction.
654    ///
655    /// This will additionally assign stack slots to needs-stack-map values, if
656    /// no such assignment has already been made.
657    fn rewrite_safepoint(&mut self, func: &mut Function, inst: ir::Inst) {
658        log::trace!(
659            "rewriting:   found safepoint: {inst:?}: {}",
660            func.dfg.display_inst(inst)
661        );
662
663        let live = self
664            .liveness
665            .safepoints
666            .get(&inst)
667            .expect("should only call `rewrite_safepoint` on safepoint instructions");
668
669        for val in live {
670            // Get or create the stack slot for this live needs-stack-map value.
671            let slot = self.stack_slots.get_or_create_stack_slot(func, *val);
672
673            log::trace!(
674                "rewriting:     adding stack map entry for {val:?} at {slot:?}: {}",
675                func.dfg.display_inst(inst)
676            );
677            let ty = func.dfg.value_type(*val);
678            func.dfg.append_user_stack_map_entry(
679                inst,
680                ir::UserStackMapEntry {
681                    ty,
682                    slot,
683                    offset: 0,
684                },
685            );
686        }
687    }
688
689    /// If `val` is a needs-stack-map value that has been spilled to a stack
690    /// slot, then rewrite `val` to be a load from its associated stack
691    /// slot.
692    ///
693    /// Returns `true` if `val` was rewritten, `false` if not.
694    ///
695    /// The given cursor must point just before the use of the value that we are
696    /// replacing.
697    fn rewrite_use(&mut self, pos: &mut FuncCursor<'_>, val: &mut ir::Value) -> bool {
698        if !self.liveness.live_across_any_safepoint.contains(*val) {
699            return false;
700        }
701
702        let old_val = *val;
703        log::trace!("rewriting:     found use of {old_val:?}");
704
705        let ty = pos.func.dfg.value_type(*val);
706        let slot = self.stack_slots.get_or_create_stack_slot(pos.func, *val);
707        *val = pos.ins().stack_load(ty, slot, 0);
708
709        log::trace!(
710            "rewriting:     reloading {old_val:?}: {}",
711            pos.func
712                .dfg
713                .display_inst(pos.func.dfg.value_def(*val).unwrap_inst())
714        );
715
716        true
717    }
718
719    /// Rewrite the function's instructions to spill and reload values that are
720    /// live across safepoints:
721    ///
722    /// 1. Definitions of needs-stack-map values that are live across some
723    ///    safepoint need to be spilled to their assigned stack slot.
724    ///
725    /// 2. Instructions that are themselves safepoints must have stack map
726    ///    entries added for the needs-stack-map values that are live across
727    ///    them.
728    ///
729    /// 3. Uses of needs-stack-map values that have been spilled to a stack slot
730    ///    need to be replaced with reloads from the slot.
731    fn rewrite(&mut self, func: &mut Function) {
732        // Shared temporary storage for operand and result lists.
733        let mut vals: SmallVec<[_; 8]> = Default::default();
734
735        // Rewrite the function's instructions in post-order. This ensures that
736        // we rewrite uses before defs, and therefore once we see a def we know
737        // its stack slot will never be used for that value again. Therefore,
738        // the slot can be reappropriated for a new needs-stack-map value with a
739        // non-overlapping live range. See `rewrite_def` and `free_stack_slots`
740        // for more details.
741        for block_index in 0..self.liveness.post_order.len() {
742            let block = self.liveness.post_order[block_index];
743            log::trace!("rewriting: processing {block:?}");
744
745            let mut option_inst = func.layout.last_inst(block);
746            while let Some(inst) = option_inst {
747                // If this instruction defines a needs-stack-map value that is
748                // live across a safepoint, then spill the value to its stack
749                // slot.
750                let mut pos = FuncCursor::new(func).after_inst(inst);
751                vals.extend_from_slice(pos.func.dfg.inst_results(inst));
752                for val in vals.drain(..) {
753                    self.rewrite_def(&mut pos, val);
754                }
755
756                // If this instruction is a safepoint, then we must add stack
757                // map entries for the needs-stack-map values that are live
758                // across it.
759                if self.liveness.safepoints.contains_key(&inst) {
760                    self.rewrite_safepoint(func, inst);
761                }
762
763                // Replace all uses of needs-stack-map values with loads from
764                // the value's associated stack slot.
765                let mut pos = FuncCursor::new(func).at_inst(inst);
766                vals.extend(pos.func.dfg.inst_values(inst));
767                let mut replaced_any = false;
768                for val in &mut vals {
769                    replaced_any |= self.rewrite_use(&mut pos, val);
770                }
771                if replaced_any {
772                    pos.func.dfg.overwrite_inst_values(inst, vals.drain(..));
773                    log::trace!(
774                        "rewriting:     updated {inst:?} operands with reloaded values: {}",
775                        pos.func.dfg.display_inst(inst)
776                    );
777                } else {
778                    vals.clear();
779                }
780
781                option_inst = func.layout.prev_inst(inst);
782            }
783
784            // Spill needs-stack-map values defined by block parameters to their
785            // associated stack slots.
786            let mut pos = FuncCursor::new(func).at_position(CursorPosition::Before(block));
787            pos.next_inst(); // Advance to the first instruction in the block.
788            vals.clear();
789            vals.extend_from_slice(pos.func.dfg.block_params(block));
790            for val in vals.drain(..) {
791                self.rewrite_def(&mut pos, val);
792            }
793        }
794    }
795}
796
797#[cfg(test)]
798mod tests {
799    use super::*;
800    use alloc::string::ToString;
801    use cranelift_codegen::isa::CallConv;
802
803    #[test]
804    fn needs_stack_map_and_loop() {
805        let mut sig = Signature::new(CallConv::SystemV);
806        sig.params.push(AbiParam::new(ir::types::I32));
807        sig.params.push(AbiParam::new(ir::types::I32));
808
809        let mut fn_ctx = FunctionBuilderContext::new();
810        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
811        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
812
813        let name = builder
814            .func
815            .declare_imported_user_function(ir::UserExternalName {
816                namespace: 0,
817                index: 0,
818            });
819        let mut sig = Signature::new(CallConv::SystemV);
820        sig.params.push(AbiParam::new(ir::types::I32));
821        let signature = builder.func.import_signature(sig);
822        let func_ref = builder.import_function(ir::ExtFuncData {
823            name: ir::ExternalName::user(name),
824            signature,
825            colocated: true,
826        });
827
828        // Here the value `v1` is technically not live but our single-pass liveness
829        // analysis treats every branch argument to a block as live to avoid
830        // needing to do a fixed-point loop.
831        //
832        //     block0(v0, v1):
833        //       call $foo(v0)
834        //       jump block0(v0, v1)
835        let block0 = builder.create_block();
836        builder.append_block_params_for_function_params(block0);
837        let a = builder.func.dfg.block_params(block0)[0];
838        let b = builder.func.dfg.block_params(block0)[1];
839        builder.declare_value_needs_stack_map(a);
840        builder.declare_value_needs_stack_map(b);
841        builder.switch_to_block(block0);
842        builder.ins().call(func_ref, &[a]);
843        builder.ins().jump(block0, &[a, b]);
844        builder.seal_all_blocks();
845        builder.finalize();
846
847        assert_eq_output!(
848            func.display().to_string(),
849            r#"
850function %sample(i32, i32) system_v {
851    ss0 = explicit_slot 4, align = 4
852    ss1 = explicit_slot 4, align = 4
853    sig0 = (i32) system_v
854    fn0 = colocated u0:0 sig0
855
856block0(v0: i32, v1: i32):
857    stack_store v0, ss0
858    stack_store v1, ss1
859    v4 = stack_load.i32 ss0
860    call fn0(v4), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
861    v2 = stack_load.i32 ss0
862    v3 = stack_load.i32 ss1
863    jump block0(v2, v3)
864}
865            "#
866        );
867    }
868
869    #[test]
870    fn needs_stack_map_simple() {
871        let _ = env_logger::try_init();
872
873        let sig = Signature::new(CallConv::SystemV);
874
875        let mut fn_ctx = FunctionBuilderContext::new();
876        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
877        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
878
879        let name = builder
880            .func
881            .declare_imported_user_function(ir::UserExternalName {
882                namespace: 0,
883                index: 0,
884            });
885        let mut sig = Signature::new(CallConv::SystemV);
886        sig.params.push(AbiParam::new(ir::types::I32));
887        let signature = builder.func.import_signature(sig);
888        let func_ref = builder.import_function(ir::ExtFuncData {
889            name: ir::ExternalName::user(name),
890            signature,
891            colocated: true,
892        });
893
894        // At each `call` we are losing one more value as no longer live, so
895        // each stack map should be one smaller than the last. `v3` is never
896        // live across a safepoint, so should never appear in a stack map. Note
897        // that a value that is an argument to the call, but is not live after
898        // the call, should not appear in the stack map. This is why `v0`
899        // appears in the first call's stack map, but not the second call's
900        // stack map.
901        //
902        //     block0:
903        //       v0 = needs stack map
904        //       v1 = needs stack map
905        //       v2 = needs stack map
906        //       v3 = needs stack map
907        //       call $foo(v3)
908        //       call $foo(v0)
909        //       call $foo(v1)
910        //       call $foo(v2)
911        //       return
912        let block0 = builder.create_block();
913        builder.append_block_params_for_function_params(block0);
914        builder.switch_to_block(block0);
915        let v0 = builder.ins().iconst(ir::types::I32, 0);
916        builder.declare_value_needs_stack_map(v0);
917        let v1 = builder.ins().iconst(ir::types::I32, 1);
918        builder.declare_value_needs_stack_map(v1);
919        let v2 = builder.ins().iconst(ir::types::I32, 2);
920        builder.declare_value_needs_stack_map(v2);
921        let v3 = builder.ins().iconst(ir::types::I32, 3);
922        builder.declare_value_needs_stack_map(v3);
923        builder.ins().call(func_ref, &[v3]);
924        builder.ins().call(func_ref, &[v0]);
925        builder.ins().call(func_ref, &[v1]);
926        builder.ins().call(func_ref, &[v2]);
927        builder.ins().return_(&[]);
928        builder.seal_all_blocks();
929        builder.finalize();
930
931        assert_eq_output!(
932            func.display().to_string(),
933            r#"
934function %sample() system_v {
935    ss0 = explicit_slot 4, align = 4
936    ss1 = explicit_slot 4, align = 4
937    ss2 = explicit_slot 4, align = 4
938    sig0 = (i32) system_v
939    fn0 = colocated u0:0 sig0
940
941block0:
942    v0 = iconst.i32 0
943    stack_store v0, ss2  ; v0 = 0
944    v1 = iconst.i32 1
945    stack_store v1, ss1  ; v1 = 1
946    v2 = iconst.i32 2
947    stack_store v2, ss0  ; v2 = 2
948    v3 = iconst.i32 3
949    call fn0(v3), stack_map=[i32 @ ss2+0, i32 @ ss1+0, i32 @ ss0+0]  ; v3 = 3
950    v6 = stack_load.i32 ss2
951    call fn0(v6), stack_map=[i32 @ ss1+0, i32 @ ss0+0]
952    v5 = stack_load.i32 ss1
953    call fn0(v5), stack_map=[i32 @ ss0+0]
954    v4 = stack_load.i32 ss0
955    call fn0(v4)
956    return
957}
958            "#
959        );
960    }
961
962    #[test]
963    fn needs_stack_map_and_post_order_early_return() {
964        let mut sig = Signature::new(CallConv::SystemV);
965        sig.params.push(AbiParam::new(ir::types::I32));
966
967        let mut fn_ctx = FunctionBuilderContext::new();
968        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
969        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
970
971        let name = builder
972            .func
973            .declare_imported_user_function(ir::UserExternalName {
974                namespace: 0,
975                index: 0,
976            });
977        let signature = builder
978            .func
979            .import_signature(Signature::new(CallConv::SystemV));
980        let func_ref = builder.import_function(ir::ExtFuncData {
981            name: ir::ExternalName::user(name),
982            signature,
983            colocated: true,
984        });
985
986        // Here we rely on the post-order to make sure that we never visit block
987        // 4 and add `v1` to our live set, then visit block 2 and add `v1` to
988        // its stack map even though `v1` is not in scope. Thanksfully, that
989        // sequence is impossible because it would be an invalid post-order
990        // traversal. The only valid post-order traversals are [3, 1, 2, 0] and
991        // [2, 3, 1, 0].
992        //
993        //     block0(v0):
994        //       brif v0, block1, block2
995        //
996        //     block1:
997        //       <stuff>
998        //       v1 = get some gc ref
999        //       jump block3
1000        //
1001        //     block2:
1002        //       call $needs_safepoint_accidentally
1003        //       return
1004        //
1005        //     block3:
1006        //       stuff keeping v1 live
1007        //       return
1008        let block0 = builder.create_block();
1009        let block1 = builder.create_block();
1010        let block2 = builder.create_block();
1011        let block3 = builder.create_block();
1012        builder.append_block_params_for_function_params(block0);
1013
1014        builder.switch_to_block(block0);
1015        let v0 = builder.func.dfg.block_params(block0)[0];
1016        builder.ins().brif(v0, block1, &[], block2, &[]);
1017
1018        builder.switch_to_block(block1);
1019        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1020        builder.declare_value_needs_stack_map(v1);
1021        builder.ins().jump(block3, &[]);
1022
1023        builder.switch_to_block(block2);
1024        builder.ins().call(func_ref, &[]);
1025        builder.ins().return_(&[]);
1026
1027        builder.switch_to_block(block3);
1028        // NB: Our simplistic liveness analysis conservatively treats any use of
1029        // a value as keeping it live, regardless if the use has side effects or
1030        // is otherwise itself live, so an `iadd_imm` suffices to keep `v1` live
1031        // here.
1032        builder.ins().iadd_imm(v1, 0);
1033        builder.ins().return_(&[]);
1034
1035        builder.seal_all_blocks();
1036        builder.finalize();
1037
1038        assert_eq_output!(
1039            func.display().to_string(),
1040            r#"
1041function %sample(i32) system_v {
1042    sig0 = () system_v
1043    fn0 = colocated u0:0 sig0
1044
1045block0(v0: i32):
1046    brif v0, block1, block2
1047
1048block1:
1049    v1 = iconst.i64 0x1234_5678
1050    jump block3
1051
1052block2:
1053    call fn0()
1054    return
1055
1056block3:
1057    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1058    return
1059}
1060            "#
1061        );
1062    }
1063
1064    #[test]
1065    fn needs_stack_map_conditional_branches_and_liveness() {
1066        let mut sig = Signature::new(CallConv::SystemV);
1067        sig.params.push(AbiParam::new(ir::types::I32));
1068
1069        let mut fn_ctx = FunctionBuilderContext::new();
1070        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1071        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1072
1073        let name = builder
1074            .func
1075            .declare_imported_user_function(ir::UserExternalName {
1076                namespace: 0,
1077                index: 0,
1078            });
1079        let signature = builder
1080            .func
1081            .import_signature(Signature::new(CallConv::SystemV));
1082        let func_ref = builder.import_function(ir::ExtFuncData {
1083            name: ir::ExternalName::user(name),
1084            signature,
1085            colocated: true,
1086        });
1087
1088        // We should not have a stack map entry for `v1` in block 1 because it
1089        // is not live across the call.
1090        //
1091        //     block0(v0):
1092        //       v1 = needs stack map
1093        //       brif v0, block1, block2
1094        //
1095        //     block1:
1096        //       call $foo()
1097        //       return
1098        //
1099        //     block2:
1100        //       keep v1 alive
1101        //       return
1102        let block0 = builder.create_block();
1103        let block1 = builder.create_block();
1104        let block2 = builder.create_block();
1105        builder.append_block_params_for_function_params(block0);
1106
1107        builder.switch_to_block(block0);
1108        let v0 = builder.func.dfg.block_params(block0)[0];
1109        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1110        builder.declare_value_needs_stack_map(v1);
1111        builder.ins().brif(v0, block1, &[], block2, &[]);
1112
1113        builder.switch_to_block(block1);
1114        builder.ins().call(func_ref, &[]);
1115        builder.ins().return_(&[]);
1116
1117        builder.switch_to_block(block2);
1118        // NB: Our simplistic liveness analysis conservatively treats any use of
1119        // a value as keeping it live, regardless if the use has side effects or
1120        // is otherwise itself live, so an `iadd_imm` suffices to keep `v1` live
1121        // here.
1122        builder.ins().iadd_imm(v1, 0);
1123        builder.ins().return_(&[]);
1124
1125        builder.seal_all_blocks();
1126        builder.finalize();
1127
1128        assert_eq_output!(
1129            func.display().to_string(),
1130            r#"
1131function %sample(i32) system_v {
1132    sig0 = () system_v
1133    fn0 = colocated u0:0 sig0
1134
1135block0(v0: i32):
1136    v1 = iconst.i64 0x1234_5678
1137    brif v0, block1, block2
1138
1139block1:
1140    call fn0()
1141    return
1142
1143block2:
1144    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1145    return
1146}
1147            "#
1148        );
1149
1150        // Now do the same test but with block 1 and 2 swapped so that we
1151        // exercise what we are trying to exercise, regardless of which
1152        // post-order traversal we happen to take.
1153        func.clear();
1154        fn_ctx.clear();
1155
1156        let mut sig = Signature::new(CallConv::SystemV);
1157        sig.params.push(AbiParam::new(ir::types::I32));
1158
1159        func.signature = sig;
1160        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1161
1162        let name = builder
1163            .func
1164            .declare_imported_user_function(ir::UserExternalName {
1165                namespace: 0,
1166                index: 0,
1167            });
1168        let signature = builder
1169            .func
1170            .import_signature(Signature::new(CallConv::SystemV));
1171        let func_ref = builder.import_function(ir::ExtFuncData {
1172            name: ir::ExternalName::user(name),
1173            signature,
1174            colocated: true,
1175        });
1176
1177        let block0 = builder.create_block();
1178        let block1 = builder.create_block();
1179        let block2 = builder.create_block();
1180        builder.append_block_params_for_function_params(block0);
1181
1182        builder.switch_to_block(block0);
1183        let v0 = builder.func.dfg.block_params(block0)[0];
1184        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1185        builder.declare_value_needs_stack_map(v1);
1186        builder.ins().brif(v0, block1, &[], block2, &[]);
1187
1188        builder.switch_to_block(block1);
1189        builder.ins().iadd_imm(v1, 0);
1190        builder.ins().return_(&[]);
1191
1192        builder.switch_to_block(block2);
1193        builder.ins().call(func_ref, &[]);
1194        builder.ins().return_(&[]);
1195
1196        builder.seal_all_blocks();
1197        builder.finalize();
1198
1199        assert_eq_output!(
1200            func.display().to_string(),
1201            r#"
1202function u0:0(i32) system_v {
1203    sig0 = () system_v
1204    fn0 = colocated u0:0 sig0
1205
1206block0(v0: i32):
1207    v1 = iconst.i64 0x1234_5678
1208    brif v0, block1, block2
1209
1210block1:
1211    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1212    return
1213
1214block2:
1215    call fn0()
1216    return
1217}
1218            "#
1219        );
1220    }
1221
1222    #[test]
1223    fn needs_stack_map_and_tail_calls() {
1224        let mut sig = Signature::new(CallConv::SystemV);
1225        sig.params.push(AbiParam::new(ir::types::I32));
1226
1227        let mut fn_ctx = FunctionBuilderContext::new();
1228        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1229        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1230
1231        let name = builder
1232            .func
1233            .declare_imported_user_function(ir::UserExternalName {
1234                namespace: 0,
1235                index: 0,
1236            });
1237        let signature = builder
1238            .func
1239            .import_signature(Signature::new(CallConv::SystemV));
1240        let func_ref = builder.import_function(ir::ExtFuncData {
1241            name: ir::ExternalName::user(name),
1242            signature,
1243            colocated: true,
1244        });
1245
1246        // Depending on which post-order traversal we take, we might consider
1247        // `v1` live inside `block1`. But nothing is live after a tail call so
1248        // we shouldn't spill `v1` either way here.
1249        //
1250        //     block0(v0):
1251        //       v1 = needs stack map
1252        //       brif v0, block1, block2
1253        //
1254        //     block1:
1255        //       return_call $foo()
1256        //
1257        //     block2:
1258        //       keep v1 alive
1259        //       return
1260        let block0 = builder.create_block();
1261        let block1 = builder.create_block();
1262        let block2 = builder.create_block();
1263        builder.append_block_params_for_function_params(block0);
1264
1265        builder.switch_to_block(block0);
1266        let v0 = builder.func.dfg.block_params(block0)[0];
1267        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1268        builder.declare_value_needs_stack_map(v1);
1269        builder.ins().brif(v0, block1, &[], block2, &[]);
1270
1271        builder.switch_to_block(block1);
1272        builder.ins().return_call(func_ref, &[]);
1273
1274        builder.switch_to_block(block2);
1275        // NB: Our simplistic liveness analysis conservatively treats any use of
1276        // a value as keeping it live, regardless if the use has side effects or
1277        // is otherwise itself live, so an `iadd_imm` suffices to keep `v1` live
1278        // here.
1279        builder.ins().iadd_imm(v1, 0);
1280        builder.ins().return_(&[]);
1281
1282        builder.seal_all_blocks();
1283        builder.finalize();
1284
1285        assert_eq_output!(
1286            func.display().to_string(),
1287            r#"
1288function %sample(i32) system_v {
1289    sig0 = () system_v
1290    fn0 = colocated u0:0 sig0
1291
1292block0(v0: i32):
1293    v1 = iconst.i64 0x1234_5678
1294    brif v0, block1, block2
1295
1296block1:
1297    return_call fn0()
1298
1299block2:
1300    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1301    return
1302}
1303            "#
1304        );
1305
1306        // Do the same test but with block 1 and 2 swapped so that we exercise
1307        // what we are trying to exercise, regardless of which post-order
1308        // traversal we happen to take.
1309        func.clear();
1310        fn_ctx.clear();
1311
1312        let mut sig = Signature::new(CallConv::SystemV);
1313        sig.params.push(AbiParam::new(ir::types::I32));
1314        func.signature = sig;
1315
1316        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1317
1318        let name = builder
1319            .func
1320            .declare_imported_user_function(ir::UserExternalName {
1321                namespace: 0,
1322                index: 0,
1323            });
1324        let signature = builder
1325            .func
1326            .import_signature(Signature::new(CallConv::SystemV));
1327        let func_ref = builder.import_function(ir::ExtFuncData {
1328            name: ir::ExternalName::user(name),
1329            signature,
1330            colocated: true,
1331        });
1332
1333        let block0 = builder.create_block();
1334        let block1 = builder.create_block();
1335        let block2 = builder.create_block();
1336        builder.append_block_params_for_function_params(block0);
1337
1338        builder.switch_to_block(block0);
1339        let v0 = builder.func.dfg.block_params(block0)[0];
1340        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1341        builder.declare_value_needs_stack_map(v1);
1342        builder.ins().brif(v0, block1, &[], block2, &[]);
1343
1344        builder.switch_to_block(block1);
1345        builder.ins().iadd_imm(v1, 0);
1346        builder.ins().return_(&[]);
1347
1348        builder.switch_to_block(block2);
1349        builder.ins().return_call(func_ref, &[]);
1350
1351        builder.seal_all_blocks();
1352        builder.finalize();
1353
1354        assert_eq_output!(
1355            func.display().to_string(),
1356            r#"
1357function u0:0(i32) system_v {
1358    sig0 = () system_v
1359    fn0 = colocated u0:0 sig0
1360
1361block0(v0: i32):
1362    v1 = iconst.i64 0x1234_5678
1363    brif v0, block1, block2
1364
1365block1:
1366    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1367    return
1368
1369block2:
1370    return_call fn0()
1371}
1372            "#
1373        );
1374    }
1375
1376    #[test]
1377    fn needs_stack_map_and_cfg_diamond() {
1378        let mut sig = Signature::new(CallConv::SystemV);
1379        sig.params.push(AbiParam::new(ir::types::I32));
1380
1381        let mut fn_ctx = FunctionBuilderContext::new();
1382        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1383        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1384
1385        let name = builder
1386            .func
1387            .declare_imported_user_function(ir::UserExternalName {
1388                namespace: 0,
1389                index: 0,
1390            });
1391        let signature = builder
1392            .func
1393            .import_signature(Signature::new(CallConv::SystemV));
1394        let func_ref = builder.import_function(ir::ExtFuncData {
1395            name: ir::ExternalName::user(name),
1396            signature,
1397            colocated: true,
1398        });
1399
1400        // Create an if/else CFG diamond that and check that various things get
1401        // spilled as needed.
1402        //
1403        //     block0(v0):
1404        //       brif v0, block1, block2
1405        //
1406        //     block1:
1407        //       v1 = needs stack map
1408        //       v2 = needs stack map
1409        //       call $foo()
1410        //       jump block3(v1, v2)
1411        //
1412        //     block2:
1413        //       v3 = needs stack map
1414        //       v4 = needs stack map
1415        //       call $foo()
1416        //       jump block3(v3, v3)  ;; Note: v4 is not live
1417        //
1418        //     block3(v5, v6):
1419        //       call $foo()
1420        //       keep v5 alive, but not v6
1421        let block0 = builder.create_block();
1422        let block1 = builder.create_block();
1423        let block2 = builder.create_block();
1424        let block3 = builder.create_block();
1425        builder.append_block_params_for_function_params(block0);
1426
1427        builder.switch_to_block(block0);
1428        let v0 = builder.func.dfg.block_params(block0)[0];
1429        builder.ins().brif(v0, block1, &[], block2, &[]);
1430
1431        builder.switch_to_block(block1);
1432        let v1 = builder.ins().iconst(ir::types::I64, 1);
1433        builder.declare_value_needs_stack_map(v1);
1434        let v2 = builder.ins().iconst(ir::types::I64, 2);
1435        builder.declare_value_needs_stack_map(v2);
1436        builder.ins().call(func_ref, &[]);
1437        builder.ins().jump(block3, &[v1, v2]);
1438
1439        builder.switch_to_block(block2);
1440        let v3 = builder.ins().iconst(ir::types::I64, 3);
1441        builder.declare_value_needs_stack_map(v3);
1442        let v4 = builder.ins().iconst(ir::types::I64, 4);
1443        builder.declare_value_needs_stack_map(v4);
1444        builder.ins().call(func_ref, &[]);
1445        builder.ins().jump(block3, &[v3, v3]);
1446
1447        builder.switch_to_block(block3);
1448        builder.append_block_param(block3, ir::types::I64);
1449        builder.append_block_param(block3, ir::types::I64);
1450        builder.ins().call(func_ref, &[]);
1451        // NB: Our simplistic liveness analysis conservatively treats any use of
1452        // a value as keeping it live, regardless if the use has side effects or
1453        // is otherwise itself live, so an `iadd_imm` suffices to keep `v1` live
1454        // here.
1455        builder.ins().iadd_imm(v1, 0);
1456        builder.ins().return_(&[]);
1457
1458        builder.seal_all_blocks();
1459        builder.finalize();
1460
1461        assert_eq_output!(
1462            func.display().to_string(),
1463            r#"
1464function %sample(i32) system_v {
1465    ss0 = explicit_slot 8, align = 8
1466    ss1 = explicit_slot 8, align = 8
1467    sig0 = () system_v
1468    fn0 = colocated u0:0 sig0
1469
1470block0(v0: i32):
1471    brif v0, block1, block2
1472
1473block1:
1474    v1 = iconst.i64 1
1475    stack_store v1, ss0  ; v1 = 1
1476    v2 = iconst.i64 2
1477    stack_store v2, ss1  ; v2 = 2
1478    call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss1+0]
1479    v9 = stack_load.i64 ss0
1480    v10 = stack_load.i64 ss1
1481    jump block3(v9, v10)
1482
1483block2:
1484    v3 = iconst.i64 3
1485    stack_store v3, ss0  ; v3 = 3
1486    v4 = iconst.i64 4
1487    call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss0+0]
1488    v11 = stack_load.i64 ss0
1489    v12 = stack_load.i64 ss0
1490    jump block3(v11, v12)
1491
1492block3(v5: i64, v6: i64):
1493    call fn0(), stack_map=[i64 @ ss0+0]
1494    v8 = stack_load.i64 ss0
1495    v7 = iadd_imm v8, 0
1496    return
1497}
1498            "#
1499        );
1500    }
1501
1502    #[test]
1503    fn needs_stack_map_and_heterogeneous_types() {
1504        let _ = env_logger::try_init();
1505
1506        let mut sig = Signature::new(CallConv::SystemV);
1507        for ty in [
1508            ir::types::I8,
1509            ir::types::I16,
1510            ir::types::I32,
1511            ir::types::I64,
1512            ir::types::I128,
1513            ir::types::F32,
1514            ir::types::F64,
1515            ir::types::I8X16,
1516            ir::types::I16X8,
1517        ] {
1518            sig.params.push(AbiParam::new(ty));
1519            sig.returns.push(AbiParam::new(ty));
1520        }
1521
1522        let mut fn_ctx = FunctionBuilderContext::new();
1523        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1524        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1525
1526        let name = builder
1527            .func
1528            .declare_imported_user_function(ir::UserExternalName {
1529                namespace: 0,
1530                index: 0,
1531            });
1532        let signature = builder
1533            .func
1534            .import_signature(Signature::new(CallConv::SystemV));
1535        let func_ref = builder.import_function(ir::ExtFuncData {
1536            name: ir::ExternalName::user(name),
1537            signature,
1538            colocated: true,
1539        });
1540
1541        // Test that we support stack maps of heterogeneous types and properly
1542        // coalesce types into stack slots based on their size.
1543        //
1544        //     block0(v0, v1, v2, v3, v4, v5, v6, v7, v8):
1545        //       call $foo()
1546        //       return v0, v1, v2, v3, v4, v5, v6, v7, v8
1547        let block0 = builder.create_block();
1548        builder.append_block_params_for_function_params(block0);
1549
1550        builder.switch_to_block(block0);
1551        let params = builder.func.dfg.block_params(block0).to_vec();
1552        for val in &params {
1553            builder.declare_value_needs_stack_map(*val);
1554        }
1555        builder.ins().call(func_ref, &[]);
1556        builder.ins().return_(&params);
1557
1558        builder.seal_all_blocks();
1559        builder.finalize();
1560
1561        assert_eq_output!(
1562            func.display().to_string(),
1563            r#"
1564function %sample(i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8) -> i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8 system_v {
1565    ss0 = explicit_slot 1
1566    ss1 = explicit_slot 2, align = 2
1567    ss2 = explicit_slot 4, align = 4
1568    ss3 = explicit_slot 8, align = 8
1569    ss4 = explicit_slot 16, align = 16
1570    ss5 = explicit_slot 4, align = 4
1571    ss6 = explicit_slot 8, align = 8
1572    ss7 = explicit_slot 16, align = 16
1573    ss8 = explicit_slot 16, align = 16
1574    sig0 = () system_v
1575    fn0 = colocated u0:0 sig0
1576
1577block0(v0: i8, v1: i16, v2: i32, v3: i64, v4: i128, v5: f32, v6: f64, v7: i8x16, v8: i16x8):
1578    stack_store v0, ss0
1579    stack_store v1, ss1
1580    stack_store v2, ss2
1581    stack_store v3, ss3
1582    stack_store v4, ss4
1583    stack_store v5, ss5
1584    stack_store v6, ss6
1585    stack_store v7, ss7
1586    stack_store v8, ss8
1587    call fn0(), stack_map=[i8 @ ss0+0, i16 @ ss1+0, i32 @ ss2+0, i64 @ ss3+0, i128 @ ss4+0, f32 @ ss5+0, f64 @ ss6+0, i8x16 @ ss7+0, i16x8 @ ss8+0]
1588    v9 = stack_load.i8 ss0
1589    v10 = stack_load.i16 ss1
1590    v11 = stack_load.i32 ss2
1591    v12 = stack_load.i64 ss3
1592    v13 = stack_load.i128 ss4
1593    v14 = stack_load.f32 ss5
1594    v15 = stack_load.f64 ss6
1595    v16 = stack_load.i8x16 ss7
1596    v17 = stack_load.i16x8 ss8
1597    return v9, v10, v11, v12, v13, v14, v15, v16, v17
1598}
1599            "#
1600        );
1601    }
1602
1603    #[test]
1604    fn series_of_non_overlapping_live_ranges_needs_stack_map() {
1605        let sig = Signature::new(CallConv::SystemV);
1606
1607        let mut fn_ctx = FunctionBuilderContext::new();
1608        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1609        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1610
1611        let name = builder
1612            .func
1613            .declare_imported_user_function(ir::UserExternalName {
1614                namespace: 0,
1615                index: 0,
1616            });
1617        let signature = builder
1618            .func
1619            .import_signature(Signature::new(CallConv::SystemV));
1620        let foo_func_ref = builder.import_function(ir::ExtFuncData {
1621            name: ir::ExternalName::user(name),
1622            signature,
1623            colocated: true,
1624        });
1625
1626        let name = builder
1627            .func
1628            .declare_imported_user_function(ir::UserExternalName {
1629                namespace: 0,
1630                index: 1,
1631            });
1632        let mut sig = Signature::new(CallConv::SystemV);
1633        sig.params.push(AbiParam::new(ir::types::I32));
1634        let signature = builder.func.import_signature(sig);
1635        let consume_func_ref = builder.import_function(ir::ExtFuncData {
1636            name: ir::ExternalName::user(name),
1637            signature,
1638            colocated: true,
1639        });
1640
1641        // Create a series of needs-stack-map values that do not have
1642        // overlapping live ranges, but which do appear in stack maps for calls
1643        // to `$foo`:
1644        //
1645        //     block0:
1646        //       v0 = needs stack map
1647        //       call $foo()
1648        //       call consume(v0)
1649        //       v1 = needs stack map
1650        //       call $foo()
1651        //       call consume(v1)
1652        //       v2 = needs stack map
1653        //       call $foo()
1654        //       call consume(v2)
1655        //       v3 = needs stack map
1656        //       call $foo()
1657        //       call consume(v3)
1658        //       return
1659        let block0 = builder.create_block();
1660        builder.append_block_params_for_function_params(block0);
1661        builder.switch_to_block(block0);
1662        let v0 = builder.ins().iconst(ir::types::I32, 0);
1663        builder.declare_value_needs_stack_map(v0);
1664        builder.ins().call(foo_func_ref, &[]);
1665        builder.ins().call(consume_func_ref, &[v0]);
1666        let v1 = builder.ins().iconst(ir::types::I32, 1);
1667        builder.declare_value_needs_stack_map(v1);
1668        builder.ins().call(foo_func_ref, &[]);
1669        builder.ins().call(consume_func_ref, &[v1]);
1670        let v2 = builder.ins().iconst(ir::types::I32, 2);
1671        builder.declare_value_needs_stack_map(v2);
1672        builder.ins().call(foo_func_ref, &[]);
1673        builder.ins().call(consume_func_ref, &[v2]);
1674        let v3 = builder.ins().iconst(ir::types::I32, 3);
1675        builder.declare_value_needs_stack_map(v3);
1676        builder.ins().call(foo_func_ref, &[]);
1677        builder.ins().call(consume_func_ref, &[v3]);
1678        builder.ins().return_(&[]);
1679        builder.seal_all_blocks();
1680        builder.finalize();
1681
1682        assert_eq_output!(
1683            func.display().to_string(),
1684            r#"
1685function %sample() system_v {
1686    ss0 = explicit_slot 4, align = 4
1687    sig0 = () system_v
1688    sig1 = (i32) system_v
1689    fn0 = colocated u0:0 sig0
1690    fn1 = colocated u0:1 sig1
1691
1692block0:
1693    v0 = iconst.i32 0
1694    stack_store v0, ss0  ; v0 = 0
1695    call fn0(), stack_map=[i32 @ ss0+0]
1696    v7 = stack_load.i32 ss0
1697    call fn1(v7)
1698    v1 = iconst.i32 1
1699    stack_store v1, ss0  ; v1 = 1
1700    call fn0(), stack_map=[i32 @ ss0+0]
1701    v6 = stack_load.i32 ss0
1702    call fn1(v6)
1703    v2 = iconst.i32 2
1704    stack_store v2, ss0  ; v2 = 2
1705    call fn0(), stack_map=[i32 @ ss0+0]
1706    v5 = stack_load.i32 ss0
1707    call fn1(v5)
1708    v3 = iconst.i32 3
1709    stack_store v3, ss0  ; v3 = 3
1710    call fn0(), stack_map=[i32 @ ss0+0]
1711    v4 = stack_load.i32 ss0
1712    call fn1(v4)
1713    return
1714}
1715            "#
1716        );
1717    }
1718
1719    #[test]
1720    fn vars_block_params_and_needs_stack_map() {
1721        let _ = env_logger::try_init();
1722
1723        let mut sig = Signature::new(CallConv::SystemV);
1724        sig.params.push(AbiParam::new(ir::types::I32));
1725        sig.returns.push(AbiParam::new(ir::types::I32));
1726
1727        let mut fn_ctx = FunctionBuilderContext::new();
1728        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1729        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1730
1731        let name = builder
1732            .func
1733            .declare_imported_user_function(ir::UserExternalName {
1734                namespace: 0,
1735                index: 0,
1736            });
1737        let mut sig = Signature::new(CallConv::SystemV);
1738        sig.params.push(AbiParam::new(ir::types::I32));
1739        let signature = builder.func.import_signature(sig);
1740        let func_ref = builder.import_function(ir::ExtFuncData {
1741            name: ir::ExternalName::user(name),
1742            signature,
1743            colocated: true,
1744        });
1745
1746        // Use a variable, create a control flow diamond so that the variable
1747        // forces a block parameter on the control join point, and make sure
1748        // that we get stack maps for all the appropriate uses of the variable
1749        // in all blocks, as well as that we are reusing stack slots for each of
1750        // the values.
1751        //
1752        //                        block0:
1753        //                          x := needs stack map
1754        //                          call $foo(x)
1755        //                          br_if v0, block1, block2
1756        //
1757        //
1758        //     block1:                                     block2:
1759        //       call $foo(x)                                call $foo(x)
1760        //       call $foo(x)                                call $foo(x)
1761        //       x := new needs stack map                    x := new needs stack map
1762        //       call $foo(x)                                call $foo(x)
1763        //       jump block3                                 jump block3
1764        //
1765        //
1766        //                        block3:
1767        //                          call $foo(x)
1768        //                          return x
1769
1770        let x = Variable::from_u32(0);
1771        builder.declare_var(x, ir::types::I32);
1772        builder.declare_var_needs_stack_map(x);
1773
1774        let block0 = builder.create_block();
1775        let block1 = builder.create_block();
1776        let block2 = builder.create_block();
1777        let block3 = builder.create_block();
1778
1779        builder.append_block_params_for_function_params(block0);
1780        builder.switch_to_block(block0);
1781        let v0 = builder.func.dfg.block_params(block0)[0];
1782        let val = builder.ins().iconst(ir::types::I32, 42);
1783        builder.def_var(x, val);
1784        {
1785            let x = builder.use_var(x);
1786            builder.ins().call(func_ref, &[x]);
1787        }
1788        builder.ins().brif(v0, block1, &[], block2, &[]);
1789
1790        builder.switch_to_block(block1);
1791        {
1792            let x = builder.use_var(x);
1793            builder.ins().call(func_ref, &[x]);
1794            builder.ins().call(func_ref, &[x]);
1795        }
1796        let val = builder.ins().iconst(ir::types::I32, 36);
1797        builder.def_var(x, val);
1798        {
1799            let x = builder.use_var(x);
1800            builder.ins().call(func_ref, &[x]);
1801        }
1802        builder.ins().jump(block3, &[]);
1803
1804        builder.switch_to_block(block2);
1805        {
1806            let x = builder.use_var(x);
1807            builder.ins().call(func_ref, &[x]);
1808            builder.ins().call(func_ref, &[x]);
1809        }
1810        let val = builder.ins().iconst(ir::types::I32, 36);
1811        builder.def_var(x, val);
1812        {
1813            let x = builder.use_var(x);
1814            builder.ins().call(func_ref, &[x]);
1815        }
1816        builder.ins().jump(block3, &[]);
1817
1818        builder.switch_to_block(block3);
1819        let x = builder.use_var(x);
1820        builder.ins().call(func_ref, &[x]);
1821        builder.ins().return_(&[x]);
1822
1823        builder.seal_all_blocks();
1824        builder.finalize();
1825
1826        assert_eq_output!(
1827            func.display().to_string(),
1828            r#"
1829function %sample(i32) -> i32 system_v {
1830    ss0 = explicit_slot 4, align = 4
1831    ss1 = explicit_slot 4, align = 4
1832    sig0 = (i32) system_v
1833    fn0 = colocated u0:0 sig0
1834
1835block0(v0: i32):
1836    v1 = iconst.i32 42
1837    v2 -> v1
1838    v4 -> v1
1839    stack_store v1, ss0  ; v1 = 42
1840    v13 = stack_load.i32 ss0
1841    call fn0(v13), stack_map=[i32 @ ss0+0]
1842    brif v0, block1, block2
1843
1844block1:
1845    call fn0(v2), stack_map=[i32 @ ss0+0]  ; v2 = 42
1846    call fn0(v2)  ; v2 = 42
1847    v3 = iconst.i32 36
1848    stack_store v3, ss0  ; v3 = 36
1849    v10 = stack_load.i32 ss0
1850    call fn0(v10), stack_map=[i32 @ ss0+0]
1851    v9 = stack_load.i32 ss0
1852    jump block3(v9)
1853
1854block2:
1855    call fn0(v4), stack_map=[i32 @ ss0+0]  ; v4 = 42
1856    call fn0(v4)  ; v4 = 42
1857    v5 = iconst.i32 36
1858    stack_store v5, ss1  ; v5 = 36
1859    v12 = stack_load.i32 ss1
1860    call fn0(v12), stack_map=[i32 @ ss1+0]
1861    v11 = stack_load.i32 ss1
1862    jump block3(v11)
1863
1864block3(v6: i32):
1865    stack_store v6, ss0
1866    v8 = stack_load.i32 ss0
1867    call fn0(v8), stack_map=[i32 @ ss0+0]
1868    v7 = stack_load.i32 ss0
1869    return v7
1870}
1871            "#
1872        );
1873    }
1874
1875    #[test]
1876    fn var_needs_stack_map() {
1877        let mut sig = Signature::new(CallConv::SystemV);
1878        sig.params
1879            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1880        sig.returns
1881            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1882
1883        let mut fn_ctx = FunctionBuilderContext::new();
1884        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1885        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1886
1887        let var = Variable::from_u32(0);
1888        builder.declare_var(var, cranelift_codegen::ir::types::I32);
1889        builder.declare_var_needs_stack_map(var);
1890
1891        let name = builder
1892            .func
1893            .declare_imported_user_function(ir::UserExternalName {
1894                namespace: 0,
1895                index: 0,
1896            });
1897        let signature = builder
1898            .func
1899            .import_signature(Signature::new(CallConv::SystemV));
1900        let func_ref = builder.import_function(ir::ExtFuncData {
1901            name: ir::ExternalName::user(name),
1902            signature,
1903            colocated: true,
1904        });
1905
1906        let block0 = builder.create_block();
1907        builder.append_block_params_for_function_params(block0);
1908        builder.switch_to_block(block0);
1909
1910        let arg = builder.func.dfg.block_params(block0)[0];
1911        builder.def_var(var, arg);
1912
1913        builder.ins().call(func_ref, &[]);
1914
1915        let val = builder.use_var(var);
1916        builder.ins().return_(&[val]);
1917
1918        builder.seal_all_blocks();
1919        builder.finalize();
1920
1921        assert_eq_output!(
1922            func.display().to_string(),
1923            r#"
1924function %sample(i32) -> i32 system_v {
1925    ss0 = explicit_slot 4, align = 4
1926    sig0 = () system_v
1927    fn0 = colocated u0:0 sig0
1928
1929block0(v0: i32):
1930    stack_store v0, ss0
1931    call fn0(), stack_map=[i32 @ ss0+0]
1932    v1 = stack_load.i32 ss0
1933    return v1
1934}
1935            "#
1936        );
1937    }
1938
1939    #[test]
1940    fn first_inst_defines_needs_stack_map() {
1941        let mut sig = Signature::new(CallConv::SystemV);
1942        sig.params
1943            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1944        sig.returns
1945            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1946        sig.returns
1947            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1948
1949        let mut fn_ctx = FunctionBuilderContext::new();
1950        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1951        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1952
1953        let name = builder
1954            .func
1955            .declare_imported_user_function(ir::UserExternalName {
1956                namespace: 0,
1957                index: 0,
1958            });
1959        let signature = builder
1960            .func
1961            .import_signature(Signature::new(CallConv::SystemV));
1962        let func_ref = builder.import_function(ir::ExtFuncData {
1963            name: ir::ExternalName::user(name),
1964            signature,
1965            colocated: true,
1966        });
1967
1968        // Regression test found via fuzzing in
1969        // https://github.com/bytecodealliance/wasmtime/pull/8941 involving the
1970        // combination of cursor positions after we have block parameters that
1971        // need inclusion in stack maps and when the first instruction in a
1972        // block defines a value that needs inclusion in stack maps.
1973        //
1974        // block0(v0: i32):
1975        //   v1 = iconst.i32 42
1976        //   call $foo()
1977        //   return v0, v1
1978
1979        let block0 = builder.create_block();
1980        builder.append_block_params_for_function_params(block0);
1981        builder.switch_to_block(block0);
1982
1983        let arg = builder.func.dfg.block_params(block0)[0];
1984        builder.declare_value_needs_stack_map(arg);
1985
1986        let val = builder.ins().iconst(ir::types::I32, 42);
1987        builder.declare_value_needs_stack_map(val);
1988
1989        builder.ins().call(func_ref, &[]);
1990
1991        builder.ins().return_(&[arg, val]);
1992
1993        builder.seal_all_blocks();
1994        builder.finalize();
1995
1996        assert_eq_output!(
1997            func.display().to_string(),
1998            r#"
1999function %sample(i32) -> i32, i32 system_v {
2000    ss0 = explicit_slot 4, align = 4
2001    ss1 = explicit_slot 4, align = 4
2002    sig0 = () system_v
2003    fn0 = colocated u0:0 sig0
2004
2005block0(v0: i32):
2006    stack_store v0, ss0
2007    v1 = iconst.i32 42
2008    stack_store v1, ss1  ; v1 = 42
2009    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
2010    v2 = stack_load.i32 ss0
2011    v3 = stack_load.i32 ss1
2012    return v2, v3
2013}
2014            "#
2015        );
2016    }
2017
2018    #[test]
2019    fn needs_stack_map_and_loops_and_partially_live_values() {
2020        let _ = env_logger::try_init();
2021
2022        let mut sig = Signature::new(CallConv::SystemV);
2023        sig.params.push(AbiParam::new(ir::types::I32));
2024
2025        let mut fn_ctx = FunctionBuilderContext::new();
2026        let mut func =
2027            Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig.clone());
2028        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2029
2030        let name = builder
2031            .func
2032            .declare_imported_user_function(ir::UserExternalName {
2033                namespace: 0,
2034                index: 0,
2035            });
2036        let signature = builder
2037            .func
2038            .import_signature(Signature::new(CallConv::SystemV));
2039        let foo_func_ref = builder.import_function(ir::ExtFuncData {
2040            name: ir::ExternalName::user(name),
2041            signature,
2042            colocated: true,
2043        });
2044
2045        let name = builder
2046            .func
2047            .declare_imported_user_function(ir::UserExternalName {
2048                namespace: 1,
2049                index: 1,
2050            });
2051        let signature = builder.func.import_signature(sig);
2052        let bar_func_ref = builder.import_function(ir::ExtFuncData {
2053            name: ir::ExternalName::user(name),
2054            signature,
2055            colocated: true,
2056        });
2057
2058        // Test that we support stack maps in loops and that we properly handle
2059        // value that are only live for part of the loop body on each iteration,
2060        // but are live across the whole loop because they will be used again
2061        // the next iteration. Note that `v0` below, which is a GC value, is not
2062        // live *within a single iteration of the loop* after the call to `bar`,
2063        // but is actually live across the whole loop because it will be used
2064        // again in the *next iteration of the loop*:
2065        //
2066        //     block0(v0: i32):
2067        //       jump block1
2068        //
2069        //     block1:
2070        //       call $foo()
2071        //       call $bar(v0)
2072        //       call $foo()
2073        //       jump block1
2074        let block0 = builder.create_block();
2075        let block1 = builder.create_block();
2076        builder.append_block_params_for_function_params(block0);
2077
2078        builder.switch_to_block(block0);
2079        builder.ins().jump(block1, &[]);
2080
2081        builder.switch_to_block(block1);
2082        let v0 = builder.func.dfg.block_params(block0)[0];
2083        builder.declare_value_needs_stack_map(v0);
2084        builder.ins().call(foo_func_ref, &[]);
2085        builder.ins().call(bar_func_ref, &[v0]);
2086        builder.ins().call(foo_func_ref, &[]);
2087        builder.ins().jump(block1, &[]);
2088
2089        builder.seal_all_blocks();
2090        builder.finalize();
2091
2092        assert_eq_output!(
2093            func.display().to_string(),
2094            r#"
2095function %sample(i32) system_v {
2096    ss0 = explicit_slot 4, align = 4
2097    sig0 = () system_v
2098    sig1 = (i32) system_v
2099    fn0 = colocated u0:0 sig0
2100    fn1 = colocated u1:1 sig1
2101
2102block0(v0: i32):
2103    stack_store v0, ss0
2104    jump block1
2105
2106block1:
2107    call fn0(), stack_map=[i32 @ ss0+0]
2108    v1 = stack_load.i32 ss0
2109    call fn1(v1), stack_map=[i32 @ ss0+0]
2110    call fn0(), stack_map=[i32 @ ss0+0]
2111    jump block1
2112}
2113            "#,
2114        );
2115    }
2116
2117    #[test]
2118    fn needs_stack_map_and_irreducible_loops() {
2119        let _ = env_logger::try_init();
2120
2121        let mut sig = Signature::new(CallConv::SystemV);
2122        sig.params.push(AbiParam::new(ir::types::I32));
2123        sig.params.push(AbiParam::new(ir::types::I32));
2124
2125        let mut fn_ctx = FunctionBuilderContext::new();
2126        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2127        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2128
2129        let name = builder
2130            .func
2131            .declare_imported_user_function(ir::UserExternalName {
2132                namespace: 0,
2133                index: 0,
2134            });
2135        let signature = builder
2136            .func
2137            .import_signature(Signature::new(CallConv::SystemV));
2138        let foo_func_ref = builder.import_function(ir::ExtFuncData {
2139            name: ir::ExternalName::user(name),
2140            signature,
2141            colocated: true,
2142        });
2143
2144        let name = builder
2145            .func
2146            .declare_imported_user_function(ir::UserExternalName {
2147                namespace: 1,
2148                index: 1,
2149            });
2150        let mut sig = Signature::new(CallConv::SystemV);
2151        sig.params.push(AbiParam::new(ir::types::I32));
2152        let signature = builder.func.import_signature(sig);
2153        let bar_func_ref = builder.import_function(ir::ExtFuncData {
2154            name: ir::ExternalName::user(name),
2155            signature,
2156            colocated: true,
2157        });
2158
2159        // Test an irreducible loop with multiple entry points, both block1 and
2160        // block2, in this case:
2161        //
2162        //     block0(v0: i32, v1: i32):
2163        //       brif v0, block1, block2
2164        //
2165        //     block1:
2166        //       jump block3
2167        //
2168        //     block2:
2169        //       jump block4
2170        //
2171        //     block3:
2172        //       call $foo()
2173        //       call $bar(v1)
2174        //       call $foo()
2175        //       jump block2
2176        //
2177        //     block4:
2178        //       call $foo()
2179        //       call $bar(v1)
2180        //       call $foo()
2181        //       jump block1
2182        let block0 = builder.create_block();
2183        let block1 = builder.create_block();
2184        let block2 = builder.create_block();
2185        let block3 = builder.create_block();
2186        let block4 = builder.create_block();
2187        builder.append_block_params_for_function_params(block0);
2188
2189        builder.switch_to_block(block0);
2190        let v0 = builder.func.dfg.block_params(block0)[0];
2191        let v1 = builder.func.dfg.block_params(block0)[1];
2192        builder.declare_value_needs_stack_map(v1);
2193        builder.ins().brif(v0, block1, &[], block2, &[]);
2194
2195        builder.switch_to_block(block1);
2196        builder.ins().jump(block3, &[]);
2197
2198        builder.switch_to_block(block2);
2199        builder.ins().jump(block4, &[]);
2200
2201        builder.switch_to_block(block3);
2202        builder.ins().call(foo_func_ref, &[]);
2203        builder.ins().call(bar_func_ref, &[v1]);
2204        builder.ins().call(foo_func_ref, &[]);
2205        builder.ins().jump(block2, &[]);
2206
2207        builder.switch_to_block(block4);
2208        builder.ins().call(foo_func_ref, &[]);
2209        builder.ins().call(bar_func_ref, &[v1]);
2210        builder.ins().call(foo_func_ref, &[]);
2211        builder.ins().jump(block1, &[]);
2212
2213        builder.seal_all_blocks();
2214        builder.finalize();
2215
2216        assert_eq_output!(
2217            func.display().to_string(),
2218            r#"
2219function %sample(i32, i32) system_v {
2220    ss0 = explicit_slot 4, align = 4
2221    sig0 = () system_v
2222    sig1 = (i32) system_v
2223    fn0 = colocated u0:0 sig0
2224    fn1 = colocated u1:1 sig1
2225
2226block0(v0: i32, v1: i32):
2227    stack_store v1, ss0
2228    brif v0, block1, block2
2229
2230block1:
2231    jump block3
2232
2233block2:
2234    jump block4
2235
2236block3:
2237    call fn0(), stack_map=[i32 @ ss0+0]
2238    v3 = stack_load.i32 ss0
2239    call fn1(v3), stack_map=[i32 @ ss0+0]
2240    call fn0(), stack_map=[i32 @ ss0+0]
2241    jump block2
2242
2243block4:
2244    call fn0(), stack_map=[i32 @ ss0+0]
2245    v2 = stack_load.i32 ss0
2246    call fn1(v2), stack_map=[i32 @ ss0+0]
2247    call fn0(), stack_map=[i32 @ ss0+0]
2248    jump block1
2249}
2250            "#,
2251        );
2252    }
2253
2254    #[test]
2255    fn needs_stack_map_and_back_edge_to_back_edge() {
2256        let _ = env_logger::try_init();
2257
2258        let mut sig = Signature::new(CallConv::SystemV);
2259        sig.params.push(AbiParam::new(ir::types::I32));
2260        sig.params.push(AbiParam::new(ir::types::I32));
2261        sig.params.push(AbiParam::new(ir::types::I32));
2262        sig.params.push(AbiParam::new(ir::types::I32));
2263
2264        let mut fn_ctx = FunctionBuilderContext::new();
2265        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2266        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2267
2268        let name = builder
2269            .func
2270            .declare_imported_user_function(ir::UserExternalName {
2271                namespace: 0,
2272                index: 0,
2273            });
2274        let signature = builder
2275            .func
2276            .import_signature(Signature::new(CallConv::SystemV));
2277        let foo_func_ref = builder.import_function(ir::ExtFuncData {
2278            name: ir::ExternalName::user(name),
2279            signature,
2280            colocated: true,
2281        });
2282
2283        let name = builder
2284            .func
2285            .declare_imported_user_function(ir::UserExternalName {
2286                namespace: 1,
2287                index: 1,
2288            });
2289        let mut sig = Signature::new(CallConv::SystemV);
2290        sig.params.push(AbiParam::new(ir::types::I32));
2291        let signature = builder.func.import_signature(sig);
2292        let bar_func_ref = builder.import_function(ir::ExtFuncData {
2293            name: ir::ExternalName::user(name),
2294            signature,
2295            colocated: true,
2296        });
2297
2298        // Test that we detect the `block1 -> block2 -> block3 -> block2 ->
2299        // block1` loop in our liveness analysis and keep `v{0,1,2}` live across
2300        // the whole loop body.
2301        //
2302        //     block0(v0, v1, v2, v3):
2303        //       jump block1(v3)
2304        //
2305        //     block1(v4):
2306        //       call foo_func_ref()
2307        //       call bar_func_ref(v0)
2308        //       call foo_func_ref()
2309        //       jump block2
2310        //
2311        //     block2:
2312        //       call foo_func_ref()
2313        //       call bar_func_ref(v1)
2314        //       call foo_func_ref()
2315        //       v5 = iadd_imm v4, -1
2316        //       brif v4, block1(v5), block3
2317        //
2318        //     block3:
2319        //       call foo_func_ref()
2320        //       call bar_func_ref(v2)
2321        //       call foo_func_ref()
2322        //       jump block2
2323
2324        let block0 = builder.create_block();
2325        let block1 = builder.create_block();
2326        let block2 = builder.create_block();
2327        let block3 = builder.create_block();
2328
2329        builder.append_block_params_for_function_params(block0);
2330
2331        builder.switch_to_block(block0);
2332
2333        let v0 = builder.func.dfg.block_params(block0)[0];
2334        builder.declare_value_needs_stack_map(v0);
2335        let v1 = builder.func.dfg.block_params(block0)[1];
2336        builder.declare_value_needs_stack_map(v1);
2337        let v2 = builder.func.dfg.block_params(block0)[2];
2338        builder.declare_value_needs_stack_map(v2);
2339        let v3 = builder.func.dfg.block_params(block0)[3];
2340
2341        builder.ins().jump(block1, &[v3]);
2342
2343        builder.switch_to_block(block1);
2344        let v4 = builder.append_block_param(block1, ir::types::I32);
2345        builder.ins().call(foo_func_ref, &[]);
2346        builder.ins().call(bar_func_ref, &[v0]);
2347        builder.ins().call(foo_func_ref, &[]);
2348        builder.ins().jump(block2, &[]);
2349
2350        builder.switch_to_block(block2);
2351        builder.ins().call(foo_func_ref, &[]);
2352        builder.ins().call(bar_func_ref, &[v1]);
2353        builder.ins().call(foo_func_ref, &[]);
2354        let v5 = builder.ins().iadd_imm(v4, -1);
2355        builder.ins().brif(v4, block1, &[v5], block3, &[]);
2356
2357        builder.switch_to_block(block3);
2358        builder.ins().call(foo_func_ref, &[]);
2359        builder.ins().call(bar_func_ref, &[v2]);
2360        builder.ins().call(foo_func_ref, &[]);
2361        builder.ins().jump(block2, &[]);
2362
2363        builder.seal_all_blocks();
2364        builder.finalize();
2365
2366        assert_eq_output!(
2367            func.display().to_string(),
2368            r#"
2369function %sample(i32, i32, i32, i32) system_v {
2370    ss0 = explicit_slot 4, align = 4
2371    ss1 = explicit_slot 4, align = 4
2372    ss2 = explicit_slot 4, align = 4
2373    sig0 = () system_v
2374    sig1 = (i32) system_v
2375    fn0 = colocated u0:0 sig0
2376    fn1 = colocated u1:1 sig1
2377
2378block0(v0: i32, v1: i32, v2: i32, v3: i32):
2379    stack_store v0, ss0
2380    stack_store v1, ss1
2381    stack_store v2, ss2
2382    jump block1(v3)
2383
2384block1(v4: i32):
2385    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2386    v8 = stack_load.i32 ss0
2387    call fn1(v8), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2388    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2389    jump block2
2390
2391block2:
2392    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2393    v7 = stack_load.i32 ss1
2394    call fn1(v7), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2395    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2396    v5 = iadd_imm.i32 v4, -1
2397    brif.i32 v4, block1(v5), block3
2398
2399block3:
2400    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2401    v6 = stack_load.i32 ss2
2402    call fn1(v6), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2403    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2404    jump block2
2405}
2406            "#,
2407        );
2408    }
2409}