bn_bdash_extras/
llil.rs

1//! More ergonomic matching over Binary Ninja's [`low_level_il`][binaryninja::low_level_il] types
2//!
3//! ```
4//! # use bn_bdash_extras::llil::match_instr;
5//! # use bn_bdash_extras::llil::{Instruction, ExpressionKind::*};
6//! # let instr: binaryninja::low_level_il::instruction::LowLevelILInstruction<
7//! #     binaryninja::low_level_il::function::Mutable,
8//! #     binaryninja::low_level_il::function::SSA> = todo!();
9//! match_instr!{
10//!     instr,
11//!     // Basic patterns
12//!     CallSsa(ConstPtr(address), _) => println!("Direct call to {:#x}", address),
13//!     
14//!     // Variable bindings and guards
15//!     instr @ SetRegSsa(dest, add @ Add(RegSsa(src), Const(value))) if value > 10 => {
16//!         println!(
17//!             "Increment of {src:?} by {value} > 10 at {:#x} (dest={dest:?}, add={add:?})",
18//!             instr.address(),
19//!         );
20//!     },
21//!     
22//!     // OR patterns
23//!     CallSsa(_, _) | TailCallSsa(_, _) => println!("Function call"),
24//!     
25//!     _ => {}
26//! };
27//! ```
28//!
29//! NOTE: This currently only supports the operations I've had a need to match against. It will need
30//! to be expanded as it is used for more things.
31
32use std::convert::Into;
33
34use binaryninja::low_level_il::{
35    expression::{ExpressionHandler as _, LowLevelILExpression, LowLevelILExpressionKind},
36    instruction::{InstructionHandler as _, LowLevelILInstruction, LowLevelILInstructionKind},
37    LowLevelILRegisterKind, LowLevelILSSARegisterKind,
38};
39
40mod bn {
41    pub use binaryninja::{
42        architecture::CoreRegister,
43        architecture::Register,
44        low_level_il::{
45            expression::{ExpressionHandler, ValueExpr},
46            function::{FunctionForm, FunctionMutability},
47            instruction::{InstructionHandler, LowLevelILInstructionKind},
48            operation::{BinaryOp, Operation},
49        },
50    };
51}
52
53#[derive(Debug)]
54pub struct Instruction<'func, M, F>
55where
56    M: bn::FunctionMutability,
57    F: bn::FunctionForm,
58{
59    pub kind: InstructionKind<'func, M, F>,
60    pub inner: LowLevelILInstruction<'func, M, F>,
61}
62
63#[derive(Debug)]
64pub enum InstructionKind<'func, M, F>
65where
66    M: bn::FunctionMutability,
67    F: bn::FunctionForm,
68{
69    If(
70        Expression<'func, M, F>,
71        LowLevelILInstruction<'func, M, F>,
72        LowLevelILInstruction<'func, M, F>,
73    ),
74    SetReg(
75        LowLevelILRegisterKind<bn::CoreRegister>,
76        Expression<'func, M, F>,
77    ),
78    SetRegSsa(
79        LowLevelILSSARegisterKind<bn::CoreRegister>,
80        Expression<'func, M, F>,
81    ),
82    RegPhi(
83        LowLevelILSSARegisterKind<bn::CoreRegister>,
84        Vec<LowLevelILSSARegisterKind<bn::CoreRegister>>,
85    ),
86    Call(Expression<'func, M, F>),
87    TailCall(Expression<'func, M, F>),
88    CallSsa(Expression<'func, M, F>, Vec<Expression<'func, M, F>>),
89    TailCallSsa(Expression<'func, M, F>, Vec<Expression<'func, M, F>>),
90    Goto(LowLevelILInstruction<'func, M, F>),
91    Jump(Expression<'func, M, F>),
92    Unknown(LowLevelILInstruction<'func, M, F>),
93}
94
95#[derive(Debug)]
96pub struct BinaryExpression<'func, M, F>(pub Expression<'func, M, F>, pub Expression<'func, M, F>)
97where
98    M: bn::FunctionMutability,
99    F: bn::FunctionForm;
100
101impl<'func, M, F> BinaryExpression<'func, M, F>
102where
103    M: bn::FunctionMutability,
104    F: bn::FunctionForm,
105    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
106{
107    #[must_use]
108    pub fn kinds(&self) -> (ExpressionKind<'func, M, F>, ExpressionKind<'func, M, F>) {
109        (self.0.kind.clone(), self.1.kind.clone())
110    }
111
112    #[must_use]
113    pub fn inners(
114        &self,
115    ) -> (
116        LowLevelILExpression<'func, M, F, bn::ValueExpr>,
117        LowLevelILExpression<'func, M, F, bn::ValueExpr>,
118    ) {
119        (self.0.inner, self.1.inner)
120    }
121}
122
123impl<'func, M, F> Clone for BinaryExpression<'func, M, F>
124where
125    M: bn::FunctionMutability,
126    F: bn::FunctionForm,
127    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
128{
129    fn clone(&self) -> Self {
130        Self(self.0.clone(), self.1.clone())
131    }
132}
133
134#[derive(Debug)]
135pub struct Expression<'func, M, F>
136where
137    M: bn::FunctionMutability,
138    F: bn::FunctionForm,
139{
140    pub inner: LowLevelILExpression<'func, M, F, bn::ValueExpr>,
141    pub kind: ExpressionKind<'func, M, F>,
142}
143
144impl<'func, M, F> Clone for Expression<'func, M, F>
145where
146    M: bn::FunctionMutability,
147    F: bn::FunctionForm,
148    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
149{
150    fn clone(&self) -> Self {
151        Self {
152            inner: self.inner,
153            kind: self.kind.clone(),
154        }
155    }
156}
157
158#[derive(Debug)]
159pub enum ExpressionKind<'func, M, F>
160where
161    M: bn::FunctionMutability,
162    F: bn::FunctionForm,
163{
164    Add(Box<BinaryExpression<'func, M, F>>),
165    Sub(Box<BinaryExpression<'func, M, F>>),
166    And(Box<BinaryExpression<'func, M, F>>),
167    Xor(Box<BinaryExpression<'func, M, F>>),
168    Lsl(Box<BinaryExpression<'func, M, F>>),
169    Lsr(Box<BinaryExpression<'func, M, F>>),
170    CmpE(Box<BinaryExpression<'func, M, F>>),
171    Reg(LowLevelILRegisterKind<bn::CoreRegister>),
172    RegSsa(LowLevelILSSARegisterKind<bn::CoreRegister>),
173    Const(u64),
174    ConstPtr(u64),
175    Unknown(LowLevelILExpression<'func, M, F, bn::ValueExpr>),
176}
177
178impl<'func, M, F> Clone for ExpressionKind<'func, M, F>
179where
180    M: bn::FunctionMutability,
181    F: bn::FunctionForm,
182    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
183{
184    fn clone(&self) -> Self {
185        match self {
186            ExpressionKind::Add(inner) => ExpressionKind::Add(inner.clone()),
187            ExpressionKind::Sub(inner) => ExpressionKind::Sub(inner.clone()),
188            ExpressionKind::And(inner) => ExpressionKind::And(inner.clone()),
189            ExpressionKind::Xor(inner) => ExpressionKind::Xor(inner.clone()),
190            ExpressionKind::Lsl(inner) => ExpressionKind::Lsl(inner.clone()),
191            ExpressionKind::Lsr(inner) => ExpressionKind::Lsr(inner.clone()),
192            ExpressionKind::CmpE(inner) => ExpressionKind::CmpE(inner.clone()),
193            ExpressionKind::Reg(reg) => ExpressionKind::Reg(*reg),
194            ExpressionKind::RegSsa(reg) => ExpressionKind::RegSsa(*reg),
195            ExpressionKind::Const(value) => ExpressionKind::Const(*value),
196            ExpressionKind::ConstPtr(value) => ExpressionKind::ConstPtr(*value),
197            ExpressionKind::Unknown(expr) => ExpressionKind::Unknown(*expr),
198        }
199    }
200}
201
202impl<'func, M, F> From<LowLevelILInstruction<'func, M, F>> for Instruction<'func, M, F>
203where
204    M: bn::FunctionMutability,
205    F: bn::FunctionForm,
206    LowLevelILInstruction<'func, M, F>: bn::InstructionHandler<'func, M, F>,
207    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
208{
209    fn from(instr: LowLevelILInstruction<'func, M, F>) -> Self {
210        Self {
211            kind: InstructionKind::from(instr),
212            inner: instr,
213        }
214    }
215}
216
217impl<'a, 'func, M, F> From<&'a LowLevelILInstruction<'func, M, F>> for Instruction<'func, M, F>
218where
219    M: bn::FunctionMutability,
220    F: bn::FunctionForm,
221    LowLevelILInstruction<'func, M, F>: bn::InstructionHandler<'func, M, F>,
222    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
223{
224    fn from(instr: &'a LowLevelILInstruction<'func, M, F>) -> Self {
225        (*instr).into()
226    }
227}
228
229impl<'func, M, F> From<LowLevelILInstruction<'func, M, F>> for InstructionKind<'func, M, F>
230where
231    M: bn::FunctionMutability,
232    F: bn::FunctionForm,
233    LowLevelILInstruction<'func, M, F>: bn::InstructionHandler<'func, M, F>,
234    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
235{
236    fn from(instr: LowLevelILInstruction<'func, M, F>) -> Self {
237        use LowLevelILExpressionKind as ExpressionKind;
238        use LowLevelILInstructionKind as Kind;
239        match instr.kind() {
240            Kind::If(operation) => Self::If(
241                operation.condition().into(),
242                operation.true_target(),
243                operation.false_target(),
244            ),
245            Kind::SetReg(operation) => {
246                InstructionKind::SetReg(operation.dest_reg(), operation.source_expr().into())
247            }
248            Kind::SetRegSsa(operation) => {
249                InstructionKind::SetRegSsa(operation.dest_reg(), operation.source_expr().into())
250            }
251            Kind::Call(operation) => InstructionKind::Call(operation.target().into()),
252            Kind::TailCall(operation) => InstructionKind::TailCall(operation.target().into()),
253            Kind::CallSsa(operation) => {
254                let ExpressionKind::CallParamSsa(params) = operation.param_expr().kind() else {
255                    panic!(
256                        "Unexpected call parameter expression kind: {:?}",
257                        operation.param_expr().kind()
258                    );
259                };
260                InstructionKind::CallSsa(
261                    operation.target().into(),
262                    params.param_exprs().into_iter().map(Into::into).collect(),
263                )
264            }
265            Kind::TailCallSsa(operation) => {
266                let ExpressionKind::CallParamSsa(params) = operation.param_expr().kind() else {
267                    panic!(
268                        "Unexpected call parameter expression kind: {:?}",
269                        operation.param_expr().kind()
270                    );
271                };
272                InstructionKind::TailCallSsa(
273                    operation.target().into(),
274                    params.param_exprs().into_iter().map(Into::into).collect(),
275                )
276            }
277            Kind::RegPhi(operation) => {
278                let source_regs = operation.source_regs();
279                InstructionKind::RegPhi(operation.dest_reg(), source_regs)
280            }
281            Kind::Goto(operation) => InstructionKind::Goto(operation.target()),
282            Kind::Jump(operation) => InstructionKind::Jump(operation.target().into()),
283            _ => InstructionKind::Unknown(instr),
284        }
285    }
286}
287
288impl<'a, 'func, M, F> From<&'a LowLevelILInstruction<'func, M, F>> for InstructionKind<'func, M, F>
289where
290    M: bn::FunctionMutability,
291    F: bn::FunctionForm,
292    LowLevelILInstruction<'func, M, F>: bn::InstructionHandler<'func, M, F>,
293    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
294{
295    fn from(instr: &'a LowLevelILInstruction<'func, M, F>) -> Self {
296        (*instr).into()
297    }
298}
299
300impl<'func, M, F> From<LowLevelILExpression<'func, M, F, bn::ValueExpr>> for Expression<'func, M, F>
301where
302    M: bn::FunctionMutability,
303    F: bn::FunctionForm,
304    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
305{
306    fn from(expr: LowLevelILExpression<'func, M, F, bn::ValueExpr>) -> Self {
307        Self {
308            inner: expr,
309            kind: ExpressionKind::from(expr),
310        }
311    }
312}
313
314impl<'func, M, F> From<LowLevelILExpression<'func, M, F, bn::ValueExpr>>
315    for ExpressionKind<'func, M, F>
316where
317    M: bn::FunctionMutability,
318    F: bn::FunctionForm,
319    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
320{
321    fn from(expr: LowLevelILExpression<'func, M, F, bn::ValueExpr>) -> Self {
322        use LowLevelILExpressionKind as Kind;
323        match expr.kind() {
324            Kind::Add(operation) => ExpressionKind::Add(Box::new(operation.into())),
325            Kind::Sub(operation) => ExpressionKind::Sub(Box::new(operation.into())),
326            Kind::And(operation) => ExpressionKind::And(Box::new(operation.into())),
327            Kind::Xor(operation) => ExpressionKind::Xor(Box::new(operation.into())),
328            Kind::Lsl(operation) => ExpressionKind::Lsl(Box::new(operation.into())),
329            Kind::Lsr(operation) => ExpressionKind::Lsr(Box::new(operation.into())),
330            Kind::CmpE(operation) => ExpressionKind::CmpE(Box::new(BinaryExpression(
331                operation.left().into(),
332                operation.right().into(),
333            ))),
334            Kind::Const(operation) => ExpressionKind::Const(operation.value()),
335            Kind::ConstPtr(operation) => ExpressionKind::ConstPtr(operation.value()),
336            Kind::Reg(operation) => ExpressionKind::Reg(operation.source_reg()),
337            Kind::RegSsa(operation) => ExpressionKind::RegSsa(operation.source_reg()),
338            _ => ExpressionKind::Unknown(expr),
339        }
340    }
341}
342
343impl<'func, M, F> From<bn::Operation<'func, M, F, bn::BinaryOp>> for BinaryExpression<'func, M, F>
344where
345    M: bn::FunctionMutability,
346    F: bn::FunctionForm,
347    LowLevelILExpression<'func, M, F, bn::ValueExpr>: bn::ExpressionHandler<'func, M, F>,
348{
349    fn from(operation: bn::Operation<'func, M, F, bn::BinaryOp>) -> Self {
350        Self(operation.left().into(), operation.right().into())
351    }
352}
353
354impl<'func, M, F> Instruction<'func, M, F>
355where
356    M: bn::FunctionMutability,
357    F: bn::FunctionForm,
358    LowLevelILInstruction<'func, M, F>: bn::InstructionHandler<'func, M, F>,
359{
360    #[must_use]
361    pub fn size(&self) -> Option<usize> {
362        use bn::LowLevelILInstructionKind as Kind;
363        match self.inner.kind() {
364            Kind::SetReg(ref op) => Some(op.size()),
365            Kind::SetRegSsa(ref op) => Some(op.size()),
366            Kind::Store(ref op) => Some(op.size()),
367            Kind::StoreSsa(ref op) => Some(op.size()),
368            Kind::Push(ref op) => Some(op.size()),
369            _ => None,
370        }
371    }
372}
373
374/// Checks if two [`LowLevelILSSARegisterKind`] instances represent the same register, irrespective of their version.
375pub fn is_same_register<R: bn::Register>(
376    this: &LowLevelILSSARegisterKind<R>,
377    other: &LowLevelILSSARegisterKind<R>,
378) -> bool {
379    match (this, other) {
380        (
381            LowLevelILSSARegisterKind::Full { kind: k1, .. },
382            LowLevelILSSARegisterKind::Full { kind: k2, .. },
383        ) => k1 == k2,
384        (
385            LowLevelILSSARegisterKind::Partial {
386                full_reg: fr1,
387                partial_reg: pr1,
388                ..
389            },
390            LowLevelILSSARegisterKind::Partial {
391                full_reg: fr2,
392                partial_reg: pr2,
393                ..
394            },
395        ) => fr1 == fr2 && pr1 == pr2,
396        _ => false,
397    }
398}
399
400/// Checks whether a given [`LowLevelILSSARegisterKind`] is a full register.
401pub fn is_full_register<R: bn::Register>(reg: &LowLevelILSSARegisterKind<R>) -> bool {
402    matches!(reg, LowLevelILSSARegisterKind::Full { .. })
403}
404
405/// Extract the underlying [`LowLevelILRegisterKind`] from a [`LowLevelILSSARegisterKind`] if it is a full register.
406/// If not, logs a warning and returns [`None`].
407pub fn require_full_register<R: bn::Register, T: std::fmt::Debug>(
408    reg: LowLevelILSSARegisterKind<R>,
409    ctxt: &T,
410) -> Option<LowLevelILRegisterKind<R>> {
411    if let LowLevelILSSARegisterKind::Full { kind, .. } = reg {
412        Some(kind)
413    } else {
414        log::warn!("Expected register used in {ctxt:?} to be a full SSA register, got {reg:?}");
415        None
416    }
417}
418
419/// Trait for extracting the bound value from a matched object.
420///
421/// This trait allows the macro to extract the appropriate value for binding:
422/// - For `Expression` objects: extracts the `.inner` field to get `LowLevelILExpression`
423/// - For other types: returns the value unchanged
424#[doc(hidden)]
425pub trait ExtractBoundValue {
426    type BoundType;
427    fn extract_bound_value(self) -> Self::BoundType;
428}
429
430impl<'func, M, F> ExtractBoundValue for Expression<'func, M, F>
431where
432    M: bn::FunctionMutability,
433    F: bn::FunctionForm,
434{
435    type BoundType = LowLevelILExpression<'func, M, F, bn::ValueExpr>;
436
437    fn extract_bound_value(self) -> Self::BoundType {
438        self.inner
439    }
440}
441
442// Implementations for specific types that should return themselves
443impl<R: bn::Register> ExtractBoundValue for LowLevelILRegisterKind<R> {
444    type BoundType = Self;
445    fn extract_bound_value(self) -> Self::BoundType {
446        self
447    }
448}
449
450impl<R: bn::Register> ExtractBoundValue for LowLevelILSSARegisterKind<R> {
451    type BoundType = Self;
452    fn extract_bound_value(self) -> Self::BoundType {
453        self
454    }
455}
456
457impl ExtractBoundValue for u64 {
458    type BoundType = Self;
459    fn extract_bound_value(self) -> Self::BoundType {
460        self
461    }
462}
463
464impl<M, F> ExtractBoundValue for LowLevelILExpression<'_, M, F, bn::ValueExpr>
465where
466    M: bn::FunctionMutability,
467    F: bn::FunctionForm,
468{
469    type BoundType = Self;
470    fn extract_bound_value(self) -> Self::BoundType {
471        self
472    }
473}
474
475impl<M, F> ExtractBoundValue for Box<BinaryExpression<'_, M, F>>
476where
477    M: bn::FunctionMutability,
478    F: bn::FunctionForm,
479{
480    type BoundType = Self;
481    fn extract_bound_value(self) -> Self::BoundType {
482        self
483    }
484}
485
486impl<M, F> ExtractBoundValue for LowLevelILInstruction<'_, M, F>
487where
488    M: bn::FunctionMutability,
489    F: bn::FunctionForm,
490{
491    type BoundType = Self;
492    fn extract_bound_value(self) -> Self::BoundType {
493        self
494    }
495}
496
497impl<T> ExtractBoundValue for Vec<T>
498where
499    T: ExtractBoundValue,
500{
501    type BoundType = Self;
502    fn extract_bound_value(self) -> Self::BoundType {
503        self
504    }
505}
506
507/// `match` style pattern matching and destructuring of instructions and their expressions.
508///
509/// Provides pattern matching with support for nested expressions, variable bindings with `@`,
510/// OR patterns, and guard conditions.
511///
512/// # Examples
513/// ```no_run
514/// # use bn_bdash_extras::llil::match_instr;
515/// # use bn_bdash_extras::llil::{Instruction, ExpressionKind::*};
516/// # let instr: binaryninja::low_level_il::instruction::LowLevelILInstruction<
517/// #     binaryninja::low_level_il::function::Mutable,
518/// #     binaryninja::low_level_il::function::SSA> = todo!();
519/// match_instr!{
520///     instr,
521///     // Basic patterns
522///     CallSsa(ConstPtr(address), _) => println!("Direct call to {:#x}", address),
523///     
524///     // Variable bindings and guards
525///     instr @ SetRegSsa(dest, add @ Add(RegSsa(src), Const(value))) if value > 10 => {
526///         println!(
527///             "Increment of {src:?} by {value} > 10 at {:#x} (dest={dest:?}, add={add:?})",
528///             instr.address(),
529///         );
530///     },
531///     
532///     // OR patterns
533///     CallSsa(_, _) | TailCallSsa(_, _) => println!("Function call"),
534///     
535///     _ => {}
536/// };
537/// ```
538#[doc(inline)]
539pub use bn_bdash_extras_macros::match_instr;
540
541/// `let / else` style pattern matching and destructuring of instructions and their expressions.
542///
543/// `try_let_instr!` allows you to match a [`LowLevelILInstruction`] against a single pattern,
544/// binding variables if the match succeeds and executing the `else` branch if the match fails.
545/// It's analogous to `let-else` syntax in Rust, but allows matching through expressions in a
546/// single step. This is useful for writing concise and readable instruction-matching code.
547///
548/// # Example
549/// ```no_run
550/// # use bn_bdash_extras::llil::try_let_instr;
551/// # use bn_bdash_extras::llil::{Instruction, ExpressionKind::*};
552/// # fn example<'func, M, F>(
553/// #     instr: binaryninja::low_level_il::instruction::LowLevelILInstruction<'func, M, F>,
554/// # ) -> Option<()> where
555/// #     M: binaryninja::low_level_il::function::FunctionMutability,
556/// #     F: binaryninja::low_level_il::function::FunctionForm,
557/// #     binaryninja::low_level_il::instruction::LowLevelILInstruction<'func, M, F>:
558/// #         binaryninja::low_level_il::instruction::InstructionHandler<'func, M, F>,
559/// #     binaryninja::low_level_il::expression::LowLevelILExpression<'func, M, F, binaryninja::low_level_il::expression::ValueExpr>:
560/// #         binaryninja::low_level_il::expression::ExpressionHandler<'func, M, F> {
561/// try_let_instr!{
562///     let SetRegSsa(dest, Lsr(RegSsa(source), Const(5))) = instr else { return None }
563/// }
564/// # Some(())
565/// # }
566/// ```
567///
568/// This expands to a match on `instr`, binding `dest` and `source` if the pattern matches,
569/// or returning `None` if it does not.
570#[doc(inline)]
571pub use bn_bdash_extras_macros::try_let_instr;
572
573/// Derive macro for mapping instruction pattern matching to structs.
574///
575/// This macro allows you to annotate a struct with a `#[pattern(...)]` attribute,
576/// specifying a Rust pattern that matches a [`LowLevelILInstruction`].
577/// An implementation of the [`TryFrom`] trait will be generated to support converting
578/// from [`LowLevelILInstruction`]. This enables ergonomic and type-safe matching and
579/// extraction of instructions and their subexpressions.
580///
581/// # Details
582/// Bindings in the pattern correspond to the struct's fields. Each binding must have a matching field
583/// of the appropriate type.
584///
585/// Fields are initialized using [`Into`] conversions from the matched instruction and expressions.
586/// This allows you to bind an expression to a field of type [`Expression`], [`ExpressionKind`], or
587/// [`LowLevelILExpression`] depending on your needs, and similarly for the instruction itself.
588///
589/// If the struct binds to an instruction or expression type that requires generic parameters,
590/// struct must be defined with the necessary generic parameters for the object it captures.
591/// For instructions and expressions this will often be the function mutability and form traits.
592/// These MUST use the names `'func`, `M`, and `F` to avoid conflicting with the generated implementations.
593///
594/// # Example
595/// ```no_run
596/// # use binaryninja::{
597/// #     architecture::CoreRegister,
598/// #     low_level_il::{
599/// #         function::{FunctionForm, FunctionMutability},
600/// #         instruction::LowLevelILInstruction,
601/// #         LowLevelILSSARegisterKind,
602/// #     },
603/// # };
604/// # use bn_bdash_extras::{
605/// #     llil::{
606/// #         BinaryExpression, Expression,
607/// #         ExpressionKind::{self, Const, RegSsa},
608/// #         Instruction,
609/// #         InstrMatch,
610/// #     },
611/// # };
612/// #
613/// #[derive(InstrMatch)]
614/// #[pattern(instr @ SetRegSsa(dest, Lsr(RegSsa(source), Const(5))))]
615/// struct SetToLsrBy5<'func, M, F>
616///  where
617///      M: FunctionMutability,
618///      F: FunctionForm,
619/// {
620///     instr: LowLevelILInstruction<'func, M, F>,
621///     dest: LowLevelILSSARegisterKind<CoreRegister>,
622///     source: LowLevelILSSARegisterKind<CoreRegister>,
623/// }
624///
625/// # fn example<'func, M, F>(
626/// #     instr: LowLevelILInstruction<'func, M, F>,
627/// # ) where
628/// #     M: binaryninja::low_level_il::function::FunctionMutability,
629/// #     F: binaryninja::low_level_il::function::FunctionForm,
630/// #     LowLevelILInstruction<'func, M, F>: binaryninja::low_level_il::instruction::InstructionHandler<'func, M, F>,
631/// #     binaryninja::low_level_il::expression::LowLevelILExpression<'func, M, F, binaryninja::low_level_il::expression::ValueExpr>:
632/// #         binaryninja::low_level_il::expression::ExpressionHandler<'func, M, F> {
633/// // Usage:
634/// let match_result = SetToLsrBy5::try_from(instr);
635/// if let Ok(matched) = match_result {
636///     println!(
637///         "Matched SetRegSsa({:?}, Lsr(RegSsa({:?}), Const(5))) at address: {:#x}",
638///         matched.dest, matched.source, matched.instr.address(),
639///     );
640///     // You can now use `matched.instr` and other fields as needed.
641/// } else {
642///     println!("Instruction did not match");
643/// }
644/// # }
645/// ```
646///
647#[doc(inline)]
648pub use bn_bdash_extras_macros::InstrMatch;