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}