qcode/value/util/body_mut.rs
1//! The exclusive mutation backing for a function pass ([`BodyMut`]).
2//!
3//! [`BodyView`] gives the read layer a `Copy` static provider that
4//! routes arena reads to the pass's own function. [`BodyMut`] is its mutable
5//! sibling: a single function's arenas borrowed `&mut` in place from
6//! `Context.bodies[id]` for exclusive mutation by one worker (the driver's
7//! `Context::split` hands out disjoint body borrows).
8//!
9//! All *shared* data (types, varnodes, spaces, registers, name map) stays behind
10//! the `&Shared` view, reachable read-only. The inherent verb + read methods below
11//! route each write to the borrowed function's arena; a function pass must not mutate another
12//! function (asserted). The module-scope twin of every verb is an inherent method
13//! on [`Context`]; the shorter-lived reborrow needed to
14//! hand the host to a value that owns it by value (a [`Builder`](crate::builder::Builder)
15//! or a mutation `BaseRef`) is [`BodyMut::reborrow`].
16
17use crate::{
18 context::Context,
19 error::{Error, ErrorTy, Result},
20 value::{
21 BlockParamRef, BlockRef, BodyView, FunctionBody, FunctionId, FunctionRef, InstructionRef,
22 QCodeView, ValueId,
23 block::{BasicBlock, BlockId, EdgeData, EdgeId},
24 block_param::{BlockParam, BlockParamId},
25 insn::{Instruction, InstructionId, Mnemonic},
26 },
27};
28
29/// A single function borrowed `&mut` in place from `Context.bodies[id]` for
30/// exclusive mutation (its interface stays in `Context.interfaces[id]`,
31/// reachable read-only through `interfaces`).
32///
33/// Out of scope (and asserted against on construction): a function with
34/// *reattributed* blocks — a roster block stored in, or parented to, a different
35/// function. Those functions go through the sequential (module) path.
36pub struct BodyMut<'a, 'str> {
37 pub fun: &'a mut FunctionBody<'str>,
38 /// The module's shared IR state, **read-only**. A checked-out function pass
39 /// reaches shared data (types, literals, spaces, registers) immutably; it
40 /// mints types/literals through the interners' `&self` paths, and mints no
41 /// varnodes/temp-spaces (only the V1 argpromote pass does, and it runs on
42 /// the module path). Holds **no** `&Context` — bodies are out of reach by
43 /// construction (context-split stage 5b-ii Pin B).
44 pub shared: &'a crate::context::Shared<'str>,
45 /// Every function's published interface (never checked out): the
46 /// caller-reasoning surface a pass may consult about its callees.
47 pub interfaces:
48 &'a jstd::registry::Registry<FunctionId, crate::value::function::FunctionInterface<'str>>,
49}
50
51impl<'a, 'str> BodyMut<'a, 'str> {
52 /// Wrap `fun` over the module's shared state and interface registry. Block
53 /// ownership is now derived from the storing arena (a rostered block lives in
54 /// `fun`'s own arena by construction), so there is no reattribution state left
55 /// to scan for here.
56 pub fn new(
57 fun: &'a mut FunctionBody<'str>,
58 shared: &'a crate::context::Shared<'str>,
59 interfaces: &'a jstd::registry::Registry<
60 FunctionId,
61 crate::value::function::FunctionInterface<'str>,
62 >,
63 ) -> Self {
64 Self {
65 fun,
66 shared,
67 interfaces,
68 }
69 }
70
71 /// Wrap `fun` (checked out under `id`) over a whole module `&Context` — the
72 /// module/test-scope convenience constructor (narrows to the shared state +
73 /// interface registry).
74 pub fn from_ctx(fun: &'a mut FunctionBody<'str>, ctx: &'a Context<'str>) -> Self {
75 Self::new(fun, &ctx.shared, &ctx.interfaces)
76 }
77
78 /// A shorter-lived `BodyMut` reborrowing this one's exclusive references, so
79 /// the host can be handed to a mutation ref (which owns its host by value)
80 /// without consuming the original.
81 pub fn reborrow(&mut self) -> BodyMut<'_, 'str> {
82 BodyMut {
83 fun: &mut *self.fun,
84 shared: self.shared,
85 interfaces: self.interfaces,
86 }
87 }
88
89 /// Narrows this pass backing to the concrete body-local builder.
90 pub fn builder(&mut self, block: BlockId) -> crate::builder::Builder<'str, '_> {
91 crate::builder::Builder::new(&mut *self.fun, self.shared, self.interfaces, block)
92 }
93}
94
95/// The verb + read surface of a checked-out function pass, delegating to the
96/// owned `FunctionBody`'s inherent verbs and `self.shared`. The module-scope twin of
97/// each verb is an inherent method on [`Context`]; the
98/// primitives below (`function{,_mut}`/`shared`/`view`, and the no-op
99/// call-site cache) are the checked-out specializations.
100impl<'a, 'str> BodyMut<'a, 'str> {
101 // ---- primitives ---------------------------------------------------------
102
103 /// The owned function's storage (write). Panics if `f` is not this function.
104 pub fn function_mut(&mut self, f: FunctionId) -> &mut FunctionBody<'str> {
105 assert_eq!(
106 f,
107 self.fun.id(),
108 "a checked-out function pass may not mutate another function"
109 );
110 self.fun
111 }
112 /// The owned function's storage (read).
113 pub fn function(&self, f: FunctionId) -> &FunctionBody<'str> {
114 assert_eq!(
115 f,
116 self.fun.id(),
117 "a checked-out function pass may not read another function's arenas mutably"
118 );
119 self.fun
120 }
121 /// The module's shared IR state ([`Shared`]) (read).
122 ///
123 /// [`Shared`]: crate::context::Shared
124 pub fn shr(&self) -> &crate::context::Shared<'str> {
125 self.shared
126 }
127 /// The static immutable provider for shared reads over this pass body.
128 pub fn view(&self) -> BodyView<'_, 'str> {
129 BodyView::new(&*self.fun, self.shared, self.interfaces)
130 }
131 // ---- function-scoped read wrappers --------------------------------------
132
133 /// A read [`BlockRef`] over `id`, body-routed.
134 pub fn block_ref(&self, id: BlockId) -> BlockRef<'str, '_, BodyView<'_, 'str>> {
135 self.view().block_ref(id)
136 }
137 /// A read [`InstructionRef`] over `id`.
138 pub fn insn_ref(&self, id: InstructionId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
139 self.view().insn_ref(id)
140 }
141 /// A read [`BlockParamRef`] over `id`.
142 pub fn param_ref(&self, id: BlockParamId) -> BlockParamRef<'str, '_, BodyView<'_, 'str>> {
143 self.view().param_ref(id)
144 }
145 /// A read [`FunctionRef`] over `id`.
146 pub fn function_ref(&self, id: FunctionId) -> FunctionRef<'str, '_, BodyView<'_, 'str>> {
147 self.view().function_ref(id)
148 }
149
150 // ---- births -------------------------------------------------------------
151 //
152 // (The derived mut accessors — `instruction_mut`/`block_mut`/
153 // `block_param_mut` — are [`QCodeMut`](crate::value::QCodeMut) defaults.)
154
155 pub fn push_edge(&mut self, edge: EdgeData) -> EdgeId {
156 self.fun.edges.push(edge)
157 }
158
159 pub fn push_insn(&mut self, insn: Instruction<'str>) -> InstructionId {
160 self.fun.push_insn(insn)
161 }
162
163 pub fn push_block(&mut self, block: BasicBlock<'str>) -> BlockId {
164 self.fun.push_block(block)
165 }
166
167 pub fn make_block(&mut self) -> BlockId {
168 self.fun.make_block()
169 }
170
171 pub fn push_block_param(&mut self, param: BlockParam<'str>) -> BlockParamId {
172 self.fun.push_block_param(param)
173 }
174
175 pub fn push_mnemonic(&mut self, mnemonic: Mnemonic, size: usize) -> InstructionId {
176 let shared = self.shared;
177 self.fun.push_mnemonic(shared, mnemonic, size)
178 }
179
180 pub fn push_mnemonic_with_type(
181 &mut self,
182 mnemonic: Mnemonic,
183 type_id: crate::types::TypeId,
184 ) -> InstructionId {
185 self.fun.push_mnemonic_with_type(mnemonic, type_id)
186 }
187
188 // ---- CFG / use-map verbs ------------------------------------------------
189 //
190 // The body-local mutation verbs live on the [`QCodeMut`] trait
191 // (`value::view_mut`), shared with the module host. Only the verbs whose
192 // spelling diverges between hosts stay inherent here.
193
194 /// Remove CFG edge `edge_id` (unqualified; `EdgeId` is body-local). The
195 /// module-path twin is the function-qualified
196 /// [`Context::remove_cfg_edge`](crate::context::Context::remove_cfg_edge).
197 pub fn remove_cfg_edge(&mut self, edge_id: EdgeId) {
198 self.fun.remove_cfg_edge(edge_id)
199 }
200
201 // ---- names --------------------------------------------------------------
202
203 pub fn register_local_name(
204 &mut self,
205 id: ValueId,
206 name: std::borrow::Cow<'str, str>,
207 old_name: Option<&str>,
208 ) -> Result<()> {
209 let existing = match id.name_scope_function() {
210 Some(func) => self
211 .function(func)
212 .names
213 .get(&name)
214 .map(|id| id.qualify(func)),
215 None => self.shr().get_named(&name),
216 };
217 if let Some(existing) = existing {
218 return if existing == id {
219 Ok(())
220 } else {
221 Err(Error::spanless(ErrorTy::DuplicateName(name.to_string())))
222 };
223 }
224 match id.name_scope_function() {
225 Some(func) => self
226 .function_mut(func)
227 .names
228 .register(name, id.localize(func), old_name),
229 None => {
230 unimplemented!("a checked-out host has read-only shared access (mints via &self)")
231 }
232 }
233 }
234}