Skip to main content

pcode_types/expression/
ops.rs

1//! The operators of SLEIGH's p-code expression language.
2//!
3//! SLEIGH's operands carry a size but no type, so an operator has to say how
4//! its bits are to be read. Where a machine operation differs between signed,
5//! unsigned and floating-point interpretations, SLEIGH spells the three
6//! differently — `/`, `s/` and `f/` — and each spelling is a separate variant
7//! here. A consumer lowering to its own IR reads the variant, not the operand.
8
9use serde::{Deserialize, Serialize};
10
11/// A prefix operator in a p-code expression.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub enum UnaryOperator {
14    /// `!x` — boolean negation. Yields 1 when `x` is zero and 0 otherwise, in
15    /// one byte, regardless of how wide `x` is.
16    LogicalNot,
17
18    /// `~x` — bitwise complement, in the width of `x`.
19    BitwiseNot,
20
21    /// `-x` — two's-complement negation, in the width of `x`. Wraps.
22    Minus,
23
24    /// `f-x` — floating-point negation, in the width of `x`.
25    FloatMinus,
26
27    /// `&x` or `&:n x` — the address of a varnode, as a constant.
28    ///
29    /// This does not read `x`; it yields where `x` lives. The payload is the
30    /// explicit result width from `&:n`, in bytes, or `None` for a bare `&`,
31    /// in which case the address is the width of the containing space's
32    /// addresses.
33    AddressOf(Option<usize>),
34}
35
36/// An infix operator in a p-code expression.
37///
38/// Unless a variant says otherwise, both operands and the result are the same
39/// width, and arithmetic wraps rather than trapping. The comparisons are the
40/// exception: they yield one byte holding 0 or 1, whatever their operands'
41/// width. [`Self::is_comparison`] and its siblings classify a variant without
42/// having to match all thirty-six.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
44pub enum BinaryOperator {
45    /// `*` — multiplication. The low half of the product, so the same for
46    /// signed and unsigned operands.
47    Mul,
48
49    /// `/` — unsigned division.
50    Div,
51
52    /// `s/` — signed division, truncating towards zero.
53    SignedDiv,
54
55    /// `%` — unsigned remainder.
56    Mod,
57
58    /// `s%` — signed remainder, taking its sign from the dividend.
59    SignedMod,
60
61    /// `f/` — floating-point division.
62    FloatDiv,
63
64    /// `f*` — floating-point multiplication.
65    FloatMul,
66
67    /// `+` — addition. Two's-complement, so the same for signed and unsigned
68    /// operands; a carry or overflow flag is computed separately, with
69    /// [`Builtin::Carry`](super::Builtin::Carry) or
70    /// [`Builtin::Sborrow`](super::Builtin::Sborrow).
71    Add,
72
73    /// `-` — subtraction, likewise sign-agnostic.
74    Sub,
75
76    /// `f+` — floating-point addition.
77    FloatAdd,
78
79    /// `f-` — floating-point subtraction.
80    FloatSub,
81
82    /// `<<` — left shift. Bits shifted off the top are discarded.
83    LeftShift,
84
85    /// `>>` — logical right shift, shifting in zeroes.
86    RightShift,
87
88    /// `s>>` — arithmetic right shift, shifting in copies of the sign bit.
89    SignedRightShift,
90
91    /// `s<` — signed less-than.
92    SignedLessThan,
93
94    /// `s>` — signed greater-than.
95    SignedGreaterThan,
96
97    /// `s<=` — signed less-than-or-equal.
98    SignedLessEqual,
99
100    /// `s>=` — signed greater-than-or-equal.
101    SignedGreaterEqual,
102
103    /// `<=` — unsigned less-than-or-equal.
104    LessEqual,
105
106    /// `>=` — unsigned greater-than-or-equal.
107    GreaterEqual,
108
109    /// `<` — unsigned less-than.
110    LessThan,
111
112    /// `>` — unsigned greater-than.
113    GreaterThan,
114
115    /// `f<=` — floating-point less-than-or-equal.
116    FloatLessEqual,
117
118    /// `f>=` — floating-point greater-than-or-equal.
119    FloatGreaterEqual,
120
121    /// `f<` — floating-point less-than.
122    FloatLessThan,
123
124    /// `f>` — floating-point greater-than.
125    FloatGreaterThan,
126
127    /// `==` — bitwise equality. Sign-agnostic, since two's-complement
128    /// equality is bit equality.
129    Equal,
130
131    /// `!=` — bitwise inequality.
132    NotEqual,
133
134    /// `f==` — floating-point equality, which is *not* bit equality: `NaN`
135    /// compares unequal to itself, and the two zeroes compare equal.
136    FloatEqual,
137
138    /// `f!=` — floating-point inequality.
139    FloatNotEqual,
140
141    /// `^^` — boolean exclusive-or. Operands are read as false when zero and
142    /// true otherwise; the result is one byte.
143    LogicalXor,
144
145    /// `&&` — boolean and. One byte, and **not** short-circuiting: p-code has
146    /// no control flow inside an expression, so both operands are evaluated.
147    LogicalAnd,
148
149    /// `||` — boolean or, likewise one byte and not short-circuiting.
150    LogicalOr,
151
152    /// `^` — bitwise exclusive-or, in the width of the operands.
153    BitwiseXor,
154
155    /// `|` — bitwise or.
156    BitwiseOr,
157
158    /// `&` — bitwise and.
159    BitwiseAnd,
160}
161
162impl BinaryOperator {
163    /// Does this operator yield a one-byte boolean rather than a value in the
164    /// width of its operands?
165    pub fn is_comparison(self) -> bool {
166        matches!(
167            self,
168            BinaryOperator::LessEqual
169                | BinaryOperator::GreaterEqual
170                | BinaryOperator::LessThan
171                | BinaryOperator::GreaterThan
172                | BinaryOperator::SignedLessThan
173                | BinaryOperator::SignedGreaterThan
174                | BinaryOperator::SignedLessEqual
175                | BinaryOperator::SignedGreaterEqual
176                | BinaryOperator::Equal
177                | BinaryOperator::NotEqual
178                | BinaryOperator::FloatLessThan
179                | BinaryOperator::FloatLessEqual
180                | BinaryOperator::FloatGreaterThan
181                | BinaryOperator::FloatGreaterEqual
182                | BinaryOperator::FloatEqual
183                | BinaryOperator::FloatNotEqual
184        )
185    }
186
187    /// Is this a shift, whose right operand is a distance rather than a value
188    /// of the same width?
189    pub fn is_shift(self) -> bool {
190        matches!(
191            self,
192            BinaryOperator::LeftShift
193                | BinaryOperator::RightShift
194                | BinaryOperator::SignedRightShift
195        )
196    }
197
198    /// Is this one of the four comparisons that read their operands as
199    /// two's-complement signed integers?
200    pub fn is_signed_comparison(self) -> bool {
201        matches!(
202            self,
203            BinaryOperator::SignedLessThan
204                | BinaryOperator::SignedGreaterThan
205                | BinaryOperator::SignedLessEqual
206                | BinaryOperator::SignedGreaterEqual
207        )
208    }
209
210    /// Is this one of the six comparisons that read their operands as
211    /// floating-point?
212    pub fn is_float_comparison(self) -> bool {
213        matches!(
214            self,
215            BinaryOperator::FloatLessThan
216                | BinaryOperator::FloatLessEqual
217                | BinaryOperator::FloatGreaterThan
218                | BinaryOperator::FloatGreaterEqual
219                | BinaryOperator::FloatEqual
220                | BinaryOperator::FloatNotEqual
221        )
222    }
223
224    /// Does this operator read its operands as floating-point, whether it
225    /// compares them or computes with them?
226    pub fn is_float(self) -> bool {
227        matches!(
228            self,
229            BinaryOperator::FloatDiv
230                | BinaryOperator::FloatMul
231                | BinaryOperator::FloatAdd
232                | BinaryOperator::FloatSub
233                | BinaryOperator::FloatLessEqual
234                | BinaryOperator::FloatGreaterEqual
235                | BinaryOperator::FloatLessThan
236                | BinaryOperator::FloatGreaterThan
237                | BinaryOperator::FloatEqual
238                | BinaryOperator::FloatNotEqual
239        )
240    }
241
242    /// Is this a boolean connective (`&&`, `||`, `^^`) rather than a bitwise
243    /// one? Both operands and the result are truth values, one byte wide.
244    pub fn is_logical(self) -> bool {
245        matches!(
246            self,
247            BinaryOperator::LogicalXor | BinaryOperator::LogicalAnd | BinaryOperator::LogicalOr
248        )
249    }
250
251    /// Does this operator read its operands as two's-complement signed
252    /// integers? Covers the signed comparisons plus `s/`, `s%` and `s>>`.
253    pub fn is_signed_integer(self) -> bool {
254        matches!(
255            self,
256            BinaryOperator::SignedDiv
257                | BinaryOperator::SignedMod
258                | BinaryOperator::SignedLessThan
259                | BinaryOperator::SignedGreaterThan
260                | BinaryOperator::SignedLessEqual
261                | BinaryOperator::SignedGreaterEqual
262                | BinaryOperator::SignedRightShift
263        )
264    }
265
266    pub fn pretty_print(self) -> &'static str {
267        match self {
268            BinaryOperator::Mul => "*",
269            BinaryOperator::Div => "/",
270            BinaryOperator::SignedDiv => "s/",
271            BinaryOperator::Mod => "%",
272            BinaryOperator::SignedMod => "s%",
273            BinaryOperator::FloatDiv => "f/",
274            BinaryOperator::FloatMul => "f*",
275            BinaryOperator::Add => "+",
276            BinaryOperator::Sub => "-",
277            BinaryOperator::FloatAdd => "f+",
278            BinaryOperator::FloatSub => "f-",
279            BinaryOperator::LeftShift => "<<",
280            BinaryOperator::RightShift => ">>",
281            BinaryOperator::SignedRightShift => "s>>",
282            BinaryOperator::SignedLessThan => "s<",
283            BinaryOperator::SignedGreaterThan => "s>",
284            BinaryOperator::SignedLessEqual => "s<=",
285            BinaryOperator::SignedGreaterEqual => "s>=",
286            BinaryOperator::LessEqual => "<=",
287            BinaryOperator::GreaterEqual => ">=",
288            BinaryOperator::LessThan => "<",
289            BinaryOperator::GreaterThan => ">",
290            BinaryOperator::FloatLessEqual => "f<=",
291            BinaryOperator::FloatGreaterEqual => "f>=",
292            BinaryOperator::FloatLessThan => "f<",
293            BinaryOperator::FloatGreaterThan => "f>",
294            BinaryOperator::Equal => "==",
295            BinaryOperator::NotEqual => "!=",
296            BinaryOperator::FloatEqual => "f==",
297            BinaryOperator::FloatNotEqual => "f!=",
298            BinaryOperator::LogicalXor => "^^",
299            BinaryOperator::LogicalAnd => "&&",
300            BinaryOperator::LogicalOr => "||",
301            BinaryOperator::BitwiseXor => "^",
302            BinaryOperator::BitwiseOr => "|",
303            BinaryOperator::BitwiseAnd => "&",
304        }
305    }
306}