Skip to main content

qcode/
value.rs

1//! IR value types and the central [`ValueId`] discriminant.
2//!
3//! Every piece of IR state is a *value* — a constant, an instruction result,
4//! a memory location, a basic block, or a function. Values are stored in a
5//! [`Context`] arena and addressed through cheap, `Copy` ID types. To inspect
6//! a value you convert its ID into a *reference* type (e.g. [`InstructionRef`],
7//! [`BlockRef`]) that borrows the context.
8//!
9//! # Type Map
10//!
11//! | ID type            | Reference type     | What it represents          |
12//! |--------------------|--------------------|-----------------------------|
13//! | [`LiteralId`]      | [`LiteralRef`]     | Integer constant            |
14//! | [`InstructionId`]  | [`InstructionRef`] | SSA value                   |
15//! | [`VarnodeId`]      | [`VarnodeRef`]     | Named memory location       |
16//! | [`BlockId`]        | [`BlockRef`]       | Basic block                 |
17//! | [`FunctionId`]     | [`FunctionRef`]    | Lifted or external function |
18//!
19//! The [`ValueId`] enum unifies all five ID types so that code that works with
20//! arbitrary values (e.g. use-def chains, operand lists) can do so without
21//! generics.
22
23use crate::{context::Context, space::SpaceRef};
24use serde::{Deserialize, Serialize};
25use std::fmt::{Debug, Display, Formatter};
26
27/// Declares a composite IR ID: a `{ func: FunctionId, local: LocalX }` pair.
28///
29/// SSA is intra-function, so every instruction/block/param/edge handle carries
30/// the owning function plus a function-local index. Unlike the `Identifier`
31/// newtypes these do **not** index a global `Registry` — they route through the
32/// owning [`FunctionBody`]'s arena. Derived `Ord` compares `func` then `local`.
33#[macro_export]
34macro_rules! composite_id {
35    ($name:ident, $local:ty) => {
36        #[derive(
37            Clone,
38            Copy,
39            PartialEq,
40            Eq,
41            Hash,
42            PartialOrd,
43            Ord,
44            Default,
45            ::serde::Serialize,
46            ::serde::Deserialize,
47        )]
48        pub struct $name {
49            pub func: $crate::value::FunctionId,
50            pub local: $local,
51        }
52
53        impl $name {
54            pub const fn new(func: $crate::value::FunctionId, local: $local) -> Self {
55                Self { func, local }
56            }
57
58            /// Drop the qualifying function and yield the bare body-local index for
59            /// in-body storage, asserting (debug builds) that the id belongs to
60            /// `func` — the strict-locality tripwire. Use at every storage-flip
61            /// write site where the ambient owning function is known.
62            #[inline]
63            pub fn localize(self, func: $crate::value::FunctionId) -> $local {
64                debug_assert_eq!(
65                    self.func, func,
66                    concat!(stringify!($name), "::localize: foreign id"),
67                );
68                self.local
69            }
70        }
71
72        impl ::core::fmt::Debug for $name {
73            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
74                write!(
75                    f,
76                    concat!(stringify!($name), "({}:{})"),
77                    self.func, self.local
78                )
79            }
80        }
81
82        impl ::core::fmt::Display for $name {
83            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
84                write!(f, "{}:{}", self.func, self.local)
85            }
86        }
87    };
88}
89
90pub use block::cfg::LocalBlockId;
91pub use block::{BasicBlock, BlockId, BlockMutRef, BlockRef};
92pub use block_param::{BlockParam, BlockParamId, BlockParamMutRef, BlockParamRef, LocalParamId};
93pub use bytes::{
94    Bytes, BytesDisplay, BytesId, BytesRef, StringEncoding, decode_string, escape_decoded,
95    render_bytes_literal,
96};
97pub use function::{
98    ArgMemKind, BodyArenaKindStats, BodyArenaStats, DerivedOutput, ExternArg, ExternArgmem,
99    ExternInterface, ExternSlot, Footprint, FunctionBody, FunctionEffects, FunctionId,
100    FunctionKind, FunctionMutRef, FunctionRef, InterfaceSlot, MemoryChannelState,
101    MemoryInterfaceMap, ParamAttrs, RamBase, RamField, RamLocations, RamObject, RamRegion,
102    RegisterChannelState, RegisterEffectSets, RegisterInterfaceMap, SlotBase, WrittenSpaces,
103    WrittenSpacesState,
104};
105pub use insn::LocalInsnId;
106pub use insn::{Instruction, InstructionId, InstructionRef};
107pub use literal::{LiteralId, LiteralRef};
108pub use poison::{Poison, PoisonId, PoisonRef};
109pub use temp::{
110    LocalTempId, LocalTempSpaceId, Temp, TempId, TempRef, TempSpace, TempSpaceId, TempSpaceRef,
111};
112pub use util::named::{Named, Renameable};
113pub use varnode::{Varnode, VarnodeId, VarnodeRef, register::Register, register::RegisterId};
114
115pub mod block;
116pub mod block_param;
117pub mod bytes;
118pub mod function;
119pub mod insn;
120pub mod interner;
121pub mod literal;
122pub mod poison;
123pub mod registry;
124pub mod temp;
125pub mod util;
126pub mod varnode;
127pub mod view;
128pub mod view_mut;
129
130pub use view::{BodyView, ModuleView, QCodeView};
131pub use view_mut::QCodeMut;
132
133/// A type-erased handle to any IR value stored in a [`Context`].
134///
135/// `ValueId` is the "universal pointer" used wherever code must refer to a
136/// value without knowing its concrete type at compile time — for example in
137/// instruction operand lists, use-def chains, and the address/name maps.
138///
139/// It is `Copy`, cheap to compare, and contains no borrow of the context.
140/// To inspect the value, call `Context::get_value` which returns a
141/// [`ValueRef`] tied to the context's lifetime.
142///
143/// # Exhaustiveness
144///
145/// `ValueId` is `#[non_exhaustive]`; new variants may be added in future
146/// versions without a major semver bump.
147#[non_exhaustive]
148#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
149pub enum ValueId {
150    /// A compile-time integer constant, optionally carrying a symbolic label.
151    Literal(LiteralId),
152    /// A compile-time opaque byte blob wider than a [`Literal`](crate::value::literal::Literal) can hold.
153    Bytes(BytesId),
154    /// An SSA value produced by an [`Instruction`].
155    Instruction(InstructionId),
156    /// A control-flow node ([`BasicBlock`]).
157    BasicBlock(BlockId),
158    /// A typed parameter declared at the entry of a basic block.
159    BlockParam(BlockParamId),
160    /// A named memory location ([`Varnode`]) such as a register or global.
161    Varnode(VarnodeId),
162    /// A function-local temporary memory value.
163    Temp(TempId),
164    /// A lifted or external [`FunctionBody`].
165    Function(FunctionId),
166    /// A typed **poison** value: a placeholder with undefined bits (argpromote
167    /// v2 clobber slots). Never folded by GVN; reading it in the emulator is a
168    /// hard error. See [`poison`].
169    Poison(PoisonId),
170}
171
172impl ValueId {
173    pub fn ty(&self) -> &'static str {
174        match self {
175            ValueId::Literal(_) => "Literal",
176            ValueId::Bytes(_) => "Bytes",
177            ValueId::Instruction(_) => "Instruction",
178            ValueId::BasicBlock(_) => "BasicBlock",
179            ValueId::BlockParam(_) => "BlockParam",
180            ValueId::Varnode(_) => "Varnode",
181            ValueId::Temp(_) => "Temp",
182            ValueId::Function(_) => "Function",
183            ValueId::Poison(_) => "Poison",
184        }
185    }
186
187    /// The function that owns this value's definition, if it is an SSA def
188    /// (an [`Instruction`] result or a [`BlockParam`]). Shared values (literals,
189    /// bytes, varnodes) and functions/blocks return `None` — they have no single
190    /// owning function and their per-function use-lists live in each using
191    /// function's reverse-use map (exposed through [`FunctionBody::users_of`]).
192    pub fn owning_function(self) -> Option<FunctionId> {
193        match self {
194            ValueId::Instruction(id) => Some(id.func),
195            ValueId::BlockParam(id) => Some(id.func),
196            ValueId::Temp(id) => Some(id.func),
197            _ => None,
198        }
199    }
200
201    /// The function whose **name table** owns this value's name, if any. Block,
202    /// instruction, and block-param names are function-scoped, so those return
203    /// their function; module-scoped values (functions, varnodes, spaces,
204    /// literals, bytes) return `None` and use the global name map. Unlike
205    /// [`owning_function`](Self::owning_function) this includes blocks (by their
206    /// storage function).
207    pub fn name_scope_function(self) -> Option<FunctionId> {
208        match self {
209            ValueId::Instruction(id) => Some(id.func),
210            ValueId::BlockParam(id) => Some(id.func),
211            ValueId::BasicBlock(id) => Some(id.func),
212            ValueId::Temp(id) => Some(id.func),
213            _ => None,
214        }
215    }
216
217    pub fn as_literal(self) -> Option<LiteralId> {
218        if let ValueId::Literal(id) = self {
219            Some(id)
220        } else {
221            None
222        }
223    }
224
225    pub fn as_instruction(self) -> Option<InstructionId> {
226        if let ValueId::Instruction(id) = self {
227            Some(id)
228        } else {
229            None
230        }
231    }
232
233    pub fn as_block(self) -> Option<BlockId> {
234        if let ValueId::BasicBlock(id) = self {
235            Some(id)
236        } else {
237            None
238        }
239    }
240
241    pub fn as_block_param(self) -> Option<BlockParamId> {
242        if let ValueId::BlockParam(id) = self {
243            Some(id)
244        } else {
245            None
246        }
247    }
248
249    pub fn as_bytes(self) -> Option<BytesId> {
250        if let ValueId::Bytes(id) = self {
251            Some(id)
252        } else {
253            None
254        }
255    }
256
257    pub fn is_varnode(self) -> bool {
258        matches!(self, ValueId::Varnode(_))
259    }
260
261    pub fn as_varnode(self) -> Option<VarnodeId> {
262        if let ValueId::Varnode(id) = self {
263            Some(id)
264        } else {
265            None
266        }
267    }
268
269    pub fn as_temp(self) -> Option<TempId> {
270        if let ValueId::Temp(id) = self {
271            Some(id)
272        } else {
273            None
274        }
275    }
276
277    pub fn as_function(self) -> Option<FunctionId> {
278        if let ValueId::Function(id) = self {
279            Some(id)
280        } else {
281            None
282        }
283    }
284
285    pub fn as_poison(self) -> Option<PoisonId> {
286        if let ValueId::Poison(id) = self {
287            Some(id)
288        } else {
289            None
290        }
291    }
292
293    pub fn is_poison(self) -> bool {
294        matches!(self, ValueId::Poison(_))
295    }
296}
297
298impl From<LiteralId> for ValueId {
299    fn from(id: LiteralId) -> Self {
300        ValueId::Literal(id)
301    }
302}
303
304impl From<BytesId> for ValueId {
305    fn from(id: BytesId) -> Self {
306        ValueId::Bytes(id)
307    }
308}
309
310impl From<InstructionId> for ValueId {
311    fn from(id: InstructionId) -> Self {
312        ValueId::Instruction(id)
313    }
314}
315
316impl From<BlockId> for ValueId {
317    fn from(id: BlockId) -> Self {
318        ValueId::BasicBlock(id)
319    }
320}
321
322impl From<BlockParamId> for ValueId {
323    fn from(id: BlockParamId) -> Self {
324        ValueId::BlockParam(id)
325    }
326}
327
328impl From<VarnodeId> for ValueId {
329    fn from(id: VarnodeId) -> Self {
330        ValueId::Varnode(id)
331    }
332}
333
334impl From<TempId> for ValueId {
335    fn from(id: TempId) -> Self {
336        ValueId::Temp(id)
337    }
338}
339
340impl From<FunctionId> for ValueId {
341    fn from(id: FunctionId) -> Self {
342        ValueId::Function(id)
343    }
344}
345
346impl From<PoisonId> for ValueId {
347    fn from(id: PoisonId) -> Self {
348        ValueId::Poison(id)
349    }
350}
351
352impl ValueId {
353    /// A total-order sort key that does not require `Into<usize>` (which the
354    /// composite instruction/block/param IDs deliberately lack). The tuple is
355    /// `(variant_tag, function-or-0, local-or-global-index)`; global values put
356    /// their index in the third slot with function 0.
357    pub fn order_key(&self) -> (u8, u32, u32) {
358        match *self {
359            ValueId::Literal(id) => (0, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
360            ValueId::Bytes(id) => (1, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
361            ValueId::Varnode(id) => (2, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
362            ValueId::Function(id) => (3, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
363            ValueId::Instruction(id) => {
364                (4, usize::from(id.func) as u32, usize::from(id.local) as u32)
365            }
366            ValueId::BasicBlock(id) => {
367                (5, usize::from(id.func) as u32, usize::from(id.local) as u32)
368            }
369            ValueId::BlockParam(id) => {
370                (6, usize::from(id.func) as u32, usize::from(id.local) as u32)
371            }
372            ValueId::Temp(id) => (7, usize::from(id.func) as u32, usize::from(id.local) as u32),
373            ValueId::Poison(id) => (8, 0, u32::try_from(usize::from(id)).unwrap_or(u32::MAX)),
374        }
375    }
376}
377
378impl Display for ValueId {
379    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
380        match *self {
381            ValueId::Literal(id) => write!(f, "Literal({})", usize::from(id)),
382            ValueId::Bytes(id) => write!(f, "Bytes({})", usize::from(id)),
383            ValueId::Varnode(id) => write!(f, "Varnode({})", usize::from(id)),
384            ValueId::Function(id) => write!(f, "Function({})", usize::from(id)),
385            ValueId::Instruction(id) => write!(f, "Instruction({id})"),
386            ValueId::BasicBlock(id) => write!(f, "BasicBlock({id})"),
387            ValueId::BlockParam(id) => write!(f, "BlockParam({id})"),
388            ValueId::Temp(id) => write!(f, "Temp({id})"),
389            ValueId::Poison(id) => write!(f, "Poison({})", usize::from(id)),
390        }
391    }
392}
393
394/// The **body-local** twin of [`ValueId`]: the form an in-body operand holds once
395/// storage is localized (stage 6a, ruling 2).
396///
397/// It mirrors `ValueId` variant-for-variant, but its arena arms
398/// (`Instruction`/`BasicBlock`/`BlockParam`) carry a **bare function-local index**
399/// (`LocalInsnId`/`LocalBlockId`/`LocalParamId`) with **no** owning `FunctionId` —
400/// SSA is intra-function, so the owning function is the ambient body and does not
401/// need re-storing on every operand. The shared/module arms
402/// (`Literal`/`Bytes`/`Varnode`/`Function`) carry the exact same globally-interned
403/// or module ids as `ValueId` (they are already the boundary currency).
404///
405/// The two forms convert through [`LocalValueId::qualify`] (stamp the ambient
406/// `func`) and [`ValueId::localize`] (drop it, asserting it matched). `ValueId`
407/// stays the qualified boundary currency (module tables, refs, consumer crates);
408/// `LocalValueId` is confined to in-body storage.
409#[non_exhaustive]
410#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
411pub enum LocalValueId {
412    /// A compile-time integer constant (module-interned; same id as `ValueId`).
413    Literal(LiteralId),
414    /// A compile-time opaque byte blob (module-interned; same id as `ValueId`).
415    Bytes(BytesId),
416    /// An SSA value produced by an [`Instruction`] — bare body-local index.
417    Instruction(LocalInsnId),
418    /// A control-flow node ([`BasicBlock`]) — bare body-local index.
419    BasicBlock(LocalBlockId),
420    /// A typed block-entry parameter — bare body-local index.
421    BlockParam(LocalParamId),
422    /// A named memory location ([`Varnode`]) (module id; same as `ValueId`).
423    Varnode(VarnodeId),
424    /// A body-owned temporary value — bare body-local index.
425    Temp(LocalTempId),
426    /// A lifted or external [`FunctionBody`] (module id; same as `ValueId`).
427    Function(FunctionId),
428    /// A typed poison value (module-interned; same id as `ValueId`).
429    Poison(PoisonId),
430}
431
432impl LocalValueId {
433    /// Qualify a body-local id back into the boundary [`ValueId`] by stamping the
434    /// owning function `func` onto the arena arms. Shared/module arms pass through
435    /// unchanged.
436    pub fn qualify(self, func: FunctionId) -> ValueId {
437        match self {
438            LocalValueId::Literal(id) => ValueId::Literal(id),
439            LocalValueId::Bytes(id) => ValueId::Bytes(id),
440            LocalValueId::Varnode(id) => ValueId::Varnode(id),
441            LocalValueId::Function(id) => ValueId::Function(id),
442            LocalValueId::Poison(id) => ValueId::Poison(id),
443            LocalValueId::Instruction(local) => {
444                ValueId::Instruction(InstructionId::new(func, local))
445            }
446            LocalValueId::BasicBlock(local) => ValueId::BasicBlock(BlockId::new(func, local)),
447            LocalValueId::BlockParam(local) => ValueId::BlockParam(BlockParamId::new(func, local)),
448            LocalValueId::Temp(local) => ValueId::Temp(TempId::new(func, local)),
449        }
450    }
451}
452
453impl ValueId {
454    /// Localize a qualified id for storage inside `func`'s body, dropping the
455    /// owning `FunctionId` from the arena arms. In debug builds this asserts the
456    /// id's `func` equals `func` — the strict-locality tripwire (ruling 2): a
457    /// foreign operand is a bug and fires loudly here rather than misrouting.
458    /// Shared/module arms pass through unchanged.
459    pub fn localize(self, func: FunctionId) -> LocalValueId {
460        match self {
461            ValueId::Literal(id) => LocalValueId::Literal(id),
462            ValueId::Bytes(id) => LocalValueId::Bytes(id),
463            ValueId::Varnode(id) => LocalValueId::Varnode(id),
464            ValueId::Function(id) => LocalValueId::Function(id),
465            ValueId::Poison(id) => LocalValueId::Poison(id),
466            ValueId::Instruction(id) => {
467                debug_assert_eq!(
468                    id.func, func,
469                    "localize: foreign instruction operand {id:?} in function {func} \
470                     (strict IR locality, ruling 2)"
471                );
472                LocalValueId::Instruction(id.local)
473            }
474            ValueId::BasicBlock(id) => {
475                debug_assert_eq!(
476                    id.func, func,
477                    "localize: foreign block operand {id:?} in function {func} \
478                     (strict IR locality, ruling 2)"
479                );
480                LocalValueId::BasicBlock(id.local)
481            }
482            ValueId::BlockParam(id) => {
483                debug_assert_eq!(
484                    id.func, func,
485                    "localize: foreign block-param operand {id:?} in function {func} \
486                     (strict IR locality, ruling 2)"
487                );
488                LocalValueId::BlockParam(id.local)
489            }
490            ValueId::Temp(id) => LocalValueId::Temp(id.localize(func)),
491        }
492    }
493
494    /// Drop the owning `FunctionId` from the arena arms **without** a locality
495    /// check, using the id's *own* embedded func. Unlike [`localize`](Self::localize)
496    /// there is no ambient function to assert against: this is for keying a
497    /// per-function body map (e.g. `FunctionBody.users`) by a value that already
498    /// carries its own func. Within one body's map every arena key has that
499    /// body's func, so stripping it is injective and lookup-stable; shared/module
500    /// arms pass through unchanged.
501    pub fn strip_func(self) -> LocalValueId {
502        match self {
503            ValueId::Literal(id) => LocalValueId::Literal(id),
504            ValueId::Bytes(id) => LocalValueId::Bytes(id),
505            ValueId::Varnode(id) => LocalValueId::Varnode(id),
506            ValueId::Function(id) => LocalValueId::Function(id),
507            ValueId::Poison(id) => LocalValueId::Poison(id),
508            ValueId::Instruction(id) => LocalValueId::Instruction(id.local),
509            ValueId::BasicBlock(id) => LocalValueId::BasicBlock(id.local),
510            ValueId::BlockParam(id) => LocalValueId::BlockParam(id.local),
511            ValueId::Temp(id) => LocalValueId::Temp(id.local),
512        }
513    }
514
515    /// Localize this id **only if it is function-agnostic** — a shared/module arm
516    /// (`Literal`/`Bytes`/`Varnode`/`Function`) that carries no owning function and
517    /// so needs no ambient body to convert. Returns `None` for the function-scoped
518    /// arena arms (`Instruction`/`BasicBlock`/`BlockParam`/`Temp`), which cannot be
519    /// dropped into a *different* body's local space without misrouting. This is the
520    /// safe localizer for a value that may legitimately be a constant flowing into a
521    /// minted body but must otherwise be remapped through a value map.
522    pub fn as_function_agnostic(self) -> Option<LocalValueId> {
523        match self {
524            ValueId::Literal(id) => Some(LocalValueId::Literal(id)),
525            ValueId::Bytes(id) => Some(LocalValueId::Bytes(id)),
526            ValueId::Varnode(id) => Some(LocalValueId::Varnode(id)),
527            ValueId::Function(id) => Some(LocalValueId::Function(id)),
528            ValueId::Poison(id) => Some(LocalValueId::Poison(id)),
529            ValueId::Instruction(_)
530            | ValueId::BasicBlock(_)
531            | ValueId::BlockParam(_)
532            | ValueId::Temp(_) => None,
533        }
534    }
535}
536
537/// Trait implemented by all typed value reference types.
538///
539/// Provides a uniform interface to a value's [`ValueId`] and its size in bytes.
540/// Terminators and non-data values (blocks, functions) report `size() == 0`.
541pub trait Value<'str, 'ctx>: Display {
542    /// The context-unique identifier for this value.
543    fn id(&self) -> ValueId;
544
545    /// The size of this value's output in bytes, or `0` for non-data values
546    /// (terminators, blocks, functions).
547    fn size(&self) -> usize;
548}
549
550/// A borrowed, type-erased view of any value in a [`Context`].
551///
552/// `ValueRef` is the runtime-typed counterpart to [`ValueId`]. It is
553/// produced by `Context::get_value` and borrows the context for `'ctx`.
554/// Use pattern matching to downcast to a concrete reference type.
555pub enum ValueRef<'str, 'ctx, R = ModuleView<'ctx, 'str>> {
556    Literal(LiteralRef<'str, 'ctx>),
557    Bytes(BytesRef<'str, 'ctx>),
558    Instruction(InstructionRef<'str, 'ctx, R>),
559    BasicBlock(BlockRef<'str, 'ctx, R>),
560    BlockParam(BlockParamRef<'str, 'ctx, R>),
561    Varnode(VarnodeRef<'str, 'ctx>),
562    Temp(TempRef<'str, 'ctx, R>),
563    Function(FunctionRef<'str, 'ctx, R>),
564    Poison(PoisonRef<'str, 'ctx>),
565}
566
567impl<R> Debug for ValueRef<'_, '_, R> {
568    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
569        match self {
570            ValueRef::Literal(_) => f.write_str("Literal"),
571            ValueRef::Bytes(_) => f.write_str("Bytes"),
572            ValueRef::Instruction(_) => f.write_str("Instruction"),
573            ValueRef::BasicBlock(_) => f.write_str("BasicBlock"),
574            ValueRef::BlockParam(_) => f.write_str("BlockParam"),
575            ValueRef::Varnode(_) => f.write_str("Varnode"),
576            ValueRef::Temp(_) => f.write_str("Temp"),
577            ValueRef::Function(_) => f.write_str("Function"),
578            ValueRef::Poison(_) => f.write_str("Poison"),
579        }
580    }
581}
582
583impl<'str, 'ctx, R> From<LiteralRef<'str, 'ctx>> for ValueRef<'str, 'ctx, R> {
584    fn from(lit_ref: LiteralRef<'str, 'ctx>) -> Self {
585        ValueRef::Literal(lit_ref)
586    }
587}
588
589impl<'str, 'ctx, R> From<BytesRef<'str, 'ctx>> for ValueRef<'str, 'ctx, R> {
590    fn from(bytes_ref: BytesRef<'str, 'ctx>) -> Self {
591        ValueRef::Bytes(bytes_ref)
592    }
593}
594
595impl<'str, 'ctx, R> From<InstructionRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
596    fn from(insn_ref: InstructionRef<'str, 'ctx, R>) -> Self {
597        ValueRef::Instruction(insn_ref)
598    }
599}
600
601impl<'str, 'ctx, R> From<BlockRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
602    fn from(bb_ref: BlockRef<'str, 'ctx, R>) -> Self {
603        ValueRef::BasicBlock(bb_ref)
604    }
605}
606
607impl<'str, 'ctx, R> From<BlockParamRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
608    fn from(param_ref: BlockParamRef<'str, 'ctx, R>) -> Self {
609        ValueRef::BlockParam(param_ref)
610    }
611}
612
613impl<'str, 'ctx, R> From<VarnodeRef<'str, 'ctx>> for ValueRef<'str, 'ctx, R> {
614    fn from(var_ref: VarnodeRef<'str, 'ctx>) -> Self {
615        ValueRef::Varnode(var_ref)
616    }
617}
618
619impl<'str, 'ctx, R> From<TempRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
620    fn from(temp_ref: TempRef<'str, 'ctx, R>) -> Self {
621        ValueRef::Temp(temp_ref)
622    }
623}
624
625impl<'str, 'ctx, R> From<FunctionRef<'str, 'ctx, R>> for ValueRef<'str, 'ctx, R> {
626    fn from(fn_ref: FunctionRef<'str, 'ctx, R>) -> Self {
627        ValueRef::Function(fn_ref)
628    }
629}
630
631impl<'str, 'ctx, R> From<PoisonRef<'str, 'ctx>> for ValueRef<'str, 'ctx, R> {
632    fn from(poison_ref: PoisonRef<'str, 'ctx>) -> Self {
633        ValueRef::Poison(poison_ref)
634    }
635}
636
637impl<'str, 'ctx> ValueRef<'str, 'ctx> {
638    pub fn new(id: ValueId, ctx: &'ctx Context<'str>) -> Self {
639        ValueRef::from_view(ModuleView::new(ctx), id)
640    }
641
642    pub fn from_id(ctx: &'ctx Context<'str>, id: ValueId) -> Self {
643        Self::new(id, ctx)
644    }
645}
646
647impl<'str: 'ctx, 'ctx, R> ValueRef<'str, 'ctx, R>
648where
649    R: QCodeView<'ctx, 'str>,
650{
651    pub fn from_view(view: R, id: ValueId) -> Self {
652        match id {
653            ValueId::Literal(id) => ValueRef::Literal(LiteralRef::from_id(view.shared(), id)),
654            ValueId::Bytes(id) => ValueRef::Bytes(BytesRef::from_id(view.shared(), id)),
655            ValueId::Varnode(id) => ValueRef::Varnode(Varnode::from_id(view.shared(), id)),
656            ValueId::Temp(id) => ValueRef::Temp(TempRef::new(view, id)),
657            ValueId::Instruction(id) => ValueRef::Instruction(InstructionRef::new(view, id)),
658            ValueId::BasicBlock(id) => ValueRef::BasicBlock(BlockRef::new(view, id)),
659            ValueId::BlockParam(id) => ValueRef::BlockParam(BlockParamRef::new(view, id)),
660            ValueId::Function(id) => ValueRef::Function(FunctionRef::new(view, id)),
661            ValueId::Poison(id) => ValueRef::Poison(PoisonRef::from_id(view.shared(), id)),
662        }
663    }
664
665    fn inner(&self) -> &dyn Value<'str, 'ctx> {
666        match self {
667            ValueRef::Literal(r) => r,
668            ValueRef::Bytes(r) => r,
669            ValueRef::Instruction(r) => r,
670            ValueRef::BasicBlock(r) => r,
671            ValueRef::BlockParam(r) => r,
672            ValueRef::Varnode(r) => r,
673            ValueRef::Temp(r) => r,
674            ValueRef::Function(r) => r,
675            ValueRef::Poison(r) => r,
676        }
677    }
678
679    pub fn space(&self) -> Option<SpaceRef<'ctx>> {
680        match self {
681            ValueRef::Varnode(v) => Some(v.space()),
682            ValueRef::Instruction(i) => i.space(),
683            ValueRef::Temp(_) => None,
684            ValueRef::Literal(_)
685            | ValueRef::Bytes(_)
686            | ValueRef::BasicBlock(_)
687            | ValueRef::BlockParam(_)
688            | ValueRef::Function(_)
689            | ValueRef::Poison(_) => None,
690        }
691    }
692
693    /// Qualified memory-space provenance. Unlike [`space`](Self::space), this
694    /// represents body-local temporary spaces without pretending they are
695    /// shared [`Space`](crate::space::Space) values.
696    pub fn memory_space(&self) -> Option<crate::space::MemorySpaceId> {
697        match self {
698            ValueRef::Varnode(v) => Some(crate::space::MemorySpaceId::Shared(v.space().id)),
699            ValueRef::Instruction(i) => i.memory_space(),
700            ValueRef::Temp(t) => Some(t.memory_space()),
701            ValueRef::Literal(_)
702            | ValueRef::Bytes(_)
703            | ValueRef::BasicBlock(_)
704            | ValueRef::BlockParam(_)
705            | ValueRef::Function(_)
706            | ValueRef::Poison(_) => None,
707        }
708    }
709}
710
711impl<'str: 'ctx, 'ctx, R> Display for ValueRef<'str, 'ctx, R>
712where
713    R: QCodeView<'ctx, 'str>,
714{
715    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
716        // A value operand's rendering — `<ty> <atom>` uniformly, bare for value
717        // references with no scalar type — is defined once, as tokens, in the
718        // instruction `segment` module; `Display` is those tokens concatenated.
719        //
720        // Shared-leaf refs (literal, bytes, varnode) carry only a `&Shared`, so
721        // they render through the `&Shared` token path; the arena-cluster refs
722        // route reads through their static `QCodeView` provider (context-split
723        // Pin B).
724        let tokens = match self {
725            ValueRef::Literal(r) => insn::segment::value_tokens_shared(r.ctx, self.id()),
726            ValueRef::Bytes(r) => insn::segment::value_tokens_shared(r.ctx, self.id()),
727            ValueRef::Varnode(r) => insn::segment::value_tokens_shared(r.ctx, self.id()),
728            ValueRef::Poison(r) => insn::segment::value_tokens_shared(r.ctx, self.id()),
729            ValueRef::Temp(r) => insn::segment::value_tokens_view(r.view, self.id()),
730            ValueRef::Instruction(r) => insn::segment::value_tokens_view(r.view, self.id()),
731            ValueRef::BasicBlock(r) => insn::segment::value_tokens_view(r.view, self.id()),
732            ValueRef::BlockParam(r) => insn::segment::value_tokens_view(r.view, self.id()),
733            ValueRef::Function(r) => insn::segment::value_tokens_view(r.view, self.id()),
734        };
735        for token in tokens {
736            write!(f, "{}", token.text)?;
737        }
738        Ok(())
739    }
740}
741
742impl<'str: 'ctx, 'ctx, R> Value<'str, 'ctx> for ValueRef<'str, 'ctx, R>
743where
744    R: QCodeView<'ctx, 'str>,
745{
746    fn id(&self) -> ValueId {
747        self.inner().id()
748    }
749
750    fn size(&self) -> usize {
751        self.inner().size()
752    }
753}
754
755#[cfg(test)]
756mod local_value_id_tests {
757    use super::*;
758
759    /// Every `ValueId` variant round-trips losslessly through
760    /// `localize(func).qualify(func)`, with the arena arms carrying the given
761    /// `func` and the shared/module arms ignoring it.
762    #[test]
763    fn qualify_localize_round_trips_every_variant() {
764        let func = FunctionId::from(7usize);
765        let other = FunctionId::from(3usize);
766
767        let arena: [ValueId; 4] = [
768            ValueId::Instruction(InstructionId::new(func, LocalInsnId::from(2usize))),
769            ValueId::BasicBlock(BlockId::new(func, LocalBlockId::from(5usize))),
770            ValueId::BlockParam(BlockParamId::new(func, LocalParamId::from(1usize))),
771            ValueId::Temp(TempId::new(func, LocalTempId::from(4usize))),
772        ];
773        for id in arena {
774            assert_eq!(id.localize(func).qualify(func), id, "{id:?}");
775        }
776
777        // Shared/module arms pass through regardless of the ambient func.
778        let shared: [ValueId; 4] = [
779            ValueId::Literal(LiteralId::from(0usize)),
780            ValueId::Bytes(BytesId::from(0usize)),
781            ValueId::Varnode(VarnodeId::from(0usize)),
782            ValueId::Function(other),
783        ];
784        for id in shared {
785            assert_eq!(id.localize(func).qualify(func), id, "{id:?}");
786            // Any func requalifies a shared arm to itself — it carries no func.
787            assert_eq!(id.localize(func).qualify(other), id, "{id:?}");
788        }
789    }
790
791    /// `localize` asserts the operand's func matches the ambient body (the
792    /// strict-locality tripwire). A foreign arena id panics in debug builds.
793    #[test]
794    #[should_panic(expected = "strict IR locality")]
795    #[cfg(debug_assertions)]
796    fn localize_rejects_foreign_arena_id() {
797        let func = FunctionId::from(7usize);
798        let foreign = FunctionId::from(9usize);
799        let id = ValueId::Instruction(InstructionId::new(foreign, LocalInsnId::from(0usize)));
800        let _ = id.localize(func);
801    }
802
803    #[test]
804    #[should_panic(expected = "TempId::localize: foreign id")]
805    #[cfg(debug_assertions)]
806    fn localize_rejects_foreign_temporary_id() {
807        let owner = FunctionId::from(7usize);
808        let foreign = FunctionId::from(9usize);
809        let id = ValueId::Temp(TempId::new(foreign, LocalTempId::from(0usize)));
810        let _ = id.localize(owner);
811    }
812}