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!("values needing inclusion in stack maps: {stack_map_values:?}");
614        log::trace!(
615            "before inserting safepoint spills and reloads:\n{}",
616            func.display()
617        );
618
619        self.clear();
620        self.liveness.run(func, stack_map_values);
621        self.rewrite(func);
622
623        log::trace!(
624            "after inserting safepoint spills and reloads:\n{}",
625            func.display()
626        );
627    }
628
629    /// Spill this value to a stack slot if it has been declared that it must be
630    /// included in stack maps and is live across any safepoints.
631    ///
632    /// The given cursor must point just after this value's definition.
633    fn rewrite_def(&mut self, pos: &mut FuncCursor<'_>, val: ir::Value) {
634        if let Some(slot) = self.stack_slots.get(val) {
635            let i = pos.ins().stack_store(val, slot, 0);
636            log::trace!(
637                "rewriting:   spilling {val:?} to {slot:?}: {}",
638                pos.func.dfg.display_inst(i)
639            );
640
641            // Now that we've defined this value, there cannot be any more uses
642            // of it, and therefore this stack slot is now available for reuse.
643            let ty = pos.func.dfg.value_type(val);
644            let size = SlotSize::try_from(ty).unwrap();
645            self.stack_slots.free_stack_slot(size, slot);
646        }
647    }
648
649    /// Add a stack map entry for each needs-stack-map value that is live across
650    /// the given safepoint instruction.
651    ///
652    /// This will additionally assign stack slots to needs-stack-map values, if
653    /// no such assignment has already been made.
654    fn rewrite_safepoint(&mut self, func: &mut Function, inst: ir::Inst) {
655        log::trace!(
656            "rewriting:   found safepoint: {inst:?}: {}",
657            func.dfg.display_inst(inst)
658        );
659
660        let live = self
661            .liveness
662            .safepoints
663            .get(&inst)
664            .expect("should only call `rewrite_safepoint` on safepoint instructions");
665
666        for val in live {
667            // Get or create the stack slot for this live needs-stack-map value.
668            let slot = self.stack_slots.get_or_create_stack_slot(func, *val);
669
670            log::trace!(
671                "rewriting:     adding stack map entry for {val:?} at {slot:?}: {}",
672                func.dfg.display_inst(inst)
673            );
674            let ty = func.dfg.value_type(*val);
675            func.dfg.append_user_stack_map_entry(
676                inst,
677                ir::UserStackMapEntry {
678                    ty,
679                    slot,
680                    offset: 0,
681                },
682            );
683        }
684    }
685
686    /// If `val` is a needs-stack-map value that has been spilled to a stack
687    /// slot, then rewrite `val` to be a load from its associated stack
688    /// slot.
689    ///
690    /// Returns `true` if `val` was rewritten, `false` if not.
691    ///
692    /// The given cursor must point just before the use of the value that we are
693    /// replacing.
694    fn rewrite_use(&mut self, pos: &mut FuncCursor<'_>, val: &mut ir::Value) -> bool {
695        if !self.liveness.live_across_any_safepoint.contains(*val) {
696            return false;
697        }
698
699        let old_val = *val;
700        log::trace!("rewriting:     found use of {old_val:?}");
701
702        let ty = pos.func.dfg.value_type(*val);
703        let slot = self.stack_slots.get_or_create_stack_slot(pos.func, *val);
704        *val = pos.ins().stack_load(ty, slot, 0);
705
706        log::trace!(
707            "rewriting:     reloading {old_val:?}: {}",
708            pos.func
709                .dfg
710                .display_inst(pos.func.dfg.value_def(*val).unwrap_inst())
711        );
712
713        true
714    }
715
716    /// Rewrite the function's instructions to spill and reload values that are
717    /// live across safepoints:
718    ///
719    /// 1. Definitions of needs-stack-map values that are live across some
720    ///    safepoint need to be spilled to their assigned stack slot.
721    ///
722    /// 2. Instructions that are themselves safepoints must have stack map
723    ///    entries added for the needs-stack-map values that are live across
724    ///    them.
725    ///
726    /// 3. Uses of needs-stack-map values that have been spilled to a stack slot
727    ///    need to be replaced with reloads from the slot.
728    fn rewrite(&mut self, func: &mut Function) {
729        // Shared temporary storage for operand and result lists.
730        let mut vals: SmallVec<[_; 8]> = Default::default();
731
732        // Rewrite the function's instructions in post-order. This ensures that
733        // we rewrite uses before defs, and therefore once we see a def we know
734        // its stack slot will never be used for that value again. Therefore,
735        // the slot can be reappropriated for a new needs-stack-map value with a
736        // non-overlapping live range. See `rewrite_def` and `free_stack_slots`
737        // for more details.
738        for block_index in 0..self.liveness.post_order.len() {
739            let block = self.liveness.post_order[block_index];
740            log::trace!("rewriting: processing {block:?}");
741
742            let mut option_inst = func.layout.last_inst(block);
743            while let Some(inst) = option_inst {
744                // If this instruction defines a needs-stack-map value that is
745                // live across a safepoint, then spill the value to its stack
746                // slot.
747                let mut pos = FuncCursor::new(func).after_inst(inst);
748                vals.extend_from_slice(pos.func.dfg.inst_results(inst));
749                for val in vals.drain(..) {
750                    self.rewrite_def(&mut pos, val);
751                }
752
753                // If this instruction is a safepoint, then we must add stack
754                // map entries for the needs-stack-map values that are live
755                // across it.
756                if self.liveness.safepoints.contains_key(&inst) {
757                    self.rewrite_safepoint(func, inst);
758                }
759
760                // Replace all uses of needs-stack-map values with loads from
761                // the value's associated stack slot.
762                let mut pos = FuncCursor::new(func).at_inst(inst);
763                vals.extend(pos.func.dfg.inst_values(inst));
764                let mut replaced_any = false;
765                for val in &mut vals {
766                    replaced_any |= self.rewrite_use(&mut pos, val);
767                }
768                if replaced_any {
769                    pos.func.dfg.overwrite_inst_values(inst, vals.drain(..));
770                    log::trace!(
771                        "rewriting:     updated {inst:?} operands with reloaded values: {}",
772                        pos.func.dfg.display_inst(inst)
773                    );
774                } else {
775                    vals.clear();
776                }
777
778                option_inst = func.layout.prev_inst(inst);
779            }
780
781            // Spill needs-stack-map values defined by block parameters to their
782            // associated stack slots.
783            let mut pos = FuncCursor::new(func).at_position(CursorPosition::Before(block));
784            pos.next_inst(); // Advance to the first instruction in the block.
785            vals.clear();
786            vals.extend_from_slice(pos.func.dfg.block_params(block));
787            for val in vals.drain(..) {
788                self.rewrite_def(&mut pos, val);
789            }
790        }
791    }
792}
793
794#[cfg(test)]
795mod tests {
796    use super::*;
797    use alloc::string::ToString;
798    use cranelift_codegen::isa::CallConv;
799
800    #[test]
801    fn needs_stack_map_and_loop() {
802        let mut sig = Signature::new(CallConv::SystemV);
803        sig.params.push(AbiParam::new(ir::types::I32));
804        sig.params.push(AbiParam::new(ir::types::I32));
805
806        let mut fn_ctx = FunctionBuilderContext::new();
807        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
808        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
809
810        let name = builder
811            .func
812            .declare_imported_user_function(ir::UserExternalName {
813                namespace: 0,
814                index: 0,
815            });
816        let mut sig = Signature::new(CallConv::SystemV);
817        sig.params.push(AbiParam::new(ir::types::I32));
818        let signature = builder.func.import_signature(sig);
819        let func_ref = builder.import_function(ir::ExtFuncData {
820            name: ir::ExternalName::user(name),
821            signature,
822            colocated: true,
823        });
824
825        // Here the value `v1` is technically not live but our single-pass liveness
826        // analysis treats every branch argument to a block as live to avoid
827        // needing to do a fixed-point loop.
828        //
829        //     block0(v0, v1):
830        //       call $foo(v0)
831        //       jump block0(v0, v1)
832        let block0 = builder.create_block();
833        builder.append_block_params_for_function_params(block0);
834        let a = builder.func.dfg.block_params(block0)[0];
835        let b = builder.func.dfg.block_params(block0)[1];
836        builder.declare_value_needs_stack_map(a);
837        builder.declare_value_needs_stack_map(b);
838        builder.switch_to_block(block0);
839        builder.ins().call(func_ref, &[a]);
840        builder.ins().jump(block0, &[a.into(), b.into()]);
841        builder.seal_all_blocks();
842        builder.finalize();
843
844        assert_eq_output!(
845            func.display().to_string(),
846            r#"
847function %sample(i32, i32) system_v {
848    ss0 = explicit_slot 4, align = 4
849    ss1 = explicit_slot 4, align = 4
850    sig0 = (i32) system_v
851    fn0 = colocated u0:0 sig0
852
853block0(v0: i32, v1: i32):
854    stack_store v0, ss0
855    stack_store v1, ss1
856    v4 = stack_load.i32 ss0
857    call fn0(v4), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
858    v2 = stack_load.i32 ss0
859    v3 = stack_load.i32 ss1
860    jump block0(v2, v3)
861}
862            "#
863        );
864    }
865
866    #[test]
867    fn needs_stack_map_simple() {
868        let _ = env_logger::try_init();
869
870        let sig = Signature::new(CallConv::SystemV);
871
872        let mut fn_ctx = FunctionBuilderContext::new();
873        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
874        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
875
876        let name = builder
877            .func
878            .declare_imported_user_function(ir::UserExternalName {
879                namespace: 0,
880                index: 0,
881            });
882        let mut sig = Signature::new(CallConv::SystemV);
883        sig.params.push(AbiParam::new(ir::types::I32));
884        let signature = builder.func.import_signature(sig);
885        let func_ref = builder.import_function(ir::ExtFuncData {
886            name: ir::ExternalName::user(name),
887            signature,
888            colocated: true,
889        });
890
891        // At each `call` we are losing one more value as no longer live, so
892        // each stack map should be one smaller than the last. `v3` is never
893        // live across a safepoint, so should never appear in a stack map. Note
894        // that a value that is an argument to the call, but is not live after
895        // the call, should not appear in the stack map. This is why `v0`
896        // appears in the first call's stack map, but not the second call's
897        // stack map.
898        //
899        //     block0:
900        //       v0 = needs stack map
901        //       v1 = needs stack map
902        //       v2 = needs stack map
903        //       v3 = needs stack map
904        //       call $foo(v3)
905        //       call $foo(v0)
906        //       call $foo(v1)
907        //       call $foo(v2)
908        //       return
909        let block0 = builder.create_block();
910        builder.append_block_params_for_function_params(block0);
911        builder.switch_to_block(block0);
912        let v0 = builder.ins().iconst(ir::types::I32, 0);
913        builder.declare_value_needs_stack_map(v0);
914        let v1 = builder.ins().iconst(ir::types::I32, 1);
915        builder.declare_value_needs_stack_map(v1);
916        let v2 = builder.ins().iconst(ir::types::I32, 2);
917        builder.declare_value_needs_stack_map(v2);
918        let v3 = builder.ins().iconst(ir::types::I32, 3);
919        builder.declare_value_needs_stack_map(v3);
920        builder.ins().call(func_ref, &[v3]);
921        builder.ins().call(func_ref, &[v0]);
922        builder.ins().call(func_ref, &[v1]);
923        builder.ins().call(func_ref, &[v2]);
924        builder.ins().return_(&[]);
925        builder.seal_all_blocks();
926        builder.finalize();
927
928        assert_eq_output!(
929            func.display().to_string(),
930            r#"
931function %sample() system_v {
932    ss0 = explicit_slot 4, align = 4
933    ss1 = explicit_slot 4, align = 4
934    ss2 = explicit_slot 4, align = 4
935    sig0 = (i32) system_v
936    fn0 = colocated u0:0 sig0
937
938block0:
939    v0 = iconst.i32 0
940    stack_store v0, ss2  ; v0 = 0
941    v1 = iconst.i32 1
942    stack_store v1, ss1  ; v1 = 1
943    v2 = iconst.i32 2
944    stack_store v2, ss0  ; v2 = 2
945    v3 = iconst.i32 3
946    call fn0(v3), stack_map=[i32 @ ss2+0, i32 @ ss1+0, i32 @ ss0+0]  ; v3 = 3
947    v6 = stack_load.i32 ss2
948    call fn0(v6), stack_map=[i32 @ ss1+0, i32 @ ss0+0]
949    v5 = stack_load.i32 ss1
950    call fn0(v5), stack_map=[i32 @ ss0+0]
951    v4 = stack_load.i32 ss0
952    call fn0(v4)
953    return
954}
955            "#
956        );
957    }
958
959    #[test]
960    fn needs_stack_map_and_post_order_early_return() {
961        let mut sig = Signature::new(CallConv::SystemV);
962        sig.params.push(AbiParam::new(ir::types::I32));
963
964        let mut fn_ctx = FunctionBuilderContext::new();
965        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
966        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
967
968        let name = builder
969            .func
970            .declare_imported_user_function(ir::UserExternalName {
971                namespace: 0,
972                index: 0,
973            });
974        let signature = builder
975            .func
976            .import_signature(Signature::new(CallConv::SystemV));
977        let func_ref = builder.import_function(ir::ExtFuncData {
978            name: ir::ExternalName::user(name),
979            signature,
980            colocated: true,
981        });
982
983        // Here we rely on the post-order to make sure that we never visit block
984        // 4 and add `v1` to our live set, then visit block 2 and add `v1` to
985        // its stack map even though `v1` is not in scope. Thanksfully, that
986        // sequence is impossible because it would be an invalid post-order
987        // traversal. The only valid post-order traversals are [3, 1, 2, 0] and
988        // [2, 3, 1, 0].
989        //
990        //     block0(v0):
991        //       brif v0, block1, block2
992        //
993        //     block1:
994        //       <stuff>
995        //       v1 = get some gc ref
996        //       jump block3
997        //
998        //     block2:
999        //       call $needs_safepoint_accidentally
1000        //       return
1001        //
1002        //     block3:
1003        //       stuff keeping v1 live
1004        //       return
1005        let block0 = builder.create_block();
1006        let block1 = builder.create_block();
1007        let block2 = builder.create_block();
1008        let block3 = builder.create_block();
1009        builder.append_block_params_for_function_params(block0);
1010
1011        builder.switch_to_block(block0);
1012        let v0 = builder.func.dfg.block_params(block0)[0];
1013        builder.ins().brif(v0, block1, &[], block2, &[]);
1014
1015        builder.switch_to_block(block1);
1016        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1017        builder.declare_value_needs_stack_map(v1);
1018        builder.ins().jump(block3, &[]);
1019
1020        builder.switch_to_block(block2);
1021        builder.ins().call(func_ref, &[]);
1022        builder.ins().return_(&[]);
1023
1024        builder.switch_to_block(block3);
1025        // NB: Our simplistic liveness analysis conservatively treats any use of
1026        // a value as keeping it live, regardless if the use has side effects or
1027        // is otherwise itself live, so an `iadd_imm` suffices to keep `v1` live
1028        // here.
1029        builder.ins().iadd_imm(v1, 0);
1030        builder.ins().return_(&[]);
1031
1032        builder.seal_all_blocks();
1033        builder.finalize();
1034
1035        assert_eq_output!(
1036            func.display().to_string(),
1037            r#"
1038function %sample(i32) system_v {
1039    sig0 = () system_v
1040    fn0 = colocated u0:0 sig0
1041
1042block0(v0: i32):
1043    brif v0, block1, block2
1044
1045block1:
1046    v1 = iconst.i64 0x1234_5678
1047    jump block3
1048
1049block2:
1050    call fn0()
1051    return
1052
1053block3:
1054    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1055    return
1056}
1057            "#
1058        );
1059    }
1060
1061    #[test]
1062    fn needs_stack_map_conditional_branches_and_liveness() {
1063        let mut sig = Signature::new(CallConv::SystemV);
1064        sig.params.push(AbiParam::new(ir::types::I32));
1065
1066        let mut fn_ctx = FunctionBuilderContext::new();
1067        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1068        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1069
1070        let name = builder
1071            .func
1072            .declare_imported_user_function(ir::UserExternalName {
1073                namespace: 0,
1074                index: 0,
1075            });
1076        let signature = builder
1077            .func
1078            .import_signature(Signature::new(CallConv::SystemV));
1079        let func_ref = builder.import_function(ir::ExtFuncData {
1080            name: ir::ExternalName::user(name),
1081            signature,
1082            colocated: true,
1083        });
1084
1085        // We should not have a stack map entry for `v1` in block 1 because it
1086        // is not live across the call.
1087        //
1088        //     block0(v0):
1089        //       v1 = needs stack map
1090        //       brif v0, block1, block2
1091        //
1092        //     block1:
1093        //       call $foo()
1094        //       return
1095        //
1096        //     block2:
1097        //       keep v1 alive
1098        //       return
1099        let block0 = builder.create_block();
1100        let block1 = builder.create_block();
1101        let block2 = builder.create_block();
1102        builder.append_block_params_for_function_params(block0);
1103
1104        builder.switch_to_block(block0);
1105        let v0 = builder.func.dfg.block_params(block0)[0];
1106        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1107        builder.declare_value_needs_stack_map(v1);
1108        builder.ins().brif(v0, block1, &[], block2, &[]);
1109
1110        builder.switch_to_block(block1);
1111        builder.ins().call(func_ref, &[]);
1112        builder.ins().return_(&[]);
1113
1114        builder.switch_to_block(block2);
1115        // NB: Our simplistic liveness analysis conservatively treats any use of
1116        // a value as keeping it live, regardless if the use has side effects or
1117        // is otherwise itself live, so an `iadd_imm` suffices to keep `v1` live
1118        // here.
1119        builder.ins().iadd_imm(v1, 0);
1120        builder.ins().return_(&[]);
1121
1122        builder.seal_all_blocks();
1123        builder.finalize();
1124
1125        assert_eq_output!(
1126            func.display().to_string(),
1127            r#"
1128function %sample(i32) system_v {
1129    sig0 = () system_v
1130    fn0 = colocated u0:0 sig0
1131
1132block0(v0: i32):
1133    v1 = iconst.i64 0x1234_5678
1134    brif v0, block1, block2
1135
1136block1:
1137    call fn0()
1138    return
1139
1140block2:
1141    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1142    return
1143}
1144            "#
1145        );
1146
1147        // Now do the same test but with block 1 and 2 swapped so that we
1148        // exercise what we are trying to exercise, regardless of which
1149        // post-order traversal we happen to take.
1150        func.clear();
1151        fn_ctx.clear();
1152
1153        let mut sig = Signature::new(CallConv::SystemV);
1154        sig.params.push(AbiParam::new(ir::types::I32));
1155
1156        func.signature = sig;
1157        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1158
1159        let name = builder
1160            .func
1161            .declare_imported_user_function(ir::UserExternalName {
1162                namespace: 0,
1163                index: 0,
1164            });
1165        let signature = builder
1166            .func
1167            .import_signature(Signature::new(CallConv::SystemV));
1168        let func_ref = builder.import_function(ir::ExtFuncData {
1169            name: ir::ExternalName::user(name),
1170            signature,
1171            colocated: true,
1172        });
1173
1174        let block0 = builder.create_block();
1175        let block1 = builder.create_block();
1176        let block2 = builder.create_block();
1177        builder.append_block_params_for_function_params(block0);
1178
1179        builder.switch_to_block(block0);
1180        let v0 = builder.func.dfg.block_params(block0)[0];
1181        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1182        builder.declare_value_needs_stack_map(v1);
1183        builder.ins().brif(v0, block1, &[], block2, &[]);
1184
1185        builder.switch_to_block(block1);
1186        builder.ins().iadd_imm(v1, 0);
1187        builder.ins().return_(&[]);
1188
1189        builder.switch_to_block(block2);
1190        builder.ins().call(func_ref, &[]);
1191        builder.ins().return_(&[]);
1192
1193        builder.seal_all_blocks();
1194        builder.finalize();
1195
1196        assert_eq_output!(
1197            func.display().to_string(),
1198            r#"
1199function u0:0(i32) system_v {
1200    sig0 = () system_v
1201    fn0 = colocated u0:0 sig0
1202
1203block0(v0: i32):
1204    v1 = iconst.i64 0x1234_5678
1205    brif v0, block1, block2
1206
1207block1:
1208    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1209    return
1210
1211block2:
1212    call fn0()
1213    return
1214}
1215            "#
1216        );
1217    }
1218
1219    #[test]
1220    fn needs_stack_map_and_tail_calls() {
1221        let mut sig = Signature::new(CallConv::SystemV);
1222        sig.params.push(AbiParam::new(ir::types::I32));
1223
1224        let mut fn_ctx = FunctionBuilderContext::new();
1225        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1226        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1227
1228        let name = builder
1229            .func
1230            .declare_imported_user_function(ir::UserExternalName {
1231                namespace: 0,
1232                index: 0,
1233            });
1234        let signature = builder
1235            .func
1236            .import_signature(Signature::new(CallConv::SystemV));
1237        let func_ref = builder.import_function(ir::ExtFuncData {
1238            name: ir::ExternalName::user(name),
1239            signature,
1240            colocated: true,
1241        });
1242
1243        // Depending on which post-order traversal we take, we might consider
1244        // `v1` live inside `block1`. But nothing is live after a tail call so
1245        // we shouldn't spill `v1` either way here.
1246        //
1247        //     block0(v0):
1248        //       v1 = needs stack map
1249        //       brif v0, block1, block2
1250        //
1251        //     block1:
1252        //       return_call $foo()
1253        //
1254        //     block2:
1255        //       keep v1 alive
1256        //       return
1257        let block0 = builder.create_block();
1258        let block1 = builder.create_block();
1259        let block2 = builder.create_block();
1260        builder.append_block_params_for_function_params(block0);
1261
1262        builder.switch_to_block(block0);
1263        let v0 = builder.func.dfg.block_params(block0)[0];
1264        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1265        builder.declare_value_needs_stack_map(v1);
1266        builder.ins().brif(v0, block1, &[], block2, &[]);
1267
1268        builder.switch_to_block(block1);
1269        builder.ins().return_call(func_ref, &[]);
1270
1271        builder.switch_to_block(block2);
1272        // NB: Our simplistic liveness analysis conservatively treats any use of
1273        // a value as keeping it live, regardless if the use has side effects or
1274        // is otherwise itself live, so an `iadd_imm` suffices to keep `v1` live
1275        // here.
1276        builder.ins().iadd_imm(v1, 0);
1277        builder.ins().return_(&[]);
1278
1279        builder.seal_all_blocks();
1280        builder.finalize();
1281
1282        assert_eq_output!(
1283            func.display().to_string(),
1284            r#"
1285function %sample(i32) system_v {
1286    sig0 = () system_v
1287    fn0 = colocated u0:0 sig0
1288
1289block0(v0: i32):
1290    v1 = iconst.i64 0x1234_5678
1291    brif v0, block1, block2
1292
1293block1:
1294    return_call fn0()
1295
1296block2:
1297    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1298    return
1299}
1300            "#
1301        );
1302
1303        // Do the same test but with block 1 and 2 swapped so that we exercise
1304        // what we are trying to exercise, regardless of which post-order
1305        // traversal we happen to take.
1306        func.clear();
1307        fn_ctx.clear();
1308
1309        let mut sig = Signature::new(CallConv::SystemV);
1310        sig.params.push(AbiParam::new(ir::types::I32));
1311        func.signature = sig;
1312
1313        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1314
1315        let name = builder
1316            .func
1317            .declare_imported_user_function(ir::UserExternalName {
1318                namespace: 0,
1319                index: 0,
1320            });
1321        let signature = builder
1322            .func
1323            .import_signature(Signature::new(CallConv::SystemV));
1324        let func_ref = builder.import_function(ir::ExtFuncData {
1325            name: ir::ExternalName::user(name),
1326            signature,
1327            colocated: true,
1328        });
1329
1330        let block0 = builder.create_block();
1331        let block1 = builder.create_block();
1332        let block2 = builder.create_block();
1333        builder.append_block_params_for_function_params(block0);
1334
1335        builder.switch_to_block(block0);
1336        let v0 = builder.func.dfg.block_params(block0)[0];
1337        let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1338        builder.declare_value_needs_stack_map(v1);
1339        builder.ins().brif(v0, block1, &[], block2, &[]);
1340
1341        builder.switch_to_block(block1);
1342        builder.ins().iadd_imm(v1, 0);
1343        builder.ins().return_(&[]);
1344
1345        builder.switch_to_block(block2);
1346        builder.ins().return_call(func_ref, &[]);
1347
1348        builder.seal_all_blocks();
1349        builder.finalize();
1350
1351        assert_eq_output!(
1352            func.display().to_string(),
1353            r#"
1354function u0:0(i32) system_v {
1355    sig0 = () system_v
1356    fn0 = colocated u0:0 sig0
1357
1358block0(v0: i32):
1359    v1 = iconst.i64 0x1234_5678
1360    brif v0, block1, block2
1361
1362block1:
1363    v2 = iadd_imm.i64 v1, 0  ; v1 = 0x1234_5678
1364    return
1365
1366block2:
1367    return_call fn0()
1368}
1369            "#
1370        );
1371    }
1372
1373    #[test]
1374    fn needs_stack_map_and_cfg_diamond() {
1375        let mut sig = Signature::new(CallConv::SystemV);
1376        sig.params.push(AbiParam::new(ir::types::I32));
1377
1378        let mut fn_ctx = FunctionBuilderContext::new();
1379        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1380        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1381
1382        let name = builder
1383            .func
1384            .declare_imported_user_function(ir::UserExternalName {
1385                namespace: 0,
1386                index: 0,
1387            });
1388        let signature = builder
1389            .func
1390            .import_signature(Signature::new(CallConv::SystemV));
1391        let func_ref = builder.import_function(ir::ExtFuncData {
1392            name: ir::ExternalName::user(name),
1393            signature,
1394            colocated: true,
1395        });
1396
1397        // Create an if/else CFG diamond that and check that various things get
1398        // spilled as needed.
1399        //
1400        //     block0(v0):
1401        //       brif v0, block1, block2
1402        //
1403        //     block1:
1404        //       v1 = needs stack map
1405        //       v2 = needs stack map
1406        //       call $foo()
1407        //       jump block3(v1, v2)
1408        //
1409        //     block2:
1410        //       v3 = needs stack map
1411        //       v4 = needs stack map
1412        //       call $foo()
1413        //       jump block3(v3, v3)  ;; Note: v4 is not live
1414        //
1415        //     block3(v5, v6):
1416        //       call $foo()
1417        //       keep v5 alive, but not v6
1418        let block0 = builder.create_block();
1419        let block1 = builder.create_block();
1420        let block2 = builder.create_block();
1421        let block3 = builder.create_block();
1422        builder.append_block_params_for_function_params(block0);
1423
1424        builder.switch_to_block(block0);
1425        let v0 = builder.func.dfg.block_params(block0)[0];
1426        builder.ins().brif(v0, block1, &[], block2, &[]);
1427
1428        builder.switch_to_block(block1);
1429        let v1 = builder.ins().iconst(ir::types::I64, 1);
1430        builder.declare_value_needs_stack_map(v1);
1431        let v2 = builder.ins().iconst(ir::types::I64, 2);
1432        builder.declare_value_needs_stack_map(v2);
1433        builder.ins().call(func_ref, &[]);
1434        builder.ins().jump(block3, &[v1.into(), v2.into()]);
1435
1436        builder.switch_to_block(block2);
1437        let v3 = builder.ins().iconst(ir::types::I64, 3);
1438        builder.declare_value_needs_stack_map(v3);
1439        let v4 = builder.ins().iconst(ir::types::I64, 4);
1440        builder.declare_value_needs_stack_map(v4);
1441        builder.ins().call(func_ref, &[]);
1442        builder.ins().jump(block3, &[v3.into(), v3.into()]);
1443
1444        builder.switch_to_block(block3);
1445        builder.append_block_param(block3, ir::types::I64);
1446        builder.append_block_param(block3, ir::types::I64);
1447        builder.ins().call(func_ref, &[]);
1448        // NB: Our simplistic liveness analysis conservatively treats any use of
1449        // a value as keeping it live, regardless if the use has side effects or
1450        // is otherwise itself live, so an `iadd_imm` suffices to keep `v1` live
1451        // here.
1452        builder.ins().iadd_imm(v1, 0);
1453        builder.ins().return_(&[]);
1454
1455        builder.seal_all_blocks();
1456        builder.finalize();
1457
1458        assert_eq_output!(
1459            func.display().to_string(),
1460            r#"
1461function %sample(i32) system_v {
1462    ss0 = explicit_slot 8, align = 8
1463    ss1 = explicit_slot 8, align = 8
1464    sig0 = () system_v
1465    fn0 = colocated u0:0 sig0
1466
1467block0(v0: i32):
1468    brif v0, block1, block2
1469
1470block1:
1471    v1 = iconst.i64 1
1472    stack_store v1, ss0  ; v1 = 1
1473    v2 = iconst.i64 2
1474    stack_store v2, ss1  ; v2 = 2
1475    call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss1+0]
1476    v9 = stack_load.i64 ss0
1477    v10 = stack_load.i64 ss1
1478    jump block3(v9, v10)
1479
1480block2:
1481    v3 = iconst.i64 3
1482    stack_store v3, ss0  ; v3 = 3
1483    v4 = iconst.i64 4
1484    call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss0+0]
1485    v11 = stack_load.i64 ss0
1486    v12 = stack_load.i64 ss0
1487    jump block3(v11, v12)
1488
1489block3(v5: i64, v6: i64):
1490    call fn0(), stack_map=[i64 @ ss0+0]
1491    v8 = stack_load.i64 ss0
1492    v7 = iadd_imm v8, 0
1493    return
1494}
1495            "#
1496        );
1497    }
1498
1499    #[test]
1500    fn needs_stack_map_and_heterogeneous_types() {
1501        let _ = env_logger::try_init();
1502
1503        let mut sig = Signature::new(CallConv::SystemV);
1504        for ty in [
1505            ir::types::I8,
1506            ir::types::I16,
1507            ir::types::I32,
1508            ir::types::I64,
1509            ir::types::I128,
1510            ir::types::F32,
1511            ir::types::F64,
1512            ir::types::I8X16,
1513            ir::types::I16X8,
1514        ] {
1515            sig.params.push(AbiParam::new(ty));
1516            sig.returns.push(AbiParam::new(ty));
1517        }
1518
1519        let mut fn_ctx = FunctionBuilderContext::new();
1520        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1521        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1522
1523        let name = builder
1524            .func
1525            .declare_imported_user_function(ir::UserExternalName {
1526                namespace: 0,
1527                index: 0,
1528            });
1529        let signature = builder
1530            .func
1531            .import_signature(Signature::new(CallConv::SystemV));
1532        let func_ref = builder.import_function(ir::ExtFuncData {
1533            name: ir::ExternalName::user(name),
1534            signature,
1535            colocated: true,
1536        });
1537
1538        // Test that we support stack maps of heterogeneous types and properly
1539        // coalesce types into stack slots based on their size.
1540        //
1541        //     block0(v0, v1, v2, v3, v4, v5, v6, v7, v8):
1542        //       call $foo()
1543        //       return v0, v1, v2, v3, v4, v5, v6, v7, v8
1544        let block0 = builder.create_block();
1545        builder.append_block_params_for_function_params(block0);
1546
1547        builder.switch_to_block(block0);
1548        let params = builder.func.dfg.block_params(block0).to_vec();
1549        for val in &params {
1550            builder.declare_value_needs_stack_map(*val);
1551        }
1552        builder.ins().call(func_ref, &[]);
1553        builder.ins().return_(&params);
1554
1555        builder.seal_all_blocks();
1556        builder.finalize();
1557
1558        assert_eq_output!(
1559            func.display().to_string(),
1560            r#"
1561function %sample(i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8) -> i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8 system_v {
1562    ss0 = explicit_slot 1
1563    ss1 = explicit_slot 2, align = 2
1564    ss2 = explicit_slot 4, align = 4
1565    ss3 = explicit_slot 8, align = 8
1566    ss4 = explicit_slot 16, align = 16
1567    ss5 = explicit_slot 4, align = 4
1568    ss6 = explicit_slot 8, align = 8
1569    ss7 = explicit_slot 16, align = 16
1570    ss8 = explicit_slot 16, align = 16
1571    sig0 = () system_v
1572    fn0 = colocated u0:0 sig0
1573
1574block0(v0: i8, v1: i16, v2: i32, v3: i64, v4: i128, v5: f32, v6: f64, v7: i8x16, v8: i16x8):
1575    stack_store v0, ss0
1576    stack_store v1, ss1
1577    stack_store v2, ss2
1578    stack_store v3, ss3
1579    stack_store v4, ss4
1580    stack_store v5, ss5
1581    stack_store v6, ss6
1582    stack_store v7, ss7
1583    stack_store v8, ss8
1584    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]
1585    v9 = stack_load.i8 ss0
1586    v10 = stack_load.i16 ss1
1587    v11 = stack_load.i32 ss2
1588    v12 = stack_load.i64 ss3
1589    v13 = stack_load.i128 ss4
1590    v14 = stack_load.f32 ss5
1591    v15 = stack_load.f64 ss6
1592    v16 = stack_load.i8x16 ss7
1593    v17 = stack_load.i16x8 ss8
1594    return v9, v10, v11, v12, v13, v14, v15, v16, v17
1595}
1596            "#
1597        );
1598    }
1599
1600    #[test]
1601    fn series_of_non_overlapping_live_ranges_needs_stack_map() {
1602        let sig = Signature::new(CallConv::SystemV);
1603
1604        let mut fn_ctx = FunctionBuilderContext::new();
1605        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1606        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1607
1608        let name = builder
1609            .func
1610            .declare_imported_user_function(ir::UserExternalName {
1611                namespace: 0,
1612                index: 0,
1613            });
1614        let signature = builder
1615            .func
1616            .import_signature(Signature::new(CallConv::SystemV));
1617        let foo_func_ref = builder.import_function(ir::ExtFuncData {
1618            name: ir::ExternalName::user(name),
1619            signature,
1620            colocated: true,
1621        });
1622
1623        let name = builder
1624            .func
1625            .declare_imported_user_function(ir::UserExternalName {
1626                namespace: 0,
1627                index: 1,
1628            });
1629        let mut sig = Signature::new(CallConv::SystemV);
1630        sig.params.push(AbiParam::new(ir::types::I32));
1631        let signature = builder.func.import_signature(sig);
1632        let consume_func_ref = builder.import_function(ir::ExtFuncData {
1633            name: ir::ExternalName::user(name),
1634            signature,
1635            colocated: true,
1636        });
1637
1638        // Create a series of needs-stack-map values that do not have
1639        // overlapping live ranges, but which do appear in stack maps for calls
1640        // to `$foo`:
1641        //
1642        //     block0:
1643        //       v0 = needs stack map
1644        //       call $foo()
1645        //       call consume(v0)
1646        //       v1 = needs stack map
1647        //       call $foo()
1648        //       call consume(v1)
1649        //       v2 = needs stack map
1650        //       call $foo()
1651        //       call consume(v2)
1652        //       v3 = needs stack map
1653        //       call $foo()
1654        //       call consume(v3)
1655        //       return
1656        let block0 = builder.create_block();
1657        builder.append_block_params_for_function_params(block0);
1658        builder.switch_to_block(block0);
1659        let v0 = builder.ins().iconst(ir::types::I32, 0);
1660        builder.declare_value_needs_stack_map(v0);
1661        builder.ins().call(foo_func_ref, &[]);
1662        builder.ins().call(consume_func_ref, &[v0]);
1663        let v1 = builder.ins().iconst(ir::types::I32, 1);
1664        builder.declare_value_needs_stack_map(v1);
1665        builder.ins().call(foo_func_ref, &[]);
1666        builder.ins().call(consume_func_ref, &[v1]);
1667        let v2 = builder.ins().iconst(ir::types::I32, 2);
1668        builder.declare_value_needs_stack_map(v2);
1669        builder.ins().call(foo_func_ref, &[]);
1670        builder.ins().call(consume_func_ref, &[v2]);
1671        let v3 = builder.ins().iconst(ir::types::I32, 3);
1672        builder.declare_value_needs_stack_map(v3);
1673        builder.ins().call(foo_func_ref, &[]);
1674        builder.ins().call(consume_func_ref, &[v3]);
1675        builder.ins().return_(&[]);
1676        builder.seal_all_blocks();
1677        builder.finalize();
1678
1679        assert_eq_output!(
1680            func.display().to_string(),
1681            r#"
1682function %sample() system_v {
1683    ss0 = explicit_slot 4, align = 4
1684    sig0 = () system_v
1685    sig1 = (i32) system_v
1686    fn0 = colocated u0:0 sig0
1687    fn1 = colocated u0:1 sig1
1688
1689block0:
1690    v0 = iconst.i32 0
1691    stack_store v0, ss0  ; v0 = 0
1692    call fn0(), stack_map=[i32 @ ss0+0]
1693    v7 = stack_load.i32 ss0
1694    call fn1(v7)
1695    v1 = iconst.i32 1
1696    stack_store v1, ss0  ; v1 = 1
1697    call fn0(), stack_map=[i32 @ ss0+0]
1698    v6 = stack_load.i32 ss0
1699    call fn1(v6)
1700    v2 = iconst.i32 2
1701    stack_store v2, ss0  ; v2 = 2
1702    call fn0(), stack_map=[i32 @ ss0+0]
1703    v5 = stack_load.i32 ss0
1704    call fn1(v5)
1705    v3 = iconst.i32 3
1706    stack_store v3, ss0  ; v3 = 3
1707    call fn0(), stack_map=[i32 @ ss0+0]
1708    v4 = stack_load.i32 ss0
1709    call fn1(v4)
1710    return
1711}
1712            "#
1713        );
1714    }
1715
1716    #[test]
1717    fn vars_block_params_and_needs_stack_map() {
1718        let _ = env_logger::try_init();
1719
1720        let mut sig = Signature::new(CallConv::SystemV);
1721        sig.params.push(AbiParam::new(ir::types::I32));
1722        sig.returns.push(AbiParam::new(ir::types::I32));
1723
1724        let mut fn_ctx = FunctionBuilderContext::new();
1725        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1726        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1727
1728        let name = builder
1729            .func
1730            .declare_imported_user_function(ir::UserExternalName {
1731                namespace: 0,
1732                index: 0,
1733            });
1734        let mut sig = Signature::new(CallConv::SystemV);
1735        sig.params.push(AbiParam::new(ir::types::I32));
1736        let signature = builder.func.import_signature(sig);
1737        let func_ref = builder.import_function(ir::ExtFuncData {
1738            name: ir::ExternalName::user(name),
1739            signature,
1740            colocated: true,
1741        });
1742
1743        // Use a variable, create a control flow diamond so that the variable
1744        // forces a block parameter on the control join point, and make sure
1745        // that we get stack maps for all the appropriate uses of the variable
1746        // in all blocks, as well as that we are reusing stack slots for each of
1747        // the values.
1748        //
1749        //                        block0:
1750        //                          x := needs stack map
1751        //                          call $foo(x)
1752        //                          br_if v0, block1, block2
1753        //
1754        //
1755        //     block1:                                     block2:
1756        //       call $foo(x)                                call $foo(x)
1757        //       call $foo(x)                                call $foo(x)
1758        //       x := new needs stack map                    x := new needs stack map
1759        //       call $foo(x)                                call $foo(x)
1760        //       jump block3                                 jump block3
1761        //
1762        //
1763        //                        block3:
1764        //                          call $foo(x)
1765        //                          return x
1766
1767        let x = builder.declare_var(ir::types::I32);
1768        builder.declare_var_needs_stack_map(x);
1769
1770        let block0 = builder.create_block();
1771        let block1 = builder.create_block();
1772        let block2 = builder.create_block();
1773        let block3 = builder.create_block();
1774
1775        builder.append_block_params_for_function_params(block0);
1776        builder.switch_to_block(block0);
1777        let v0 = builder.func.dfg.block_params(block0)[0];
1778        let val = builder.ins().iconst(ir::types::I32, 42);
1779        builder.def_var(x, val);
1780        {
1781            let x = builder.use_var(x);
1782            builder.ins().call(func_ref, &[x]);
1783        }
1784        builder.ins().brif(v0, block1, &[], block2, &[]);
1785
1786        builder.switch_to_block(block1);
1787        {
1788            let x = builder.use_var(x);
1789            builder.ins().call(func_ref, &[x]);
1790            builder.ins().call(func_ref, &[x]);
1791        }
1792        let val = builder.ins().iconst(ir::types::I32, 36);
1793        builder.def_var(x, val);
1794        {
1795            let x = builder.use_var(x);
1796            builder.ins().call(func_ref, &[x]);
1797        }
1798        builder.ins().jump(block3, &[]);
1799
1800        builder.switch_to_block(block2);
1801        {
1802            let x = builder.use_var(x);
1803            builder.ins().call(func_ref, &[x]);
1804            builder.ins().call(func_ref, &[x]);
1805        }
1806        let val = builder.ins().iconst(ir::types::I32, 36);
1807        builder.def_var(x, val);
1808        {
1809            let x = builder.use_var(x);
1810            builder.ins().call(func_ref, &[x]);
1811        }
1812        builder.ins().jump(block3, &[]);
1813
1814        builder.switch_to_block(block3);
1815        let x = builder.use_var(x);
1816        builder.ins().call(func_ref, &[x]);
1817        builder.ins().return_(&[x]);
1818
1819        builder.seal_all_blocks();
1820        builder.finalize();
1821
1822        assert_eq_output!(
1823            func.display().to_string(),
1824            r#"
1825function %sample(i32) -> i32 system_v {
1826    ss0 = explicit_slot 4, align = 4
1827    ss1 = explicit_slot 4, align = 4
1828    sig0 = (i32) system_v
1829    fn0 = colocated u0:0 sig0
1830
1831block0(v0: i32):
1832    v1 = iconst.i32 42
1833    v2 -> v1
1834    v4 -> v1
1835    stack_store v1, ss0  ; v1 = 42
1836    v13 = stack_load.i32 ss0
1837    call fn0(v13), stack_map=[i32 @ ss0+0]
1838    brif v0, block1, block2
1839
1840block1:
1841    call fn0(v2), stack_map=[i32 @ ss0+0]  ; v2 = 42
1842    call fn0(v2)  ; v2 = 42
1843    v3 = iconst.i32 36
1844    stack_store v3, ss0  ; v3 = 36
1845    v10 = stack_load.i32 ss0
1846    call fn0(v10), stack_map=[i32 @ ss0+0]
1847    v9 = stack_load.i32 ss0
1848    jump block3(v9)
1849
1850block2:
1851    call fn0(v4), stack_map=[i32 @ ss0+0]  ; v4 = 42
1852    call fn0(v4)  ; v4 = 42
1853    v5 = iconst.i32 36
1854    stack_store v5, ss1  ; v5 = 36
1855    v12 = stack_load.i32 ss1
1856    call fn0(v12), stack_map=[i32 @ ss1+0]
1857    v11 = stack_load.i32 ss1
1858    jump block3(v11)
1859
1860block3(v6: i32):
1861    stack_store v6, ss0
1862    v8 = stack_load.i32 ss0
1863    call fn0(v8), stack_map=[i32 @ ss0+0]
1864    v7 = stack_load.i32 ss0
1865    return v7
1866}
1867            "#
1868        );
1869    }
1870
1871    #[test]
1872    fn var_needs_stack_map() {
1873        let mut sig = Signature::new(CallConv::SystemV);
1874        sig.params
1875            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1876        sig.returns
1877            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1878
1879        let mut fn_ctx = FunctionBuilderContext::new();
1880        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1881        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1882
1883        let var = builder.declare_var(cranelift_codegen::ir::types::I32);
1884        builder.declare_var_needs_stack_map(var);
1885
1886        let name = builder
1887            .func
1888            .declare_imported_user_function(ir::UserExternalName {
1889                namespace: 0,
1890                index: 0,
1891            });
1892        let signature = builder
1893            .func
1894            .import_signature(Signature::new(CallConv::SystemV));
1895        let func_ref = builder.import_function(ir::ExtFuncData {
1896            name: ir::ExternalName::user(name),
1897            signature,
1898            colocated: true,
1899        });
1900
1901        let block0 = builder.create_block();
1902        builder.append_block_params_for_function_params(block0);
1903        builder.switch_to_block(block0);
1904
1905        let arg = builder.func.dfg.block_params(block0)[0];
1906        builder.def_var(var, arg);
1907
1908        builder.ins().call(func_ref, &[]);
1909
1910        let val = builder.use_var(var);
1911        builder.ins().return_(&[val]);
1912
1913        builder.seal_all_blocks();
1914        builder.finalize();
1915
1916        assert_eq_output!(
1917            func.display().to_string(),
1918            r#"
1919function %sample(i32) -> i32 system_v {
1920    ss0 = explicit_slot 4, align = 4
1921    sig0 = () system_v
1922    fn0 = colocated u0:0 sig0
1923
1924block0(v0: i32):
1925    stack_store v0, ss0
1926    call fn0(), stack_map=[i32 @ ss0+0]
1927    v1 = stack_load.i32 ss0
1928    return v1
1929}
1930            "#
1931        );
1932    }
1933
1934    #[test]
1935    fn first_inst_defines_needs_stack_map() {
1936        let mut sig = Signature::new(CallConv::SystemV);
1937        sig.params
1938            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1939        sig.returns
1940            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1941        sig.returns
1942            .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1943
1944        let mut fn_ctx = FunctionBuilderContext::new();
1945        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1946        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1947
1948        let name = builder
1949            .func
1950            .declare_imported_user_function(ir::UserExternalName {
1951                namespace: 0,
1952                index: 0,
1953            });
1954        let signature = builder
1955            .func
1956            .import_signature(Signature::new(CallConv::SystemV));
1957        let func_ref = builder.import_function(ir::ExtFuncData {
1958            name: ir::ExternalName::user(name),
1959            signature,
1960            colocated: true,
1961        });
1962
1963        // Regression test found via fuzzing in
1964        // https://github.com/bytecodealliance/wasmtime/pull/8941 involving the
1965        // combination of cursor positions after we have block parameters that
1966        // need inclusion in stack maps and when the first instruction in a
1967        // block defines a value that needs inclusion in stack maps.
1968        //
1969        // block0(v0: i32):
1970        //   v1 = iconst.i32 42
1971        //   call $foo()
1972        //   return v0, v1
1973
1974        let block0 = builder.create_block();
1975        builder.append_block_params_for_function_params(block0);
1976        builder.switch_to_block(block0);
1977
1978        let arg = builder.func.dfg.block_params(block0)[0];
1979        builder.declare_value_needs_stack_map(arg);
1980
1981        let val = builder.ins().iconst(ir::types::I32, 42);
1982        builder.declare_value_needs_stack_map(val);
1983
1984        builder.ins().call(func_ref, &[]);
1985
1986        builder.ins().return_(&[arg, val]);
1987
1988        builder.seal_all_blocks();
1989        builder.finalize();
1990
1991        assert_eq_output!(
1992            func.display().to_string(),
1993            r#"
1994function %sample(i32) -> i32, i32 system_v {
1995    ss0 = explicit_slot 4, align = 4
1996    ss1 = explicit_slot 4, align = 4
1997    sig0 = () system_v
1998    fn0 = colocated u0:0 sig0
1999
2000block0(v0: i32):
2001    stack_store v0, ss0
2002    v1 = iconst.i32 42
2003    stack_store v1, ss1  ; v1 = 42
2004    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
2005    v2 = stack_load.i32 ss0
2006    v3 = stack_load.i32 ss1
2007    return v2, v3
2008}
2009            "#
2010        );
2011    }
2012
2013    #[test]
2014    fn needs_stack_map_and_loops_and_partially_live_values() {
2015        let _ = env_logger::try_init();
2016
2017        let mut sig = Signature::new(CallConv::SystemV);
2018        sig.params.push(AbiParam::new(ir::types::I32));
2019
2020        let mut fn_ctx = FunctionBuilderContext::new();
2021        let mut func =
2022            Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig.clone());
2023        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2024
2025        let name = builder
2026            .func
2027            .declare_imported_user_function(ir::UserExternalName {
2028                namespace: 0,
2029                index: 0,
2030            });
2031        let signature = builder
2032            .func
2033            .import_signature(Signature::new(CallConv::SystemV));
2034        let foo_func_ref = builder.import_function(ir::ExtFuncData {
2035            name: ir::ExternalName::user(name),
2036            signature,
2037            colocated: true,
2038        });
2039
2040        let name = builder
2041            .func
2042            .declare_imported_user_function(ir::UserExternalName {
2043                namespace: 1,
2044                index: 1,
2045            });
2046        let signature = builder.func.import_signature(sig);
2047        let bar_func_ref = builder.import_function(ir::ExtFuncData {
2048            name: ir::ExternalName::user(name),
2049            signature,
2050            colocated: true,
2051        });
2052
2053        // Test that we support stack maps in loops and that we properly handle
2054        // value that are only live for part of the loop body on each iteration,
2055        // but are live across the whole loop because they will be used again
2056        // the next iteration. Note that `v0` below, which is a GC value, is not
2057        // live *within a single iteration of the loop* after the call to `bar`,
2058        // but is actually live across the whole loop because it will be used
2059        // again in the *next iteration of the loop*:
2060        //
2061        //     block0(v0: i32):
2062        //       jump block1
2063        //
2064        //     block1:
2065        //       call $foo()
2066        //       call $bar(v0)
2067        //       call $foo()
2068        //       jump block1
2069        let block0 = builder.create_block();
2070        let block1 = builder.create_block();
2071        builder.append_block_params_for_function_params(block0);
2072
2073        builder.switch_to_block(block0);
2074        builder.ins().jump(block1, &[]);
2075
2076        builder.switch_to_block(block1);
2077        let v0 = builder.func.dfg.block_params(block0)[0];
2078        builder.declare_value_needs_stack_map(v0);
2079        builder.ins().call(foo_func_ref, &[]);
2080        builder.ins().call(bar_func_ref, &[v0]);
2081        builder.ins().call(foo_func_ref, &[]);
2082        builder.ins().jump(block1, &[]);
2083
2084        builder.seal_all_blocks();
2085        builder.finalize();
2086
2087        assert_eq_output!(
2088            func.display().to_string(),
2089            r#"
2090function %sample(i32) system_v {
2091    ss0 = explicit_slot 4, align = 4
2092    sig0 = () system_v
2093    sig1 = (i32) system_v
2094    fn0 = colocated u0:0 sig0
2095    fn1 = colocated u1:1 sig1
2096
2097block0(v0: i32):
2098    stack_store v0, ss0
2099    jump block1
2100
2101block1:
2102    call fn0(), stack_map=[i32 @ ss0+0]
2103    v1 = stack_load.i32 ss0
2104    call fn1(v1), stack_map=[i32 @ ss0+0]
2105    call fn0(), stack_map=[i32 @ ss0+0]
2106    jump block1
2107}
2108            "#,
2109        );
2110    }
2111
2112    #[test]
2113    fn needs_stack_map_and_irreducible_loops() {
2114        let _ = env_logger::try_init();
2115
2116        let mut sig = Signature::new(CallConv::SystemV);
2117        sig.params.push(AbiParam::new(ir::types::I32));
2118        sig.params.push(AbiParam::new(ir::types::I32));
2119
2120        let mut fn_ctx = FunctionBuilderContext::new();
2121        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2122        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2123
2124        let name = builder
2125            .func
2126            .declare_imported_user_function(ir::UserExternalName {
2127                namespace: 0,
2128                index: 0,
2129            });
2130        let signature = builder
2131            .func
2132            .import_signature(Signature::new(CallConv::SystemV));
2133        let foo_func_ref = builder.import_function(ir::ExtFuncData {
2134            name: ir::ExternalName::user(name),
2135            signature,
2136            colocated: true,
2137        });
2138
2139        let name = builder
2140            .func
2141            .declare_imported_user_function(ir::UserExternalName {
2142                namespace: 1,
2143                index: 1,
2144            });
2145        let mut sig = Signature::new(CallConv::SystemV);
2146        sig.params.push(AbiParam::new(ir::types::I32));
2147        let signature = builder.func.import_signature(sig);
2148        let bar_func_ref = builder.import_function(ir::ExtFuncData {
2149            name: ir::ExternalName::user(name),
2150            signature,
2151            colocated: true,
2152        });
2153
2154        // Test an irreducible loop with multiple entry points, both block1 and
2155        // block2, in this case:
2156        //
2157        //     block0(v0: i32, v1: i32):
2158        //       brif v0, block1, block2
2159        //
2160        //     block1:
2161        //       jump block3
2162        //
2163        //     block2:
2164        //       jump block4
2165        //
2166        //     block3:
2167        //       call $foo()
2168        //       call $bar(v1)
2169        //       call $foo()
2170        //       jump block2
2171        //
2172        //     block4:
2173        //       call $foo()
2174        //       call $bar(v1)
2175        //       call $foo()
2176        //       jump block1
2177        let block0 = builder.create_block();
2178        let block1 = builder.create_block();
2179        let block2 = builder.create_block();
2180        let block3 = builder.create_block();
2181        let block4 = builder.create_block();
2182        builder.append_block_params_for_function_params(block0);
2183
2184        builder.switch_to_block(block0);
2185        let v0 = builder.func.dfg.block_params(block0)[0];
2186        let v1 = builder.func.dfg.block_params(block0)[1];
2187        builder.declare_value_needs_stack_map(v1);
2188        builder.ins().brif(v0, block1, &[], block2, &[]);
2189
2190        builder.switch_to_block(block1);
2191        builder.ins().jump(block3, &[]);
2192
2193        builder.switch_to_block(block2);
2194        builder.ins().jump(block4, &[]);
2195
2196        builder.switch_to_block(block3);
2197        builder.ins().call(foo_func_ref, &[]);
2198        builder.ins().call(bar_func_ref, &[v1]);
2199        builder.ins().call(foo_func_ref, &[]);
2200        builder.ins().jump(block2, &[]);
2201
2202        builder.switch_to_block(block4);
2203        builder.ins().call(foo_func_ref, &[]);
2204        builder.ins().call(bar_func_ref, &[v1]);
2205        builder.ins().call(foo_func_ref, &[]);
2206        builder.ins().jump(block1, &[]);
2207
2208        builder.seal_all_blocks();
2209        builder.finalize();
2210
2211        assert_eq_output!(
2212            func.display().to_string(),
2213            r#"
2214function %sample(i32, i32) system_v {
2215    ss0 = explicit_slot 4, align = 4
2216    sig0 = () system_v
2217    sig1 = (i32) system_v
2218    fn0 = colocated u0:0 sig0
2219    fn1 = colocated u1:1 sig1
2220
2221block0(v0: i32, v1: i32):
2222    stack_store v1, ss0
2223    brif v0, block1, block2
2224
2225block1:
2226    jump block3
2227
2228block2:
2229    jump block4
2230
2231block3:
2232    call fn0(), stack_map=[i32 @ ss0+0]
2233    v3 = stack_load.i32 ss0
2234    call fn1(v3), stack_map=[i32 @ ss0+0]
2235    call fn0(), stack_map=[i32 @ ss0+0]
2236    jump block2
2237
2238block4:
2239    call fn0(), stack_map=[i32 @ ss0+0]
2240    v2 = stack_load.i32 ss0
2241    call fn1(v2), stack_map=[i32 @ ss0+0]
2242    call fn0(), stack_map=[i32 @ ss0+0]
2243    jump block1
2244}
2245            "#,
2246        );
2247    }
2248
2249    #[test]
2250    fn needs_stack_map_and_back_edge_to_back_edge() {
2251        let _ = env_logger::try_init();
2252
2253        let mut sig = Signature::new(CallConv::SystemV);
2254        sig.params.push(AbiParam::new(ir::types::I32));
2255        sig.params.push(AbiParam::new(ir::types::I32));
2256        sig.params.push(AbiParam::new(ir::types::I32));
2257        sig.params.push(AbiParam::new(ir::types::I32));
2258
2259        let mut fn_ctx = FunctionBuilderContext::new();
2260        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2261        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2262
2263        let name = builder
2264            .func
2265            .declare_imported_user_function(ir::UserExternalName {
2266                namespace: 0,
2267                index: 0,
2268            });
2269        let signature = builder
2270            .func
2271            .import_signature(Signature::new(CallConv::SystemV));
2272        let foo_func_ref = builder.import_function(ir::ExtFuncData {
2273            name: ir::ExternalName::user(name),
2274            signature,
2275            colocated: true,
2276        });
2277
2278        let name = builder
2279            .func
2280            .declare_imported_user_function(ir::UserExternalName {
2281                namespace: 1,
2282                index: 1,
2283            });
2284        let mut sig = Signature::new(CallConv::SystemV);
2285        sig.params.push(AbiParam::new(ir::types::I32));
2286        let signature = builder.func.import_signature(sig);
2287        let bar_func_ref = builder.import_function(ir::ExtFuncData {
2288            name: ir::ExternalName::user(name),
2289            signature,
2290            colocated: true,
2291        });
2292
2293        // Test that we detect the `block1 -> block2 -> block3 -> block2 ->
2294        // block1` loop in our liveness analysis and keep `v{0,1,2}` live across
2295        // the whole loop body.
2296        //
2297        //     block0(v0, v1, v2, v3):
2298        //       jump block1(v3)
2299        //
2300        //     block1(v4):
2301        //       call foo_func_ref()
2302        //       call bar_func_ref(v0)
2303        //       call foo_func_ref()
2304        //       jump block2
2305        //
2306        //     block2:
2307        //       call foo_func_ref()
2308        //       call bar_func_ref(v1)
2309        //       call foo_func_ref()
2310        //       v5 = iadd_imm v4, -1
2311        //       brif v4, block1(v5), block3
2312        //
2313        //     block3:
2314        //       call foo_func_ref()
2315        //       call bar_func_ref(v2)
2316        //       call foo_func_ref()
2317        //       jump block2
2318
2319        let block0 = builder.create_block();
2320        let block1 = builder.create_block();
2321        let block2 = builder.create_block();
2322        let block3 = builder.create_block();
2323
2324        builder.append_block_params_for_function_params(block0);
2325
2326        builder.switch_to_block(block0);
2327
2328        let v0 = builder.func.dfg.block_params(block0)[0];
2329        builder.declare_value_needs_stack_map(v0);
2330        let v1 = builder.func.dfg.block_params(block0)[1];
2331        builder.declare_value_needs_stack_map(v1);
2332        let v2 = builder.func.dfg.block_params(block0)[2];
2333        builder.declare_value_needs_stack_map(v2);
2334        let v3 = builder.func.dfg.block_params(block0)[3];
2335
2336        builder.ins().jump(block1, &[v3.into()]);
2337
2338        builder.switch_to_block(block1);
2339        let v4 = builder.append_block_param(block1, ir::types::I32);
2340        builder.ins().call(foo_func_ref, &[]);
2341        builder.ins().call(bar_func_ref, &[v0]);
2342        builder.ins().call(foo_func_ref, &[]);
2343        builder.ins().jump(block2, &[]);
2344
2345        builder.switch_to_block(block2);
2346        builder.ins().call(foo_func_ref, &[]);
2347        builder.ins().call(bar_func_ref, &[v1]);
2348        builder.ins().call(foo_func_ref, &[]);
2349        let v5 = builder.ins().iadd_imm(v4, -1);
2350        builder.ins().brif(v4, block1, &[v5.into()], block3, &[]);
2351
2352        builder.switch_to_block(block3);
2353        builder.ins().call(foo_func_ref, &[]);
2354        builder.ins().call(bar_func_ref, &[v2]);
2355        builder.ins().call(foo_func_ref, &[]);
2356        builder.ins().jump(block2, &[]);
2357
2358        builder.seal_all_blocks();
2359        builder.finalize();
2360
2361        assert_eq_output!(
2362            func.display().to_string(),
2363            r#"
2364function %sample(i32, i32, i32, i32) system_v {
2365    ss0 = explicit_slot 4, align = 4
2366    ss1 = explicit_slot 4, align = 4
2367    ss2 = explicit_slot 4, align = 4
2368    sig0 = () system_v
2369    sig1 = (i32) system_v
2370    fn0 = colocated u0:0 sig0
2371    fn1 = colocated u1:1 sig1
2372
2373block0(v0: i32, v1: i32, v2: i32, v3: i32):
2374    stack_store v0, ss0
2375    stack_store v1, ss1
2376    stack_store v2, ss2
2377    jump block1(v3)
2378
2379block1(v4: i32):
2380    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2381    v8 = stack_load.i32 ss0
2382    call fn1(v8), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2383    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2384    jump block2
2385
2386block2:
2387    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2388    v7 = stack_load.i32 ss1
2389    call fn1(v7), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2390    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2391    v5 = iadd_imm.i32 v4, -1
2392    brif.i32 v4, block1(v5), block3
2393
2394block3:
2395    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2396    v6 = stack_load.i32 ss2
2397    call fn1(v6), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2398    call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2399    jump block2
2400}
2401            "#,
2402        );
2403    }
2404
2405    fn import_func(
2406        builder: &mut FunctionBuilder,
2407        params: impl IntoIterator<Item = ir::Type>,
2408        results: impl IntoIterator<Item = ir::Type>,
2409    ) -> ir::FuncRef {
2410        let index = u32::try_from(builder.func.dfg.ext_funcs.len()).unwrap();
2411
2412        let name = builder
2413            .func
2414            .declare_imported_user_function(ir::UserExternalName {
2415                namespace: 0,
2416                index,
2417            });
2418        let name = ir::ExternalName::user(name);
2419
2420        let mut signature = Signature::new(CallConv::SystemV);
2421        signature
2422            .params
2423            .extend(params.into_iter().map(|ty| AbiParam::new(ty)));
2424        signature
2425            .returns
2426            .extend(results.into_iter().map(|ty| AbiParam::new(ty)));
2427        let signature = builder.func.import_signature(signature);
2428
2429        builder.import_function(ir::ExtFuncData {
2430            name,
2431            signature,
2432            colocated: true,
2433        })
2434    }
2435
2436    #[test]
2437    fn issue_10397_stack_map_vars_and_indirect_block_params() {
2438        let _ = env_logger::try_init();
2439
2440        let mut sig = Signature::new(CallConv::SystemV);
2441        if false {
2442            sig.params.push(AbiParam::new(ir::types::I32));
2443        }
2444
2445        let mut fn_ctx = FunctionBuilderContext::new();
2446        let mut func = Function::with_name_signature(ir::UserFuncName::testcase("f"), sig);
2447        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2448
2449        let alloc_struct = import_func(&mut builder, None, Some(ir::types::I32));
2450        let alloc_array = import_func(&mut builder, None, Some(ir::types::I32));
2451        let array_init_elem = import_func(&mut builder, Some(ir::types::I32), None);
2452        let type_of = import_func(&mut builder, Some(ir::types::I32), Some(ir::types::I32));
2453        let ref_test = import_func(
2454            &mut builder,
2455            vec![ir::types::I32, ir::types::I32],
2456            Some(ir::types::I32),
2457        );
2458        let access_array = import_func(&mut builder, Some(ir::types::I32), None);
2459        let should_continue_inner_loop = import_func(&mut builder, None, Some(ir::types::I32));
2460        let access_struct = import_func(&mut builder, Some(ir::types::I32), None);
2461        let should_return = import_func(&mut builder, None, Some(ir::types::I32));
2462
2463        // This test exercises the combination of declaring stack maps and when
2464        // we need to insert block params during SSA construction. The following
2465        // CLIF uses vars in such a way that it will ultimately require that we
2466        // insert block params. However, some of the inserted block params are
2467        // only ever used in such a way that they are passed as arguments to
2468        // *other* blocks. That is, there are regions where we never use them
2469        // directly, and therefore, if we are only declaring that the variable's
2470        // values need inclusion in stack maps on direct uses, we will
2471        // completely miss these inserted block params. That leads to incomplete
2472        // stack maps, which leads to collecting objects too early, which leads
2473        // to use-after-free bugs.
2474        //
2475        // After inserting safepoint spills and stack map annotations to the
2476        // following pseudo-CLIF, we should have stack map entries for the
2477        // `live: {...}` annotations (or an over-approximation of that
2478        // annotation).
2479        //
2480        //     block_entry:
2481        //       v0 = call alloc_struct()                          ;; live: {}
2482        //       define var_struct = v0
2483        //       jump block_outer_loop_head
2484        //
2485        //     block_outer_loop_head:
2486        //       v1 = call alloc_array()                           ;; live: {struct}
2487        //       define var_array = v1
2488        //       v2 = iconst.i32 0
2489        //       jump block_array_init_loop_head(v2)
2490        //
2491        //     block_array_init_loop_head(v3):
2492        //       v4 = iconst.i32 1
2493        //       v5 = icmp ult v3, v4
2494        //       br_if v5, block_array_init_loop_body, block_array_init_loop_done
2495        //
2496        //     block_array_init_loop_body:
2497        //       call array_init_elem(v1, v4)                      ;; live: {struct, array}
2498        //       v6 = iconst.i32 1
2499        //       v7 = iadd v4, v6
2500        //       jump block_array_init_loop_head(v7)
2501        //
2502        //     block_array_init_loop_done:
2503        //       jump block_inner_loop_head
2504        //
2505        //     block_inner_loop_head:
2506        //       v8 = use var_array
2507        //       v9 = iconst.i32 0
2508        //       v10 = icmp eq v8, v9
2509        //       br_if v10, block_ref_test_done(v9), block_ref_test_non_null
2510        //
2511        //     block_ref_test_non_null:
2512        //       v11 = call type_of(v8)                            ;; live: {struct, array}
2513        //       v12 = iconst.i32 0xbeefbeef
2514        //       v13 = icmp eq v11, v12
2515        //       v14 = iconst.i31 1
2516        //       br_if v13, block_ref_test_done(v14), block_ref_test_slow
2517        //
2518        //     block_ref_test_slow:
2519        //       v15 = call ref_test(v8, v12)                      ;; live: {struct, array}
2520        //       jump block_ref_test_done(v15)
2521        //
2522        //     block_ref_test_done(v16):
2523        //       trapz v16, user1
2524        //       define var_array = v8
2525        //       call access_array(v8)                             ;; live: {struct}
2526        //       v17 = call should_continue_inner_loop()           ;; live: {struct}
2527        //       br_if v17, block_inner_loop_head, block_after_inner_loop
2528        //
2529        //     block_after_inner_loop:
2530        //       v18 = use var_struct
2531        //       call access_struct(v18)                           ;; live: {struct}
2532        //       v19 = call should_return()                        ;; live: {struct}
2533        //       br_if v19, block_return, block_outer_loop_head
2534        //
2535        //     block_return:
2536        //       return
2537
2538        let var_struct = builder.declare_var(cranelift_codegen::ir::types::I32);
2539        builder.declare_var_needs_stack_map(var_struct);
2540
2541        let var_array = builder.declare_var(cranelift_codegen::ir::types::I32);
2542        builder.declare_var_needs_stack_map(var_array);
2543
2544        let block_entry = builder.create_block();
2545        let block_outer_loop_head = builder.create_block();
2546        let block_array_init_loop_head = builder.create_block();
2547        let block_array_init_loop_body = builder.create_block();
2548        let block_array_init_loop_done = builder.create_block();
2549        let block_inner_loop_head = builder.create_block();
2550        let block_ref_test_non_null = builder.create_block();
2551        let block_ref_test_slow = builder.create_block();
2552        let block_ref_test_done = builder.create_block();
2553        let block_after_inner_loop = builder.create_block();
2554        let block_return = builder.create_block();
2555
2556        builder.append_block_params_for_function_params(block_entry);
2557        builder.switch_to_block(block_entry);
2558        builder.seal_block(block_entry);
2559        let call_inst = builder.ins().call(alloc_struct, &[]);
2560        let v0 = builder.func.dfg.first_result(call_inst);
2561        builder.def_var(var_struct, v0);
2562        builder.ins().jump(block_outer_loop_head, &[]);
2563
2564        builder.switch_to_block(block_outer_loop_head);
2565        let call_inst = builder.ins().call(alloc_array, &[]);
2566        let v1 = builder.func.dfg.first_result(call_inst);
2567        builder.def_var(var_array, v1);
2568        let v2 = builder.ins().iconst(ir::types::I32, 0);
2569        builder.ins().jump(block_array_init_loop_head, &[v2.into()]);
2570
2571        builder.switch_to_block(block_array_init_loop_head);
2572        let v3 = builder.append_block_param(block_array_init_loop_head, ir::types::I32);
2573        let v4 = builder.ins().iconst(ir::types::I32, 1);
2574        let v5 = builder
2575            .ins()
2576            .icmp(ir::condcodes::IntCC::UnsignedLessThan, v3, v4);
2577        builder.ins().brif(
2578            v5,
2579            block_array_init_loop_body,
2580            &[],
2581            block_array_init_loop_done,
2582            &[],
2583        );
2584
2585        builder.switch_to_block(block_array_init_loop_body);
2586        builder.seal_block(block_array_init_loop_body);
2587        builder.ins().call(array_init_elem, &[v1, v4]);
2588        let v6 = builder.ins().iconst(ir::types::I32, 1);
2589        let v7 = builder.ins().iadd(v4, v6);
2590        builder.ins().jump(block_array_init_loop_head, &[v7.into()]);
2591        builder.seal_block(block_array_init_loop_head);
2592
2593        builder.switch_to_block(block_array_init_loop_done);
2594        builder.seal_block(block_array_init_loop_done);
2595        builder.ins().jump(block_inner_loop_head, &[]);
2596
2597        builder.switch_to_block(block_inner_loop_head);
2598        let v8 = builder.use_var(var_array);
2599        let v9 = builder.ins().iconst(ir::types::I32, 0);
2600        let v10 = builder.ins().icmp(ir::condcodes::IntCC::Equal, v8, v9);
2601        builder.ins().brif(
2602            v10,
2603            block_ref_test_done,
2604            &[v9.into()],
2605            block_ref_test_non_null,
2606            &[],
2607        );
2608
2609        builder.switch_to_block(block_ref_test_non_null);
2610        builder.seal_block(block_ref_test_non_null);
2611        let call_inst = builder.ins().call(type_of, &[v8]);
2612        let v11 = builder.func.dfg.first_result(call_inst);
2613        let v12 = builder.ins().iconst(ir::types::I32, 0xbeefbeef);
2614        let v13 = builder.ins().icmp(ir::condcodes::IntCC::Equal, v11, v12);
2615        let v14 = builder.ins().iconst(ir::types::I32, 1);
2616        builder.ins().brif(
2617            v13,
2618            block_ref_test_done,
2619            &[v14.into()],
2620            block_ref_test_slow,
2621            &[],
2622        );
2623
2624        builder.switch_to_block(block_ref_test_slow);
2625        builder.seal_block(block_ref_test_slow);
2626        let call_inst = builder.ins().call(ref_test, &[v8, v12]);
2627        let v15 = builder.func.dfg.first_result(call_inst);
2628        builder.ins().jump(block_ref_test_done, &[v15.into()]);
2629
2630        builder.switch_to_block(block_ref_test_done);
2631        let v16 = builder.append_block_param(block_ref_test_done, ir::types::I32);
2632        builder.seal_block(block_ref_test_done);
2633        builder.ins().trapz(v16, ir::TrapCode::user(1).unwrap());
2634        builder.def_var(var_array, v8);
2635        builder.ins().call(access_array, &[v8]);
2636        let call_inst = builder.ins().call(should_continue_inner_loop, &[]);
2637        let v17 = builder.func.dfg.first_result(call_inst);
2638        builder
2639            .ins()
2640            .brif(v17, block_inner_loop_head, &[], block_after_inner_loop, &[]);
2641        builder.seal_block(block_inner_loop_head);
2642
2643        builder.switch_to_block(block_after_inner_loop);
2644        builder.seal_block(block_after_inner_loop);
2645        let v18 = builder.use_var(var_struct);
2646        builder.ins().call(access_struct, &[v18]);
2647        let call_inst = builder.ins().call(should_return, &[]);
2648        let v19 = builder.func.dfg.first_result(call_inst);
2649        builder
2650            .ins()
2651            .brif(v19, block_return, &[], block_outer_loop_head, &[]);
2652        builder.seal_block(block_outer_loop_head);
2653
2654        builder.switch_to_block(block_return);
2655        builder.seal_block(block_return);
2656        builder.ins().return_(&[]);
2657
2658        builder.finalize();
2659        assert_eq_output!(
2660            func.display().to_string(),
2661            r#"
2662function %f() system_v {
2663    ss0 = explicit_slot 4, align = 4
2664    ss1 = explicit_slot 4, align = 4
2665    ss2 = explicit_slot 4, align = 4
2666    sig0 = () -> i32 system_v
2667    sig1 = () -> i32 system_v
2668    sig2 = (i32) system_v
2669    sig3 = (i32) -> i32 system_v
2670    sig4 = (i32, i32) -> i32 system_v
2671    sig5 = (i32) system_v
2672    sig6 = () -> i32 system_v
2673    sig7 = (i32) system_v
2674    sig8 = () -> i32 system_v
2675    fn0 = colocated u0:0 sig0
2676    fn1 = colocated u0:1 sig1
2677    fn2 = colocated u0:2 sig2
2678    fn3 = colocated u0:3 sig3
2679    fn4 = colocated u0:4 sig4
2680    fn5 = colocated u0:5 sig5
2681    fn6 = colocated u0:6 sig6
2682    fn7 = colocated u0:7 sig7
2683    fn8 = colocated u0:8 sig8
2684
2685block0:
2686    v0 = call fn0()
2687    jump block1(v0)
2688
2689block1(v22: i32):
2690    v21 -> v22
2691    stack_store v22, ss1
2692    v1 = call fn1(), stack_map=[i32 @ ss1+0]
2693    v8 -> v1
2694    v18 -> v1
2695    stack_store v1, ss0
2696    v2 = iconst.i32 0
2697    jump block2(v2)  ; v2 = 0
2698
2699block2(v3: i32):
2700    v4 = iconst.i32 1
2701    v5 = icmp ult v3, v4  ; v4 = 1
2702    brif v5, block3, block4
2703
2704block3:
2705    v24 = stack_load.i32 ss0
2706    call fn2(v24, v4), stack_map=[i32 @ ss0+0, i32 @ ss1+0]  ; v4 = 1
2707    v6 = iconst.i32 1
2708    v7 = iadd.i32 v4, v6  ; v4 = 1, v6 = 1
2709    jump block2(v7)
2710
2711block4:
2712    v26 = stack_load.i32 ss1
2713    jump block5(v26)
2714
2715block5(v20: i32):
2716    v19 -> v20
2717    stack_store v20, ss2
2718    v9 = iconst.i32 0
2719    v10 = icmp.i32 eq v8, v9  ; v9 = 0
2720    brif v10, block8(v9), block6  ; v9 = 0
2721
2722block6:
2723    v11 = call fn3(v8), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2724    v12 = iconst.i32 -1091584273
2725    v13 = icmp eq v11, v12  ; v12 = -1091584273
2726    v14 = iconst.i32 1
2727    brif v13, block8(v14), block7  ; v14 = 1
2728
2729block7:
2730    v15 = call fn4(v8, v12), stack_map=[i32 @ ss0+0, i32 @ ss2+0]  ; v12 = -1091584273
2731    jump block8(v15)
2732
2733block8(v16: i32):
2734    trapz v16, user1
2735    call fn5(v8), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2736    v17 = call fn6(), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2737    brif v17, block5(v19), block9
2738
2739block9:
2740    v25 = stack_load.i32 ss2
2741    call fn7(v25), stack_map=[i32 @ ss2+0]
2742    v23 = call fn8(), stack_map=[i32 @ ss2+0]
2743    brif v23, block10, block1(v19)
2744
2745block10:
2746    return
2747}
2748            "#,
2749        );
2750    }
2751}