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, func: &Function, val: ir::Value) {
371 debug_assert!(!func.dfg.value_is_alias(val));
372 if self.currently_live.remove(&val) {
373 log::trace!("liveness: defining {val:?}, removing it from the live set");
374 }
375 }
376
377 fn record_safepoint(&mut self, func: &Function, inst: Inst) {
379 log::trace!(
380 "liveness: found safepoint: {inst:?}: {}",
381 func.dfg.display_inst(inst)
382 );
383 log::trace!("liveness: live set = {:?}", self.currently_live);
384
385 let mut live = self.currently_live.iter().copied().collect::<SmallVec<_>>();
386 live.sort();
389 debug_assert!(live.iter().all(|v| !func.dfg.value_is_alias(*v)));
390
391 self.live_across_any_safepoint.extend(live.iter().copied());
392 self.safepoints.insert(inst, live);
393 }
394
395 fn process_use(&mut self, func: &Function, inst: Inst, val: Value) {
398 debug_assert!(!func.dfg.value_is_alias(val));
399 if self.currently_live.insert(val) {
400 log::trace!(
401 "liveness: found use of {val:?}, marking it live: {inst:?}: {}",
402 func.dfg.display_inst(inst)
403 );
404 }
405 }
406
407 fn process_block(
409 &mut self,
410 func: &mut Function,
411 stack_map_values: &EntitySet<ir::Value>,
412 block_index: usize,
413 record_safepoints: RecordSafepoints,
414 ) {
415 let block = self.post_order[block_index];
416 log::trace!("liveness: traversing {block:?}");
417
418 self.currently_live.clear();
420 self.currently_live
421 .extend(self.live_outs[block_index].iter().copied());
422
423 let mut option_inst = func.layout.last_inst(block);
426 while let Some(inst) = option_inst {
427 for val in func.dfg.inst_results(inst) {
429 self.process_def(func, *val);
430 }
431
432 let opcode = func.dfg.insts[inst].opcode();
435 if record_safepoints == RecordSafepoints::Yes && opcode.is_safepoint() {
436 self.record_safepoint(func, inst);
437 }
438
439 for val in func.dfg.inst_values(inst) {
441 let val = func.dfg.resolve_aliases(val);
442 if stack_map_values.contains(val) {
443 self.process_use(func, inst, val);
444 }
445 }
446
447 option_inst = func.layout.prev_inst(inst);
448 }
449
450 for val in func.dfg.block_params(block) {
453 self.process_def(func, *val);
454 }
455 }
456
457 pub fn run(&mut self, func: &mut Function, stack_map_values: &EntitySet<ir::Value>) {
459 self.clear();
460 self.post_order.extend(self.dfs.post_order_iter(func));
461 self.reserve_capacity(func);
462 self.initialize_block_to_index_map();
463 self.initialize_predecessors_map(func);
464
465 self.worklist
470 .extend((0..u32::try_from(self.post_order.len()).unwrap()).rev());
471
472 while let Some(block_index) = self.worklist.pop() {
474 let block_index = usize::try_from(block_index).unwrap();
475
476 let initial_live_in_len = self.live_ins[block_index].len();
479
480 for successor in func.block_successors(self.post_order[block_index]) {
483 let successor_index = self.block_to_index[successor];
484 debug_assert_ne!(successor_index, u32::MAX);
485 let successor_index = usize::try_from(successor_index).unwrap();
486 self.live_outs[block_index].extend(self.live_ins[successor_index].iter().copied());
487 }
488 debug_assert!(
489 self.live_outs[block_index]
490 .iter()
491 .all(|v| !func.dfg.value_is_alias(*v))
492 );
493
494 self.process_block(func, stack_map_values, block_index, RecordSafepoints::No);
498
499 self.live_ins[block_index].extend(self.currently_live.iter().copied());
502 debug_assert!(
503 self.live_ins[block_index]
504 .iter()
505 .all(|v| !func.dfg.value_is_alias(*v))
506 );
507
508 if self.live_ins[block_index].len() != initial_live_in_len {
511 self.worklist
512 .extend(self.predecessors[block_index].iter().copied());
513 }
514 }
515
516 for block_index in 0..self.post_order.len() {
520 self.process_block(func, stack_map_values, block_index, RecordSafepoints::Yes);
521
522 debug_assert_eq!(
523 self.currently_live, self.live_ins[block_index],
524 "when we recompute the live-in set for a block as part of \
525 computing live sets at each safepoint, we should get the same \
526 result we computed in the fixed-point"
527 );
528 }
529 }
530}
531
532#[derive(Default)]
538struct StackSlots {
539 stack_slots: HashMap<ir::Value, ir::StackSlot>,
546
547 free_stack_slots: SlotSizeMap<SmallVec<[ir::StackSlot; 4]>>,
551}
552
553impl StackSlots {
554 fn clear(&mut self) {
555 let StackSlots {
556 stack_slots,
557 free_stack_slots,
558 } = self;
559 stack_slots.clear();
560 free_stack_slots.clear();
561 }
562
563 fn get(&self, val: ir::Value) -> Option<ir::StackSlot> {
564 self.stack_slots.get(&val).copied()
565 }
566
567 fn get_or_create_stack_slot(&mut self, func: &mut Function, val: ir::Value) -> ir::StackSlot {
568 debug_assert!(!func.dfg.value_is_alias(val));
569 *self.stack_slots.entry(val).or_insert_with(|| {
570 log::trace!("rewriting: {val:?} needs a stack slot");
571 let ty = func.dfg.value_type(val);
572 let size = ty.bytes();
573 match self.free_stack_slots[SlotSize::unwrap_new(size)].pop() {
574 Some(slot) => {
575 log::trace!("rewriting: reusing free stack slot {slot:?} for {val:?}");
576 slot
577 }
578 None => {
579 debug_assert!(size.is_power_of_two());
580 let log2_size = size.ilog2();
581 let slot = func.create_sized_stack_slot(ir::StackSlotData::new(
582 ir::StackSlotKind::ExplicitSlot,
583 size,
584 log2_size.try_into().unwrap(),
585 ));
586 log::trace!("rewriting: created new stack slot {slot:?} for {val:?}");
587 slot
588 }
589 }
590 })
591 }
592
593 fn free_stack_slot(&mut self, size: SlotSize, slot: ir::StackSlot) {
594 log::trace!("rewriting: returning {slot:?} to the free list");
595 self.free_stack_slots[size].push(slot);
596 }
597}
598
599#[derive(Default)]
606pub(super) struct SafepointSpiller {
607 liveness: LivenessAnalysis,
608 stack_slots: StackSlots,
609
610 pub(super) make_alias_region: Option<
614 Box<
615 dyn Fn(&mut ir::AliasRegionSet, ir::Type, ir::StackSlot, u32) -> Option<ir::AliasRegion>
616 + Send
617 + Sync,
618 >,
619 >,
620}
621
622impl SafepointSpiller {
623 pub fn clear(&mut self) {
626 let SafepointSpiller {
627 liveness,
628 stack_slots,
629 make_alias_region: _,
630 } = self;
631 liveness.clear();
632 stack_slots.clear();
633 }
634
635 pub fn run(
639 &mut self,
640 func: &mut Function,
641 stack_map_values: &EntitySet<ir::Value>,
642 pointer_type: ir::Type,
643 ) {
644 log::trace!("values needing inclusion in stack maps: {stack_map_values:?}");
645 log::trace!(
646 "before inserting safepoint spills and reloads:\n{}",
647 func.display()
648 );
649
650 self.clear();
651 self.liveness.run(func, stack_map_values);
652 self.rewrite(func, pointer_type);
653
654 log::trace!(
655 "after inserting safepoint spills and reloads:\n{}",
656 func.display()
657 );
658 }
659
660 fn rewrite_def(&mut self, pos: &mut FuncCursor<'_>, val: ir::Value, pointer_type: ir::Type) {
665 debug_assert!(!pos.func.dfg.value_is_alias(val));
666 if let Some(slot) = self.stack_slots.get(val) {
667 let ty = pos.func.dfg.value_type(val);
668
669 let mut flags = ir::MemFlagsData::trusted();
670 flags.set_notrap();
671 if let Some(make_alias_region) = &self.make_alias_region {
672 if let Some(region) =
673 make_alias_region(&mut pos.func.dfg.alias_regions, ty, slot, 0)
674 {
675 flags.set_alias_region(Some(region));
676 }
677 }
678
679 let addr = pos.ins().stack_addr(pointer_type, slot, 0);
680 let inst = pos.ins().store(flags, val, addr, 0);
681 log::trace!(
682 "rewriting: spilling {val:?} to {slot:?}: {}",
683 pos.func.dfg.display_inst(inst)
684 );
685
686 let size = SlotSize::try_from(ty).unwrap();
689 self.stack_slots.free_stack_slot(size, slot);
690 }
691 }
692
693 fn rewrite_safepoint(&mut self, func: &mut Function, inst: ir::Inst) {
699 log::trace!(
700 "rewriting: found safepoint: {inst:?}: {}",
701 func.dfg.display_inst(inst)
702 );
703
704 let live = self
705 .liveness
706 .safepoints
707 .get(&inst)
708 .expect("should only call `rewrite_safepoint` on safepoint instructions");
709
710 for val in live {
711 debug_assert!(!func.dfg.value_is_alias(*val));
712
713 let slot = self.stack_slots.get_or_create_stack_slot(func, *val);
715
716 log::trace!(
717 "rewriting: adding stack map entry for {val:?} at {slot:?}: {}",
718 func.dfg.display_inst(inst)
719 );
720 let ty = func.dfg.value_type(*val);
721 func.dfg.append_user_stack_map_entry(
722 inst,
723 ir::UserStackMapEntry {
724 ty,
725 slot,
726 offset: 0,
727 },
728 );
729 }
730 }
731
732 fn rewrite_use(
741 &mut self,
742 pos: &mut FuncCursor<'_>,
743 val: &mut ir::Value,
744 pointer_type: ir::Type,
745 ) -> bool {
746 debug_assert!(!pos.func.dfg.value_is_alias(*val));
747 if !self.liveness.live_across_any_safepoint.contains(*val) {
748 return false;
749 }
750
751 let old_val = *val;
752 log::trace!("rewriting: found use of {old_val:?}");
753
754 let ty = pos.func.dfg.value_type(*val);
755 let slot = self.stack_slots.get_or_create_stack_slot(pos.func, *val);
756
757 let mut flags = ir::MemFlagsData::trusted();
758 flags.set_notrap();
759 if let Some(make_alias_region) = &self.make_alias_region {
760 if let Some(region) = make_alias_region(&mut pos.func.dfg.alias_regions, ty, slot, 0) {
761 flags.set_alias_region(Some(region));
762 }
763 }
764
765 let addr = pos.ins().stack_addr(pointer_type, slot, 0);
766 *val = pos.ins().load(ty, flags, addr, 0);
767
768 log::trace!(
769 "rewriting: reloading {old_val:?}: {}",
770 pos.func
771 .dfg
772 .display_inst(pos.func.dfg.value_def(*val).unwrap_inst())
773 );
774
775 true
776 }
777
778 fn rewrite(&mut self, func: &mut Function, pointer_type: ir::Type) {
791 let mut vals: SmallVec<[_; 8]> = Default::default();
793
794 for block_index in 0..self.liveness.post_order.len() {
801 let block = self.liveness.post_order[block_index];
802 log::trace!("rewriting: processing {block:?}");
803
804 vals.extend(
810 self.liveness.live_outs[block_index]
811 .iter()
812 .copied()
813 .filter(|val| self.liveness.live_across_any_safepoint.contains(*val)),
814 );
815 vals.sort_unstable();
816 for val in vals.drain(..) {
817 self.stack_slots.get_or_create_stack_slot(func, val);
818 }
819
820 let mut option_inst = func.layout.last_inst(block);
821 while let Some(inst) = option_inst {
822 let mut pos = FuncCursor::new(func).after_inst(inst);
826 vals.extend_from_slice(pos.func.dfg.inst_results(inst));
827 for val in vals.drain(..) {
828 self.rewrite_def(&mut pos, val, pointer_type);
829 }
830
831 if self.liveness.safepoints.contains_key(&inst) {
835 self.rewrite_safepoint(func, inst);
836 }
837
838 let mut pos = FuncCursor::new(func).at_inst(inst);
841 vals.extend(pos.func.dfg.inst_values(inst));
842 let mut replaced_any = false;
843 for val in &mut vals {
844 *val = pos.func.dfg.resolve_aliases(*val);
845 replaced_any |= self.rewrite_use(&mut pos, val, pointer_type);
846 }
847 if replaced_any {
848 pos.func.dfg.overwrite_inst_values(inst, vals.drain(..));
849 log::trace!(
850 "rewriting: updated {inst:?} operands with reloaded values: {}",
851 pos.func.dfg.display_inst(inst)
852 );
853 } else {
854 vals.clear();
855 }
856
857 option_inst = func.layout.prev_inst(inst);
858 }
859
860 let mut pos = FuncCursor::new(func).at_position(CursorPosition::Before(block));
863 pos.next_inst(); vals.clear();
865 vals.extend_from_slice(pos.func.dfg.block_params(block));
866 for val in vals.drain(..) {
867 self.rewrite_def(&mut pos, val, pointer_type);
868 }
869 }
870 }
871}
872
873#[cfg(test)]
874mod tests {
875 use super::*;
876 use alloc::string::ToString;
877 use cranelift_codegen::ir::{BlockCall, ExceptionTableData};
878 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig};
879
880 fn systemv_frontend_config() -> TargetFrontendConfig {
881 TargetFrontendConfig {
882 default_call_conv: CallConv::SystemV,
883 pointer_width: target_lexicon::PointerWidth::U64,
884 page_size_align_log2: 12,
885 }
886 }
887
888 #[test]
889 fn needs_stack_map_and_loop() {
890 let mut sig = Signature::new(CallConv::SystemV);
891 sig.params.push(AbiParam::new(ir::types::I32));
892 sig.params.push(AbiParam::new(ir::types::I32));
893
894 let mut fn_ctx = FunctionBuilderContext::new();
895 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
896 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
897
898 let name = builder
899 .func
900 .declare_imported_user_function(ir::UserExternalName {
901 namespace: 0,
902 index: 0,
903 });
904 let mut sig = Signature::new(CallConv::SystemV);
905 sig.params.push(AbiParam::new(ir::types::I32));
906 let signature = builder.func.import_signature(sig);
907 let func_ref = builder.import_function(ir::ExtFuncData {
908 name: ir::ExternalName::user(name),
909 signature,
910 colocated: true,
911 patchable: false,
912 });
913
914 let block0 = builder.create_block();
922 builder.append_block_params_for_function_params(block0);
923 let a = builder.func.dfg.block_params(block0)[0];
924 let b = builder.func.dfg.block_params(block0)[1];
925 builder.declare_value_needs_stack_map(a);
926 builder.declare_value_needs_stack_map(b);
927 builder.switch_to_block(block0);
928 builder.ins().call(func_ref, &[a]);
929 builder.ins().jump(block0, &[a.into(), b.into()]);
930 builder.seal_all_blocks();
931 builder.finalize(systemv_frontend_config());
932
933 assert_eq_output!(
934 func.display().to_string(),
935 r#"
936function %sample(i32, i32) system_v {
937 ss0 = explicit_slot 4, align = 4
938 ss1 = explicit_slot 4, align = 4
939 sig0 = (i32) system_v
940 fn0 = colocated u0:0 sig0
941
942block0(v0: i32, v1: i32):
943 v8 = stack_addr.i64 ss0
944 store notrap aligned v0, v8
945 v9 = stack_addr.i64 ss1
946 store notrap aligned v1, v9
947 v6 = stack_addr.i64 ss0
948 v7 = load.i32 notrap aligned v6
949 call fn0(v7), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
950 v2 = stack_addr.i64 ss0
951 v3 = load.i32 notrap aligned v2
952 v4 = stack_addr.i64 ss1
953 v5 = load.i32 notrap aligned v4
954 jump block0(v3, v5)
955}
956 "#
957 );
958 }
959
960 #[test]
961 fn needs_stack_map_simple() {
962 let _ = env_logger::try_init();
963
964 let sig = Signature::new(CallConv::SystemV);
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 mut sig = Signature::new(CallConv::SystemV);
977 sig.params.push(AbiParam::new(ir::types::I32));
978 let signature = builder.func.import_signature(sig);
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();
1005 builder.append_block_params_for_function_params(block0);
1006 builder.switch_to_block(block0);
1007 let v0 = builder.ins().iconst(ir::types::I32, 0);
1008 builder.declare_value_needs_stack_map(v0);
1009 let v1 = builder.ins().iconst(ir::types::I32, 1);
1010 builder.declare_value_needs_stack_map(v1);
1011 let v2 = builder.ins().iconst(ir::types::I32, 2);
1012 builder.declare_value_needs_stack_map(v2);
1013 let v3 = builder.ins().iconst(ir::types::I32, 3);
1014 builder.declare_value_needs_stack_map(v3);
1015 builder.ins().call(func_ref, &[v3]);
1016 builder.ins().call(func_ref, &[v0]);
1017 builder.ins().call(func_ref, &[v1]);
1018 builder.ins().call(func_ref, &[v2]);
1019 builder.ins().return_(&[]);
1020 builder.seal_all_blocks();
1021 builder.finalize(systemv_frontend_config());
1022
1023 assert_eq_output!(
1024 func.display().to_string(),
1025 r#"
1026function %sample() system_v {
1027 ss0 = explicit_slot 4, align = 4
1028 ss1 = explicit_slot 4, align = 4
1029 ss2 = explicit_slot 4, align = 4
1030 sig0 = (i32) system_v
1031 fn0 = colocated u0:0 sig0
1032
1033block0:
1034 v0 = iconst.i32 0
1035 v12 = stack_addr.i64 ss2
1036 store notrap aligned v0, v12 ; v0 = 0
1037 v1 = iconst.i32 1
1038 v11 = stack_addr.i64 ss1
1039 store notrap aligned v1, v11 ; v1 = 1
1040 v2 = iconst.i32 2
1041 v10 = stack_addr.i64 ss0
1042 store notrap aligned v2, v10 ; v2 = 2
1043 v3 = iconst.i32 3
1044 call fn0(v3), stack_map=[i32 @ ss2+0, i32 @ ss1+0, i32 @ ss0+0] ; v3 = 3
1045 v8 = stack_addr.i64 ss2
1046 v9 = load.i32 notrap aligned v8
1047 call fn0(v9), stack_map=[i32 @ ss1+0, i32 @ ss0+0]
1048 v6 = stack_addr.i64 ss1
1049 v7 = load.i32 notrap aligned v6
1050 call fn0(v7), stack_map=[i32 @ ss0+0]
1051 v4 = stack_addr.i64 ss0
1052 v5 = load.i32 notrap aligned v4
1053 call fn0(v5)
1054 return
1055}
1056 "#
1057 );
1058 }
1059
1060 #[test]
1061 fn needs_stack_map_and_post_order_early_return() {
1062 let mut sig = Signature::new(CallConv::SystemV);
1063 sig.params.push(AbiParam::new(ir::types::I32));
1064
1065 let mut fn_ctx = FunctionBuilderContext::new();
1066 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1067 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1068
1069 let name = builder
1070 .func
1071 .declare_imported_user_function(ir::UserExternalName {
1072 namespace: 0,
1073 index: 0,
1074 });
1075 let signature = builder
1076 .func
1077 .import_signature(Signature::new(CallConv::SystemV));
1078 let func_ref = builder.import_function(ir::ExtFuncData {
1079 name: ir::ExternalName::user(name),
1080 signature,
1081 colocated: true,
1082 patchable: false,
1083 });
1084
1085 let block0 = builder.create_block();
1108 let block1 = builder.create_block();
1109 let block2 = builder.create_block();
1110 let block3 = builder.create_block();
1111 builder.append_block_params_for_function_params(block0);
1112
1113 builder.switch_to_block(block0);
1114 let v0 = builder.func.dfg.block_params(block0)[0];
1115 builder.ins().brif(v0, block1, &[], block2, &[]);
1116
1117 builder.switch_to_block(block1);
1118 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1119 builder.declare_value_needs_stack_map(v1);
1120 builder.ins().jump(block3, &[]);
1121
1122 builder.switch_to_block(block2);
1123 builder.ins().call(func_ref, &[]);
1124 builder.ins().return_(&[]);
1125
1126 builder.switch_to_block(block3);
1127 builder.ins().iadd_imm_s(v1, 0);
1132 builder.ins().return_(&[]);
1133
1134 builder.seal_all_blocks();
1135 builder.finalize(systemv_frontend_config());
1136
1137 assert_eq_output!(
1138 func.display().to_string(),
1139 r#"
1140function %sample(i32) system_v {
1141 sig0 = () system_v
1142 fn0 = colocated u0:0 sig0
1143
1144block0(v0: i32):
1145 brif v0, block1, block2
1146
1147block1:
1148 v1 = iconst.i64 0x1234_5678
1149 jump block3
1150
1151block2:
1152 call fn0()
1153 return
1154
1155block3:
1156 v2 = iconst.i64 0
1157 v3 = iadd.i64 v1, v2 ; v1 = 0x1234_5678, v2 = 0
1158 return
1159}
1160 "#
1161 );
1162 }
1163
1164 #[test]
1165 fn needs_stack_map_conditional_branches_and_liveness() {
1166 let mut sig = Signature::new(CallConv::SystemV);
1167 sig.params.push(AbiParam::new(ir::types::I32));
1168
1169 let mut fn_ctx = FunctionBuilderContext::new();
1170 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1171 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1172
1173 let name = builder
1174 .func
1175 .declare_imported_user_function(ir::UserExternalName {
1176 namespace: 0,
1177 index: 0,
1178 });
1179 let signature = builder
1180 .func
1181 .import_signature(Signature::new(CallConv::SystemV));
1182 let func_ref = builder.import_function(ir::ExtFuncData {
1183 name: ir::ExternalName::user(name),
1184 signature,
1185 colocated: true,
1186 patchable: false,
1187 });
1188
1189 let block0 = builder.create_block();
1204 let block1 = builder.create_block();
1205 let block2 = builder.create_block();
1206 builder.append_block_params_for_function_params(block0);
1207
1208 builder.switch_to_block(block0);
1209 let v0 = builder.func.dfg.block_params(block0)[0];
1210 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1211 builder.declare_value_needs_stack_map(v1);
1212 builder.ins().brif(v0, block1, &[], block2, &[]);
1213
1214 builder.switch_to_block(block1);
1215 builder.ins().call(func_ref, &[]);
1216 builder.ins().return_(&[]);
1217
1218 builder.switch_to_block(block2);
1219 builder.ins().iadd_imm_s(v1, 0);
1224 builder.ins().return_(&[]);
1225
1226 builder.seal_all_blocks();
1227 builder.finalize(systemv_frontend_config());
1228
1229 assert_eq_output!(
1230 func.display().to_string(),
1231 r#"
1232function %sample(i32) system_v {
1233 sig0 = () system_v
1234 fn0 = colocated u0:0 sig0
1235
1236block0(v0: i32):
1237 v1 = iconst.i64 0x1234_5678
1238 brif v0, block1, block2
1239
1240block1:
1241 call fn0()
1242 return
1243
1244block2:
1245 v2 = iconst.i64 0
1246 v3 = iadd.i64 v1, v2 ; v1 = 0x1234_5678, v2 = 0
1247 return
1248}
1249 "#
1250 );
1251
1252 func.clear();
1256 fn_ctx.clear();
1257
1258 let mut sig = Signature::new(CallConv::SystemV);
1259 sig.params.push(AbiParam::new(ir::types::I32));
1260
1261 func.signature = sig;
1262 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1263
1264 let name = builder
1265 .func
1266 .declare_imported_user_function(ir::UserExternalName {
1267 namespace: 0,
1268 index: 0,
1269 });
1270 let signature = builder
1271 .func
1272 .import_signature(Signature::new(CallConv::SystemV));
1273 let func_ref = builder.import_function(ir::ExtFuncData {
1274 name: ir::ExternalName::user(name),
1275 signature,
1276 colocated: true,
1277 patchable: false,
1278 });
1279
1280 let block0 = builder.create_block();
1281 let block1 = builder.create_block();
1282 let block2 = builder.create_block();
1283 builder.append_block_params_for_function_params(block0);
1284
1285 builder.switch_to_block(block0);
1286 let v0 = builder.func.dfg.block_params(block0)[0];
1287 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1288 builder.declare_value_needs_stack_map(v1);
1289 builder.ins().brif(v0, block1, &[], block2, &[]);
1290
1291 builder.switch_to_block(block1);
1292 builder.ins().iadd_imm_s(v1, 0);
1293 builder.ins().return_(&[]);
1294
1295 builder.switch_to_block(block2);
1296 builder.ins().call(func_ref, &[]);
1297 builder.ins().return_(&[]);
1298
1299 builder.seal_all_blocks();
1300 builder.finalize(systemv_frontend_config());
1301
1302 assert_eq_output!(
1303 func.display().to_string(),
1304 r#"
1305function u0:0(i32) system_v {
1306 sig0 = () system_v
1307 fn0 = colocated u0:0 sig0
1308
1309block0(v0: i32):
1310 v1 = iconst.i64 0x1234_5678
1311 brif v0, block1, block2
1312
1313block1:
1314 v2 = iconst.i64 0
1315 v3 = iadd.i64 v1, v2 ; v1 = 0x1234_5678, v2 = 0
1316 return
1317
1318block2:
1319 call fn0()
1320 return
1321}
1322 "#
1323 );
1324 }
1325
1326 #[test]
1327 fn needs_stack_map_and_tail_calls() {
1328 let mut sig = Signature::new(CallConv::SystemV);
1329 sig.params.push(AbiParam::new(ir::types::I32));
1330
1331 let mut fn_ctx = FunctionBuilderContext::new();
1332 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1333 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1334
1335 let name = builder
1336 .func
1337 .declare_imported_user_function(ir::UserExternalName {
1338 namespace: 0,
1339 index: 0,
1340 });
1341 let signature = builder
1342 .func
1343 .import_signature(Signature::new(CallConv::SystemV));
1344 let func_ref = builder.import_function(ir::ExtFuncData {
1345 name: ir::ExternalName::user(name),
1346 signature,
1347 colocated: true,
1348 patchable: false,
1349 });
1350
1351 let block0 = builder.create_block();
1366 let block1 = builder.create_block();
1367 let block2 = builder.create_block();
1368 builder.append_block_params_for_function_params(block0);
1369
1370 builder.switch_to_block(block0);
1371 let v0 = builder.func.dfg.block_params(block0)[0];
1372 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1373 builder.declare_value_needs_stack_map(v1);
1374 builder.ins().brif(v0, block1, &[], block2, &[]);
1375
1376 builder.switch_to_block(block1);
1377 builder.ins().return_call(func_ref, &[]);
1378
1379 builder.switch_to_block(block2);
1380 builder.ins().iadd_imm_s(v1, 0);
1385 builder.ins().return_(&[]);
1386
1387 builder.seal_all_blocks();
1388 builder.finalize(systemv_frontend_config());
1389
1390 assert_eq_output!(
1391 func.display().to_string(),
1392 r#"
1393function %sample(i32) system_v {
1394 sig0 = () system_v
1395 fn0 = colocated u0:0 sig0
1396
1397block0(v0: i32):
1398 v1 = iconst.i64 0x1234_5678
1399 brif v0, block1, block2
1400
1401block1:
1402 return_call fn0()
1403
1404block2:
1405 v2 = iconst.i64 0
1406 v3 = iadd.i64 v1, v2 ; v1 = 0x1234_5678, v2 = 0
1407 return
1408}
1409 "#
1410 );
1411
1412 func.clear();
1416 fn_ctx.clear();
1417
1418 let mut sig = Signature::new(CallConv::SystemV);
1419 sig.params.push(AbiParam::new(ir::types::I32));
1420 func.signature = sig;
1421
1422 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1423
1424 let name = builder
1425 .func
1426 .declare_imported_user_function(ir::UserExternalName {
1427 namespace: 0,
1428 index: 0,
1429 });
1430 let signature = builder
1431 .func
1432 .import_signature(Signature::new(CallConv::SystemV));
1433 let func_ref = builder.import_function(ir::ExtFuncData {
1434 name: ir::ExternalName::user(name),
1435 signature,
1436 colocated: true,
1437 patchable: false,
1438 });
1439
1440 let block0 = builder.create_block();
1441 let block1 = builder.create_block();
1442 let block2 = builder.create_block();
1443 builder.append_block_params_for_function_params(block0);
1444
1445 builder.switch_to_block(block0);
1446 let v0 = builder.func.dfg.block_params(block0)[0];
1447 let v1 = builder.ins().iconst(ir::types::I64, 0x12345678);
1448 builder.declare_value_needs_stack_map(v1);
1449 builder.ins().brif(v0, block1, &[], block2, &[]);
1450
1451 builder.switch_to_block(block1);
1452 builder.ins().iadd_imm_s(v1, 0);
1453 builder.ins().return_(&[]);
1454
1455 builder.switch_to_block(block2);
1456 builder.ins().return_call(func_ref, &[]);
1457
1458 builder.seal_all_blocks();
1459 builder.finalize(systemv_frontend_config());
1460
1461 assert_eq_output!(
1462 func.display().to_string(),
1463 r#"
1464function u0:0(i32) system_v {
1465 sig0 = () system_v
1466 fn0 = colocated u0:0 sig0
1467
1468block0(v0: i32):
1469 v1 = iconst.i64 0x1234_5678
1470 brif v0, block1, block2
1471
1472block1:
1473 v2 = iconst.i64 0
1474 v3 = iadd.i64 v1, v2 ; v1 = 0x1234_5678, v2 = 0
1475 return
1476
1477block2:
1478 return_call fn0()
1479}
1480 "#
1481 );
1482 }
1483
1484 #[test]
1485 fn needs_stack_map_and_cfg_diamond() {
1486 let mut sig = Signature::new(CallConv::SystemV);
1487 sig.params.push(AbiParam::new(ir::types::I32));
1488
1489 let mut fn_ctx = FunctionBuilderContext::new();
1490 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1491 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1492
1493 let name = builder
1494 .func
1495 .declare_imported_user_function(ir::UserExternalName {
1496 namespace: 0,
1497 index: 0,
1498 });
1499 let signature = builder
1500 .func
1501 .import_signature(Signature::new(CallConv::SystemV));
1502 let func_ref = builder.import_function(ir::ExtFuncData {
1503 name: ir::ExternalName::user(name),
1504 signature,
1505 colocated: true,
1506 patchable: false,
1507 });
1508
1509 let block0 = builder.create_block();
1531 let block1 = builder.create_block();
1532 let block2 = builder.create_block();
1533 let block3 = builder.create_block();
1534 builder.append_block_params_for_function_params(block0);
1535
1536 builder.switch_to_block(block0);
1537 let v0 = builder.func.dfg.block_params(block0)[0];
1538 builder.ins().brif(v0, block1, &[], block2, &[]);
1539
1540 builder.switch_to_block(block1);
1541 let v1 = builder.ins().iconst(ir::types::I64, 1);
1542 builder.declare_value_needs_stack_map(v1);
1543 let v2 = builder.ins().iconst(ir::types::I64, 2);
1544 builder.declare_value_needs_stack_map(v2);
1545 builder.ins().call(func_ref, &[]);
1546 builder.ins().jump(block3, &[v1.into(), v2.into()]);
1547
1548 builder.switch_to_block(block2);
1549 let v3 = builder.ins().iconst(ir::types::I64, 3);
1550 builder.declare_value_needs_stack_map(v3);
1551 let v4 = builder.ins().iconst(ir::types::I64, 4);
1552 builder.declare_value_needs_stack_map(v4);
1553 builder.ins().call(func_ref, &[]);
1554 builder.ins().jump(block3, &[v3.into(), v3.into()]);
1555
1556 builder.switch_to_block(block3);
1557 builder.append_block_param(block3, ir::types::I64);
1558 builder.append_block_param(block3, ir::types::I64);
1559 builder.ins().call(func_ref, &[]);
1560 builder.ins().iadd_imm_s(v1, 0);
1565 builder.ins().return_(&[]);
1566
1567 builder.seal_all_blocks();
1568 builder.finalize(systemv_frontend_config());
1569
1570 assert_eq_output!(
1571 func.display().to_string(),
1572 r#"
1573function %sample(i32) system_v {
1574 ss0 = explicit_slot 8, align = 8
1575 ss1 = explicit_slot 8, align = 8
1576 sig0 = () system_v
1577 fn0 = colocated u0:0 sig0
1578
1579block0(v0: i32):
1580 brif v0, block1, block2
1581
1582block1:
1583 v1 = iconst.i64 1
1584 v16 = stack_addr.i64 ss0
1585 store notrap aligned v1, v16 ; v1 = 1
1586 v2 = iconst.i64 2
1587 v15 = stack_addr.i64 ss1
1588 store notrap aligned v2, v15 ; v2 = 2
1589 call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss1+0]
1590 v11 = stack_addr.i64 ss0
1591 v12 = load.i64 notrap aligned v11
1592 v13 = stack_addr.i64 ss1
1593 v14 = load.i64 notrap aligned v13
1594 jump block3(v12, v14)
1595
1596block2:
1597 v3 = iconst.i64 3
1598 v21 = stack_addr.i64 ss0
1599 store notrap aligned v3, v21 ; v3 = 3
1600 v4 = iconst.i64 4
1601 call fn0(), stack_map=[i64 @ ss0+0, i64 @ ss0+0]
1602 v17 = stack_addr.i64 ss0
1603 v18 = load.i64 notrap aligned v17
1604 v19 = stack_addr.i64 ss0
1605 v20 = load.i64 notrap aligned v19
1606 jump block3(v18, v20)
1607
1608block3(v5: i64, v6: i64):
1609 call fn0(), stack_map=[i64 @ ss0+0]
1610 v7 = iconst.i64 0
1611 v9 = stack_addr.i64 ss0
1612 v10 = load.i64 notrap aligned v9
1613 v8 = iadd v10, v7 ; v7 = 0
1614 return
1615}
1616 "#
1617 );
1618 }
1619
1620 #[test]
1621 fn needs_stack_map_and_heterogeneous_types() {
1622 let _ = env_logger::try_init();
1623
1624 let mut sig = Signature::new(CallConv::SystemV);
1625 for ty in [
1626 ir::types::I8,
1627 ir::types::I16,
1628 ir::types::I32,
1629 ir::types::I64,
1630 ir::types::I128,
1631 ir::types::F32,
1632 ir::types::F64,
1633 ir::types::I8X16,
1634 ir::types::I16X8,
1635 ] {
1636 sig.params.push(AbiParam::new(ty));
1637 sig.returns.push(AbiParam::new(ty));
1638 }
1639
1640 let mut fn_ctx = FunctionBuilderContext::new();
1641 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1642 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1643
1644 let name = builder
1645 .func
1646 .declare_imported_user_function(ir::UserExternalName {
1647 namespace: 0,
1648 index: 0,
1649 });
1650 let signature = builder
1651 .func
1652 .import_signature(Signature::new(CallConv::SystemV));
1653 let func_ref = builder.import_function(ir::ExtFuncData {
1654 name: ir::ExternalName::user(name),
1655 signature,
1656 colocated: true,
1657 patchable: false,
1658 });
1659
1660 let block0 = builder.create_block();
1667 builder.append_block_params_for_function_params(block0);
1668
1669 builder.switch_to_block(block0);
1670 let params = builder.func.dfg.block_params(block0).to_vec();
1671 for val in ¶ms {
1672 builder.declare_value_needs_stack_map(*val);
1673 }
1674 builder.ins().call(func_ref, &[]);
1675 builder.ins().return_(¶ms);
1676
1677 builder.seal_all_blocks();
1678 builder.finalize(systemv_frontend_config());
1679
1680 assert_eq_output!(
1681 func.display().to_string(),
1682 r#"
1683function %sample(i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8) -> i8, i16, i32, i64, i128, f32, f64, i8x16, i16x8 system_v {
1684 ss0 = explicit_slot 1
1685 ss1 = explicit_slot 2, align = 2
1686 ss2 = explicit_slot 4, align = 4
1687 ss3 = explicit_slot 8, align = 8
1688 ss4 = explicit_slot 16, align = 16
1689 ss5 = explicit_slot 4, align = 4
1690 ss6 = explicit_slot 8, align = 8
1691 ss7 = explicit_slot 16, align = 16
1692 ss8 = explicit_slot 16, align = 16
1693 sig0 = () system_v
1694 fn0 = colocated u0:0 sig0
1695
1696block0(v0: i8, v1: i16, v2: i32, v3: i64, v4: i128, v5: f32, v6: f64, v7: i8x16, v8: i16x8):
1697 v27 = stack_addr.i64 ss0
1698 store notrap aligned v0, v27
1699 v28 = stack_addr.i64 ss1
1700 store notrap aligned v1, v28
1701 v29 = stack_addr.i64 ss2
1702 store notrap aligned v2, v29
1703 v30 = stack_addr.i64 ss3
1704 store notrap aligned v3, v30
1705 v31 = stack_addr.i64 ss4
1706 store notrap aligned v4, v31
1707 v32 = stack_addr.i64 ss5
1708 store notrap aligned v5, v32
1709 v33 = stack_addr.i64 ss6
1710 store notrap aligned v6, v33
1711 v34 = stack_addr.i64 ss7
1712 store notrap aligned v7, v34
1713 v35 = stack_addr.i64 ss8
1714 store notrap aligned v8, v35
1715 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]
1716 v9 = stack_addr.i64 ss0
1717 v10 = load.i8 notrap aligned v9
1718 v11 = stack_addr.i64 ss1
1719 v12 = load.i16 notrap aligned v11
1720 v13 = stack_addr.i64 ss2
1721 v14 = load.i32 notrap aligned v13
1722 v15 = stack_addr.i64 ss3
1723 v16 = load.i64 notrap aligned v15
1724 v17 = stack_addr.i64 ss4
1725 v18 = load.i128 notrap aligned v17
1726 v19 = stack_addr.i64 ss5
1727 v20 = load.f32 notrap aligned v19
1728 v21 = stack_addr.i64 ss6
1729 v22 = load.f64 notrap aligned v21
1730 v23 = stack_addr.i64 ss7
1731 v24 = load.i8x16 notrap aligned v23
1732 v25 = stack_addr.i64 ss8
1733 v26 = load.i16x8 notrap aligned v25
1734 return v10, v12, v14, v16, v18, v20, v22, v24, v26
1735}
1736 "#
1737 );
1738 }
1739
1740 #[test]
1741 fn series_of_non_overlapping_live_ranges_needs_stack_map() {
1742 let sig = Signature::new(CallConv::SystemV);
1743
1744 let mut fn_ctx = FunctionBuilderContext::new();
1745 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1746 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1747
1748 let name = builder
1749 .func
1750 .declare_imported_user_function(ir::UserExternalName {
1751 namespace: 0,
1752 index: 0,
1753 });
1754 let signature = builder
1755 .func
1756 .import_signature(Signature::new(CallConv::SystemV));
1757 let foo_func_ref = builder.import_function(ir::ExtFuncData {
1758 name: ir::ExternalName::user(name),
1759 signature,
1760 colocated: true,
1761 patchable: false,
1762 });
1763
1764 let name = builder
1765 .func
1766 .declare_imported_user_function(ir::UserExternalName {
1767 namespace: 0,
1768 index: 1,
1769 });
1770 let mut sig = Signature::new(CallConv::SystemV);
1771 sig.params.push(AbiParam::new(ir::types::I32));
1772 let signature = builder.func.import_signature(sig);
1773 let consume_func_ref = builder.import_function(ir::ExtFuncData {
1774 name: ir::ExternalName::user(name),
1775 signature,
1776 colocated: true,
1777 patchable: false,
1778 });
1779
1780 let block0 = builder.create_block();
1799 builder.append_block_params_for_function_params(block0);
1800 builder.switch_to_block(block0);
1801 let v0 = builder.ins().iconst(ir::types::I32, 0);
1802 builder.declare_value_needs_stack_map(v0);
1803 builder.ins().call(foo_func_ref, &[]);
1804 builder.ins().call(consume_func_ref, &[v0]);
1805 let v1 = builder.ins().iconst(ir::types::I32, 1);
1806 builder.declare_value_needs_stack_map(v1);
1807 builder.ins().call(foo_func_ref, &[]);
1808 builder.ins().call(consume_func_ref, &[v1]);
1809 let v2 = builder.ins().iconst(ir::types::I32, 2);
1810 builder.declare_value_needs_stack_map(v2);
1811 builder.ins().call(foo_func_ref, &[]);
1812 builder.ins().call(consume_func_ref, &[v2]);
1813 let v3 = builder.ins().iconst(ir::types::I32, 3);
1814 builder.declare_value_needs_stack_map(v3);
1815 builder.ins().call(foo_func_ref, &[]);
1816 builder.ins().call(consume_func_ref, &[v3]);
1817 builder.ins().return_(&[]);
1818 builder.seal_all_blocks();
1819 builder.finalize(systemv_frontend_config());
1820
1821 assert_eq_output!(
1822 func.display().to_string(),
1823 r#"
1824function %sample() system_v {
1825 ss0 = explicit_slot 4, align = 4
1826 sig0 = () system_v
1827 sig1 = (i32) system_v
1828 fn0 = colocated u0:0 sig0
1829 fn1 = colocated u0:1 sig1
1830
1831block0:
1832 v0 = iconst.i32 0
1833 v15 = stack_addr.i64 ss0
1834 store notrap aligned v0, v15 ; v0 = 0
1835 call fn0(), stack_map=[i32 @ ss0+0]
1836 v13 = stack_addr.i64 ss0
1837 v14 = load.i32 notrap aligned v13
1838 call fn1(v14)
1839 v1 = iconst.i32 1
1840 v12 = stack_addr.i64 ss0
1841 store notrap aligned v1, v12 ; v1 = 1
1842 call fn0(), stack_map=[i32 @ ss0+0]
1843 v10 = stack_addr.i64 ss0
1844 v11 = load.i32 notrap aligned v10
1845 call fn1(v11)
1846 v2 = iconst.i32 2
1847 v9 = stack_addr.i64 ss0
1848 store notrap aligned v2, v9 ; v2 = 2
1849 call fn0(), stack_map=[i32 @ ss0+0]
1850 v7 = stack_addr.i64 ss0
1851 v8 = load.i32 notrap aligned v7
1852 call fn1(v8)
1853 v3 = iconst.i32 3
1854 v6 = stack_addr.i64 ss0
1855 store notrap aligned v3, v6 ; v3 = 3
1856 call fn0(), stack_map=[i32 @ ss0+0]
1857 v4 = stack_addr.i64 ss0
1858 v5 = load.i32 notrap aligned v4
1859 call fn1(v5)
1860 return
1861}
1862 "#
1863 );
1864 }
1865
1866 #[test]
1867 fn vars_block_params_and_needs_stack_map() {
1868 let _ = env_logger::try_init();
1869
1870 let mut sig = Signature::new(CallConv::SystemV);
1871 sig.params.push(AbiParam::new(ir::types::I32));
1872 sig.returns.push(AbiParam::new(ir::types::I32));
1873
1874 let mut fn_ctx = FunctionBuilderContext::new();
1875 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
1876 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1877
1878 let name = builder
1879 .func
1880 .declare_imported_user_function(ir::UserExternalName {
1881 namespace: 0,
1882 index: 0,
1883 });
1884 let mut sig = Signature::new(CallConv::SystemV);
1885 sig.params.push(AbiParam::new(ir::types::I32));
1886 let signature = builder.func.import_signature(sig);
1887 let func_ref = builder.import_function(ir::ExtFuncData {
1888 name: ir::ExternalName::user(name),
1889 signature,
1890 colocated: true,
1891 patchable: false,
1892 });
1893
1894 let x = builder.declare_var(ir::types::I32);
1919 builder.declare_var_needs_stack_map(x);
1920
1921 let block0 = builder.create_block();
1922 let block1 = builder.create_block();
1923 let block2 = builder.create_block();
1924 let block3 = builder.create_block();
1925
1926 builder.append_block_params_for_function_params(block0);
1927 builder.switch_to_block(block0);
1928 let v0 = builder.func.dfg.block_params(block0)[0];
1929 let val = builder.ins().iconst(ir::types::I32, 42);
1930 builder.def_var(x, val);
1931 {
1932 let x = builder.use_var(x);
1933 builder.ins().call(func_ref, &[x]);
1934 }
1935 builder.ins().brif(v0, block1, &[], block2, &[]);
1936
1937 builder.switch_to_block(block1);
1938 {
1939 let x = builder.use_var(x);
1940 builder.ins().call(func_ref, &[x]);
1941 builder.ins().call(func_ref, &[x]);
1942 }
1943 let val = builder.ins().iconst(ir::types::I32, 36);
1944 builder.def_var(x, val);
1945 {
1946 let x = builder.use_var(x);
1947 builder.ins().call(func_ref, &[x]);
1948 }
1949 builder.ins().jump(block3, &[]);
1950
1951 builder.switch_to_block(block2);
1952 {
1953 let x = builder.use_var(x);
1954 builder.ins().call(func_ref, &[x]);
1955 builder.ins().call(func_ref, &[x]);
1956 }
1957 let val = builder.ins().iconst(ir::types::I32, 36);
1958 builder.def_var(x, val);
1959 {
1960 let x = builder.use_var(x);
1961 builder.ins().call(func_ref, &[x]);
1962 }
1963 builder.ins().jump(block3, &[]);
1964
1965 builder.switch_to_block(block3);
1966 let x = builder.use_var(x);
1967 builder.ins().call(func_ref, &[x]);
1968 builder.ins().return_(&[x]);
1969
1970 builder.seal_all_blocks();
1971 builder.finalize(systemv_frontend_config());
1972
1973 assert_eq_output!(
1974 func.display().to_string(),
1975 r#"
1976function %sample(i32) -> i32 system_v {
1977 ss0 = explicit_slot 4, align = 4
1978 ss1 = explicit_slot 4, align = 4
1979 sig0 = (i32) system_v
1980 fn0 = colocated u0:0 sig0
1981
1982block0(v0: i32):
1983 v1 = iconst.i32 42
1984 v2 -> v1
1985 v4 -> v1
1986 v32 = stack_addr.i64 ss0
1987 store notrap aligned v1, v32 ; v1 = 42
1988 v30 = stack_addr.i64 ss0
1989 v31 = load.i32 notrap aligned v30
1990 call fn0(v31), stack_map=[i32 @ ss0+0]
1991 brif v0, block1, block2
1992
1993block1:
1994 v19 = stack_addr.i64 ss0
1995 v20 = load.i32 notrap aligned v19
1996 call fn0(v20), stack_map=[i32 @ ss0+0]
1997 v17 = stack_addr.i64 ss0
1998 v18 = load.i32 notrap aligned v17
1999 call fn0(v18)
2000 v3 = iconst.i32 36
2001 v16 = stack_addr.i64 ss0
2002 store notrap aligned v3, v16 ; v3 = 36
2003 v14 = stack_addr.i64 ss0
2004 v15 = load.i32 notrap aligned v14
2005 call fn0(v15), stack_map=[i32 @ ss0+0]
2006 v12 = stack_addr.i64 ss0
2007 v13 = load.i32 notrap aligned v12
2008 jump block3(v13)
2009
2010block2:
2011 v28 = stack_addr.i64 ss0
2012 v29 = load.i32 notrap aligned v28
2013 call fn0(v29), stack_map=[i32 @ ss0+0]
2014 v26 = stack_addr.i64 ss0
2015 v27 = load.i32 notrap aligned v26
2016 call fn0(v27)
2017 v5 = iconst.i32 36
2018 v25 = stack_addr.i64 ss1
2019 store notrap aligned v5, v25 ; v5 = 36
2020 v23 = stack_addr.i64 ss1
2021 v24 = load.i32 notrap aligned v23
2022 call fn0(v24), stack_map=[i32 @ ss1+0]
2023 v21 = stack_addr.i64 ss1
2024 v22 = load.i32 notrap aligned v21
2025 jump block3(v22)
2026
2027block3(v6: i32):
2028 v11 = stack_addr.i64 ss0
2029 store notrap aligned v6, v11
2030 v9 = stack_addr.i64 ss0
2031 v10 = load.i32 notrap aligned v9
2032 call fn0(v10), stack_map=[i32 @ ss0+0]
2033 v7 = stack_addr.i64 ss0
2034 v8 = load.i32 notrap aligned v7
2035 return v8
2036}
2037 "#
2038 );
2039 }
2040
2041 #[test]
2042 fn var_needs_stack_map() {
2043 let mut sig = Signature::new(CallConv::SystemV);
2044 sig.params
2045 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
2046 sig.returns
2047 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
2048
2049 let mut fn_ctx = FunctionBuilderContext::new();
2050 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2051 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2052
2053 let var = builder.declare_var(cranelift_codegen::ir::types::I32);
2054 builder.declare_var_needs_stack_map(var);
2055
2056 let name = builder
2057 .func
2058 .declare_imported_user_function(ir::UserExternalName {
2059 namespace: 0,
2060 index: 0,
2061 });
2062 let signature = builder
2063 .func
2064 .import_signature(Signature::new(CallConv::SystemV));
2065 let func_ref = builder.import_function(ir::ExtFuncData {
2066 name: ir::ExternalName::user(name),
2067 signature,
2068 colocated: true,
2069 patchable: false,
2070 });
2071
2072 let block0 = builder.create_block();
2073 builder.append_block_params_for_function_params(block0);
2074 builder.switch_to_block(block0);
2075
2076 let arg = builder.func.dfg.block_params(block0)[0];
2077 builder.def_var(var, arg);
2078
2079 builder.ins().call(func_ref, &[]);
2080
2081 let val = builder.use_var(var);
2082 builder.ins().return_(&[val]);
2083
2084 builder.seal_all_blocks();
2085 builder.finalize(systemv_frontend_config());
2086
2087 assert_eq_output!(
2088 func.display().to_string(),
2089 r#"
2090function %sample(i32) -> i32 system_v {
2091 ss0 = explicit_slot 4, align = 4
2092 sig0 = () system_v
2093 fn0 = colocated u0:0 sig0
2094
2095block0(v0: i32):
2096 v3 = stack_addr.i64 ss0
2097 store notrap aligned v0, v3
2098 call fn0(), stack_map=[i32 @ ss0+0]
2099 v1 = stack_addr.i64 ss0
2100 v2 = load.i32 notrap aligned v1
2101 return v2
2102}
2103 "#
2104 );
2105 }
2106
2107 #[test]
2108 fn first_inst_defines_needs_stack_map() {
2109 let mut sig = Signature::new(CallConv::SystemV);
2110 sig.params
2111 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
2112 sig.returns
2113 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
2114 sig.returns
2115 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
2116
2117 let mut fn_ctx = FunctionBuilderContext::new();
2118 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2119 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2120
2121 let name = builder
2122 .func
2123 .declare_imported_user_function(ir::UserExternalName {
2124 namespace: 0,
2125 index: 0,
2126 });
2127 let signature = builder
2128 .func
2129 .import_signature(Signature::new(CallConv::SystemV));
2130 let func_ref = builder.import_function(ir::ExtFuncData {
2131 name: ir::ExternalName::user(name),
2132 signature,
2133 colocated: true,
2134 patchable: false,
2135 });
2136
2137 let block0 = builder.create_block();
2149 builder.append_block_params_for_function_params(block0);
2150 builder.switch_to_block(block0);
2151
2152 let arg = builder.func.dfg.block_params(block0)[0];
2153 builder.declare_value_needs_stack_map(arg);
2154
2155 let val = builder.ins().iconst(ir::types::I32, 42);
2156 builder.declare_value_needs_stack_map(val);
2157
2158 builder.ins().call(func_ref, &[]);
2159
2160 builder.ins().return_(&[arg, val]);
2161
2162 builder.seal_all_blocks();
2163 builder.finalize(systemv_frontend_config());
2164
2165 assert_eq_output!(
2166 func.display().to_string(),
2167 r#"
2168function %sample(i32) -> i32, i32 system_v {
2169 ss0 = explicit_slot 4, align = 4
2170 ss1 = explicit_slot 4, align = 4
2171 sig0 = () system_v
2172 fn0 = colocated u0:0 sig0
2173
2174block0(v0: i32):
2175 v7 = stack_addr.i64 ss0
2176 store notrap aligned v0, v7
2177 v1 = iconst.i32 42
2178 v6 = stack_addr.i64 ss1
2179 store notrap aligned v1, v6 ; v1 = 42
2180 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
2181 v2 = stack_addr.i64 ss0
2182 v3 = load.i32 notrap aligned v2
2183 v4 = stack_addr.i64 ss1
2184 v5 = load.i32 notrap aligned v4
2185 return v3, v5
2186}
2187 "#
2188 );
2189 }
2190
2191 #[test]
2192 fn needs_stack_map_and_loops_and_partially_live_values() {
2193 let _ = env_logger::try_init();
2194
2195 let mut sig = Signature::new(CallConv::SystemV);
2196 sig.params.push(AbiParam::new(ir::types::I32));
2197
2198 let mut fn_ctx = FunctionBuilderContext::new();
2199 let mut func =
2200 Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig.clone());
2201 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2202
2203 let name = builder
2204 .func
2205 .declare_imported_user_function(ir::UserExternalName {
2206 namespace: 0,
2207 index: 0,
2208 });
2209 let signature = builder
2210 .func
2211 .import_signature(Signature::new(CallConv::SystemV));
2212 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2213 name: ir::ExternalName::user(name),
2214 signature,
2215 colocated: true,
2216 patchable: false,
2217 });
2218
2219 let name = builder
2220 .func
2221 .declare_imported_user_function(ir::UserExternalName {
2222 namespace: 1,
2223 index: 1,
2224 });
2225 let signature = builder.func.import_signature(sig);
2226 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2227 name: ir::ExternalName::user(name),
2228 signature,
2229 colocated: true,
2230 patchable: false,
2231 });
2232
2233 let block0 = builder.create_block();
2250 let block1 = builder.create_block();
2251 builder.append_block_params_for_function_params(block0);
2252
2253 builder.switch_to_block(block0);
2254 builder.ins().jump(block1, &[]);
2255
2256 builder.switch_to_block(block1);
2257 let v0 = builder.func.dfg.block_params(block0)[0];
2258 builder.declare_value_needs_stack_map(v0);
2259 builder.ins().call(foo_func_ref, &[]);
2260 builder.ins().call(bar_func_ref, &[v0]);
2261 builder.ins().call(foo_func_ref, &[]);
2262 builder.ins().jump(block1, &[]);
2263
2264 builder.seal_all_blocks();
2265 builder.finalize(systemv_frontend_config());
2266
2267 assert_eq_output!(
2268 func.display().to_string(),
2269 r#"
2270function %sample(i32) system_v {
2271 ss0 = explicit_slot 4, align = 4
2272 sig0 = () system_v
2273 sig1 = (i32) system_v
2274 fn0 = colocated u0:0 sig0
2275 fn1 = colocated u1:1 sig1
2276
2277block0(v0: i32):
2278 v3 = stack_addr.i64 ss0
2279 store notrap aligned v0, v3
2280 jump block1
2281
2282block1:
2283 call fn0(), stack_map=[i32 @ ss0+0]
2284 v1 = stack_addr.i64 ss0
2285 v2 = load.i32 notrap aligned v1
2286 call fn1(v2), stack_map=[i32 @ ss0+0]
2287 call fn0(), stack_map=[i32 @ ss0+0]
2288 jump block1
2289}
2290 "#,
2291 );
2292 }
2293
2294 #[test]
2295 fn needs_stack_map_and_irreducible_loops() {
2296 let _ = env_logger::try_init();
2297
2298 let mut sig = Signature::new(CallConv::SystemV);
2299 sig.params.push(AbiParam::new(ir::types::I32));
2300 sig.params.push(AbiParam::new(ir::types::I32));
2301
2302 let mut fn_ctx = FunctionBuilderContext::new();
2303 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2304 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2305
2306 let name = builder
2307 .func
2308 .declare_imported_user_function(ir::UserExternalName {
2309 namespace: 0,
2310 index: 0,
2311 });
2312 let signature = builder
2313 .func
2314 .import_signature(Signature::new(CallConv::SystemV));
2315 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2316 name: ir::ExternalName::user(name),
2317 signature,
2318 colocated: true,
2319 patchable: false,
2320 });
2321
2322 let name = builder
2323 .func
2324 .declare_imported_user_function(ir::UserExternalName {
2325 namespace: 1,
2326 index: 1,
2327 });
2328 let mut sig = Signature::new(CallConv::SystemV);
2329 sig.params.push(AbiParam::new(ir::types::I32));
2330 let signature = builder.func.import_signature(sig);
2331 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2332 name: ir::ExternalName::user(name),
2333 signature,
2334 colocated: true,
2335 patchable: false,
2336 });
2337
2338 let block0 = builder.create_block();
2362 let block1 = builder.create_block();
2363 let block2 = builder.create_block();
2364 let block3 = builder.create_block();
2365 let block4 = builder.create_block();
2366 builder.append_block_params_for_function_params(block0);
2367
2368 builder.switch_to_block(block0);
2369 let v0 = builder.func.dfg.block_params(block0)[0];
2370 let v1 = builder.func.dfg.block_params(block0)[1];
2371 builder.declare_value_needs_stack_map(v1);
2372 builder.ins().brif(v0, block1, &[], block2, &[]);
2373
2374 builder.switch_to_block(block1);
2375 builder.ins().jump(block3, &[]);
2376
2377 builder.switch_to_block(block2);
2378 builder.ins().jump(block4, &[]);
2379
2380 builder.switch_to_block(block3);
2381 builder.ins().call(foo_func_ref, &[]);
2382 builder.ins().call(bar_func_ref, &[v1]);
2383 builder.ins().call(foo_func_ref, &[]);
2384 builder.ins().jump(block2, &[]);
2385
2386 builder.switch_to_block(block4);
2387 builder.ins().call(foo_func_ref, &[]);
2388 builder.ins().call(bar_func_ref, &[v1]);
2389 builder.ins().call(foo_func_ref, &[]);
2390 builder.ins().jump(block1, &[]);
2391
2392 builder.seal_all_blocks();
2393 builder.finalize(systemv_frontend_config());
2394
2395 assert_eq_output!(
2396 func.display().to_string(),
2397 r#"
2398function %sample(i32, i32) system_v {
2399 ss0 = explicit_slot 4, align = 4
2400 sig0 = () system_v
2401 sig1 = (i32) system_v
2402 fn0 = colocated u0:0 sig0
2403 fn1 = colocated u1:1 sig1
2404
2405block0(v0: i32, v1: i32):
2406 v6 = stack_addr.i64 ss0
2407 store notrap aligned v1, v6
2408 brif v0, block1, block2
2409
2410block1:
2411 jump block3
2412
2413block2:
2414 jump block4
2415
2416block3:
2417 call fn0(), stack_map=[i32 @ ss0+0]
2418 v4 = stack_addr.i64 ss0
2419 v5 = load.i32 notrap aligned v4
2420 call fn1(v5), stack_map=[i32 @ ss0+0]
2421 call fn0(), stack_map=[i32 @ ss0+0]
2422 jump block2
2423
2424block4:
2425 call fn0(), stack_map=[i32 @ ss0+0]
2426 v2 = stack_addr.i64 ss0
2427 v3 = load.i32 notrap aligned v2
2428 call fn1(v3), stack_map=[i32 @ ss0+0]
2429 call fn0(), stack_map=[i32 @ ss0+0]
2430 jump block1
2431}
2432 "#,
2433 );
2434 }
2435
2436 #[test]
2437 fn needs_stack_map_and_back_edge_to_back_edge() {
2438 let _ = env_logger::try_init();
2439
2440 let mut sig = Signature::new(CallConv::SystemV);
2441 sig.params.push(AbiParam::new(ir::types::I32));
2442 sig.params.push(AbiParam::new(ir::types::I32));
2443 sig.params.push(AbiParam::new(ir::types::I32));
2444 sig.params.push(AbiParam::new(ir::types::I32));
2445
2446 let mut fn_ctx = FunctionBuilderContext::new();
2447 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2448 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2449
2450 let name = builder
2451 .func
2452 .declare_imported_user_function(ir::UserExternalName {
2453 namespace: 0,
2454 index: 0,
2455 });
2456 let signature = builder
2457 .func
2458 .import_signature(Signature::new(CallConv::SystemV));
2459 let foo_func_ref = builder.import_function(ir::ExtFuncData {
2460 name: ir::ExternalName::user(name),
2461 signature,
2462 colocated: true,
2463 patchable: false,
2464 });
2465
2466 let name = builder
2467 .func
2468 .declare_imported_user_function(ir::UserExternalName {
2469 namespace: 1,
2470 index: 1,
2471 });
2472 let mut sig = Signature::new(CallConv::SystemV);
2473 sig.params.push(AbiParam::new(ir::types::I32));
2474 let signature = builder.func.import_signature(sig);
2475 let bar_func_ref = builder.import_function(ir::ExtFuncData {
2476 name: ir::ExternalName::user(name),
2477 signature,
2478 colocated: true,
2479 patchable: false,
2480 });
2481
2482 let block0 = builder.create_block();
2509 let block1 = builder.create_block();
2510 let block2 = builder.create_block();
2511 let block3 = builder.create_block();
2512
2513 builder.append_block_params_for_function_params(block0);
2514
2515 builder.switch_to_block(block0);
2516
2517 let v0 = builder.func.dfg.block_params(block0)[0];
2518 builder.declare_value_needs_stack_map(v0);
2519 let v1 = builder.func.dfg.block_params(block0)[1];
2520 builder.declare_value_needs_stack_map(v1);
2521 let v2 = builder.func.dfg.block_params(block0)[2];
2522 builder.declare_value_needs_stack_map(v2);
2523 let v3 = builder.func.dfg.block_params(block0)[3];
2524
2525 builder.ins().jump(block1, &[v3.into()]);
2526
2527 builder.switch_to_block(block1);
2528 let v4 = builder.append_block_param(block1, ir::types::I32);
2529 builder.ins().call(foo_func_ref, &[]);
2530 builder.ins().call(bar_func_ref, &[v0]);
2531 builder.ins().call(foo_func_ref, &[]);
2532 builder.ins().jump(block2, &[]);
2533
2534 builder.switch_to_block(block2);
2535 builder.ins().call(foo_func_ref, &[]);
2536 builder.ins().call(bar_func_ref, &[v1]);
2537 builder.ins().call(foo_func_ref, &[]);
2538 let v5 = builder.ins().iadd_imm_s(v4, -1);
2539 builder.ins().brif(v4, block1, &[v5.into()], block3, &[]);
2540
2541 builder.switch_to_block(block3);
2542 builder.ins().call(foo_func_ref, &[]);
2543 builder.ins().call(bar_func_ref, &[v2]);
2544 builder.ins().call(foo_func_ref, &[]);
2545 builder.ins().jump(block2, &[]);
2546
2547 builder.seal_all_blocks();
2548 builder.finalize(systemv_frontend_config());
2549
2550 assert_eq_output!(
2551 func.display().to_string(),
2552 r#"
2553function %sample(i32, i32, i32, i32) system_v {
2554 ss0 = explicit_slot 4, align = 4
2555 ss1 = explicit_slot 4, align = 4
2556 ss2 = explicit_slot 4, align = 4
2557 sig0 = () system_v
2558 sig1 = (i32) system_v
2559 fn0 = colocated u0:0 sig0
2560 fn1 = colocated u1:1 sig1
2561
2562block0(v0: i32, v1: i32, v2: i32, v3: i32):
2563 v13 = stack_addr.i64 ss0
2564 store notrap aligned v0, v13
2565 v14 = stack_addr.i64 ss1
2566 store notrap aligned v1, v14
2567 v15 = stack_addr.i64 ss2
2568 store notrap aligned v2, v15
2569 jump block1(v3)
2570
2571block1(v4: i32):
2572 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2573 v11 = stack_addr.i64 ss0
2574 v12 = load.i32 notrap aligned v11
2575 call fn1(v12), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2576 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2577 jump block2
2578
2579block2:
2580 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2581 v9 = stack_addr.i64 ss1
2582 v10 = load.i32 notrap aligned v9
2583 call fn1(v10), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2584 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2585 v5 = iconst.i32 -1
2586 v6 = iadd.i32 v4, v5 ; v5 = -1
2587 brif.i32 v4, block1(v6), block3
2588
2589block3:
2590 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2591 v7 = stack_addr.i64 ss2
2592 v8 = load.i32 notrap aligned v7
2593 call fn1(v8), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2594 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0, i32 @ ss2+0]
2595 jump block2
2596}
2597 "#,
2598 );
2599 }
2600
2601 fn import_func(
2602 builder: &mut FunctionBuilder,
2603 params: impl IntoIterator<Item = ir::Type>,
2604 results: impl IntoIterator<Item = ir::Type>,
2605 ) -> ir::FuncRef {
2606 let index = u32::try_from(builder.func.dfg.ext_funcs.len()).unwrap();
2607
2608 let name = builder
2609 .func
2610 .declare_imported_user_function(ir::UserExternalName {
2611 namespace: 0,
2612 index,
2613 });
2614 let name = ir::ExternalName::user(name);
2615
2616 let mut signature = Signature::new(CallConv::SystemV);
2617 signature
2618 .params
2619 .extend(params.into_iter().map(|ty| AbiParam::new(ty)));
2620 signature
2621 .returns
2622 .extend(results.into_iter().map(|ty| AbiParam::new(ty)));
2623 let signature = builder.func.import_signature(signature);
2624
2625 builder.import_function(ir::ExtFuncData {
2626 name,
2627 signature,
2628 colocated: true,
2629 patchable: false,
2630 })
2631 }
2632
2633 #[test]
2634 fn issue_10397_stack_map_vars_and_indirect_block_params() {
2635 let _ = env_logger::try_init();
2636
2637 let mut sig = Signature::new(CallConv::SystemV);
2638 if false {
2639 sig.params.push(AbiParam::new(ir::types::I32));
2640 }
2641
2642 let mut fn_ctx = FunctionBuilderContext::new();
2643 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("f"), sig);
2644 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2645
2646 let alloc_struct = import_func(&mut builder, None, Some(ir::types::I32));
2647 let alloc_array = import_func(&mut builder, None, Some(ir::types::I32));
2648 let array_init_elem = import_func(&mut builder, Some(ir::types::I32), None);
2649 let type_of = import_func(&mut builder, Some(ir::types::I32), Some(ir::types::I32));
2650 let ref_test = import_func(
2651 &mut builder,
2652 vec![ir::types::I32, ir::types::I32],
2653 Some(ir::types::I32),
2654 );
2655 let access_array = import_func(&mut builder, Some(ir::types::I32), None);
2656 let should_continue_inner_loop = import_func(&mut builder, None, Some(ir::types::I32));
2657 let access_struct = import_func(&mut builder, Some(ir::types::I32), None);
2658 let should_return = import_func(&mut builder, None, Some(ir::types::I32));
2659
2660 let var_struct = builder.declare_var(cranelift_codegen::ir::types::I32);
2736 builder.declare_var_needs_stack_map(var_struct);
2737
2738 let var_array = builder.declare_var(cranelift_codegen::ir::types::I32);
2739 builder.declare_var_needs_stack_map(var_array);
2740
2741 let block_entry = builder.create_block();
2742 let block_outer_loop_head = builder.create_block();
2743 let block_array_init_loop_head = builder.create_block();
2744 let block_array_init_loop_body = builder.create_block();
2745 let block_array_init_loop_done = builder.create_block();
2746 let block_inner_loop_head = builder.create_block();
2747 let block_ref_test_non_null = builder.create_block();
2748 let block_ref_test_slow = builder.create_block();
2749 let block_ref_test_done = builder.create_block();
2750 let block_after_inner_loop = builder.create_block();
2751 let block_return = builder.create_block();
2752
2753 builder.append_block_params_for_function_params(block_entry);
2754 builder.switch_to_block(block_entry);
2755 builder.seal_block(block_entry);
2756 let call_inst = builder.ins().call(alloc_struct, &[]);
2757 let v0 = builder.func.dfg.first_result(call_inst);
2758 builder.def_var(var_struct, v0);
2759 builder.ins().jump(block_outer_loop_head, &[]);
2760
2761 builder.switch_to_block(block_outer_loop_head);
2762 let call_inst = builder.ins().call(alloc_array, &[]);
2763 let v1 = builder.func.dfg.first_result(call_inst);
2764 builder.def_var(var_array, v1);
2765 let v2 = builder.ins().iconst(ir::types::I32, 0);
2766 builder.ins().jump(block_array_init_loop_head, &[v2.into()]);
2767
2768 builder.switch_to_block(block_array_init_loop_head);
2769 let v3 = builder.append_block_param(block_array_init_loop_head, ir::types::I32);
2770 let v4 = builder.ins().iconst(ir::types::I32, 1);
2771 let v5 = builder
2772 .ins()
2773 .icmp(ir::condcodes::IntCC::UnsignedLessThan, v3, v4);
2774 builder.ins().brif(
2775 v5,
2776 block_array_init_loop_body,
2777 &[],
2778 block_array_init_loop_done,
2779 &[],
2780 );
2781
2782 builder.switch_to_block(block_array_init_loop_body);
2783 builder.seal_block(block_array_init_loop_body);
2784 builder.ins().call(array_init_elem, &[v1, v4]);
2785 let v6 = builder.ins().iconst(ir::types::I32, 1);
2786 let v7 = builder.ins().iadd(v4, v6);
2787 builder.ins().jump(block_array_init_loop_head, &[v7.into()]);
2788 builder.seal_block(block_array_init_loop_head);
2789
2790 builder.switch_to_block(block_array_init_loop_done);
2791 builder.seal_block(block_array_init_loop_done);
2792 builder.ins().jump(block_inner_loop_head, &[]);
2793
2794 builder.switch_to_block(block_inner_loop_head);
2795 let v8 = builder.use_var(var_array);
2796 let v9 = builder.ins().iconst(ir::types::I32, 0);
2797 let v10 = builder.ins().icmp(ir::condcodes::IntCC::Equal, v8, v9);
2798 builder.ins().brif(
2799 v10,
2800 block_ref_test_done,
2801 &[v9.into()],
2802 block_ref_test_non_null,
2803 &[],
2804 );
2805
2806 builder.switch_to_block(block_ref_test_non_null);
2807 builder.seal_block(block_ref_test_non_null);
2808 let call_inst = builder.ins().call(type_of, &[v8]);
2809 let v11 = builder.func.dfg.first_result(call_inst);
2810 let v12 = builder.ins().iconst(ir::types::I32, 0xbeefbeef);
2811 let v13 = builder.ins().icmp(ir::condcodes::IntCC::Equal, v11, v12);
2812 let v14 = builder.ins().iconst(ir::types::I32, 1);
2813 builder.ins().brif(
2814 v13,
2815 block_ref_test_done,
2816 &[v14.into()],
2817 block_ref_test_slow,
2818 &[],
2819 );
2820
2821 builder.switch_to_block(block_ref_test_slow);
2822 builder.seal_block(block_ref_test_slow);
2823 let call_inst = builder.ins().call(ref_test, &[v8, v12]);
2824 let v15 = builder.func.dfg.first_result(call_inst);
2825 builder.ins().jump(block_ref_test_done, &[v15.into()]);
2826
2827 builder.switch_to_block(block_ref_test_done);
2828 let v16 = builder.append_block_param(block_ref_test_done, ir::types::I32);
2829 builder.seal_block(block_ref_test_done);
2830 builder.ins().trapz(v16, ir::TrapCode::user(1).unwrap());
2831 builder.def_var(var_array, v8);
2832 builder.ins().call(access_array, &[v8]);
2833 let call_inst = builder.ins().call(should_continue_inner_loop, &[]);
2834 let v17 = builder.func.dfg.first_result(call_inst);
2835 builder
2836 .ins()
2837 .brif(v17, block_inner_loop_head, &[], block_after_inner_loop, &[]);
2838 builder.seal_block(block_inner_loop_head);
2839
2840 builder.switch_to_block(block_after_inner_loop);
2841 builder.seal_block(block_after_inner_loop);
2842 let v18 = builder.use_var(var_struct);
2843 builder.ins().call(access_struct, &[v18]);
2844 let call_inst = builder.ins().call(should_return, &[]);
2845 let v19 = builder.func.dfg.first_result(call_inst);
2846 builder
2847 .ins()
2848 .brif(v19, block_return, &[], block_outer_loop_head, &[]);
2849 builder.seal_block(block_outer_loop_head);
2850
2851 builder.switch_to_block(block_return);
2852 builder.seal_block(block_return);
2853 builder.ins().return_(&[]);
2854
2855 builder.finalize(systemv_frontend_config());
2856 assert_eq_output!(
2857 func.display().to_string(),
2858 r#"
2859function %f() system_v {
2860 ss0 = explicit_slot 4, align = 4
2861 ss1 = explicit_slot 4, align = 4
2862 ss2 = explicit_slot 4, align = 4
2863 sig0 = () -> i32 system_v
2864 sig1 = () -> i32 system_v
2865 sig2 = (i32) system_v
2866 sig3 = (i32) -> i32 system_v
2867 sig4 = (i32, i32) -> i32 system_v
2868 sig5 = (i32) system_v
2869 sig6 = () -> i32 system_v
2870 sig7 = (i32) system_v
2871 sig8 = () -> i32 system_v
2872 fn0 = colocated u0:0 sig0
2873 fn1 = colocated u0:1 sig1
2874 fn2 = colocated u0:2 sig2
2875 fn3 = colocated u0:3 sig3
2876 fn4 = colocated u0:4 sig4
2877 fn5 = colocated u0:5 sig5
2878 fn6 = colocated u0:6 sig6
2879 fn7 = colocated u0:7 sig7
2880 fn8 = colocated u0:8 sig8
2881
2882block0:
2883 v0 = call fn0()
2884 jump block1(v0)
2885
2886block1(v22: i32):
2887 v21 -> v22
2888 v44 = stack_addr.i64 ss1
2889 store notrap aligned v22, v44
2890 v1 = call fn1(), stack_map=[i32 @ ss1+0]
2891 v8 -> v1
2892 v18 -> v1
2893 v43 = stack_addr.i64 ss0
2894 store notrap aligned v1, v43
2895 v2 = iconst.i32 0
2896 jump block2(v2) ; v2 = 0
2897
2898block2(v3: i32):
2899 v4 = iconst.i32 1
2900 v5 = icmp ult v3, v4 ; v4 = 1
2901 brif v5, block3, block4
2902
2903block3:
2904 v24 = stack_addr.i64 ss0
2905 v25 = load.i32 notrap aligned v24
2906 call fn2(v25, v4), stack_map=[i32 @ ss0+0, i32 @ ss1+0] ; v4 = 1
2907 v6 = iconst.i32 1
2908 v7 = iadd.i32 v4, v6 ; v4 = 1, v6 = 1
2909 jump block2(v7)
2910
2911block4:
2912 v41 = stack_addr.i64 ss1
2913 v42 = load.i32 notrap aligned v41
2914 jump block5(v42)
2915
2916block5(v20: i32):
2917 v19 -> v20
2918 v40 = stack_addr.i64 ss2
2919 store notrap aligned v20, v40
2920 v9 = iconst.i32 0
2921 v38 = stack_addr.i64 ss0
2922 v39 = load.i32 notrap aligned v38
2923 v10 = icmp eq v39, v9 ; v9 = 0
2924 brif v10, block8(v9), block6 ; v9 = 0
2925
2926block6:
2927 v36 = stack_addr.i64 ss0
2928 v37 = load.i32 notrap aligned v36
2929 v11 = call fn3(v37), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2930 v12 = iconst.i32 -1091584273
2931 v13 = icmp eq v11, v12 ; v12 = -1091584273
2932 v14 = iconst.i32 1
2933 brif v13, block8(v14), block7 ; v14 = 1
2934
2935block7:
2936 v34 = stack_addr.i64 ss0
2937 v35 = load.i32 notrap aligned v34
2938 v15 = call fn4(v35, v12), stack_map=[i32 @ ss0+0, i32 @ ss2+0] ; v12 = -1091584273
2939 jump block8(v15)
2940
2941block8(v16: i32):
2942 trapz v16, user1
2943 v32 = stack_addr.i64 ss0
2944 v33 = load.i32 notrap aligned v32
2945 call fn5(v33), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2946 v17 = call fn6(), stack_map=[i32 @ ss0+0, i32 @ ss2+0]
2947 v30 = stack_addr.i64 ss2
2948 v31 = load.i32 notrap aligned v30
2949 brif v17, block5(v31), block9
2950
2951block9:
2952 v28 = stack_addr.i64 ss2
2953 v29 = load.i32 notrap aligned v28
2954 call fn7(v29), stack_map=[i32 @ ss2+0]
2955 v23 = call fn8(), stack_map=[i32 @ ss2+0]
2956 v26 = stack_addr.i64 ss2
2957 v27 = load.i32 notrap aligned v26
2958 brif v23, block10, block1(v27)
2959
2960block10:
2961 return
2962}
2963 "#,
2964 );
2965 }
2966
2967 #[test]
2968 fn needs_stack_map_across_try_call() {
2969 let _ = env_logger::try_init();
2970
2971 let sig = Signature::new(CallConv::SystemV);
2986
2987 let mut fn_ctx = FunctionBuilderContext::new();
2988 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
2989 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2990
2991 let name0 = builder
2993 .func
2994 .declare_imported_user_function(ir::UserExternalName {
2995 namespace: 0,
2996 index: 0,
2997 });
2998 let mut sig0 = Signature::new(CallConv::SystemV);
2999 sig0.returns.push(AbiParam::new(ir::types::I32));
3000 let signature0 = builder.func.import_signature(sig0);
3001 let func_ref0 = builder.import_function(ir::ExtFuncData {
3002 name: ir::ExternalName::user(name0),
3003 signature: signature0,
3004 colocated: true,
3005 patchable: false,
3006 });
3007
3008 let name1 = builder
3010 .func
3011 .declare_imported_user_function(ir::UserExternalName {
3012 namespace: 0,
3013 index: 1,
3014 });
3015 let mut sig1 = Signature::new(CallConv::SystemV);
3016 sig1.params.push(AbiParam::new(ir::types::I32));
3017 let signature1 = builder.func.import_signature(sig1);
3018 let func_ref1 = builder.import_function(ir::ExtFuncData {
3019 name: ir::ExternalName::user(name1),
3020 signature: signature1,
3021 colocated: true,
3022 patchable: false,
3023 });
3024
3025 let block0 = builder.create_block();
3026 let block1 = builder.create_block();
3027 let block2 = builder.create_block();
3028
3029 builder.switch_to_block(block0);
3030
3031 let call0 = builder.ins().call(func_ref0, &[]);
3033 let v0 = builder.func.dfg.inst_results(call0)[0];
3034 builder.declare_value_needs_stack_map(v0);
3035
3036 let call1 = builder.ins().call(func_ref0, &[]);
3038 let v1 = builder.func.dfg.inst_results(call1)[0];
3039 builder.declare_value_needs_stack_map(v1);
3040
3041 let normal_return = BlockCall::new(block1, [], &mut builder.func.dfg.value_lists);
3043 let exception_table = builder
3044 .func
3045 .dfg
3046 .exception_tables
3047 .push(ExceptionTableData::new(signature0, normal_return, []));
3048 builder.ins().try_call(func_ref0, &[], exception_table);
3049
3050 builder.switch_to_block(block1);
3052 builder.ins().call(func_ref1, &[v0]);
3053 builder.ins().return_(&[]);
3054
3055 builder.switch_to_block(block2);
3057 builder.ins().return_(&[]);
3058
3059 builder.seal_all_blocks();
3060 builder.finalize(systemv_frontend_config());
3061
3062 let output = func.display().to_string();
3067 assert!(
3068 output.contains("try_call fn0(), sig0, block1, [], stack_map=[i32 @ ss0+0]"),
3069 "try_call should have stack_map entry for v0 (spilled to ss0), got:\n{output}"
3070 );
3071 }
3072
3073 #[test]
3074 fn rewrite_uses_of_alias_values() {
3075 let _ = env_logger::try_init();
3076
3077 let mut sig = Signature::new(CallConv::SystemV);
3078 sig.params.push(AbiParam::new(ir::types::I32));
3079 let mut fn_ctx = FunctionBuilderContext::new();
3080 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
3081 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
3082
3083 let name0 = builder
3085 .func
3086 .declare_imported_user_function(ir::UserExternalName {
3087 namespace: 0,
3088 index: 0,
3089 });
3090 let mut sig = Signature::new(CallConv::SystemV);
3091 sig.returns.push(AbiParam::new(ir::types::I32));
3092 let signature = builder.func.import_signature(sig);
3093 let fn0 = builder.import_function(ir::ExtFuncData {
3094 name: ir::ExternalName::user(name0),
3095 signature,
3096 colocated: true,
3097 patchable: false,
3098 });
3099
3100 let name1 = builder
3102 .func
3103 .declare_imported_user_function(ir::UserExternalName {
3104 namespace: 0,
3105 index: 1,
3106 });
3107 let mut sig = Signature::new(CallConv::SystemV);
3108 sig.params.push(AbiParam::new(ir::types::I32));
3109 let signature = builder.func.import_signature(sig);
3110 let fn1 = builder.import_function(ir::ExtFuncData {
3111 name: ir::ExternalName::user(name1),
3112 signature,
3113 colocated: true,
3114 patchable: false,
3115 });
3116
3117 let var = builder.declare_var(ir::types::I32);
3118 builder.declare_var_needs_stack_map(var);
3119
3120 let block0 = builder.create_block();
3121 let block1 = builder.create_block();
3122 let block2 = builder.create_block();
3123
3124 builder.append_block_params_for_function_params(block0);
3125 builder.switch_to_block(block0);
3126 let v0 = builder.func.dfg.block_params(block0)[0];
3127 let inst = builder.ins().call(fn0, &[]);
3128 let v1 = builder.func.dfg.first_result(inst);
3129 builder.def_var(var, v1);
3130 builder.ins().brif(v0, block1, &[], block2, &[]);
3131
3132 builder.switch_to_block(block1);
3133 builder.def_var(var, v1);
3134 builder.ins().jump(block2, &[]);
3135
3136 builder.switch_to_block(block2);
3137 let v2 = builder.use_var(var);
3138 builder.ins().call(fn1, &[v2]);
3139 let v3 = builder.use_var(var);
3140 builder.ins().call(fn1, &[v3]);
3141 builder.ins().return_(&[]);
3142
3143 builder.seal_all_blocks();
3144
3145 builder.finalize(systemv_frontend_config());
3146
3147 assert!(func.dfg.value_is_alias(v3));
3153 assert_eq_output!(
3154 func.display().to_string(),
3155 r#"
3156function %sample(i32) system_v {
3157 ss0 = explicit_slot 4, align = 4
3158 sig0 = () -> i32 system_v
3159 sig1 = (i32) system_v
3160 fn0 = colocated u0:0 sig0
3161 fn1 = colocated u0:1 sig1
3162
3163block0(v0: i32):
3164 v1 = call fn0()
3165 v2 -> v1
3166 v7 = stack_addr.i64 ss0
3167 store notrap aligned v1, v7
3168 brif v0, block1, block2
3169
3170block1:
3171 jump block2
3172
3173block2:
3174 v5 = stack_addr.i64 ss0
3175 v6 = load.i32 notrap aligned v5
3176 call fn1(v6), stack_map=[i32 @ ss0+0]
3177 v3 = stack_addr.i64 ss0
3178 v4 = load.i32 notrap aligned v3
3179 call fn1(v4)
3180 return
3181}
3182 "#,
3183 );
3184 }
3185
3186 #[test]
3197 fn var_redefined_in_same_block_keeps_earlier_value_in_stack_map() {
3198 let mut sig = Signature::new(CallConv::SystemV);
3199 sig.returns
3200 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
3201 sig.returns
3202 .push(AbiParam::new(cranelift_codegen::ir::types::I32));
3203
3204 let mut fn_ctx = FunctionBuilderContext::new();
3205 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
3206 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
3207
3208 let var = builder.declare_var(cranelift_codegen::ir::types::I32);
3209 builder.declare_var_needs_stack_map(var);
3210
3211 let name = builder
3212 .func
3213 .declare_imported_user_function(ir::UserExternalName {
3214 namespace: 0,
3215 index: 0,
3216 });
3217 let signature = builder
3218 .func
3219 .import_signature(Signature::new(CallConv::SystemV));
3220 let func_ref = builder.import_function(ir::ExtFuncData {
3221 name: ir::ExternalName::user(name),
3222 signature,
3223 colocated: true,
3224 patchable: false,
3225 });
3226
3227 let block0 = builder.create_block();
3228 builder.switch_to_block(block0);
3229
3230 let v1 = builder.ins().iconst(ir::types::I32, 11);
3233 builder.def_var(var, v1);
3234 let v1_use = builder.use_var(var);
3235
3236 let v2 = builder.ins().iconst(ir::types::I32, 22);
3242 builder.def_var(var, v2);
3243 let v2_use = builder.use_var(var);
3244
3245 builder.ins().call(func_ref, &[]);
3248
3249 builder.ins().return_(&[v1_use, v2_use]);
3250
3251 builder.seal_all_blocks();
3252 builder.finalize(systemv_frontend_config());
3253
3254 assert_eq_output!(
3255 func.display().to_string(),
3256 r#"
3257function %sample() -> i32, i32 system_v {
3258 ss0 = explicit_slot 4, align = 4
3259 ss1 = explicit_slot 4, align = 4
3260 sig0 = () system_v
3261 fn0 = colocated u0:0 sig0
3262
3263block0:
3264 v0 = iconst.i32 11
3265 v7 = stack_addr.i64 ss0
3266 store notrap aligned v0, v7 ; v0 = 11
3267 v1 = iconst.i32 22
3268 v6 = stack_addr.i64 ss1
3269 store notrap aligned v1, v6 ; v1 = 22
3270 call fn0(), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
3271 v2 = stack_addr.i64 ss0
3272 v3 = load.i32 notrap aligned v2
3273 v4 = stack_addr.i64 ss1
3274 v5 = load.i32 notrap aligned v4
3275 return v3, v5
3276}
3277 "#
3278 );
3279 }
3280
3281 #[test]
3282 fn loop_invariant_value_needs_stack_map() {
3283 let sig = Signature::new(CallConv::SystemV);
3284 let mut fn_ctx = FunctionBuilderContext::new();
3285 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
3286 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
3287
3288 let alloc_name = builder
3290 .func
3291 .declare_imported_user_function(ir::UserExternalName {
3292 namespace: 0,
3293 index: 1,
3294 });
3295 let mut sig = Signature::new(CallConv::SystemV);
3296 sig.returns.push(AbiParam::new(ir::types::I32));
3297 let signature = builder.func.import_signature(sig);
3298 let alloc = builder.import_function(ir::ExtFuncData {
3299 name: ir::ExternalName::user(alloc_name),
3300 signature,
3301 colocated: true,
3302 patchable: false,
3303 });
3304
3305 let observe_name = builder
3307 .func
3308 .declare_imported_user_function(ir::UserExternalName {
3309 namespace: 0,
3310 index: 1,
3311 });
3312 let mut sig = Signature::new(CallConv::SystemV);
3313 sig.params.push(AbiParam::new(ir::types::I32));
3314 let signature = builder.func.import_signature(sig);
3315 let observe = builder.import_function(ir::ExtFuncData {
3316 name: ir::ExternalName::user(observe_name),
3317 signature,
3318 colocated: true,
3319 patchable: false,
3320 });
3321
3322 let block0 = builder.create_block();
3323 let block1 = builder.create_block();
3324 let block2 = builder.create_block();
3325
3326 builder.switch_to_block(block0);
3328 let call_inst = builder.ins().call(alloc, &[]);
3329 let v0 = builder.func.dfg.first_result(call_inst);
3330 builder.declare_value_needs_stack_map(v0);
3331 builder.ins().jump(block1, &[]);
3332
3333 builder.switch_to_block(block1);
3338 builder.ins().call(observe, &[v0]);
3339 let call_inst = builder.ins().call(alloc, &[]);
3340 let v1 = builder.func.dfg.first_result(call_inst);
3341 builder.declare_value_needs_stack_map(v1);
3342 builder.ins().call(observe, &[v1]);
3344 builder.ins().brif(v1, block2, &[], block1, &[]);
3345
3346 builder.switch_to_block(block2);
3348 builder.ins().call(observe, &[v1]);
3349 builder.ins().call(observe, &[v1]);
3350 builder.ins().return_(&[]);
3351
3352 builder.seal_all_blocks();
3353 builder.finalize(systemv_frontend_config());
3354
3355 assert_eq_output!(
3358 func.display().to_string(),
3359 r#"
3360function %sample() system_v {
3361 ss0 = explicit_slot 4, align = 4
3362 ss1 = explicit_slot 4, align = 4
3363 sig0 = () -> i32 system_v
3364 sig1 = (i32) system_v
3365 fn0 = colocated u0:1 sig0
3366 fn1 = colocated u0:1 sig1
3367
3368block0:
3369 v0 = call fn0()
3370 v13 = stack_addr.i64 ss1
3371 store notrap aligned v0, v13
3372 jump block1
3373
3374block1:
3375 v11 = stack_addr.i64 ss1
3376 v12 = load.i32 notrap aligned v11
3377 call fn1(v12), stack_map=[i32 @ ss1+0]
3378 v1 = call fn0(), stack_map=[i32 @ ss1+0]
3379 v10 = stack_addr.i64 ss0
3380 store notrap aligned v1, v10
3381 v8 = stack_addr.i64 ss0
3382 v9 = load.i32 notrap aligned v8
3383 call fn1(v9), stack_map=[i32 @ ss1+0, i32 @ ss0+0]
3384 v6 = stack_addr.i64 ss0
3385 v7 = load.i32 notrap aligned v6
3386 brif v7, block2, block1
3387
3388block2:
3389 v4 = stack_addr.i64 ss0
3390 v5 = load.i32 notrap aligned v4
3391 call fn1(v5), stack_map=[i32 @ ss0+0]
3392 v2 = stack_addr.i64 ss0
3393 v3 = load.i32 notrap aligned v2
3394 call fn1(v3)
3395 return
3396}
3397 "#
3398 );
3399 }
3400
3401 #[test]
3402 fn stack_map_alias_region() {
3403 let _ = env_logger::try_init();
3404
3405 let sig = Signature::new(CallConv::SystemV);
3406
3407 let mut fn_ctx = FunctionBuilderContext::new();
3408 let mut func = Function::with_name_signature(ir::UserFuncName::testcase("sample"), sig);
3409 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
3410
3411 builder.make_stack_map_alias_region(Box::new(|regions, _ty, _slot, _offset| {
3414 Some(regions.insert(ir::AliasRegionData {
3415 user_id: 0,
3416 description: "stack map".into(),
3417 }))
3418 }));
3419
3420 let name = builder
3421 .func
3422 .declare_imported_user_function(ir::UserExternalName {
3423 namespace: 0,
3424 index: 0,
3425 });
3426 let mut sig = Signature::new(CallConv::SystemV);
3427 sig.params.push(AbiParam::new(ir::types::I32));
3428 let signature = builder.func.import_signature(sig);
3429 let func_ref = builder.import_function(ir::ExtFuncData {
3430 name: ir::ExternalName::user(name),
3431 signature,
3432 colocated: true,
3433 patchable: false,
3434 });
3435
3436 let block0 = builder.create_block();
3447 builder.append_block_params_for_function_params(block0);
3448 builder.switch_to_block(block0);
3449 let v0 = builder.ins().iconst(ir::types::I32, 42);
3450 builder.declare_value_needs_stack_map(v0);
3451 builder.ins().call(func_ref, &[v0]);
3452 builder.ins().call(func_ref, &[v0]);
3453 builder.ins().return_(&[]);
3454 builder.seal_all_blocks();
3455 builder.finalize(systemv_frontend_config());
3456
3457 assert_eq_output!(
3458 func.display().to_string(),
3459 r#"
3460function %sample() system_v {
3461 ss0 = explicit_slot 4, align = 4
3462 region0 = 0 "stack map"
3463 sig0 = (i32) system_v
3464 fn0 = colocated u0:0 sig0
3465
3466block0:
3467 v0 = iconst.i32 42
3468 v5 = stack_addr.i64 ss0
3469 store notrap aligned region0 v0, v5 ; v0 = 42
3470 v3 = stack_addr.i64 ss0
3471 v4 = load.i32 notrap aligned region0 v3
3472 call fn0(v4), stack_map=[i32 @ ss0+0]
3473 v1 = stack_addr.i64 ss0
3474 v2 = load.i32 notrap aligned region0 v1
3475 call fn0(v2)
3476 return
3477}
3478 "#
3479 );
3480 }
3481}