Skip to main content

pcode_types/
expression.rs

1mod ids;
2mod ops;
3mod types;
4
5pub use ids::LocalVarInterner;
6pub use ids::{Builtin, LocalVarId};
7pub use ops::{BinaryOperator, UnaryOperator};
8pub use types::{Binop, Load, Range, RangeParam, SpaceRef, Unop};
9
10use crate::{BitRangeFieldId, FieldId, PCodeOpId, PMacroId, RegisterId, TableId};
11use serde::{Deserialize, Serialize};
12
13/// A named thing a p-code expression refers to.
14///
15/// Everything but [`Ident::Global`] is fully resolved: by the time an
16/// instruction's AST reaches a consumer, a name has become an index into the
17/// compiled specification. Look identifiers up through the producer's
18/// specification table — its register table for a [`RegisterId`], bit-range
19/// table for a bit-range field, and so on.
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub enum Ident {
22    /// A temporary declared inside a constructor or macro body — `local x:4`,
23    /// or an assignment to a name that was never declared. Unique within one
24    /// decoded instruction; see [`LocalVarId`].
25    Named(LocalVarId),
26
27    /// A machine register, as named by `define register`.
28    Register(RegisterId),
29
30    /// A named sub-range of a register, as named by `define bitrange`.
31    /// The producer's bit-range table gives the parent register and byte
32    /// window.
33    BitRange(BitRangeFieldId),
34
35    /// A token, context or global field. In an emitted instruction this is
36    /// normally already folded to a constant; one surviving here means the
37    /// decode could not supply a value for it.
38    Field(FieldId),
39
40    /// A sub-table operand. One surviving in an emitted instruction means its
41    /// sub-constructor exported nothing.
42    Table(TableId),
43    /// An identifier absent from the symbol table during Phase 2 parsing.
44    /// Resolved to the appropriate variant by the Phase 3 resolve pass.
45    Global(Box<str>),
46}
47
48/// The shape of a p-code expression node.
49///
50/// `S` is the span type: `(usize, usize)` at parse time, `()` in the stored/runtime form.
51#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
52pub enum ExpressionTy<S = ()> {
53    /// An integer literal.
54    SizedInt {
55        /// The value, zero-extended into a `u64`.
56        value: u64,
57        /// Explicit width in bytes from a `:n` suffix, or `None` when the
58        /// literal takes its width from the context it appears in.
59        size: Option<usize>,
60    },
61
62    /// `x(n)` — drop the low `n` bytes of `x`, keeping the high end.
63    SubPieceMsb {
64        /// The value being truncated.
65        src: Box<Expression<S>>,
66        /// How many bytes to drop from the bottom.
67        count: usize,
68    },
69
70    /// `x:n` — keep the low `n` bytes of `x`.
71    SubPieceLsb {
72        /// The value being truncated.
73        src: Box<Expression<S>>,
74        /// How many low bytes to keep.
75        count: usize,
76    },
77
78    /// `*[space]:n ptr` — a memory read.
79    Load(Load<S>),
80
81    /// `x[start, size]` — a bit range of a value.
82    Range(Range<S>),
83
84    /// A call to one of SLEIGH's built-in functions.
85    FunctionCall {
86        /// Which builtin.
87        builtin: Builtin,
88        /// Its arguments, in source order.
89        args: Vec<Expression<S>>,
90    },
91
92    /// A call to a `define pcodeop` — an operation the specification declares
93    /// but does not define, so a consumer must give it meaning. The name is
94    /// the `id`-th entry of
95    /// the producer's user-defined operation table.
96    PcodeOp {
97        /// Index into the specification's user-defined operation list.
98        id: PCodeOpId,
99        /// Its arguments, in source order.
100        args: Vec<Expression<S>>,
101    },
102
103    /// A call to a `macro`. Expanded away before a consumer sees the AST;
104    /// one surviving is a bug in this crate.
105    MacroCall {
106        /// The macro being called.
107        id: PMacroId,
108        /// Its arguments, in source order.
109        args: Vec<Expression<S>>,
110    },
111
112    /// A call to a name the symbol table did not hold — necessarily a macro
113    /// parameter, substituted when the macro is inlined. A consumer does not
114    /// see this variant.
115    DeferredCall {
116        /// The name being called.
117        name: Box<str>,
118        /// Its arguments, in source order.
119        args: Vec<Expression<S>>,
120    },
121
122    /// A reference to a named thing.
123    Ident(Ident),
124
125    /// A prefix operator applied to one operand.
126    Unop(Unop<S>),
127
128    /// An infix operator applied to two operands.
129    Binop(Binop<S>),
130}
131
132impl From<ExpressionTy> for Expression {
133    fn from(ty: ExpressionTy) -> Self {
134        Self {
135            ty,
136            size: None,
137            span: (),
138        }
139    }
140}
141
142impl ExpressionTy {
143    /// Wraps this node in an expression with a known byte width.
144    pub fn with_size(self, size: usize) -> Expression {
145        Expression {
146            ty: self,
147            size: Some(size),
148            span: (),
149        }
150    }
151
152    pub(crate) fn pretty_print(&self, spec: &impl crate::PcodeResolver) -> String {
153        match self {
154            ExpressionTy::SizedInt { value, size } => match size {
155                Some(size) => format!("{value}:{size}"),
156                None => value.to_string(),
157            },
158            ExpressionTy::SubPieceMsb { src, count } => {
159                format!("subpiece_msb({}, {})", src.pretty_print(spec), count)
160            }
161            ExpressionTy::SubPieceLsb { src, count } => {
162                format!("subpiece_lsb({}, {})", src.pretty_print(spec), count)
163            }
164            ExpressionTy::Load(load) => load.pretty_print(spec),
165            ExpressionTy::Range(range) => range.pretty_print(spec),
166            ExpressionTy::FunctionCall { builtin, args } => {
167                format!("{}({})", builtin.as_str(), pretty_print_args(args, spec))
168            }
169            ExpressionTy::PcodeOp { id, args } => format!(
170                "{}({})",
171                spec.pcode_op_name(*id),
172                pretty_print_args(args, spec)
173            ),
174            ExpressionTy::MacroCall { id, args } => format!(
175                "{}({})",
176                spec.macro_name(*id),
177                pretty_print_args(args, spec)
178            ),
179            ExpressionTy::DeferredCall { name, args } => {
180                format!("{}({})", name, pretty_print_args(args, spec))
181            }
182            ExpressionTy::Ident(ident) => pretty_print_ident(spec, ident),
183            ExpressionTy::Unop(unop) => unop.pretty_print(spec),
184            ExpressionTy::Binop(binop) => binop.pretty_print(spec),
185        }
186    }
187}
188
189impl<S> ExpressionTy<S> {
190    /// Discards source spans.
191    pub fn strip_span(self) -> ExpressionTy<()> {
192        match self {
193            ExpressionTy::SizedInt { value, size } => ExpressionTy::SizedInt { value, size },
194            ExpressionTy::SubPieceMsb { src, count } => ExpressionTy::SubPieceMsb {
195                src: Box::new(src.strip_span()),
196                count,
197            },
198            ExpressionTy::SubPieceLsb { src, count } => ExpressionTy::SubPieceLsb {
199                src: Box::new(src.strip_span()),
200                count,
201            },
202            ExpressionTy::Load(load) => ExpressionTy::Load(load.strip_span()),
203            ExpressionTy::Range(range) => ExpressionTy::Range(range.strip_span()),
204            ExpressionTy::FunctionCall { builtin, args } => ExpressionTy::FunctionCall {
205                builtin,
206                args: args.into_iter().map(Expression::strip_span).collect(),
207            },
208            ExpressionTy::PcodeOp { id, args } => ExpressionTy::PcodeOp {
209                id,
210                args: args.into_iter().map(Expression::strip_span).collect(),
211            },
212            ExpressionTy::MacroCall { id, args } => ExpressionTy::MacroCall {
213                id,
214                args: args.into_iter().map(Expression::strip_span).collect(),
215            },
216            ExpressionTy::DeferredCall { name, args } => ExpressionTy::DeferredCall {
217                name,
218                args: args.into_iter().map(Expression::strip_span).collect(),
219            },
220            ExpressionTy::Ident(ident) => ExpressionTy::Ident(ident),
221            ExpressionTy::Unop(unop) => ExpressionTy::Unop(unop.strip_span()),
222            ExpressionTy::Binop(binop) => ExpressionTy::Binop(binop.strip_span()),
223        }
224    }
225}
226
227/// A p-code expression: a node kind, plus the width its value has.
228///
229/// `S` is the span type. It is `(usize, usize)` — a byte range into the
230/// preprocessed source — while the compiler is lowering, and `()` in the form
231/// a consumer receives, which is the default span parameter.
232#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
233pub struct Expression<S = ()> {
234    /// What kind of expression this is, and its operands.
235    pub ty: ExpressionTy<S>,
236
237    /// Width of the value in bytes.
238    ///
239    /// `None` means the width was not written in the source and could not be
240    /// inferred — a literal in a position that does not pin one down, most
241    /// often. A consumer that needs a width must supply one from context
242    /// rather than assume.
243    pub size: Option<usize>,
244
245    /// Where this node came from in the preprocessed source, or `()` once the
246    /// compiler is done with it.
247    pub span: S,
248}
249
250impl<S: std::fmt::Debug> std::fmt::Debug for Expression<S> {
251    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252        if let Some(size) = self.size {
253            write!(f, "{:?} (size: {:?})", self.ty, size)
254        } else {
255            self.ty.fmt(f)
256        }
257    }
258}
259
260impl<S> Expression<S> {
261    /// Discards source spans, giving the form a consumer receives.
262    pub fn strip_span(self) -> Expression<()> {
263        Expression {
264            ty: self.ty.strip_span(),
265            size: self.size,
266            span: (),
267        }
268    }
269}
270
271impl Expression {
272    /// Renders this expression in a SLEIGH-like syntax, resolving identifiers
273    /// against `spec`. For diagnostics and tests; not a stable format.
274    pub fn pretty_print(&self, spec: &impl crate::PcodeResolver) -> String {
275        self.ty.pretty_print(spec)
276    }
277}
278
279impl Expression<(usize, usize)> {
280    /// Creates an integer literal with its parse-time span and optional width.
281    pub fn new_int(value: u64, size: Option<usize>, span: (usize, usize)) -> Self {
282        Self {
283            ty: ExpressionTy::SizedInt { value, size },
284            size,
285            span,
286        }
287    }
288}
289
290fn pretty_print_args(args: &[Expression], spec: &impl crate::PcodeResolver) -> String {
291    args.iter()
292        .map(|arg| arg.pretty_print(spec))
293        .collect::<Vec<_>>()
294        .join(", ")
295}
296
297pub fn pretty_print_ident(spec: &impl crate::PcodeResolver, ident: &Ident) -> String {
298    match ident {
299        Ident::Named(id) => format!("v{}", id.0),
300        Ident::Register(_) => spec.ident_name(ident),
301        Ident::BitRange(_) => spec.ident_name(ident),
302        Ident::Field(_) => spec.ident_name(ident),
303        Ident::Table(id) => format!("table{}", usize::from(*id)),
304        Ident::Global(name) => format!("?{name}"),
305    }
306}