1use jstd::{Identifier, registry::Registry, stable_arena::StableArena};
2use rustc_hash::{FxHashMap, FxHashSet};
3use std::{
4 borrow::Cow,
5 collections::BTreeSet,
6 fmt::{Display, Formatter},
7 marker::PhantomData,
8};
9
10mod footprint;
11pub use footprint::{Footprint, RamBase, RamField, RamLocations, RamObject, RamRegion};
12
13mod signature;
14pub use signature::{
15 ArgMemKind, ExternArg, ExternArgmem, ExternInterface, ExternSlot, FunctionSignature, ParamAttrs,
16};
17
18use crate::{
19 context::Context,
20 error::{Error, ErrorTy, Result},
21 value::{
22 BasicBlock, BlockId, BlockRef, Instruction, InstructionId, LocalValueId, ModuleView,
23 QCodeView, Temp, TempId, TempSpace, TempSpaceId, Value, ValueId, VarnodeId,
24 block::EdgeData,
25 block::cfg::{EdgeId, LocalBlockId},
26 block_param::{BlockParam, BlockParamId, LocalParamId},
27 insn::{LocalInsnId, Mnemonic},
28 util::{
29 base_ref::{BaseRef, WithCtx, WithCtxMut},
30 named::{Named, Renameable, update_context_name},
31 },
32 },
33};
34
35#[derive(Identifier)]
36pub struct FunctionId(u32);
37
38#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
51pub struct FunctionInterface<'str> {
52 pub name: Cow<'str, str>,
54
55 pub address: Option<u64>,
57
58 pub is_external: bool,
64
65 pub signature: Option<FunctionSignature>,
67
68 #[serde(default)]
70 pub kind: FunctionKind,
71
72 #[serde(default)]
82 pub effects: FunctionEffects,
83
84 #[serde(default)]
87 pub import_ordinal: Option<u16>,
88}
89
90#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
94pub struct FunctionEffects {
95 #[serde(default)]
97 pub register: RegisterChannelState,
98 #[serde(default)]
101 pub memory: MemoryChannelState,
102}
103
104impl FunctionEffects {
105 pub fn materialized(&self) -> Option<&RegisterInterfaceMap> {
108 self.register.materialized()
109 }
110
111 pub fn is_solved(&self) -> bool {
114 self.register.is_solved()
115 }
116}
117
118#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
125pub enum RegisterChannelState {
126 #[default]
129 Unsolved,
130 Top,
134 Solved(RegisterEffectSets),
139 Materialized(RegisterInterfaceMap),
143}
144
145impl RegisterChannelState {
146 pub fn materialized(&self) -> Option<&RegisterInterfaceMap> {
149 match self {
150 RegisterChannelState::Materialized(map) => Some(map),
151 _ => None,
152 }
153 }
154
155 pub fn is_solved(&self) -> bool {
158 matches!(
159 self,
160 RegisterChannelState::Solved(_) | RegisterChannelState::Materialized(_)
161 )
162 }
163}
164
165#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
173pub struct MemoryChannelState {
174 #[serde(default)]
178 pub coarse: WrittenSpacesState,
179 #[serde(default)]
191 pub precise: Option<Footprint>,
192
193 #[serde(default)]
207 pub materialized: Option<MemoryInterfaceMap>,
208}
209
210impl MemoryChannelState {
211 pub fn materialized(&self) -> Option<&MemoryInterfaceMap> {
214 self.materialized.as_ref()
215 }
216}
217
218#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
222pub enum WrittenSpacesState {
223 #[default]
226 Unstamped,
227 Unbounded,
230 Bounded(Vec<crate::space::SpaceId>),
233}
234
235#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
240pub struct RegisterEffectSets {
241 #[serde(alias = "loads")]
243 pub reads: Vec<VarnodeId>,
244 #[serde(alias = "stores")]
246 pub writes: Vec<VarnodeId>,
247}
248
249#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
269pub struct DerivedOutput {
270 pub register: VarnodeId,
272 pub projection: crate::value::insn::Callee,
275}
276
277#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
291pub struct RegisterInterfaceMap {
292 pub inputs: Vec<VarnodeId>,
294 pub outputs: Vec<VarnodeId>,
298 pub returns: usize,
303 #[serde(default)]
314 pub projections: Vec<DerivedOutput>,
315}
316
317#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
335pub struct InterfaceSlot {
336 pub base: SlotBase,
337 pub offset: i64,
338 pub size: usize,
339}
340
341#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
356pub enum SlotBase {
357 Arg(usize),
364 Global(u64),
368 Unmappable,
371}
372
373impl InterfaceSlot {
374 pub fn is_bindable(&self) -> bool {
379 !matches!(self.base, SlotBase::Unmappable)
380 }
381}
382
383#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
395pub struct MemoryInterfaceMap {
396 pub inputs: Vec<InterfaceSlot>,
399 pub outputs: Vec<InterfaceSlot>,
402}
403
404#[derive(Clone, serde::Serialize, serde::Deserialize)]
409pub struct FunctionBody<'str> {
410 #[serde(skip)]
421 id: Option<FunctionId>,
422
423 root: Option<LocalBlockId>,
427
428 pub(crate) insns: StableArena<LocalInsnId, Instruction<'str>>,
433
434 pub(crate) blocks: StableArena<LocalBlockId, BasicBlock<'str>>,
438
439 #[serde(default)]
443 pub(crate) roster: Vec<LocalBlockId>,
444
445 pub(crate) params: StableArena<LocalParamId, BlockParam<'str>>,
447
448 pub(crate) edges: StableArena<EdgeId, EdgeData>,
451
452 pub(crate) temp_spaces: Registry<crate::value::LocalTempSpaceId, TempSpace>,
456
457 pub(crate) temps: Registry<crate::value::LocalTempId, Temp<'str>>,
459
460 pub instruction_addrs: BTreeSet<u64>,
465
466 #[serde(default)]
474 pub(crate) names: crate::context::NameTable<'str, LocalValueId>,
475
476 #[serde(default)]
493 pub(crate) users: FxHashMap<LocalValueId, Vec<LocalInsnId>>,
494}
495
496#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
503pub struct BodyArenaKindStats {
504 pub issued: usize,
505 pub live: usize,
506 pub dead: usize,
507 pub capacity: usize,
508 pub structural_bytes: usize,
509}
510
511#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
513pub struct BodyArenaStats {
514 pub instructions: BodyArenaKindStats,
515 pub blocks: BodyArenaKindStats,
516 pub params: BodyArenaKindStats,
517 pub edges: BodyArenaKindStats,
518}
519
520impl BodyArenaKindStats {
521 fn stable_arena<Id: jstd::registry::Identifier, T>(arena: &StableArena<Id, T>) -> Self {
522 let issued = arena.issued_len();
523 let live = arena.len();
524 Self {
525 issued,
526 live,
527 dead: issued - live,
528 capacity: arena.capacity(),
529 structural_bytes: arena.structural_bytes(),
530 }
531 }
532
533 fn add_assign(&mut self, other: Self) {
534 self.issued += other.issued;
535 self.live += other.live;
536 self.dead += other.dead;
537 self.capacity += other.capacity;
538 self.structural_bytes += other.structural_bytes;
539 }
540}
541
542impl BodyArenaStats {
543 pub(crate) fn add_assign(&mut self, other: Self) {
544 self.instructions.add_assign(other.instructions);
545 self.blocks.add_assign(other.blocks);
546 self.params.add_assign(other.params);
547 self.edges.add_assign(other.edges);
548 }
549}
550
551#[derive(Debug, Clone, Copy, PartialEq, Eq)]
556pub enum WrittenSpaces<'a> {
557 Unstamped,
561 Unbounded,
563 Bounded(&'a [crate::space::SpaceId]),
565}
566
567#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
568pub enum FunctionKind {
569 #[default]
570 Machine,
571 Lambda,
572}
573
574impl<'str> FunctionInterface<'str> {
575 pub fn new(name: Cow<'str, str>) -> Self {
577 Self {
578 name,
579 address: None,
580 is_external: false,
581 signature: None,
582 kind: FunctionKind::Machine,
583 effects: FunctionEffects::default(),
584 import_ordinal: None,
585 }
586 }
587
588 pub fn param_attr(&self, index: usize) -> Option<ParamAttrs> {
591 self.signature
592 .as_ref()
593 .and_then(|s| s.param_attrs.as_ref())
594 .and_then(|attrs| attrs.get(index))
595 .copied()
596 }
597}
598
599impl<'str> FunctionBody<'str> {
600 pub fn arena_stats(&self) -> BodyArenaStats {
602 BodyArenaStats {
603 instructions: BodyArenaKindStats::stable_arena(&self.insns),
604 blocks: BodyArenaKindStats::stable_arena(&self.blocks),
605 params: BodyArenaKindStats::stable_arena(&self.params),
606 edges: BodyArenaKindStats::stable_arena(&self.edges),
607 }
608 }
609
610 pub fn shrink_to_fit(&mut self) {
618 self.insns.shrink_to_fit();
619 self.blocks.shrink_to_fit();
620 self.params.shrink_to_fit();
621 self.edges.shrink_to_fit();
622 self.roster.shrink_to_fit();
623 for mut block in self.blocks.iter_mut() {
624 block.instructions.shrink_to_fit();
625 block.params.shrink_to_fit();
626 block.edges.shrink_to_fit();
627 }
628 for insns in self.users.values_mut() {
629 insns.shrink_to_fit();
630 }
631 self.users.shrink_to_fit();
632 }
633
634 pub fn install_id(&mut self, id: FunctionId) {
637 assert!(self.id.is_none(), "body already installed");
638 self.id = Some(id);
639 }
640
641 pub fn resolve_minted_callee(&mut self, slot: u32, real: FunctionId) -> usize {
644 let mut patched = 0;
645 for mut insn in self.insns.iter_mut() {
646 patched += usize::from(insn.mnemonic_mut().resolve_minted_callee(slot, real));
647 }
648 patched
649 }
650
651 pub fn resolve_minted_callees(
656 &mut self,
657 installed: &[FunctionId],
658 ) -> std::result::Result<usize, u32> {
659 let mut patched = 0;
660 for mut insn in self.insns.iter_mut() {
661 let mnemonic = insn.mnemonic_mut();
662 let Some(slot) = mnemonic.minted_callee_slot() else {
663 continue;
664 };
665 let Some(&real) = installed.get(slot as usize) else {
666 return Err(slot);
667 };
668 mnemonic.resolve_minted_callee(slot, real);
669 patched += 1;
670 }
671 Ok(patched)
672 }
673
674 pub fn empty_with_id(id: FunctionId) -> Self {
679 Self {
680 id: Some(id),
681 root: None,
682 insns: StableArena::default(),
683 blocks: StableArena::default(),
684 roster: Vec::new(),
685 params: StableArena::default(),
686 edges: StableArena::default(),
687 temp_spaces: Registry::default(),
688 temps: Registry::default(),
689 instruction_addrs: BTreeSet::new(),
690 names: crate::context::NameTable::default(),
691 users: FxHashMap::default(),
692 }
693 }
694
695 pub fn detached() -> Self {
703 Self {
704 id: None,
705 root: None,
706 insns: StableArena::default(),
707 blocks: StableArena::default(),
708 roster: Vec::new(),
709 params: StableArena::default(),
710 edges: StableArena::default(),
711 temp_spaces: Registry::default(),
712 temps: Registry::default(),
713 instruction_addrs: BTreeSet::new(),
714 names: crate::context::NameTable::default(),
715 users: FxHashMap::default(),
716 }
717 }
718
719 pub fn id(&self) -> FunctionId {
723 self.id.expect("detached body: no registry id yet")
724 }
725
726 pub fn try_id(&self) -> Option<FunctionId> {
729 self.id
730 }
731
732 pub(crate) fn rehydrate_id(&mut self, id: FunctionId) {
736 self.id = Some(id);
737 }
738
739 pub(crate) fn local_users_of(&self, value: ValueId) -> &[LocalInsnId] {
742 self.users
743 .get(&value.strip_func())
744 .map(Vec::as_slice)
745 .unwrap_or(&[])
746 }
747
748 pub fn has_users(&self, value: ValueId) -> bool {
755 if value
756 .owning_function()
757 .is_some_and(|owner| owner != self.id())
758 {
759 return false;
760 }
761 !self.local_users_of(value).is_empty()
762 }
763
764 pub fn users_of(&self, value: ValueId) -> Vec<InstructionId> {
768 if value
769 .owning_function()
770 .is_some_and(|owner| owner != self.id())
771 {
772 return Vec::new();
773 }
774 self.local_users_of(value)
775 .iter()
776 .map(|&local| InstructionId::new(self.id(), local))
777 .collect()
778 }
779
780 pub fn user_map_entries(&self) -> impl Iterator<Item = (LocalValueId, &[LocalInsnId])> {
784 self.users.iter().map(|(v, u)| (*v, u.as_slice()))
785 }
786
787 pub fn root_id(&self) -> Option<LocalBlockId> {
789 self.root
790 }
791
792 pub fn set_root_id(&mut self, root: Option<LocalBlockId>) {
797 self.root = root;
798 }
799
800 pub fn block(&self, id: BlockId) -> &BasicBlock<'str> {
814 assert_eq!(id.func, self.id(), "block belongs to another function");
815 &self.blocks[id.local]
816 }
817 pub fn block_mut(&mut self, id: BlockId) -> &mut BasicBlock<'str> {
819 assert_eq!(id.func, self.id(), "block belongs to another function");
820 &mut self.blocks[id.local]
821 }
822
823 pub fn contains_block(&self, id: BlockId) -> bool {
825 id.func == self.id() && self.blocks.contains(id.local)
826 }
827 pub fn insn(&self, id: InstructionId) -> &Instruction<'str> {
829 assert_eq!(
830 id.func,
831 self.id(),
832 "instruction belongs to another function"
833 );
834 &self.insns[id.local]
835 }
836 pub fn insn_mut(&mut self, id: InstructionId) -> &mut Instruction<'str> {
838 assert_eq!(
839 id.func,
840 self.id(),
841 "instruction belongs to another function"
842 );
843 &mut self.insns[id.local]
844 }
845
846 pub fn contains_instruction(&self, id: InstructionId) -> bool {
848 id.func == self.id() && self.insns.contains(id.local)
849 }
850 pub fn block_param(&self, id: BlockParamId) -> &BlockParam<'str> {
852 assert_eq!(
853 id.func,
854 self.id(),
855 "block parameter belongs to another function"
856 );
857 &self.params[id.local]
858 }
859 pub fn block_param_mut(&mut self, id: BlockParamId) -> &mut BlockParam<'str> {
861 assert_eq!(
862 id.func,
863 self.id(),
864 "block parameter belongs to another function"
865 );
866 &mut self.params[id.local]
867 }
868
869 pub fn contains_block_param(&self, id: BlockParamId) -> bool {
871 id.func == self.id() && self.params.contains(id.local)
872 }
873
874 pub fn local_type_of(
880 &self,
881 shared: &crate::context::Shared<'str>,
882 id: crate::value::LocalValueId,
883 ) -> crate::types::TypeId {
884 use crate::value::LocalValueId;
885 match id {
886 LocalValueId::Literal(id) => shared.values.literals[id].type_id,
887 LocalValueId::Bytes(id) => shared.values.bytes[id].type_id,
888 LocalValueId::Instruction(local) => self.insns[local].type_id,
889 LocalValueId::BlockParam(local) => self.params[local].type_id,
890 LocalValueId::Varnode(id) => shared
891 .values
892 .varnode_types
893 .get(&id)
894 .copied()
895 .unwrap_or_else(|| {
896 shared
897 .types
898 .get_or_make_int(shared.values.varnodes[id].size_bytes())
899 }),
900 LocalValueId::Temp(local) => shared.types.get_or_make_int(self.temps[local].size),
901 LocalValueId::Poison(id) => shared.values.poisons[id].type_id,
902 LocalValueId::BasicBlock(_) | LocalValueId::Function(_) => {
903 shared.types.get_or_make_int(0)
904 }
905 }
906 }
907
908 pub fn local_stored_type_of(
912 &self,
913 shared: &crate::context::Shared<'str>,
914 id: crate::value::LocalValueId,
915 ) -> Option<crate::types::TypeId> {
916 use crate::value::LocalValueId;
917 match id {
918 LocalValueId::Literal(id) => Some(shared.values.literals[id].type_id),
919 LocalValueId::Bytes(id) => Some(shared.values.bytes[id].type_id),
920 LocalValueId::Instruction(local) => Some(self.insns[local].type_id),
921 LocalValueId::BlockParam(local) => Some(self.params[local].type_id),
922 LocalValueId::Varnode(id) => shared.values.varnode_types.get(&id).copied(),
923 LocalValueId::Poison(id) => Some(shared.values.poisons[id].type_id),
924 LocalValueId::Temp(_) | LocalValueId::BasicBlock(_) | LocalValueId::Function(_) => None,
925 }
926 }
927
928 pub fn push_temp_space(&mut self, space: TempSpace) -> TempSpaceId {
930 TempSpaceId::new(self.id(), self.temp_spaces.push(space))
931 }
932
933 pub fn push_temp(&mut self, temp: Temp<'str>) -> TempId {
935 assert!(
936 usize::from(temp.space) < self.temp_spaces.len(),
937 "temporary references a missing local space"
938 );
939 let name = temp.name.clone();
940 if let Some(name) = &name {
941 assert!(
942 !self.names.contains(name),
943 "temporary name {name:?} is already registered in this function"
944 );
945 }
946 let local = self.temps.push(temp);
947 if let Some(name) = name {
948 self.names
949 .register(name, LocalValueId::Temp(local), None)
950 .expect("temporary name was checked before insertion");
951 }
952 TempId::new(self.id(), local)
953 }
954
955 #[track_caller]
957 pub fn temp_space(&self, id: TempSpaceId) -> &TempSpace {
958 assert_eq!(
959 id.func,
960 self.id(),
961 "temporary space belongs to another function"
962 );
963 debug_assert!(
964 self.contains_temp_space(id),
965 "missing temporary space {id:?} in function {:?} (arena length {})",
966 self.id(),
967 self.temp_spaces.len()
968 );
969 &self.temp_spaces[id.local]
970 }
971
972 pub fn contains_temp_space(&self, id: TempSpaceId) -> bool {
974 id.func == self.id() && usize::from(id.local) < self.temp_spaces.len()
975 }
976
977 #[track_caller]
979 pub fn temp(&self, id: TempId) -> &Temp<'str> {
980 assert_eq!(id.func, self.id(), "temporary belongs to another function");
981 debug_assert!(
982 self.contains_temp(id),
983 "missing temporary {id:?} in function {:?} (arena length {})",
984 self.id(),
985 self.temps.len()
986 );
987 &self.temps[id.local]
988 }
989
990 pub fn contains_temp(&self, id: TempId) -> bool {
992 id.func == self.id() && usize::from(id.local) < self.temps.len()
993 }
994
995 pub fn remove_block_param(&mut self, id: BlockParamId) {
1000 assert!(
1001 self.contains_block_param(id),
1002 "cannot remove stale param {id:?}"
1003 );
1004 let key = ValueId::BlockParam(id).strip_func();
1005 let name = self.params[id.local].name.clone();
1006 if let Some(name) = name {
1007 self.names.forget(name.as_ref());
1008 }
1009 self.users.remove(&key);
1010 self.params.remove(id.local);
1011 }
1012 pub fn edge(&self, id: EdgeId) -> &EdgeData {
1014 &self.edges[id]
1015 }
1016
1017 pub fn push_insn(&mut self, insn: Instruction<'str>) -> InstructionId {
1030 InstructionId::new(self.id(), self.push_insn_local(insn))
1031 }
1032
1033 pub fn push_insn_local(&mut self, insn: Instruction<'str>) -> LocalInsnId {
1037 let args: Vec<LocalValueId> = insn.mnemonic().args().into_iter().collect();
1038 let local = self.insns.push(insn);
1039 for arg in args {
1040 self.users.entry(arg).or_default().push(local);
1041 }
1042 local
1043 }
1044
1045 pub fn push_block(&mut self, block: BasicBlock<'str>) -> BlockId {
1049 let func = self.id();
1050 BlockId::new(func, self.push_block_local(block))
1051 }
1052
1053 pub fn push_block_local(&mut self, block: BasicBlock<'str>) -> LocalBlockId {
1057 let local = self.blocks.push(block);
1058 self.roster.push(local);
1059 local
1060 }
1061
1062 pub fn make_block(&mut self) -> BlockId {
1065 self.push_block(BasicBlock::detached())
1066 }
1067
1068 pub fn make_block_local(&mut self) -> LocalBlockId {
1071 self.push_block_local(BasicBlock::detached())
1072 }
1073
1074 pub fn block_local(&self, block: LocalBlockId) -> &BasicBlock<'str> {
1077 &self.blocks[block]
1078 }
1079
1080 pub fn mnemonic_local(&self, insn: LocalInsnId) -> &Mnemonic {
1083 self.insns[insn].mnemonic()
1084 }
1085
1086 pub fn push_block_param(&mut self, param: BlockParam<'str>) -> BlockParamId {
1088 let local = self.params.push(param);
1089 BlockParamId::new(self.id(), local)
1090 }
1091
1092 pub fn push_block_param_local(
1096 &mut self,
1097 block: LocalBlockId,
1098 param: BlockParam<'str>,
1099 ) -> LocalParamId {
1100 let local = self.params.push(param);
1101 self.blocks[block].params.push(local);
1102 local
1103 }
1104
1105 pub fn append_insn_local(&mut self, block: LocalBlockId, insn: LocalInsnId) {
1109 self.insns[insn].parent = Some(block);
1110 self.blocks[block].instructions.push(insn);
1111 }
1112
1113 pub fn push_mnemonic(
1116 &mut self,
1117 shared: &crate::context::Shared<'str>,
1118 mnemonic: Mnemonic,
1119 size: usize,
1120 ) -> InstructionId {
1121 let type_id = shared.types.get_or_make_int(size);
1122 let insn = Instruction::new(type_id, mnemonic);
1123 self.push_insn(insn)
1124 }
1125
1126 pub fn push_mnemonic_with_type(
1128 &mut self,
1129 mnemonic: Mnemonic,
1130 type_id: crate::types::TypeId,
1131 ) -> InstructionId {
1132 let insn = Instruction::new(type_id, mnemonic);
1133 self.push_insn(insn)
1134 }
1135
1136 pub fn push_mnemonic_with_type_local(
1140 &mut self,
1141 mnemonic: Mnemonic,
1142 type_id: crate::types::TypeId,
1143 ) -> LocalInsnId {
1144 self.push_insn_local(Instruction::new(type_id, mnemonic))
1145 }
1146
1147 pub fn insert_insn_before(
1150 &mut self,
1151 block: BlockId,
1152 before: InstructionId,
1153 insn: InstructionId,
1154 ) {
1155 let index = self
1156 .block(block)
1157 .instructions
1158 .iter()
1159 .position(|&local| InstructionId::new(block.func, local) == before)
1160 .expect("before not in block");
1161 self.insn_mut(insn).parent = Some(block.local);
1162 self.block_mut(block)
1163 .instructions
1164 .insert(index, insn.localize(block.func));
1165 }
1166
1167 pub fn move_insn_before(&mut self, insn: InstructionId, before: InstructionId) {
1173 let id = self.id();
1174 assert_eq!(insn.func, id, "instruction belongs to another function");
1175 assert_eq!(
1176 before.func, id,
1177 "anchor instruction belongs to another function"
1178 );
1179 if insn == before {
1180 return;
1181 }
1182 assert!(
1183 !self.insn(insn).mnemonic().is_terminator(),
1184 "moving a terminator requires updating its CFG edges"
1185 );
1186
1187 let source = self
1188 .insn(insn)
1189 .parent
1190 .map(|local| BlockId::new(id, local))
1191 .expect("moved instruction must belong to a block");
1192 let target = self
1193 .insn(before)
1194 .parent
1195 .map(|local| BlockId::new(id, local))
1196 .expect("anchor instruction must belong to a block");
1197 let source_index = self
1198 .block(source)
1199 .instructions
1200 .iter()
1201 .position(|&local| local == insn.local)
1202 .expect("moved instruction missing from its parent block");
1203 let before_index = self
1204 .block(target)
1205 .instructions
1206 .iter()
1207 .position(|&local| local == before.local)
1208 .expect("anchor instruction missing from its parent block");
1209 let insert_index = if source == target && source_index < before_index {
1210 before_index - 1
1211 } else {
1212 before_index
1213 };
1214
1215 self.block_mut(source).instructions.remove(source_index);
1216 self.block_mut(target)
1217 .instructions
1218 .insert(insert_index, insn.local);
1219 self.insn_mut(insn).parent = Some(target.local);
1220 }
1221
1222 pub fn add_cfg_edge(&mut self, from: BlockId, to: BlockId) -> EdgeId {
1225 self.add_cfg_edge_local(from.local, to.local)
1226 }
1227
1228 pub fn add_cfg_edge_local(&mut self, from: LocalBlockId, to: LocalBlockId) -> EdgeId {
1231 let edge_id = self.edges.push(EdgeData { from, to });
1232 self.blocks[from].edges.insert(edge_id);
1233 self.blocks[to].edges.insert(edge_id);
1234 edge_id
1235 }
1236
1237 pub fn remove_cfg_edge(&mut self, edge_id: EdgeId) {
1240 let EdgeData { from, to } = *self.edge(edge_id);
1241 let func = self.id();
1242 self.block_mut(BlockId::new(func, from))
1243 .edges
1244 .remove(&edge_id);
1245 self.block_mut(BlockId::new(func, to))
1246 .edges
1247 .remove(&edge_id);
1248 self.edges.remove(edge_id);
1249 }
1250
1251 pub fn replace_all_uses_with(&mut self, old: ValueId, new: ValueId) {
1254 if old == new {
1255 return;
1256 }
1257 let Some(func) = old.owning_function() else {
1258 return;
1259 };
1260 assert_eq!(
1261 func,
1262 self.id(),
1263 "cannot replace uses of a value owned by another function"
1264 );
1265 if let Some(new_owner) = new.owning_function() {
1266 assert_eq!(
1267 new_owner,
1268 self.id(),
1269 "cannot replace uses with a value owned by another function"
1270 );
1271 }
1272 let users = self.users_of(old);
1273 let old = old.localize(func);
1274 let new = new.localize(func);
1275 for user in users {
1276 self.insn_mut(user).mnemonic_mut().replace_value(old, new);
1277 self.users.entry(new).or_default().push(user.localize(func));
1278 }
1279 self.users.remove(&old);
1280 }
1281
1282 pub fn replace_instruction(&mut self, id: InstructionId, new: ValueId) {
1287 if new == ValueId::Instruction(id) {
1291 return;
1292 }
1293 self.replace_all_uses_with(ValueId::Instruction(id), new);
1294 self.remove_instruction(id);
1295 }
1296
1297 pub fn remove_instructions(&mut self, dead: &FxHashSet<LocalInsnId>) {
1301 let mut ids: Vec<_> = dead.iter().copied().collect();
1302 ids.sort_unstable();
1303 let mut affected_args: FxHashSet<LocalValueId> = FxHashSet::default();
1304 for &id in &ids {
1305 assert!(
1306 self.insns.contains(id),
1307 "cannot remove stale instruction {id:?}"
1308 );
1309 affected_args.extend(self.insns[id].mnemonic().args());
1310 }
1311 for arg in affected_args {
1312 let remove_key = if let Some(users) = self.users.get_mut(&arg) {
1313 users.retain(|local| !dead.contains(local));
1314 users.is_empty()
1315 } else {
1316 false
1317 };
1318 if remove_key {
1319 self.users.remove(&arg);
1320 }
1321 }
1322 for id in ids {
1323 self.users.remove(&LocalValueId::Instruction(id));
1324 self.insns.remove(id);
1325 }
1326 }
1327
1328 pub fn remove_instruction(&mut self, id: InstructionId) {
1332 assert_eq!(
1333 id.func,
1334 self.id(),
1335 "instruction belongs to another function"
1336 );
1337 let func = self.id();
1338 let (parent, name, is_terminator, args) = {
1339 let insn = self.insn(id);
1340 (
1341 insn.parent.map(|l| BlockId::new(self.id(), l)),
1342 insn.name.clone(),
1343 insn.mnemonic().is_terminator(),
1344 insn.mnemonic().args().into_iter().collect::<Vec<_>>(),
1345 )
1346 };
1347
1348 if let Some(block_id) = parent {
1349 self.block_mut(block_id)
1350 .instructions
1351 .retain(|&local| local != id.localize(block_id.func));
1352 if is_terminator {
1353 let mut succ: Vec<EdgeId> = {
1354 let block = self.block(block_id);
1355 block
1356 .edges
1357 .iter()
1358 .copied()
1359 .filter(|&e| self.edge(e).from == block_id.local)
1360 .collect()
1361 };
1362 succ.sort_unstable();
1363 for edge_id in succ {
1364 self.remove_cfg_edge(edge_id);
1365 }
1366 }
1367 }
1368
1369 if let Some(n) = name {
1370 self.names.forget(n.as_ref());
1371 }
1372 for arg in args {
1373 let remove_key = if let Some(users) = self.users.get_mut(&arg) {
1374 users.retain(|&local| local != id.localize(func));
1375 users.is_empty()
1376 } else {
1377 false
1378 };
1379 if remove_key {
1380 self.users.remove(&arg);
1381 }
1382 }
1383 self.users.remove(&ValueId::Instruction(id).strip_func());
1384 self.insns.remove(id.local);
1385 }
1386
1387 pub fn remove_block_instructions(&mut self, block_id: BlockId, dead: &FxHashSet<LocalInsnId>) {
1400 assert_eq!(
1401 block_id.func,
1402 self.id(),
1403 "block belongs to another function"
1404 );
1405 if dead.is_empty() {
1406 return;
1407 }
1408
1409 let mut names = Vec::new();
1410 for &id in dead {
1411 let insn = &self.insns[id];
1412 assert!(
1413 !insn.mnemonic().is_terminator(),
1414 "bulk removal does not unlink CFG edges; {id:?} is a terminator"
1415 );
1416 if let Some(name) = insn.name.clone() {
1417 names.push(name);
1418 }
1419 }
1420
1421 self.block_mut(block_id)
1422 .instructions
1423 .retain(|local| !dead.contains(local));
1424 self.purge_instructions(dead, names);
1425 }
1426
1427 fn purge_instructions(&mut self, dead: &FxHashSet<LocalInsnId>, names: Vec<Cow<'str, str>>) {
1435 for name in names {
1436 self.names.forget(name.as_ref());
1437 }
1438 let mut operands: FxHashSet<LocalValueId> = FxHashSet::default();
1440 for &id in dead {
1441 operands.extend(self.insns[id].mnemonic().args());
1442 }
1443 for arg in operands {
1444 let now_empty = if let Some(users) = self.users.get_mut(&arg) {
1445 users.retain(|local| !dead.contains(local));
1446 users.is_empty()
1447 } else {
1448 false
1449 };
1450 if now_empty {
1451 self.users.remove(&arg);
1452 }
1453 }
1454 for &id in dead {
1455 self.users.remove(&LocalValueId::Instruction(id));
1456 self.insns.remove(id);
1457 }
1458 }
1459
1460 pub fn rehome_outgoing_edges(&mut self, keep: BlockId, remove: BlockId) {
1463 let outgoing: Vec<EdgeId> = {
1464 let block = self.block(remove);
1465 block
1466 .edges
1467 .iter()
1468 .copied()
1469 .filter(|&e| self.edge(e).from == remove.local)
1470 .collect()
1471 };
1472 for eid in outgoing {
1473 self.edges[eid].from = keep.local;
1474 self.block_mut(keep).edges.insert(eid);
1475 self.block_mut(remove).edges.remove(&eid);
1476 }
1477 }
1478
1479 pub fn replace_instruction_mnemonic(&mut self, id: InstructionId, mnemonic: Mnemonic) {
1482 assert_eq!(
1483 id.func,
1484 self.id(),
1485 "instruction belongs to another function"
1486 );
1487 self.replace_instruction_mnemonic_local(id.local, mnemonic);
1488 }
1489
1490 pub fn replace_instruction_mnemonic_local(&mut self, id: LocalInsnId, mnemonic: Mnemonic) {
1495 let old_args = self.insns[id]
1496 .mnemonic()
1497 .args()
1498 .into_iter()
1499 .collect::<Vec<_>>();
1500 for arg in old_args {
1501 let now_empty = if let Some(users) = self.users.get_mut(&arg) {
1502 users.retain(|&local| local != id);
1503 users.is_empty()
1504 } else {
1505 false
1506 };
1507 if now_empty {
1508 self.users.remove(&arg);
1509 }
1510 }
1511 *self.insns[id].mnemonic_mut() = mnemonic;
1512 let new_args = self.insns[id]
1513 .mnemonic()
1514 .args()
1515 .into_iter()
1516 .collect::<Vec<_>>();
1517 for arg in new_args {
1518 self.users.entry(arg).or_default().push(id);
1519 }
1520 }
1521
1522 pub fn rename_block_local(&mut self, block: LocalBlockId, name: Cow<'str, str>) -> Result<()> {
1528 let target = LocalValueId::BasicBlock(block);
1529 if let Some(existing) = self.names.get(&name) {
1530 return if existing == target {
1531 Ok(())
1532 } else {
1533 Err(Error::spanless(ErrorTy::DuplicateName(name.to_string())))
1534 };
1535 }
1536 let old_name = self.blocks[block].local_name().map(str::to_owned);
1537 self.names
1538 .register(name.clone(), target, old_name.as_deref())?;
1539 self.blocks[block].set_name(Some(name));
1540 Ok(())
1541 }
1542
1543 pub fn unroster_block(&mut self, block: BlockId) {
1546 self.roster.retain(|&b| b != block.localize(block.func));
1547 }
1548
1549 pub fn clear_block_instructions(&mut self, block: BlockId) {
1559 assert_eq!(block.func, self.id(), "block belongs to another function");
1560 let mut outgoing: Vec<EdgeId> = self
1561 .block(block)
1562 .edges
1563 .iter()
1564 .copied()
1565 .filter(|&edge| self.edges[edge].from == block.local)
1566 .collect();
1567 outgoing.sort_unstable();
1568 for edge in outgoing {
1569 self.remove_cfg_edge(edge);
1570 }
1571 let insns = std::mem::take(&mut self.block_mut(block).instructions);
1577 let dead: FxHashSet<LocalInsnId> = insns.iter().copied().collect();
1578 let names: Vec<Cow<'str, str>> = insns
1579 .iter()
1580 .filter_map(|&local| self.insns[local].name.clone())
1581 .collect();
1582 self.purge_instructions(&dead, names);
1583 }
1584
1585 pub fn delete_block(&mut self, block: BlockId) {
1586 assert_eq!(block.func, self.id(), "block belongs to another function");
1587 let mut edges: Vec<EdgeId> = self.block(block).edges.iter().copied().collect();
1588 edges.sort_unstable();
1589 for edge in edges {
1590 self.remove_cfg_edge(edge);
1591 }
1592 let insns: Vec<InstructionId> = self
1593 .block(block)
1594 .instructions
1595 .iter()
1596 .map(|&local| InstructionId::new(self.id(), local))
1597 .collect();
1598 for insn in insns {
1599 self.remove_instruction(insn);
1600 }
1601 let params: Vec<BlockParamId> = self
1602 .block(block)
1603 .params
1604 .iter()
1605 .map(|&local| BlockParamId::new(self.id(), local))
1606 .collect();
1607 for param in params {
1608 self.remove_block_param(param);
1609 }
1610 let name = self.block(block).local_name().map(str::to_owned);
1611 self.unroster_block(block);
1612 if self.root == Some(block.local) {
1613 self.root = None;
1614 }
1615 if let Some(name) = name {
1616 self.names.forget(&name);
1617 }
1618 self.blocks.remove(block.local);
1619 }
1620
1621 pub fn absorb_block(&mut self, keep: BlockId, other: BlockId, edge_ab: EdgeId) {
1625 assert_eq!(
1626 keep.func, other.func,
1627 "cannot absorb across function arenas"
1628 );
1629 let (branch_id, branch_args) = self
1630 .block(keep)
1631 .instructions
1632 .last()
1633 .and_then(
1634 |&local| match self.insn(InstructionId::new(keep.func, local)).mnemonic() {
1635 Mnemonic::Branch(branch) if BlockId::new(keep.func, branch.target) == other => {
1636 Some((InstructionId::new(keep.func, local), branch.args.clone()))
1637 }
1638 _ => None,
1639 },
1640 )
1641 .expect("absorbed block must be reached by keep's terminal branch");
1642 let other_params: Vec<_> = self
1643 .block(other)
1644 .params
1645 .iter()
1646 .map(|&local| BlockParamId::new(other.func, local))
1647 .collect();
1648 if !other_params.is_empty() {
1649 assert_eq!(
1650 other_params.len(),
1651 branch_args.len(),
1652 "cannot absorb block with {} params through branch with {} args",
1653 other_params.len(),
1654 branch_args.len()
1655 );
1656 for (param, arg) in other_params.iter().copied().zip(branch_args) {
1657 self.replace_all_uses_with(ValueId::BlockParam(param), arg.qualify(keep.func));
1658 }
1659 }
1660 self.remove_cfg_edge(edge_ab);
1661 self.remove_instruction(branch_id);
1662 let b_insns = std::mem::take(&mut self.block_mut(other).instructions);
1663 for &local in &b_insns {
1664 self.insn_mut(InstructionId::new(other.func, local)).parent = Some(keep.local);
1665 }
1666 self.block_mut(keep).instructions.extend(b_insns);
1667 self.rehome_outgoing_edges(keep, other);
1668 let (b_addr, b_extra, b_name) = {
1669 let b = self.block(other);
1670 (
1671 b.address,
1672 b.extra_addresses.clone(),
1673 b.local_name().map(str::to_owned),
1674 )
1675 };
1676 for param in other_params {
1677 self.remove_block_param(param);
1678 }
1679 self.unroster_block(other);
1680 if self.root == Some(other.local) {
1681 self.root = Some(keep.local);
1682 }
1683 if let Some(name) = b_name {
1684 self.names.forget(&name);
1685 }
1686 self.blocks.remove(other.local);
1687 if let Some(addr) = b_addr {
1688 self.block_mut(keep).extra_addresses.push(addr);
1689 }
1690 self.block_mut(keep).extra_addresses.extend(b_extra);
1691 }
1692
1693 pub fn register_local_name(
1698 &mut self,
1699 shared: &crate::context::Shared<'str>,
1700 id: ValueId,
1701 name: Cow<'str, str>,
1702 old_name: Option<&str>,
1703 ) -> Result<()> {
1704 if id.name_scope_function().is_none() {
1705 return match shared.get_named(&name) {
1706 Some(existing) if existing == id => Ok(()),
1707 Some(_) => Err(Error::spanless(ErrorTy::DuplicateName(name.to_string()))),
1708 None => unimplemented!(
1709 "a function body cannot register a global name (shared is read-only)"
1710 ),
1711 };
1712 }
1713 self.register_body_name(id, name, old_name)
1714 }
1715
1716 pub fn register_body_name(
1721 &mut self,
1722 id: ValueId,
1723 name: Cow<'str, str>,
1724 old_name: Option<&str>,
1725 ) -> Result<()> {
1726 assert!(
1727 id.name_scope_function().is_some(),
1728 "register_body_name on a global-scoped value {id:?}"
1729 );
1730 if let Some(existing) = self.names.get(&name).map(|id| id.qualify(self.id())) {
1731 return if existing == id {
1732 Ok(())
1733 } else {
1734 Err(Error::spanless(ErrorTy::DuplicateName(name.to_string())))
1735 };
1736 }
1737 self.names.register(name, id.localize(self.id()), old_name)
1738 }
1739
1740 pub fn from_id<'ctx>(ctx: &'ctx Context<'str>, id: FunctionId) -> FunctionRef<'str, 'ctx> {
1742 FunctionRef::new(ModuleView::new(ctx), id)
1743 }
1744
1745 pub fn from_id_mut<'ctx>(
1747 ctx: &'ctx mut Context<'str>,
1748 id: FunctionId,
1749 ) -> FunctionMutRef<'str, 'ctx> {
1750 FunctionMutRef::new(ctx, id)
1751 }
1752
1753 pub fn from_name<'ctx>(
1755 ctx: &'ctx Context<'str>,
1756 name: &str,
1757 ) -> Option<FunctionRef<'str, 'ctx>> {
1758 ctx.get_named(name)
1759 .and_then(ValueId::as_function)
1760 .map(|id| FunctionBody::from_id(ctx, id))
1761 }
1762
1763 pub fn make<'ctx>(
1765 ctx: &'ctx mut Context<'str>,
1766 name: Cow<'str, str>,
1767 ) -> Result<FunctionMutRef<'str, 'ctx>> {
1768 let id = FunctionId::from(ctx.bodies.len());
1769 let pushed = ctx.push_function(
1770 FunctionInterface::new(name.clone()),
1771 FunctionBody::empty_with_id(id),
1772 );
1773 debug_assert_eq!(pushed, id);
1774 ctx.update_name(name, id.into(), None)?;
1775 Ok(Self::from_id_mut(ctx, id))
1776 }
1777
1778 pub fn make_lambda<'ctx>(
1780 ctx: &'ctx mut Context<'str>,
1781 name: Cow<'str, str>,
1782 ) -> Result<FunctionMutRef<'str, 'ctx>> {
1783 let mut function = Self::make(ctx, name)?;
1784 function.interface_mut().kind = FunctionKind::Lambda;
1785 function.set_is_pure(true);
1786 function.set_register_effects(RegisterChannelState::Materialized(
1787 RegisterInterfaceMap::default(),
1788 ));
1789 Ok(function)
1790 }
1791
1792 pub fn make_at_addr<'ctx>(
1794 ctx: &'ctx mut Context<'str>,
1795 address: u64,
1796 name: Option<Cow<'str, str>>,
1797 ) -> FunctionMutRef<'str, 'ctx> {
1798 let mut addresses = crate::address_index::AddressIndex::analyze(ctx);
1799 Self::make_at_addr_indexed(ctx, &mut addresses, address, name)
1800 }
1801
1802 pub fn make_at_addr_indexed<'ctx>(
1804 ctx: &'ctx mut Context<'str>,
1805 addresses: &mut crate::address_index::AddressIndex,
1806 address: u64,
1807 name: Option<Cow<'str, str>>,
1808 ) -> FunctionMutRef<'str, 'ctx> {
1809 let name = name.unwrap_or_else(|| Cow::Owned(format!("fn_{address:x}")));
1810 let id = FunctionId::from(ctx.bodies.len());
1811 let pushed = ctx.push_function(
1812 FunctionInterface::new(name.clone()),
1813 FunctionBody::empty_with_id(id),
1814 );
1815 debug_assert_eq!(pushed, id);
1816
1817 Self::from_id_mut(ctx, id)
1818 .with_name(name)
1819 .expect("Function name is not unique")
1820 .with_address_indexed(addresses, address)
1821 .expect("Function address is not unique")
1822 }
1823
1824 pub fn make_external<'ctx>(
1829 ctx: &'ctx mut Context<'str>,
1830 address: u64,
1831 name: Option<Cow<'str, str>>,
1832 ) -> FunctionMutRef<'str, 'ctx> {
1833 let mut f = Self::make_at_addr(ctx, address, name);
1834 f.interface_mut().is_external = true;
1835 f
1836 }
1837
1838 pub fn make_external_indexed<'ctx>(
1840 ctx: &'ctx mut Context<'str>,
1841 addresses: &mut crate::address_index::AddressIndex,
1842 address: u64,
1843 name: Option<Cow<'str, str>>,
1844 ) -> FunctionMutRef<'str, 'ctx> {
1845 let mut function = Self::make_at_addr_indexed(ctx, addresses, address, name);
1846 function.interface_mut().is_external = true;
1847 function
1848 }
1849
1850 pub fn from_addr_or_create<'ctx>(
1852 ctx: &'ctx mut Context<'str>,
1853 address: u64,
1854 ) -> FunctionMutRef<'str, 'ctx> {
1855 let mut addresses = crate::address_index::AddressIndex::analyze(ctx);
1856 Self::from_addr_or_create_indexed(ctx, &mut addresses, address)
1857 }
1858
1859 pub fn from_addr_or_create_indexed<'ctx>(
1862 ctx: &'ctx mut Context<'str>,
1863 addresses: &mut crate::address_index::AddressIndex,
1864 address: u64,
1865 ) -> FunctionMutRef<'str, 'ctx> {
1866 match addresses.function_at(address) {
1867 Some(id) => Self::from_id_mut(ctx, id),
1868 None => Self::make_at_addr_indexed(ctx, addresses, address, None),
1869 }
1870 }
1871}
1872
1873impl<'s, 'ctx: 's, 'str: 'ctx, R> FunctionRef<'str, 'ctx, R>
1874where
1875 R: QCodeView<'ctx, 'str>,
1876{
1877 fn inner(&'s self) -> &'ctx FunctionBody<'str> {
1878 self.view.function(self.id)
1879 }
1880
1881 fn interface(&'s self) -> &'ctx FunctionInterface<'str> {
1884 self.view.interface(self.id)
1885 }
1886
1887 fn size(&self) -> usize {
1888 0
1889 }
1890
1891 pub fn address(&'s self) -> Option<u64> {
1893 self.interface().address
1894 }
1895
1896 pub fn is_external(&'s self) -> bool {
1898 self.interface().is_external
1899 }
1900
1901 pub fn import_ordinal(&'s self) -> Option<u16> {
1904 self.interface().import_ordinal
1905 }
1906
1907 pub fn signature(&'s self) -> Option<&'ctx FunctionSignature> {
1909 self.interface().signature.as_ref()
1910 }
1911
1912 pub fn users_of(&'s self, value: ValueId) -> Vec<InstructionId> {
1916 let func = self.id;
1917 if value.owning_function().is_some_and(|owner| owner != func) {
1918 return Vec::new();
1919 }
1920 self.inner().users_of(value)
1921 }
1922
1923 pub fn local_users_of(&'s self, value: ValueId) -> &'ctx [LocalInsnId] {
1929 let func = self.id;
1930 if value.owning_function().is_some_and(|owner| owner != func) {
1931 return &[];
1932 }
1933 self.inner().local_users_of(value)
1934 }
1935
1936 pub fn has_users(&'s self, value: ValueId) -> bool {
1939 let func = self.id;
1940 if value.owning_function().is_some_and(|owner| owner != func) {
1941 return false;
1942 }
1943 self.inner().has_users(value)
1944 }
1945
1946 pub fn user_map_entries(&'s self) -> impl Iterator<Item = (ValueId, Vec<InstructionId>)> + 's {
1949 let func = self.id;
1950 self.inner().user_map_entries().map(move |(v, u)| {
1951 (
1952 v.qualify(func),
1953 u.iter()
1954 .map(|&local| InstructionId::new(func, local))
1955 .collect(),
1956 )
1957 })
1958 }
1959
1960 pub fn local_named(&'s self, name: &str) -> Option<ValueId> {
1963 self.inner().names.get(name).map(|id| id.qualify(self.id))
1964 }
1965
1966 pub fn param_attr(&'s self, index: usize) -> Option<ParamAttrs> {
1971 self.interface().param_attr(index)
1972 }
1973
1974 pub fn param_attrs(&'s self) -> Option<&'ctx [ParamAttrs]> {
1976 self.interface()
1977 .signature
1978 .as_ref()
1979 .and_then(|s| s.param_attrs.as_deref())
1980 }
1981
1982 pub fn written_spaces(&'s self) -> Option<&'ctx [crate::space::SpaceId]> {
1989 match &self.interface().effects.memory.coarse {
1990 WrittenSpacesState::Bounded(spaces) => Some(spaces),
1991 _ => None,
1992 }
1993 }
1994
1995 pub fn written_spaces_state(&'s self) -> WrittenSpaces<'ctx> {
1999 match &self.interface().effects.memory.coarse {
2000 WrittenSpacesState::Unstamped => WrittenSpaces::Unstamped,
2001 WrittenSpacesState::Unbounded => WrittenSpaces::Unbounded,
2002 WrittenSpacesState::Bounded(spaces) => WrittenSpaces::Bounded(spaces),
2003 }
2004 }
2005
2006 pub fn is_reg_materialized(&'s self) -> bool {
2011 matches!(
2012 self.interface().effects.register,
2013 RegisterChannelState::Materialized(_)
2014 )
2015 }
2016
2017 pub fn effects(&'s self) -> &'ctx FunctionEffects {
2021 &self.interface().effects
2022 }
2023
2024 pub fn is_pure(&'s self) -> bool {
2029 self.interface()
2030 .signature
2031 .as_ref()
2032 .is_some_and(|s| s.is_pure)
2033 }
2034
2035 pub fn is_lambda(&'s self) -> bool {
2037 self.interface().kind == FunctionKind::Lambda
2038 }
2039
2040 pub fn kind(&'s self) -> FunctionKind {
2041 self.interface().kind
2042 }
2043
2044 pub fn extern_interface(&'s self) -> Option<&'ctx crate::value::ExternInterface> {
2048 self.interface()
2049 .signature
2050 .as_ref()
2051 .and_then(|s| s.extern_interface.as_ref())
2052 }
2053
2054 pub fn argmem(&'s self) -> Option<&'ctx crate::value::ExternArgmem> {
2058 self.interface()
2059 .signature
2060 .as_ref()
2061 .and_then(|s| s.argmem.as_ref())
2062 }
2063
2064 pub fn input_arg_name(&'s self, index: usize) -> Option<String> {
2070 if let Some(root) = self.root()
2076 && let Some(name) = root
2077 .params()
2078 .nth(index)
2079 .and_then(|p| p.name().map(str::to_owned))
2080 {
2081 return Some(name);
2082 }
2083
2084 self.extern_interface()
2087 .and_then(|iface| iface.args.get(index))
2088 .and_then(|a| a.name.as_ref().map(|n| n.to_string()))
2089 }
2090
2091 pub fn reads_unbounded_stack(&'s self) -> bool {
2095 self.interface()
2096 .signature
2097 .as_ref()
2098 .is_some_and(|s| s.reads_unbounded_stack)
2099 }
2100
2101 pub fn frame_escapes_to_unbounded(&'s self) -> bool {
2105 self.interface()
2106 .signature
2107 .as_ref()
2108 .is_some_and(|s| s.frame_escapes_to_unbounded)
2109 }
2110
2111 pub fn name(&'s self) -> &'ctx str {
2113 self.interface().name.as_ref()
2114 }
2115
2116 pub fn instruction_addrs(&'s self) -> impl Iterator<Item = u64> + 'ctx {
2120 self.inner().instruction_addrs.iter().copied()
2121 }
2122
2123 pub fn has_map(&'s self) -> bool {
2127 self.blocks().any(|block| {
2128 block
2129 .instructions()
2130 .any(|insn| matches!(insn.mnemonic(), Mnemonic::Map(_)))
2131 })
2132 }
2133
2134 pub fn has_scan(&'s self) -> bool {
2138 self.blocks().any(|block| {
2139 block
2140 .instructions()
2141 .any(|insn| matches!(insn.mnemonic(), Mnemonic::Scan(_)))
2142 })
2143 }
2144
2145 pub fn root(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
2147 self.inner()
2148 .root
2149 .map(|local| BlockRef::new(self.view, BlockId::new(self.id, local)))
2150 }
2151
2152 pub fn blocks(&'s self) -> impl Iterator<Item = BlockRef<'str, 'ctx, R>> + 's {
2154 let view = self.view;
2155 let mut ids = self.block_ids();
2156 ids.sort_by_key(|&id| (BlockRef::new(view, id).address(), id.local));
2160 ids.into_iter().map(move |id| BlockRef::new(view, id))
2161 }
2162
2163 pub fn block_ids(&'s self) -> Vec<BlockId> {
2165 let func = self.id;
2166 self.inner()
2167 .roster
2168 .iter()
2169 .copied()
2170 .map(|local| BlockId::new(func, local))
2171 .collect()
2172 }
2173
2174 pub fn instruction_ids(&'s self) -> Vec<InstructionId> {
2177 let func = self.id;
2178 self.inner()
2179 .insns
2180 .iter()
2181 .map(|i| InstructionId::new(func, i.id))
2182 .collect()
2183 }
2184
2185 pub fn edge_ids(&'s self) -> Vec<crate::value::block::EdgeId> {
2188 self.inner().edges.iter().map(|e| e.id).collect()
2189 }
2190
2191 pub fn iter(&'s self) -> BlockIter<'str, 'ctx, R> {
2194 BlockIter {
2195 view: self.view,
2196 inner: self.block_ids().into_iter(),
2197 marker: PhantomData,
2198 }
2199 }
2200
2201 fn fmt(&'s self, f: &mut Formatter<'_>) -> std::fmt::Result {
2202 if self.is_external() {
2203 return writeln!(f, "extern fn {};", self.name());
2204 }
2205 let keyword = match self.kind() {
2206 FunctionKind::Machine => "fn",
2207 FunctionKind::Lambda => "lambda",
2208 };
2209 writeln!(f, "{keyword} {}:", self.name())?;
2210 for block in self.blocks() {
2211 block.fmt(f)?;
2212 }
2213 Ok(())
2214 }
2215}
2216
2217#[derive(Clone, Copy)]
2218pub struct FunctionRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
2219 pub id: FunctionId,
2220 pub(in crate::value) view: R,
2221 marker: PhantomData<&'ctx &'str ()>,
2222}
2223
2224impl<'str, 'ctx, R> FunctionRef<'str, 'ctx, R> {
2225 pub fn new(view: R, id: FunctionId) -> Self {
2226 Self {
2227 id,
2228 view,
2229 marker: PhantomData,
2230 }
2231 }
2232
2233 pub fn id(&self) -> ValueId {
2234 self.id.into()
2235 }
2236}
2237
2238impl<'str, 'ctx> FunctionRef<'str, 'ctx> {
2239 pub fn from_id(ctx: &'ctx Context<'str>, id: FunctionId) -> Self {
2240 Self::new(ModuleView::new(ctx), id)
2241 }
2242}
2243
2244impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 'ctx, 'str> for FunctionRef<'str, 'ctx> {
2245 fn ctx(&'s self) -> &'ctx Context<'str> {
2246 self.view.context()
2250 }
2251}
2252
2253impl<'str: 'ctx, 'ctx, R> Named for FunctionRef<'str, 'ctx, R>
2254where
2255 R: QCodeView<'ctx, 'str>,
2256{
2257 fn name(&self) -> Option<&str> {
2258 Some(self.view.interface(self.id).name.as_ref())
2259 }
2260}
2261
2262impl<'str: 'ctx, 'ctx, R> Display for FunctionRef<'str, 'ctx, R>
2263where
2264 R: QCodeView<'ctx, 'str>,
2265{
2266 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2267 FunctionRef::fmt(self, f)
2268 }
2269}
2270
2271impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for FunctionRef<'str, 'ctx, R>
2272where
2273 R: QCodeView<'ctx, 'str>,
2274{
2275 fn id(&self) -> ValueId {
2276 self.id()
2277 }
2278
2279 fn size(&self) -> usize {
2280 FunctionRef::size(self)
2281 }
2282}
2283
2284pub struct BlockIter<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
2285 view: R,
2286 inner: std::vec::IntoIter<BlockId>,
2287 marker: PhantomData<&'ctx &'str ()>,
2288}
2289
2290impl<'str: 'ctx, 'ctx, R> Iterator for BlockIter<'str, 'ctx, R>
2291where
2292 R: QCodeView<'ctx, 'str>,
2293{
2294 type Item = BlockRef<'str, 'ctx, R>;
2295
2296 fn next(&mut self) -> Option<Self::Item> {
2297 self.inner.next().map(|id| BlockRef::new(self.view, id))
2298 }
2299}
2300
2301impl<'str: 'ctx, 'ctx, R> IntoIterator for &FunctionRef<'str, 'ctx, R>
2302where
2303 R: QCodeView<'ctx, 'str>,
2304{
2305 type Item = BlockRef<'str, 'ctx, R>;
2306 type IntoIter = BlockIter<'str, 'ctx, R>;
2307
2308 fn into_iter(self) -> Self::IntoIter {
2309 self.iter()
2310 }
2311}
2312
2313pub type FunctionMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, FunctionId>;
2314
2315impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for FunctionMutRef<'str, 'ctx> {
2316 fn ctx(&'s self) -> &'s Context<'str> {
2317 self.ctx
2318 }
2319}
2320
2321impl<'s, 'ctx: 's, 'str: 'ctx> WithCtxMut<'s, 'str> for FunctionMutRef<'str, 'ctx> {
2322 fn ctx_mut(&'s mut self) -> &'s mut Context<'str> {
2323 self.ctx
2324 }
2325}
2326
2327impl Display for FunctionMutRef<'_, '_> {
2328 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2329 self.as_ref().fmt(f)
2330 }
2331}
2332
2333impl<'ctx, 'str> Value<'str, 'ctx> for FunctionMutRef<'str, 'ctx> {
2334 fn id(&self) -> ValueId {
2335 self.id()
2336 }
2337
2338 fn size(&self) -> usize {
2339 self.as_ref().size()
2340 }
2341}
2342
2343impl Named for FunctionMutRef<'_, '_> {
2344 fn name(&self) -> Option<&str> {
2345 Some(self.ctx.interfaces[self.id].name.as_ref())
2346 }
2347}
2348
2349impl<'str, 'ctx> Renameable<'str, 'ctx> for FunctionMutRef<'str, 'ctx> {
2350 fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
2351 let id = self.id();
2352 let old_name = self.ctx.interfaces[self.id].name.as_ref().to_owned();
2353 update_context_name(id, self.ctx, name.clone(), Some(old_name.as_ref()))?;
2354 self.ctx.interfaces[self.id].name = name;
2355 Ok(())
2356 }
2357}
2358
2359impl<'str, 'ctx> FunctionMutRef<'str, 'ctx> {
2360 pub fn as_ref(&self) -> FunctionRef<'str, '_> {
2361 FunctionRef::new(ModuleView::new(self.ctx), self.id)
2362 }
2363
2364 fn inner(&self) -> &FunctionBody<'str> {
2365 self.ctx.function(self.id)
2366 }
2367
2368 fn interface(&self) -> &FunctionInterface<'str> {
2369 &self.ctx.interfaces[self.id]
2370 }
2371
2372 fn address(&self) -> Option<u64> {
2373 self.interface().address
2374 }
2375
2376 pub fn name(&self) -> &str {
2377 self.interface().name.as_ref()
2378 }
2379
2380 pub fn blocks(&self) -> impl Iterator<Item = BlockRef<'str, '_>> {
2381 self.as_ref().blocks().collect::<Vec<_>>().into_iter()
2382 }
2383
2384 pub fn root(&self) -> Option<BlockRef<'str, '_>> {
2385 self.as_ref().root()
2386 }
2387
2388 pub(crate) fn inner_mut(&mut self) -> &mut FunctionBody<'str> {
2389 &mut self.ctx.bodies[self.id]
2390 }
2391
2392 pub(crate) fn interface_mut(&mut self) -> &mut FunctionInterface<'str> {
2395 &mut self.ctx.interfaces[self.id]
2396 }
2397
2398 fn set_address(&mut self, address: u64) -> Result<()> {
2399 let mut addresses = crate::address_index::AddressIndex::analyze(&*self.ctx);
2400 self.set_address_indexed(&mut addresses, address)
2401 }
2402
2403 fn set_address_indexed(
2404 &mut self,
2405 addresses: &mut crate::address_index::AddressIndex,
2406 address: u64,
2407 ) -> Result<()> {
2408 let old_address = self.interface().address;
2409 self.interface_mut().address = Some(address);
2410 if let Err(error) = self
2411 .ctx
2412 .set_address_indexed(addresses, address, self.id.into())
2413 {
2414 self.interface_mut().address = old_address;
2415 return Err(error);
2416 }
2417 Ok(())
2418 }
2419
2420 fn with_address_indexed(
2421 mut self,
2422 addresses: &mut crate::address_index::AddressIndex,
2423 address: u64,
2424 ) -> Result<Self> {
2425 self.set_address_indexed(addresses, address)?;
2426 Ok(self)
2427 }
2428
2429 pub fn set_root(&mut self, id: BlockId) -> Result<()> {
2434 assert_eq!(
2435 id.func, self.id,
2436 "cannot root a function at a block stored in another function arena"
2437 );
2438 self.add_block(id);
2439 self.inner_mut().root = Some(id.localize(self.id));
2440
2441 let block_addr = BasicBlock::from_id(&*self.ctx, id).address();
2442 let self_addr = self.address();
2443
2444 match (self_addr, block_addr) {
2445 (Some(fn_addr), Some(block_addr)) if fn_addr != block_addr => {
2446 return Err(Error::spanless(ErrorTy::FunctionRootAddressMismatch {
2447 fn_addr,
2448 block_addr,
2449 }));
2450 }
2451 (None, Some(addr)) => {
2452 self.set_address(addr)
2453 .expect("This address should be valid");
2454 }
2455 (Some(addr), None) => {
2456 BasicBlock::from_id_mut(self.ctx, id)
2457 .set_address(addr)
2458 .expect("This address should be valid");
2459 }
2460 _ => {}
2461 }
2462 Ok(())
2463 }
2464
2465 pub fn make_root(&mut self) -> BlockRef<'str, '_> {
2466 let func = self.id;
2467 let root = BasicBlock::make(self.ctx, func).id;
2468 self.set_root(root).expect("We just created the block");
2469 BasicBlock::from_id(&*self.ctx, root)
2470 }
2471
2472 pub fn ensure_root(&mut self, id: BlockId) -> Result<()> {
2473 assert_eq!(
2474 id.func, self.id,
2475 "cannot ensure a function root from another function arena"
2476 );
2477 if let Some(root) = self.inner().root {
2478 if root != id.localize(self.id) {
2479 return Err(Error::spanless(ErrorTy::FunctionRootMismatch {
2480 expected: BlockId::new(self.id, root),
2481 actual: id,
2482 }));
2483 }
2484 Ok(())
2485 } else {
2486 self.set_root(id)
2487 }
2488 }
2489
2490 pub fn set_external(&mut self, is_external: bool) {
2491 self.interface_mut().is_external = is_external;
2492 assert!(
2493 self.inner().blocks.is_empty(),
2494 "External functions should not have blocks"
2495 );
2496 }
2497
2498 pub fn set_import_ordinal(&mut self, ordinal: Option<u16>) {
2502 self.interface_mut().import_ordinal = ordinal;
2503 }
2504
2505 pub fn set_kind(&mut self, kind: FunctionKind) {
2506 self.interface_mut().kind = kind;
2507 if kind == FunctionKind::Lambda {
2508 self.set_is_pure(true);
2509 self.set_register_effects(RegisterChannelState::Materialized(
2510 RegisterInterfaceMap::default(),
2511 ));
2512 }
2513 }
2514
2515 pub fn set_signature(&mut self, sig: FunctionSignature) {
2516 self.ctx.interfaces[self.id].signature = Some(sig);
2517 }
2518
2519 pub fn set_param_attrs(&mut self, attrs: Vec<ParamAttrs>) {
2522 self.interface_mut()
2523 .signature
2524 .get_or_insert_default()
2525 .param_attrs = Some(attrs);
2526 }
2527
2528 pub fn clear_param_attrs(&mut self) {
2531 if let Some(sig) = self.interface_mut().signature.as_mut() {
2532 sig.param_attrs = None;
2533 }
2534 }
2535
2536 pub fn set_written_spaces(&mut self, spaces: Option<Vec<crate::space::SpaceId>>) {
2542 let coarse = match spaces {
2543 Some(spaces) => WrittenSpacesState::Bounded(spaces),
2544 None => WrittenSpacesState::Unbounded,
2545 };
2546 let precise = self.interface_mut().effects.memory.precise.take();
2549 self.set_memory_solved(coarse, precise);
2550 }
2551
2552 pub fn set_extern_interface(&mut self, iface: crate::value::ExternInterface) {
2556 self.interface_mut()
2557 .signature
2558 .get_or_insert_default()
2559 .extern_interface = Some(iface);
2560 }
2561
2562 pub fn set_argmem(&mut self, argmem: crate::value::ExternArgmem) {
2566 self.interface_mut()
2567 .signature
2568 .get_or_insert_default()
2569 .argmem = Some(argmem);
2570 }
2571
2572 pub fn set_register_effects(&mut self, register: RegisterChannelState) {
2575 self.interface_mut().effects.register = register;
2576 }
2577
2578 pub fn set_memory_effects(&mut self, memory: MemoryChannelState) {
2589 self.interface_mut().effects.memory = memory;
2590 }
2591
2592 pub fn set_memory_solved(&mut self, coarse: WrittenSpacesState, precise: Option<Footprint>) {
2598 let memory = &mut self.interface_mut().effects.memory;
2599 memory.coarse = coarse;
2600 memory.precise = precise;
2601 }
2602
2603 pub fn set_memory_interface(&mut self, materialized: Option<MemoryInterfaceMap>) {
2606 self.interface_mut().effects.memory.materialized = materialized;
2607 }
2608
2609 pub fn set_is_pure(&mut self, value: bool) {
2613 self.interface_mut()
2614 .signature
2615 .get_or_insert_default()
2616 .is_pure = value;
2617 }
2618
2619 pub fn set_reads_unbounded_stack(&mut self, value: bool) {
2622 self.interface_mut()
2623 .signature
2624 .get_or_insert_default()
2625 .reads_unbounded_stack = value;
2626 }
2627
2628 pub fn set_frame_escapes_to_unbounded(&mut self, value: bool) {
2632 self.interface_mut()
2633 .signature
2634 .get_or_insert_default()
2635 .frame_escapes_to_unbounded = value;
2636 }
2637
2638 pub fn add_instruction_addr(&mut self, addr: u64) {
2640 self.inner_mut().instruction_addrs.insert(addr);
2641 }
2642
2643 pub fn add_block(&mut self, id: BlockId) {
2651 assert_eq!(
2652 id.func, self.id,
2653 "cannot add a block stored in another function arena"
2654 );
2655 let local = id.localize(self.id);
2656 if !self.inner().roster.contains(&local) {
2659 self.inner_mut().roster.push(local);
2660 }
2661 }
2662}
2663
2664#[cfg(test)]
2665mod tests {
2666 use wazabin_qcode_macro::qcode;
2667
2668 use super::*;
2669
2670 fn foreign_block_fixture() -> (Context<'static>, FunctionId, BlockId) {
2671 let mut ctx = Context::new();
2672 let owner = FunctionBody::make(&mut ctx, "block_owner".into())
2673 .unwrap()
2674 .id;
2675 let destination = FunctionBody::make(&mut ctx, "block_destination".into())
2676 .unwrap()
2677 .id;
2678 let block = BasicBlock::make(&mut ctx, owner).id;
2679 (ctx, destination, block)
2680 }
2681
2682 #[test]
2683 fn raw_root_and_roster_are_local_while_refs_qualify_per_function() {
2684 let mut ctx = Context::new();
2685 let a = FunctionBody::make(&mut ctx, "local_root_a".into())
2686 .unwrap()
2687 .id;
2688 let b = FunctionBody::make(&mut ctx, "local_root_b".into())
2689 .unwrap()
2690 .id;
2691 let a_root = BasicBlock::make(&mut ctx, a).id;
2692 let b_root = BasicBlock::make(&mut ctx, b).id;
2693 assert_eq!(a_root.local, b_root.local, "arena-local ids should collide");
2694 FunctionBody::from_id_mut(&mut ctx, a)
2695 .set_root(a_root)
2696 .unwrap();
2697 FunctionBody::from_id_mut(&mut ctx, b)
2698 .set_root(b_root)
2699 .unwrap();
2700
2701 assert_eq!(ctx.bodies[a].root_id(), Some(a_root.local));
2702 assert_eq!(ctx.bodies[b].root_id(), Some(b_root.local));
2703 assert_eq!(ctx.bodies[a].roster, vec![a_root.local]);
2704 assert_eq!(ctx.bodies[b].roster, vec![b_root.local]);
2705 assert_eq!(
2706 FunctionBody::from_id(&ctx, a).root().map(|root| root.id),
2707 Some(a_root)
2708 );
2709 assert_eq!(
2710 FunctionBody::from_id(&ctx, b).root().map(|root| root.id),
2711 Some(b_root)
2712 );
2713 }
2714
2715 #[test]
2716 #[should_panic(expected = "cannot add a block stored in another function arena")]
2717 fn add_block_rejects_foreign_storage() {
2718 let (mut ctx, destination, block) = foreign_block_fixture();
2719 FunctionBody::from_id_mut(&mut ctx, destination).add_block(block);
2720 }
2721
2722 #[test]
2723 #[should_panic(expected = "cannot root a function at a block stored in another function arena")]
2724 fn set_root_rejects_foreign_storage() {
2725 let (mut ctx, destination, block) = foreign_block_fixture();
2726 FunctionBody::from_id_mut(&mut ctx, destination)
2727 .set_root(block)
2728 .unwrap();
2729 }
2730
2731 #[test]
2732 #[should_panic(expected = "cannot ensure a function root from another function arena")]
2733 fn ensure_root_rejects_foreign_storage() {
2734 let (mut ctx, destination, block) = foreign_block_fixture();
2735 FunctionBody::from_id_mut(&mut ctx, destination)
2736 .ensure_root(block)
2737 .unwrap();
2738 }
2739
2740 #[test]
2741 fn function_ref_users_of_rejects_foreign_owned_values() {
2742 let mut ctx = Context::new();
2743 qcode!(
2744 ctx,
2745 "
2746 fn users_a:
2747 <a_entry>
2748 %a_def = i64 1 + i64 2;
2749 %a_user = %a_def + i64 3;
2750 return at %a_user;
2751
2752 fn users_b:
2753 <b_entry>
2754 %b_def = i64 1 + i64 2;
2755 %b_user = %b_def + i64 3;
2756 return at %b_user;
2757 "
2758 );
2759
2760 let a_ids = FunctionRef::from_id(&ctx, users_a)
2761 .root()
2762 .unwrap()
2763 .instruction_ids();
2764 let a_def = ValueId::Instruction(a_ids[0]);
2765 assert_eq!(
2766 FunctionRef::from_id(&ctx, users_a).users_of(a_def),
2767 vec![a_ids[1]]
2768 );
2769 assert!(
2770 FunctionRef::from_id(&ctx, users_b)
2771 .users_of(a_def)
2772 .is_empty()
2773 );
2774
2775 let one = ctx.get_const(1, 8).id();
2776 assert!(!FunctionRef::from_id(&ctx, users_b).users_of(one).is_empty());
2777 }
2778
2779 fn colliding_body_ids() -> (
2780 Context<'static>,
2781 FunctionId,
2782 FunctionId,
2783 BlockId,
2784 BlockId,
2785 InstructionId,
2786 InstructionId,
2787 BlockParamId,
2788 BlockParamId,
2789 ) {
2790 let mut ctx = Context::new();
2791 qcode!(
2792 ctx,
2793 "
2794 fn raw_a:
2795 <a_entry @a:i64>
2796 %a_def = i64 1 + i64 2;
2797 return at %a_def;
2798 fn raw_b:
2799 <b_entry @b:i64>
2800 %b_def = i64 1 + i64 2;
2801 return at %b_def;
2802 "
2803 );
2804 let a_root = FunctionRef::from_id(&ctx, raw_a).root().unwrap();
2805 let b_root = FunctionRef::from_id(&ctx, raw_b).root().unwrap();
2806 let a_block = a_root.id;
2807 let b_block = b_root.id;
2808 let a_insn = a_root.instruction_ids()[0];
2809 let b_insn = b_root.instruction_ids()[0];
2810 let a_param = a_root.params().next().unwrap().id;
2811 let b_param = b_root.params().next().unwrap().id;
2812 assert_eq!(a_block.local, b_block.local);
2813 assert_eq!(a_insn.local, b_insn.local);
2814 assert_eq!(a_param.local, b_param.local);
2815 (
2816 ctx, raw_a, raw_b, a_block, b_block, a_insn, b_insn, a_param, b_param,
2817 )
2818 }
2819
2820 #[test]
2824 fn replace_instruction_with_itself_is_a_noop() {
2825 let mut ctx = Context::new();
2826 qcode!(
2827 ctx,
2828 "
2829 fn f:
2830 <entry @a:i32>
2831 %x = @a + 1;
2832 %y = %x + 2;
2833 return %y;
2834 "
2835 );
2836 let root = FunctionBody::from_id(&ctx, f).root().unwrap().id;
2838 let insns: Vec<InstructionId> = BasicBlock::from_id(&ctx, root)
2839 .instruction_ids()
2840 .into_iter()
2841 .collect();
2842 let x = insns[0];
2843 let users_before = ctx.bodies[f].users_of(ValueId::Instruction(x));
2844 assert!(!users_before.is_empty(), "x should have a user (%y)");
2845
2846 ctx.bodies[f].replace_instruction(x, ValueId::Instruction(x));
2848
2849 assert!(
2850 ctx.bodies[f].insns.contains(x.local),
2851 "x must survive a self-replacement"
2852 );
2853 assert_eq!(
2854 ctx.bodies[f].users_of(ValueId::Instruction(x)),
2855 users_before,
2856 "x's users must be unchanged"
2857 );
2858 }
2859
2860 #[test]
2861 fn body_users_of_rejects_foreign_owned_values() {
2862 let (ctx, a, b, _, _, a_insn, _, _, _) = colliding_body_ids();
2863 assert!(
2864 ctx.bodies[b]
2865 .users_of(ValueId::Instruction(a_insn))
2866 .is_empty()
2867 );
2868 assert!(
2869 !ctx.bodies[a]
2870 .users_of(ValueId::Instruction(a_insn))
2871 .is_empty()
2872 );
2873 }
2874
2875 #[test]
2876 #[should_panic(expected = "cannot replace uses of a value owned by another function")]
2877 fn body_replace_uses_rejects_foreign_old() {
2878 let (mut ctx, _, b, _, _, a_insn, b_insn, _, _) = colliding_body_ids();
2879 ctx.bodies[b]
2880 .replace_all_uses_with(ValueId::Instruction(a_insn), ValueId::Instruction(b_insn));
2881 }
2882
2883 #[test]
2884 #[should_panic(expected = "cannot replace uses with a value owned by another function")]
2885 fn body_replace_uses_rejects_foreign_new() {
2886 let (mut ctx, _, b, _, _, a_insn, b_insn, _, _) = colliding_body_ids();
2887 ctx.bodies[b]
2888 .replace_all_uses_with(ValueId::Instruction(b_insn), ValueId::Instruction(a_insn));
2889 }
2890
2891 #[test]
2892 #[should_panic(expected = "block belongs to another function")]
2893 fn body_block_access_rejects_colliding_foreign_id() {
2894 let (ctx, _, b, a_block, _, _, _, _, _) = colliding_body_ids();
2895 let _ = ctx.bodies[b].block(a_block);
2896 }
2897
2898 #[test]
2899 #[should_panic(expected = "instruction belongs to another function")]
2900 fn body_insn_access_rejects_colliding_foreign_id() {
2901 let (ctx, _, b, _, _, a_insn, _, _, _) = colliding_body_ids();
2902 let _ = ctx.bodies[b].insn(a_insn);
2903 }
2904
2905 #[test]
2906 #[should_panic(expected = "block parameter belongs to another function")]
2907 fn body_param_access_rejects_colliding_foreign_id() {
2908 let (ctx, _, b, _, _, _, _, a_param, _) = colliding_body_ids();
2909 let _ = ctx.bodies[b].block_param(a_param);
2910 }
2911
2912 #[test]
2917 fn make_function_creates_function_with_correct_name_root_address() {
2918 let mut ctx = Context::new();
2919 let f = FunctionBody::make(&mut ctx, "main".into()).unwrap();
2920 assert_eq!(f.name(), "main");
2921 }
2922
2923 #[test]
2924 fn get_function_by_name_returns_correct_function() {
2925 let mut ctx = Context::new();
2926 let id = FunctionBody::make(&mut ctx, "foo".into()).unwrap().id();
2927 let f = FunctionBody::from_name(&ctx, "foo").unwrap();
2928 assert_eq!(f.id(), id);
2929 assert_eq!(f.name(), "foo");
2930 }
2931
2932 #[test]
2933 fn get_function_by_name_returns_none_if_not_found() {
2934 let ctx = Context::new();
2935 assert!(FunctionBody::from_name(&ctx, "nonexistent").is_none());
2936 }
2937
2938 #[test]
2939 fn get_function_by_addr_returns_correct_function() {
2940 let mut ctx = Context::new();
2941 let id = FunctionBody::make_at_addr(&mut ctx, 0x2000, None).id();
2942 let addresses = crate::address_index::AddressIndex::analyze(&ctx);
2943 let f = FunctionBody::from_id(&ctx, addresses.function_at(0x2000).unwrap());
2944 assert_eq!(f.id(), id);
2945 assert_eq!(f.address(), Some(0x2000));
2946 assert_eq!(f.name(), "fn_2000");
2947 }
2948
2949 #[test]
2950 fn get_function_by_addr_returns_none_if_missing() {
2951 let ctx = Context::new();
2952 let addresses = crate::address_index::AddressIndex::analyze(&ctx);
2953 assert!(addresses.function_at(0xdeadbeef).is_none());
2954 }
2955
2956 #[test]
2957 fn add_block_via_function_mut_ref_updates_blocks_list() {
2958 let mut ctx = Context::new();
2959 let baz_id = FunctionBody::make(&mut ctx, "baz".into()).unwrap().id;
2960 let root = BasicBlock::make(&mut ctx, baz_id).id;
2961 let extra = BasicBlock::make(&mut ctx, baz_id).id;
2962
2963 let mut baz = FunctionBody::from_id_mut(&mut ctx, baz_id);
2964 baz.add_block(root);
2965 baz.add_block(extra);
2966
2967 let block_ids: Vec<_> = baz.blocks().map(|b| b.id).collect();
2968 assert!(block_ids.contains(&root));
2969 assert!(block_ids.contains(&extra));
2970 }
2971
2972 #[test]
2973 fn display_shows_function_name_and_block_contents() {
2974 let mut ctx = Context::new();
2975 FunctionBody::make(&mut ctx, "display_test".into()).unwrap();
2976
2977 let f = FunctionBody::from_name(&ctx, "display_test").unwrap();
2978
2979 let s = f.to_string();
2980 assert!(s.contains("fn display_test:"));
2981 }
2982
2983 #[test]
2984 fn iter_yields_all_blocks() {
2985 let mut ctx = Context::new();
2986 let f_id = FunctionBody::make(&mut ctx, "iter_fn".into()).unwrap().id;
2987 let root = BasicBlock::make(&mut ctx, f_id).id;
2988 let extra = BasicBlock::make(&mut ctx, f_id).id;
2989 let mut f = FunctionBody::from_id_mut(&mut ctx, f_id);
2990 f.add_block(root);
2991 f.add_block(extra);
2992
2993 let f = FunctionBody::from_name(&ctx, "iter_fn").unwrap();
2994 let ids: Vec<_> = f.iter().map(|b| b.id).collect();
2995 assert!(ids.contains(&root));
2996 assert!(ids.contains(&extra));
2997 }
2998
2999 #[test]
3000 fn into_iterator_for_function_ref_matches_iter() {
3001 let mut ctx = Context::new();
3002 let f_id = FunctionBody::make(&mut ctx, "into_iter_fn".into())
3003 .unwrap()
3004 .id;
3005 let b1 = BasicBlock::make(&mut ctx, f_id).id;
3006 let b2 = BasicBlock::make(&mut ctx, f_id).id;
3007 let mut f = FunctionBody::from_id_mut(&mut ctx, f_id);
3008 f.add_block(b1);
3009 f.add_block(b2);
3010
3011 let f = FunctionBody::from_name(&ctx, "into_iter_fn").unwrap();
3012 let mut via_iter: Vec<usize> = f.iter().map(|b| usize::from(b.id.local)).collect();
3013 let mut via_into: Vec<usize> = (&f).into_iter().map(|b| usize::from(b.id.local)).collect();
3014 via_iter.sort();
3015 via_into.sort();
3016 assert_eq!(via_iter, via_into);
3017 }
3018
3019 #[test]
3020 fn qcode_fn_single_block_populates_function() {
3021 let mut ctx = Context::new();
3022 qcode!(
3023 ctx,
3024 "
3025 fn simple:
3026 <entry>
3027 return at 0;
3028 "
3029 );
3030
3031 let f = FunctionBody::from_name(&ctx, "simple").unwrap();
3032 assert_eq!(f.name(), "simple");
3033 assert!(f.root().is_some());
3034 assert_eq!(f.root().unwrap().name().unwrap(), "entry");
3035 assert_eq!(f.blocks().count(), 1);
3036 }
3037
3038 #[test]
3039 fn qcode_fn_multi_block_populates_all_blocks() {
3040 let mut ctx = Context::new();
3041 qcode!(
3042 ctx,
3043 "
3044 fn multiblock:
3045 <bb1>
3046 if i8 1 goto <bb2> else goto <bb3>;
3047
3048 <bb2>
3049 goto <bb3>;
3050
3051 <bb3>
3052 return at 0;
3053 "
3054 );
3055
3056 let f = FunctionBody::from_name(&ctx, "multiblock").unwrap();
3057 assert_eq!(f.root().unwrap().name().unwrap(), "bb1");
3058 let block_names: Vec<_> = f.blocks().filter_map(|b| b.name()).collect();
3059 assert!(block_names.contains(&"bb1"), "missing bb1");
3060 assert!(block_names.contains(&"bb2"), "missing bb2");
3061 assert!(block_names.contains(&"bb3"), "missing bb3");
3062 assert_eq!(f.blocks().count(), 3);
3063 }
3064
3065 #[test]
3066 fn qcode_fn_id_variable_is_set() {
3067 let mut ctx = Context::new();
3068 qcode!(
3069 ctx,
3070 "
3071 fn myfn:
3072 <start>
3073 return at 0;
3074 "
3075 );
3076
3077 let by_name = FunctionBody::from_name(&ctx, "myfn").unwrap();
3078 assert_eq!(by_name.name(), "myfn");
3079 }
3080
3081 #[test]
3084 fn indexed_address_registration_keeps_foreign_block_rootless() {
3085 let mut ctx = Context::new();
3086
3087 let block_id = {
3090 let __f = ctx.anon_function();
3091 BasicBlock::make(&mut ctx, __f)
3092 }
3093 .id;
3094 let mut addresses = crate::address_index::AddressIndex::analyze(&ctx);
3095 addresses
3096 .register(
3097 &mut ctx,
3098 0x1000,
3099 crate::address_index::AddressTarget::Block(block_id),
3100 )
3101 .unwrap();
3102
3103 let fn_id = FunctionBody::make(&mut ctx, "fn_1000".into()).unwrap().id;
3104 addresses
3105 .register(
3106 &mut ctx,
3107 0x1000,
3108 crate::address_index::AddressTarget::Function(fn_id),
3109 )
3110 .unwrap();
3111
3112 assert_eq!(addresses.function_at(0x1000), Some(fn_id));
3113 assert_eq!(addresses.block_at(0x1000), None);
3114 assert!(FunctionBody::from_id(&ctx, fn_id).root().is_none());
3115 assert_ne!(block_id.func, fn_id);
3116 }
3117}
3118
3119#[cfg(test)]
3120mod memory_interface_tests {
3121 use super::*;
3122
3123 fn slot() -> InterfaceSlot {
3124 InterfaceSlot {
3125 base: SlotBase::Arg(0),
3126 offset: 8,
3127 size: 8,
3128 }
3129 }
3130
3131 #[test]
3138 fn memory_interface_round_trips_through_the_wire_format() {
3139 let state = MemoryChannelState {
3140 materialized: Some(MemoryInterfaceMap {
3141 inputs: vec![slot()],
3142 outputs: vec![InterfaceSlot {
3143 base: SlotBase::Global(0x2000),
3144 offset: 0,
3145 size: 4,
3146 }],
3147 }),
3148 ..MemoryChannelState::default()
3149 };
3150 let config = bincode::config::standard();
3151 let bytes = bincode::serde::encode_to_vec(&state, config).expect("encode memory state");
3152 let (decoded, _): (MemoryChannelState, _) =
3153 bincode::serde::decode_from_slice(&bytes, config).expect("decode memory state");
3154 assert_eq!(decoded, state);
3155 }
3156
3157 #[test]
3159 fn default_memory_state_is_not_materialized() {
3160 assert_eq!(MemoryChannelState::default().materialized(), None);
3161 }
3162
3163 #[test]
3166 fn stamping_written_spaces_preserves_the_materialized_interface() {
3167 let mut ctx = Context::new();
3168 let fid = FunctionBody::make(&mut ctx, "keeps_interface".into())
3169 .unwrap()
3170 .id;
3171 let map = MemoryInterfaceMap {
3172 inputs: vec![slot()],
3173 outputs: vec![],
3174 };
3175 let mut body = FunctionBody::from_id_mut(&mut ctx, fid);
3176 body.set_memory_effects(MemoryChannelState {
3177 materialized: Some(map.clone()),
3178 ..MemoryChannelState::default()
3179 });
3180 body.set_written_spaces(None);
3181
3182 let effects = FunctionBody::from_id(&ctx, fid).effects().memory.clone();
3183 assert_eq!(effects.materialized(), Some(&map));
3184 assert_eq!(effects.coarse, WrittenSpacesState::Unbounded);
3185 }
3186
3187 #[test]
3191 fn an_unmappable_base_is_distinct_from_a_global_and_is_not_bindable() {
3192 let unmappable = InterfaceSlot {
3193 base: SlotBase::Unmappable,
3194 offset: 0,
3195 size: 8,
3196 };
3197 let global = InterfaceSlot {
3198 base: SlotBase::Global(0),
3199 offset: 0,
3200 size: 8,
3201 };
3202 assert_ne!(unmappable, global);
3203 assert!(!unmappable.is_bindable());
3204 assert!(global.is_bindable());
3205 assert!(
3206 InterfaceSlot {
3207 base: SlotBase::Arg(0),
3208 offset: -8,
3209 size: 8,
3210 }
3211 .is_bindable()
3212 );
3213 }
3214}