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}")));
1857 let name = ctx.shared.name_map.unique(name);
1858 let id = FunctionId::from(ctx.bodies.len());
1859 let pushed = ctx.push_function(
1860 FunctionInterface::new(name.clone()),
1861 FunctionBody::empty_with_id(id),
1862 );
1863 debug_assert_eq!(pushed, id);
1864
1865 Self::from_id_mut(ctx, id)
1866 .with_name(name)
1867 .expect("Function name is not unique")
1868 .with_address_indexed(addresses, address)
1869 .expect("Function address is not unique")
1870 }
1871
1872 pub fn make_external<'ctx>(
1877 ctx: &'ctx mut Context<'str>,
1878 address: u64,
1879 name: Option<Cow<'str, str>>,
1880 ) -> FunctionMutRef<'str, 'ctx> {
1881 let mut f = Self::make_at_addr(ctx, address, name);
1882 f.interface_mut().is_external = true;
1883 f
1884 }
1885
1886 pub fn make_external_indexed<'ctx>(
1888 ctx: &'ctx mut Context<'str>,
1889 addresses: &mut crate::address_index::AddressIndex,
1890 address: u64,
1891 name: Option<Cow<'str, str>>,
1892 ) -> FunctionMutRef<'str, 'ctx> {
1893 let mut function = Self::make_at_addr_indexed(ctx, addresses, address, name);
1894 function.interface_mut().is_external = true;
1895 function
1896 }
1897
1898 pub fn from_addr_or_create<'ctx>(
1900 ctx: &'ctx mut Context<'str>,
1901 address: u64,
1902 ) -> FunctionMutRef<'str, 'ctx> {
1903 let mut addresses = crate::address_index::AddressIndex::analyze(ctx);
1904 Self::from_addr_or_create_indexed(ctx, &mut addresses, address)
1905 }
1906
1907 pub fn from_addr_or_create_indexed<'ctx>(
1910 ctx: &'ctx mut Context<'str>,
1911 addresses: &mut crate::address_index::AddressIndex,
1912 address: u64,
1913 ) -> FunctionMutRef<'str, 'ctx> {
1914 match addresses.function_at(address) {
1915 Some(id) => Self::from_id_mut(ctx, id),
1916 None => Self::make_at_addr_indexed(ctx, addresses, address, None),
1917 }
1918 }
1919}
1920
1921impl<'s, 'ctx: 's, 'str: 'ctx, R> FunctionRef<'str, 'ctx, R>
1922where
1923 R: QCodeView<'ctx, 'str>,
1924{
1925 fn inner(&'s self) -> &'ctx FunctionBody<'str> {
1926 self.view.function(self.id)
1927 }
1928
1929 fn interface(&'s self) -> &'ctx FunctionInterface<'str> {
1932 self.view.interface(self.id)
1933 }
1934
1935 fn size(&self) -> usize {
1936 0
1937 }
1938
1939 pub fn address(&'s self) -> Option<u64> {
1941 self.interface().address
1942 }
1943
1944 pub fn is_external(&'s self) -> bool {
1946 self.interface().is_external
1947 }
1948
1949 pub fn import_ordinal(&'s self) -> Option<u16> {
1952 self.interface().import_ordinal
1953 }
1954
1955 pub fn signature(&'s self) -> Option<&'ctx FunctionSignature> {
1957 self.interface().signature.as_ref()
1958 }
1959
1960 pub fn users_of(&'s self, value: ValueId) -> Vec<InstructionId> {
1964 let func = self.id;
1965 if value.owning_function().is_some_and(|owner| owner != func) {
1966 return Vec::new();
1967 }
1968 self.inner().users_of(value)
1969 }
1970
1971 pub fn local_users_of(&'s self, value: ValueId) -> &'ctx [LocalInsnId] {
1977 let func = self.id;
1978 if value.owning_function().is_some_and(|owner| owner != func) {
1979 return &[];
1980 }
1981 self.inner().local_users_of(value)
1982 }
1983
1984 pub fn has_users(&'s self, value: ValueId) -> bool {
1987 let func = self.id;
1988 if value.owning_function().is_some_and(|owner| owner != func) {
1989 return false;
1990 }
1991 self.inner().has_users(value)
1992 }
1993
1994 pub fn user_map_entries(&'s self) -> impl Iterator<Item = (ValueId, Vec<InstructionId>)> + 's {
1997 let func = self.id;
1998 self.inner().user_map_entries().map(move |(v, u)| {
1999 (
2000 v.qualify(func),
2001 u.iter()
2002 .map(|&local| InstructionId::new(func, local))
2003 .collect(),
2004 )
2005 })
2006 }
2007
2008 pub fn local_named(&'s self, name: &str) -> Option<ValueId> {
2011 self.inner().names.get(name).map(|id| id.qualify(self.id))
2012 }
2013
2014 pub fn param_attr(&'s self, index: usize) -> Option<ParamAttrs> {
2019 self.interface().param_attr(index)
2020 }
2021
2022 pub fn param_attrs(&'s self) -> Option<&'ctx [ParamAttrs]> {
2024 self.interface()
2025 .signature
2026 .as_ref()
2027 .and_then(|s| s.param_attrs.as_deref())
2028 }
2029
2030 pub fn written_spaces(&'s self) -> Option<&'ctx [crate::space::SpaceId]> {
2037 match &self.interface().effects.memory.coarse {
2038 WrittenSpacesState::Bounded(spaces) => Some(spaces),
2039 _ => None,
2040 }
2041 }
2042
2043 pub fn written_spaces_state(&'s self) -> WrittenSpaces<'ctx> {
2047 match &self.interface().effects.memory.coarse {
2048 WrittenSpacesState::Unstamped => WrittenSpaces::Unstamped,
2049 WrittenSpacesState::Unbounded => WrittenSpaces::Unbounded,
2050 WrittenSpacesState::Bounded(spaces) => WrittenSpaces::Bounded(spaces),
2051 }
2052 }
2053
2054 pub fn is_reg_materialized(&'s self) -> bool {
2059 matches!(
2060 self.interface().effects.register,
2061 RegisterChannelState::Materialized(_)
2062 )
2063 }
2064
2065 pub fn effects(&'s self) -> &'ctx FunctionEffects {
2069 &self.interface().effects
2070 }
2071
2072 pub fn is_pure(&'s self) -> bool {
2077 self.interface()
2078 .signature
2079 .as_ref()
2080 .is_some_and(|s| s.is_pure)
2081 }
2082
2083 pub fn is_lambda(&'s self) -> bool {
2085 self.interface().kind == FunctionKind::Lambda
2086 }
2087
2088 pub fn kind(&'s self) -> FunctionKind {
2089 self.interface().kind
2090 }
2091
2092 pub fn extern_interface(&'s self) -> Option<&'ctx crate::value::ExternInterface> {
2096 self.interface()
2097 .signature
2098 .as_ref()
2099 .and_then(|s| s.extern_interface.as_ref())
2100 }
2101
2102 pub fn argmem(&'s self) -> Option<&'ctx crate::value::ExternArgmem> {
2106 self.interface()
2107 .signature
2108 .as_ref()
2109 .and_then(|s| s.argmem.as_ref())
2110 }
2111
2112 pub fn input_arg_name(&'s self, index: usize) -> Option<String> {
2118 if let Some(root) = self.root()
2124 && let Some(name) = root
2125 .params()
2126 .nth(index)
2127 .and_then(|p| p.name().map(str::to_owned))
2128 {
2129 return Some(name);
2130 }
2131
2132 self.extern_interface()
2135 .and_then(|iface| iface.args.get(index))
2136 .and_then(|a| a.name.as_ref().map(|n| n.to_string()))
2137 }
2138
2139 pub fn reads_unbounded_stack(&'s self) -> bool {
2143 self.interface()
2144 .signature
2145 .as_ref()
2146 .is_some_and(|s| s.reads_unbounded_stack)
2147 }
2148
2149 pub fn frame_escapes_to_unbounded(&'s self) -> bool {
2153 self.interface()
2154 .signature
2155 .as_ref()
2156 .is_some_and(|s| s.frame_escapes_to_unbounded)
2157 }
2158
2159 pub fn name(&'s self) -> &'ctx str {
2161 self.interface().name.as_ref()
2162 }
2163
2164 pub fn instruction_addrs(&'s self) -> impl Iterator<Item = u64> + 'ctx {
2168 self.inner().instruction_addrs.iter().copied()
2169 }
2170
2171 pub fn has_map(&'s self) -> bool {
2175 self.blocks().any(|block| {
2176 block
2177 .instructions()
2178 .any(|insn| matches!(insn.mnemonic(), Mnemonic::Map(_)))
2179 })
2180 }
2181
2182 pub fn has_scan(&'s self) -> bool {
2186 self.blocks().any(|block| {
2187 block
2188 .instructions()
2189 .any(|insn| matches!(insn.mnemonic(), Mnemonic::Scan(_)))
2190 })
2191 }
2192
2193 pub fn root(&'s self) -> Option<BlockRef<'str, 'ctx, R>> {
2195 self.inner()
2196 .root
2197 .map(|local| BlockRef::new(self.view, BlockId::new(self.id, local)))
2198 }
2199
2200 pub fn blocks(&'s self) -> impl Iterator<Item = BlockRef<'str, 'ctx, R>> + 's {
2202 let view = self.view;
2203 let mut ids = self.block_ids();
2204 ids.sort_by_key(|&id| (BlockRef::new(view, id).address(), id.local));
2208 ids.into_iter().map(move |id| BlockRef::new(view, id))
2209 }
2210
2211 pub fn block_ids(&'s self) -> Vec<BlockId> {
2213 let func = self.id;
2214 self.inner()
2215 .roster
2216 .iter()
2217 .copied()
2218 .map(|local| BlockId::new(func, local))
2219 .collect()
2220 }
2221
2222 pub fn instruction_ids(&'s self) -> Vec<InstructionId> {
2225 let func = self.id;
2226 self.inner()
2227 .insns
2228 .iter()
2229 .map(|i| InstructionId::new(func, i.id))
2230 .collect()
2231 }
2232
2233 pub fn edge_ids(&'s self) -> Vec<crate::value::block::EdgeId> {
2236 self.inner().edges.iter().map(|e| e.id).collect()
2237 }
2238
2239 pub fn iter(&'s self) -> BlockIter<'str, 'ctx, R> {
2242 BlockIter {
2243 view: self.view,
2244 inner: self.block_ids().into_iter(),
2245 marker: PhantomData,
2246 }
2247 }
2248
2249 fn fmt(&'s self, f: &mut Formatter<'_>) -> std::fmt::Result {
2250 if self.is_external() {
2251 return writeln!(f, "extern fn {};", self.name());
2252 }
2253 let keyword = match self.kind() {
2254 FunctionKind::Machine => "fn",
2255 FunctionKind::Lambda => "lambda",
2256 };
2257 writeln!(f, "{keyword} {}:", self.name())?;
2258 for block in self.blocks() {
2259 block.fmt(f)?;
2260 }
2261 Ok(())
2262 }
2263}
2264
2265#[derive(Clone, Copy)]
2266pub struct FunctionRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
2267 pub id: FunctionId,
2268 pub(in crate::value) view: R,
2269 marker: PhantomData<&'ctx &'str ()>,
2270}
2271
2272impl<'str, 'ctx, R> FunctionRef<'str, 'ctx, R> {
2273 pub fn new(view: R, id: FunctionId) -> Self {
2274 Self {
2275 id,
2276 view,
2277 marker: PhantomData,
2278 }
2279 }
2280
2281 pub fn id(&self) -> ValueId {
2282 self.id.into()
2283 }
2284}
2285
2286impl<'str, 'ctx> FunctionRef<'str, 'ctx> {
2287 pub fn from_id(ctx: &'ctx Context<'str>, id: FunctionId) -> Self {
2288 Self::new(ModuleView::new(ctx), id)
2289 }
2290}
2291
2292impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 'ctx, 'str> for FunctionRef<'str, 'ctx> {
2293 fn ctx(&'s self) -> &'ctx Context<'str> {
2294 self.view.context()
2298 }
2299}
2300
2301impl<'str: 'ctx, 'ctx, R> Named for FunctionRef<'str, 'ctx, R>
2302where
2303 R: QCodeView<'ctx, 'str>,
2304{
2305 fn name(&self) -> Option<&str> {
2306 Some(self.view.interface(self.id).name.as_ref())
2307 }
2308}
2309
2310impl<'str: 'ctx, 'ctx, R> Display for FunctionRef<'str, 'ctx, R>
2311where
2312 R: QCodeView<'ctx, 'str>,
2313{
2314 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2315 FunctionRef::fmt(self, f)
2316 }
2317}
2318
2319impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for FunctionRef<'str, 'ctx, R>
2320where
2321 R: QCodeView<'ctx, 'str>,
2322{
2323 fn id(&self) -> ValueId {
2324 self.id()
2325 }
2326
2327 fn size(&self) -> usize {
2328 FunctionRef::size(self)
2329 }
2330}
2331
2332pub struct BlockIter<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
2333 view: R,
2334 inner: std::vec::IntoIter<BlockId>,
2335 marker: PhantomData<&'ctx &'str ()>,
2336}
2337
2338impl<'str: 'ctx, 'ctx, R> Iterator for BlockIter<'str, 'ctx, R>
2339where
2340 R: QCodeView<'ctx, 'str>,
2341{
2342 type Item = BlockRef<'str, 'ctx, R>;
2343
2344 fn next(&mut self) -> Option<Self::Item> {
2345 self.inner.next().map(|id| BlockRef::new(self.view, id))
2346 }
2347}
2348
2349impl<'str: 'ctx, 'ctx, R> IntoIterator for &FunctionRef<'str, 'ctx, R>
2350where
2351 R: QCodeView<'ctx, 'str>,
2352{
2353 type Item = BlockRef<'str, 'ctx, R>;
2354 type IntoIter = BlockIter<'str, 'ctx, R>;
2355
2356 fn into_iter(self) -> Self::IntoIter {
2357 self.iter()
2358 }
2359}
2360
2361pub type FunctionMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, FunctionId>;
2362
2363impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for FunctionMutRef<'str, 'ctx> {
2364 fn ctx(&'s self) -> &'s Context<'str> {
2365 self.ctx
2366 }
2367}
2368
2369impl<'s, 'ctx: 's, 'str: 'ctx> WithCtxMut<'s, 'str> for FunctionMutRef<'str, 'ctx> {
2370 fn ctx_mut(&'s mut self) -> &'s mut Context<'str> {
2371 self.ctx
2372 }
2373}
2374
2375impl Display for FunctionMutRef<'_, '_> {
2376 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2377 self.as_ref().fmt(f)
2378 }
2379}
2380
2381impl<'ctx, 'str> Value<'str, 'ctx> for FunctionMutRef<'str, 'ctx> {
2382 fn id(&self) -> ValueId {
2383 self.id()
2384 }
2385
2386 fn size(&self) -> usize {
2387 self.as_ref().size()
2388 }
2389}
2390
2391impl Named for FunctionMutRef<'_, '_> {
2392 fn name(&self) -> Option<&str> {
2393 Some(self.ctx.interfaces[self.id].name.as_ref())
2394 }
2395}
2396
2397impl<'str, 'ctx> Renameable<'str, 'ctx> for FunctionMutRef<'str, 'ctx> {
2398 fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
2399 let id = self.id();
2400 let old_name = self.ctx.interfaces[self.id].name.as_ref().to_owned();
2401 update_context_name(id, self.ctx, name.clone(), Some(old_name.as_ref()))?;
2402 self.ctx.interfaces[self.id].name = name;
2403 Ok(())
2404 }
2405}
2406
2407impl<'str, 'ctx> FunctionMutRef<'str, 'ctx> {
2408 pub fn as_ref(&self) -> FunctionRef<'str, '_> {
2409 FunctionRef::new(ModuleView::new(self.ctx), self.id)
2410 }
2411
2412 fn inner(&self) -> &FunctionBody<'str> {
2413 self.ctx.function(self.id)
2414 }
2415
2416 fn interface(&self) -> &FunctionInterface<'str> {
2417 &self.ctx.interfaces[self.id]
2418 }
2419
2420 fn address(&self) -> Option<u64> {
2421 self.interface().address
2422 }
2423
2424 pub fn name(&self) -> &str {
2425 self.interface().name.as_ref()
2426 }
2427
2428 pub fn blocks(&self) -> impl Iterator<Item = BlockRef<'str, '_>> {
2429 self.as_ref().blocks().collect::<Vec<_>>().into_iter()
2430 }
2431
2432 pub fn root(&self) -> Option<BlockRef<'str, '_>> {
2433 self.as_ref().root()
2434 }
2435
2436 pub(crate) fn inner_mut(&mut self) -> &mut FunctionBody<'str> {
2437 &mut self.ctx.bodies[self.id]
2438 }
2439
2440 pub(crate) fn interface_mut(&mut self) -> &mut FunctionInterface<'str> {
2443 &mut self.ctx.interfaces[self.id]
2444 }
2445
2446 fn set_address(&mut self, address: u64) -> Result<()> {
2447 let mut addresses = crate::address_index::AddressIndex::analyze(&*self.ctx);
2448 self.set_address_indexed(&mut addresses, address)
2449 }
2450
2451 fn set_address_indexed(
2452 &mut self,
2453 addresses: &mut crate::address_index::AddressIndex,
2454 address: u64,
2455 ) -> Result<()> {
2456 let old_address = self.interface().address;
2457 self.interface_mut().address = Some(address);
2458 if let Err(error) = self
2459 .ctx
2460 .set_address_indexed(addresses, address, self.id.into())
2461 {
2462 self.interface_mut().address = old_address;
2463 return Err(error);
2464 }
2465 Ok(())
2466 }
2467
2468 fn with_address_indexed(
2469 mut self,
2470 addresses: &mut crate::address_index::AddressIndex,
2471 address: u64,
2472 ) -> Result<Self> {
2473 self.set_address_indexed(addresses, address)?;
2474 Ok(self)
2475 }
2476
2477 pub fn set_root(&mut self, id: BlockId) -> Result<()> {
2482 assert_eq!(
2483 id.func, self.id,
2484 "cannot root a function at a block stored in another function arena"
2485 );
2486 self.add_block(id);
2487 self.inner_mut().root = Some(id.localize(self.id));
2488
2489 let block_addr = BasicBlock::from_id(&*self.ctx, id).address();
2490 let self_addr = self.address();
2491
2492 match (self_addr, block_addr) {
2493 (Some(fn_addr), Some(block_addr)) if fn_addr != block_addr => {
2494 return Err(Error::spanless(ErrorTy::FunctionRootAddressMismatch {
2495 fn_addr,
2496 block_addr,
2497 }));
2498 }
2499 (None, Some(addr)) => {
2500 self.set_address(addr)
2501 .expect("This address should be valid");
2502 }
2503 (Some(addr), None) => {
2504 BasicBlock::from_id_mut(self.ctx, id)
2505 .set_address(addr)
2506 .expect("This address should be valid");
2507 }
2508 _ => {}
2509 }
2510 Ok(())
2511 }
2512
2513 pub fn make_root(&mut self) -> BlockRef<'str, '_> {
2514 let func = self.id;
2515 let root = BasicBlock::make(self.ctx, func).id;
2516 self.set_root(root).expect("We just created the block");
2517 BasicBlock::from_id(&*self.ctx, root)
2518 }
2519
2520 pub fn ensure_root(&mut self, id: BlockId) -> Result<()> {
2521 assert_eq!(
2522 id.func, self.id,
2523 "cannot ensure a function root from another function arena"
2524 );
2525 if let Some(root) = self.inner().root {
2526 if root != id.localize(self.id) {
2527 return Err(Error::spanless(ErrorTy::FunctionRootMismatch {
2528 expected: BlockId::new(self.id, root),
2529 actual: id,
2530 }));
2531 }
2532 Ok(())
2533 } else {
2534 self.set_root(id)
2535 }
2536 }
2537
2538 pub fn set_external(&mut self, is_external: bool) {
2539 self.interface_mut().is_external = is_external;
2540 assert!(
2541 self.inner().blocks.is_empty(),
2542 "External functions should not have blocks"
2543 );
2544 }
2545
2546 pub fn set_import_ordinal(&mut self, ordinal: Option<u16>) {
2550 self.interface_mut().import_ordinal = ordinal;
2551 }
2552
2553 pub fn set_kind(&mut self, kind: FunctionKind) {
2554 self.interface_mut().kind = kind;
2555 if kind == FunctionKind::Lambda {
2556 self.set_is_pure(true);
2557 self.set_register_effects(RegisterChannelState::Materialized(
2558 RegisterInterfaceMap::default(),
2559 ));
2560 }
2561 }
2562
2563 pub fn set_signature(&mut self, sig: FunctionSignature) {
2564 self.ctx.interfaces[self.id].signature = Some(sig);
2565 }
2566
2567 pub fn set_param_attrs(&mut self, attrs: Vec<ParamAttrs>) {
2570 self.interface_mut()
2571 .signature
2572 .get_or_insert_default()
2573 .param_attrs = Some(attrs);
2574 }
2575
2576 pub fn clear_param_attrs(&mut self) {
2579 if let Some(sig) = self.interface_mut().signature.as_mut() {
2580 sig.param_attrs = None;
2581 }
2582 }
2583
2584 pub fn set_written_spaces(&mut self, spaces: Option<Vec<crate::space::SpaceId>>) {
2590 let coarse = match spaces {
2591 Some(spaces) => WrittenSpacesState::Bounded(spaces),
2592 None => WrittenSpacesState::Unbounded,
2593 };
2594 let precise = self.interface_mut().effects.memory.precise.take();
2597 self.set_memory_solved(coarse, precise);
2598 }
2599
2600 pub fn set_extern_interface(&mut self, iface: crate::value::ExternInterface) {
2604 self.interface_mut()
2605 .signature
2606 .get_or_insert_default()
2607 .extern_interface = Some(iface);
2608 }
2609
2610 pub fn set_argmem(&mut self, argmem: crate::value::ExternArgmem) {
2614 self.interface_mut()
2615 .signature
2616 .get_or_insert_default()
2617 .argmem = Some(argmem);
2618 }
2619
2620 pub fn set_register_effects(&mut self, register: RegisterChannelState) {
2623 self.interface_mut().effects.register = register;
2624 }
2625
2626 pub fn set_memory_effects(&mut self, memory: MemoryChannelState) {
2637 self.interface_mut().effects.memory = memory;
2638 }
2639
2640 pub fn set_memory_solved(&mut self, coarse: WrittenSpacesState, precise: Option<Footprint>) {
2646 let memory = &mut self.interface_mut().effects.memory;
2647 memory.coarse = coarse;
2648 memory.precise = precise;
2649 }
2650
2651 pub fn set_memory_interface(&mut self, materialized: Option<MemoryInterfaceMap>) {
2654 self.interface_mut().effects.memory.materialized = materialized;
2655 }
2656
2657 pub fn set_is_pure(&mut self, value: bool) {
2661 self.interface_mut()
2662 .signature
2663 .get_or_insert_default()
2664 .is_pure = value;
2665 }
2666
2667 pub fn set_reads_unbounded_stack(&mut self, value: bool) {
2670 self.interface_mut()
2671 .signature
2672 .get_or_insert_default()
2673 .reads_unbounded_stack = value;
2674 }
2675
2676 pub fn set_frame_escapes_to_unbounded(&mut self, value: bool) {
2680 self.interface_mut()
2681 .signature
2682 .get_or_insert_default()
2683 .frame_escapes_to_unbounded = value;
2684 }
2685
2686 pub fn add_instruction_addr(&mut self, addr: u64) {
2688 self.inner_mut().instruction_addrs.insert(addr);
2689 }
2690
2691 pub fn add_block(&mut self, id: BlockId) {
2699 assert_eq!(
2700 id.func, self.id,
2701 "cannot add a block stored in another function arena"
2702 );
2703 let local = id.localize(self.id);
2704 if !self.inner().roster.contains(&local) {
2707 self.inner_mut().roster.push(local);
2708 }
2709 }
2710}
2711
2712#[cfg(test)]
2713mod tests {
2714 use wazabin_qcode_macro::qcode;
2715
2716 use super::*;
2717
2718 fn foreign_block_fixture() -> (Context<'static>, FunctionId, BlockId) {
2719 let mut ctx = Context::new();
2720 let owner = FunctionBody::make(&mut ctx, "block_owner".into())
2721 .unwrap()
2722 .id;
2723 let destination = FunctionBody::make(&mut ctx, "block_destination".into())
2724 .unwrap()
2725 .id;
2726 let block = BasicBlock::make(&mut ctx, owner).id;
2727 (ctx, destination, block)
2728 }
2729
2730 #[test]
2731 fn raw_root_and_roster_are_local_while_refs_qualify_per_function() {
2732 let mut ctx = Context::new();
2733 let a = FunctionBody::make(&mut ctx, "local_root_a".into())
2734 .unwrap()
2735 .id;
2736 let b = FunctionBody::make(&mut ctx, "local_root_b".into())
2737 .unwrap()
2738 .id;
2739 let a_root = BasicBlock::make(&mut ctx, a).id;
2740 let b_root = BasicBlock::make(&mut ctx, b).id;
2741 assert_eq!(a_root.local, b_root.local, "arena-local ids should collide");
2742 FunctionBody::from_id_mut(&mut ctx, a)
2743 .set_root(a_root)
2744 .unwrap();
2745 FunctionBody::from_id_mut(&mut ctx, b)
2746 .set_root(b_root)
2747 .unwrap();
2748
2749 assert_eq!(ctx.bodies[a].root_id(), Some(a_root.local));
2750 assert_eq!(ctx.bodies[b].root_id(), Some(b_root.local));
2751 assert_eq!(ctx.bodies[a].roster, vec![a_root.local]);
2752 assert_eq!(ctx.bodies[b].roster, vec![b_root.local]);
2753 assert_eq!(
2754 FunctionBody::from_id(&ctx, a).root().map(|root| root.id),
2755 Some(a_root)
2756 );
2757 assert_eq!(
2758 FunctionBody::from_id(&ctx, b).root().map(|root| root.id),
2759 Some(b_root)
2760 );
2761 }
2762
2763 #[test]
2764 #[should_panic(expected = "cannot add a block stored in another function arena")]
2765 fn add_block_rejects_foreign_storage() {
2766 let (mut ctx, destination, block) = foreign_block_fixture();
2767 FunctionBody::from_id_mut(&mut ctx, destination).add_block(block);
2768 }
2769
2770 #[test]
2771 #[should_panic(expected = "cannot root a function at a block stored in another function arena")]
2772 fn set_root_rejects_foreign_storage() {
2773 let (mut ctx, destination, block) = foreign_block_fixture();
2774 FunctionBody::from_id_mut(&mut ctx, destination)
2775 .set_root(block)
2776 .unwrap();
2777 }
2778
2779 #[test]
2780 #[should_panic(expected = "cannot ensure a function root from another function arena")]
2781 fn ensure_root_rejects_foreign_storage() {
2782 let (mut ctx, destination, block) = foreign_block_fixture();
2783 FunctionBody::from_id_mut(&mut ctx, destination)
2784 .ensure_root(block)
2785 .unwrap();
2786 }
2787
2788 #[test]
2789 fn function_ref_users_of_rejects_foreign_owned_values() {
2790 let mut ctx = Context::new();
2791 qcode!(
2792 ctx,
2793 "
2794 fn users_a:
2795 <a_entry>
2796 %a_def = i64 1 + i64 2;
2797 %a_user = %a_def + i64 3;
2798 return at %a_user;
2799
2800 fn users_b:
2801 <b_entry>
2802 %b_def = i64 1 + i64 2;
2803 %b_user = %b_def + i64 3;
2804 return at %b_user;
2805 "
2806 );
2807
2808 let a_ids = FunctionRef::from_id(&ctx, users_a)
2809 .root()
2810 .unwrap()
2811 .instruction_ids();
2812 let a_def = ValueId::Instruction(a_ids[0]);
2813 assert_eq!(
2814 FunctionRef::from_id(&ctx, users_a).users_of(a_def),
2815 vec![a_ids[1]]
2816 );
2817 assert!(
2818 FunctionRef::from_id(&ctx, users_b)
2819 .users_of(a_def)
2820 .is_empty()
2821 );
2822
2823 let one = ctx.get_const(1, 8).id();
2824 assert!(!FunctionRef::from_id(&ctx, users_b).users_of(one).is_empty());
2825 }
2826
2827 fn colliding_body_ids() -> (
2828 Context<'static>,
2829 FunctionId,
2830 FunctionId,
2831 BlockId,
2832 BlockId,
2833 InstructionId,
2834 InstructionId,
2835 BlockParamId,
2836 BlockParamId,
2837 ) {
2838 let mut ctx = Context::new();
2839 qcode!(
2840 ctx,
2841 "
2842 fn raw_a:
2843 <a_entry @a:i64>
2844 %a_def = i64 1 + i64 2;
2845 return at %a_def;
2846 fn raw_b:
2847 <b_entry @b:i64>
2848 %b_def = i64 1 + i64 2;
2849 return at %b_def;
2850 "
2851 );
2852 let a_root = FunctionRef::from_id(&ctx, raw_a).root().unwrap();
2853 let b_root = FunctionRef::from_id(&ctx, raw_b).root().unwrap();
2854 let a_block = a_root.id;
2855 let b_block = b_root.id;
2856 let a_insn = a_root.instruction_ids()[0];
2857 let b_insn = b_root.instruction_ids()[0];
2858 let a_param = a_root.params().next().unwrap().id;
2859 let b_param = b_root.params().next().unwrap().id;
2860 assert_eq!(a_block.local, b_block.local);
2861 assert_eq!(a_insn.local, b_insn.local);
2862 assert_eq!(a_param.local, b_param.local);
2863 (
2864 ctx, raw_a, raw_b, a_block, b_block, a_insn, b_insn, a_param, b_param,
2865 )
2866 }
2867
2868 #[test]
2872 fn replace_instruction_with_itself_is_a_noop() {
2873 let mut ctx = Context::new();
2874 qcode!(
2875 ctx,
2876 "
2877 fn f:
2878 <entry @a:i32>
2879 %x = @a + 1;
2880 %y = %x + 2;
2881 return %y;
2882 "
2883 );
2884 let root = FunctionBody::from_id(&ctx, f).root().unwrap().id;
2886 let insns: Vec<InstructionId> = BasicBlock::from_id(&ctx, root)
2887 .instruction_ids()
2888 .into_iter()
2889 .collect();
2890 let x = insns[0];
2891 let users_before = ctx.bodies[f].users_of(ValueId::Instruction(x));
2892 assert!(!users_before.is_empty(), "x should have a user (%y)");
2893
2894 ctx.bodies[f].replace_instruction(x, ValueId::Instruction(x));
2896
2897 assert!(
2898 ctx.bodies[f].insns.contains(x.local),
2899 "x must survive a self-replacement"
2900 );
2901 assert_eq!(
2902 ctx.bodies[f].users_of(ValueId::Instruction(x)),
2903 users_before,
2904 "x's users must be unchanged"
2905 );
2906 }
2907
2908 #[test]
2909 fn body_users_of_rejects_foreign_owned_values() {
2910 let (ctx, a, b, _, _, a_insn, _, _, _) = colliding_body_ids();
2911 assert!(
2912 ctx.bodies[b]
2913 .users_of(ValueId::Instruction(a_insn))
2914 .is_empty()
2915 );
2916 assert!(
2917 !ctx.bodies[a]
2918 .users_of(ValueId::Instruction(a_insn))
2919 .is_empty()
2920 );
2921 }
2922
2923 #[test]
2924 #[should_panic(expected = "cannot replace uses of a value owned by another function")]
2925 fn body_replace_uses_rejects_foreign_old() {
2926 let (mut ctx, _, b, _, _, a_insn, b_insn, _, _) = colliding_body_ids();
2927 ctx.bodies[b]
2928 .replace_all_uses_with(ValueId::Instruction(a_insn), ValueId::Instruction(b_insn));
2929 }
2930
2931 #[test]
2932 #[should_panic(expected = "cannot replace uses with a value owned by another function")]
2933 fn body_replace_uses_rejects_foreign_new() {
2934 let (mut ctx, _, b, _, _, a_insn, b_insn, _, _) = colliding_body_ids();
2935 ctx.bodies[b]
2936 .replace_all_uses_with(ValueId::Instruction(b_insn), ValueId::Instruction(a_insn));
2937 }
2938
2939 #[test]
2940 #[should_panic(expected = "block belongs to another function")]
2941 fn body_block_access_rejects_colliding_foreign_id() {
2942 let (ctx, _, b, a_block, _, _, _, _, _) = colliding_body_ids();
2943 let _ = ctx.bodies[b].block(a_block);
2944 }
2945
2946 #[test]
2947 #[should_panic(expected = "instruction belongs to another function")]
2948 fn body_insn_access_rejects_colliding_foreign_id() {
2949 let (ctx, _, b, _, _, a_insn, _, _, _) = colliding_body_ids();
2950 let _ = ctx.bodies[b].insn(a_insn);
2951 }
2952
2953 #[test]
2954 #[should_panic(expected = "block parameter belongs to another function")]
2955 fn body_param_access_rejects_colliding_foreign_id() {
2956 let (ctx, _, b, _, _, _, _, a_param, _) = colliding_body_ids();
2957 let _ = ctx.bodies[b].block_param(a_param);
2958 }
2959
2960 #[test]
2965 fn make_function_creates_function_with_correct_name_root_address() {
2966 let mut ctx = Context::new();
2967 let f = FunctionBody::make(&mut ctx, "main".into()).unwrap();
2968 assert_eq!(f.name(), "main");
2969 }
2970
2971 #[test]
2972 fn get_function_by_name_returns_correct_function() {
2973 let mut ctx = Context::new();
2974 let id = FunctionBody::make(&mut ctx, "foo".into()).unwrap().id();
2975 let f = FunctionBody::from_name(&ctx, "foo").unwrap();
2976 assert_eq!(f.id(), id);
2977 assert_eq!(f.name(), "foo");
2978 }
2979
2980 #[test]
2981 fn get_function_by_name_returns_none_if_not_found() {
2982 let ctx = Context::new();
2983 assert!(FunctionBody::from_name(&ctx, "nonexistent").is_none());
2984 }
2985
2986 #[test]
2987 fn get_function_by_addr_returns_correct_function() {
2988 let mut ctx = Context::new();
2989 let id = FunctionBody::make_at_addr(&mut ctx, 0x2000, None).id();
2990 let addresses = crate::address_index::AddressIndex::analyze(&ctx);
2991 let f = FunctionBody::from_id(&ctx, addresses.function_at(0x2000).unwrap());
2992 assert_eq!(f.id(), id);
2993 assert_eq!(f.address(), Some(0x2000));
2994 assert_eq!(f.name(), "fn_2000");
2995 }
2996
2997 #[test]
2998 fn get_function_by_addr_returns_none_if_missing() {
2999 let ctx = Context::new();
3000 let addresses = crate::address_index::AddressIndex::analyze(&ctx);
3001 assert!(addresses.function_at(0xdeadbeef).is_none());
3002 }
3003
3004 #[test]
3005 fn add_block_via_function_mut_ref_updates_blocks_list() {
3006 let mut ctx = Context::new();
3007 let baz_id = FunctionBody::make(&mut ctx, "baz".into()).unwrap().id;
3008 let root = BasicBlock::make(&mut ctx, baz_id).id;
3009 let extra = BasicBlock::make(&mut ctx, baz_id).id;
3010
3011 let mut baz = FunctionBody::from_id_mut(&mut ctx, baz_id);
3012 baz.add_block(root);
3013 baz.add_block(extra);
3014
3015 let block_ids: Vec<_> = baz.blocks().map(|b| b.id).collect();
3016 assert!(block_ids.contains(&root));
3017 assert!(block_ids.contains(&extra));
3018 }
3019
3020 #[test]
3021 fn display_shows_function_name_and_block_contents() {
3022 let mut ctx = Context::new();
3023 FunctionBody::make(&mut ctx, "display_test".into()).unwrap();
3024
3025 let f = FunctionBody::from_name(&ctx, "display_test").unwrap();
3026
3027 let s = f.to_string();
3028 assert!(s.contains("fn display_test:"));
3029 }
3030
3031 #[test]
3032 fn iter_yields_all_blocks() {
3033 let mut ctx = Context::new();
3034 let f_id = FunctionBody::make(&mut ctx, "iter_fn".into()).unwrap().id;
3035 let root = BasicBlock::make(&mut ctx, f_id).id;
3036 let extra = BasicBlock::make(&mut ctx, f_id).id;
3037 let mut f = FunctionBody::from_id_mut(&mut ctx, f_id);
3038 f.add_block(root);
3039 f.add_block(extra);
3040
3041 let f = FunctionBody::from_name(&ctx, "iter_fn").unwrap();
3042 let ids: Vec<_> = f.iter().map(|b| b.id).collect();
3043 assert!(ids.contains(&root));
3044 assert!(ids.contains(&extra));
3045 }
3046
3047 #[test]
3048 fn into_iterator_for_function_ref_matches_iter() {
3049 let mut ctx = Context::new();
3050 let f_id = FunctionBody::make(&mut ctx, "into_iter_fn".into())
3051 .unwrap()
3052 .id;
3053 let b1 = BasicBlock::make(&mut ctx, f_id).id;
3054 let b2 = BasicBlock::make(&mut ctx, f_id).id;
3055 let mut f = FunctionBody::from_id_mut(&mut ctx, f_id);
3056 f.add_block(b1);
3057 f.add_block(b2);
3058
3059 let f = FunctionBody::from_name(&ctx, "into_iter_fn").unwrap();
3060 let mut via_iter: Vec<usize> = f.iter().map(|b| usize::from(b.id.local)).collect();
3061 let mut via_into: Vec<usize> = (&f).into_iter().map(|b| usize::from(b.id.local)).collect();
3062 via_iter.sort();
3063 via_into.sort();
3064 assert_eq!(via_iter, via_into);
3065 }
3066
3067 #[test]
3068 fn qcode_fn_single_block_populates_function() {
3069 let mut ctx = Context::new();
3070 qcode!(
3071 ctx,
3072 "
3073 fn simple:
3074 <entry>
3075 return at 0;
3076 "
3077 );
3078
3079 let f = FunctionBody::from_name(&ctx, "simple").unwrap();
3080 assert_eq!(f.name(), "simple");
3081 assert!(f.root().is_some());
3082 assert_eq!(f.root().unwrap().name().unwrap(), "entry");
3083 assert_eq!(f.blocks().count(), 1);
3084 }
3085
3086 #[test]
3087 fn qcode_fn_multi_block_populates_all_blocks() {
3088 let mut ctx = Context::new();
3089 qcode!(
3090 ctx,
3091 "
3092 fn multiblock:
3093 <bb1>
3094 if i8 1 goto <bb2> else goto <bb3>;
3095
3096 <bb2>
3097 goto <bb3>;
3098
3099 <bb3>
3100 return at 0;
3101 "
3102 );
3103
3104 let f = FunctionBody::from_name(&ctx, "multiblock").unwrap();
3105 assert_eq!(f.root().unwrap().name().unwrap(), "bb1");
3106 let block_names: Vec<_> = f.blocks().filter_map(|b| b.name()).collect();
3107 assert!(block_names.contains(&"bb1"), "missing bb1");
3108 assert!(block_names.contains(&"bb2"), "missing bb2");
3109 assert!(block_names.contains(&"bb3"), "missing bb3");
3110 assert_eq!(f.blocks().count(), 3);
3111 }
3112
3113 #[test]
3114 fn qcode_fn_id_variable_is_set() {
3115 let mut ctx = Context::new();
3116 qcode!(
3117 ctx,
3118 "
3119 fn myfn:
3120 <start>
3121 return at 0;
3122 "
3123 );
3124
3125 let by_name = FunctionBody::from_name(&ctx, "myfn").unwrap();
3126 assert_eq!(by_name.name(), "myfn");
3127 }
3128
3129 #[test]
3132 fn indexed_address_registration_keeps_foreign_block_rootless() {
3133 let mut ctx = Context::new();
3134
3135 let block_id = {
3138 let __f = ctx.anon_function();
3139 BasicBlock::make(&mut ctx, __f)
3140 }
3141 .id;
3142 let mut addresses = crate::address_index::AddressIndex::analyze(&ctx);
3143 addresses
3144 .register(
3145 &mut ctx,
3146 0x1000,
3147 crate::address_index::AddressTarget::Block(block_id),
3148 )
3149 .unwrap();
3150
3151 let fn_id = FunctionBody::make(&mut ctx, "fn_1000".into()).unwrap().id;
3152 addresses
3153 .register(
3154 &mut ctx,
3155 0x1000,
3156 crate::address_index::AddressTarget::Function(fn_id),
3157 )
3158 .unwrap();
3159
3160 assert_eq!(addresses.function_at(0x1000), Some(fn_id));
3161 assert_eq!(addresses.block_at(0x1000), None);
3162 assert!(FunctionBody::from_id(&ctx, fn_id).root().is_none());
3163 assert_ne!(block_id.func, fn_id);
3164 }
3165}
3166
3167#[cfg(test)]
3168mod memory_interface_tests {
3169 use super::*;
3170
3171 fn slot() -> InterfaceSlot {
3172 InterfaceSlot {
3173 base: SlotBase::Arg(0),
3174 offset: 8,
3175 size: 8,
3176 }
3177 }
3178
3179 #[test]
3186 fn memory_interface_round_trips_through_the_wire_format() {
3187 let state = MemoryChannelState {
3188 materialized: Some(MemoryInterfaceMap {
3189 inputs: vec![slot()],
3190 outputs: vec![InterfaceSlot {
3191 base: SlotBase::Global(0x2000),
3192 offset: 0,
3193 size: 4,
3194 }],
3195 }),
3196 ..MemoryChannelState::default()
3197 };
3198 let config = bincode::config::standard();
3199 let bytes = bincode::serde::encode_to_vec(&state, config).expect("encode memory state");
3200 let (decoded, _): (MemoryChannelState, _) =
3201 bincode::serde::decode_from_slice(&bytes, config).expect("decode memory state");
3202 assert_eq!(decoded, state);
3203 }
3204
3205 #[test]
3207 fn default_memory_state_is_not_materialized() {
3208 assert_eq!(MemoryChannelState::default().materialized(), None);
3209 }
3210
3211 #[test]
3214 fn stamping_written_spaces_preserves_the_materialized_interface() {
3215 let mut ctx = Context::new();
3216 let fid = FunctionBody::make(&mut ctx, "keeps_interface".into())
3217 .unwrap()
3218 .id;
3219 let map = MemoryInterfaceMap {
3220 inputs: vec![slot()],
3221 outputs: vec![],
3222 };
3223 let mut body = FunctionBody::from_id_mut(&mut ctx, fid);
3224 body.set_memory_effects(MemoryChannelState {
3225 materialized: Some(map.clone()),
3226 ..MemoryChannelState::default()
3227 });
3228 body.set_written_spaces(None);
3229
3230 let effects = FunctionBody::from_id(&ctx, fid).effects().memory.clone();
3231 assert_eq!(effects.materialized(), Some(&map));
3232 assert_eq!(effects.coarse, WrittenSpacesState::Unbounded);
3233 }
3234
3235 #[test]
3239 fn an_unmappable_base_is_distinct_from_a_global_and_is_not_bindable() {
3240 let unmappable = InterfaceSlot {
3241 base: SlotBase::Unmappable,
3242 offset: 0,
3243 size: 8,
3244 };
3245 let global = InterfaceSlot {
3246 base: SlotBase::Global(0),
3247 offset: 0,
3248 size: 8,
3249 };
3250 assert_ne!(unmappable, global);
3251 assert!(!unmappable.is_bindable());
3252 assert!(global.is_bindable());
3253 assert!(
3254 InterfaceSlot {
3255 base: SlotBase::Arg(0),
3256 offset: -8,
3257 size: 8,
3258 }
3259 .is_bindable()
3260 );
3261 }
3262}