1#![no_std]
7
8extern crate alloc;
9use alloc::collections::{BTreeMap, BTreeSet};
10use alloc::string::String;
11use alloc::vec;
12use alloc::vec::Vec;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
20pub enum AddressSpaceId {
21 Register,
23 Ram,
25 Unique,
27 Const,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
36pub struct Varnode {
37 pub space: AddressSpaceId,
38 pub offset: u64,
39 pub size: u32,
41}
42
43impl Varnode {
44 #[inline]
46 pub fn register(offset: u64, size: u32) -> Self {
47 Self {
48 space: AddressSpaceId::Register,
49 offset,
50 size,
51 }
52 }
53
54 #[inline]
56 pub fn unique(offset: u64, size: u32) -> Self {
57 Self {
58 space: AddressSpaceId::Unique,
59 offset,
60 size,
61 }
62 }
63
64 #[inline]
66 pub fn ram(offset: u64, size: u32) -> Self {
67 Self {
68 space: AddressSpaceId::Ram,
69 offset,
70 size,
71 }
72 }
73
74 #[inline]
76 pub fn constant(value: u64, size: u32) -> Self {
77 Self {
78 space: AddressSpaceId::Const,
79 offset: value,
80 size,
81 }
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct ConstructorSpan {
92 pub constructor_id: u32,
94 pub table_id: u32,
96 pub source: &'static str,
99}
100
101#[derive(Debug, Clone)]
103pub struct Instruction {
104 pub len: u64,
106 pub disassembly: String,
108 pub ops: Vec<PcodeOp>,
110 pub constructor: Option<ConstructorSpan>,
114}
115
116impl Instruction {
117 pub fn new(len: u64, disassembly: String, ops: Vec<PcodeOp>) -> Self {
120 Self {
121 len,
122 disassembly,
123 ops,
124 constructor: None,
125 }
126 }
127}
128
129#[derive(Debug, Clone)]
131pub enum DecodeError {
132 UnknownInstruction,
134}
135
136impl core::fmt::Display for DecodeError {
137 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
138 match self {
139 DecodeError::UnknownInstruction => write!(f, "unknown instruction encoding"),
140 }
141 }
142}
143
144pub fn offset_unique_varnodes(op: &mut PcodeOp, offset: u64) {
147 if let Some(out) = get_output_mut(op) {
149 if out.space == AddressSpaceId::Unique {
150 out.offset += offset;
151 }
152 }
153 visit_reads_mut(op, &mut |v| {
155 if v.space == AddressSpaceId::Unique {
156 v.offset += offset;
157 }
158 });
159}
160
161pub fn optimize(ops: &mut Vec<PcodeOp>) {
167 for _round in 0..4 {
169 let before = ops.len();
170 optimize_once(ops);
171 if ops.len() == before {
172 break;
173 }
174 }
175}
176
177fn optimize_once(ops: &mut Vec<PcodeOp>) {
178 for op in ops.iter_mut() {
181 if let Some((out, value)) = const_fold_op(op) {
182 *op = PcodeOp::Copy {
183 out,
184 input: Varnode::constant(value, out.size),
185 };
186 continue;
187 }
188
189 match op {
190 PcodeOp::IntZext { out, input } if input.space == AddressSpaceId::Const => {
191 *op = PcodeOp::Copy {
192 out: *out,
193 input: Varnode::constant(mask_to_size(input.offset, out.size), out.size),
194 };
195 }
196 PcodeOp::IntSext { out, input } if input.space == AddressSpaceId::Const => {
197 *op = PcodeOp::Copy {
198 out: *out,
199 input: Varnode::constant(
200 sign_extend_to_size(input.offset, input.size, out.size),
201 out.size,
202 ),
203 };
204 }
205 PcodeOp::IntLsr { out, left, right }
207 | PcodeOp::IntLsl { out, left, right }
208 | PcodeOp::IntAsr { out, left, right }
209 if right.space == AddressSpaceId::Const && right.offset == 0 =>
210 {
211 *op = PcodeOp::Copy {
212 out: *out,
213 input: *left,
214 };
215 }
216 PcodeOp::IntOr { out, left, right }
218 if right.space == AddressSpaceId::Const && right.offset == 0 =>
219 {
220 *op = PcodeOp::Copy {
221 out: *out,
222 input: *left,
223 };
224 }
225 PcodeOp::IntOr { out, left, right }
226 if left.space == AddressSpaceId::Const && left.offset == 0 =>
227 {
228 *op = PcodeOp::Copy {
229 out: *out,
230 input: *right,
231 };
232 }
233 PcodeOp::IntAnd { out, left, right }
235 if right.space == AddressSpaceId::Const
236 && right.offset == all_ones_mask(out.size) =>
237 {
238 *op = PcodeOp::Copy {
239 out: *out,
240 input: *left,
241 };
242 }
243 _ => {}
244 }
245 }
246
247 for op in ops.iter_mut() {
249 if let PcodeOp::Subpiece { out, input, lsb: 0 } = op {
250 if out.size == input.size {
251 *op = PcodeOp::Copy {
252 out: *out,
253 input: *input,
254 };
255 }
256 }
257 }
258
259 for op in ops.iter_mut() {
265 if let PcodeOp::Subpiece { out, input, lsb: 0 } = op {
266 if input.space == AddressSpaceId::Const && input.size > out.size {
267 let mask: u64 = if out.size >= 8 {
268 u64::MAX
269 } else {
270 (1u64 << (out.size as u64 * 8)) - 1
271 };
272 let truncated = Varnode {
273 space: AddressSpaceId::Const,
274 offset: input.offset & mask,
275 size: out.size,
276 };
277 *op = PcodeOp::Copy {
278 out: *out,
279 input: truncated,
280 };
281 }
282 }
283 }
284
285 let mut analysis = analyze_unique_outputs(ops);
289
290 let mut i = 0;
293 while i + 1 < ops.len() {
294 let collapse = if let PcodeOp::IntAnd {
295 out: out1,
296 left: _,
297 right: mask1,
298 } = &ops[i]
299 {
300 if out1.space == AddressSpaceId::Unique && mask1.space == AddressSpaceId::Const {
301 if let PcodeOp::IntAnd {
302 out: out2,
303 left: in2,
304 right: mask2,
305 } = &ops[i + 1]
306 {
307 if *in2 == *out1 && *mask2 == *mask1 {
308 let total_reads = match analysis.get(i) {
312 Some(Some(info)) => info.future_reads,
313 _ => usize::MAX, };
315 if total_reads == 1 {
316 Some(*out2)
317 } else {
318 None
319 }
320 } else {
321 None
322 }
323 } else {
324 None
325 }
326 } else {
327 None
328 }
329 } else {
330 None
331 };
332
333 if let Some(new_out) = collapse {
334 if let PcodeOp::IntAnd { out, .. } = &mut ops[i] {
335 *out = new_out;
336 }
337 ops.remove(i + 1);
338 analysis = analyze_unique_outputs(ops);
339 continue;
340 }
341 i += 1;
342 }
343
344 let mut i = 0;
349 while i < ops.len() {
350 if let PcodeOp::Copy { out, input } = &ops[i] {
351 if out.space == AddressSpaceId::Unique {
352 let target = *out;
353 let replacement = *input;
354 let entry = analysis.get(i).and_then(|entry| entry.as_ref());
355 if let Some(UniqueOutputInfo {
356 future_reads: 1,
357 next_access: Some(NextAccess::Read(read_idx)),
358 }) = entry
359 {
360 replace_reads(&mut ops[*read_idx], &target, &replacement);
361 ops.remove(i);
362 analysis = analyze_unique_outputs(ops);
363 continue;
364 }
365 }
366 }
367 i += 1;
368 }
369
370 {
377 let mut to_remove = Vec::new();
378 for (i, entry) in analysis.iter().enumerate() {
379 if let Some(info) = entry {
380 if info.future_reads == 0 || matches!(info.next_access, Some(NextAccess::Write)) {
381 to_remove.push(i);
382 }
383 }
384 }
385 for &idx in to_remove.iter().rev() {
386 ops.remove(idx);
387 }
388 }
389
390 analysis = analyze_unique_outputs(ops);
396 let mut i = 0;
397 while i < ops.len() {
398 let should_sink = match (
399 get_output(&ops[i]),
400 analysis.get(i).and_then(|entry| entry.as_ref()),
401 ) {
402 (
403 Some(out),
404 Some(UniqueOutputInfo {
405 future_reads: 1,
406 next_access: Some(NextAccess::Read(copy_idx)),
407 }),
408 ) if out.space == AddressSpaceId::Unique => {
409 if let PcodeOp::Copy {
410 out: copy_dest,
411 input: copy_src,
412 } = &ops[*copy_idx]
413 {
414 if *copy_src == out {
415 let d = *copy_dest;
416 let safe = (i + 1..*copy_idx)
417 .all(|k| count_reads(&ops[k], &d) == 0 && !writes_to(&ops[k], &d));
418 if safe {
419 Some((*copy_idx, d))
420 } else {
421 None
422 }
423 } else {
424 None
425 }
426 } else {
427 None
428 }
429 }
430 _ => None,
431 };
432
433 if let Some((copy_idx, new_dest)) = should_sink {
434 if let Some(out) = get_output_mut(&mut ops[i]) {
435 *out = new_dest;
436 }
437 ops.remove(copy_idx);
438 analysis = analyze_unique_outputs(ops);
439 continue;
440 }
441 i += 1;
442 }
443}
444
445fn all_ones_mask(size_bytes: u32) -> u64 {
446 let bits = size_bytes.saturating_mul(8);
447 if bits >= 64 {
448 u64::MAX
449 } else {
450 u64::MAX >> (64 - bits)
451 }
452}
453
454fn mask_to_size(value: u64, size_bytes: u32) -> u64 {
455 value & all_ones_mask(size_bytes)
456}
457
458fn bits_for_size(size_bytes: u32) -> u32 {
459 size_bytes.saturating_mul(8)
460}
461
462fn foldable_width(size_bytes: u32) -> bool {
463 (1..=8).contains(&size_bytes)
464}
465
466fn signed_value(value: u64, size_bytes: u32) -> i64 {
467 let bits = bits_for_size(size_bytes);
468 if bits >= 64 {
469 value as i64
470 } else {
471 let shift = 64 - bits;
472 ((value << shift) as i64) >> shift
473 }
474}
475
476fn sign_extend_to_size(value: u64, input_size: u32, output_size: u32) -> u64 {
477 if !foldable_width(input_size) || !foldable_width(output_size) {
478 return value;
479 }
480 mask_to_size(signed_value(value, input_size) as u64, output_size)
481}
482
483fn const_fold_op(op: &PcodeOp) -> Option<(Varnode, u64)> {
484 match *op {
485 PcodeOp::IntZext { out, input } if input.space == AddressSpaceId::Const => {
486 fold_unary_const(out, input, |value, _| value)
487 }
488 PcodeOp::IntSext { out, input } if input.space == AddressSpaceId::Const => {
489 if foldable_width(input.size) && foldable_width(out.size) {
490 Some((out, sign_extend_to_size(input.offset, input.size, out.size)))
491 } else {
492 None
493 }
494 }
495 PcodeOp::IntNeg { out, input } if input.space == AddressSpaceId::Const => {
496 fold_unary_const(out, input, |value, size| {
497 value.wrapping_neg() & all_ones_mask(size)
498 })
499 }
500 PcodeOp::IntNot { out, input } if input.space == AddressSpaceId::Const => {
501 fold_unary_const(out, input, |value, size| !value & all_ones_mask(size))
502 }
503 PcodeOp::BoolNot { out, input } if input.space == AddressSpaceId::Const => {
504 fold_unary_const(out, input, |value, _| u64::from(value == 0))
505 }
506 PcodeOp::IntAdd { out, left, right }
507 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
508 {
509 fold_binary_const(out, left, right, |a, b, size| {
510 a.wrapping_add(b) & all_ones_mask(size)
511 })
512 }
513 PcodeOp::IntSub { out, left, right }
514 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
515 {
516 fold_binary_const(out, left, right, |a, b, size| {
517 a.wrapping_sub(b) & all_ones_mask(size)
518 })
519 }
520 PcodeOp::IntMult { out, left, right }
521 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
522 {
523 fold_binary_const(out, left, right, |a, b, size| {
524 a.wrapping_mul(b) & all_ones_mask(size)
525 })
526 }
527 PcodeOp::IntAnd { out, left, right }
528 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
529 {
530 fold_binary_const(out, left, right, |a, b, size| (a & b) & all_ones_mask(size))
531 }
532 PcodeOp::IntOr { out, left, right }
533 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
534 {
535 fold_binary_const(out, left, right, |a, b, size| (a | b) & all_ones_mask(size))
536 }
537 PcodeOp::IntXor { out, left, right }
538 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
539 {
540 fold_binary_const(out, left, right, |a, b, size| (a ^ b) & all_ones_mask(size))
541 }
542 PcodeOp::IntLsl { out, left, right }
543 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
544 {
545 fold_shift_const(out, left, right, |value, shift, size| {
546 if shift >= bits_for_size(size) {
547 0
548 } else {
549 (value << shift) & all_ones_mask(size)
550 }
551 })
552 }
553 PcodeOp::IntLsr { out, left, right }
554 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
555 {
556 fold_shift_const(out, left, right, |value, shift, size| {
557 if shift >= bits_for_size(size) {
558 0
559 } else {
560 (value >> shift) & all_ones_mask(size)
561 }
562 })
563 }
564 PcodeOp::IntAsr { out, left, right }
565 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
566 {
567 fold_shift_const(out, left, right, |value, shift, size| {
568 if shift >= bits_for_size(size) {
569 u64::from(signed_value(value, size) < 0) * all_ones_mask(size)
570 } else {
571 mask_to_size((signed_value(value, size) >> shift) as u64, size)
572 }
573 })
574 }
575 PcodeOp::IntEq { out, left, right }
576 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
577 {
578 fold_compare_const(out, left, right, |a, b, _| a == b)
579 }
580 PcodeOp::IntNotEq { out, left, right }
581 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
582 {
583 fold_compare_const(out, left, right, |a, b, _| a != b)
584 }
585 PcodeOp::IntLess { out, left, right }
586 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
587 {
588 fold_compare_const(out, left, right, |a, b, _| a < b)
589 }
590 PcodeOp::IntLessEq { out, left, right }
591 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
592 {
593 fold_compare_const(out, left, right, |a, b, _| a <= b)
594 }
595 PcodeOp::IntSLess { out, left, right }
596 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
597 {
598 fold_compare_const(out, left, right, |a, b, size| {
599 signed_value(a, size) < signed_value(b, size)
600 })
601 }
602 PcodeOp::IntSLessEq { out, left, right }
603 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
604 {
605 fold_compare_const(out, left, right, |a, b, size| {
606 signed_value(a, size) <= signed_value(b, size)
607 })
608 }
609 PcodeOp::BoolAnd { out, left, right }
610 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
611 {
612 fold_binary_const(out, left, right, |a, b, _| u64::from(a != 0 && b != 0))
613 }
614 PcodeOp::BoolOr { out, left, right }
615 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
616 {
617 fold_binary_const(out, left, right, |a, b, _| u64::from(a != 0 || b != 0))
618 }
619 PcodeOp::BoolXor { out, left, right }
620 if left.space == AddressSpaceId::Const && right.space == AddressSpaceId::Const =>
621 {
622 fold_binary_const(out, left, right, |a, b, _| u64::from((a != 0) ^ (b != 0)))
623 }
624 _ => None,
625 }
626}
627
628fn fold_unary_const(
629 out: Varnode,
630 input: Varnode,
631 f: impl FnOnce(u64, u32) -> u64,
632) -> Option<(Varnode, u64)> {
633 if !foldable_width(out.size) || !foldable_width(input.size) {
634 return None;
635 }
636 Some((
637 out,
638 mask_to_size(
639 f(mask_to_size(input.offset, input.size), input.size),
640 out.size,
641 ),
642 ))
643}
644
645fn fold_binary_const(
646 out: Varnode,
647 left: Varnode,
648 right: Varnode,
649 f: impl FnOnce(u64, u64, u32) -> u64,
650) -> Option<(Varnode, u64)> {
651 if !foldable_width(out.size) || !foldable_width(left.size) || !foldable_width(right.size) {
652 return None;
653 }
654 let size = left.size.max(right.size);
655 let value = f(
656 mask_to_size(left.offset, left.size),
657 mask_to_size(right.offset, right.size),
658 size,
659 );
660 Some((out, mask_to_size(value, out.size)))
661}
662
663fn fold_shift_const(
664 out: Varnode,
665 left: Varnode,
666 right: Varnode,
667 f: impl FnOnce(u64, u32, u32) -> u64,
668) -> Option<(Varnode, u64)> {
669 if !foldable_width(out.size) || !foldable_width(left.size) || !foldable_width(right.size) {
670 return None;
671 }
672 let shift = right.offset.min(u32::MAX as u64) as u32;
673 Some((
674 out,
675 mask_to_size(
676 f(mask_to_size(left.offset, left.size), shift, left.size),
677 out.size,
678 ),
679 ))
680}
681
682fn fold_compare_const(
683 out: Varnode,
684 left: Varnode,
685 right: Varnode,
686 f: impl FnOnce(u64, u64, u32) -> bool,
687) -> Option<(Varnode, u64)> {
688 if !foldable_width(out.size) || !foldable_width(left.size) || !foldable_width(right.size) {
689 return None;
690 }
691 let size = left.size.max(right.size);
692 let result = f(
693 mask_to_size(left.offset, left.size),
694 mask_to_size(right.offset, right.size),
695 size,
696 );
697 Some((out, u64::from(result)))
698}
699
700#[derive(Clone, Copy)]
701struct UniqueOutputInfo {
702 future_reads: usize,
703 next_access: Option<NextAccess>,
704}
705
706#[derive(Clone, Copy)]
707enum NextAccess {
708 Read(usize),
709 Write,
710}
711
712fn analyze_unique_outputs(ops: &[PcodeOp]) -> Vec<Option<UniqueOutputInfo>> {
713 let mut future_reads = BTreeMap::<Varnode, usize>::new();
714 let mut next_access = BTreeMap::<Varnode, NextAccess>::new();
715 let mut result = vec![None; ops.len()];
716
717 let mut cbranch_protected = BTreeSet::<Varnode>::new();
723 {
724 let cbranch_indices: Vec<usize> = ops.iter().enumerate()
726 .filter(|(_, op)| matches!(op, PcodeOp::CBranch { dest, .. } if dest.space == AddressSpaceId::Const))
727 .map(|(i, _)| i)
728 .collect();
729 for &cb_idx in &cbranch_indices {
730 let mut before = BTreeSet::new();
732 let mut after = BTreeSet::new();
733 for (i, op) in ops.iter().enumerate() {
734 if let Some(out) = get_output(op) {
735 if out.space == AddressSpaceId::Unique {
736 if i < cb_idx {
737 before.insert(out);
738 } else if i > cb_idx {
739 after.insert(out);
740 }
741 }
742 }
743 }
744 for v in before.intersection(&after) {
745 cbranch_protected.insert(*v);
746 }
747 }
748 }
749
750 for (idx, op) in ops.iter().enumerate().rev() {
751 if let Some(out) = get_output(op).filter(|out| out.space == AddressSpaceId::Unique) {
752 if cbranch_protected.contains(&out) {
753 result[idx] = None;
756 } else {
757 result[idx] = Some(UniqueOutputInfo {
758 future_reads: future_reads.get(&out).copied().unwrap_or(0),
759 next_access: next_access.get(&out).copied(),
760 });
761 future_reads.remove(&out);
762 next_access.insert(out, NextAccess::Write);
763 }
764 }
765
766 visit_reads(op, &mut |v| {
767 if v.space == AddressSpaceId::Unique {
768 *future_reads.entry(*v).or_insert(0) += 1;
769 next_access.insert(*v, NextAccess::Read(idx));
770 }
771 });
772 }
773
774 result
775}
776
777pub fn get_output(op: &PcodeOp) -> Option<Varnode> {
778 match op {
779 PcodeOp::Copy { out, .. }
780 | PcodeOp::Load { out, .. }
781 | PcodeOp::Subpiece { out, .. }
782 | PcodeOp::IntNeg { out, .. }
783 | PcodeOp::IntNot { out, .. }
784 | PcodeOp::IntZext { out, .. }
785 | PcodeOp::IntSext { out, .. }
786 | PcodeOp::BoolNot { out, .. }
787 | PcodeOp::FloatNeg { out, .. }
788 | PcodeOp::FloatAbs { out, .. }
789 | PcodeOp::FloatSqrt { out, .. }
790 | PcodeOp::FloatNan { out, .. }
791 | PcodeOp::Int2Float { out, .. }
792 | PcodeOp::Float2Float { out, .. }
793 | PcodeOp::Trunc { out, .. }
794 | PcodeOp::FloatCeil { out, .. }
795 | PcodeOp::FloatFloor { out, .. }
796 | PcodeOp::FloatRound { out, .. }
797 | PcodeOp::Popcount { out, .. }
798 | PcodeOp::Lzcount { out, .. }
799 | PcodeOp::IntAdd { out, .. }
800 | PcodeOp::IntSub { out, .. }
801 | PcodeOp::IntMult { out, .. }
802 | PcodeOp::IntDiv { out, .. }
803 | PcodeOp::IntSDiv { out, .. }
804 | PcodeOp::IntRem { out, .. }
805 | PcodeOp::IntSRem { out, .. }
806 | PcodeOp::IntEq { out, .. }
807 | PcodeOp::IntNotEq { out, .. }
808 | PcodeOp::IntLess { out, .. }
809 | PcodeOp::IntLessEq { out, .. }
810 | PcodeOp::IntSLess { out, .. }
811 | PcodeOp::IntSLessEq { out, .. }
812 | PcodeOp::IntAnd { out, .. }
813 | PcodeOp::IntOr { out, .. }
814 | PcodeOp::IntXor { out, .. }
815 | PcodeOp::IntLsl { out, .. }
816 | PcodeOp::IntLsr { out, .. }
817 | PcodeOp::IntAsr { out, .. }
818 | PcodeOp::IntCarry { out, .. }
819 | PcodeOp::IntSCarry { out, .. }
820 | PcodeOp::IntSBorrow { out, .. }
821 | PcodeOp::BoolAnd { out, .. }
822 | PcodeOp::BoolOr { out, .. }
823 | PcodeOp::BoolXor { out, .. }
824 | PcodeOp::FloatAdd { out, .. }
825 | PcodeOp::FloatSub { out, .. }
826 | PcodeOp::FloatMult { out, .. }
827 | PcodeOp::FloatDiv { out, .. }
828 | PcodeOp::FloatEq { out, .. }
829 | PcodeOp::FloatNotEq { out, .. }
830 | PcodeOp::FloatLess { out, .. }
831 | PcodeOp::FloatLessEq { out, .. } => Some(*out),
832 _ => None,
833 }
834}
835
836fn get_output_mut(op: &mut PcodeOp) -> Option<&mut Varnode> {
837 match op {
838 PcodeOp::Copy { out, .. }
839 | PcodeOp::Load { out, .. }
840 | PcodeOp::Subpiece { out, .. }
841 | PcodeOp::IntNeg { out, .. }
842 | PcodeOp::IntNot { out, .. }
843 | PcodeOp::IntZext { out, .. }
844 | PcodeOp::IntSext { out, .. }
845 | PcodeOp::BoolNot { out, .. }
846 | PcodeOp::FloatNeg { out, .. }
847 | PcodeOp::FloatAbs { out, .. }
848 | PcodeOp::FloatSqrt { out, .. }
849 | PcodeOp::FloatNan { out, .. }
850 | PcodeOp::Int2Float { out, .. }
851 | PcodeOp::Float2Float { out, .. }
852 | PcodeOp::Trunc { out, .. }
853 | PcodeOp::FloatCeil { out, .. }
854 | PcodeOp::FloatFloor { out, .. }
855 | PcodeOp::FloatRound { out, .. }
856 | PcodeOp::Popcount { out, .. }
857 | PcodeOp::Lzcount { out, .. }
858 | PcodeOp::IntAdd { out, .. }
859 | PcodeOp::IntSub { out, .. }
860 | PcodeOp::IntMult { out, .. }
861 | PcodeOp::IntDiv { out, .. }
862 | PcodeOp::IntSDiv { out, .. }
863 | PcodeOp::IntRem { out, .. }
864 | PcodeOp::IntSRem { out, .. }
865 | PcodeOp::IntEq { out, .. }
866 | PcodeOp::IntNotEq { out, .. }
867 | PcodeOp::IntLess { out, .. }
868 | PcodeOp::IntLessEq { out, .. }
869 | PcodeOp::IntSLess { out, .. }
870 | PcodeOp::IntSLessEq { out, .. }
871 | PcodeOp::IntAnd { out, .. }
872 | PcodeOp::IntOr { out, .. }
873 | PcodeOp::IntXor { out, .. }
874 | PcodeOp::IntLsl { out, .. }
875 | PcodeOp::IntLsr { out, .. }
876 | PcodeOp::IntAsr { out, .. }
877 | PcodeOp::IntCarry { out, .. }
878 | PcodeOp::IntSCarry { out, .. }
879 | PcodeOp::IntSBorrow { out, .. }
880 | PcodeOp::BoolAnd { out, .. }
881 | PcodeOp::BoolOr { out, .. }
882 | PcodeOp::BoolXor { out, .. }
883 | PcodeOp::FloatAdd { out, .. }
884 | PcodeOp::FloatSub { out, .. }
885 | PcodeOp::FloatMult { out, .. }
886 | PcodeOp::FloatDiv { out, .. }
887 | PcodeOp::FloatEq { out, .. }
888 | PcodeOp::FloatNotEq { out, .. }
889 | PcodeOp::FloatLess { out, .. }
890 | PcodeOp::FloatLessEq { out, .. } => Some(out),
891 _ => None,
892 }
893}
894
895pub fn writes_to(op: &PcodeOp, target: &Varnode) -> bool {
896 match op {
897 PcodeOp::Copy { out, .. }
898 | PcodeOp::Load { out, .. }
899 | PcodeOp::Subpiece { out, .. }
900 | PcodeOp::IntNeg { out, .. }
901 | PcodeOp::IntNot { out, .. }
902 | PcodeOp::IntZext { out, .. }
903 | PcodeOp::IntSext { out, .. }
904 | PcodeOp::BoolNot { out, .. }
905 | PcodeOp::FloatNeg { out, .. }
906 | PcodeOp::FloatAbs { out, .. }
907 | PcodeOp::FloatSqrt { out, .. }
908 | PcodeOp::FloatNan { out, .. }
909 | PcodeOp::Int2Float { out, .. }
910 | PcodeOp::Float2Float { out, .. }
911 | PcodeOp::Trunc { out, .. }
912 | PcodeOp::FloatCeil { out, .. }
913 | PcodeOp::FloatFloor { out, .. }
914 | PcodeOp::FloatRound { out, .. }
915 | PcodeOp::Popcount { out, .. }
916 | PcodeOp::Lzcount { out, .. }
917 | PcodeOp::IntAdd { out, .. }
918 | PcodeOp::IntSub { out, .. }
919 | PcodeOp::IntMult { out, .. }
920 | PcodeOp::IntDiv { out, .. }
921 | PcodeOp::IntSDiv { out, .. }
922 | PcodeOp::IntRem { out, .. }
923 | PcodeOp::IntSRem { out, .. }
924 | PcodeOp::IntEq { out, .. }
925 | PcodeOp::IntNotEq { out, .. }
926 | PcodeOp::IntLess { out, .. }
927 | PcodeOp::IntLessEq { out, .. }
928 | PcodeOp::IntSLess { out, .. }
929 | PcodeOp::IntSLessEq { out, .. }
930 | PcodeOp::IntAnd { out, .. }
931 | PcodeOp::IntOr { out, .. }
932 | PcodeOp::IntXor { out, .. }
933 | PcodeOp::IntLsl { out, .. }
934 | PcodeOp::IntLsr { out, .. }
935 | PcodeOp::IntAsr { out, .. }
936 | PcodeOp::IntCarry { out, .. }
937 | PcodeOp::IntSCarry { out, .. }
938 | PcodeOp::IntSBorrow { out, .. }
939 | PcodeOp::BoolAnd { out, .. }
940 | PcodeOp::BoolOr { out, .. }
941 | PcodeOp::BoolXor { out, .. }
942 | PcodeOp::FloatAdd { out, .. }
943 | PcodeOp::FloatSub { out, .. }
944 | PcodeOp::FloatMult { out, .. }
945 | PcodeOp::FloatDiv { out, .. }
946 | PcodeOp::FloatEq { out, .. }
947 | PcodeOp::FloatNotEq { out, .. }
948 | PcodeOp::FloatLess { out, .. }
949 | PcodeOp::FloatLessEq { out, .. } => out == target,
950 PcodeOp::CallOther { out: Some(out), .. } => out == target,
951 _ => false,
952 }
953}
954
955pub fn reads_varnode(op: &PcodeOp, target: &Varnode) -> bool {
957 count_reads(op, target) > 0
958}
959
960pub fn count_reads(op: &PcodeOp, target: &Varnode) -> usize {
961 let mut n = 0;
962 visit_reads(op, &mut |v| {
963 if v == target {
964 n += 1
965 }
966 });
967 n
968}
969
970fn replace_reads(op: &mut PcodeOp, target: &Varnode, replacement: &Varnode) -> bool {
971 let mut found = false;
972 visit_reads_mut(op, &mut |v| {
973 if v == target {
974 *v = *replacement;
975 found = true;
976 }
977 });
978 found
979}
980
981pub fn visit_reads(op: &PcodeOp, f: &mut impl FnMut(&Varnode)) {
982 match op {
983 PcodeOp::Copy { input, .. } => f(input),
984 PcodeOp::Load { ptr, .. } => f(ptr),
985 PcodeOp::Store { ptr, val, .. } => {
986 f(ptr);
987 f(val);
988 }
989 PcodeOp::Branch { dest }
990 | PcodeOp::BranchInd { dest }
991 | PcodeOp::Call { dest }
992 | PcodeOp::CallInd { dest }
993 | PcodeOp::Return { dest } => f(dest),
994 PcodeOp::CBranch { dest, cond } => {
995 f(dest);
996 f(cond);
997 }
998 PcodeOp::Subpiece { input, .. } => f(input),
999 PcodeOp::IntNeg { input, .. }
1000 | PcodeOp::IntNot { input, .. }
1001 | PcodeOp::IntZext { input, .. }
1002 | PcodeOp::IntSext { input, .. }
1003 | PcodeOp::BoolNot { input, .. }
1004 | PcodeOp::FloatNeg { input, .. }
1005 | PcodeOp::FloatAbs { input, .. }
1006 | PcodeOp::FloatSqrt { input, .. }
1007 | PcodeOp::FloatNan { input, .. }
1008 | PcodeOp::Int2Float { input, .. }
1009 | PcodeOp::Float2Float { input, .. }
1010 | PcodeOp::Trunc { input, .. }
1011 | PcodeOp::FloatCeil { input, .. }
1012 | PcodeOp::FloatFloor { input, .. }
1013 | PcodeOp::FloatRound { input, .. }
1014 | PcodeOp::Popcount { input, .. }
1015 | PcodeOp::Lzcount { input, .. } => f(input),
1016 PcodeOp::IntAdd { left, right, .. }
1017 | PcodeOp::IntSub { left, right, .. }
1018 | PcodeOp::IntMult { left, right, .. }
1019 | PcodeOp::IntDiv { left, right, .. }
1020 | PcodeOp::IntSDiv { left, right, .. }
1021 | PcodeOp::IntRem { left, right, .. }
1022 | PcodeOp::IntSRem { left, right, .. }
1023 | PcodeOp::IntEq { left, right, .. }
1024 | PcodeOp::IntNotEq { left, right, .. }
1025 | PcodeOp::IntLess { left, right, .. }
1026 | PcodeOp::IntLessEq { left, right, .. }
1027 | PcodeOp::IntSLess { left, right, .. }
1028 | PcodeOp::IntSLessEq { left, right, .. }
1029 | PcodeOp::IntAnd { left, right, .. }
1030 | PcodeOp::IntOr { left, right, .. }
1031 | PcodeOp::IntXor { left, right, .. }
1032 | PcodeOp::IntLsl { left, right, .. }
1033 | PcodeOp::IntLsr { left, right, .. }
1034 | PcodeOp::IntAsr { left, right, .. }
1035 | PcodeOp::IntCarry { left, right, .. }
1036 | PcodeOp::IntSCarry { left, right, .. }
1037 | PcodeOp::IntSBorrow { left, right, .. }
1038 | PcodeOp::BoolAnd { left, right, .. }
1039 | PcodeOp::BoolOr { left, right, .. }
1040 | PcodeOp::BoolXor { left, right, .. }
1041 | PcodeOp::FloatAdd { left, right, .. }
1042 | PcodeOp::FloatSub { left, right, .. }
1043 | PcodeOp::FloatMult { left, right, .. }
1044 | PcodeOp::FloatDiv { left, right, .. }
1045 | PcodeOp::FloatEq { left, right, .. }
1046 | PcodeOp::FloatNotEq { left, right, .. }
1047 | PcodeOp::FloatLess { left, right, .. }
1048 | PcodeOp::FloatLessEq { left, right, .. } => {
1049 f(left);
1050 f(right);
1051 }
1052 PcodeOp::CallOther { inputs, .. } => {
1053 for v in inputs {
1054 f(v);
1055 }
1056 }
1057 }
1058}
1059
1060fn visit_reads_mut(op: &mut PcodeOp, f: &mut impl FnMut(&mut Varnode)) {
1061 match op {
1062 PcodeOp::Copy { input, .. } => f(input),
1063 PcodeOp::Load { ptr, .. } => f(ptr),
1064 PcodeOp::Store { ptr, val, .. } => {
1065 f(ptr);
1066 f(val);
1067 }
1068 PcodeOp::Branch { dest }
1069 | PcodeOp::BranchInd { dest }
1070 | PcodeOp::Call { dest }
1071 | PcodeOp::CallInd { dest }
1072 | PcodeOp::Return { dest } => f(dest),
1073 PcodeOp::CBranch { dest, cond } => {
1074 f(dest);
1075 f(cond);
1076 }
1077 PcodeOp::Subpiece { input, .. } => f(input),
1078 PcodeOp::IntNeg { input, .. }
1079 | PcodeOp::IntNot { input, .. }
1080 | PcodeOp::IntZext { input, .. }
1081 | PcodeOp::IntSext { input, .. }
1082 | PcodeOp::BoolNot { input, .. }
1083 | PcodeOp::FloatNeg { input, .. }
1084 | PcodeOp::FloatAbs { input, .. }
1085 | PcodeOp::FloatSqrt { input, .. }
1086 | PcodeOp::FloatNan { input, .. }
1087 | PcodeOp::Int2Float { input, .. }
1088 | PcodeOp::Float2Float { input, .. }
1089 | PcodeOp::Trunc { input, .. }
1090 | PcodeOp::FloatCeil { input, .. }
1091 | PcodeOp::FloatFloor { input, .. }
1092 | PcodeOp::FloatRound { input, .. }
1093 | PcodeOp::Popcount { input, .. }
1094 | PcodeOp::Lzcount { input, .. } => f(input),
1095 PcodeOp::IntAdd { left, right, .. }
1096 | PcodeOp::IntSub { left, right, .. }
1097 | PcodeOp::IntMult { left, right, .. }
1098 | PcodeOp::IntDiv { left, right, .. }
1099 | PcodeOp::IntSDiv { left, right, .. }
1100 | PcodeOp::IntRem { left, right, .. }
1101 | PcodeOp::IntSRem { left, right, .. }
1102 | PcodeOp::IntEq { left, right, .. }
1103 | PcodeOp::IntNotEq { left, right, .. }
1104 | PcodeOp::IntLess { left, right, .. }
1105 | PcodeOp::IntLessEq { left, right, .. }
1106 | PcodeOp::IntSLess { left, right, .. }
1107 | PcodeOp::IntSLessEq { left, right, .. }
1108 | PcodeOp::IntAnd { left, right, .. }
1109 | PcodeOp::IntOr { left, right, .. }
1110 | PcodeOp::IntXor { left, right, .. }
1111 | PcodeOp::IntLsl { left, right, .. }
1112 | PcodeOp::IntLsr { left, right, .. }
1113 | PcodeOp::IntAsr { left, right, .. }
1114 | PcodeOp::IntCarry { left, right, .. }
1115 | PcodeOp::IntSCarry { left, right, .. }
1116 | PcodeOp::IntSBorrow { left, right, .. }
1117 | PcodeOp::BoolAnd { left, right, .. }
1118 | PcodeOp::BoolOr { left, right, .. }
1119 | PcodeOp::BoolXor { left, right, .. }
1120 | PcodeOp::FloatAdd { left, right, .. }
1121 | PcodeOp::FloatSub { left, right, .. }
1122 | PcodeOp::FloatMult { left, right, .. }
1123 | PcodeOp::FloatDiv { left, right, .. }
1124 | PcodeOp::FloatEq { left, right, .. }
1125 | PcodeOp::FloatNotEq { left, right, .. }
1126 | PcodeOp::FloatLess { left, right, .. }
1127 | PcodeOp::FloatLessEq { left, right, .. } => {
1128 f(left);
1129 f(right);
1130 }
1131 PcodeOp::CallOther { inputs, .. } => {
1132 for v in inputs {
1133 f(v);
1134 }
1135 }
1136 }
1137}
1138
1139#[derive(Debug, Clone, PartialEq, Eq)]
1144pub enum PcodeOp {
1145 Copy {
1147 out: Varnode,
1148 input: Varnode,
1149 },
1150 Load {
1151 out: Varnode,
1152 space: AddressSpaceId,
1153 ptr: Varnode,
1154 },
1155 Store {
1156 space: AddressSpaceId,
1157 ptr: Varnode,
1158 val: Varnode,
1159 },
1160
1161 Branch {
1163 dest: Varnode,
1164 },
1165 CBranch {
1166 dest: Varnode,
1167 cond: Varnode,
1168 },
1169 BranchInd {
1170 dest: Varnode,
1171 },
1172 Call {
1173 dest: Varnode,
1174 },
1175 CallInd {
1176 dest: Varnode,
1177 },
1178 Return {
1179 dest: Varnode,
1180 },
1181
1182 IntAdd {
1184 out: Varnode,
1185 left: Varnode,
1186 right: Varnode,
1187 },
1188 IntSub {
1189 out: Varnode,
1190 left: Varnode,
1191 right: Varnode,
1192 },
1193 IntMult {
1194 out: Varnode,
1195 left: Varnode,
1196 right: Varnode,
1197 },
1198 IntDiv {
1199 out: Varnode,
1200 left: Varnode,
1201 right: Varnode,
1202 },
1203 IntSDiv {
1204 out: Varnode,
1205 left: Varnode,
1206 right: Varnode,
1207 },
1208 IntRem {
1209 out: Varnode,
1210 left: Varnode,
1211 right: Varnode,
1212 },
1213 IntSRem {
1214 out: Varnode,
1215 left: Varnode,
1216 right: Varnode,
1217 },
1218 IntNeg {
1219 out: Varnode,
1220 input: Varnode,
1221 },
1222
1223 IntEq {
1225 out: Varnode,
1226 left: Varnode,
1227 right: Varnode,
1228 },
1229 IntNotEq {
1230 out: Varnode,
1231 left: Varnode,
1232 right: Varnode,
1233 },
1234 IntLess {
1235 out: Varnode,
1236 left: Varnode,
1237 right: Varnode,
1238 },
1239 IntLessEq {
1240 out: Varnode,
1241 left: Varnode,
1242 right: Varnode,
1243 },
1244 IntSLess {
1245 out: Varnode,
1246 left: Varnode,
1247 right: Varnode,
1248 },
1249 IntSLessEq {
1250 out: Varnode,
1251 left: Varnode,
1252 right: Varnode,
1253 },
1254
1255 IntAnd {
1257 out: Varnode,
1258 left: Varnode,
1259 right: Varnode,
1260 },
1261 IntOr {
1262 out: Varnode,
1263 left: Varnode,
1264 right: Varnode,
1265 },
1266 IntXor {
1267 out: Varnode,
1268 left: Varnode,
1269 right: Varnode,
1270 },
1271 IntNot {
1272 out: Varnode,
1273 input: Varnode,
1274 },
1275
1276 IntLsl {
1278 out: Varnode,
1279 left: Varnode,
1280 right: Varnode,
1281 },
1282 IntLsr {
1283 out: Varnode,
1284 left: Varnode,
1285 right: Varnode,
1286 },
1287 IntAsr {
1288 out: Varnode,
1289 left: Varnode,
1290 right: Varnode,
1291 },
1292
1293 IntZext {
1295 out: Varnode,
1296 input: Varnode,
1297 },
1298 IntSext {
1299 out: Varnode,
1300 input: Varnode,
1301 },
1302 Subpiece {
1303 out: Varnode,
1304 input: Varnode,
1305 lsb: u32,
1306 },
1307
1308 IntCarry {
1310 out: Varnode,
1311 left: Varnode,
1312 right: Varnode,
1313 },
1314 IntSCarry {
1315 out: Varnode,
1316 left: Varnode,
1317 right: Varnode,
1318 },
1319 IntSBorrow {
1320 out: Varnode,
1321 left: Varnode,
1322 right: Varnode,
1323 },
1324
1325 BoolAnd {
1327 out: Varnode,
1328 left: Varnode,
1329 right: Varnode,
1330 },
1331 BoolOr {
1332 out: Varnode,
1333 left: Varnode,
1334 right: Varnode,
1335 },
1336 BoolXor {
1337 out: Varnode,
1338 left: Varnode,
1339 right: Varnode,
1340 },
1341 BoolNot {
1342 out: Varnode,
1343 input: Varnode,
1344 },
1345
1346 FloatAdd {
1348 out: Varnode,
1349 left: Varnode,
1350 right: Varnode,
1351 },
1352 FloatSub {
1353 out: Varnode,
1354 left: Varnode,
1355 right: Varnode,
1356 },
1357 FloatMult {
1358 out: Varnode,
1359 left: Varnode,
1360 right: Varnode,
1361 },
1362 FloatDiv {
1363 out: Varnode,
1364 left: Varnode,
1365 right: Varnode,
1366 },
1367 FloatNeg {
1368 out: Varnode,
1369 input: Varnode,
1370 },
1371 FloatAbs {
1372 out: Varnode,
1373 input: Varnode,
1374 },
1375 FloatSqrt {
1376 out: Varnode,
1377 input: Varnode,
1378 },
1379
1380 FloatEq {
1382 out: Varnode,
1383 left: Varnode,
1384 right: Varnode,
1385 },
1386 FloatNotEq {
1387 out: Varnode,
1388 left: Varnode,
1389 right: Varnode,
1390 },
1391 FloatLess {
1392 out: Varnode,
1393 left: Varnode,
1394 right: Varnode,
1395 },
1396 FloatLessEq {
1397 out: Varnode,
1398 left: Varnode,
1399 right: Varnode,
1400 },
1401 FloatNan {
1402 out: Varnode,
1403 input: Varnode,
1404 },
1405
1406 Int2Float {
1408 out: Varnode,
1409 input: Varnode,
1410 },
1411 Float2Float {
1412 out: Varnode,
1413 input: Varnode,
1414 },
1415 Trunc {
1416 out: Varnode,
1417 input: Varnode,
1418 },
1419 FloatCeil {
1420 out: Varnode,
1421 input: Varnode,
1422 },
1423 FloatFloor {
1424 out: Varnode,
1425 input: Varnode,
1426 },
1427 FloatRound {
1428 out: Varnode,
1429 input: Varnode,
1430 },
1431
1432 Popcount {
1434 out: Varnode,
1435 input: Varnode,
1436 },
1437 Lzcount {
1438 out: Varnode,
1439 input: Varnode,
1440 },
1441
1442 CallOther {
1445 out: Option<Varnode>,
1446 func_id: u64,
1447 inputs: Vec<Varnode>,
1448 },
1449}
1450
1451#[cfg(test)]
1452mod tests {
1453 use super::*;
1454
1455 #[test]
1456 fn optimize_intand_all_ones_wide_size_no_panic() {
1457 let mut ops = vec![
1458 PcodeOp::IntAnd {
1459 out: Varnode::unique(0, 16),
1460 left: Varnode::register(0, 16),
1461 right: Varnode::constant(u64::MAX, 16),
1462 },
1463 PcodeOp::Copy {
1464 out: Varnode::register(16, 16),
1465 input: Varnode::unique(0, 16),
1466 },
1467 ];
1468
1469 optimize(&mut ops);
1470
1471 assert!(matches!(
1472 ops.as_slice(),
1473 [PcodeOp::Copy { out, input }]
1474 if *out == Varnode::register(16, 16)
1475 && *input == Varnode::register(0, 16)
1476 ));
1477 }
1478
1479 #[test]
1480 fn optimize_folds_constant_integer_ops() {
1481 let mut ops = vec![
1482 PcodeOp::IntAdd {
1483 out: Varnode::unique(0, 4),
1484 left: Varnode::constant(0xffff_ffff, 4),
1485 right: Varnode::constant(2, 4),
1486 },
1487 PcodeOp::IntXor {
1488 out: Varnode::unique(8, 4),
1489 left: Varnode::unique(0, 4),
1490 right: Varnode::constant(0x10, 4),
1491 },
1492 PcodeOp::Copy {
1493 out: Varnode::register(0, 4),
1494 input: Varnode::unique(8, 4),
1495 },
1496 ];
1497
1498 optimize(&mut ops);
1499
1500 assert_eq!(
1501 ops,
1502 vec![PcodeOp::Copy {
1503 out: Varnode::register(0, 4),
1504 input: Varnode::constant(0x11, 4),
1505 }]
1506 );
1507 }
1508
1509 #[test]
1510 fn optimize_folds_constant_comparisons_and_bool_ops() {
1511 let mut ops = vec![
1512 PcodeOp::IntSLess {
1513 out: Varnode::unique(0, 1),
1514 left: Varnode::constant(0xff, 1),
1515 right: Varnode::constant(1, 1),
1516 },
1517 PcodeOp::BoolNot {
1518 out: Varnode::unique(1, 1),
1519 input: Varnode::unique(0, 1),
1520 },
1521 PcodeOp::Copy {
1522 out: Varnode::register(0, 1),
1523 input: Varnode::unique(1, 1),
1524 },
1525 ];
1526
1527 optimize(&mut ops);
1528
1529 assert_eq!(
1530 ops,
1531 vec![PcodeOp::Copy {
1532 out: Varnode::register(0, 1),
1533 input: Varnode::constant(0, 1),
1534 }]
1535 );
1536 }
1537
1538 #[test]
1539 fn optimize_folds_constant_shifts() {
1540 let mut ops = vec![
1541 PcodeOp::IntAsr {
1542 out: Varnode::unique(0, 1),
1543 left: Varnode::constant(0x80, 1),
1544 right: Varnode::constant(4, 1),
1545 },
1546 PcodeOp::Copy {
1547 out: Varnode::register(0, 1),
1548 input: Varnode::unique(0, 1),
1549 },
1550 ];
1551
1552 optimize(&mut ops);
1553
1554 assert_eq!(
1555 ops,
1556 vec![PcodeOp::Copy {
1557 out: Varnode::register(0, 1),
1558 input: Varnode::constant(0xf8, 1),
1559 }]
1560 );
1561 }
1562
1563 #[test]
1564 fn optimize_sign_extend_constant_masks_to_output_size() {
1565 let mut ops = vec![
1566 PcodeOp::IntSext {
1567 out: Varnode::unique(0, 2),
1568 input: Varnode::constant(0x80, 1),
1569 },
1570 PcodeOp::Copy {
1571 out: Varnode::register(0, 2),
1572 input: Varnode::unique(0, 2),
1573 },
1574 ];
1575
1576 optimize(&mut ops);
1577
1578 assert_eq!(
1579 ops,
1580 vec![PcodeOp::Copy {
1581 out: Varnode::register(0, 2),
1582 input: Varnode::constant(0xff80, 2),
1583 }]
1584 );
1585 }
1586}