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!(
614 "values needing inclusion in stack maps: {:?}",
615 stack_map_values
616 );
617 log::trace!(
618 "before inserting safepoint spills and reloads:\n{}",
619 func.display()
620 );
621
622 self.clear();
623 self.liveness.run(func, stack_map_values);
624 self.rewrite(func);
625
626 log::trace!(
627 "after inserting safepoint spills and reloads:\n{}",
628 func.display()
629 );
630 }
631
632 fn rewrite_def(&mut self, pos: &mut FuncCursor<'_>, val: ir::Value) {
637 if let Some(slot) = self.stack_slots.get(val) {
638 let i = pos.ins().stack_store(val, slot, 0);
639 log::trace!(
640 "rewriting: spilling {val:?} to {slot:?}: {}",
641 pos.func.dfg.display_inst(i)
642 );
643
644 let ty = pos.func.dfg.value_type(val);
647 let size = SlotSize::try_from(ty).unwrap();
648 self.stack_slots.free_stack_slot(size, slot);
649 }
650 }
651
652 fn rewrite_safepoint(&mut self, func: &mut Function, inst: ir::Inst) {
658 log::trace!(
659 "rewriting: found safepoint: {inst:?}: {}",
660 func.dfg.display_inst(inst)
661 );
662
663 let live = self
664 .liveness
665 .safepoints
666 .get(&inst)
667 .expect("should only call `rewrite_safepoint` on safepoint instructions");
668
669 for val in live {
670 let slot = self.stack_slots.get_or_create_stack_slot(func, *val);
672
673 log::trace!(
674 "rewriting: adding stack map entry for {val:?} at {slot:?}: {}",
675 func.dfg.display_inst(inst)
676 );
677 let ty = func.dfg.value_type(*val);
678 func.dfg.append_user_stack_map_entry(
679 inst,
680 ir::UserStackMapEntry {
681 ty,
682 slot,
683 offset: 0,
684 },
685 );
686 }
687 }
688
689 fn rewrite_use(&mut self, pos: &mut FuncCursor<'_>, val: &mut ir::Value) -> bool {
698 if !self.liveness.live_across_any_safepoint.contains(*val) {
699 return false;
700 }
701
702 let old_val = *val;
703 log::trace!("rewriting: found use of {old_val:?}");
704
705 let ty = pos.func.dfg.value_type(*val);
706 let slot = self.stack_slots.get_or_create_stack_slot(pos.func, *val);
707 *val = pos.ins().stack_load(ty, slot, 0);
708
709 log::trace!(
710 "rewriting: reloading {old_val:?}: {}",
711 pos.func
712 .dfg
713 .display_inst(pos.func.dfg.value_def(*val).unwrap_inst())
714 );
715
716 true
717 }
718
719 fn rewrite(&mut self, func: &mut Function) {
732 let mut vals: SmallVec<[_; 8]> = Default::default();
734
735 for block_index in 0..self.liveness.post_order.len() {
742 let block = self.liveness.post_order[block_index];
743 log::trace!("rewriting: processing {block:?}");
744
745 let mut option_inst = func.layout.last_inst(block);
746 while let Some(inst) = option_inst {
747 let mut pos = FuncCursor::new(func).after_inst(inst);
751 vals.extend_from_slice(pos.func.dfg.inst_results(inst));
752 for val in vals.drain(..) {
753 self.rewrite_def(&mut pos, val);
754 }
755
756 if self.liveness.safepoints.contains_key(&inst) {
760 self.rewrite_safepoint(func, inst);
761 }
762
763 let mut pos = FuncCursor::new(func).at_inst(inst);
766 vals.extend(pos.func.dfg.inst_values(inst));
767 let mut replaced_any = false;
768 for val in &mut vals {
769 replaced_any |= self.rewrite_use(&mut pos, val);
770 }
771 if replaced_any {
772 pos.func.dfg.overwrite_inst_values(inst, vals.drain(..));
773 log::trace!(
774 "rewriting: updated {inst:?} operands with reloaded values: {}",
775 pos.func.dfg.display_inst(inst)
776 );
777 } else {
778 vals.clear();
779 }
780
781 option_inst = func.layout.prev_inst(inst);
782 }
783
784 let mut pos = FuncCursor::new(func).at_position(CursorPosition::Before(block));
787 pos.next_inst(); vals.clear();
789 vals.extend_from_slice(pos.func.dfg.block_params(block));
790 for val in vals.drain(..) {
791 self.rewrite_def(&mut pos, val);
792 }
793 }
794 }
795}
796
797#[cfg(test)]
798mod tests {
799 use super::*;
800 use alloc::string::ToString;
801 use cranelift_codegen::isa::CallConv;
802
803 #[test]
804 fn needs_stack_map_and_loop() {
805 let mut sig = Signature::new(CallConv::SystemV);
806 sig.params.push(AbiParam::new(ir::types::I32));
807 sig.params.push(AbiParam::new(ir::types::I32));
808
809 let mut fn_ctx = FunctionBuilderContext::new();
810 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
811 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
812
813 let name = builder
814 .func
815 .declare_imported_user_function(ir::UserExternalName {
816 namespace: 0,
817 index: 0,
818 });
819 let mut sig = Signature::new(CallConv::SystemV);
820 sig.params.push(AbiParam::new(ir::types::I32));
821 let signature = builder.func.import_signature(sig);
822 let func_ref = builder.import_function(ir::ExtFuncData {
823 name: ir::ExternalName::user(name),
824 signature,
825 colocated: true,
826 });
827
828 let block0 = builder.create_block();
836 builder.append_block_params_for_function_params(block0);
837 let a = builder.func.dfg.block_params(block0)[0];
838 let b = builder.func.dfg.block_params(block0)[1];
839 builder.declare_value_needs_stack_map(a);
840 builder.declare_value_needs_stack_map(b);
841 builder.switch_to_block(block0);
842 builder.ins().call(func_ref, &[a]);
843 builder.ins().jump(block0, &[a, b]);
844 builder.seal_all_blocks();
845 builder.finalize();
846
847 assert_eq_output!(
848 func.display().to_string(),
849 r#"
850function %sample(i32, i32) system_v {
851 ss0 = explicit_slot 4, align = 4
852 ss1 = explicit_slot 4, align = 4
853 sig0 = (i32) system_v
854 fn0 = colocated u0:0 sig0
855
856block0(v0: i32, v1: i32):
857 stack_store v0, ss0
858 stack_store v1, ss1
859 v4 = stack_load.i32 ss0
860 call fn0(v4), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
861 v2 = stack_load.i32 ss0
862 v3 = stack_load.i32 ss1
863 jump block0(v2, v3)
864}
865 "#
866 );
867 }
868
869 #[test]
870 fn needs_stack_map_simple() {
871 let _ = env_logger::try_init();
872
873 let sig = Signature::new(CallConv::SystemV);
874
875 let mut fn_ctx = FunctionBuilderContext::new();
876 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
877 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
878
879 let name = builder
880 .func
881 .declare_imported_user_function(ir::UserExternalName {
882 namespace: 0,
883 index: 0,
884 });
885 let mut sig = Signature::new(CallConv::SystemV);
886 sig.params.push(AbiParam::new(ir::types::I32));
887 let signature = builder.func.import_signature(sig);
888 let func_ref = builder.import_function(ir::ExtFuncData {
889 name: ir::ExternalName::user(name),
890 signature,
891 colocated: true,
892 });
893
894 let block0 = builder.create_block();
913 builder.append_block_params_for_function_params(block0);
914 builder.switch_to_block(block0);
915 let v0 = builder.ins().iconst(ir::types::I32, 0);
916 builder.declare_value_needs_stack_map(v0);
917 let v1 = builder.ins().iconst(ir::types::I32, 1);
918 builder.declare_value_needs_stack_map(v1);
919 let v2 = builder.ins().iconst(ir::types::I32, 2);
920 builder.declare_value_needs_stack_map(v2);
921 let v3 = builder.ins().iconst(ir::types::I32, 3);
922 builder.declare_value_needs_stack_map(v3);
923 builder.ins().call(func_ref, &[v3]);
924 builder.ins().call(func_ref, &[v0]);
925 builder.ins().call(func_ref, &[v1]);
926 builder.ins().call(func_ref, &[v2]);
927 builder.ins().return_(&[]);
928 builder.seal_all_blocks();
929 builder.finalize();
930
931 assert_eq_output!(
932 func.display().to_string(),
933 r#"
934function %sample() system_v {
935 ss0 = explicit_slot 4, align = 4
936 ss1 = explicit_slot 4, align = 4
937 ss2 = explicit_slot 4, align = 4
938 sig0 = (i32) system_v
939 fn0 = colocated u0:0 sig0
940
941block0:
942 v0 = iconst.i32 0
943 stack_store v0, ss2 ; v0 = 0
944 v1 = iconst.i32 1
945 stack_store v1, ss1 ; v1 = 1
946 v2 = iconst.i32 2
947 stack_store v2, ss0 ; v2 = 2
948 v3 = iconst.i32 3
949 call fn0(v3), stack_map=[i32 @ ss2+0, i32 @ ss1+0, i32 @ ss0+0] ; v3 = 3
950 v6 = stack_load.i32 ss2
951 call fn0(v6), stack_map=[i32 @ ss1+0, i32 @ ss0+0]
952 v5 = stack_load.i32 ss1
953 call fn0(v5), stack_map=[i32 @ ss0+0]
954 v4 = stack_load.i32 ss0
955 call fn0(v4)
956 return
957}
958 "#
959 );
960 }
961
962 #[test]
963 fn needs_stack_map_and_post_order_early_return() {
964 let mut sig = Signature::new(CallConv::SystemV);
965 sig.params.push(AbiParam::new(ir::types::I32));
966
967 let mut fn_ctx = FunctionBuilderContext::new();
968 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
969 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
970
971 let name = builder
972 .func
973 .declare_imported_user_function(ir::UserExternalName {
974 namespace: 0,
975 index: 0,
976 });
977 let signature = builder
978 .func
979 .import_signature(Signature::new(CallConv::SystemV));
980 let func_ref = builder.import_function(ir::ExtFuncData {
981 name: ir::ExternalName::user(name),
982 signature,
983 colocated: true,
984 });
985
986 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 });
1087
1088 let block0 = builder.create_block();
1103 let block1 = builder.create_block();
1104 let block2 = builder.create_block();
1105 builder.append_block_params_for_function_params(block0);
1106
1107 builder.switch_to_block(block0);
1108 let v0 = builder.func.dfg.block_params(block0)[0];
1109 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1110 builder.declare_value_needs_stack_map(v1);
1111 builder.ins().brif(v0, block1, &[], block2, &[]);
1112
1113 builder.switch_to_block(block1);
1114 builder.ins().call(func_ref, &[]);
1115 builder.ins().return_(&[]);
1116
1117 builder.switch_to_block(block2);
1118 builder.ins().iadd_imm(v1, 0);
1123 builder.ins().return_(&[]);
1124
1125 builder.seal_all_blocks();
1126 builder.finalize();
1127
1128 assert_eq_output!(
1129 func.display().to_string(),
1130 r#"
1131function %sample(i32) system_v {
1132 sig0 = () system_v
1133 fn0 = colocated u0:0 sig0
1134
1135block0(v0: i32):
1136 v1 = iconst.i64 0x1234_5678
1137 brif v0, block1, block2
1138
1139block1:
1140 call fn0()
1141 return
1142
1143block2:
1144 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1145 return
1146}
1147 "#
1148 );
1149
1150 func.clear();
1154 fn_ctx.clear();
1155
1156 let mut sig = Signature::new(CallConv::SystemV);
1157 sig.params.push(AbiParam::new(ir::types::I32));
1158
1159 func.signature = sig;
1160 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1161
1162 let name = builder
1163 .func
1164 .declare_imported_user_function(ir::UserExternalName {
1165 namespace: 0,
1166 index: 0,
1167 });
1168 let signature = builder
1169 .func
1170 .import_signature(Signature::new(CallConv::SystemV));
1171 let func_ref = builder.import_function(ir::ExtFuncData {
1172 name: ir::ExternalName::user(name),
1173 signature,
1174 colocated: true,
1175 });
1176
1177 let block0 = builder.create_block();
1178 let block1 = builder.create_block();
1179 let block2 = builder.create_block();
1180 builder.append_block_params_for_function_params(block0);
1181
1182 builder.switch_to_block(block0);
1183 let v0 = builder.func.dfg.block_params(block0)[0];
1184 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1185 builder.declare_value_needs_stack_map(v1);
1186 builder.ins().brif(v0, block1, &[], block2, &[]);
1187
1188 builder.switch_to_block(block1);
1189 builder.ins().iadd_imm(v1, 0);
1190 builder.ins().return_(&[]);
1191
1192 builder.switch_to_block(block2);
1193 builder.ins().call(func_ref, &[]);
1194 builder.ins().return_(&[]);
1195
1196 builder.seal_all_blocks();
1197 builder.finalize();
1198
1199 assert_eq_output!(
1200 func.display().to_string(),
1201 r#"
1202function u0:0(i32) system_v {
1203 sig0 = () system_v
1204 fn0 = colocated u0:0 sig0
1205
1206block0(v0: i32):
1207 v1 = iconst.i64 0x1234_5678
1208 brif v0, block1, block2
1209
1210block1:
1211 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1212 return
1213
1214block2:
1215 call fn0()
1216 return
1217}
1218 "#
1219 );
1220 }
1221
1222 #[test]
1223 fn needs_stack_map_and_tail_calls() {
1224 let mut sig = Signature::new(CallConv::SystemV);
1225 sig.params.push(AbiParam::new(ir::types::I32));
1226
1227 let mut fn_ctx = FunctionBuilderContext::new();
1228 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1229 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1230
1231 let name = builder
1232 .func
1233 .declare_imported_user_function(ir::UserExternalName {
1234 namespace: 0,
1235 index: 0,
1236 });
1237 let signature = builder
1238 .func
1239 .import_signature(Signature::new(CallConv::SystemV));
1240 let func_ref = builder.import_function(ir::ExtFuncData {
1241 name: ir::ExternalName::user(name),
1242 signature,
1243 colocated: true,
1244 });
1245
1246 let block0 = builder.create_block();
1261 let block1 = builder.create_block();
1262 let block2 = builder.create_block();
1263 builder.append_block_params_for_function_params(block0);
1264
1265 builder.switch_to_block(block0);
1266 let v0 = builder.func.dfg.block_params(block0)[0];
1267 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1268 builder.declare_value_needs_stack_map(v1);
1269 builder.ins().brif(v0, block1, &[], block2, &[]);
1270
1271 builder.switch_to_block(block1);
1272 builder.ins().return_call(func_ref, &[]);
1273
1274 builder.switch_to_block(block2);
1275 builder.ins().iadd_imm(v1, 0);
1280 builder.ins().return_(&[]);
1281
1282 builder.seal_all_blocks();
1283 builder.finalize();
1284
1285 assert_eq_output!(
1286 func.display().to_string(),
1287 r#"
1288function %sample(i32) system_v {
1289 sig0 = () system_v
1290 fn0 = colocated u0:0 sig0
1291
1292block0(v0: i32):
1293 v1 = iconst.i64 0x1234_5678
1294 brif v0, block1, block2
1295
1296block1:
1297 return_call fn0()
1298
1299block2:
1300 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1301 return
1302}
1303 "#
1304 );
1305
1306 func.clear();
1310 fn_ctx.clear();
1311
1312 let mut sig = Signature::new(CallConv::SystemV);
1313 sig.params.push(AbiParam::new(ir::types::I32));
1314 func.signature = sig;
1315
1316 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1317
1318 let name = builder
1319 .func
1320 .declare_imported_user_function(ir::UserExternalName {
1321 namespace: 0,
1322 index: 0,
1323 });
1324 let signature = builder
1325 .func
1326 .import_signature(Signature::new(CallConv::SystemV));
1327 let func_ref = builder.import_function(ir::ExtFuncData {
1328 name: ir::ExternalName::user(name),
1329 signature,
1330 colocated: true,
1331 });
1332
1333 let block0 = builder.create_block();
1334 let block1 = builder.create_block();
1335 let block2 = builder.create_block();
1336 builder.append_block_params_for_function_params(block0);
1337
1338 builder.switch_to_block(block0);
1339 let v0 = builder.func.dfg.block_params(block0)[0];
1340 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1341 builder.declare_value_needs_stack_map(v1);
1342 builder.ins().brif(v0, block1, &[], block2, &[]);
1343
1344 builder.switch_to_block(block1);
1345 builder.ins().iadd_imm(v1, 0);
1346 builder.ins().return_(&[]);
1347
1348 builder.switch_to_block(block2);
1349 builder.ins().return_call(func_ref, &[]);
1350
1351 builder.seal_all_blocks();
1352 builder.finalize();
1353
1354 assert_eq_output!(
1355 func.display().to_string(),
1356 r#"
1357function u0:0(i32) system_v {
1358 sig0 = () system_v
1359 fn0 = colocated u0:0 sig0
1360
1361block0(v0: i32):
1362 v1 = iconst.i64 0x1234_5678
1363 brif v0, block1, block2
1364
1365block1:
1366 v2 = iadd_imm.i64 v1, 0 ; v1 = 0x1234_5678
1367 return
1368
1369block2:
1370 return_call fn0()
1371}
1372 "#
1373 );
1374 }
1375
1376 #[test]
1377 fn needs_stack_map_and_cfg_diamond() {
1378 let mut sig = Signature::new(CallConv::SystemV);
1379 sig.params.push(AbiParam::new(ir::types::I32));
1380
1381 let mut fn_ctx = FunctionBuilderContext::new();
1382 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1383 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1384
1385 let name = builder
1386 .func
1387 .declare_imported_user_function(ir::UserExternalName {
1388 namespace: 0,
1389 index: 0,
1390 });
1391 let signature = builder
1392 .func
1393 .import_signature(Signature::new(CallConv::SystemV));
1394 let func_ref = builder.import_function(ir::ExtFuncData {
1395 name: ir::ExternalName::user(name),
1396 signature,
1397 colocated: true,
1398 });
1399
1400 let block0 = builder.create_block();
1422 let block1 = builder.create_block();
1423 let block2 = builder.create_block();
1424 let block3 = builder.create_block();
1425 builder.append_block_params_for_function_params(block0);
1426
1427 builder.switch_to_block(block0);
1428 let v0 = builder.func.dfg.block_params(block0)[0];
1429 builder.ins().brif(v0, block1, &[], block2, &[]);
1430
1431 builder.switch_to_block(block1);
1432 let v1 = builder.ins().iconst(ir::types::I64, 1);
1433 builder.declare_value_needs_stack_map(v1);
1434 let v2 = builder.ins().iconst(ir::types::I64, 2);
1435 builder.declare_value_needs_stack_map(v2);
1436 builder.ins().call(func_ref, &[]);
1437 builder.ins().jump(block3, &[v1, v2]);
1438
1439 builder.switch_to_block(block2);
1440 let v3 = builder.ins().iconst(ir::types::I64, 3);
1441 builder.declare_value_needs_stack_map(v3);
1442 let v4 = builder.ins().iconst(ir::types::I64, 4);
1443 builder.declare_value_needs_stack_map(v4);
1444 builder.ins().call(func_ref, &[]);
1445 builder.ins().jump(block3, &[v3, v3]);
1446
1447 builder.switch_to_block(block3);
1448 builder.append_block_param(block3, ir::types::I64);
1449 builder.append_block_param(block3, ir::types::I64);
1450 builder.ins().call(func_ref, &[]);
1451 builder.ins().iadd_imm(v1, 0);
1456 builder.ins().return_(&[]);
1457
1458 builder.seal_all_blocks();
1459 builder.finalize();
1460
1461 assert_eq_output!(
1462 func.display().to_string(),
1463 r#"
1464function %sample(i32) system_v {
1465 ss0 = explicit_slot 8, align = 8
1466 ss1 = explicit_slot 8, align = 8
1467 sig0 = () system_v
1468 fn0 = colocated u0:0 sig0
1469
1470block0(v0: i32):
1471 brif v0, block1, block2
1472
1473block1:
1474 v1 = iconst.i64 1
1475 stack_store v1, ss0 ; v1 = 1
1476 v2 = iconst.i64 2
1477 stack_store v2, ss1 ; v2 = 2
1478 call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss1+0]
1479 v9 = stack_load.i64 ss0
1480 v10 = stack_load.i64 ss1
1481 jump block3(v9, v10)
1482
1483block2:
1484 v3 = iconst.i64 3
1485 stack_store v3, ss0 ; v3 = 3
1486 v4 = iconst.i64 4
1487 call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss0+0]
1488 v11 = stack_load.i64 ss0
1489 v12 = stack_load.i64 ss0
1490 jump block3(v11, v12)
1491
1492block3(v5: i64, v6: i64):
1493 call fn0(), stack_map=[i64 @ ss0+0]
1494 v8 = stack_load.i64 ss0
1495 v7 = iadd_imm v8, 0
1496 return
1497}
1498 "#
1499 );
1500 }
1501
1502 #[test]
1503 fn needs_stack_map_and_heterogeneous_types() {
1504 let _ = env_logger::try_init();
1505
1506 let mut sig = Signature::new(CallConv::SystemV);
1507 for ty in [
1508 ir::types::I8,
1509 ir::types::I16,
1510 ir::types::I32,
1511 ir::types::I64,
1512 ir::types::I128,
1513 ir::types::F32,
1514 ir::types::F64,
1515 ir::types::I8X16,
1516 ir::types::I16X8,
1517 ] {
1518 sig.params.push(AbiParam::new(ty));
1519 sig.returns.push(AbiParam::new(ty));
1520 }
1521
1522 let mut fn_ctx = FunctionBuilderContext::new();
1523 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1524 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1525
1526 let name = builder
1527 .func
1528 .declare_imported_user_function(ir::UserExternalName {
1529 namespace: 0,
1530 index: 0,
1531 });
1532 let signature = builder
1533 .func
1534 .import_signature(Signature::new(CallConv::SystemV));
1535 let func_ref = builder.import_function(ir::ExtFuncData {
1536 name: ir::ExternalName::user(name),
1537 signature,
1538 colocated: true,
1539 });
1540
1541 let block0 = builder.create_block();
1548 builder.append_block_params_for_function_params(block0);
1549
1550 builder.switch_to_block(block0);
1551 let params = builder.func.dfg.block_params(block0).to_vec();
1552 for val in ¶ms {
1553 builder.declare_value_needs_stack_map(*val);
1554 }
1555 builder.ins().call(func_ref, &[]);
1556 builder.ins().return_(¶ms);
1557
1558 builder.seal_all_blocks();
1559 builder.finalize();
1560
1561 assert_eq_output!(
1562 func.display().to_string(),
1563 r#"
1564function %sample(i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8) -> i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8 system_v {
1565 ss0 = explicit_slot 1
1566 ss1 = explicit_slot 2, align = 2
1567 ss2 = explicit_slot 4, align = 4
1568 ss3 = explicit_slot 8, align = 8
1569 ss4 = explicit_slot 16, align = 16
1570 ss5 = explicit_slot 4, align = 4
1571 ss6 = explicit_slot 8, align = 8
1572 ss7 = explicit_slot 16, align = 16
1573 ss8 = explicit_slot 16, align = 16
1574 sig0 = () system_v
1575 fn0 = colocated u0:0 sig0
1576
1577block0(v0: i8, v1: i16, v2: i32, v3: i64, v4: i128, v5: f32, v6: f64, v7: i8x16, v8: i16x8):
1578 stack_store v0, ss0
1579 stack_store v1, ss1
1580 stack_store v2, ss2
1581 stack_store v3, ss3
1582 stack_store v4, ss4
1583 stack_store v5, ss5
1584 stack_store v6, ss6
1585 stack_store v7, ss7
1586 stack_store v8, ss8
1587 call fn0(), stack_map=[i8 @ ss0+0, i16 @ ss1+0, i32 @ ss2+0, i64 @ ss3+0, i128 @ ss4+0, f32 @ ss5+0, f64 @ ss6+0, i8x16 @ ss7+0, i16x8 @ ss8+0]
1588 v9 = stack_load.i8 ss0
1589 v10 = stack_load.i16 ss1
1590 v11 = stack_load.i32 ss2
1591 v12 = stack_load.i64 ss3
1592 v13 = stack_load.i128 ss4
1593 v14 = stack_load.f32 ss5
1594 v15 = stack_load.f64 ss6
1595 v16 = stack_load.i8x16 ss7
1596 v17 = stack_load.i16x8 ss8
1597 return v9, v10, v11, v12, v13, v14, v15, v16, v17
1598}
1599 "#
1600 );
1601 }
1602
1603 #[test]
1604 fn series_of_non_overlapping_live_ranges_needs_stack_map() {
1605 let sig = Signature::new(CallConv::SystemV);
1606
1607 let mut fn_ctx = FunctionBuilderContext::new();
1608 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1609 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1610
1611 let name = builder
1612 .func
1613 .declare_imported_user_function(ir::UserExternalName {
1614 namespace: 0,
1615 index: 0,
1616 });
1617 let signature = builder
1618 .func
1619 .import_signature(Signature::new(CallConv::SystemV));
1620 let foo_func_ref = builder.import_function(ir::ExtFuncData {
1621 name: ir::ExternalName::user(name),
1622 signature,
1623 colocated: true,
1624 });
1625
1626 let name = builder
1627 .func
1628 .declare_imported_user_function(ir::UserExternalName {
1629 namespace: 0,
1630 index: 1,
1631 });
1632 let mut sig = Signature::new(CallConv::SystemV);
1633 sig.params.push(AbiParam::new(ir::types::I32));
1634 let signature = builder.func.import_signature(sig);
1635 let consume_func_ref = builder.import_function(ir::ExtFuncData {
1636 name: ir::ExternalName::user(name),
1637 signature,
1638 colocated: true,
1639 });
1640
1641 let block0 = builder.create_block();
1660 builder.append_block_params_for_function_params(block0);
1661 builder.switch_to_block(block0);
1662 let v0 = builder.ins().iconst(ir::types::I32, 0);
1663 builder.declare_value_needs_stack_map(v0);
1664 builder.ins().call(foo_func_ref, &[]);
1665 builder.ins().call(consume_func_ref, &[v0]);
1666 let v1 = builder.ins().iconst(ir::types::I32, 1);
1667 builder.declare_value_needs_stack_map(v1);
1668 builder.ins().call(foo_func_ref, &[]);
1669 builder.ins().call(consume_func_ref, &[v1]);
1670 let v2 = builder.ins().iconst(ir::types::I32, 2);
1671 builder.declare_value_needs_stack_map(v2);
1672 builder.ins().call(foo_func_ref, &[]);
1673 builder.ins().call(consume_func_ref, &[v2]);
1674 let v3 = builder.ins().iconst(ir::types::I32, 3);
1675 builder.declare_value_needs_stack_map(v3);
1676 builder.ins().call(foo_func_ref, &[]);
1677 builder.ins().call(consume_func_ref, &[v3]);
1678 builder.ins().return_(&[]);
1679 builder.seal_all_blocks();
1680 builder.finalize();
1681
1682 assert_eq_output!(
1683 func.display().to_string(),
1684 r#"
1685function %sample() system_v {
1686 ss0 = explicit_slot 4, align = 4
1687 sig0 = () system_v
1688 sig1 = (i32) system_v
1689 fn0 = colocated u0:0 sig0
1690 fn1 = colocated u0:1 sig1
1691
1692block0:
1693 v0 = iconst.i32 0
1694 stack_store v0, ss0 ; v0 = 0
1695 call fn0(), stack_map=[i32 @ ss0+0]
1696 v7 = stack_load.i32 ss0
1697 call fn1(v7)
1698 v1 = iconst.i32 1
1699 stack_store v1, ss0 ; v1 = 1
1700 call fn0(), stack_map=[i32 @ ss0+0]
1701 v6 = stack_load.i32 ss0
1702 call fn1(v6)
1703 v2 = iconst.i32 2
1704 stack_store v2, ss0 ; v2 = 2
1705 call fn0(), stack_map=[i32 @ ss0+0]
1706 v5 = stack_load.i32 ss0
1707 call fn1(v5)
1708 v3 = iconst.i32 3
1709 stack_store v3, ss0 ; v3 = 3
1710 call fn0(), stack_map=[i32 @ ss0+0]
1711 v4 = stack_load.i32 ss0
1712 call fn1(v4)
1713 return
1714}
1715 "#
1716 );
1717 }
1718
1719 #[test]
1720 fn vars_block_params_and_needs_stack_map() {
1721 let _ = env_logger::try_init();
1722
1723 let mut sig = Signature::new(CallConv::SystemV);
1724 sig.params.push(AbiParam::new(ir::types::I32));
1725 sig.returns.push(AbiParam::new(ir::types::I32));
1726
1727 let mut fn_ctx = FunctionBuilderContext::new();
1728 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1729 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1730
1731 let name = builder
1732 .func
1733 .declare_imported_user_function(ir::UserExternalName {
1734 namespace: 0,
1735 index: 0,
1736 });
1737 let mut sig = Signature::new(CallConv::SystemV);
1738 sig.params.push(AbiParam::new(ir::types::I32));
1739 let signature = builder.func.import_signature(sig);
1740 let func_ref = builder.import_function(ir::ExtFuncData {
1741 name: ir::ExternalName::user(name),
1742 signature,
1743 colocated: true,
1744 });
1745
1746 let x = Variable::from_u32(0);
1771 builder.declare_var(x, ir::types::I32);
1772 builder.declare_var_needs_stack_map(x);
1773
1774 let block0 = builder.create_block();
1775 let block1 = builder.create_block();
1776 let block2 = builder.create_block();
1777 let block3 = builder.create_block();
1778
1779 builder.append_block_params_for_function_params(block0);
1780 builder.switch_to_block(block0);
1781 let v0 = builder.func.dfg.block_params(block0)[0];
1782 let val = builder.ins().iconst(ir::types::I32, 42);
1783 builder.def_var(x, val);
1784 {
1785 let x = builder.use_var(x);
1786 builder.ins().call(func_ref, &[x]);
1787 }
1788 builder.ins().brif(v0, block1, &[], block2, &[]);
1789
1790 builder.switch_to_block(block1);
1791 {
1792 let x = builder.use_var(x);
1793 builder.ins().call(func_ref, &[x]);
1794 builder.ins().call(func_ref, &[x]);
1795 }
1796 let val = builder.ins().iconst(ir::types::I32, 36);
1797 builder.def_var(x, val);
1798 {
1799 let x = builder.use_var(x);
1800 builder.ins().call(func_ref, &[x]);
1801 }
1802 builder.ins().jump(block3, &[]);
1803
1804 builder.switch_to_block(block2);
1805 {
1806 let x = builder.use_var(x);
1807 builder.ins().call(func_ref, &[x]);
1808 builder.ins().call(func_ref, &[x]);
1809 }
1810 let val = builder.ins().iconst(ir::types::I32, 36);
1811 builder.def_var(x, val);
1812 {
1813 let x = builder.use_var(x);
1814 builder.ins().call(func_ref, &[x]);
1815 }
1816 builder.ins().jump(block3, &[]);
1817
1818 builder.switch_to_block(block3);
1819 let x = builder.use_var(x);
1820 builder.ins().call(func_ref, &[x]);
1821 builder.ins().return_(&[x]);
1822
1823 builder.seal_all_blocks();
1824 builder.finalize();
1825
1826 assert_eq_output!(
1827 func.display().to_string(),
1828 r#"
1829function %sample(i32) -> i32 system_v {
1830 ss0 = explicit_slot 4, align = 4
1831 ss1 = explicit_slot 4, align = 4
1832 sig0 = (i32) system_v
1833 fn0 = colocated u0:0 sig0
1834
1835block0(v0: i32):
1836 v1 = iconst.i32 42
1837 v2 -> v1
1838 v4 -> v1
1839 stack_store v1, ss0 ; v1 = 42
1840 v13 = stack_load.i32 ss0
1841 call fn0(v13), stack_map=[i32 @ ss0+0]
1842 brif v0, block1, block2
1843
1844block1:
1845 call fn0(v2), stack_map=[i32 @ ss0+0] ; v2 = 42
1846 call fn0(v2) ; v2 = 42
1847 v3 = iconst.i32 36
1848 stack_store v3, ss0 ; v3 = 36
1849 v10 = stack_load.i32 ss0
1850 call fn0(v10), stack_map=[i32 @ ss0+0]
1851 v9 = stack_load.i32 ss0
1852 jump block3(v9)
1853
1854block2:
1855 call fn0(v4), stack_map=[i32 @ ss0+0] ; v4 = 42
1856 call fn0(v4) ; v4 = 42
1857 v5 = iconst.i32 36
1858 stack_store v5, ss1 ; v5 = 36
1859 v12 = stack_load.i32 ss1
1860 call fn0(v12), stack_map=[i32 @ ss1+0]
1861 v11 = stack_load.i32 ss1
1862 jump block3(v11)
1863
1864block3(v6: i32):
1865 stack_store v6, ss0
1866 v8 = stack_load.i32 ss0
1867 call fn0(v8), stack_map=[i32 @ ss0+0]
1868 v7 = stack_load.i32 ss0
1869 return v7
1870}
1871 "#
1872 );
1873 }
1874
1875 #[test]
1876 fn var_needs_stack_map() {
1877 let mut sig = Signature::new(CallConv::SystemV);
1878 sig.params
1879 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1880 sig.returns
1881 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1882
1883 let mut fn_ctx = FunctionBuilderContext::new();
1884 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1885 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1886
1887 let var = Variable::from_u32(0);
1888 builder.declare_var(var, cranelift_codegen::ir::types::I32);
1889 builder.declare_var_needs_stack_map(var);
1890
1891 let name = builder
1892 .func
1893 .declare_imported_user_function(ir::UserExternalName {
1894 namespace: 0,
1895 index: 0,
1896 });
1897 let signature = builder
1898 .func
1899 .import_signature(Signature::new(CallConv::SystemV));
1900 let func_ref = builder.import_function(ir::ExtFuncData {
1901 name: ir::ExternalName::user(name),
1902 signature,
1903 colocated: true,
1904 });
1905
1906 let block0 = builder.create_block();
1907 builder.append_block_params_for_function_params(block0);
1908 builder.switch_to_block(block0);
1909
1910 let arg = builder.func.dfg.block_params(block0)[0];
1911 builder.def_var(var, arg);
1912
1913 builder.ins().call(func_ref, &[]);
1914
1915 let val = builder.use_var(var);
1916 builder.ins().return_(&[val]);
1917
1918 builder.seal_all_blocks();
1919 builder.finalize();
1920
1921 assert_eq_output!(
1922 func.display().to_string(),
1923 r#"
1924function %sample(i32) -> i32 system_v {
1925 ss0 = explicit_slot 4, align = 4
1926 sig0 = () system_v
1927 fn0 = colocated u0:0 sig0
1928
1929block0(v0: i32):
1930 stack_store v0, ss0
1931 call fn0(), stack_map=[i32 @ ss0+0]
1932 v1 = stack_load.i32 ss0
1933 return v1
1934}
1935 "#
1936 );
1937 }
1938
1939 #[test]
1940 fn first_inst_defines_needs_stack_map() {
1941 let mut sig = Signature::new(CallConv::SystemV);
1942 sig.params
1943 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1944 sig.returns
1945 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1946 sig.returns
1947 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
1948
1949 let mut fn_ctx = FunctionBuilderContext::new();
1950 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1951 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1952
1953 let name = builder
1954 .func
1955 .declare_imported_user_function(ir::UserExternalName {
1956 namespace: 0,
1957 index: 0,
1958 });
1959 let signature = builder
1960 .func
1961 .import_signature(Signature::new(CallConv::SystemV));
1962 let func_ref = builder.import_function(ir::ExtFuncData {
1963 name: ir::ExternalName::user(name),
1964 signature,
1965 colocated: true,
1966 });
1967
1968 let block0 = builder.create_block();
1980 builder.append_block_params_for_function_params(block0);
1981 builder.switch_to_block(block0);
1982
1983 let arg = builder.func.dfg.block_params(block0)[0];
1984 builder.declare_value_needs_stack_map(arg);
1985
1986 let val = builder.ins().iconst(ir::types::I32, 42);
1987 builder.declare_value_needs_stack_map(val);
1988
1989 builder.ins().call(func_ref, &[]);
1990
1991 builder.ins().return_(&[arg, val]);
1992
1993 builder.seal_all_blocks();
1994 builder.finalize();
1995
1996 assert_eq_output!(
1997 func.display().to_string(),
1998 r#"
1999function %sample(i32) -> i32, i32 system_v {
2000 ss0 = explicit_slot 4, align = 4
2001 ss1 = explicit_slot 4, align = 4
2002 sig0 = () system_v
2003 fn0 = colocated u0:0 sig0
2004
2005block0(v0: i32):
2006 stack_store v0, ss0
2007 v1 = iconst.i32 42
2008 stack_store v1, ss1 ; v1 = 42
2009 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
2010 v2 = stack_load.i32 ss0
2011 v3 = stack_load.i32 ss1
2012 return v2, v3
2013}
2014 "#
2015 );
2016 }
2017
2018 #[test]
2019 fn needs_stack_map_and_loops_and_partially_live_values() {
2020 let _ = env_logger::try_init();
2021
2022 let mut sig = Signature::new(CallConv::SystemV);
2023 sig.params.push(AbiParam::new(ir::types::I32));
2024
2025 let mut fn_ctx = FunctionBuilderContext::new();
2026 let mut func =
2027 Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig.clone());
2028 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2029
2030 let name = builder
2031 .func
2032 .declare_imported_user_function(ir::UserExternalName {
2033 namespace: 0,
2034 index: 0,
2035 });
2036 let signature = builder
2037 .func
2038 .import_signature(Signature::new(CallConv::SystemV));
2039 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2040 name: ir::ExternalName::user(name),
2041 signature,
2042 colocated: true,
2043 });
2044
2045 let name = builder
2046 .func
2047 .declare_imported_user_function(ir::UserExternalName {
2048 namespace: 1,
2049 index: 1,
2050 });
2051 let signature = builder.func.import_signature(sig);
2052 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2053 name: ir::ExternalName::user(name),
2054 signature,
2055 colocated: true,
2056 });
2057
2058 let block0 = builder.create_block();
2075 let block1 = builder.create_block();
2076 builder.append_block_params_for_function_params(block0);
2077
2078 builder.switch_to_block(block0);
2079 builder.ins().jump(block1, &[]);
2080
2081 builder.switch_to_block(block1);
2082 let v0 = builder.func.dfg.block_params(block0)[0];
2083 builder.declare_value_needs_stack_map(v0);
2084 builder.ins().call(foo_func_ref, &[]);
2085 builder.ins().call(bar_func_ref, &[v0]);
2086 builder.ins().call(foo_func_ref, &[]);
2087 builder.ins().jump(block1, &[]);
2088
2089 builder.seal_all_blocks();
2090 builder.finalize();
2091
2092 assert_eq_output!(
2093 func.display().to_string(),
2094 r#"
2095function %sample(i32) system_v {
2096 ss0 = explicit_slot 4, align = 4
2097 sig0 = () system_v
2098 sig1 = (i32) system_v
2099 fn0 = colocated u0:0 sig0
2100 fn1 = colocated u1:1 sig1
2101
2102block0(v0: i32):
2103 stack_store v0, ss0
2104 jump block1
2105
2106block1:
2107 call fn0(), stack_map=[i32 @ ss0+0]
2108 v1 = stack_load.i32 ss0
2109 call fn1(v1), stack_map=[i32 @ ss0+0]
2110 call fn0(), stack_map=[i32 @ ss0+0]
2111 jump block1
2112}
2113 "#,
2114 );
2115 }
2116
2117 #[test]
2118 fn needs_stack_map_and_irreducible_loops() {
2119 let _ = env_logger::try_init();
2120
2121 let mut sig = Signature::new(CallConv::SystemV);
2122 sig.params.push(AbiParam::new(ir::types::I32));
2123 sig.params.push(AbiParam::new(ir::types::I32));
2124
2125 let mut fn_ctx = FunctionBuilderContext::new();
2126 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2127 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2128
2129 let name = builder
2130 .func
2131 .declare_imported_user_function(ir::UserExternalName {
2132 namespace: 0,
2133 index: 0,
2134 });
2135 let signature = builder
2136 .func
2137 .import_signature(Signature::new(CallConv::SystemV));
2138 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2139 name: ir::ExternalName::user(name),
2140 signature,
2141 colocated: true,
2142 });
2143
2144 let name = builder
2145 .func
2146 .declare_imported_user_function(ir::UserExternalName {
2147 namespace: 1,
2148 index: 1,
2149 });
2150 let mut sig = Signature::new(CallConv::SystemV);
2151 sig.params.push(AbiParam::new(ir::types::I32));
2152 let signature = builder.func.import_signature(sig);
2153 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2154 name: ir::ExternalName::user(name),
2155 signature,
2156 colocated: true,
2157 });
2158
2159 let block0 = builder.create_block();
2183 let block1 = builder.create_block();
2184 let block2 = builder.create_block();
2185 let block3 = builder.create_block();
2186 let block4 = builder.create_block();
2187 builder.append_block_params_for_function_params(block0);
2188
2189 builder.switch_to_block(block0);
2190 let v0 = builder.func.dfg.block_params(block0)[0];
2191 let v1 = builder.func.dfg.block_params(block0)[1];
2192 builder.declare_value_needs_stack_map(v1);
2193 builder.ins().brif(v0, block1, &[], block2, &[]);
2194
2195 builder.switch_to_block(block1);
2196 builder.ins().jump(block3, &[]);
2197
2198 builder.switch_to_block(block2);
2199 builder.ins().jump(block4, &[]);
2200
2201 builder.switch_to_block(block3);
2202 builder.ins().call(foo_func_ref, &[]);
2203 builder.ins().call(bar_func_ref, &[v1]);
2204 builder.ins().call(foo_func_ref, &[]);
2205 builder.ins().jump(block2, &[]);
2206
2207 builder.switch_to_block(block4);
2208 builder.ins().call(foo_func_ref, &[]);
2209 builder.ins().call(bar_func_ref, &[v1]);
2210 builder.ins().call(foo_func_ref, &[]);
2211 builder.ins().jump(block1, &[]);
2212
2213 builder.seal_all_blocks();
2214 builder.finalize();
2215
2216 assert_eq_output!(
2217 func.display().to_string(),
2218 r#"
2219function %sample(i32, i32) system_v {
2220 ss0 = explicit_slot 4, align = 4
2221 sig0 = () system_v
2222 sig1 = (i32) system_v
2223 fn0 = colocated u0:0 sig0
2224 fn1 = colocated u1:1 sig1
2225
2226block0(v0: i32, v1: i32):
2227 stack_store v1, ss0
2228 brif v0, block1, block2
2229
2230block1:
2231 jump block3
2232
2233block2:
2234 jump block4
2235
2236block3:
2237 call fn0(), stack_map=[i32 @ ss0+0]
2238 v3 = stack_load.i32 ss0
2239 call fn1(v3), stack_map=[i32 @ ss0+0]
2240 call fn0(), stack_map=[i32 @ ss0+0]
2241 jump block2
2242
2243block4:
2244 call fn0(), stack_map=[i32 @ ss0+0]
2245 v2 = stack_load.i32 ss0
2246 call fn1(v2), stack_map=[i32 @ ss0+0]
2247 call fn0(), stack_map=[i32 @ ss0+0]
2248 jump block1
2249}
2250 "#,
2251 );
2252 }
2253
2254 #[test]
2255 fn needs_stack_map_and_back_edge_to_back_edge() {
2256 let _ = env_logger::try_init();
2257
2258 let mut sig = Signature::new(CallConv::SystemV);
2259 sig.params.push(AbiParam::new(ir::types::I32));
2260 sig.params.push(AbiParam::new(ir::types::I32));
2261 sig.params.push(AbiParam::new(ir::types::I32));
2262 sig.params.push(AbiParam::new(ir::types::I32));
2263
2264 let mut fn_ctx = FunctionBuilderContext::new();
2265 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2266 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2267
2268 let name = builder
2269 .func
2270 .declare_imported_user_function(ir::UserExternalName {
2271 namespace: 0,
2272 index: 0,
2273 });
2274 let signature = builder
2275 .func
2276 .import_signature(Signature::new(CallConv::SystemV));
2277 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2278 name: ir::ExternalName::user(name),
2279 signature,
2280 colocated: true,
2281 });
2282
2283 let name = builder
2284 .func
2285 .declare_imported_user_function(ir::UserExternalName {
2286 namespace: 1,
2287 index: 1,
2288 });
2289 let mut sig = Signature::new(CallConv::SystemV);
2290 sig.params.push(AbiParam::new(ir::types::I32));
2291 let signature = builder.func.import_signature(sig);
2292 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2293 name: ir::ExternalName::user(name),
2294 signature,
2295 colocated: true,
2296 });
2297
2298 let block0 = builder.create_block();
2325 let block1 = builder.create_block();
2326 let block2 = builder.create_block();
2327 let block3 = builder.create_block();
2328
2329 builder.append_block_params_for_function_params(block0);
2330
2331 builder.switch_to_block(block0);
2332
2333 let v0 = builder.func.dfg.block_params(block0)[0];
2334 builder.declare_value_needs_stack_map(v0);
2335 let v1 = builder.func.dfg.block_params(block0)[1];
2336 builder.declare_value_needs_stack_map(v1);
2337 let v2 = builder.func.dfg.block_params(block0)[2];
2338 builder.declare_value_needs_stack_map(v2);
2339 let v3 = builder.func.dfg.block_params(block0)[3];
2340
2341 builder.ins().jump(block1, &[v3]);
2342
2343 builder.switch_to_block(block1);
2344 let v4 = builder.append_block_param(block1, ir::types::I32);
2345 builder.ins().call(foo_func_ref, &[]);
2346 builder.ins().call(bar_func_ref, &[v0]);
2347 builder.ins().call(foo_func_ref, &[]);
2348 builder.ins().jump(block2, &[]);
2349
2350 builder.switch_to_block(block2);
2351 builder.ins().call(foo_func_ref, &[]);
2352 builder.ins().call(bar_func_ref, &[v1]);
2353 builder.ins().call(foo_func_ref, &[]);
2354 let v5 = builder.ins().iadd_imm(v4, -1);
2355 builder.ins().brif(v4, block1, &[v5], block3, &[]);
2356
2357 builder.switch_to_block(block3);
2358 builder.ins().call(foo_func_ref, &[]);
2359 builder.ins().call(bar_func_ref, &[v2]);
2360 builder.ins().call(foo_func_ref, &[]);
2361 builder.ins().jump(block2, &[]);
2362
2363 builder.seal_all_blocks();
2364 builder.finalize();
2365
2366 assert_eq_output!(
2367 func.display().to_string(),
2368 r#"
2369function %sample(i32, i32, i32, i32) system_v {
2370 ss0 = explicit_slot 4, align = 4
2371 ss1 = explicit_slot 4, align = 4
2372 ss2 = explicit_slot 4, align = 4
2373 sig0 = () system_v
2374 sig1 = (i32) system_v
2375 fn0 = colocated u0:0 sig0
2376 fn1 = colocated u1:1 sig1
2377
2378block0(v0: i32, v1: i32, v2: i32, v3: i32):
2379 stack_store v0, ss0
2380 stack_store v1, ss1
2381 stack_store v2, ss2
2382 jump block1(v3)
2383
2384block1(v4: i32):
2385 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2386 v8 = stack_load.i32 ss0
2387 call fn1(v8), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2388 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2389 jump block2
2390
2391block2:
2392 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2393 v7 = stack_load.i32 ss1
2394 call fn1(v7), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2395 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2396 v5 = iadd_imm.i32 v4, -1
2397 brif.i32 v4, block1(v5), block3
2398
2399block3:
2400 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2401 v6 = stack_load.i32 ss2
2402 call fn1(v6), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2403 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2404 jump block2
2405}
2406 "#,
2407 );
2408 }
2409}