Skip to main content

pcode_types/
streaming.rs

1//! Feeding the planner and emitter one statement at a time.
2//!
3//! **For producer implementations.** A consumer of p-code lowers an owned
4//! [`PcodeAst`](crate::PcodeAst) with [`plan_instruction`](crate::plan_instruction),
5//! [`emit_instruction`](crate::emit_instruction) or
6//! [`lower_instruction`](crate::lower_instruction), and never needs this
7//! module. It exists for a producer — a SLEIGH decoder, say — that would
8//! rather not build that instruction-wide AST only to drop it again: the
9//! producer resolves its own source template on the fly and hands each
10//! statement to a [`Planner`], then an [`Emitter`], as it goes.
11//!
12//! The lowering passes in [`crate::instruction`] walk a *shape*: an
13//! expression is a literal, an identifier, a load, an operator over
14//! sub-expressions, and so on. Nothing in them needs the nodes to be owned
15//! [`Expression`] values. Abstracting the shape behind [`ExprNode`] lets a
16//! producer hand the passes a view that resolves its template in place — an
17//! operand field to the constant this encoding gave it, a sub-table to what
18//! it exports — with nothing allocated to say so.
19//!
20//! [`&Expression`](Expression) is itself an [`ExprNode`], and
21//! [`StmtKind`] is built from an [`AstNode`] with [`From`], so an owned AST is
22//! one such shape rather than a special case; the AST entry points above are
23//! that shape fed through the same passes.
24
25use std::{borrow::Cow, slice};
26
27pub use crate::instruction::{Emitter, Planner, SizeInference};
28use crate::{
29    AstNode, BinaryOperator, Builtin, Expression, ExpressionTy, Ident, LabelOrNode, Load,
30    PCodeOpId, PcodeSpaceRef, RangeParam, SpaceId, UnaryOperator,
31};
32
33/// One p-code expression, generic over how it is stored.
34///
35/// A node is a cheap handle — the passes copy it freely and ask for its
36/// [`kind`](Self::kind) and [`size`](Self::size) more than once — so an
37/// implementation should be a reference plus whatever context resolves it,
38/// never an owned tree.
39pub trait ExprNode: Copy {
40    /// The arguments of a call node, in order.
41    type Args: Iterator<Item = Self> + ExactSizeIterator + Clone;
42
43    /// The width in bytes this node carries, if the producer knows one.
44    ///
45    /// This is the [`Expression::size`] a materialised AST would have: what
46    /// was written, or what the producer inferred while expanding. `None`
47    /// leaves the passes to derive one from the node's shape.
48    fn size(self) -> Option<usize>;
49
50    /// The shape of this node, with its children as nodes of the same kind.
51    ///
52    /// `'a` is the life of anything the shape borrows from the node — the
53    /// name of a deferred load space — so it is bounded by the node's own.
54    fn kind<'a>(self) -> ExprKind<'a, Self>
55    where
56        Self: 'a;
57}
58
59/// The shape of one expression node. See [`ExpressionTy`] for the meaning of
60/// each variant; this is the same inventory with the children abstracted.
61#[derive(Debug, Clone)]
62pub enum ExprKind<'a, E: ExprNode> {
63    /// An integer literal.
64    SizedInt {
65        /// The value.
66        value: u64,
67        /// The width the literal was written with, if any.
68        size: Option<usize>,
69    },
70    /// A named storage location.
71    Ident(Ident),
72    /// `*[space]:size ptr`.
73    Load(LoadNode<'a, E>),
74    /// `value[start, size]`.
75    Range(RangeNode<E>),
76    /// `src(count)`: drop `count` low bytes.
77    SubPieceMsb {
78        /// The value truncated.
79        src: E,
80        /// Bytes dropped.
81        count: usize,
82    },
83    /// `src:count`: keep `count` low bytes.
84    SubPieceLsb {
85        /// The value truncated.
86        src: E,
87        /// Bytes kept.
88        count: usize,
89    },
90    /// A built-in function.
91    FunctionCall {
92        /// Which one.
93        builtin: Builtin,
94        /// Its arguments.
95        args: E::Args,
96    },
97    /// A `define pcodeop` call.
98    PcodeOp {
99        /// The operation.
100        id: PCodeOpId,
101        /// Its arguments.
102        args: E::Args,
103    },
104    /// A unary operator.
105    Unop {
106        /// The operator.
107        op: UnaryOperator,
108        /// Its operand.
109        e: E,
110    },
111    /// A binary operator.
112    Binop {
113        /// The operator.
114        op: BinaryOperator,
115        /// Left operand.
116        lhs: E,
117        /// Right operand.
118        rhs: E,
119    },
120    /// A node that has no place in a consumer-form AST: a macro call or a
121    /// deferred call. The passes reject it with
122    /// [`PcodeLowerError::InternalNode`](crate::PcodeLowerError::InternalNode).
123    Internal(&'static str),
124}
125
126/// The address space a load or store names.
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub enum LoadSpace<'a> {
129    /// No space was written: the specification's default.
130    Default,
131    /// A resolved space.
132    Resolved(SpaceId),
133    /// A space name compilation never resolved, borrowed from the producer.
134    /// Cannot occur in a consumer-form AST; the passes report it as
135    /// unresolved, and an AST built from the node keeps the name.
136    Deferred(&'a str),
137}
138
139impl<'a> From<&'a Option<PcodeSpaceRef>> for LoadSpace<'a> {
140    fn from(space: &'a Option<PcodeSpaceRef>) -> Self {
141        match space {
142            None => LoadSpace::Default,
143            Some(PcodeSpaceRef::Resolved(id)) => LoadSpace::Resolved(*id),
144            Some(PcodeSpaceRef::Deferred(name)) => LoadSpace::Deferred(name),
145        }
146    }
147}
148
149impl From<LoadSpace<'_>> for Option<PcodeSpaceRef> {
150    /// The owned form, as it reads in an AST.
151    fn from(space: LoadSpace<'_>) -> Self {
152        match space {
153            LoadSpace::Default => None,
154            LoadSpace::Resolved(id) => Some(PcodeSpaceRef::Resolved(id)),
155            LoadSpace::Deferred(name) => Some(PcodeSpaceRef::Deferred(name.into())),
156        }
157    }
158}
159
160/// `*[space]:size ptr`, with the pointer as a node.
161#[derive(Debug, Clone, Copy)]
162pub struct LoadNode<'a, E> {
163    /// The space read.
164    pub space: LoadSpace<'a>,
165    /// Bytes read, if written.
166    pub size: Option<usize>,
167    /// The address.
168    pub ptr: E,
169}
170
171impl<'a, E> LoadNode<'a, E> {
172    fn from_load<S>(load: &'a Load<S>, node: impl FnOnce(&'a Expression<S>) -> E) -> Self {
173        Self {
174            space: (&load.space).into(),
175            size: load.size,
176            ptr: node(&load.ptr),
177        }
178    }
179}
180
181/// `value[start, size]`, with the value as a node.
182#[derive(Debug, Clone, Copy)]
183pub struct RangeNode<E> {
184    /// The value sliced.
185    pub value: E,
186    /// Lowest bit taken.
187    pub start: RangeParam,
188    /// Bits taken.
189    pub size: RangeParam,
190}
191
192/// Where a direct branch or call goes.
193#[derive(Debug, Clone)]
194pub enum TargetNode<'a, E> {
195    /// An instruction-local label.
196    Label(Cow<'a, str>),
197    /// A name the producer never resolved. Cannot occur in a consumer-form
198    /// AST; the passes reject it.
199    Node(&'a str),
200    /// An address.
201    Expr(E),
202}
203
204impl<'a, S> From<&'a LabelOrNode<S>> for TargetNode<'a, &'a Expression<S>> {
205    fn from(target: &'a LabelOrNode<S>) -> Self {
206        match target {
207            LabelOrNode::Label(name) => TargetNode::Label(Cow::Borrowed(name)),
208            LabelOrNode::Node(name) => TargetNode::Node(name),
209            LabelOrNode::Expr(expr) => TargetNode::Expr(expr),
210        }
211    }
212}
213
214/// The shape of one statement. See [`AstNode`] for the meaning of each
215/// variant.
216#[derive(Debug, Clone)]
217pub enum StmtKind<'a, E: ExprNode> {
218    /// `lhs = rhs`.
219    Assignment {
220        /// The storage written.
221        lhs: Ident,
222        /// The width written, if any.
223        size: Option<usize>,
224        /// The value.
225        rhs: E,
226    },
227    /// `*[space]:size ptr = rhs`.
228    LoadAssignment {
229        /// Where the value goes.
230        load: LoadNode<'a, E>,
231        /// The width written, if any.
232        size: Option<usize>,
233        /// The value.
234        rhs: E,
235    },
236    /// `value[start, size] = rhs`.
237    RangeAssignment {
238        /// The bits written.
239        range: RangeNode<E>,
240        /// The width written, if any.
241        size: Option<usize>,
242        /// The value.
243        rhs: E,
244    },
245    /// `<name>`.
246    Label(Cow<'a, str>),
247    /// `goto target`.
248    Branch {
249        /// Where.
250        target: TargetNode<'a, E>,
251    },
252    /// `if condition goto target`.
253    ConditionalBranch {
254        /// The one-byte condition.
255        condition: E,
256        /// Where.
257        target: TargetNode<'a, E>,
258    },
259    /// `goto [target]`.
260    BranchIndirect {
261        /// The address.
262        target: E,
263    },
264    /// `call target`.
265    Call {
266        /// Where.
267        target: TargetNode<'a, E>,
268    },
269    /// `call [target]`.
270    CallIndirect {
271        /// The address.
272        target: E,
273    },
274    /// `return [target]`.
275    Return {
276        /// The address.
277        target: E,
278    },
279    /// An expression evaluated for its effect.
280    Expression(E),
281    /// A statement that has no place in a consumer-form AST: `build`,
282    /// `delayslot`, `export`, or a deferred `build`. The passes reject it
283    /// with [`PcodeLowerError::InternalNode`](crate::PcodeLowerError::InternalNode).
284    Internal(&'static str),
285}
286
287impl<'a, S> From<&'a AstNode<S>> for StmtKind<'a, &'a Expression<S>> {
288    fn from(statement: &'a AstNode<S>) -> Self {
289        match statement {
290            AstNode::Assignment { lhs, size, rhs } => StmtKind::Assignment {
291                lhs: lhs.clone(),
292                size: *size,
293                rhs,
294            },
295            AstNode::LoadAssignment { lhs, size, rhs } => StmtKind::LoadAssignment {
296                load: LoadNode::from_load(lhs, |ptr| ptr),
297                size: *size,
298                rhs,
299            },
300            AstNode::RangeAssignment { lhs, size, rhs } => StmtKind::RangeAssignment {
301                range: RangeNode {
302                    value: &lhs.value,
303                    start: lhs.start,
304                    size: lhs.size,
305                },
306                size: *size,
307                rhs,
308            },
309            AstNode::Build(_) => StmtKind::Internal("build statement"),
310            AstNode::DelaySlot(_) => StmtKind::Internal("delay-slot directive"),
311            AstNode::DeferredBuild(_) => StmtKind::Internal("deferred build statement"),
312            AstNode::Label(name) => StmtKind::Label(Cow::Borrowed(name)),
313            AstNode::Branch { target } => StmtKind::Branch {
314                target: target.into(),
315            },
316            AstNode::ConditionalBranch { condition, target } => StmtKind::ConditionalBranch {
317                condition,
318                target: target.into(),
319            },
320            AstNode::BranchIndirect { target } => StmtKind::BranchIndirect { target },
321            AstNode::Call { target } => StmtKind::Call {
322                target: target.into(),
323            },
324            AstNode::CallIndirect { target } => StmtKind::CallIndirect { target },
325            AstNode::Return { target } => StmtKind::Return { target },
326            AstNode::Export(_) => StmtKind::Internal("export statement"),
327            AstNode::Expression(expr) => StmtKind::Expression(expr),
328        }
329    }
330}
331
332impl<'a, S> ExprNode for &'a Expression<S> {
333    type Args = slice::Iter<'a, Expression<S>>;
334
335    fn size(self) -> Option<usize> {
336        self.size
337    }
338
339    fn kind<'b>(self) -> ExprKind<'b, Self>
340    where
341        Self: 'b,
342    {
343        match &self.ty {
344            ExpressionTy::SizedInt { value, size } => ExprKind::SizedInt {
345                value: *value,
346                size: *size,
347            },
348            ExpressionTy::Ident(ident) => ExprKind::Ident(ident.clone()),
349            ExpressionTy::Load(load) => ExprKind::Load(LoadNode::from_load(load, |ptr| ptr)),
350            ExpressionTy::Range(range) => ExprKind::Range(RangeNode {
351                value: &range.value,
352                start: range.start,
353                size: range.size,
354            }),
355            ExpressionTy::SubPieceMsb { src, count } => {
356                ExprKind::SubPieceMsb { src, count: *count }
357            }
358            ExpressionTy::SubPieceLsb { src, count } => {
359                ExprKind::SubPieceLsb { src, count: *count }
360            }
361            ExpressionTy::FunctionCall { builtin, args } => ExprKind::FunctionCall {
362                builtin: *builtin,
363                args: args.iter(),
364            },
365            ExpressionTy::PcodeOp { id, args } => ExprKind::PcodeOp {
366                id: *id,
367                args: args.iter(),
368            },
369            ExpressionTy::MacroCall { .. } => ExprKind::Internal("macro call"),
370            ExpressionTy::DeferredCall { .. } => ExprKind::Internal("deferred call"),
371            ExpressionTy::Unop(unop) => ExprKind::Unop {
372                op: unop.op,
373                e: &unop.e,
374            },
375            ExpressionTy::Binop(binop) => ExprKind::Binop {
376                op: binop.op,
377                lhs: &binop.lhs,
378                rhs: &binop.rhs,
379            },
380        }
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    use super::{ExprKind, ExprNode, LoadSpace, StmtKind};
387    use crate::{
388        AstNode, Expression, ExpressionTy, Ident, Load, PcodeSpaceRef, RegisterId, SpaceId,
389    };
390
391    fn load(space: Option<PcodeSpaceRef>) -> Expression {
392        Expression {
393            ty: ExpressionTy::Load(Load {
394                space,
395                size: Some(4),
396                ptr: Box::new(Expression {
397                    ty: ExpressionTy::Ident(Ident::Register(RegisterId::new(0))),
398                    size: Some(8),
399                    span: (),
400                }),
401            }),
402            size: Some(4),
403            span: (),
404        }
405    }
406
407    /// A deferred space keeps its name through the node and back: an AST
408    /// rebuilt from the view reads exactly as the one it was built from.
409    #[test]
410    fn deferred_load_space_keeps_its_name() {
411        let deferred = Some(PcodeSpaceRef::Deferred("segment".into()));
412        let expr = load(deferred.clone());
413        let ExprKind::Load(node) = (&expr).kind() else {
414            panic!("a load");
415        };
416        assert_eq!(node.space, LoadSpace::Deferred("segment"));
417        assert_eq!(Option::<PcodeSpaceRef>::from(node.space), deferred);
418
419        let store = AstNode::LoadAssignment {
420            lhs: Load {
421                space: deferred.clone(),
422                size: Some(4),
423                ptr: Box::new(expr.clone()),
424            },
425            size: None,
426            rhs: expr,
427        };
428        let StmtKind::LoadAssignment { load, .. } = StmtKind::from(&store) else {
429            panic!("a store");
430        };
431        assert_eq!(load.space, LoadSpace::Deferred("segment"));
432
433        assert_eq!(
434            LoadSpace::from(&Some(PcodeSpaceRef::Resolved(SpaceId::new(3)))),
435            LoadSpace::Resolved(SpaceId::new(3))
436        );
437        assert_eq!(LoadSpace::from(&None), LoadSpace::Default);
438    }
439}