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 patchable: false,
824 });
825
826 let block0 = builder.create_block();
834 builder.append_block_params_for_function_params(block0);
835 let a = builder.func.dfg.block_params(block0)[0];
836 let b = builder.func.dfg.block_params(block0)[1];
837 builder.declare_value_needs_stack_map(a);
838 builder.declare_value_needs_stack_map(b);
839 builder.switch_to_block(block0);
840 builder.ins().call(func_ref, &[a]);
841 builder.ins().jump(block0, &[a.into(), b.into()]);
842 builder.seal_all_blocks();
843 builder.finalize();
844
845 assert_eq_output!(
846 func.display().to_string(),
847 r#"
848function %sample(i32, i32) system_v {
849 ss0 = explicit_slot 4, align = 4
850 ss1 = explicit_slot 4, align = 4
851 sig0 = (i32) system_v
852 fn0 = colocated u0:0 sig0
853
854block0(v0: i32, v1: i32):
855 stack_store v0, ss0
856 stack_store v1, ss1
857 v4 = stack_load.i32 ss0
858 call fn0(v4), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
859 v2 = stack_load.i32 ss0
860 v3 = stack_load.i32 ss1
861 jump block0(v2, v3)
862}
863 "#
864 );
865 }
866
867 #[test]
868 fn needs_stack_map_simple() {
869 let _ = env_logger::try_init();
870
871 let sig = Signature::new(CallConv::SystemV);
872
873 let mut fn_ctx = FunctionBuilderContext::new();
874 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
875 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
876
877 let name = builder
878 .func
879 .declare_imported_user_function(ir::UserExternalName {
880 namespace: 0,
881 index: 0,
882 });
883 let mut sig = Signature::new(CallConv::SystemV);
884 sig.params.push(AbiParam::new(ir::types::I32));
885 let signature = builder.func.import_signature(sig);
886 let func_ref = builder.import_function(ir::ExtFuncData {
887 name: ir::ExternalName::user(name),
888 signature,
889 colocated: true,
890 patchable: false,
891 });
892
893 let block0 = builder.create_block();
912 builder.append_block_params_for_function_params(block0);
913 builder.switch_to_block(block0);
914 let v0 = builder.ins().iconst(ir::types::I32, 0);
915 builder.declare_value_needs_stack_map(v0);
916 let v1 = builder.ins().iconst(ir::types::I32, 1);
917 builder.declare_value_needs_stack_map(v1);
918 let v2 = builder.ins().iconst(ir::types::I32, 2);
919 builder.declare_value_needs_stack_map(v2);
920 let v3 = builder.ins().iconst(ir::types::I32, 3);
921 builder.declare_value_needs_stack_map(v3);
922 builder.ins().call(func_ref, &[v3]);
923 builder.ins().call(func_ref, &[v0]);
924 builder.ins().call(func_ref, &[v1]);
925 builder.ins().call(func_ref, &[v2]);
926 builder.ins().return_(&[]);
927 builder.seal_all_blocks();
928 builder.finalize();
929
930 assert_eq_output!(
931 func.display().to_string(),
932 r#"
933function %sample() system_v {
934 ss0 = explicit_slot 4, align = 4
935 ss1 = explicit_slot 4, align = 4
936 ss2 = explicit_slot 4, align = 4
937 sig0 = (i32) system_v
938 fn0 = colocated u0:0 sig0
939
940block0:
941 v0 = iconst.i32 0
942 stack_store v0, ss2 ; v0 = 0
943 v1 = iconst.i32 1
944 stack_store v1, ss1 ; v1 = 1
945 v2 = iconst.i32 2
946 stack_store v2, ss0 ; v2 = 2
947 v3 = iconst.i32 3
948 call fn0(v3), stack_map=[i32 @ ss2+0, i32 @ ss1+0, i32 @ ss0+0] ; v3 = 3
949 v6 = stack_load.i32 ss2
950 call fn0(v6), stack_map=[i32 @ ss1+0, i32 @ ss0+0]
951 v5 = stack_load.i32 ss1
952 call fn0(v5), stack_map=[i32 @ ss0+0]
953 v4 = stack_load.i32 ss0
954 call fn0(v4)
955 return
956}
957 "#
958 );
959 }
960
961 #[test]
962 fn needs_stack_map_and_post_order_early_return() {
963 let mut sig = Signature::new(CallConv::SystemV);
964 sig.params.push(AbiParam::new(ir::types::I32));
965
966 let mut fn_ctx = FunctionBuilderContext::new();
967 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
968 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
969
970 let name = builder
971 .func
972 .declare_imported_user_function(ir::UserExternalName {
973 namespace: 0,
974 index: 0,
975 });
976 let signature = builder
977 .func
978 .import_signature(Signature::new(CallConv::SystemV));
979 let func_ref = builder.import_function(ir::ExtFuncData {
980 name: ir::ExternalName::user(name),
981 signature,
982 colocated: true,
983 patchable: false,
984 });
985
986 let block0 = builder.create_block();
1009 let block1 = builder.create_block();
1010 let block2 = builder.create_block();
1011 let block3 = builder.create_block();
1012 builder.append_block_params_for_function_params(block0);
1013
1014 builder.switch_to_block(block0);
1015 let v0 = builder.func.dfg.block_params(block0)[0];
1016 builder.ins().brif(v0, block1, &[], block2, &[]);
1017
1018 builder.switch_to_block(block1);
1019 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1020 builder.declare_value_needs_stack_map(v1);
1021 builder.ins().jump(block3, &[]);
1022
1023 builder.switch_to_block(block2);
1024 builder.ins().call(func_ref, &[]);
1025 builder.ins().return_(&[]);
1026
1027 builder.switch_to_block(block3);
1028 builder.ins().iadd_imm(v1, 0);
1033 builder.ins().return_(&[]);
1034
1035 builder.seal_all_blocks();
1036 builder.finalize();
1037
1038 assert_eq_output!(
1039 func.display().to_string(),
1040 r#"
1041function %sample(i32) system_v {
1042 sig0 = () system_v
1043 fn0 = colocated u0:0 sig0
1044
1045block0(v0: i32):
1046 brif v0, block1, block2
1047
1048block1:
1049 v1 = iconst.i64 0x1234_5678
1050 jump block3
1051
1052block2:
1053 call fn0()
1054 return
1055
1056block3:
1057 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1058 return
1059}
1060 "#
1061 );
1062 }
1063
1064 #[test]
1065 fn needs_stack_map_conditional_branches_and_liveness() {
1066 let mut sig = Signature::new(CallConv::SystemV);
1067 sig.params.push(AbiParam::new(ir::types::I32));
1068
1069 let mut fn_ctx = FunctionBuilderContext::new();
1070 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1071 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1072
1073 let name = builder
1074 .func
1075 .declare_imported_user_function(ir::UserExternalName {
1076 namespace: 0,
1077 index: 0,
1078 });
1079 let signature = builder
1080 .func
1081 .import_signature(Signature::new(CallConv::SystemV));
1082 let func_ref = builder.import_function(ir::ExtFuncData {
1083 name: ir::ExternalName::user(name),
1084 signature,
1085 colocated: true,
1086 patchable: false,
1087 });
1088
1089 let block0 = builder.create_block();
1104 let block1 = builder.create_block();
1105 let block2 = builder.create_block();
1106 builder.append_block_params_for_function_params(block0);
1107
1108 builder.switch_to_block(block0);
1109 let v0 = builder.func.dfg.block_params(block0)[0];
1110 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1111 builder.declare_value_needs_stack_map(v1);
1112 builder.ins().brif(v0, block1, &[], block2, &[]);
1113
1114 builder.switch_to_block(block1);
1115 builder.ins().call(func_ref, &[]);
1116 builder.ins().return_(&[]);
1117
1118 builder.switch_to_block(block2);
1119 builder.ins().iadd_imm(v1, 0);
1124 builder.ins().return_(&[]);
1125
1126 builder.seal_all_blocks();
1127 builder.finalize();
1128
1129 assert_eq_output!(
1130 func.display().to_string(),
1131 r#"
1132function %sample(i32) system_v {
1133 sig0 = () system_v
1134 fn0 = colocated u0:0 sig0
1135
1136block0(v0: i32):
1137 v1 = iconst.i64 0x1234_5678
1138 brif v0, block1, block2
1139
1140block1:
1141 call fn0()
1142 return
1143
1144block2:
1145 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1146 return
1147}
1148 "#
1149 );
1150
1151 func.clear();
1155 fn_ctx.clear();
1156
1157 let mut sig = Signature::new(CallConv::SystemV);
1158 sig.params.push(AbiParam::new(ir::types::I32));
1159
1160 func.signature = sig;
1161 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1162
1163 let name = builder
1164 .func
1165 .declare_imported_user_function(ir::UserExternalName {
1166 namespace: 0,
1167 index: 0,
1168 });
1169 let signature = builder
1170 .func
1171 .import_signature(Signature::new(CallConv::SystemV));
1172 let func_ref = builder.import_function(ir::ExtFuncData {
1173 name: ir::ExternalName::user(name),
1174 signature,
1175 colocated: true,
1176 patchable: false,
1177 });
1178
1179 let block0 = builder.create_block();
1180 let block1 = builder.create_block();
1181 let block2 = builder.create_block();
1182 builder.append_block_params_for_function_params(block0);
1183
1184 builder.switch_to_block(block0);
1185 let v0 = builder.func.dfg.block_params(block0)[0];
1186 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1187 builder.declare_value_needs_stack_map(v1);
1188 builder.ins().brif(v0, block1, &[], block2, &[]);
1189
1190 builder.switch_to_block(block1);
1191 builder.ins().iadd_imm(v1, 0);
1192 builder.ins().return_(&[]);
1193
1194 builder.switch_to_block(block2);
1195 builder.ins().call(func_ref, &[]);
1196 builder.ins().return_(&[]);
1197
1198 builder.seal_all_blocks();
1199 builder.finalize();
1200
1201 assert_eq_output!(
1202 func.display().to_string(),
1203 r#"
1204function u0:0(i32) system_v {
1205 sig0 = () system_v
1206 fn0 = colocated u0:0 sig0
1207
1208block0(v0: i32):
1209 v1 = iconst.i64 0x1234_5678
1210 brif v0, block1, block2
1211
1212block1:
1213 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1214 return
1215
1216block2:
1217 call fn0()
1218 return
1219}
1220 "#
1221 );
1222 }
1223
1224 #[test]
1225 fn needs_stack_map_and_tail_calls() {
1226 let mut sig = Signature::new(CallConv::SystemV);
1227 sig.params.push(AbiParam::new(ir::types::I32));
1228
1229 let mut fn_ctx = FunctionBuilderContext::new();
1230 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1231 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1232
1233 let name = builder
1234 .func
1235 .declare_imported_user_function(ir::UserExternalName {
1236 namespace: 0,
1237 index: 0,
1238 });
1239 let signature = builder
1240 .func
1241 .import_signature(Signature::new(CallConv::SystemV));
1242 let func_ref = builder.import_function(ir::ExtFuncData {
1243 name: ir::ExternalName::user(name),
1244 signature,
1245 colocated: true,
1246 patchable: false,
1247 });
1248
1249 let block0 = builder.create_block();
1264 let block1 = builder.create_block();
1265 let block2 = builder.create_block();
1266 builder.append_block_params_for_function_params(block0);
1267
1268 builder.switch_to_block(block0);
1269 let v0 = builder.func.dfg.block_params(block0)[0];
1270 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1271 builder.declare_value_needs_stack_map(v1);
1272 builder.ins().brif(v0, block1, &[], block2, &[]);
1273
1274 builder.switch_to_block(block1);
1275 builder.ins().return_call(func_ref, &[]);
1276
1277 builder.switch_to_block(block2);
1278 builder.ins().iadd_imm(v1, 0);
1283 builder.ins().return_(&[]);
1284
1285 builder.seal_all_blocks();
1286 builder.finalize();
1287
1288 assert_eq_output!(
1289 func.display().to_string(),
1290 r#"
1291function %sample(i32) system_v {
1292 sig0 = () system_v
1293 fn0 = colocated u0:0 sig0
1294
1295block0(v0: i32):
1296 v1 = iconst.i64 0x1234_5678
1297 brif v0, block1, block2
1298
1299block1:
1300 return_call fn0()
1301
1302block2:
1303 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1304 return
1305}
1306 "#
1307 );
1308
1309 func.clear();
1313 fn_ctx.clear();
1314
1315 let mut sig = Signature::new(CallConv::SystemV);
1316 sig.params.push(AbiParam::new(ir::types::I32));
1317 func.signature = sig;
1318
1319 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1320
1321 let name = builder
1322 .func
1323 .declare_imported_user_function(ir::UserExternalName {
1324 namespace: 0,
1325 index: 0,
1326 });
1327 let signature = builder
1328 .func
1329 .import_signature(Signature::new(CallConv::SystemV));
1330 let func_ref = builder.import_function(ir::ExtFuncData {
1331 name: ir::ExternalName::user(name),
1332 signature,
1333 colocated: true,
1334 patchable: false,
1335 });
1336
1337 let block0 = builder.create_block();
1338 let block1 = builder.create_block();
1339 let block2 = builder.create_block();
1340 builder.append_block_params_for_function_params(block0);
1341
1342 builder.switch_to_block(block0);
1343 let v0 = builder.func.dfg.block_params(block0)[0];
1344 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1345 builder.declare_value_needs_stack_map(v1);
1346 builder.ins().brif(v0, block1, &[], block2, &[]);
1347
1348 builder.switch_to_block(block1);
1349 builder.ins().iadd_imm(v1, 0);
1350 builder.ins().return_(&[]);
1351
1352 builder.switch_to_block(block2);
1353 builder.ins().return_call(func_ref, &[]);
1354
1355 builder.seal_all_blocks();
1356 builder.finalize();
1357
1358 assert_eq_output!(
1359 func.display().to_string(),
1360 r#"
1361function u0:0(i32) system_v {
1362 sig0 = () system_v
1363 fn0 = colocated u0:0 sig0
1364
1365block0(v0: i32):
1366 v1 = iconst.i64 0x1234_5678
1367 brif v0, block1, block2
1368
1369block1:
1370 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1371 return
1372
1373block2:
1374 return_call fn0()
1375}
1376 "#
1377 );
1378 }
1379
1380 #[test]
1381 fn needs_stack_map_and_cfg_diamond() {
1382 let mut sig = Signature::new(CallConv::SystemV);
1383 sig.params.push(AbiParam::new(ir::types::I32));
1384
1385 let mut fn_ctx = FunctionBuilderContext::new();
1386 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1387 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1388
1389 let name = builder
1390 .func
1391 .declare_imported_user_function(ir::UserExternalName {
1392 namespace: 0,
1393 index: 0,
1394 });
1395 let signature = builder
1396 .func
1397 .import_signature(Signature::new(CallConv::SystemV));
1398 let func_ref = builder.import_function(ir::ExtFuncData {
1399 name: ir::ExternalName::user(name),
1400 signature,
1401 colocated: true,
1402 patchable: false,
1403 });
1404
1405 let block0 = builder.create_block();
1427 let block1 = builder.create_block();
1428 let block2 = builder.create_block();
1429 let block3 = builder.create_block();
1430 builder.append_block_params_for_function_params(block0);
1431
1432 builder.switch_to_block(block0);
1433 let v0 = builder.func.dfg.block_params(block0)[0];
1434 builder.ins().brif(v0, block1, &[], block2, &[]);
1435
1436 builder.switch_to_block(block1);
1437 let v1 = builder.ins().iconst(ir::types::I64, 1);
1438 builder.declare_value_needs_stack_map(v1);
1439 let v2 = builder.ins().iconst(ir::types::I64, 2);
1440 builder.declare_value_needs_stack_map(v2);
1441 builder.ins().call(func_ref, &[]);
1442 builder.ins().jump(block3, &[v1.into(), v2.into()]);
1443
1444 builder.switch_to_block(block2);
1445 let v3 = builder.ins().iconst(ir::types::I64, 3);
1446 builder.declare_value_needs_stack_map(v3);
1447 let v4 = builder.ins().iconst(ir::types::I64, 4);
1448 builder.declare_value_needs_stack_map(v4);
1449 builder.ins().call(func_ref, &[]);
1450 builder.ins().jump(block3, &[v3.into(), v3.into()]);
1451
1452 builder.switch_to_block(block3);
1453 builder.append_block_param(block3, ir::types::I64);
1454 builder.append_block_param(block3, ir::types::I64);
1455 builder.ins().call(func_ref, &[]);
1456 builder.ins().iadd_imm(v1, 0);
1461 builder.ins().return_(&[]);
1462
1463 builder.seal_all_blocks();
1464 builder.finalize();
1465
1466 assert_eq_output!(
1467 func.display().to_string(),
1468 r#"
1469function %sample(i32) system_v {
1470 ss0 = explicit_slot 8, align = 8
1471 ss1 = explicit_slot 8, align = 8
1472 sig0 = () system_v
1473 fn0 = colocated u0:0 sig0
1474
1475block0(v0: i32):
1476 brif v0, block1, block2
1477
1478block1:
1479 v1 = iconst.i64 1
1480 stack_store v1, ss0 ; v1 = 1
1481 v2 = iconst.i64 2
1482 stack_store v2, ss1 ; v2 = 2
1483 call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss1+0]
1484 v9 = stack_load.i64 ss0
1485 v10 = stack_load.i64 ss1
1486 jump block3(v9, v10)
1487
1488block2:
1489 v3 = iconst.i64 3
1490 stack_store v3, ss0 ; v3 = 3
1491 v4 = iconst.i64 4
1492 call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss0+0]
1493 v11 = stack_load.i64 ss0
1494 v12 = stack_load.i64 ss0
1495 jump block3(v11, v12)
1496
1497block3(v5: i64, v6: i64):
1498 call fn0(), stack_map=[i64 @ ss0+0]
1499 v8 = stack_load.i64 ss0
1500 v7 = iadd_imm v8, 0
1501 return
1502}
1503 "#
1504 );
1505 }
1506
1507 #[test]
1508 fn needs_stack_map_and_heterogeneous_types() {
1509 let _ = env_logger::try_init();
1510
1511 let mut sig = Signature::new(CallConv::SystemV);
1512 for ty in [
1513 ir::types::I8,
1514 ir::types::I16,
1515 ir::types::I32,
1516 ir::types::I64,
1517 ir::types::I128,
1518 ir::types::F32,
1519 ir::types::F64,
1520 ir::types::I8X16,
1521 ir::types::I16X8,
1522 ] {
1523 sig.params.push(AbiParam::new(ty));
1524 sig.returns.push(AbiParam::new(ty));
1525 }
1526
1527 let mut fn_ctx = FunctionBuilderContext::new();
1528 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1529 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1530
1531 let name = builder
1532 .func
1533 .declare_imported_user_function(ir::UserExternalName {
1534 namespace: 0,
1535 index: 0,
1536 });
1537 let signature = builder
1538 .func
1539 .import_signature(Signature::new(CallConv::SystemV));
1540 let func_ref = builder.import_function(ir::ExtFuncData {
1541 name: ir::ExternalName::user(name),
1542 signature,
1543 colocated: true,
1544 patchable: false,
1545 });
1546
1547 let block0 = builder.create_block();
1554 builder.append_block_params_for_function_params(block0);
1555
1556 builder.switch_to_block(block0);
1557 let params = builder.func.dfg.block_params(block0).to_vec();
1558 for val in ¶ms {
1559 builder.declare_value_needs_stack_map(*val);
1560 }
1561 builder.ins().call(func_ref, &[]);
1562 builder.ins().return_(¶ms);
1563
1564 builder.seal_all_blocks();
1565 builder.finalize();
1566
1567 assert_eq_output!(
1568 func.display().to_string(),
1569 r#"
1570function %sample(i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8) -> i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8 system_v {
1571 ss0 = explicit_slot 1
1572 ss1 = explicit_slot 2, align = 2
1573 ss2 = explicit_slot 4, align = 4
1574 ss3 = explicit_slot 8, align = 8
1575 ss4 = explicit_slot 16, align = 16
1576 ss5 = explicit_slot 4, align = 4
1577 ss6 = explicit_slot 8, align = 8
1578 ss7 = explicit_slot 16, align = 16
1579 ss8 = explicit_slot 16, align = 16
1580 sig0 = () system_v
1581 fn0 = colocated u0:0 sig0
1582
1583block0(v0: i8, v1: i16, v2: i32, v3: i64, v4: i128, v5: f32, v6: f64, v7: i8x16, v8: i16x8):
1584 stack_store v0, ss0
1585 stack_store v1, ss1
1586 stack_store v2, ss2
1587 stack_store v3, ss3
1588 stack_store v4, ss4
1589 stack_store v5, ss5
1590 stack_store v6, ss6
1591 stack_store v7, ss7
1592 stack_store v8, ss8
1593 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]
1594 v9 = stack_load.i8 ss0
1595 v10 = stack_load.i16 ss1
1596 v11 = stack_load.i32 ss2
1597 v12 = stack_load.i64 ss3
1598 v13 = stack_load.i128 ss4
1599 v14 = stack_load.f32 ss5
1600 v15 = stack_load.f64 ss6
1601 v16 = stack_load.i8x16 ss7
1602 v17 = stack_load.i16x8 ss8
1603 return v9, v10, v11, v12, v13, v14, v15, v16, v17
1604}
1605 "#
1606 );
1607 }
1608
1609 #[test]
1610 fn series_of_non_overlapping_live_ranges_needs_stack_map() {
1611 let sig = Signature::new(CallConv::SystemV);
1612
1613 let mut fn_ctx = FunctionBuilderContext::new();
1614 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1615 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1616
1617 let name = builder
1618 .func
1619 .declare_imported_user_function(ir::UserExternalName {
1620 namespace: 0,
1621 index: 0,
1622 });
1623 let signature = builder
1624 .func
1625 .import_signature(Signature::new(CallConv::SystemV));
1626 let foo_func_ref = builder.import_function(ir::ExtFuncData {
1627 name: ir::ExternalName::user(name),
1628 signature,
1629 colocated: true,
1630 patchable: false,
1631 });
1632
1633 let name = builder
1634 .func
1635 .declare_imported_user_function(ir::UserExternalName {
1636 namespace: 0,
1637 index: 1,
1638 });
1639 let mut sig = Signature::new(CallConv::SystemV);
1640 sig.params.push(AbiParam::new(ir::types::I32));
1641 let signature = builder.func.import_signature(sig);
1642 let consume_func_ref = builder.import_function(ir::ExtFuncData {
1643 name: ir::ExternalName::user(name),
1644 signature,
1645 colocated: true,
1646 patchable: false,
1647 });
1648
1649 let block0 = builder.create_block();
1668 builder.append_block_params_for_function_params(block0);
1669 builder.switch_to_block(block0);
1670 let v0 = builder.ins().iconst(ir::types::I32, 0);
1671 builder.declare_value_needs_stack_map(v0);
1672 builder.ins().call(foo_func_ref, &[]);
1673 builder.ins().call(consume_func_ref, &[v0]);
1674 let v1 = builder.ins().iconst(ir::types::I32, 1);
1675 builder.declare_value_needs_stack_map(v1);
1676 builder.ins().call(foo_func_ref, &[]);
1677 builder.ins().call(consume_func_ref, &[v1]);
1678 let v2 = builder.ins().iconst(ir::types::I32, 2);
1679 builder.declare_value_needs_stack_map(v2);
1680 builder.ins().call(foo_func_ref, &[]);
1681 builder.ins().call(consume_func_ref, &[v2]);
1682 let v3 = builder.ins().iconst(ir::types::I32, 3);
1683 builder.declare_value_needs_stack_map(v3);
1684 builder.ins().call(foo_func_ref, &[]);
1685 builder.ins().call(consume_func_ref, &[v3]);
1686 builder.ins().return_(&[]);
1687 builder.seal_all_blocks();
1688 builder.finalize();
1689
1690 assert_eq_output!(
1691 func.display().to_string(),
1692 r#"
1693function %sample() system_v {
1694 ss0 = explicit_slot 4, align = 4
1695 sig0 = () system_v
1696 sig1 = (i32) system_v
1697 fn0 = colocated u0:0 sig0
1698 fn1 = colocated u0:1 sig1
1699
1700block0:
1701 v0 = iconst.i32 0
1702 stack_store v0, ss0 ; v0 = 0
1703 call fn0(), stack_map=[i32 @ ss0+0]
1704 v7 = stack_load.i32 ss0
1705 call fn1(v7)
1706 v1 = iconst.i32 1
1707 stack_store v1, ss0 ; v1 = 1
1708 call fn0(), stack_map=[i32 @ ss0+0]
1709 v6 = stack_load.i32 ss0
1710 call fn1(v6)
1711 v2 = iconst.i32 2
1712 stack_store v2, ss0 ; v2 = 2
1713 call fn0(), stack_map=[i32 @ ss0+0]
1714 v5 = stack_load.i32 ss0
1715 call fn1(v5)
1716 v3 = iconst.i32 3
1717 stack_store v3, ss0 ; v3 = 3
1718 call fn0(), stack_map=[i32 @ ss0+0]
1719 v4 = stack_load.i32 ss0
1720 call fn1(v4)
1721 return
1722}
1723 "#
1724 );
1725 }
1726
1727 #[test]
1728 fn vars_block_params_and_needs_stack_map() {
1729 let _ = env_logger::try_init();
1730
1731 let mut sig = Signature::new(CallConv::SystemV);
1732 sig.params.push(AbiParam::new(ir::types::I32));
1733 sig.returns.push(AbiParam::new(ir::types::I32));
1734
1735 let mut fn_ctx = FunctionBuilderContext::new();
1736 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1737 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1738
1739 let name = builder
1740 .func
1741 .declare_imported_user_function(ir::UserExternalName {
1742 namespace: 0,
1743 index: 0,
1744 });
1745 let mut sig = Signature::new(CallConv::SystemV);
1746 sig.params.push(AbiParam::new(ir::types::I32));
1747 let signature = builder.func.import_signature(sig);
1748 let func_ref = builder.import_function(ir::ExtFuncData {
1749 name: ir::ExternalName::user(name),
1750 signature,
1751 colocated: true,
1752 patchable: false,
1753 });
1754
1755 let x = builder.declare_var(ir::types::I32);
1780 builder.declare_var_needs_stack_map(x);
1781
1782 let block0 = builder.create_block();
1783 let block1 = builder.create_block();
1784 let block2 = builder.create_block();
1785 let block3 = builder.create_block();
1786
1787 builder.append_block_params_for_function_params(block0);
1788 builder.switch_to_block(block0);
1789 let v0 = builder.func.dfg.block_params(block0)[0];
1790 let val = builder.ins().iconst(ir::types::I32, 42);
1791 builder.def_var(x, val);
1792 {
1793 let x = builder.use_var(x);
1794 builder.ins().call(func_ref, &[x]);
1795 }
1796 builder.ins().brif(v0, block1, &[], block2, &[]);
1797
1798 builder.switch_to_block(block1);
1799 {
1800 let x = builder.use_var(x);
1801 builder.ins().call(func_ref, &[x]);
1802 builder.ins().call(func_ref, &[x]);
1803 }
1804 let val = builder.ins().iconst(ir::types::I32, 36);
1805 builder.def_var(x, val);
1806 {
1807 let x = builder.use_var(x);
1808 builder.ins().call(func_ref, &[x]);
1809 }
1810 builder.ins().jump(block3, &[]);
1811
1812 builder.switch_to_block(block2);
1813 {
1814 let x = builder.use_var(x);
1815 builder.ins().call(func_ref, &[x]);
1816 builder.ins().call(func_ref, &[x]);
1817 }
1818 let val = builder.ins().iconst(ir::types::I32, 36);
1819 builder.def_var(x, val);
1820 {
1821 let x = builder.use_var(x);
1822 builder.ins().call(func_ref, &[x]);
1823 }
1824 builder.ins().jump(block3, &[]);
1825
1826 builder.switch_to_block(block3);
1827 let x = builder.use_var(x);
1828 builder.ins().call(func_ref, &[x]);
1829 builder.ins().return_(&[x]);
1830
1831 builder.seal_all_blocks();
1832 builder.finalize();
1833
1834 assert_eq_output!(
1835 func.display().to_string(),
1836 r#"
1837function %sample(i32) -> i32 system_v {
1838 ss0 = explicit_slot 4, align = 4
1839 ss1 = explicit_slot 4, align = 4
1840 sig0 = (i32) system_v
1841 fn0 = colocated u0:0 sig0
1842
1843block0(v0: i32):
1844 v1 = iconst.i32 42
1845 v2 -> v1
1846 v4 -> v1
1847 stack_store v1, ss0 ; v1 = 42
1848 v13 = stack_load.i32 ss0
1849 call fn0(v13), stack_map=[i32 @ ss0+0]
1850 brif v0, block1, block2
1851
1852block1:
1853 call fn0(v2), stack_map=[i32 @ ss0+0] ; v2 = 42
1854 call fn0(v2) ; v2 = 42
1855 v3 = iconst.i32 36
1856 stack_store v3, ss0 ; v3 = 36
1857 v10 = stack_load.i32 ss0
1858 call fn0(v10), stack_map=[i32 @ ss0+0]
1859 v9 = stack_load.i32 ss0
1860 jump block3(v9)
1861
1862block2:
1863 call fn0(v4), stack_map=[i32 @ ss0+0] ; v4 = 42
1864 call fn0(v4) ; v4 = 42
1865 v5 = iconst.i32 36
1866 stack_store v5, ss1 ; v5 = 36
1867 v12 = stack_load.i32 ss1
1868 call fn0(v12), stack_map=[i32 @ ss1+0]
1869 v11 = stack_load.i32 ss1
1870 jump block3(v11)
1871
1872block3(v6: i32):
1873 stack_store v6, ss0
1874 v8 = stack_load.i32 ss0
1875 call fn0(v8), stack_map=[i32 @ ss0+0]
1876 v7 = stack_load.i32 ss0
1877 return v7
1878}
1879 "#
1880 );
1881 }
1882
1883 #[test]
1884 fn var_needs_stack_map() {
1885 let mut sig = Signature::new(CallConv::SystemV);
1886 sig.params
1887 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1888 sig.returns
1889 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1890
1891 let mut fn_ctx = FunctionBuilderContext::new();
1892 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1893 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1894
1895 let var = builder.declare_var(cranelift_codegen::ir::types::I32);
1896 builder.declare_var_needs_stack_map(var);
1897
1898 let name = builder
1899 .func
1900 .declare_imported_user_function(ir::UserExternalName {
1901 namespace: 0,
1902 index: 0,
1903 });
1904 let signature = builder
1905 .func
1906 .import_signature(Signature::new(CallConv::SystemV));
1907 let func_ref = builder.import_function(ir::ExtFuncData {
1908 name: ir::ExternalName::user(name),
1909 signature,
1910 colocated: true,
1911 patchable: false,
1912 });
1913
1914 let block0 = builder.create_block();
1915 builder.append_block_params_for_function_params(block0);
1916 builder.switch_to_block(block0);
1917
1918 let arg = builder.func.dfg.block_params(block0)[0];
1919 builder.def_var(var, arg);
1920
1921 builder.ins().call(func_ref, &[]);
1922
1923 let val = builder.use_var(var);
1924 builder.ins().return_(&[val]);
1925
1926 builder.seal_all_blocks();
1927 builder.finalize();
1928
1929 assert_eq_output!(
1930 func.display().to_string(),
1931 r#"
1932function %sample(i32) -> i32 system_v {
1933 ss0 = explicit_slot 4, align = 4
1934 sig0 = () system_v
1935 fn0 = colocated u0:0 sig0
1936
1937block0(v0: i32):
1938 stack_store v0, ss0
1939 call fn0(), stack_map=[i32 @ ss0+0]
1940 v1 = stack_load.i32 ss0
1941 return v1
1942}
1943 "#
1944 );
1945 }
1946
1947 #[test]
1948 fn first_inst_defines_needs_stack_map() {
1949 let mut sig = Signature::new(CallConv::SystemV);
1950 sig.params
1951 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1952 sig.returns
1953 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1954 sig.returns
1955 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1956
1957 let mut fn_ctx = FunctionBuilderContext::new();
1958 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1959 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1960
1961 let name = builder
1962 .func
1963 .declare_imported_user_function(ir::UserExternalName {
1964 namespace: 0,
1965 index: 0,
1966 });
1967 let signature = builder
1968 .func
1969 .import_signature(Signature::new(CallConv::SystemV));
1970 let func_ref = builder.import_function(ir::ExtFuncData {
1971 name: ir::ExternalName::user(name),
1972 signature,
1973 colocated: true,
1974 patchable: false,
1975 });
1976
1977 let block0 = builder.create_block();
1989 builder.append_block_params_for_function_params(block0);
1990 builder.switch_to_block(block0);
1991
1992 let arg = builder.func.dfg.block_params(block0)[0];
1993 builder.declare_value_needs_stack_map(arg);
1994
1995 let val = builder.ins().iconst(ir::types::I32, 42);
1996 builder.declare_value_needs_stack_map(val);
1997
1998 builder.ins().call(func_ref, &[]);
1999
2000 builder.ins().return_(&[arg, val]);
2001
2002 builder.seal_all_blocks();
2003 builder.finalize();
2004
2005 assert_eq_output!(
2006 func.display().to_string(),
2007 r#"
2008function %sample(i32) -> i32, i32 system_v {
2009 ss0 = explicit_slot 4, align = 4
2010 ss1 = explicit_slot 4, align = 4
2011 sig0 = () system_v
2012 fn0 = colocated u0:0 sig0
2013
2014block0(v0: i32):
2015 stack_store v0, ss0
2016 v1 = iconst.i32 42
2017 stack_store v1, ss1 ; v1 = 42
2018 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
2019 v2 = stack_load.i32 ss0
2020 v3 = stack_load.i32 ss1
2021 return v2, v3
2022}
2023 "#
2024 );
2025 }
2026
2027 #[test]
2028 fn needs_stack_map_and_loops_and_partially_live_values() {
2029 let _ = env_logger::try_init();
2030
2031 let mut sig = Signature::new(CallConv::SystemV);
2032 sig.params.push(AbiParam::new(ir::types::I32));
2033
2034 let mut fn_ctx = FunctionBuilderContext::new();
2035 let mut func =
2036 Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig.clone());
2037 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2038
2039 let name = builder
2040 .func
2041 .declare_imported_user_function(ir::UserExternalName {
2042 namespace: 0,
2043 index: 0,
2044 });
2045 let signature = builder
2046 .func
2047 .import_signature(Signature::new(CallConv::SystemV));
2048 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2049 name: ir::ExternalName::user(name),
2050 signature,
2051 colocated: true,
2052 patchable: false,
2053 });
2054
2055 let name = builder
2056 .func
2057 .declare_imported_user_function(ir::UserExternalName {
2058 namespace: 1,
2059 index: 1,
2060 });
2061 let signature = builder.func.import_signature(sig);
2062 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2063 name: ir::ExternalName::user(name),
2064 signature,
2065 colocated: true,
2066 patchable: false,
2067 });
2068
2069 let block0 = builder.create_block();
2086 let block1 = builder.create_block();
2087 builder.append_block_params_for_function_params(block0);
2088
2089 builder.switch_to_block(block0);
2090 builder.ins().jump(block1, &[]);
2091
2092 builder.switch_to_block(block1);
2093 let v0 = builder.func.dfg.block_params(block0)[0];
2094 builder.declare_value_needs_stack_map(v0);
2095 builder.ins().call(foo_func_ref, &[]);
2096 builder.ins().call(bar_func_ref, &[v0]);
2097 builder.ins().call(foo_func_ref, &[]);
2098 builder.ins().jump(block1, &[]);
2099
2100 builder.seal_all_blocks();
2101 builder.finalize();
2102
2103 assert_eq_output!(
2104 func.display().to_string(),
2105 r#"
2106function %sample(i32) system_v {
2107 ss0 = explicit_slot 4, align = 4
2108 sig0 = () system_v
2109 sig1 = (i32) system_v
2110 fn0 = colocated u0:0 sig0
2111 fn1 = colocated u1:1 sig1
2112
2113block0(v0: i32):
2114 stack_store v0, ss0
2115 jump block1
2116
2117block1:
2118 call fn0(), stack_map=[i32 @ ss0+0]
2119 v1 = stack_load.i32 ss0
2120 call fn1(v1), stack_map=[i32 @ ss0+0]
2121 call fn0(), stack_map=[i32 @ ss0+0]
2122 jump block1
2123}
2124 "#,
2125 );
2126 }
2127
2128 #[test]
2129 fn needs_stack_map_and_irreducible_loops() {
2130 let _ = env_logger::try_init();
2131
2132 let mut sig = Signature::new(CallConv::SystemV);
2133 sig.params.push(AbiParam::new(ir::types::I32));
2134 sig.params.push(AbiParam::new(ir::types::I32));
2135
2136 let mut fn_ctx = FunctionBuilderContext::new();
2137 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2138 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2139
2140 let name = builder
2141 .func
2142 .declare_imported_user_function(ir::UserExternalName {
2143 namespace: 0,
2144 index: 0,
2145 });
2146 let signature = builder
2147 .func
2148 .import_signature(Signature::new(CallConv::SystemV));
2149 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2150 name: ir::ExternalName::user(name),
2151 signature,
2152 colocated: true,
2153 patchable: false,
2154 });
2155
2156 let name = builder
2157 .func
2158 .declare_imported_user_function(ir::UserExternalName {
2159 namespace: 1,
2160 index: 1,
2161 });
2162 let mut sig = Signature::new(CallConv::SystemV);
2163 sig.params.push(AbiParam::new(ir::types::I32));
2164 let signature = builder.func.import_signature(sig);
2165 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2166 name: ir::ExternalName::user(name),
2167 signature,
2168 colocated: true,
2169 patchable: false,
2170 });
2171
2172 let block0 = builder.create_block();
2196 let block1 = builder.create_block();
2197 let block2 = builder.create_block();
2198 let block3 = builder.create_block();
2199 let block4 = builder.create_block();
2200 builder.append_block_params_for_function_params(block0);
2201
2202 builder.switch_to_block(block0);
2203 let v0 = builder.func.dfg.block_params(block0)[0];
2204 let v1 = builder.func.dfg.block_params(block0)[1];
2205 builder.declare_value_needs_stack_map(v1);
2206 builder.ins().brif(v0, block1, &[], block2, &[]);
2207
2208 builder.switch_to_block(block1);
2209 builder.ins().jump(block3, &[]);
2210
2211 builder.switch_to_block(block2);
2212 builder.ins().jump(block4, &[]);
2213
2214 builder.switch_to_block(block3);
2215 builder.ins().call(foo_func_ref, &[]);
2216 builder.ins().call(bar_func_ref, &[v1]);
2217 builder.ins().call(foo_func_ref, &[]);
2218 builder.ins().jump(block2, &[]);
2219
2220 builder.switch_to_block(block4);
2221 builder.ins().call(foo_func_ref, &[]);
2222 builder.ins().call(bar_func_ref, &[v1]);
2223 builder.ins().call(foo_func_ref, &[]);
2224 builder.ins().jump(block1, &[]);
2225
2226 builder.seal_all_blocks();
2227 builder.finalize();
2228
2229 assert_eq_output!(
2230 func.display().to_string(),
2231 r#"
2232function %sample(i32, i32) system_v {
2233 ss0 = explicit_slot 4, align = 4
2234 sig0 = () system_v
2235 sig1 = (i32) system_v
2236 fn0 = colocated u0:0 sig0
2237 fn1 = colocated u1:1 sig1
2238
2239block0(v0: i32, v1: i32):
2240 stack_store v1, ss0
2241 brif v0, block1, block2
2242
2243block1:
2244 jump block3
2245
2246block2:
2247 jump block4
2248
2249block3:
2250 call fn0(), stack_map=[i32 @ ss0+0]
2251 v3 = stack_load.i32 ss0
2252 call fn1(v3), stack_map=[i32 @ ss0+0]
2253 call fn0(), stack_map=[i32 @ ss0+0]
2254 jump block2
2255
2256block4:
2257 call fn0(), stack_map=[i32 @ ss0+0]
2258 v2 = stack_load.i32 ss0
2259 call fn1(v2), stack_map=[i32 @ ss0+0]
2260 call fn0(), stack_map=[i32 @ ss0+0]
2261 jump block1
2262}
2263 "#,
2264 );
2265 }
2266
2267 #[test]
2268 fn needs_stack_map_and_back_edge_to_back_edge() {
2269 let _ = env_logger::try_init();
2270
2271 let mut sig = Signature::new(CallConv::SystemV);
2272 sig.params.push(AbiParam::new(ir::types::I32));
2273 sig.params.push(AbiParam::new(ir::types::I32));
2274 sig.params.push(AbiParam::new(ir::types::I32));
2275 sig.params.push(AbiParam::new(ir::types::I32));
2276
2277 let mut fn_ctx = FunctionBuilderContext::new();
2278 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2279 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2280
2281 let name = builder
2282 .func
2283 .declare_imported_user_function(ir::UserExternalName {
2284 namespace: 0,
2285 index: 0,
2286 });
2287 let signature = builder
2288 .func
2289 .import_signature(Signature::new(CallConv::SystemV));
2290 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2291 name: ir::ExternalName::user(name),
2292 signature,
2293 colocated: true,
2294 patchable: false,
2295 });
2296
2297 let name = builder
2298 .func
2299 .declare_imported_user_function(ir::UserExternalName {
2300 namespace: 1,
2301 index: 1,
2302 });
2303 let mut sig = Signature::new(CallConv::SystemV);
2304 sig.params.push(AbiParam::new(ir::types::I32));
2305 let signature = builder.func.import_signature(sig);
2306 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2307 name: ir::ExternalName::user(name),
2308 signature,
2309 colocated: true,
2310 patchable: false,
2311 });
2312
2313 let block0 = builder.create_block();
2340 let block1 = builder.create_block();
2341 let block2 = builder.create_block();
2342 let block3 = builder.create_block();
2343
2344 builder.append_block_params_for_function_params(block0);
2345
2346 builder.switch_to_block(block0);
2347
2348 let v0 = builder.func.dfg.block_params(block0)[0];
2349 builder.declare_value_needs_stack_map(v0);
2350 let v1 = builder.func.dfg.block_params(block0)[1];
2351 builder.declare_value_needs_stack_map(v1);
2352 let v2 = builder.func.dfg.block_params(block0)[2];
2353 builder.declare_value_needs_stack_map(v2);
2354 let v3 = builder.func.dfg.block_params(block0)[3];
2355
2356 builder.ins().jump(block1, &[v3.into()]);
2357
2358 builder.switch_to_block(block1);
2359 let v4 = builder.append_block_param(block1, ir::types::I32);
2360 builder.ins().call(foo_func_ref, &[]);
2361 builder.ins().call(bar_func_ref, &[v0]);
2362 builder.ins().call(foo_func_ref, &[]);
2363 builder.ins().jump(block2, &[]);
2364
2365 builder.switch_to_block(block2);
2366 builder.ins().call(foo_func_ref, &[]);
2367 builder.ins().call(bar_func_ref, &[v1]);
2368 builder.ins().call(foo_func_ref, &[]);
2369 let v5 = builder.ins().iadd_imm(v4, -1);
2370 builder.ins().brif(v4, block1, &[v5.into()], block3, &[]);
2371
2372 builder.switch_to_block(block3);
2373 builder.ins().call(foo_func_ref, &[]);
2374 builder.ins().call(bar_func_ref, &[v2]);
2375 builder.ins().call(foo_func_ref, &[]);
2376 builder.ins().jump(block2, &[]);
2377
2378 builder.seal_all_blocks();
2379 builder.finalize();
2380
2381 assert_eq_output!(
2382 func.display().to_string(),
2383 r#"
2384function %sample(i32, i32, i32, i32) system_v {
2385 ss0 = explicit_slot 4, align = 4
2386 ss1 = explicit_slot 4, align = 4
2387 ss2 = explicit_slot 4, align = 4
2388 sig0 = () system_v
2389 sig1 = (i32) system_v
2390 fn0 = colocated u0:0 sig0
2391 fn1 = colocated u1:1 sig1
2392
2393block0(v0: i32, v1: i32, v2: i32, v3: i32):
2394 stack_store v0, ss0
2395 stack_store v1, ss1
2396 stack_store v2, ss2
2397 jump block1(v3)
2398
2399block1(v4: i32):
2400 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2401 v8 = stack_load.i32 ss0
2402 call fn1(v8), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2403 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2404 jump block2
2405
2406block2:
2407 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2408 v7 = stack_load.i32 ss1
2409 call fn1(v7), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2410 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2411 v5 = iadd_imm.i32 v4, -1
2412 brif.i32 v4, block1(v5), block3
2413
2414block3:
2415 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2416 v6 = stack_load.i32 ss2
2417 call fn1(v6), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2418 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2419 jump block2
2420}
2421 "#,
2422 );
2423 }
2424
2425 fn import_func(
2426 builder: &mut FunctionBuilder,
2427 params: impl IntoIterator<Item = ir::Type>,
2428 results: impl IntoIterator<Item = ir::Type>,
2429 ) -> ir::FuncRef {
2430 let index = u32::try_from(builder.func.dfg.ext_funcs.len()).unwrap();
2431
2432 let name = builder
2433 .func
2434 .declare_imported_user_function(ir::UserExternalName {
2435 namespace: 0,
2436 index,
2437 });
2438 let name = ir::ExternalName::user(name);
2439
2440 let mut signature = Signature::new(CallConv::SystemV);
2441 signature
2442 .params
2443 .extend(params.into_iter().map(|ty| AbiParam::new(ty)));
2444 signature
2445 .returns
2446 .extend(results.into_iter().map(|ty| AbiParam::new(ty)));
2447 let signature = builder.func.import_signature(signature);
2448
2449 builder.import_function(ir::ExtFuncData {
2450 name,
2451 signature,
2452 colocated: true,
2453 patchable: false,
2454 })
2455 }
2456
2457 #[test]
2458 fn issue_10397_stack_map_vars_and_indirect_block_params() {
2459 let _ = env_logger::try_init();
2460
2461 let mut sig = Signature::new(CallConv::SystemV);
2462 if false {
2463 sig.params.push(AbiParam::new(ir::types::I32));
2464 }
2465
2466 let mut fn_ctx = FunctionBuilderContext::new();
2467 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("f"), sig);
2468 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2469
2470 let alloc_struct = import_func(&mut builder, None, Some(ir::types::I32));
2471 let alloc_array = import_func(&mut builder, None, Some(ir::types::I32));
2472 let array_init_elem = import_func(&mut builder, Some(ir::types::I32), None);
2473 let type_of = import_func(&mut builder, Some(ir::types::I32), Some(ir::types::I32));
2474 let ref_test = import_func(
2475 &mut builder,
2476 vec![ir::types::I32, ir::types::I32],
2477 Some(ir::types::I32),
2478 );
2479 let access_array = import_func(&mut builder, Some(ir::types::I32), None);
2480 let should_continue_inner_loop = import_func(&mut builder, None, Some(ir::types::I32));
2481 let access_struct = import_func(&mut builder, Some(ir::types::I32), None);
2482 let should_return = import_func(&mut builder, None, Some(ir::types::I32));
2483
2484 let var_struct = builder.declare_var(cranelift_codegen::ir::types::I32);
2560 builder.declare_var_needs_stack_map(var_struct);
2561
2562 let var_array = builder.declare_var(cranelift_codegen::ir::types::I32);
2563 builder.declare_var_needs_stack_map(var_array);
2564
2565 let block_entry = builder.create_block();
2566 let block_outer_loop_head = builder.create_block();
2567 let block_array_init_loop_head = builder.create_block();
2568 let block_array_init_loop_body = builder.create_block();
2569 let block_array_init_loop_done = builder.create_block();
2570 let block_inner_loop_head = builder.create_block();
2571 let block_ref_test_non_null = builder.create_block();
2572 let block_ref_test_slow = builder.create_block();
2573 let block_ref_test_done = builder.create_block();
2574 let block_after_inner_loop = builder.create_block();
2575 let block_return = builder.create_block();
2576
2577 builder.append_block_params_for_function_params(block_entry);
2578 builder.switch_to_block(block_entry);
2579 builder.seal_block(block_entry);
2580 let call_inst = builder.ins().call(alloc_struct, &[]);
2581 let v0 = builder.func.dfg.first_result(call_inst);
2582 builder.def_var(var_struct, v0);
2583 builder.ins().jump(block_outer_loop_head, &[]);
2584
2585 builder.switch_to_block(block_outer_loop_head);
2586 let call_inst = builder.ins().call(alloc_array, &[]);
2587 let v1 = builder.func.dfg.first_result(call_inst);
2588 builder.def_var(var_array, v1);
2589 let v2 = builder.ins().iconst(ir::types::I32, 0);
2590 builder.ins().jump(block_array_init_loop_head, &[v2.into()]);
2591
2592 builder.switch_to_block(block_array_init_loop_head);
2593 let v3 = builder.append_block_param(block_array_init_loop_head, ir::types::I32);
2594 let v4 = builder.ins().iconst(ir::types::I32, 1);
2595 let v5 = builder
2596 .ins()
2597 .icmp(ir::condcodes::IntCC::UnsignedLessThan, v3, v4);
2598 builder.ins().brif(
2599 v5,
2600 block_array_init_loop_body,
2601 &[],
2602 block_array_init_loop_done,
2603 &[],
2604 );
2605
2606 builder.switch_to_block(block_array_init_loop_body);
2607 builder.seal_block(block_array_init_loop_body);
2608 builder.ins().call(array_init_elem, &[v1, v4]);
2609 let v6 = builder.ins().iconst(ir::types::I32, 1);
2610 let v7 = builder.ins().iadd(v4, v6);
2611 builder.ins().jump(block_array_init_loop_head, &[v7.into()]);
2612 builder.seal_block(block_array_init_loop_head);
2613
2614 builder.switch_to_block(block_array_init_loop_done);
2615 builder.seal_block(block_array_init_loop_done);
2616 builder.ins().jump(block_inner_loop_head, &[]);
2617
2618 builder.switch_to_block(block_inner_loop_head);
2619 let v8 = builder.use_var(var_array);
2620 let v9 = builder.ins().iconst(ir::types::I32, 0);
2621 let v10 = builder.ins().icmp(ir::condcodes::IntCC::Equal, v8, v9);
2622 builder.ins().brif(
2623 v10,
2624 block_ref_test_done,
2625 &[v9.into()],
2626 block_ref_test_non_null,
2627 &[],
2628 );
2629
2630 builder.switch_to_block(block_ref_test_non_null);
2631 builder.seal_block(block_ref_test_non_null);
2632 let call_inst = builder.ins().call(type_of, &[v8]);
2633 let v11 = builder.func.dfg.first_result(call_inst);
2634 let v12 = builder.ins().iconst(ir::types::I32, 0xbeefbeef);
2635 let v13 = builder.ins().icmp(ir::condcodes::IntCC::Equal, v11, v12);
2636 let v14 = builder.ins().iconst(ir::types::I32, 1);
2637 builder.ins().brif(
2638 v13,
2639 block_ref_test_done,
2640 &[v14.into()],
2641 block_ref_test_slow,
2642 &[],
2643 );
2644
2645 builder.switch_to_block(block_ref_test_slow);
2646 builder.seal_block(block_ref_test_slow);
2647 let call_inst = builder.ins().call(ref_test, &[v8, v12]);
2648 let v15 = builder.func.dfg.first_result(call_inst);
2649 builder.ins().jump(block_ref_test_done, &[v15.into()]);
2650
2651 builder.switch_to_block(block_ref_test_done);
2652 let v16 = builder.append_block_param(block_ref_test_done, ir::types::I32);
2653 builder.seal_block(block_ref_test_done);
2654 builder.ins().trapz(v16, ir::TrapCode::user(1).unwrap());
2655 builder.def_var(var_array, v8);
2656 builder.ins().call(access_array, &[v8]);
2657 let call_inst = builder.ins().call(should_continue_inner_loop, &[]);
2658 let v17 = builder.func.dfg.first_result(call_inst);
2659 builder
2660 .ins()
2661 .brif(v17, block_inner_loop_head, &[], block_after_inner_loop, &[]);
2662 builder.seal_block(block_inner_loop_head);
2663
2664 builder.switch_to_block(block_after_inner_loop);
2665 builder.seal_block(block_after_inner_loop);
2666 let v18 = builder.use_var(var_struct);
2667 builder.ins().call(access_struct, &[v18]);
2668 let call_inst = builder.ins().call(should_return, &[]);
2669 let v19 = builder.func.dfg.first_result(call_inst);
2670 builder
2671 .ins()
2672 .brif(v19, block_return, &[], block_outer_loop_head, &[]);
2673 builder.seal_block(block_outer_loop_head);
2674
2675 builder.switch_to_block(block_return);
2676 builder.seal_block(block_return);
2677 builder.ins().return_(&[]);
2678
2679 builder.finalize();
2680 assert_eq_output!(
2681 func.display().to_string(),
2682 r#"
2683function %f() system_v {
2684 ss0 = explicit_slot 4, align = 4
2685 ss1 = explicit_slot 4, align = 4
2686 ss2 = explicit_slot 4, align = 4
2687 sig0 = () -> i32 system_v
2688 sig1 = () -> i32 system_v
2689 sig2 = (i32) system_v
2690 sig3 = (i32) -> i32 system_v
2691 sig4 = (i32, i32) -> i32 system_v
2692 sig5 = (i32) system_v
2693 sig6 = () -> i32 system_v
2694 sig7 = (i32) system_v
2695 sig8 = () -> i32 system_v
2696 fn0 = colocated u0:0 sig0
2697 fn1 = colocated u0:1 sig1
2698 fn2 = colocated u0:2 sig2
2699 fn3 = colocated u0:3 sig3
2700 fn4 = colocated u0:4 sig4
2701 fn5 = colocated u0:5 sig5
2702 fn6 = colocated u0:6 sig6
2703 fn7 = colocated u0:7 sig7
2704 fn8 = colocated u0:8 sig8
2705
2706block0:
2707 v0 = call fn0()
2708 jump block1(v0)
2709
2710block1(v22: i32):
2711 v21 -> v22
2712 stack_store v22, ss1
2713 v1 = call fn1(), stack_map=[i32 @ ss1+0]
2714 v8 -> v1
2715 v18 -> v1
2716 stack_store v1, ss0
2717 v2 = iconst.i32 0
2718 jump block2(v2) ; v2 = 0
2719
2720block2(v3: i32):
2721 v4 = iconst.i32 1
2722 v5 = icmp ult v3, v4 ; v4 = 1
2723 brif v5, block3, block4
2724
2725block3:
2726 v24 = stack_load.i32 ss0
2727 call fn2(v24, v4), stack_map=[i32 @ ss0+0, i32 @ ss1+0] ; v4 = 1
2728 v6 = iconst.i32 1
2729 v7 = iadd.i32 v4, v6 ; v4 = 1, v6 = 1
2730 jump block2(v7)
2731
2732block4:
2733 v26 = stack_load.i32 ss1
2734 jump block5(v26)
2735
2736block5(v20: i32):
2737 v19 -> v20
2738 stack_store v20, ss2
2739 v9 = iconst.i32 0
2740 v10 = icmp.i32 eq v8, v9 ; v9 = 0
2741 brif v10, block8(v9), block6 ; v9 = 0
2742
2743block6:
2744 v11 = call fn3(v8), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2745 v12 = iconst.i32 -1091584273
2746 v13 = icmp eq v11, v12 ; v12 = -1091584273
2747 v14 = iconst.i32 1
2748 brif v13, block8(v14), block7 ; v14 = 1
2749
2750block7:
2751 v15 = call fn4(v8, v12), stack_map=[i32 @ ss0+0, i32 @ ss2+0] ; v12 = -1091584273
2752 jump block8(v15)
2753
2754block8(v16: i32):
2755 trapz v16, user1
2756 call fn5(v8), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2757 v17 = call fn6(), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2758 brif v17, block5(v19), block9
2759
2760block9:
2761 v25 = stack_load.i32 ss2
2762 call fn7(v25), stack_map=[i32 @ ss2+0]
2763 v23 = call fn8(), stack_map=[i32 @ ss2+0]
2764 brif v23, block10, block1(v19)
2765
2766block10:
2767 return
2768}
2769 "#,
2770 );
2771 }
2772}