1use rustc_hash::FxHashMap;
8
9use crate::{
10 context::Context,
11 error::{Error, ErrorTy, Result},
12 value::{BlockId, FunctionBody, FunctionId, ValueId},
13};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum AddressTarget {
18 Function(FunctionId),
19 Block(BlockId),
20}
21
22#[derive(Debug, Clone, Default, PartialEq, Eq)]
24pub struct AddressIndex {
25 targets: FxHashMap<u64, AddressTarget>,
26 boundaries: rustc_hash::FxHashSet<u64>,
34}
35
36impl AddressIndex {
37 pub fn analyze(ctx: &Context<'_>) -> Self {
42 let mut targets = FxHashMap::default();
43
44 for block_id in ctx.block_ids() {
45 let block = ctx.block(block_id);
46 if let Some(address) = block.address {
47 targets
48 .entry(address)
49 .or_insert(AddressTarget::Block(block_id));
50 }
51 for &address in &block.extra_addresses {
52 targets
53 .entry(address)
54 .or_insert(AddressTarget::Block(block_id));
55 }
56 }
57
58 for function in ctx.functions() {
59 if let Some(address) = function.address() {
60 targets.insert(address, AddressTarget::Function(function.id));
61 }
62 }
63
64 Self {
65 targets,
66 boundaries: rustc_hash::FxHashSet::default(),
67 }
68 }
69
70 pub fn refresh(&mut self, ctx: &Context<'_>) {
73 *self = Self::analyze(ctx);
74 }
75
76 pub fn rehome_block(&mut self, addr: u64, old: BlockId, new: BlockId) {
86 if self.targets.get(&addr) == Some(&AddressTarget::Block(old)) {
87 self.targets.insert(addr, AddressTarget::Block(new));
88 }
89 }
90
91 pub fn forget(&mut self, address: u64) {
94 self.targets.remove(&address);
95 }
96
97 pub fn set_block(&mut self, address: u64, block: BlockId) {
103 self.targets.insert(address, AddressTarget::Block(block));
104 }
105
106 pub fn mark_boundary(&mut self, address: u64) {
108 self.boundaries.insert(address);
109 }
110
111 pub fn is_boundary(&self, address: u64) -> bool {
113 self.boundaries.contains(&address)
114 }
115
116 pub fn register(
122 &mut self,
123 ctx: &mut Context<'_>,
124 address: u64,
125 target: AddressTarget,
126 ) -> Result<()> {
127 let Some(existing) = self.targets.get(&address).copied() else {
128 self.targets.insert(address, target);
129 return Ok(());
130 };
131 if existing == target {
132 return Ok(());
133 }
134
135 match (existing, target) {
136 (AddressTarget::Function(function), AddressTarget::Block(block))
137 | (AddressTarget::Block(block), AddressTarget::Function(function)) => {
138 if block.func == function {
142 FunctionBody::from_id_mut(ctx, function).ensure_root(block)?;
143 }
144 self.targets
145 .insert(address, AddressTarget::Function(function));
146 Ok(())
147 }
148 _ => Err(Error::spanless(ErrorTy::DuplicateAddress(
149 address,
150 existing.into(),
151 ))),
152 }
153 }
154
155 pub fn get(&self, address: u64) -> Option<AddressTarget> {
157 self.targets.get(&address).copied()
158 }
159
160 pub fn function_at(&self, address: u64) -> Option<FunctionId> {
162 match self.get(address) {
163 Some(AddressTarget::Function(id)) => Some(id),
164 _ => None,
165 }
166 }
167
168 pub fn block_at(&self, address: u64) -> Option<BlockId> {
170 match self.get(address) {
171 Some(AddressTarget::Block(id)) => Some(id),
172 _ => None,
173 }
174 }
175
176 pub fn len(&self) -> usize {
178 self.targets.len()
179 }
180
181 pub fn is_empty(&self) -> bool {
183 self.targets.is_empty()
184 }
185}
186
187impl From<AddressTarget> for ValueId {
188 fn from(target: AddressTarget) -> Self {
189 match target {
190 AddressTarget::Function(id) => id.into(),
191 AddressTarget::Block(id) => id.into(),
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use crate::value::QCodeMut;
200 use crate::value::{BasicBlock, FunctionBody};
201
202 #[test]
203 fn function_wins_its_entry_block_collision() {
204 let mut ctx = Context::new();
205 let mut index = AddressIndex::analyze(&ctx);
206 let function = FunctionBody::make_at_addr_indexed(&mut ctx, &mut index, 0x1000, None).id;
207 let root = BasicBlock::make(&mut ctx, function)
208 .with_address_indexed(&mut index, 0x1000)
209 .id;
210
211 assert_eq!(index.get(0x1000), Some(AddressTarget::Function(function)));
212 assert_eq!(index.function_at(0x1000), Some(function));
213 assert_eq!(index.block_at(0x1000), None);
214 assert_eq!(ctx.function(function).root_id(), Some(root.local));
215 }
216
217 #[test]
218 fn indexes_primary_and_extra_block_addresses() {
219 let mut ctx = Context::new();
220 let mut index = AddressIndex::analyze(&ctx);
221 let function = ctx.anon_function();
222 let block = BasicBlock::make(&mut ctx, function)
223 .with_address_indexed(&mut index, 0x2000)
224 .id;
225 ctx.block_mut(block)
226 .extra_addresses
227 .extend([0x2001, 0x2002]);
228
229 index.refresh(&ctx);
230 assert_eq!(index.block_at(0x2000), Some(block));
231 assert_eq!(index.block_at(0x2001), Some(block));
232 assert_eq!(index.block_at(0x2002), Some(block));
233 assert_eq!(index.len(), 3);
234 }
235
236 #[test]
237 fn excludes_deleted_body_shape() {
238 let mut ctx = Context::new();
239 let mut index = AddressIndex::analyze(&ctx);
240 let function = ctx.anon_function();
241 let block = BasicBlock::make(&mut ctx, function)
242 .with_address_indexed(&mut index, 0x3000)
243 .id;
244 ctx.delete_block(block);
245
246 index.refresh(&ctx);
247 assert_eq!(index.get(0x3000), None);
248 assert!(index.is_empty());
249 }
250}