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 temp_spaces(&self) -> impl Iterator<Item = (TempSpaceId, &TempSpace)> + '_ {
974 let func = self.id();
975 self.temp_spaces
976 .iter()
977 .map(move |space| (TempSpaceId::new(func, space.id), space.inner))
978 }
979
980 pub fn contains_temp_space(&self, id: TempSpaceId) -> bool {
982 id.func == self.id() && usize::from(id.local) < self.temp_spaces.len()
983 }
984
985 #[track_caller]
987 pub fn temp(&self, id: TempId) -> &Temp<'str> {
988 assert_eq!(id.func, self.id(), "temporary belongs to another function");
989 debug_assert!(
990 self.contains_temp(id),
991 "missing temporary {id:?} in function {:?} (arena length {})",
992 self.id(),
993 self.temps.len()
994 );
995 &self.temps[id.local]
996 }
997
998 pub fn contains_temp(&self, id: TempId) -> bool {
1000 id.func == self.id() && usize::from(id.local) < self.temps.len()
1001 }
1002
1003 pub fn remove_block_param(&mut self, id: BlockParamId) {
1008 assert!(
1009 self.contains_block_param(id),
1010 "cannot remove stale param {id:?}"
1011 );
1012 let key = ValueId::BlockParam(id).strip_func();
1013 let name = self.params[id.local].name.clone();
1014 if let Some(name) = name {
1015 self.names.forget(name.as_ref());
1016 }
1017 self.users.remove(&key);
1018 self.params.remove(id.local);
1019 }
1020 pub fn edge(&self, id: EdgeId) -> &EdgeData {
1022 &self.edges[id]
1023 }
1024
1025 pub fn push_insn(&mut self, insn: Instruction<'str>) -> InstructionId {
1038 InstructionId::new(self.id(), self.push_insn_local(insn))
1039 }
1040
1041 pub fn push_insn_local(&mut self, insn: Instruction<'str>) -> LocalInsnId {
1045 let args: Vec<LocalValueId> = insn.mnemonic().args().into_iter().collect();
1046 let local = self.insns.push(insn);
1047 for arg in args {
1048 self.users.entry(arg).or_default().push(local);
1049 }
1050 local
1051 }
1052
1053 pub fn push_block(&mut self, block: BasicBlock<'str>) -> BlockId {
1057 let func = self.id();
1058 BlockId::new(func, self.push_block_local(block))
1059 }
1060
1061 pub fn push_block_local(&mut self, block: BasicBlock<'str>) -> LocalBlockId {
1065 let local = self.blocks.push(block);
1066 self.roster.push(local);
1067 local
1068 }
1069
1070 pub fn make_block(&mut self) -> BlockId {
1073 self.push_block(BasicBlock::detached())
1074 }
1075
1076 pub fn make_block_local(&mut self) -> LocalBlockId {
1079 self.push_block_local(BasicBlock::detached())
1080 }
1081
1082 pub fn block_local(&self, block: LocalBlockId) -> &BasicBlock<'str> {
1085 &self.blocks[block]
1086 }
1087
1088 pub fn mnemonic_local(&self, insn: LocalInsnId) -> &Mnemonic {
1091 self.insns[insn].mnemonic()
1092 }
1093
1094 pub fn push_block_param(&mut self, param: BlockParam<'str>) -> BlockParamId {
1096 let local = self.params.push(param);
1097 BlockParamId::new(self.id(), local)
1098 }
1099
1100 pub fn push_block_param_local(
1104 &mut self,
1105 block: LocalBlockId,
1106 param: BlockParam<'str>,
1107 ) -> LocalParamId {
1108 let local = self.params.push(param);
1109 self.blocks[block].params.push(local);
1110 local
1111 }
1112
1113 pub fn append_insn_local(&mut self, block: LocalBlockId, insn: LocalInsnId) {
1117 self.insns[insn].parent = Some(block);
1118 self.blocks[block].instructions.push(insn);
1119 }
1120
1121 pub fn push_mnemonic(
1124 &mut self,
1125 shared: &crate::context::Shared<'str>,
1126 mnemonic: Mnemonic,
1127 size: usize,
1128 ) -> InstructionId {
1129 let type_id = shared.types.get_or_make_int(size);
1130 let insn = Instruction::new(type_id, mnemonic);
1131 self.push_insn(insn)
1132 }
1133
1134 pub fn push_mnemonic_with_type(
1136 &mut self,
1137 mnemonic: Mnemonic,
1138 type_id: crate::types::TypeId,
1139 ) -> InstructionId {
1140 let insn = Instruction::new(type_id, mnemonic);
1141 self.push_insn(insn)
1142 }
1143
1144 pub fn push_mnemonic_with_type_local(
1148 &mut self,
1149 mnemonic: Mnemonic,
1150 type_id: crate::types::TypeId,
1151 ) -> LocalInsnId {
1152 self.push_insn_local(Instruction::new(type_id, mnemonic))
1153 }
1154
1155 pub fn insert_insn_before(
1158 &mut self,
1159 block: BlockId,
1160 before: InstructionId,
1161 insn: InstructionId,
1162 ) {
1163 let index = self
1164 .block(block)
1165 .instructions
1166 .iter()
1167 .position(|&local| InstructionId::new(block.func, local) == before)
1168 .expect("before not in block");
1169 self.insn_mut(insn).parent = Some(block.local);
1170 self.block_mut(block)
1171 .instructions
1172 .insert(index, insn.localize(block.func));
1173 }
1174
1175 pub fn move_insn_before(&mut self, insn: InstructionId, before: InstructionId) {
1181 let id = self.id();
1182 assert_eq!(insn.func, id, "instruction belongs to another function");
1183 assert_eq!(
1184 before.func, id,
1185 "anchor instruction belongs to another function"
1186 );
1187 if insn == before {
1188 return;
1189 }
1190 assert!(
1191 !self.insn(insn).mnemonic().is_terminator(),
1192 "moving a terminator requires updating its CFG edges"
1193 );
1194
1195 let source = self
1196 .insn(insn)
1197 .parent
1198 .map(|local| BlockId::new(id, local))
1199 .expect("moved instruction must belong to a block");
1200 let target = self
1201 .insn(before)
1202 .parent
1203 .map(|local| BlockId::new(id, local))
1204 .expect("anchor instruction must belong to a block");
1205 let source_index = self
1206 .block(source)
1207 .instructions
1208 .iter()
1209 .position(|&local| local == insn.local)
1210 .expect("moved instruction missing from its parent block");
1211 let before_index = self
1212 .block(target)
1213 .instructions
1214 .iter()
1215 .position(|&local| local == before.local)
1216 .expect("anchor instruction missing from its parent block");
1217 let insert_index = if source == target && source_index < before_index {
1218 before_index - 1
1219 } else {
1220 before_index
1221 };
1222
1223 self.block_mut(source).instructions.remove(source_index);
1224 self.block_mut(target)
1225 .instructions
1226 .insert(insert_index, insn.local);
1227 self.insn_mut(insn).parent = Some(target.local);
1228 }
1229
1230 pub fn add_cfg_edge(&mut self, from: BlockId, to: BlockId) -> EdgeId {
1233 self.add_cfg_edge_local(from.local, to.local)
1234 }
1235
1236 pub fn add_cfg_edge_local(&mut self, from: LocalBlockId, to: LocalBlockId) -> EdgeId {
1239 let edge_id = self.edges.push(EdgeData { from, to });
1240 self.blocks[from].edges.insert(edge_id);
1241 self.blocks[to].edges.insert(edge_id);
1242 edge_id
1243 }
1244
1245 pub fn remove_cfg_edge(&mut self, edge_id: EdgeId) {
1248 let EdgeData { from, to } = *self.edge(edge_id);
1249 let func = self.id();
1250 self.block_mut(BlockId::new(func, from))
1251 .edges
1252 .remove(&edge_id);
1253 self.block_mut(BlockId::new(func, to))
1254 .edges
1255 .remove(&edge_id);
1256 self.edges.remove(edge_id);
1257 }
1258
1259 pub fn replace_all_uses_with(&mut self, old: ValueId, new: ValueId) {
1262 if old == new {
1263 return;
1264 }
1265 let Some(func) = old.owning_function() else {
1266 return;
1267 };
1268 assert_eq!(
1269 func,
1270 self.id(),
1271 "cannot replace uses of a value owned by another function"
1272 );
1273 if let Some(new_owner) = new.owning_function() {
1274 assert_eq!(
1275 new_owner,
1276 self.id(),
1277 "cannot replace uses with a value owned by another function"
1278 );
1279 }
1280 let users = self.users_of(old);
1281 let old = old.localize(func);
1282 let new = new.localize(func);
1283 for user in users {
1284 self.insn_mut(user).mnemonic_mut().replace_value(old, new);
1285 self.users.entry(new).or_default().push(user.localize(func));
1286 }
1287 self.users.remove(&old);
1288 }
1289
1290 pub fn replace_instruction(&mut self, id: InstructionId, new: ValueId) {
1295 if new == ValueId::Instruction(id) {
1299 return;
1300 }
1301 self.replace_all_uses_with(ValueId::Instruction(id), new);
1302 self.remove_instruction(id);
1303 }
1304
1305 pub fn remove_instructions(&mut self, dead: &FxHashSet<LocalInsnId>) {
1309 let mut ids: Vec<_> = dead.iter().copied().collect();
1310 ids.sort_unstable();
1311 let mut affected_args: FxHashSet<LocalValueId> = FxHashSet::default();
1312 for &id in &ids {
1313 assert!(
1314 self.insns.contains(id),
1315 "cannot remove stale instruction {id:?}"
1316 );
1317 affected_args.extend(self.insns[id].mnemonic().args());
1318 }
1319 for arg in affected_args {
1320 let remove_key = if let Some(users) = self.users.get_mut(&arg) {
1321 users.retain(|local| !dead.contains(local));
1322 users.is_empty()
1323 } else {
1324 false
1325 };
1326 if remove_key {
1327 self.users.remove(&arg);
1328 }
1329 }
1330 for id in ids {
1331 self.users.remove(&LocalValueId::Instruction(id));
1332 self.insns.remove(id);
1333 }
1334 }
1335
1336 pub fn remove_instruction(&mut self, id: InstructionId) {
1340 assert_eq!(
1341 id.func,
1342 self.id(),
1343 "instruction belongs to another function"
1344 );
1345 let func = self.id();
1346 let (parent, name, is_terminator, args) = {
1347 let insn = self.insn(id);
1348 (
1349 insn.parent.map(|l| BlockId::new(self.id(), l)),
1350 insn.name.clone(),
1351 insn.mnemonic().is_terminator(),
1352 insn.mnemonic().args().into_iter().collect::<Vec<_>>(),
1353 )
1354 };
1355
1356 if let Some(block_id) = parent {
1357 self.block_mut(block_id)
1358 .instructions
1359 .retain(|&local| local != id.localize(block_id.func));
1360 if is_terminator {
1361 let mut succ: Vec<EdgeId> = {
1362 let block = self.block(block_id);
1363 block
1364 .edges
1365 .iter()
1366 .copied()
1367 .filter(|&e| self.edge(e).from == block_id.local)
1368 .collect()
1369 };
1370 succ.sort_unstable();
1371 for edge_id in succ {
1372 self.remove_cfg_edge(edge_id);
1373 }
1374 }
1375 }
1376
1377 if let Some(n) = name {
1378 self.names.forget(n.as_ref());
1379 }
1380 for arg in args {
1381 let remove_key = if let Some(users) = self.users.get_mut(&arg) {
1382 users.retain(|&local| local != id.localize(func));
1383 users.is_empty()
1384 } else {
1385 false
1386 };
1387 if remove_key {
1388 self.users.remove(&arg);
1389 }
1390 }
1391 self.users.remove(&ValueId::Instruction(id).strip_func());
1392 self.insns.remove(id.local);
1393 }
1394
1395 pub fn remove_block_instructions(&mut self, block_id: BlockId, dead: &FxHashSet<LocalInsnId>) {
1408 assert_eq!(
1409 block_id.func,
1410 self.id(),
1411 "block belongs to another function"
1412 );
1413 if dead.is_empty() {
1414 return;
1415 }
1416
1417 let mut names = Vec::new();
1418 for &id in dead {
1419 let insn = &self.insns[id];
1420 assert!(
1421 !insn.mnemonic().is_terminator(),
1422 "bulk removal does not unlink CFG edges; {id:?} is a terminator"
1423 );
1424 if let Some(name) = insn.name.clone() {
1425 names.push(name);
1426 }
1427 }
1428
1429 self.block_mut(block_id)
1430 .instructions
1431 .retain(|local| !dead.contains(local));
1432 self.purge_instructions(dead, names);
1433 }
1434
1435 fn purge_instructions(&mut self, dead: &FxHashSet<LocalInsnId>, names: Vec<Cow<'str, str>>) {
1443 for name in names {
1444 self.names.forget(name.as_ref());
1445 }
1446 let mut operands: FxHashSet<LocalValueId> = FxHashSet::default();
1448 for &id in dead {
1449 operands.extend(self.insns[id].mnemonic().args());
1450 }
1451 for arg in operands {
1452 let now_empty = if let Some(users) = self.users.get_mut(&arg) {
1453 users.retain(|local| !dead.contains(local));
1454 users.is_empty()
1455 } else {
1456 false
1457 };
1458 if now_empty {
1459 self.users.remove(&arg);
1460 }
1461 }
1462 for &id in dead {
1463 self.users.remove(&LocalValueId::Instruction(id));
1464 self.insns.remove(id);
1465 }
1466 }
1467
1468 pub fn rehome_outgoing_edges(&mut self, keep: BlockId, remove: BlockId) {
1471 let outgoing: Vec<EdgeId> = {
1472 let block = self.block(remove);
1473 block
1474 .edges
1475 .iter()
1476 .copied()
1477 .filter(|&e| self.edge(e).from == remove.local)
1478 .collect()
1479 };
1480 for eid in outgoing {
1481 self.edges[eid].from = keep.local;
1482 self.block_mut(keep).edges.insert(eid);
1483 self.block_mut(remove).edges.remove(&eid);
1484 }
1485 }
1486
1487 pub fn replace_instruction_mnemonic(&mut self, id: InstructionId, mnemonic: Mnemonic) {
1490 assert_eq!(
1491 id.func,
1492 self.id(),
1493 "instruction belongs to another function"
1494 );
1495 self.replace_instruction_mnemonic_local(id.local, mnemonic);
1496 }
1497
1498 pub fn replace_instruction_mnemonic_local(&mut self, id: LocalInsnId, mnemonic: Mnemonic) {
1503 let old_args = self.insns[id]
1504 .mnemonic()
1505 .args()
1506 .into_iter()
1507 .collect::<Vec<_>>();
1508 for arg in old_args {
1509 let now_empty = if let Some(users) = self.users.get_mut(&arg) {
1510 users.retain(|&local| local != id);
1511 users.is_empty()
1512 } else {
1513 false
1514 };
1515 if now_empty {
1516 self.users.remove(&arg);
1517 }
1518 }
1519 *self.insns[id].mnemonic_mut() = mnemonic;
1520 let new_args = self.insns[id]
1521 .mnemonic()
1522 .args()
1523 .into_iter()
1524 .collect::<Vec<_>>();
1525 for arg in new_args {
1526 self.users.entry(arg).or_default().push(id);
1527 }
1528 }
1529
1530 pub fn rename_block_local(&mut self, block: LocalBlockId, name: Cow<'str, str>) -> Result<()> {
1536 let target = LocalValueId::BasicBlock(block);
1537 if let Some(existing) = self.names.get(&name) {
1538 return if existing == target {
1539 Ok(())
1540 } else {
1541 Err(Error::spanless(ErrorTy::DuplicateName(name.to_string())))
1542 };
1543 }
1544 let old_name = self.blocks[block].local_name().map(str::to_owned);
1545 self.names
1546 .register(name.clone(), target, old_name.as_deref())?;
1547 self.blocks[block].set_name(Some(name));
1548 Ok(())
1549 }
1550
1551 pub fn unroster_block(&mut self, block: BlockId) {
1554 self.roster.retain(|&b| b != block.localize(block.func));
1555 }
1556
1557 pub fn clear_block_instructions(&mut self, block: BlockId) {
1567 assert_eq!(block.func, self.id(), "block belongs to another function");
1568 let mut outgoing: Vec<EdgeId> = self
1569 .block(block)
1570 .edges
1571 .iter()
1572 .copied()
1573 .filter(|&edge| self.edges[edge].from == block.local)
1574 .collect();
1575 outgoing.sort_unstable();
1576 for edge in outgoing {
1577 self.remove_cfg_edge(edge);
1578 }
1579 let insns = std::mem::take(&mut self.block_mut(block).instructions);
1585 let dead: FxHashSet<LocalInsnId> = insns.iter().copied().collect();
1586 let names: Vec<Cow<'str, str>> = insns
1587 .iter()
1588 .filter_map(|&local| self.insns[local].name.clone())
1589 .collect();
1590 self.purge_instructions(&dead, names);
1591 }
1592
1593 pub fn delete_block(&mut self, block: BlockId) {
1594 assert_eq!(block.func, self.id(), "block belongs to another function");
1595 let mut edges: Vec<EdgeId> = self.block(block).edges.iter().copied().collect();
1596 edges.sort_unstable();
1597 for edge in edges {
1598 self.remove_cfg_edge(edge);
1599 }
1600 let insns: Vec<InstructionId> = self
1601 .block(block)
1602 .instructions
1603 .iter()
1604 .map(|&local| InstructionId::new(self.id(), local))
1605 .collect();
1606 for insn in insns {
1607 self.remove_instruction(insn);
1608 }
1609 let params: Vec<BlockParamId> = self
1610 .block(block)
1611 .params
1612 .iter()
1613 .map(|&local| BlockParamId::new(self.id(), local))
1614 .collect();
1615 for param in params {
1616 self.remove_block_param(param);
1617 }
1618 let name = self.block(block).local_name().map(str::to_owned);
1619 self.unroster_block(block);
1620 if self.root == Some(block.local) {
1621 self.root = None;
1622 }
1623 if let Some(name) = name {
1624 self.names.forget(&name);
1625 }
1626 self.blocks.remove(block.local);
1627 }
1628
1629 pub fn absorb_block(&mut self, keep: BlockId, other: BlockId, edge_ab: EdgeId) {
1633 assert_eq!(
1634 keep.func, other.func,
1635 "cannot absorb across function arenas"
1636 );
1637 let (branch_id, branch_args) = self
1638 .block(keep)
1639 .instructions
1640 .last()
1641 .and_then(
1642 |&local| match self.insn(InstructionId::new(keep.func, local)).mnemonic() {
1643 Mnemonic::Branch(branch) if BlockId::new(keep.func, branch.target) == other => {
1644 Some((InstructionId::new(keep.func, local), branch.args.clone()))
1645 }
1646 _ => None,
1647 },
1648 )
1649 .expect("absorbed block must be reached by keep's terminal branch");
1650 let other_params: Vec<_> = self
1651 .block(other)
1652 .params
1653 .iter()
1654 .map(|&local| BlockParamId::new(other.func, local))
1655 .collect();
1656 if !other_params.is_empty() {
1657 assert_eq!(
1658 other_params.len(),
1659 branch_args.len(),
1660 "cannot absorb block with {} params through branch with {} args",
1661 other_params.len(),
1662 branch_args.len()
1663 );
1664 for (param, arg) in other_params.iter().copied().zip(branch_args) {
1665 self.replace_all_uses_with(ValueId::BlockParam(param), arg.qualify(keep.func));
1666 }
1667 }
1668 self.remove_cfg_edge(edge_ab);
1669 self.remove_instruction(branch_id);
1670 let b_insns = std::mem::take(&mut self.block_mut(other).instructions);
1671 for &local in &b_insns {
1672 self.insn_mut(InstructionId::new(other.func, local)).parent = Some(keep.local);
1673 }
1674 self.block_mut(keep).instructions.extend(b_insns);
1675 self.rehome_outgoing_edges(keep, other);
1676 let (b_addr, b_extra, b_name) = {
1677 let b = self.block(other);
1678 (
1679 b.address,
1680 b.extra_addresses.clone(),
1681 b.local_name().map(str::to_owned),
1682 )
1683 };
1684 for param in other_params {
1685 self.remove_block_param(param);
1686 }
1687 self.unroster_block(other);
1688 if self.root == Some(other.local) {
1689 self.root = Some(keep.local);
1690 }
1691 if let Some(name) = b_name {
1692 self.names.forget(&name);
1693 }
1694 self.blocks.remove(other.local);
1695 if let Some(addr) = b_addr {
1696 self.block_mut(keep).extra_addresses.push(addr);
1697 }
1698 self.block_mut(keep).extra_addresses.extend(b_extra);
1699 }
1700
1701 pub fn register_local_name(
1706 &mut self,
1707 shared: &crate::context::Shared<'str>,
1708 id: ValueId,
1709 name: Cow<'str, str>,
1710 old_name: Option<&str>,
1711 ) -> Result<()> {
1712 if id.name_scope_function().is_none() {
1713 return match shared.get_named(&name) {
1714 Some(existing) if existing == id => Ok(()),
1715 Some(_) => Err(Error::spanless(ErrorTy::DuplicateName(name.to_string()))),
1716 None => unimplemented!(
1717 "a function body cannot register a global name (shared is read-only)"
1718 ),
1719 };
1720 }
1721 self.register_body_name(id, name, old_name)
1722 }
1723
1724 pub fn register_body_name(
1729 &mut self,
1730 id: ValueId,
1731 name: Cow<'str, str>,
1732 old_name: Option<&str>,
1733 ) -> Result<()> {
1734 assert!(
1735 id.name_scope_function().is_some(),
1736 "register_body_name on a global-scoped value {id:?}"
1737 );
1738 if let Some(existing) = self.names.get(&name).map(|id| id.qualify(self.id())) {
1739 return if existing == id {
1740 Ok(())
1741 } else {
1742 Err(Error::spanless(ErrorTy::DuplicateName(name.to_string())))
1743 };
1744 }
1745 self.names.register(name, id.localize(self.id()), old_name)
1746 }
1747
1748 pub fn from_id<'ctx>(ctx: &'ctx Context<'str>, id: FunctionId) -> FunctionRef<'str, 'ctx> {
1750 FunctionRef::new(ModuleView::new(ctx), id)
1751 }
1752
1753 pub fn from_id_mut<'ctx>(
1755 ctx: &'ctx mut Context<'str>,
1756 id: FunctionId,
1757 ) -> FunctionMutRef<'str, 'ctx> {
1758 FunctionMutRef::new(ctx, id)
1759 }
1760
1761 pub fn from_name<'ctx>(
1763 ctx: &'ctx Context<'str>,
1764 name: &str,
1765 ) -> Option<FunctionRef<'str, 'ctx>> {
1766 ctx.get_named(name)
1767 .and_then(ValueId::as_function)
1768 .map(|id| FunctionBody::from_id(ctx, id))
1769 }
1770
1771 pub fn make<'ctx>(
1773 ctx: &'ctx mut Context<'str>,
1774 name: Cow<'str, str>,
1775 ) -> Result<FunctionMutRef<'str, 'ctx>> {
1776 let id = FunctionId::from(ctx.bodies.len());
1777 let pushed = ctx.push_function(
1778 FunctionInterface::new(name.clone()),
1779 FunctionBody::empty_with_id(id),
1780 );
1781 debug_assert_eq!(pushed, id);
1782 ctx.update_name(name, id.into(), None)?;
1783 Ok(Self::from_id_mut(ctx, id))
1784 }
1785
1786 pub fn make_lambda<'ctx>(
1788 ctx: &'ctx mut Context<'str>,
1789 name: Cow<'str, str>,
1790 ) -> Result<FunctionMutRef<'str, 'ctx>> {
1791 let mut function = Self::make(ctx, name)?;
1792 function.interface_mut().kind = FunctionKind::Lambda;
1793 function.set_is_pure(true);
1794 function.set_register_effects(RegisterChannelState::Materialized(
1795 RegisterInterfaceMap::default(),
1796 ));
1797 Ok(function)
1798 }
1799
1800 pub fn make_at_addr<'ctx>(
1802 ctx: &'ctx mut Context<'str>,
1803 address: u64,
1804 name: Option<Cow<'str, str>>,
1805 ) -> FunctionMutRef<'str, 'ctx> {
1806 let mut addresses = crate::address_index::AddressIndex::analyze(ctx);
1807 Self::make_at_addr_indexed(ctx, &mut addresses, address, name)
1808 }
1809
1810 pub fn make_at_addr_indexed<'ctx>(
1812 ctx: &'ctx mut Context<'str>,
1813 addresses: &mut crate::address_index::AddressIndex,
1814 address: u64,
1815 name: Option<Cow<'str, str>>,
1816 ) -> FunctionMutRef<'str, 'ctx> {
1817 let name = name.unwrap_or_else(|| Cow::Owned(format!("fn_{address:x}")));
1818 let id = FunctionId::from(ctx.bodies.len());
1819 let pushed = ctx.push_function(
1820 FunctionInterface::new(name.clone()),
1821 FunctionBody::empty_with_id(id),
1822 );
1823 debug_assert_eq!(pushed, id);
1824
1825 Self::from_id_mut(ctx, id)
1826 .with_name(name)
1827 .expect("Function name is not unique")
1828 .with_address_indexed(addresses, address)
1829 .expect("Function address is not unique")
1830 }
1831
1832 pub fn make_external<'ctx>(
1837 ctx: &'ctx mut Context<'str>,
1838 address: u64,
1839 name: Option<Cow<'str, str>>,
1840 ) -> FunctionMutRef<'str, 'ctx> {
1841 let mut f = Self::make_at_addr(ctx, address, name);
1842 f.interface_mut().is_external = true;
1843 f
1844 }
1845
1846 pub fn make_external_indexed<'ctx>(
1848 ctx: &'ctx mut Context<'str>,
1849 addresses: &mut crate::address_index::AddressIndex,
1850 address: u64,
1851 name: Option<Cow<'str, str>>,
1852 ) -> FunctionMutRef<'str, 'ctx> {
1853 let mut function = Self::make_at_addr_indexed(ctx, addresses, address, name);
1854 function.interface_mut().is_external = true;
1855 function
1856 }
1857
1858 pub fn from_addr_or_create<'ctx>(
1860 ctx: &'ctx mut Context<'str>,
1861 address: u64,
1862 ) -> FunctionMutRef<'str, 'ctx> {
1863 let mut addresses = crate::address_index::AddressIndex::analyze(ctx);
1864 Self::from_addr_or_create_indexed(ctx, &mut addresses, address)
1865 }
1866
1867 pub fn from_addr_or_create_indexed<'ctx>(
1870 ctx: &'ctx mut Context<'str>,
1871 addresses: &mut crate::address_index::AddressIndex,
1872 address: u64,
1873 ) -> FunctionMutRef<'str, 'ctx> {
1874 match addresses.function_at(address) {
1875 Some(id) => Self::from_id_mut(ctx, id),
1876 None => Self::make_at_addr_indexed(ctx, addresses, address, None),
1877 }
1878 }
1879}
1880
1881impl<'s, 'ctx: 's, 'str: 'ctx, R> FunctionRef<'str, 'ctx, R>
1882where
1883 R: QCodeView<'ctx, 'str>,
1884{
1885 fn inner(&'s self) -> &'ctx FunctionBody<'str> {
1886 self.view.function(self.id)
1887 }
1888
1889 fn interface(&'s self) -> &'ctx FunctionInterface<'str> {
1892 self.view.interface(self.id)
1893 }
1894
1895 fn size(&self) -> usize {
1896 0
1897 }
1898
1899 pub fn address(&'s self) -> Option<u64> {
1901 self.interface().address
1902 }
1903
1904 pub fn is_external(&'s self) -> bool {
1906 self.interface().is_external
1907 }
1908
1909 pub fn import_ordinal(&'s self) -> Option<u16> {
1912 self.interface().import_ordinal
1913 }
1914
1915 pub fn signature(&'s self) -> Option<&'ctx FunctionSignature> {
1917 self.interface().signature.as_ref()
1918 }
1919
1920 pub fn users_of(&'s self, value: ValueId) -> Vec<InstructionId> {
1924 let func = self.id;
1925 if value.owning_function().is_some_and(|owner| owner != func) {
1926 return Vec::new();
1927 }
1928 self.inner().users_of(value)
1929 }
1930
1931 pub fn local_users_of(&'s self, value: ValueId) -> &'ctx [LocalInsnId] {
1937 let func = self.id;
1938 if value.owning_function().is_some_and(|owner| owner != func) {
1939 return &[];
1940 }
1941 self.inner().local_users_of(value)
1942 }
1943
1944 pub fn has_users(&'s self, value: ValueId) -> bool {
1947 let func = self.id;
1948 if value.owning_function().is_some_and(|owner| owner != func) {
1949 return false;
1950 }
1951 self.inner().has_users(value)
1952 }
1953
1954 pub fn user_map_entries(&'s self) -> impl Iterator<Item = (ValueId, Vec<InstructionId>)> + 's {
1957 let func = self.id;
1958 self.inner().user_map_entries().map(move |(v, u)| {
1959 (
1960 v.qualify(func),
1961 u.iter()
1962 .map(|&local| InstructionId::new(func, local))
1963 .collect(),
1964 )
1965 })
1966 }
1967
1968 pub fn local_named(&'s self, name: &str) -> Option<ValueId> {
1971 self.inner().names.get(name).map(|id| id.qualify(self.id))
1972 }
1973
1974 pub fn param_attr(&'s self, index: usize) -> Option<ParamAttrs> {
1979 self.interface().param_attr(index)
1980 }
1981
1982 pub fn param_attrs(&'s self) -> Option<&'ctx [ParamAttrs]> {
1984 self.interface()
1985 .signature
1986 .as_ref()
1987 .and_then(|s| s.param_attrs.as_deref())
1988 }
1989
1990 pub fn written_spaces(&'s self) -> Option<&'ctx [crate::space::SpaceId]> {
1997 match &self.interface().effects.memory.coarse {
1998 WrittenSpacesState::Bounded(spaces) => Some(spaces),
1999 _ => None,
2000 }
2001 }
2002
2003 pub fn written_spaces_state(&'s self) -> WrittenSpaces<'ctx> {
2007 match &self.interface().effects.memory.coarse {
2008 WrittenSpacesState::Unstamped => WrittenSpaces::Unstamped,
2009 WrittenSpacesState::Unbounded => WrittenSpaces::Unbounded,
2010 WrittenSpacesState::Bounded(spaces) => WrittenSpaces::Bounded(spaces),
2011 }
2012 }
2013
2014 pub fn is_reg_materialized(&'s self) -> bool {
2019 matches!(
2020 self.interface().effects.register,
2021 RegisterChannelState::Materialized(_)
2022 )
2023 }
2024
2025 pub fn effects(&'s self) -> &'ctx FunctionEffects {
2029 &self.interface().effects
2030 }
2031
2032 pub fn is_pure(&'s self) -> bool {
2037 self.interface()
2038 .signature
2039 .as_ref()
2040 .is_some_and(|s| s.is_pure)
2041 }
2042
2043 pub fn is_lambda(&'s self) -> bool {
2045 self.interface().kind == FunctionKind::Lambda
2046 }
2047
2048 pub fn kind(&'s self) -> FunctionKind {
2049 self.interface().kind
2050 }
2051
2052 pub fn extern_interface(&'s self) -> Option<&'ctx crate::value::ExternInterface> {
2056 self.interface()
2057 .signature
2058 .as_ref()
2059 .and_then(|s| s.extern_interface.as_ref())
2060 }
2061
2062 pub fn argmem(&'s self) -> Option<&'ctx crate::value::ExternArgmem> {
2066 self.interface()
2067 .signature
2068 .as_ref()
2069 .and_then(|s| s.argmem.as_ref())
2070 }
2071
2072 pub fn input_arg_name(&'s self, index: usize) -> Option<String> {
2078 if let Some(root) = self.root()
2084 && let Some(name) = root
2085 .params()
2086 .nth(index)
2087 .and_then(|p| p.name().map(str::to_owned))
2088 {
2089 return Some(name);
2090 }
2091
2092 self.extern_interface()
2095 .and_then(|iface| iface.args.get(index))
2096 .and_then(|a| a.name.as_ref().map(|n| n.to_string()))
2097 }
2098
2099 pub fn reads_unbounded_stack(&'s self) -> bool {
2103 self.interface()
2104 .signature
2105 .as_ref()
2106 .is_some_and(|s| s.reads_unbounded_stack)
2107 }
2108
2109 pub fn frame_escapes_to_unbounded(&'s self) -> bool {
2113 self.interface()
2114 .signature
2115 .as_ref()
2116 .is_some_and(|s| s.frame_escapes_to_unbounded)
2117 }
2118
2119 pub fn name(&'s self) -> &'ctx str {
2121 self.interface().name.as_ref()
2122 }
2123
2124 pub fn instruction_addrs(&'s self) -> impl Iterator<Item = u64> + 'ctx {
2128 self.inner().instruction_addrs.iter().copied()
2129 }
2130
2131 pub fn has_map(&'s self) -> bool {
2135 self.blocks().any(|block| {
2136 block
2137 .instructions()
2138 .any(|insn| matches!(insn.mnemonic(), Mnemonic::Map(_)))
2139 })
2140 }
2141
2142 pub fn has_scan(&'s self) -> bool {
2146 self.blocks().any(|block| {
2147 block
2148 .instructions()
2149 .any(|insn| matches!(insn.mnemonic(), Mnemonic::Scan(_)))
2150 })
2151 }
2152
2153 pub fn root(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
2155 self.inner()
2156 .root
2157 .map(|local| BlockRef::new(self.view, BlockId::new(self.id, local)))
2158 }
2159
2160 pub fn blocks(&'s self) -> impl Iterator<Item = BlockRef<'str, 'ctx, R>> + 's {
2162 let view = self.view;
2163 let mut ids = self.block_ids();
2164 ids.sort_by_key(|&id| (BlockRef::new(view, id).address(), id.local));
2168 ids.into_iter().map(move |id| BlockRef::new(view, id))
2169 }
2170
2171 pub fn block_ids(&'s self) -> Vec<BlockId> {
2173 let func = self.id;
2174 self.inner()
2175 .roster
2176 .iter()
2177 .copied()
2178 .map(|local| BlockId::new(func, local))
2179 .collect()
2180 }
2181
2182 pub fn instruction_ids(&'s self) -> Vec<InstructionId> {
2185 let func = self.id;
2186 self.inner()
2187 .insns
2188 .iter()
2189 .map(|i| InstructionId::new(func, i.id))
2190 .collect()
2191 }
2192
2193 pub fn edge_ids(&'s self) -> Vec<crate::value::block::EdgeId> {
2196 self.inner().edges.iter().map(|e| e.id).collect()
2197 }
2198
2199 pub fn iter(&'s self) -> BlockIter<'str, 'ctx, R> {
2202 BlockIter {
2203 view: self.view,
2204 inner: self.block_ids().into_iter(),
2205 marker: PhantomData,
2206 }
2207 }
2208
2209 fn fmt(&'s self, f: &mut Formatter<'_>) -> std::fmt::Result {
2210 if self.is_external() {
2211 return writeln!(f, "extern fn {};", self.name());
2212 }
2213 let keyword = match self.kind() {
2214 FunctionKind::Machine => "fn",
2215 FunctionKind::Lambda => "lambda",
2216 };
2217 writeln!(f, "{keyword} {}:", self.name())?;
2218 for block in self.blocks() {
2219 block.fmt(f)?;
2220 }
2221 Ok(())
2222 }
2223}
2224
2225#[derive(Clone, Copy)]
2226pub struct FunctionRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
2227 pub id: FunctionId,
2228 pub(in crate::value) view: R,
2229 marker: PhantomData<&'ctx &'str ()>,
2230}
2231
2232impl<'str, 'ctx, R> FunctionRef<'str, 'ctx, R> {
2233 pub fn new(view: R, id: FunctionId) -> Self {
2234 Self {
2235 id,
2236 view,
2237 marker: PhantomData,
2238 }
2239 }
2240
2241 pub fn id(&self) -> ValueId {
2242 self.id.into()
2243 }
2244}
2245
2246impl<'str, 'ctx> FunctionRef<'str, 'ctx> {
2247 pub fn from_id(ctx: &'ctx Context<'str>, id: FunctionId) -> Self {
2248 Self::new(ModuleView::new(ctx), id)
2249 }
2250}
2251
2252impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 'ctx, 'str> for FunctionRef<'str, 'ctx> {
2253 fn ctx(&'s self) -> &'ctx Context<'str> {
2254 self.view.context()
2258 }
2259}
2260
2261impl<'str: 'ctx, 'ctx, R> Named for FunctionRef<'str, 'ctx, R>
2262where
2263 R: QCodeView<'ctx, 'str>,
2264{
2265 fn name(&self) -> Option<&str> {
2266 Some(self.view.interface(self.id).name.as_ref())
2267 }
2268}
2269
2270impl<'str: 'ctx, 'ctx, R> Display for FunctionRef<'str, 'ctx, R>
2271where
2272 R: QCodeView<'ctx, 'str>,
2273{
2274 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2275 FunctionRef::fmt(self, f)
2276 }
2277}
2278
2279impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for FunctionRef<'str, 'ctx, R>
2280where
2281 R: QCodeView<'ctx, 'str>,
2282{
2283 fn id(&self) -> ValueId {
2284 self.id()
2285 }
2286
2287 fn size(&self) -> usize {
2288 FunctionRef::size(self)
2289 }
2290}
2291
2292pub struct BlockIter<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
2293 view: R,
2294 inner: std::vec::IntoIter<BlockId>,
2295 marker: PhantomData<&'ctx &'str ()>,
2296}
2297
2298impl<'str: 'ctx, 'ctx, R> Iterator for BlockIter<'str, 'ctx, R>
2299where
2300 R: QCodeView<'ctx, 'str>,
2301{
2302 type Item = BlockRef<'str, 'ctx, R>;
2303
2304 fn next(&mut self) -> Option<Self::Item> {
2305 self.inner.next().map(|id| BlockRef::new(self.view, id))
2306 }
2307}
2308
2309impl<'str: 'ctx, 'ctx, R> IntoIterator for &FunctionRef<'str, 'ctx, R>
2310where
2311 R: QCodeView<'ctx, 'str>,
2312{
2313 type Item = BlockRef<'str, 'ctx, R>;
2314 type IntoIter = BlockIter<'str, 'ctx, R>;
2315
2316 fn into_iter(self) -> Self::IntoIter {
2317 self.iter()
2318 }
2319}
2320
2321pub type FunctionMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, FunctionId>;
2322
2323impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for FunctionMutRef<'str, 'ctx> {
2324 fn ctx(&'s self) -> &'s Context<'str> {
2325 self.ctx
2326 }
2327}
2328
2329impl<'s, 'ctx: 's, 'str: 'ctx> WithCtxMut<'s, 'str> for FunctionMutRef<'str, 'ctx> {
2330 fn ctx_mut(&'s mut self) -> &'s mut Context<'str> {
2331 self.ctx
2332 }
2333}
2334
2335impl Display for FunctionMutRef<'_, '_> {
2336 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2337 self.as_ref().fmt(f)
2338 }
2339}
2340
2341impl<'ctx, 'str> Value<'str, 'ctx> for FunctionMutRef<'str, 'ctx> {
2342 fn id(&self) -> ValueId {
2343 self.id()
2344 }
2345
2346 fn size(&self) -> usize {
2347 self.as_ref().size()
2348 }
2349}
2350
2351impl Named for FunctionMutRef<'_, '_> {
2352 fn name(&self) -> Option<&str> {
2353 Some(self.ctx.interfaces[self.id].name.as_ref())
2354 }
2355}
2356
2357impl<'str, 'ctx> Renameable<'str, 'ctx> for FunctionMutRef<'str, 'ctx> {
2358 fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
2359 let id = self.id();
2360 let old_name = self.ctx.interfaces[self.id].name.as_ref().to_owned();
2361 update_context_name(id, self.ctx, name.clone(), Some(old_name.as_ref()))?;
2362 self.ctx.interfaces[self.id].name = name;
2363 Ok(())
2364 }
2365}
2366
2367impl<'str, 'ctx> FunctionMutRef<'str, 'ctx> {
2368 pub fn as_ref(&self) -> FunctionRef<'str, '_> {
2369 FunctionRef::new(ModuleView::new(self.ctx), self.id)
2370 }
2371
2372 fn inner(&self) -> &FunctionBody<'str> {
2373 self.ctx.function(self.id)
2374 }
2375
2376 fn interface(&self) -> &FunctionInterface<'str> {
2377 &self.ctx.interfaces[self.id]
2378 }
2379
2380 fn address(&self) -> Option<u64> {
2381 self.interface().address
2382 }
2383
2384 pub fn name(&self) -> &str {
2385 self.interface().name.as_ref()
2386 }
2387
2388 pub fn blocks(&self) -> impl Iterator<Item = BlockRef<'str, '_>> {
2389 self.as_ref().blocks().collect::<Vec<_>>().into_iter()
2390 }
2391
2392 pub fn root(&self) -> Option<BlockRef<'str, '_>> {
2393 self.as_ref().root()
2394 }
2395
2396 pub(crate) fn inner_mut(&mut self) -> &mut FunctionBody<'str> {
2397 &mut self.ctx.bodies[self.id]
2398 }
2399
2400 pub(crate) fn interface_mut(&mut self) -> &mut FunctionInterface<'str> {
2403 &mut self.ctx.interfaces[self.id]
2404 }
2405
2406 fn set_address(&mut self, address: u64) -> Result<()> {
2407 let mut addresses = crate::address_index::AddressIndex::analyze(&*self.ctx);
2408 self.set_address_indexed(&mut addresses, address)
2409 }
2410
2411 fn set_address_indexed(
2412 &mut self,
2413 addresses: &mut crate::address_index::AddressIndex,
2414 address: u64,
2415 ) -> Result<()> {
2416 let old_address = self.interface().address;
2417 self.interface_mut().address = Some(address);
2418 if let Err(error) = self
2419 .ctx
2420 .set_address_indexed(addresses, address, self.id.into())
2421 {
2422 self.interface_mut().address = old_address;
2423 return Err(error);
2424 }
2425 Ok(())
2426 }
2427
2428 fn with_address_indexed(
2429 mut self,
2430 addresses: &mut crate::address_index::AddressIndex,
2431 address: u64,
2432 ) -> Result<Self> {
2433 self.set_address_indexed(addresses, address)?;
2434 Ok(self)
2435 }
2436
2437 pub fn set_root(&mut self, id: BlockId) -> Result<()> {
2442 assert_eq!(
2443 id.func, self.id,
2444 "cannot root a function at a block stored in another function arena"
2445 );
2446 self.add_block(id);
2447 self.inner_mut().root = Some(id.localize(self.id));
2448
2449 let block_addr = BasicBlock::from_id(&*self.ctx, id).address();
2450 let self_addr = self.address();
2451
2452 match (self_addr, block_addr) {
2453 (Some(fn_addr), Some(block_addr)) if fn_addr != block_addr => {
2454 return Err(Error::spanless(ErrorTy::FunctionRootAddressMismatch {
2455 fn_addr,
2456 block_addr,
2457 }));
2458 }
2459 (None, Some(addr)) => {
2460 self.set_address(addr)
2461 .expect("This address should be valid");
2462 }
2463 (Some(addr), None) => {
2464 BasicBlock::from_id_mut(self.ctx, id)
2465 .set_address(addr)
2466 .expect("This address should be valid");
2467 }
2468 _ => {}
2469 }
2470 Ok(())
2471 }
2472
2473 pub fn make_root(&mut self) -> BlockRef<'str, '_> {
2474 let func = self.id;
2475 let root = BasicBlock::make(self.ctx, func).id;
2476 self.set_root(root).expect("We just created the block");
2477 BasicBlock::from_id(&*self.ctx, root)
2478 }
2479
2480 pub fn ensure_root(&mut self, id: BlockId) -> Result<()> {
2481 assert_eq!(
2482 id.func, self.id,
2483 "cannot ensure a function root from another function arena"
2484 );
2485 if let Some(root) = self.inner().root {
2486 if root != id.localize(self.id) {
2487 return Err(Error::spanless(ErrorTy::FunctionRootMismatch {
2488 expected: BlockId::new(self.id, root),
2489 actual: id,
2490 }));
2491 }
2492 Ok(())
2493 } else {
2494 self.set_root(id)
2495 }
2496 }
2497
2498 pub fn set_external(&mut self, is_external: bool) {
2499 self.interface_mut().is_external = is_external;
2500 assert!(
2501 self.inner().blocks.is_empty(),
2502 "External functions should not have blocks"
2503 );
2504 }
2505
2506 pub fn set_import_ordinal(&mut self, ordinal: Option<u16>) {
2510 self.interface_mut().import_ordinal = ordinal;
2511 }
2512
2513 pub fn set_kind(&mut self, kind: FunctionKind) {
2514 self.interface_mut().kind = kind;
2515 if kind == FunctionKind::Lambda {
2516 self.set_is_pure(true);
2517 self.set_register_effects(RegisterChannelState::Materialized(
2518 RegisterInterfaceMap::default(),
2519 ));
2520 }
2521 }
2522
2523 pub fn set_signature(&mut self, sig: FunctionSignature) {
2524 self.ctx.interfaces[self.id].signature = Some(sig);
2525 }
2526
2527 pub fn set_param_attrs(&mut self, attrs: Vec<ParamAttrs>) {
2530 self.interface_mut()
2531 .signature
2532 .get_or_insert_default()
2533 .param_attrs = Some(attrs);
2534 }
2535
2536 pub fn clear_param_attrs(&mut self) {
2539 if let Some(sig) = self.interface_mut().signature.as_mut() {
2540 sig.param_attrs = None;
2541 }
2542 }
2543
2544 pub fn set_written_spaces(&mut self, spaces: Option<Vec<crate::space::SpaceId>>) {
2550 let coarse = match spaces {
2551 Some(spaces) => WrittenSpacesState::Bounded(spaces),
2552 None => WrittenSpacesState::Unbounded,
2553 };
2554 let precise = self.interface_mut().effects.memory.precise.take();
2557 self.set_memory_solved(coarse, precise);
2558 }
2559
2560 pub fn set_extern_interface(&mut self, iface: crate::value::ExternInterface) {
2564 self.interface_mut()
2565 .signature
2566 .get_or_insert_default()
2567 .extern_interface = Some(iface);
2568 }
2569
2570 pub fn set_argmem(&mut self, argmem: crate::value::ExternArgmem) {
2574 self.interface_mut()
2575 .signature
2576 .get_or_insert_default()
2577 .argmem = Some(argmem);
2578 }
2579
2580 pub fn set_register_effects(&mut self, register: RegisterChannelState) {
2583 self.interface_mut().effects.register = register;
2584 }
2585
2586 pub fn set_memory_effects(&mut self, memory: MemoryChannelState) {
2597 self.interface_mut().effects.memory = memory;
2598 }
2599
2600 pub fn set_memory_solved(&mut self, coarse: WrittenSpacesState, precise: Option<Footprint>) {
2606 let memory = &mut self.interface_mut().effects.memory;
2607 memory.coarse = coarse;
2608 memory.precise = precise;
2609 }
2610
2611 pub fn set_memory_interface(&mut self, materialized: Option<MemoryInterfaceMap>) {
2614 self.interface_mut().effects.memory.materialized = materialized;
2615 }
2616
2617 pub fn set_is_pure(&mut self, value: bool) {
2621 self.interface_mut()
2622 .signature
2623 .get_or_insert_default()
2624 .is_pure = value;
2625 }
2626
2627 pub fn set_reads_unbounded_stack(&mut self, value: bool) {
2630 self.interface_mut()
2631 .signature
2632 .get_or_insert_default()
2633 .reads_unbounded_stack = value;
2634 }
2635
2636 pub fn set_frame_escapes_to_unbounded(&mut self, value: bool) {
2640 self.interface_mut()
2641 .signature
2642 .get_or_insert_default()
2643 .frame_escapes_to_unbounded = value;
2644 }
2645
2646 pub fn add_instruction_addr(&mut self, addr: u64) {
2648 self.inner_mut().instruction_addrs.insert(addr);
2649 }
2650
2651 pub fn add_block(&mut self, id: BlockId) {
2659 assert_eq!(
2660 id.func, self.id,
2661 "cannot add a block stored in another function arena"
2662 );
2663 let local = id.localize(self.id);
2664 if !self.inner().roster.contains(&local) {
2667 self.inner_mut().roster.push(local);
2668 }
2669 }
2670}
2671
2672#[cfg(test)]
2673mod tests {
2674 use wazabin_qcode_macro::qcode;
2675
2676 use super::*;
2677
2678 fn foreign_block_fixture() -> (Context<'static>, FunctionId, BlockId) {
2679 let mut ctx = Context::new();
2680 let owner = FunctionBody::make(&mut ctx, "block_owner".into())
2681 .unwrap()
2682 .id;
2683 let destination = FunctionBody::make(&mut ctx, "block_destination".into())
2684 .unwrap()
2685 .id;
2686 let block = BasicBlock::make(&mut ctx, owner).id;
2687 (ctx, destination, block)
2688 }
2689
2690 #[test]
2691 fn raw_root_and_roster_are_local_while_refs_qualify_per_function() {
2692 let mut ctx = Context::new();
2693 let a = FunctionBody::make(&mut ctx, "local_root_a".into())
2694 .unwrap()
2695 .id;
2696 let b = FunctionBody::make(&mut ctx, "local_root_b".into())
2697 .unwrap()
2698 .id;
2699 let a_root = BasicBlock::make(&mut ctx, a).id;
2700 let b_root = BasicBlock::make(&mut ctx, b).id;
2701 assert_eq!(a_root.local, b_root.local, "arena-local ids should collide");
2702 FunctionBody::from_id_mut(&mut ctx, a)
2703 .set_root(a_root)
2704 .unwrap();
2705 FunctionBody::from_id_mut(&mut ctx, b)
2706 .set_root(b_root)
2707 .unwrap();
2708
2709 assert_eq!(ctx.bodies[a].root_id(), Some(a_root.local));
2710 assert_eq!(ctx.bodies[b].root_id(), Some(b_root.local));
2711 assert_eq!(ctx.bodies[a].roster, vec![a_root.local]);
2712 assert_eq!(ctx.bodies[b].roster, vec![b_root.local]);
2713 assert_eq!(
2714 FunctionBody::from_id(&ctx, a).root().map(|root| root.id),
2715 Some(a_root)
2716 );
2717 assert_eq!(
2718 FunctionBody::from_id(&ctx, b).root().map(|root| root.id),
2719 Some(b_root)
2720 );
2721 }
2722
2723 #[test]
2724 #[should_panic(expected = "cannot add a block stored in another function arena")]
2725 fn add_block_rejects_foreign_storage() {
2726 let (mut ctx, destination, block) = foreign_block_fixture();
2727 FunctionBody::from_id_mut(&mut ctx, destination).add_block(block);
2728 }
2729
2730 #[test]
2731 #[should_panic(expected = "cannot root a function at a block stored in another function arena")]
2732 fn set_root_rejects_foreign_storage() {
2733 let (mut ctx, destination, block) = foreign_block_fixture();
2734 FunctionBody::from_id_mut(&mut ctx, destination)
2735 .set_root(block)
2736 .unwrap();
2737 }
2738
2739 #[test]
2740 #[should_panic(expected = "cannot ensure a function root from another function arena")]
2741 fn ensure_root_rejects_foreign_storage() {
2742 let (mut ctx, destination, block) = foreign_block_fixture();
2743 FunctionBody::from_id_mut(&mut ctx, destination)
2744 .ensure_root(block)
2745 .unwrap();
2746 }
2747
2748 #[test]
2749 fn function_ref_users_of_rejects_foreign_owned_values() {
2750 let mut ctx = Context::new();
2751 qcode!(
2752 ctx,
2753 "
2754 fn users_a:
2755 <a_entry>
2756 %a_def = i64 1 + i64 2;
2757 %a_user = %a_def + i64 3;
2758 return at %a_user;
2759
2760 fn users_b:
2761 <b_entry>
2762 %b_def = i64 1 + i64 2;
2763 %b_user = %b_def + i64 3;
2764 return at %b_user;
2765 "
2766 );
2767
2768 let a_ids = FunctionRef::from_id(&ctx, users_a)
2769 .root()
2770 .unwrap()
2771 .instruction_ids();
2772 let a_def = ValueId::Instruction(a_ids[0]);
2773 assert_eq!(
2774 FunctionRef::from_id(&ctx, users_a).users_of(a_def),
2775 vec![a_ids[1]]
2776 );
2777 assert!(
2778 FunctionRef::from_id(&ctx, users_b)
2779 .users_of(a_def)
2780 .is_empty()
2781 );
2782
2783 let one = ctx.get_const(1, 8).id();
2784 assert!(!FunctionRef::from_id(&ctx, users_b).users_of(one).is_empty());
2785 }
2786
2787 fn colliding_body_ids() -> (
2788 Context<'static>,
2789 FunctionId,
2790 FunctionId,
2791 BlockId,
2792 BlockId,
2793 InstructionId,
2794 InstructionId,
2795 BlockParamId,
2796 BlockParamId,
2797 ) {
2798 let mut ctx = Context::new();
2799 qcode!(
2800 ctx,
2801 "
2802 fn raw_a:
2803 <a_entry @a:i64>
2804 %a_def = i64 1 + i64 2;
2805 return at %a_def;
2806 fn raw_b:
2807 <b_entry @b:i64>
2808 %b_def = i64 1 + i64 2;
2809 return at %b_def;
2810 "
2811 );
2812 let a_root = FunctionRef::from_id(&ctx, raw_a).root().unwrap();
2813 let b_root = FunctionRef::from_id(&ctx, raw_b).root().unwrap();
2814 let a_block = a_root.id;
2815 let b_block = b_root.id;
2816 let a_insn = a_root.instruction_ids()[0];
2817 let b_insn = b_root.instruction_ids()[0];
2818 let a_param = a_root.params().next().unwrap().id;
2819 let b_param = b_root.params().next().unwrap().id;
2820 assert_eq!(a_block.local, b_block.local);
2821 assert_eq!(a_insn.local, b_insn.local);
2822 assert_eq!(a_param.local, b_param.local);
2823 (
2824 ctx, raw_a, raw_b, a_block, b_block, a_insn, b_insn, a_param, b_param,
2825 )
2826 }
2827
2828 #[test]
2832 fn replace_instruction_with_itself_is_a_noop() {
2833 let mut ctx = Context::new();
2834 qcode!(
2835 ctx,
2836 "
2837 fn f:
2838 <entry @a:i32>
2839 %x = @a + 1;
2840 %y = %x + 2;
2841 return %y;
2842 "
2843 );
2844 let root = FunctionBody::from_id(&ctx, f).root().unwrap().id;
2846 let insns: Vec<InstructionId> = BasicBlock::from_id(&ctx, root)
2847 .instruction_ids()
2848 .into_iter()
2849 .collect();
2850 let x = insns[0];
2851 let users_before = ctx.bodies[f].users_of(ValueId::Instruction(x));
2852 assert!(!users_before.is_empty(), "x should have a user (%y)");
2853
2854 ctx.bodies[f].replace_instruction(x, ValueId::Instruction(x));
2856
2857 assert!(
2858 ctx.bodies[f].insns.contains(x.local),
2859 "x must survive a self-replacement"
2860 );
2861 assert_eq!(
2862 ctx.bodies[f].users_of(ValueId::Instruction(x)),
2863 users_before,
2864 "x's users must be unchanged"
2865 );
2866 }
2867
2868 #[test]
2869 fn body_users_of_rejects_foreign_owned_values() {
2870 let (ctx, a, b, _, _, a_insn, _, _, _) = colliding_body_ids();
2871 assert!(
2872 ctx.bodies[b]
2873 .users_of(ValueId::Instruction(a_insn))
2874 .is_empty()
2875 );
2876 assert!(
2877 !ctx.bodies[a]
2878 .users_of(ValueId::Instruction(a_insn))
2879 .is_empty()
2880 );
2881 }
2882
2883 #[test]
2884 #[should_panic(expected = "cannot replace uses of a value owned by another function")]
2885 fn body_replace_uses_rejects_foreign_old() {
2886 let (mut ctx, _, b, _, _, a_insn, b_insn, _, _) = colliding_body_ids();
2887 ctx.bodies[b]
2888 .replace_all_uses_with(ValueId::Instruction(a_insn), ValueId::Instruction(b_insn));
2889 }
2890
2891 #[test]
2892 #[should_panic(expected = "cannot replace uses with a value owned by another function")]
2893 fn body_replace_uses_rejects_foreign_new() {
2894 let (mut ctx, _, b, _, _, a_insn, b_insn, _, _) = colliding_body_ids();
2895 ctx.bodies[b]
2896 .replace_all_uses_with(ValueId::Instruction(b_insn), ValueId::Instruction(a_insn));
2897 }
2898
2899 #[test]
2900 #[should_panic(expected = "block belongs to another function")]
2901 fn body_block_access_rejects_colliding_foreign_id() {
2902 let (ctx, _, b, a_block, _, _, _, _, _) = colliding_body_ids();
2903 let _ = ctx.bodies[b].block(a_block);
2904 }
2905
2906 #[test]
2907 #[should_panic(expected = "instruction belongs to another function")]
2908 fn body_insn_access_rejects_colliding_foreign_id() {
2909 let (ctx, _, b, _, _, a_insn, _, _, _) = colliding_body_ids();
2910 let _ = ctx.bodies[b].insn(a_insn);
2911 }
2912
2913 #[test]
2914 #[should_panic(expected = "block parameter belongs to another function")]
2915 fn body_param_access_rejects_colliding_foreign_id() {
2916 let (ctx, _, b, _, _, _, _, a_param, _) = colliding_body_ids();
2917 let _ = ctx.bodies[b].block_param(a_param);
2918 }
2919
2920 #[test]
2925 fn make_function_creates_function_with_correct_name_root_address() {
2926 let mut ctx = Context::new();
2927 let f = FunctionBody::make(&mut ctx, "main".into()).unwrap();
2928 assert_eq!(f.name(), "main");
2929 }
2930
2931 #[test]
2932 fn get_function_by_name_returns_correct_function() {
2933 let mut ctx = Context::new();
2934 let id = FunctionBody::make(&mut ctx, "foo".into()).unwrap().id();
2935 let f = FunctionBody::from_name(&ctx, "foo").unwrap();
2936 assert_eq!(f.id(), id);
2937 assert_eq!(f.name(), "foo");
2938 }
2939
2940 #[test]
2941 fn get_function_by_name_returns_none_if_not_found() {
2942 let ctx = Context::new();
2943 assert!(FunctionBody::from_name(&ctx, "nonexistent").is_none());
2944 }
2945
2946 #[test]
2947 fn get_function_by_addr_returns_correct_function() {
2948 let mut ctx = Context::new();
2949 let id = FunctionBody::make_at_addr(&mut ctx, 0x2000, None).id();
2950 let addresses = crate::address_index::AddressIndex::analyze(&ctx);
2951 let f = FunctionBody::from_id(&ctx, addresses.function_at(0x2000).unwrap());
2952 assert_eq!(f.id(), id);
2953 assert_eq!(f.address(), Some(0x2000));
2954 assert_eq!(f.name(), "fn_2000");
2955 }
2956
2957 #[test]
2958 fn get_function_by_addr_returns_none_if_missing() {
2959 let ctx = Context::new();
2960 let addresses = crate::address_index::AddressIndex::analyze(&ctx);
2961 assert!(addresses.function_at(0xdeadbeef).is_none());
2962 }
2963
2964 #[test]
2965 fn add_block_via_function_mut_ref_updates_blocks_list() {
2966 let mut ctx = Context::new();
2967 let baz_id = FunctionBody::make(&mut ctx, "baz".into()).unwrap().id;
2968 let root = BasicBlock::make(&mut ctx, baz_id).id;
2969 let extra = BasicBlock::make(&mut ctx, baz_id).id;
2970
2971 let mut baz = FunctionBody::from_id_mut(&mut ctx, baz_id);
2972 baz.add_block(root);
2973 baz.add_block(extra);
2974
2975 let block_ids: Vec<_> = baz.blocks().map(|b| b.id).collect();
2976 assert!(block_ids.contains(&root));
2977 assert!(block_ids.contains(&extra));
2978 }
2979
2980 #[test]
2981 fn display_shows_function_name_and_block_contents() {
2982 let mut ctx = Context::new();
2983 FunctionBody::make(&mut ctx, "display_test".into()).unwrap();
2984
2985 let f = FunctionBody::from_name(&ctx, "display_test").unwrap();
2986
2987 let s = f.to_string();
2988 assert!(s.contains("fn display_test:"));
2989 }
2990
2991 #[test]
2992 fn iter_yields_all_blocks() {
2993 let mut ctx = Context::new();
2994 let f_id = FunctionBody::make(&mut ctx, "iter_fn".into()).unwrap().id;
2995 let root = BasicBlock::make(&mut ctx, f_id).id;
2996 let extra = BasicBlock::make(&mut ctx, f_id).id;
2997 let mut f = FunctionBody::from_id_mut(&mut ctx, f_id);
2998 f.add_block(root);
2999 f.add_block(extra);
3000
3001 let f = FunctionBody::from_name(&ctx, "iter_fn").unwrap();
3002 let ids: Vec<_> = f.iter().map(|b| b.id).collect();
3003 assert!(ids.contains(&root));
3004 assert!(ids.contains(&extra));
3005 }
3006
3007 #[test]
3008 fn into_iterator_for_function_ref_matches_iter() {
3009 let mut ctx = Context::new();
3010 let f_id = FunctionBody::make(&mut ctx, "into_iter_fn".into())
3011 .unwrap()
3012 .id;
3013 let b1 = BasicBlock::make(&mut ctx, f_id).id;
3014 let b2 = BasicBlock::make(&mut ctx, f_id).id;
3015 let mut f = FunctionBody::from_id_mut(&mut ctx, f_id);
3016 f.add_block(b1);
3017 f.add_block(b2);
3018
3019 let f = FunctionBody::from_name(&ctx, "into_iter_fn").unwrap();
3020 let mut via_iter: Vec<usize> = f.iter().map(|b| usize::from(b.id.local)).collect();
3021 let mut via_into: Vec<usize> = (&f).into_iter().map(|b| usize::from(b.id.local)).collect();
3022 via_iter.sort();
3023 via_into.sort();
3024 assert_eq!(via_iter, via_into);
3025 }
3026
3027 #[test]
3028 fn qcode_fn_single_block_populates_function() {
3029 let mut ctx = Context::new();
3030 qcode!(
3031 ctx,
3032 "
3033 fn simple:
3034 <entry>
3035 return at 0;
3036 "
3037 );
3038
3039 let f = FunctionBody::from_name(&ctx, "simple").unwrap();
3040 assert_eq!(f.name(), "simple");
3041 assert!(f.root().is_some());
3042 assert_eq!(f.root().unwrap().name().unwrap(), "entry");
3043 assert_eq!(f.blocks().count(), 1);
3044 }
3045
3046 #[test]
3047 fn qcode_fn_multi_block_populates_all_blocks() {
3048 let mut ctx = Context::new();
3049 qcode!(
3050 ctx,
3051 "
3052 fn multiblock:
3053 <bb1>
3054 if i8 1 goto <bb2> else goto <bb3>;
3055
3056 <bb2>
3057 goto <bb3>;
3058
3059 <bb3>
3060 return at 0;
3061 "
3062 );
3063
3064 let f = FunctionBody::from_name(&ctx, "multiblock").unwrap();
3065 assert_eq!(f.root().unwrap().name().unwrap(), "bb1");
3066 let block_names: Vec<_> = f.blocks().filter_map(|b| b.name()).collect();
3067 assert!(block_names.contains(&"bb1"), "missing bb1");
3068 assert!(block_names.contains(&"bb2"), "missing bb2");
3069 assert!(block_names.contains(&"bb3"), "missing bb3");
3070 assert_eq!(f.blocks().count(), 3);
3071 }
3072
3073 #[test]
3074 fn qcode_fn_id_variable_is_set() {
3075 let mut ctx = Context::new();
3076 qcode!(
3077 ctx,
3078 "
3079 fn myfn:
3080 <start>
3081 return at 0;
3082 "
3083 );
3084
3085 let by_name = FunctionBody::from_name(&ctx, "myfn").unwrap();
3086 assert_eq!(by_name.name(), "myfn");
3087 }
3088
3089 #[test]
3092 fn indexed_address_registration_keeps_foreign_block_rootless() {
3093 let mut ctx = Context::new();
3094
3095 let block_id = {
3098 let __f = ctx.anon_function();
3099 BasicBlock::make(&mut ctx, __f)
3100 }
3101 .id;
3102 let mut addresses = crate::address_index::AddressIndex::analyze(&ctx);
3103 addresses
3104 .register(
3105 &mut ctx,
3106 0x1000,
3107 crate::address_index::AddressTarget::Block(block_id),
3108 )
3109 .unwrap();
3110
3111 let fn_id = FunctionBody::make(&mut ctx, "fn_1000".into()).unwrap().id;
3112 addresses
3113 .register(
3114 &mut ctx,
3115 0x1000,
3116 crate::address_index::AddressTarget::Function(fn_id),
3117 )
3118 .unwrap();
3119
3120 assert_eq!(addresses.function_at(0x1000), Some(fn_id));
3121 assert_eq!(addresses.block_at(0x1000), None);
3122 assert!(FunctionBody::from_id(&ctx, fn_id).root().is_none());
3123 assert_ne!(block_id.func, fn_id);
3124 }
3125}
3126
3127#[cfg(test)]
3128mod memory_interface_tests {
3129 use super::*;
3130
3131 fn slot() -> InterfaceSlot {
3132 InterfaceSlot {
3133 base: SlotBase::Arg(0),
3134 offset: 8,
3135 size: 8,
3136 }
3137 }
3138
3139 #[test]
3146 fn memory_interface_round_trips_through_the_wire_format() {
3147 let state = MemoryChannelState {
3148 materialized: Some(MemoryInterfaceMap {
3149 inputs: vec![slot()],
3150 outputs: vec![InterfaceSlot {
3151 base: SlotBase::Global(0x2000),
3152 offset: 0,
3153 size: 4,
3154 }],
3155 }),
3156 ..MemoryChannelState::default()
3157 };
3158 let config = bincode::config::standard();
3159 let bytes = bincode::serde::encode_to_vec(&state, config).expect("encode memory state");
3160 let (decoded, _): (MemoryChannelState, _) =
3161 bincode::serde::decode_from_slice(&bytes, config).expect("decode memory state");
3162 assert_eq!(decoded, state);
3163 }
3164
3165 #[test]
3167 fn default_memory_state_is_not_materialized() {
3168 assert_eq!(MemoryChannelState::default().materialized(), None);
3169 }
3170
3171 #[test]
3174 fn stamping_written_spaces_preserves_the_materialized_interface() {
3175 let mut ctx = Context::new();
3176 let fid = FunctionBody::make(&mut ctx, "keeps_interface".into())
3177 .unwrap()
3178 .id;
3179 let map = MemoryInterfaceMap {
3180 inputs: vec![slot()],
3181 outputs: vec![],
3182 };
3183 let mut body = FunctionBody::from_id_mut(&mut ctx, fid);
3184 body.set_memory_effects(MemoryChannelState {
3185 materialized: Some(map.clone()),
3186 ..MemoryChannelState::default()
3187 });
3188 body.set_written_spaces(None);
3189
3190 let effects = FunctionBody::from_id(&ctx, fid).effects().memory.clone();
3191 assert_eq!(effects.materialized(), Some(&map));
3192 assert_eq!(effects.coarse, WrittenSpacesState::Unbounded);
3193 }
3194
3195 #[test]
3199 fn an_unmappable_base_is_distinct_from_a_global_and_is_not_bindable() {
3200 let unmappable = InterfaceSlot {
3201 base: SlotBase::Unmappable,
3202 offset: 0,
3203 size: 8,
3204 };
3205 let global = InterfaceSlot {
3206 base: SlotBase::Global(0),
3207 offset: 0,
3208 size: 8,
3209 };
3210 assert_ne!(unmappable, global);
3211 assert!(!unmappable.is_bindable());
3212 assert!(global.is_bindable());
3213 assert!(
3214 InterfaceSlot {
3215 base: SlotBase::Arg(0),
3216 offset: -8,
3217 size: 8,
3218 }
3219 .is_bindable()
3220 );
3221 }
3222}