1use 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 }
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
44struct 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
101type LiveSet = HashSet<ir::Value>;
106
107#[derive(Default)]
109struct Worklist {
110 stack: Vec<u32>,
112
113 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 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 self.need_updates.remove(&block_index) {
173 return Some(block_index);
174 }
175 }
176 None
177 }
178}
179
180pub(crate) struct LivenessAnalysis {
239 dfs: Dfs,
241
242 post_order: Vec<ir::Block>,
244
245 block_to_index: SecondaryMap<ir::Block, u32>,
247
248 predecessors: Vec<SmallVec<[u32; 4]>>,
251
252 worklist: Worklist,
255
256 live_ins: Vec<LiveSet>,
258
259 live_outs: Vec<LiveSet>,
261
262 currently_live: LiveSet,
265
266 safepoints: HashMap<ir::Inst, SmallVec<[ir::Value; 4]>>,
269
270 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 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 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 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 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 live.sort();
388
389 self.live_across_any_safepoint.extend(live.iter().copied());
390 self.safepoints.insert(inst, live);
391 }
392
393 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 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 self.currently_live.clear();
417 self.currently_live
418 .extend(self.live_outs[block_index].iter().copied());
419
420 let mut option_inst = func.layout.last_inst(block);
423 while let Some(inst) = option_inst {
424 for val in func.dfg.inst_results(inst) {
426 self.process_def(*val);
427 }
428
429 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 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 for val in func.dfg.block_params(block) {
450 self.process_def(*val);
451 }
452 }
453
454 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 self.worklist
467 .extend((0..u32::try_from(self.post_order.len()).unwrap()).rev());
468
469 while let Some(block_index) = self.worklist.pop() {
471 let block_index = usize::try_from(block_index).unwrap();
472
473 let initial_live_in_len = self.live_ins[block_index].len();
476
477 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 self.process_block(func, stack_map_values, block_index, RecordSafepoints::No);
490
491 self.live_ins[block_index].extend(self.currently_live.iter().copied());
494
495 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 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#[derive(Default)]
525struct StackSlots {
526 stack_slots: HashMap<ir::Value, ir::StackSlot>,
533
534 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#[derive(Default)]
592pub(super) struct SafepointSpiller {
593 liveness: LivenessAnalysis,
594 stack_slots: StackSlots,
595}
596
597impl SafepointSpiller {
598 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 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 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 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 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 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 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 fn rewrite(&mut self, func: &mut Function) {
729 let mut vals: SmallVec<[_; 8]> = Default::default();
731
732 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 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 self.liveness.safepoints.contains_key(&inst) {
757 self.rewrite_safepoint(func, inst);
758 }
759
760 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 let mut pos = FuncCursor::new(func).at_position(CursorPosition::Before(block));
784 pos.next_inst(); 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ¶ms {
1550 builder.declare_value_needs_stack_map(*val);
1551 }
1552 builder.ins().call(func_ref, &[]);
1553 builder.ins().return_(¶ms);
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 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 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 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 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 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 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 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}