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 split_block_before(&mut self, block: BlockId, insn: InstructionId) -> BlockId {
1484 assert_eq!(block.func, self.id(), "block belongs to another function");
1485 assert_eq!(
1486 insn.func,
1487 self.id(),
1488 "instruction belongs to another function"
1489 );
1490 let index = self
1491 .block(block)
1492 .instructions
1493 .iter()
1494 .position(|&local| local == insn.local)
1495 .expect("split point is not in the block");
1496 let tail = self.make_block();
1497 let moved: Vec<LocalInsnId> = self.block_mut(block).instructions.split_off(index);
1498 for &local in &moved {
1499 self.insn_mut(InstructionId::new(self.id(), local)).parent = Some(tail.local);
1500 }
1501 self.block_mut(tail).instructions = moved;
1502 self.rehome_outgoing_edges(tail, block);
1503 tail
1504 }
1505
1506 pub fn rehome_outgoing_edges(&mut self, keep: BlockId, remove: BlockId) {
1507 let outgoing: Vec<EdgeId> = {
1508 let block = self.block(remove);
1509 block
1510 .edges
1511 .iter()
1512 .copied()
1513 .filter(|&e| self.edge(e).from == remove.local)
1514 .collect()
1515 };
1516 for eid in outgoing {
1517 self.edges[eid].from = keep.local;
1518 self.block_mut(keep).edges.insert(eid);
1519 self.block_mut(remove).edges.remove(&eid);
1520 }
1521 }
1522
1523 pub fn replace_instruction_mnemonic(&mut self, id: InstructionId, mnemonic: Mnemonic) {
1526 assert_eq!(
1527 id.func,
1528 self.id(),
1529 "instruction belongs to another function"
1530 );
1531 self.replace_instruction_mnemonic_local(id.local, mnemonic);
1532 }
1533
1534 pub fn replace_instruction_mnemonic_local(&mut self, id: LocalInsnId, mnemonic: Mnemonic) {
1539 let old_args = self.insns[id]
1540 .mnemonic()
1541 .args()
1542 .into_iter()
1543 .collect::<Vec<_>>();
1544 for arg in old_args {
1545 let now_empty = if let Some(users) = self.users.get_mut(&arg) {
1546 users.retain(|&local| local != id);
1547 users.is_empty()
1548 } else {
1549 false
1550 };
1551 if now_empty {
1552 self.users.remove(&arg);
1553 }
1554 }
1555 *self.insns[id].mnemonic_mut() = mnemonic;
1556 let new_args = self.insns[id]
1557 .mnemonic()
1558 .args()
1559 .into_iter()
1560 .collect::<Vec<_>>();
1561 for arg in new_args {
1562 self.users.entry(arg).or_default().push(id);
1563 }
1564 }
1565
1566 pub fn rename_block_local(&mut self, block: LocalBlockId, name: Cow<'str, str>) -> Result<()> {
1572 let target = LocalValueId::BasicBlock(block);
1573 if let Some(existing) = self.names.get(&name) {
1574 return if existing == target {
1575 Ok(())
1576 } else {
1577 Err(Error::spanless(ErrorTy::DuplicateName(name.to_string())))
1578 };
1579 }
1580 let old_name = self.blocks[block].local_name().map(str::to_owned);
1581 self.names
1582 .register(name.clone(), target, old_name.as_deref())?;
1583 self.blocks[block].set_name(Some(name));
1584 Ok(())
1585 }
1586
1587 pub fn unroster_block(&mut self, block: BlockId) {
1590 self.roster.retain(|&b| b != block.localize(block.func));
1591 }
1592
1593 pub fn clear_block_instructions(&mut self, block: BlockId) {
1603 assert_eq!(block.func, self.id(), "block belongs to another function");
1604 let mut outgoing: Vec<EdgeId> = self
1605 .block(block)
1606 .edges
1607 .iter()
1608 .copied()
1609 .filter(|&edge| self.edges[edge].from == block.local)
1610 .collect();
1611 outgoing.sort_unstable();
1612 for edge in outgoing {
1613 self.remove_cfg_edge(edge);
1614 }
1615 let insns = std::mem::take(&mut self.block_mut(block).instructions);
1621 let dead: FxHashSet<LocalInsnId> = insns.iter().copied().collect();
1622 let names: Vec<Cow<'str, str>> = insns
1623 .iter()
1624 .filter_map(|&local| self.insns[local].name.clone())
1625 .collect();
1626 self.purge_instructions(&dead, names);
1627 }
1628
1629 pub fn delete_block(&mut self, block: BlockId) {
1630 assert_eq!(block.func, self.id(), "block belongs to another function");
1631 let mut edges: Vec<EdgeId> = self.block(block).edges.iter().copied().collect();
1632 edges.sort_unstable();
1633 for edge in edges {
1634 self.remove_cfg_edge(edge);
1635 }
1636 let insns: Vec<InstructionId> = self
1637 .block(block)
1638 .instructions
1639 .iter()
1640 .map(|&local| InstructionId::new(self.id(), local))
1641 .collect();
1642 for insn in insns {
1643 self.remove_instruction(insn);
1644 }
1645 let params: Vec<BlockParamId> = self
1646 .block(block)
1647 .params
1648 .iter()
1649 .map(|&local| BlockParamId::new(self.id(), local))
1650 .collect();
1651 for param in params {
1652 self.remove_block_param(param);
1653 }
1654 let name = self.block(block).local_name().map(str::to_owned);
1655 self.unroster_block(block);
1656 if self.root == Some(block.local) {
1657 self.root = None;
1658 }
1659 if let Some(name) = name {
1660 self.names.forget(&name);
1661 }
1662 self.blocks.remove(block.local);
1663 }
1664
1665 pub fn absorb_block(&mut self, keep: BlockId, other: BlockId, edge_ab: EdgeId) {
1669 assert_eq!(
1670 keep.func, other.func,
1671 "cannot absorb across function arenas"
1672 );
1673 let (branch_id, branch_args) = self
1674 .block(keep)
1675 .instructions
1676 .last()
1677 .and_then(
1678 |&local| match self.insn(InstructionId::new(keep.func, local)).mnemonic() {
1679 Mnemonic::Branch(branch) if BlockId::new(keep.func, branch.target) == other => {
1680 Some((InstructionId::new(keep.func, local), branch.args.clone()))
1681 }
1682 _ => None,
1683 },
1684 )
1685 .expect("absorbed block must be reached by keep's terminal branch");
1686 let other_params: Vec<_> = self
1687 .block(other)
1688 .params
1689 .iter()
1690 .map(|&local| BlockParamId::new(other.func, local))
1691 .collect();
1692 if !other_params.is_empty() {
1693 assert_eq!(
1694 other_params.len(),
1695 branch_args.len(),
1696 "cannot absorb block with {} params through branch with {} args",
1697 other_params.len(),
1698 branch_args.len()
1699 );
1700 for (param, arg) in other_params.iter().copied().zip(branch_args) {
1701 self.replace_all_uses_with(ValueId::BlockParam(param), arg.qualify(keep.func));
1702 }
1703 }
1704 self.remove_cfg_edge(edge_ab);
1705 self.remove_instruction(branch_id);
1706 let b_insns = std::mem::take(&mut self.block_mut(other).instructions);
1707 for &local in &b_insns {
1708 self.insn_mut(InstructionId::new(other.func, local)).parent = Some(keep.local);
1709 }
1710 self.block_mut(keep).instructions.extend(b_insns);
1711 self.rehome_outgoing_edges(keep, other);
1712 let (b_addr, b_extra, b_name) = {
1713 let b = self.block(other);
1714 (
1715 b.address,
1716 b.extra_addresses.clone(),
1717 b.local_name().map(str::to_owned),
1718 )
1719 };
1720 for param in other_params {
1721 self.remove_block_param(param);
1722 }
1723 self.unroster_block(other);
1724 if self.root == Some(other.local) {
1725 self.root = Some(keep.local);
1726 }
1727 if let Some(name) = b_name {
1728 self.names.forget(&name);
1729 }
1730 self.blocks.remove(other.local);
1731 if let Some(addr) = b_addr {
1732 self.block_mut(keep).extra_addresses.push(addr);
1733 }
1734 self.block_mut(keep).extra_addresses.extend(b_extra);
1735 }
1736
1737 pub fn register_local_name(
1742 &mut self,
1743 shared: &crate::context::Shared<'str>,
1744 id: ValueId,
1745 name: Cow<'str, str>,
1746 old_name: Option<&str>,
1747 ) -> Result<()> {
1748 if id.name_scope_function().is_none() {
1749 return match shared.get_named(&name) {
1750 Some(existing) if existing == id => Ok(()),
1751 Some(_) => Err(Error::spanless(ErrorTy::DuplicateName(name.to_string()))),
1752 None => unimplemented!(
1753 "a function body cannot register a global name (shared is read-only)"
1754 ),
1755 };
1756 }
1757 self.register_body_name(id, name, old_name)
1758 }
1759
1760 pub fn register_body_name(
1765 &mut self,
1766 id: ValueId,
1767 name: Cow<'str, str>,
1768 old_name: Option<&str>,
1769 ) -> Result<()> {
1770 assert!(
1771 id.name_scope_function().is_some(),
1772 "register_body_name on a global-scoped value {id:?}"
1773 );
1774 if let Some(existing) = self.names.get(&name).map(|id| id.qualify(self.id())) {
1775 return if existing == id {
1776 Ok(())
1777 } else {
1778 Err(Error::spanless(ErrorTy::DuplicateName(name.to_string())))
1779 };
1780 }
1781 self.names.register(name, id.localize(self.id()), old_name)
1782 }
1783
1784 pub fn from_id<'ctx>(ctx: &'ctx Context<'str>, id: FunctionId) -> FunctionRef<'str, 'ctx> {
1786 FunctionRef::new(ModuleView::new(ctx), id)
1787 }
1788
1789 pub fn from_id_mut<'ctx>(
1791 ctx: &'ctx mut Context<'str>,
1792 id: FunctionId,
1793 ) -> FunctionMutRef<'str, 'ctx> {
1794 FunctionMutRef::new(ctx, id)
1795 }
1796
1797 pub fn from_name<'ctx>(
1799 ctx: &'ctx Context<'str>,
1800 name: &str,
1801 ) -> Option<FunctionRef<'str, 'ctx>> {
1802 ctx.get_named(name)
1803 .and_then(ValueId::as_function)
1804 .map(|id| FunctionBody::from_id(ctx, id))
1805 }
1806
1807 pub fn make<'ctx>(
1809 ctx: &'ctx mut Context<'str>,
1810 name: Cow<'str, str>,
1811 ) -> Result<FunctionMutRef<'str, 'ctx>> {
1812 let id = FunctionId::from(ctx.bodies.len());
1813 let pushed = ctx.push_function(
1814 FunctionInterface::new(name.clone()),
1815 FunctionBody::empty_with_id(id),
1816 );
1817 debug_assert_eq!(pushed, id);
1818 ctx.update_name(name, id.into(), None)?;
1819 Ok(Self::from_id_mut(ctx, id))
1820 }
1821
1822 pub fn make_lambda<'ctx>(
1824 ctx: &'ctx mut Context<'str>,
1825 name: Cow<'str, str>,
1826 ) -> Result<FunctionMutRef<'str, 'ctx>> {
1827 let mut function = Self::make(ctx, name)?;
1828 function.interface_mut().kind = FunctionKind::Lambda;
1829 function.set_is_pure(true);
1830 function.set_register_effects(RegisterChannelState::Materialized(
1831 RegisterInterfaceMap::default(),
1832 ));
1833 Ok(function)
1834 }
1835
1836 pub fn make_at_addr<'ctx>(
1838 ctx: &'ctx mut Context<'str>,
1839 address: u64,
1840 name: Option<Cow<'str, str>>,
1841 ) -> FunctionMutRef<'str, 'ctx> {
1842 let mut addresses = crate::address_index::AddressIndex::analyze(ctx);
1843 Self::make_at_addr_indexed(ctx, &mut addresses, address, name)
1844 }
1845
1846 pub fn make_at_addr_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 name = name.unwrap_or_else(|| Cow::Owned(format!("fn_{address:x}")));
1854 let id = FunctionId::from(ctx.bodies.len());
1855 let pushed = ctx.push_function(
1856 FunctionInterface::new(name.clone()),
1857 FunctionBody::empty_with_id(id),
1858 );
1859 debug_assert_eq!(pushed, id);
1860
1861 Self::from_id_mut(ctx, id)
1862 .with_name(name)
1863 .expect("Function name is not unique")
1864 .with_address_indexed(addresses, address)
1865 .expect("Function address is not unique")
1866 }
1867
1868 pub fn make_external<'ctx>(
1873 ctx: &'ctx mut Context<'str>,
1874 address: u64,
1875 name: Option<Cow<'str, str>>,
1876 ) -> FunctionMutRef<'str, 'ctx> {
1877 let mut f = Self::make_at_addr(ctx, address, name);
1878 f.interface_mut().is_external = true;
1879 f
1880 }
1881
1882 pub fn make_external_indexed<'ctx>(
1884 ctx: &'ctx mut Context<'str>,
1885 addresses: &mut crate::address_index::AddressIndex,
1886 address: u64,
1887 name: Option<Cow<'str, str>>,
1888 ) -> FunctionMutRef<'str, 'ctx> {
1889 let mut function = Self::make_at_addr_indexed(ctx, addresses, address, name);
1890 function.interface_mut().is_external = true;
1891 function
1892 }
1893
1894 pub fn from_addr_or_create<'ctx>(
1896 ctx: &'ctx mut Context<'str>,
1897 address: u64,
1898 ) -> FunctionMutRef<'str, 'ctx> {
1899 let mut addresses = crate::address_index::AddressIndex::analyze(ctx);
1900 Self::from_addr_or_create_indexed(ctx, &mut addresses, address)
1901 }
1902
1903 pub fn from_addr_or_create_indexed<'ctx>(
1906 ctx: &'ctx mut Context<'str>,
1907 addresses: &mut crate::address_index::AddressIndex,
1908 address: u64,
1909 ) -> FunctionMutRef<'str, 'ctx> {
1910 match addresses.function_at(address) {
1911 Some(id) => Self::from_id_mut(ctx, id),
1912 None => Self::make_at_addr_indexed(ctx, addresses, address, None),
1913 }
1914 }
1915}
1916
1917impl<'s, 'ctx: 's, 'str: 'ctx, R> FunctionRef<'str, 'ctx, R>
1918where
1919 R: QCodeView<'ctx, 'str>,
1920{
1921 fn inner(&'s self) -> &'ctx FunctionBody<'str> {
1922 self.view.function(self.id)
1923 }
1924
1925 fn interface(&'s self) -> &'ctx FunctionInterface<'str> {
1928 self.view.interface(self.id)
1929 }
1930
1931 fn size(&self) -> usize {
1932 0
1933 }
1934
1935 pub fn address(&'s self) -> Option<u64> {
1937 self.interface().address
1938 }
1939
1940 pub fn is_external(&'s self) -> bool {
1942 self.interface().is_external
1943 }
1944
1945 pub fn import_ordinal(&'s self) -> Option<u16> {
1948 self.interface().import_ordinal
1949 }
1950
1951 pub fn signature(&'s self) -> Option<&'ctx FunctionSignature> {
1953 self.interface().signature.as_ref()
1954 }
1955
1956 pub fn users_of(&'s self, value: ValueId) -> Vec<InstructionId> {
1960 let func = self.id;
1961 if value.owning_function().is_some_and(|owner| owner != func) {
1962 return Vec::new();
1963 }
1964 self.inner().users_of(value)
1965 }
1966
1967 pub fn local_users_of(&'s self, value: ValueId) -> &'ctx [LocalInsnId] {
1973 let func = self.id;
1974 if value.owning_function().is_some_and(|owner| owner != func) {
1975 return &[];
1976 }
1977 self.inner().local_users_of(value)
1978 }
1979
1980 pub fn has_users(&'s self, value: ValueId) -> bool {
1983 let func = self.id;
1984 if value.owning_function().is_some_and(|owner| owner != func) {
1985 return false;
1986 }
1987 self.inner().has_users(value)
1988 }
1989
1990 pub fn user_map_entries(&'s self) -> impl Iterator<Item = (ValueId, Vec<InstructionId>)> + 's {
1993 let func = self.id;
1994 self.inner().user_map_entries().map(move |(v, u)| {
1995 (
1996 v.qualify(func),
1997 u.iter()
1998 .map(|&local| InstructionId::new(func, local))
1999 .collect(),
2000 )
2001 })
2002 }
2003
2004 pub fn local_named(&'s self, name: &str) -> Option<ValueId> {
2007 self.inner().names.get(name).map(|id| id.qualify(self.id))
2008 }
2009
2010 pub fn param_attr(&'s self, index: usize) -> Option<ParamAttrs> {
2015 self.interface().param_attr(index)
2016 }
2017
2018 pub fn param_attrs(&'s self) -> Option<&'ctx [ParamAttrs]> {
2020 self.interface()
2021 .signature
2022 .as_ref()
2023 .and_then(|s| s.param_attrs.as_deref())
2024 }
2025
2026 pub fn written_spaces(&'s self) -> Option<&'ctx [crate::space::SpaceId]> {
2033 match &self.interface().effects.memory.coarse {
2034 WrittenSpacesState::Bounded(spaces) => Some(spaces),
2035 _ => None,
2036 }
2037 }
2038
2039 pub fn written_spaces_state(&'s self) -> WrittenSpaces<'ctx> {
2043 match &self.interface().effects.memory.coarse {
2044 WrittenSpacesState::Unstamped => WrittenSpaces::Unstamped,
2045 WrittenSpacesState::Unbounded => WrittenSpaces::Unbounded,
2046 WrittenSpacesState::Bounded(spaces) => WrittenSpaces::Bounded(spaces),
2047 }
2048 }
2049
2050 pub fn is_reg_materialized(&'s self) -> bool {
2055 matches!(
2056 self.interface().effects.register,
2057 RegisterChannelState::Materialized(_)
2058 )
2059 }
2060
2061 pub fn effects(&'s self) -> &'ctx FunctionEffects {
2065 &self.interface().effects
2066 }
2067
2068 pub fn is_pure(&'s self) -> bool {
2073 self.interface()
2074 .signature
2075 .as_ref()
2076 .is_some_and(|s| s.is_pure)
2077 }
2078
2079 pub fn is_lambda(&'s self) -> bool {
2081 self.interface().kind == FunctionKind::Lambda
2082 }
2083
2084 pub fn kind(&'s self) -> FunctionKind {
2085 self.interface().kind
2086 }
2087
2088 pub fn extern_interface(&'s self) -> Option<&'ctx crate::value::ExternInterface> {
2092 self.interface()
2093 .signature
2094 .as_ref()
2095 .and_then(|s| s.extern_interface.as_ref())
2096 }
2097
2098 pub fn argmem(&'s self) -> Option<&'ctx crate::value::ExternArgmem> {
2102 self.interface()
2103 .signature
2104 .as_ref()
2105 .and_then(|s| s.argmem.as_ref())
2106 }
2107
2108 pub fn input_arg_name(&'s self, index: usize) -> Option<String> {
2114 if let Some(root) = self.root()
2120 && let Some(name) = root
2121 .params()
2122 .nth(index)
2123 .and_then(|p| p.name().map(str::to_owned))
2124 {
2125 return Some(name);
2126 }
2127
2128 self.extern_interface()
2131 .and_then(|iface| iface.args.get(index))
2132 .and_then(|a| a.name.as_ref().map(|n| n.to_string()))
2133 }
2134
2135 pub fn reads_unbounded_stack(&'s self) -> bool {
2139 self.interface()
2140 .signature
2141 .as_ref()
2142 .is_some_and(|s| s.reads_unbounded_stack)
2143 }
2144
2145 pub fn frame_escapes_to_unbounded(&'s self) -> bool {
2149 self.interface()
2150 .signature
2151 .as_ref()
2152 .is_some_and(|s| s.frame_escapes_to_unbounded)
2153 }
2154
2155 pub fn name(&'s self) -> &'ctx str {
2157 self.interface().name.as_ref()
2158 }
2159
2160 pub fn instruction_addrs(&'s self) -> impl Iterator<Item = u64> + 'ctx {
2164 self.inner().instruction_addrs.iter().copied()
2165 }
2166
2167 pub fn has_map(&'s self) -> bool {
2171 self.blocks().any(|block| {
2172 block
2173 .instructions()
2174 .any(|insn| matches!(insn.mnemonic(), Mnemonic::Map(_)))
2175 })
2176 }
2177
2178 pub fn has_scan(&'s self) -> bool {
2182 self.blocks().any(|block| {
2183 block
2184 .instructions()
2185 .any(|insn| matches!(insn.mnemonic(), Mnemonic::Scan(_)))
2186 })
2187 }
2188
2189 pub fn root(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
2191 self.inner()
2192 .root
2193 .map(|local| BlockRef::new(self.view, BlockId::new(self.id, local)))
2194 }
2195
2196 pub fn blocks(&'s self) -> impl Iterator<Item = BlockRef<'str, 'ctx, R>> + 's {
2198 let view = self.view;
2199 let mut ids = self.block_ids();
2200 ids.sort_by_key(|&id| (BlockRef::new(view, id).address(), id.local));
2204 ids.into_iter().map(move |id| BlockRef::new(view, id))
2205 }
2206
2207 pub fn block_ids(&'s self) -> Vec<BlockId> {
2209 let func = self.id;
2210 self.inner()
2211 .roster
2212 .iter()
2213 .copied()
2214 .map(|local| BlockId::new(func, local))
2215 .collect()
2216 }
2217
2218 pub fn instruction_ids(&'s self) -> Vec<InstructionId> {
2221 let func = self.id;
2222 self.inner()
2223 .insns
2224 .iter()
2225 .map(|i| InstructionId::new(func, i.id))
2226 .collect()
2227 }
2228
2229 pub fn edge_ids(&'s self) -> Vec<crate::value::block::EdgeId> {
2232 self.inner().edges.iter().map(|e| e.id).collect()
2233 }
2234
2235 pub fn iter(&'s self) -> BlockIter<'str, 'ctx, R> {
2238 BlockIter {
2239 view: self.view,
2240 inner: self.block_ids().into_iter(),
2241 marker: PhantomData,
2242 }
2243 }
2244
2245 fn fmt(&'s self, f: &mut Formatter<'_>) -> std::fmt::Result {
2246 if self.is_external() {
2247 return writeln!(f, "extern fn {};", self.name());
2248 }
2249 let keyword = match self.kind() {
2250 FunctionKind::Machine => "fn",
2251 FunctionKind::Lambda => "lambda",
2252 };
2253 writeln!(f, "{keyword} {}:", self.name())?;
2254 for block in self.blocks() {
2255 block.fmt(f)?;
2256 }
2257 Ok(())
2258 }
2259}
2260
2261#[derive(Clone, Copy)]
2262pub struct FunctionRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
2263 pub id: FunctionId,
2264 pub(in crate::value) view: R,
2265 marker: PhantomData<&'ctx &'str ()>,
2266}
2267
2268impl<'str, 'ctx, R> FunctionRef<'str, 'ctx, R> {
2269 pub fn new(view: R, id: FunctionId) -> Self {
2270 Self {
2271 id,
2272 view,
2273 marker: PhantomData,
2274 }
2275 }
2276
2277 pub fn id(&self) -> ValueId {
2278 self.id.into()
2279 }
2280}
2281
2282impl<'str, 'ctx> FunctionRef<'str, 'ctx> {
2283 pub fn from_id(ctx: &'ctx Context<'str>, id: FunctionId) -> Self {
2284 Self::new(ModuleView::new(ctx), id)
2285 }
2286}
2287
2288impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 'ctx, 'str> for FunctionRef<'str, 'ctx> {
2289 fn ctx(&'s self) -> &'ctx Context<'str> {
2290 self.view.context()
2294 }
2295}
2296
2297impl<'str: 'ctx, 'ctx, R> Named for FunctionRef<'str, 'ctx, R>
2298where
2299 R: QCodeView<'ctx, 'str>,
2300{
2301 fn name(&self) -> Option<&str> {
2302 Some(self.view.interface(self.id).name.as_ref())
2303 }
2304}
2305
2306impl<'str: 'ctx, 'ctx, R> Display for FunctionRef<'str, 'ctx, R>
2307where
2308 R: QCodeView<'ctx, 'str>,
2309{
2310 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2311 FunctionRef::fmt(self, f)
2312 }
2313}
2314
2315impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for FunctionRef<'str, 'ctx, R>
2316where
2317 R: QCodeView<'ctx, 'str>,
2318{
2319 fn id(&self) -> ValueId {
2320 self.id()
2321 }
2322
2323 fn size(&self) -> usize {
2324 FunctionRef::size(self)
2325 }
2326}
2327
2328pub struct BlockIter<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
2329 view: R,
2330 inner: std::vec::IntoIter<BlockId>,
2331 marker: PhantomData<&'ctx &'str ()>,
2332}
2333
2334impl<'str: 'ctx, 'ctx, R> Iterator for BlockIter<'str, 'ctx, R>
2335where
2336 R: QCodeView<'ctx, 'str>,
2337{
2338 type Item = BlockRef<'str, 'ctx, R>;
2339
2340 fn next(&mut self) -> Option<Self::Item> {
2341 self.inner.next().map(|id| BlockRef::new(self.view, id))
2342 }
2343}
2344
2345impl<'str: 'ctx, 'ctx, R> IntoIterator for &FunctionRef<'str, 'ctx, R>
2346where
2347 R: QCodeView<'ctx, 'str>,
2348{
2349 type Item = BlockRef<'str, 'ctx, R>;
2350 type IntoIter = BlockIter<'str, 'ctx, R>;
2351
2352 fn into_iter(self) -> Self::IntoIter {
2353 self.iter()
2354 }
2355}
2356
2357pub type FunctionMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, FunctionId>;
2358
2359impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for FunctionMutRef<'str, 'ctx> {
2360 fn ctx(&'s self) -> &'s Context<'str> {
2361 self.ctx
2362 }
2363}
2364
2365impl<'s, 'ctx: 's, 'str: 'ctx> WithCtxMut<'s, 'str> for FunctionMutRef<'str, 'ctx> {
2366 fn ctx_mut(&'s mut self) -> &'s mut Context<'str> {
2367 self.ctx
2368 }
2369}
2370
2371impl Display for FunctionMutRef<'_, '_> {
2372 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2373 self.as_ref().fmt(f)
2374 }
2375}
2376
2377impl<'ctx, 'str> Value<'str, 'ctx> for FunctionMutRef<'str, 'ctx> {
2378 fn id(&self) -> ValueId {
2379 self.id()
2380 }
2381
2382 fn size(&self) -> usize {
2383 self.as_ref().size()
2384 }
2385}
2386
2387impl Named for FunctionMutRef<'_, '_> {
2388 fn name(&self) -> Option<&str> {
2389 Some(self.ctx.interfaces[self.id].name.as_ref())
2390 }
2391}
2392
2393impl<'str, 'ctx> Renameable<'str, 'ctx> for FunctionMutRef<'str, 'ctx> {
2394 fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
2395 let id = self.id();
2396 let old_name = self.ctx.interfaces[self.id].name.as_ref().to_owned();
2397 update_context_name(id, self.ctx, name.clone(), Some(old_name.as_ref()))?;
2398 self.ctx.interfaces[self.id].name = name;
2399 Ok(())
2400 }
2401}
2402
2403impl<'str, 'ctx> FunctionMutRef<'str, 'ctx> {
2404 pub fn as_ref(&self) -> FunctionRef<'str, '_> {
2405 FunctionRef::new(ModuleView::new(self.ctx), self.id)
2406 }
2407
2408 fn inner(&self) -> &FunctionBody<'str> {
2409 self.ctx.function(self.id)
2410 }
2411
2412 fn interface(&self) -> &FunctionInterface<'str> {
2413 &self.ctx.interfaces[self.id]
2414 }
2415
2416 fn address(&self) -> Option<u64> {
2417 self.interface().address
2418 }
2419
2420 pub fn name(&self) -> &str {
2421 self.interface().name.as_ref()
2422 }
2423
2424 pub fn blocks(&self) -> impl Iterator<Item = BlockRef<'str, '_>> {
2425 self.as_ref().blocks().collect::<Vec<_>>().into_iter()
2426 }
2427
2428 pub fn root(&self) -> Option<BlockRef<'str, '_>> {
2429 self.as_ref().root()
2430 }
2431
2432 pub(crate) fn inner_mut(&mut self) -> &mut FunctionBody<'str> {
2433 &mut self.ctx.bodies[self.id]
2434 }
2435
2436 pub(crate) fn interface_mut(&mut self) -> &mut FunctionInterface<'str> {
2439 &mut self.ctx.interfaces[self.id]
2440 }
2441
2442 fn set_address(&mut self, address: u64) -> Result<()> {
2443 let mut addresses = crate::address_index::AddressIndex::analyze(&*self.ctx);
2444 self.set_address_indexed(&mut addresses, address)
2445 }
2446
2447 fn set_address_indexed(
2448 &mut self,
2449 addresses: &mut crate::address_index::AddressIndex,
2450 address: u64,
2451 ) -> Result<()> {
2452 let old_address = self.interface().address;
2453 self.interface_mut().address = Some(address);
2454 if let Err(error) = self
2455 .ctx
2456 .set_address_indexed(addresses, address, self.id.into())
2457 {
2458 self.interface_mut().address = old_address;
2459 return Err(error);
2460 }
2461 Ok(())
2462 }
2463
2464 fn with_address_indexed(
2465 mut self,
2466 addresses: &mut crate::address_index::AddressIndex,
2467 address: u64,
2468 ) -> Result<Self> {
2469 self.set_address_indexed(addresses, address)?;
2470 Ok(self)
2471 }
2472
2473 pub fn set_root(&mut self, id: BlockId) -> Result<()> {
2478 assert_eq!(
2479 id.func, self.id,
2480 "cannot root a function at a block stored in another function arena"
2481 );
2482 self.add_block(id);
2483 self.inner_mut().root = Some(id.localize(self.id));
2484
2485 let block_addr = BasicBlock::from_id(&*self.ctx, id).address();
2486 let self_addr = self.address();
2487
2488 match (self_addr, block_addr) {
2489 (Some(fn_addr), Some(block_addr)) if fn_addr != block_addr => {
2490 return Err(Error::spanless(ErrorTy::FunctionRootAddressMismatch {
2491 fn_addr,
2492 block_addr,
2493 }));
2494 }
2495 (None, Some(addr)) => {
2496 self.set_address(addr)
2497 .expect("This address should be valid");
2498 }
2499 (Some(addr), None) => {
2500 BasicBlock::from_id_mut(self.ctx, id)
2501 .set_address(addr)
2502 .expect("This address should be valid");
2503 }
2504 _ => {}
2505 }
2506 Ok(())
2507 }
2508
2509 pub fn make_root(&mut self) -> BlockRef<'str, '_> {
2510 let func = self.id;
2511 let root = BasicBlock::make(self.ctx, func).id;
2512 self.set_root(root).expect("We just created the block");
2513 BasicBlock::from_id(&*self.ctx, root)
2514 }
2515
2516 pub fn ensure_root(&mut self, id: BlockId) -> Result<()> {
2517 assert_eq!(
2518 id.func, self.id,
2519 "cannot ensure a function root from another function arena"
2520 );
2521 if let Some(root) = self.inner().root {
2522 if root != id.localize(self.id) {
2523 return Err(Error::spanless(ErrorTy::FunctionRootMismatch {
2524 expected: BlockId::new(self.id, root),
2525 actual: id,
2526 }));
2527 }
2528 Ok(())
2529 } else {
2530 self.set_root(id)
2531 }
2532 }
2533
2534 pub fn set_external(&mut self, is_external: bool) {
2535 self.interface_mut().is_external = is_external;
2536 assert!(
2537 self.inner().blocks.is_empty(),
2538 "External functions should not have blocks"
2539 );
2540 }
2541
2542 pub fn set_import_ordinal(&mut self, ordinal: Option<u16>) {
2546 self.interface_mut().import_ordinal = ordinal;
2547 }
2548
2549 pub fn set_kind(&mut self, kind: FunctionKind) {
2550 self.interface_mut().kind = kind;
2551 if kind == FunctionKind::Lambda {
2552 self.set_is_pure(true);
2553 self.set_register_effects(RegisterChannelState::Materialized(
2554 RegisterInterfaceMap::default(),
2555 ));
2556 }
2557 }
2558
2559 pub fn set_signature(&mut self, sig: FunctionSignature) {
2560 self.ctx.interfaces[self.id].signature = Some(sig);
2561 }
2562
2563 pub fn set_param_attrs(&mut self, attrs: Vec<ParamAttrs>) {
2566 self.interface_mut()
2567 .signature
2568 .get_or_insert_default()
2569 .param_attrs = Some(attrs);
2570 }
2571
2572 pub fn clear_param_attrs(&mut self) {
2575 if let Some(sig) = self.interface_mut().signature.as_mut() {
2576 sig.param_attrs = None;
2577 }
2578 }
2579
2580 pub fn set_written_spaces(&mut self, spaces: Option<Vec<crate::space::SpaceId>>) {
2586 let coarse = match spaces {
2587 Some(spaces) => WrittenSpacesState::Bounded(spaces),
2588 None => WrittenSpacesState::Unbounded,
2589 };
2590 let precise = self.interface_mut().effects.memory.precise.take();
2593 self.set_memory_solved(coarse, precise);
2594 }
2595
2596 pub fn set_extern_interface(&mut self, iface: crate::value::ExternInterface) {
2600 self.interface_mut()
2601 .signature
2602 .get_or_insert_default()
2603 .extern_interface = Some(iface);
2604 }
2605
2606 pub fn set_argmem(&mut self, argmem: crate::value::ExternArgmem) {
2610 self.interface_mut()
2611 .signature
2612 .get_or_insert_default()
2613 .argmem = Some(argmem);
2614 }
2615
2616 pub fn set_register_effects(&mut self, register: RegisterChannelState) {
2619 self.interface_mut().effects.register = register;
2620 }
2621
2622 pub fn set_memory_effects(&mut self, memory: MemoryChannelState) {
2633 self.interface_mut().effects.memory = memory;
2634 }
2635
2636 pub fn set_memory_solved(&mut self, coarse: WrittenSpacesState, precise: Option<Footprint>) {
2642 let memory = &mut self.interface_mut().effects.memory;
2643 memory.coarse = coarse;
2644 memory.precise = precise;
2645 }
2646
2647 pub fn set_memory_interface(&mut self, materialized: Option<MemoryInterfaceMap>) {
2650 self.interface_mut().effects.memory.materialized = materialized;
2651 }
2652
2653 pub fn set_is_pure(&mut self, value: bool) {
2657 self.interface_mut()
2658 .signature
2659 .get_or_insert_default()
2660 .is_pure = value;
2661 }
2662
2663 pub fn set_reads_unbounded_stack(&mut self, value: bool) {
2666 self.interface_mut()
2667 .signature
2668 .get_or_insert_default()
2669 .reads_unbounded_stack = value;
2670 }
2671
2672 pub fn set_frame_escapes_to_unbounded(&mut self, value: bool) {
2676 self.interface_mut()
2677 .signature
2678 .get_or_insert_default()
2679 .frame_escapes_to_unbounded = value;
2680 }
2681
2682 pub fn add_instruction_addr(&mut self, addr: u64) {
2684 self.inner_mut().instruction_addrs.insert(addr);
2685 }
2686
2687 pub fn add_block(&mut self, id: BlockId) {
2695 assert_eq!(
2696 id.func, self.id,
2697 "cannot add a block stored in another function arena"
2698 );
2699 let local = id.localize(self.id);
2700 if !self.inner().roster.contains(&local) {
2703 self.inner_mut().roster.push(local);
2704 }
2705 }
2706}
2707
2708#[cfg(test)]
2709mod tests {
2710 use wazabin_qcode_macro::qcode;
2711
2712 use super::*;
2713
2714 fn foreign_block_fixture() -> (Context<'static>, FunctionId, BlockId) {
2715 let mut ctx = Context::new();
2716 let owner = FunctionBody::make(&mut ctx, "block_owner".into())
2717 .unwrap()
2718 .id;
2719 let destination = FunctionBody::make(&mut ctx, "block_destination".into())
2720 .unwrap()
2721 .id;
2722 let block = BasicBlock::make(&mut ctx, owner).id;
2723 (ctx, destination, block)
2724 }
2725
2726 #[test]
2727 fn raw_root_and_roster_are_local_while_refs_qualify_per_function() {
2728 let mut ctx = Context::new();
2729 let a = FunctionBody::make(&mut ctx, "local_root_a".into())
2730 .unwrap()
2731 .id;
2732 let b = FunctionBody::make(&mut ctx, "local_root_b".into())
2733 .unwrap()
2734 .id;
2735 let a_root = BasicBlock::make(&mut ctx, a).id;
2736 let b_root = BasicBlock::make(&mut ctx, b).id;
2737 assert_eq!(a_root.local, b_root.local, "arena-local ids should collide");
2738 FunctionBody::from_id_mut(&mut ctx, a)
2739 .set_root(a_root)
2740 .unwrap();
2741 FunctionBody::from_id_mut(&mut ctx, b)
2742 .set_root(b_root)
2743 .unwrap();
2744
2745 assert_eq!(ctx.bodies[a].root_id(), Some(a_root.local));
2746 assert_eq!(ctx.bodies[b].root_id(), Some(b_root.local));
2747 assert_eq!(ctx.bodies[a].roster, vec![a_root.local]);
2748 assert_eq!(ctx.bodies[b].roster, vec![b_root.local]);
2749 assert_eq!(
2750 FunctionBody::from_id(&ctx, a).root().map(|root| root.id),
2751 Some(a_root)
2752 );
2753 assert_eq!(
2754 FunctionBody::from_id(&ctx, b).root().map(|root| root.id),
2755 Some(b_root)
2756 );
2757 }
2758
2759 #[test]
2760 #[should_panic(expected = "cannot add a block stored in another function arena")]
2761 fn add_block_rejects_foreign_storage() {
2762 let (mut ctx, destination, block) = foreign_block_fixture();
2763 FunctionBody::from_id_mut(&mut ctx, destination).add_block(block);
2764 }
2765
2766 #[test]
2767 #[should_panic(expected = "cannot root a function at a block stored in another function arena")]
2768 fn set_root_rejects_foreign_storage() {
2769 let (mut ctx, destination, block) = foreign_block_fixture();
2770 FunctionBody::from_id_mut(&mut ctx, destination)
2771 .set_root(block)
2772 .unwrap();
2773 }
2774
2775 #[test]
2776 #[should_panic(expected = "cannot ensure a function root from another function arena")]
2777 fn ensure_root_rejects_foreign_storage() {
2778 let (mut ctx, destination, block) = foreign_block_fixture();
2779 FunctionBody::from_id_mut(&mut ctx, destination)
2780 .ensure_root(block)
2781 .unwrap();
2782 }
2783
2784 #[test]
2785 fn function_ref_users_of_rejects_foreign_owned_values() {
2786 let mut ctx = Context::new();
2787 qcode!(
2788 ctx,
2789 "
2790 fn users_a:
2791 <a_entry>
2792 %a_def = i64 1 + i64 2;
2793 %a_user = %a_def + i64 3;
2794 return at %a_user;
2795
2796 fn users_b:
2797 <b_entry>
2798 %b_def = i64 1 + i64 2;
2799 %b_user = %b_def + i64 3;
2800 return at %b_user;
2801 "
2802 );
2803
2804 let a_ids = FunctionRef::from_id(&ctx, users_a)
2805 .root()
2806 .unwrap()
2807 .instruction_ids();
2808 let a_def = ValueId::Instruction(a_ids[0]);
2809 assert_eq!(
2810 FunctionRef::from_id(&ctx, users_a).users_of(a_def),
2811 vec![a_ids[1]]
2812 );
2813 assert!(
2814 FunctionRef::from_id(&ctx, users_b)
2815 .users_of(a_def)
2816 .is_empty()
2817 );
2818
2819 let one = ctx.get_const(1, 8).id();
2820 assert!(!FunctionRef::from_id(&ctx, users_b).users_of(one).is_empty());
2821 }
2822
2823 fn colliding_body_ids() -> (
2824 Context<'static>,
2825 FunctionId,
2826 FunctionId,
2827 BlockId,
2828 BlockId,
2829 InstructionId,
2830 InstructionId,
2831 BlockParamId,
2832 BlockParamId,
2833 ) {
2834 let mut ctx = Context::new();
2835 qcode!(
2836 ctx,
2837 "
2838 fn raw_a:
2839 <a_entry @a:i64>
2840 %a_def = i64 1 + i64 2;
2841 return at %a_def;
2842 fn raw_b:
2843 <b_entry @b:i64>
2844 %b_def = i64 1 + i64 2;
2845 return at %b_def;
2846 "
2847 );
2848 let a_root = FunctionRef::from_id(&ctx, raw_a).root().unwrap();
2849 let b_root = FunctionRef::from_id(&ctx, raw_b).root().unwrap();
2850 let a_block = a_root.id;
2851 let b_block = b_root.id;
2852 let a_insn = a_root.instruction_ids()[0];
2853 let b_insn = b_root.instruction_ids()[0];
2854 let a_param = a_root.params().next().unwrap().id;
2855 let b_param = b_root.params().next().unwrap().id;
2856 assert_eq!(a_block.local, b_block.local);
2857 assert_eq!(a_insn.local, b_insn.local);
2858 assert_eq!(a_param.local, b_param.local);
2859 (
2860 ctx, raw_a, raw_b, a_block, b_block, a_insn, b_insn, a_param, b_param,
2861 )
2862 }
2863
2864 #[test]
2868 fn replace_instruction_with_itself_is_a_noop() {
2869 let mut ctx = Context::new();
2870 qcode!(
2871 ctx,
2872 "
2873 fn f:
2874 <entry @a:i32>
2875 %x = @a + 1;
2876 %y = %x + 2;
2877 return %y;
2878 "
2879 );
2880 let root = FunctionBody::from_id(&ctx, f).root().unwrap().id;
2882 let insns: Vec<InstructionId> = BasicBlock::from_id(&ctx, root)
2883 .instruction_ids()
2884 .into_iter()
2885 .collect();
2886 let x = insns[0];
2887 let users_before = ctx.bodies[f].users_of(ValueId::Instruction(x));
2888 assert!(!users_before.is_empty(), "x should have a user (%y)");
2889
2890 ctx.bodies[f].replace_instruction(x, ValueId::Instruction(x));
2892
2893 assert!(
2894 ctx.bodies[f].insns.contains(x.local),
2895 "x must survive a self-replacement"
2896 );
2897 assert_eq!(
2898 ctx.bodies[f].users_of(ValueId::Instruction(x)),
2899 users_before,
2900 "x's users must be unchanged"
2901 );
2902 }
2903
2904 #[test]
2905 fn body_users_of_rejects_foreign_owned_values() {
2906 let (ctx, a, b, _, _, a_insn, _, _, _) = colliding_body_ids();
2907 assert!(
2908 ctx.bodies[b]
2909 .users_of(ValueId::Instruction(a_insn))
2910 .is_empty()
2911 );
2912 assert!(
2913 !ctx.bodies[a]
2914 .users_of(ValueId::Instruction(a_insn))
2915 .is_empty()
2916 );
2917 }
2918
2919 #[test]
2920 #[should_panic(expected = "cannot replace uses of a value owned by another function")]
2921 fn body_replace_uses_rejects_foreign_old() {
2922 let (mut ctx, _, b, _, _, a_insn, b_insn, _, _) = colliding_body_ids();
2923 ctx.bodies[b]
2924 .replace_all_uses_with(ValueId::Instruction(a_insn), ValueId::Instruction(b_insn));
2925 }
2926
2927 #[test]
2928 #[should_panic(expected = "cannot replace uses with a value owned by another function")]
2929 fn body_replace_uses_rejects_foreign_new() {
2930 let (mut ctx, _, b, _, _, a_insn, b_insn, _, _) = colliding_body_ids();
2931 ctx.bodies[b]
2932 .replace_all_uses_with(ValueId::Instruction(b_insn), ValueId::Instruction(a_insn));
2933 }
2934
2935 #[test]
2936 #[should_panic(expected = "block belongs to another function")]
2937 fn body_block_access_rejects_colliding_foreign_id() {
2938 let (ctx, _, b, a_block, _, _, _, _, _) = colliding_body_ids();
2939 let _ = ctx.bodies[b].block(a_block);
2940 }
2941
2942 #[test]
2943 #[should_panic(expected = "instruction belongs to another function")]
2944 fn body_insn_access_rejects_colliding_foreign_id() {
2945 let (ctx, _, b, _, _, a_insn, _, _, _) = colliding_body_ids();
2946 let _ = ctx.bodies[b].insn(a_insn);
2947 }
2948
2949 #[test]
2950 #[should_panic(expected = "block parameter belongs to another function")]
2951 fn body_param_access_rejects_colliding_foreign_id() {
2952 let (ctx, _, b, _, _, _, _, a_param, _) = colliding_body_ids();
2953 let _ = ctx.bodies[b].block_param(a_param);
2954 }
2955
2956 #[test]
2961 fn make_function_creates_function_with_correct_name_root_address() {
2962 let mut ctx = Context::new();
2963 let f = FunctionBody::make(&mut ctx, "main".into()).unwrap();
2964 assert_eq!(f.name(), "main");
2965 }
2966
2967 #[test]
2968 fn get_function_by_name_returns_correct_function() {
2969 let mut ctx = Context::new();
2970 let id = FunctionBody::make(&mut ctx, "foo".into()).unwrap().id();
2971 let f = FunctionBody::from_name(&ctx, "foo").unwrap();
2972 assert_eq!(f.id(), id);
2973 assert_eq!(f.name(), "foo");
2974 }
2975
2976 #[test]
2977 fn get_function_by_name_returns_none_if_not_found() {
2978 let ctx = Context::new();
2979 assert!(FunctionBody::from_name(&ctx, "nonexistent").is_none());
2980 }
2981
2982 #[test]
2983 fn get_function_by_addr_returns_correct_function() {
2984 let mut ctx = Context::new();
2985 let id = FunctionBody::make_at_addr(&mut ctx, 0x2000, None).id();
2986 let addresses = crate::address_index::AddressIndex::analyze(&ctx);
2987 let f = FunctionBody::from_id(&ctx, addresses.function_at(0x2000).unwrap());
2988 assert_eq!(f.id(), id);
2989 assert_eq!(f.address(), Some(0x2000));
2990 assert_eq!(f.name(), "fn_2000");
2991 }
2992
2993 #[test]
2994 fn get_function_by_addr_returns_none_if_missing() {
2995 let ctx = Context::new();
2996 let addresses = crate::address_index::AddressIndex::analyze(&ctx);
2997 assert!(addresses.function_at(0xdeadbeef).is_none());
2998 }
2999
3000 #[test]
3001 fn add_block_via_function_mut_ref_updates_blocks_list() {
3002 let mut ctx = Context::new();
3003 let baz_id = FunctionBody::make(&mut ctx, "baz".into()).unwrap().id;
3004 let root = BasicBlock::make(&mut ctx, baz_id).id;
3005 let extra = BasicBlock::make(&mut ctx, baz_id).id;
3006
3007 let mut baz = FunctionBody::from_id_mut(&mut ctx, baz_id);
3008 baz.add_block(root);
3009 baz.add_block(extra);
3010
3011 let block_ids: Vec<_> = baz.blocks().map(|b| b.id).collect();
3012 assert!(block_ids.contains(&root));
3013 assert!(block_ids.contains(&extra));
3014 }
3015
3016 #[test]
3017 fn display_shows_function_name_and_block_contents() {
3018 let mut ctx = Context::new();
3019 FunctionBody::make(&mut ctx, "display_test".into()).unwrap();
3020
3021 let f = FunctionBody::from_name(&ctx, "display_test").unwrap();
3022
3023 let s = f.to_string();
3024 assert!(s.contains("fn display_test:"));
3025 }
3026
3027 #[test]
3028 fn iter_yields_all_blocks() {
3029 let mut ctx = Context::new();
3030 let f_id = FunctionBody::make(&mut ctx, "iter_fn".into()).unwrap().id;
3031 let root = BasicBlock::make(&mut ctx, f_id).id;
3032 let extra = BasicBlock::make(&mut ctx, f_id).id;
3033 let mut f = FunctionBody::from_id_mut(&mut ctx, f_id);
3034 f.add_block(root);
3035 f.add_block(extra);
3036
3037 let f = FunctionBody::from_name(&ctx, "iter_fn").unwrap();
3038 let ids: Vec<_> = f.iter().map(|b| b.id).collect();
3039 assert!(ids.contains(&root));
3040 assert!(ids.contains(&extra));
3041 }
3042
3043 #[test]
3044 fn into_iterator_for_function_ref_matches_iter() {
3045 let mut ctx = Context::new();
3046 let f_id = FunctionBody::make(&mut ctx, "into_iter_fn".into())
3047 .unwrap()
3048 .id;
3049 let b1 = BasicBlock::make(&mut ctx, f_id).id;
3050 let b2 = BasicBlock::make(&mut ctx, f_id).id;
3051 let mut f = FunctionBody::from_id_mut(&mut ctx, f_id);
3052 f.add_block(b1);
3053 f.add_block(b2);
3054
3055 let f = FunctionBody::from_name(&ctx, "into_iter_fn").unwrap();
3056 let mut via_iter: Vec<usize> = f.iter().map(|b| usize::from(b.id.local)).collect();
3057 let mut via_into: Vec<usize> = (&f).into_iter().map(|b| usize::from(b.id.local)).collect();
3058 via_iter.sort();
3059 via_into.sort();
3060 assert_eq!(via_iter, via_into);
3061 }
3062
3063 #[test]
3064 fn qcode_fn_single_block_populates_function() {
3065 let mut ctx = Context::new();
3066 qcode!(
3067 ctx,
3068 "
3069 fn simple:
3070 <entry>
3071 return at 0;
3072 "
3073 );
3074
3075 let f = FunctionBody::from_name(&ctx, "simple").unwrap();
3076 assert_eq!(f.name(), "simple");
3077 assert!(f.root().is_some());
3078 assert_eq!(f.root().unwrap().name().unwrap(), "entry");
3079 assert_eq!(f.blocks().count(), 1);
3080 }
3081
3082 #[test]
3083 fn qcode_fn_multi_block_populates_all_blocks() {
3084 let mut ctx = Context::new();
3085 qcode!(
3086 ctx,
3087 "
3088 fn multiblock:
3089 <bb1>
3090 if i8 1 goto <bb2> else goto <bb3>;
3091
3092 <bb2>
3093 goto <bb3>;
3094
3095 <bb3>
3096 return at 0;
3097 "
3098 );
3099
3100 let f = FunctionBody::from_name(&ctx, "multiblock").unwrap();
3101 assert_eq!(f.root().unwrap().name().unwrap(), "bb1");
3102 let block_names: Vec<_> = f.blocks().filter_map(|b| b.name()).collect();
3103 assert!(block_names.contains(&"bb1"), "missing bb1");
3104 assert!(block_names.contains(&"bb2"), "missing bb2");
3105 assert!(block_names.contains(&"bb3"), "missing bb3");
3106 assert_eq!(f.blocks().count(), 3);
3107 }
3108
3109 #[test]
3110 fn qcode_fn_id_variable_is_set() {
3111 let mut ctx = Context::new();
3112 qcode!(
3113 ctx,
3114 "
3115 fn myfn:
3116 <start>
3117 return at 0;
3118 "
3119 );
3120
3121 let by_name = FunctionBody::from_name(&ctx, "myfn").unwrap();
3122 assert_eq!(by_name.name(), "myfn");
3123 }
3124
3125 #[test]
3128 fn indexed_address_registration_keeps_foreign_block_rootless() {
3129 let mut ctx = Context::new();
3130
3131 let block_id = {
3134 let __f = ctx.anon_function();
3135 BasicBlock::make(&mut ctx, __f)
3136 }
3137 .id;
3138 let mut addresses = crate::address_index::AddressIndex::analyze(&ctx);
3139 addresses
3140 .register(
3141 &mut ctx,
3142 0x1000,
3143 crate::address_index::AddressTarget::Block(block_id),
3144 )
3145 .unwrap();
3146
3147 let fn_id = FunctionBody::make(&mut ctx, "fn_1000".into()).unwrap().id;
3148 addresses
3149 .register(
3150 &mut ctx,
3151 0x1000,
3152 crate::address_index::AddressTarget::Function(fn_id),
3153 )
3154 .unwrap();
3155
3156 assert_eq!(addresses.function_at(0x1000), Some(fn_id));
3157 assert_eq!(addresses.block_at(0x1000), None);
3158 assert!(FunctionBody::from_id(&ctx, fn_id).root().is_none());
3159 assert_ne!(block_id.func, fn_id);
3160 }
3161}
3162
3163#[cfg(test)]
3164mod memory_interface_tests {
3165 use super::*;
3166
3167 fn slot() -> InterfaceSlot {
3168 InterfaceSlot {
3169 base: SlotBase::Arg(0),
3170 offset: 8,
3171 size: 8,
3172 }
3173 }
3174
3175 #[test]
3182 fn memory_interface_round_trips_through_the_wire_format() {
3183 let state = MemoryChannelState {
3184 materialized: Some(MemoryInterfaceMap {
3185 inputs: vec![slot()],
3186 outputs: vec![InterfaceSlot {
3187 base: SlotBase::Global(0x2000),
3188 offset: 0,
3189 size: 4,
3190 }],
3191 }),
3192 ..MemoryChannelState::default()
3193 };
3194 let config = bincode::config::standard();
3195 let bytes = bincode::serde::encode_to_vec(&state, config).expect("encode memory state");
3196 let (decoded, _): (MemoryChannelState, _) =
3197 bincode::serde::decode_from_slice(&bytes, config).expect("decode memory state");
3198 assert_eq!(decoded, state);
3199 }
3200
3201 #[test]
3203 fn default_memory_state_is_not_materialized() {
3204 assert_eq!(MemoryChannelState::default().materialized(), None);
3205 }
3206
3207 #[test]
3210 fn stamping_written_spaces_preserves_the_materialized_interface() {
3211 let mut ctx = Context::new();
3212 let fid = FunctionBody::make(&mut ctx, "keeps_interface".into())
3213 .unwrap()
3214 .id;
3215 let map = MemoryInterfaceMap {
3216 inputs: vec![slot()],
3217 outputs: vec![],
3218 };
3219 let mut body = FunctionBody::from_id_mut(&mut ctx, fid);
3220 body.set_memory_effects(MemoryChannelState {
3221 materialized: Some(map.clone()),
3222 ..MemoryChannelState::default()
3223 });
3224 body.set_written_spaces(None);
3225
3226 let effects = FunctionBody::from_id(&ctx, fid).effects().memory.clone();
3227 assert_eq!(effects.materialized(), Some(&map));
3228 assert_eq!(effects.coarse, WrittenSpacesState::Unbounded);
3229 }
3230
3231 #[test]
3235 fn an_unmappable_base_is_distinct_from_a_global_and_is_not_bindable() {
3236 let unmappable = InterfaceSlot {
3237 base: SlotBase::Unmappable,
3238 offset: 0,
3239 size: 8,
3240 };
3241 let global = InterfaceSlot {
3242 base: SlotBase::Global(0),
3243 offset: 0,
3244 size: 8,
3245 };
3246 assert_ne!(unmappable, global);
3247 assert!(!unmappable.is_bindable());
3248 assert!(global.is_bindable());
3249 assert!(
3250 InterfaceSlot {
3251 base: SlotBase::Arg(0),
3252 offset: -8,
3253 size: 8,
3254 }
3255 .is_bindable()
3256 );
3257 }
3258}