1use jstd::registry::Registry;
20use rustc_hash::{FxHashMap, FxHashSet};
21
22use std::borrow::Cow;
23
24use crate::{
25 context::{Context, Shared},
26 error::Result,
27 value::{
28 BasicBlock, BodyView, FunctionBody, FunctionId, Instruction, ModuleView, QCodeView,
29 ValueId,
30 block::{BlockId, EdgeId},
31 block_param::BlockParam,
32 block_param::BlockParamId,
33 function::FunctionInterface,
34 insn::{InstructionId, LocalInsnId, Mnemonic},
35 util::body_mut::BodyMut,
36 },
37};
38
39pub trait QCodeMut<'str> {
46 type View<'v>: QCodeView<'v, 'str>
48 where
49 Self: 'v,
50 'str: 'v;
51
52 fn function_mut(&mut self, id: FunctionId) -> &mut FunctionBody<'str>;
55
56 fn body(&self, id: FunctionId) -> &FunctionBody<'str>;
61
62 fn shr(&self) -> &Shared<'str>;
64
65 fn interfaces(&self) -> &Registry<FunctionId, FunctionInterface<'str>>;
67
68 fn view(&self) -> Self::View<'_>;
70
71 fn instruction_mut(&mut self, id: InstructionId) -> &mut Instruction<'str> {
75 &mut self.function_mut(id.func).insns[id.local]
76 }
77
78 fn block_mut(&mut self, id: BlockId) -> &mut BasicBlock<'str> {
80 &mut self.function_mut(id.func).blocks[id.local]
81 }
82
83 fn block_param_mut(&mut self, id: BlockParamId) -> &mut BlockParam<'str> {
85 &mut self.function_mut(id.func).params[id.local]
86 }
87
88 fn register_body_name(
96 &mut self,
97 id: ValueId,
98 name: Cow<'str, str>,
99 old_name: Option<&str>,
100 ) -> Result<()> {
101 let func = id
102 .name_scope_function()
103 .expect("register_body_name on a global-scoped value");
104 self.function_mut(func)
105 .register_body_name(id, name, old_name)
106 }
107
108 fn remove_block_param(&mut self, id: BlockParamId) {
112 self.function_mut(id.func).remove_block_param(id);
113 }
114
115 fn insert_insn_before(&mut self, block: BlockId, before: InstructionId, insn: InstructionId) {
117 self.function_mut(block.func)
118 .insert_insn_before(block, before, insn);
119 }
120
121 fn move_insn_before(&mut self, insn: InstructionId, before: InstructionId) {
126 self.function_mut(insn.func).move_insn_before(insn, before);
127 }
128
129 fn add_cfg_edge(&mut self, from: BlockId, to: BlockId) -> EdgeId {
139 debug_assert_eq!(
140 from.func, to.func,
141 "cross-function CFG edge {from:?} -> {to:?} (strict IR locality, ruling 2)"
142 );
143 self.function_mut(from.func).add_cfg_edge(from, to)
144 }
145
146 fn replace_all_uses_with(&mut self, old: impl Into<ValueId>, new: impl Into<ValueId>) {
149 let old = old.into();
150 let new = new.into();
151 if old == new {
152 return;
153 }
154 let Some(func) = old.owning_function() else {
157 return;
158 };
159 self.function_mut(func).replace_all_uses_with(old, new);
160 }
161
162 fn remove_instruction(&mut self, id: InstructionId) {
166 self.function_mut(id.func).remove_instruction(id);
167 }
168
169 fn replace_instruction(&mut self, id: InstructionId, new: impl Into<ValueId>) {
174 self.function_mut(id.func)
175 .replace_instruction(id, new.into());
176 }
177
178 fn remove_instructions(&mut self, dead: &FxHashSet<InstructionId>) {
183 let mut by_func: FxHashMap<FunctionId, FxHashSet<LocalInsnId>> = FxHashMap::default();
184 for &id in dead {
185 by_func.entry(id.func).or_default().insert(id.local);
186 }
187 for (func, dead) in by_func {
188 self.function_mut(func).remove_instructions(&dead);
189 }
190 }
191
192 fn rehome_outgoing_edges(&mut self, keep: BlockId, remove: BlockId) {
195 self.function_mut(keep.func)
196 .rehome_outgoing_edges(keep, remove);
197 }
198
199 fn replace_instruction_mnemonic(&mut self, id: InstructionId, mnemonic: Mnemonic) {
203 self.function_mut(id.func)
204 .replace_instruction_mnemonic(id, mnemonic);
205 }
206
207 fn unroster_block(&mut self, block: BlockId) {
210 self.function_mut(block.func).unroster_block(block);
211 }
212
213 fn delete_block(&mut self, block: BlockId) {
217 self.function_mut(block.func).delete_block(block);
218 }
219
220 fn absorb_block(&mut self, keep: BlockId, other: BlockId, edge_ab: EdgeId) {
224 self.function_mut(keep.func)
225 .absorb_block(keep, other, edge_ab);
226 }
227}
228
229impl<'str, H: QCodeMut<'str>> QCodeMut<'str> for &mut H {
233 type View<'v>
234 = H::View<'v>
235 where
236 Self: 'v,
237 'str: 'v;
238
239 fn function_mut(&mut self, id: FunctionId) -> &mut FunctionBody<'str> {
240 (**self).function_mut(id)
241 }
242
243 fn body(&self, id: FunctionId) -> &FunctionBody<'str> {
244 (**self).body(id)
245 }
246
247 fn shr(&self) -> &Shared<'str> {
248 (**self).shr()
249 }
250
251 fn interfaces(&self) -> &Registry<FunctionId, FunctionInterface<'str>> {
252 (**self).interfaces()
253 }
254
255 fn view(&self) -> Self::View<'_> {
256 (**self).view()
257 }
258}
259
260impl<'str> QCodeMut<'str> for Context<'str> {
261 type View<'v>
262 = ModuleView<'v, 'str>
263 where
264 Self: 'v,
265 'str: 'v;
266
267 fn function_mut(&mut self, id: FunctionId) -> &mut FunctionBody<'str> {
268 &mut self.bodies[id]
269 }
270
271 fn body(&self, id: FunctionId) -> &FunctionBody<'str> {
272 &self.bodies[id]
273 }
274
275 fn shr(&self) -> &Shared<'str> {
276 &self.shared
277 }
278
279 fn interfaces(&self) -> &Registry<FunctionId, FunctionInterface<'str>> {
280 &self.interfaces
281 }
282
283 fn view(&self) -> ModuleView<'_, 'str> {
284 ModuleView::new(self)
285 }
286}
287
288impl<'a, 'str> QCodeMut<'str> for BodyMut<'a, 'str> {
289 type View<'v>
290 = BodyView<'v, 'str>
291 where
292 Self: 'v,
293 'str: 'v;
294
295 fn function_mut(&mut self, id: FunctionId) -> &mut FunctionBody<'str> {
296 BodyMut::function_mut(self, id)
297 }
298
299 fn body(&self, id: FunctionId) -> &FunctionBody<'str> {
300 BodyMut::function(self, id)
301 }
302
303 fn shr(&self) -> &Shared<'str> {
304 self.shared
305 }
306
307 fn interfaces(&self) -> &Registry<FunctionId, FunctionInterface<'str>> {
308 self.interfaces
309 }
310
311 fn view(&self) -> BodyView<'_, 'str> {
312 BodyMut::view(self)
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319 use crate::value::{BasicBlock, insn::InstructionId};
320
321 fn absorb_forwarding_pair<'str>(host: &mut impl QCodeMut<'str>, func: FunctionId) {
323 let (keep, other, edge) = {
324 let view = host.view();
325 let ids = view.function_ref(func).block_ids();
326 let [keep, other] = ids[..] else {
327 panic!("expected exactly two rostered blocks");
328 };
329 let edge = *view.block(keep).edges.iter().next().expect("edge");
330 (keep, other, edge)
331 };
332 host.absorb_block(keep, other, edge);
333 }
334
335 fn forwarding_pair(ctx: &mut Context<'_>) -> (FunctionId, InstructionId) {
336 let func = FunctionBody::make(ctx, "f".into()).unwrap().id;
337 let keep = BasicBlock::make(ctx, func).id;
338 let other = BasicBlock::make(ctx, func).id;
339 ctx.bodies[func].set_root_id(Some(keep.local));
340 let value = ctx.get_const(7, 8).id();
341 let ret = ctx.builder(other).push_return(value).id;
342 ctx.builder(keep).push_branch(other);
343 (func, ret)
344 }
345
346 #[test]
347 fn module_host_runs_generic_transform() {
348 let mut ctx = Context::new();
349 let (func, ret) = forwarding_pair(&mut ctx);
350 absorb_forwarding_pair(&mut ctx, func);
351 assert_eq!(ctx.view().function_ref(func).block_ids().len(), 1);
352 assert!(ctx.contains_instruction(ret));
353 }
354
355 #[test]
356 fn checked_out_host_runs_generic_transform() {
357 let mut ctx = Context::new();
358 let (func, ret) = forwarding_pair(&mut ctx);
359 {
360 let mut host = BodyMut::new(&mut ctx.bodies[func], &ctx.shared, &ctx.interfaces);
361 absorb_forwarding_pair(&mut host, func);
362 assert_eq!(
363 QCodeMut::view(&host).function_ref(func).block_ids().len(),
364 1
365 );
366 }
367 assert!(ctx.contains_instruction(ret));
368 }
369
370 #[test]
371 fn shared_value_rauw_is_a_noop() {
372 let mut ctx = Context::new();
373 let (_, ret) = forwarding_pair(&mut ctx);
374 let lit = ctx.get_const(7, 8).id();
375 let other = ctx.get_const(9, 8).id();
376 QCodeMut::replace_all_uses_with(&mut ctx, lit, other);
377 assert!(ctx.contains_instruction(ret));
378 }
379}