1use crate::{
9 Ast, AstNode, BinaryOperator, BitRangeFieldId, Builtin, Expression, ExpressionTy, Ident,
10 LabelOrNode, Load, LocalVarId, PCodeOpId, PcodeAst, Range, RangeParam, RegisterId, SPACE_CONST,
11 SpaceId, TableId, UnaryOperator,
12};
13use serde::{Deserialize, Serialize};
14use std::{
15 collections::{HashMap, HashSet},
16 fmt,
17};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub struct Varnode {
26 pub space: SpaceId,
28 pub offset: u64,
30 pub size: usize,
32}
33
34impl Varnode {
35 pub const fn new(space: SpaceId, offset: u64, size: usize) -> Self {
37 Self {
38 space,
39 offset,
40 size,
41 }
42 }
43
44 pub const fn constant(value: u64, size: usize) -> Self {
46 Self::new(SPACE_CONST, value, size)
47 }
48
49 pub fn is_constant(self) -> bool {
51 self.space == SPACE_CONST
52 }
53}
54
55#[repr(u8)]
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
63pub enum Opcode {
64 Copy = 1,
65 Load,
66 Store,
67 Branch,
68 CBranch,
69 BranchInd,
70 Call,
71 CallInd,
72 CallOther,
73 Return,
74 IntEqual,
75 IntNotEqual,
76 IntSLess,
77 IntSLessEqual,
78 IntLess,
79 IntLessEqual,
80 IntZext,
81 IntSext,
82 IntAdd,
83 IntSub,
84 IntCarry,
85 IntSCarry,
86 IntSBorrow,
87 Int2Comp,
88 IntNegate,
89 IntXor,
90 IntAnd,
91 IntOr,
92 IntLeft,
93 IntRight,
94 IntSRight,
95 IntMult,
96 IntDiv,
97 IntSDiv,
98 IntRem,
99 IntSRem,
100 BoolNegate,
101 BoolXor,
102 BoolAnd,
103 BoolOr,
104 FloatEqual,
105 FloatNotEqual,
106 FloatLess,
107 FloatLessEqual,
108 FloatNan = 46,
110 FloatAdd,
111 FloatDiv,
112 FloatMult,
113 FloatSub,
114 FloatNeg,
115 FloatAbs,
116 FloatSqrt,
117 FloatInt2Float,
118 FloatFloat2Float,
119 FloatTrunc,
120 FloatCeil,
121 FloatFloor,
122 FloatRound,
123 MultiEqual,
124 Indirect,
125 Piece,
126 SubPiece,
127 Cast,
128 PtrAdd,
129 PtrSub,
130 SegmentOp,
131 CpoolRef,
132 New,
133 Insert,
134 Extract,
135 PopCount,
136 LzCount,
137}
138
139impl Opcode {
140 pub const ALL: &[Self] = &[
142 Self::Copy,
143 Self::Load,
144 Self::Store,
145 Self::Branch,
146 Self::CBranch,
147 Self::BranchInd,
148 Self::Call,
149 Self::CallInd,
150 Self::CallOther,
151 Self::Return,
152 Self::IntEqual,
153 Self::IntNotEqual,
154 Self::IntSLess,
155 Self::IntSLessEqual,
156 Self::IntLess,
157 Self::IntLessEqual,
158 Self::IntZext,
159 Self::IntSext,
160 Self::IntAdd,
161 Self::IntSub,
162 Self::IntCarry,
163 Self::IntSCarry,
164 Self::IntSBorrow,
165 Self::Int2Comp,
166 Self::IntNegate,
167 Self::IntXor,
168 Self::IntAnd,
169 Self::IntOr,
170 Self::IntLeft,
171 Self::IntRight,
172 Self::IntSRight,
173 Self::IntMult,
174 Self::IntDiv,
175 Self::IntSDiv,
176 Self::IntRem,
177 Self::IntSRem,
178 Self::BoolNegate,
179 Self::BoolXor,
180 Self::BoolAnd,
181 Self::BoolOr,
182 Self::FloatEqual,
183 Self::FloatNotEqual,
184 Self::FloatLess,
185 Self::FloatLessEqual,
186 Self::FloatNan,
187 Self::FloatAdd,
188 Self::FloatDiv,
189 Self::FloatMult,
190 Self::FloatSub,
191 Self::FloatNeg,
192 Self::FloatAbs,
193 Self::FloatSqrt,
194 Self::FloatInt2Float,
195 Self::FloatFloat2Float,
196 Self::FloatTrunc,
197 Self::FloatCeil,
198 Self::FloatFloor,
199 Self::FloatRound,
200 Self::MultiEqual,
201 Self::Indirect,
202 Self::Piece,
203 Self::SubPiece,
204 Self::Cast,
205 Self::PtrAdd,
206 Self::PtrSub,
207 Self::SegmentOp,
208 Self::CpoolRef,
209 Self::New,
210 Self::Insert,
211 Self::Extract,
212 Self::PopCount,
213 Self::LzCount,
214 ];
215
216 pub const fn ghidra_id(self) -> u8 {
218 self as u8
219 }
220
221 pub const fn is_raw_instruction_op(self) -> bool {
227 !matches!(
228 self,
229 Self::MultiEqual
230 | Self::Indirect
231 | Self::Cast
232 | Self::PtrAdd
233 | Self::PtrSub
234 | Self::SegmentOp
235 | Self::Insert
236 | Self::Extract
237 )
238 }
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
243pub struct PcodeOp {
244 pub opcode: Opcode,
246 pub output: Option<Varnode>,
248 pub inputs: Vec<Varnode>,
250}
251
252impl PcodeOp {
253 pub fn new(opcode: Opcode, output: Option<Varnode>, inputs: Vec<Varnode>) -> Self {
255 Self {
256 opcode,
257 output,
258 inputs,
259 }
260 }
261}
262
263#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
265pub struct InstructionPcode {
266 pub ops: Vec<PcodeOp>,
268}
269
270impl InstructionPcode {
271 pub const fn new() -> Self {
273 Self { ops: Vec::new() }
274 }
275
276 pub fn is_empty(&self) -> bool {
278 self.ops.is_empty()
279 }
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
287pub struct BitRangeInfo {
288 pub storage: Varnode,
290 pub start: usize,
292 pub size: usize,
294}
295
296pub trait PcodeLoweringContext {
302 fn default_space(&self) -> SpaceId;
304 fn unique_space(&self) -> SpaceId;
306 fn register_varnode(&self, id: RegisterId) -> Option<Varnode>;
308 fn bitrange_info(&self, id: BitRangeFieldId) -> Option<BitRangeInfo>;
310 fn address_size(&self, space: SpaceId) -> Option<usize>;
312}
313
314#[derive(Debug, Clone, PartialEq, Eq)]
316pub enum PcodeLowerError {
317 UnknownSize,
319 ZeroSize,
321 CopySizeMismatch { input: usize, output: usize },
323 InputSizeMismatch {
325 operation: &'static str,
326 left: usize,
327 right: usize,
328 },
329 InvalidBooleanSize(usize),
331 AddressSizeMismatch { expected: usize, actual: usize },
333 StoreSizeMismatch { declared: usize, value: usize },
335 InvalidRange {
337 start: usize,
338 size: usize,
339 storage_bits: usize,
340 },
341 UniqueSpaceOverflow,
343 UnknownRegister(RegisterId),
345 UnresolvedIdentifier(&'static str),
347 UnresolvedSpace,
349 UnresolvedRangeParameter,
351 InternalNode(&'static str),
353 Unsupported(&'static str),
355 DuplicateLabel(Box<str>),
357 UnknownLabel(Box<str>),
359 InvalidDirectTarget,
361}
362
363impl fmt::Display for PcodeLowerError {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 match self {
366 Self::UnknownSize => write!(f, "p-code expression has no known byte width"),
367 Self::ZeroSize => write!(f, "p-code varnodes cannot have zero byte width"),
368 Self::CopySizeMismatch { input, output } => write!(
369 f,
370 "p-code COPY requires equal widths, got {input} and {output} bytes"
371 ),
372 Self::InputSizeMismatch {
373 operation,
374 left,
375 right,
376 } => write!(
377 f,
378 "p-code {operation} requires equal input widths, got {left} and {right} bytes"
379 ),
380 Self::InvalidBooleanSize(size) => {
381 write!(
382 f,
383 "p-code comparison and boolean outputs must be one byte, got {size}"
384 )
385 }
386 Self::AddressSizeMismatch { expected, actual } => write!(
387 f,
388 "p-code memory pointer requires {expected} bytes, got {actual}"
389 ),
390 Self::StoreSizeMismatch { declared, value } => write!(
391 f,
392 "p-code STORE declares {declared} bytes but receives {value}"
393 ),
394 Self::InvalidRange {
395 start,
396 size,
397 storage_bits,
398 } => write!(
399 f,
400 "invalid bit range [{start}, {size}] for {storage_bits}-bit storage"
401 ),
402 Self::UniqueSpaceOverflow => write!(f, "unique-space temporary allocation overflowed"),
403 Self::UnknownRegister(id) => write!(f, "unknown register {}", usize::from(*id)),
404 Self::UnresolvedIdentifier(kind) => {
405 write!(f, "unresolved {kind} reached p-code lowering")
406 }
407 Self::UnresolvedSpace => write!(f, "unresolved address space reached p-code lowering"),
408 Self::UnresolvedRangeParameter => {
409 write!(f, "unresolved range parameter reached p-code lowering")
410 }
411 Self::InternalNode(kind) => write!(f, "unexpanded {kind} reached p-code lowering"),
412 Self::Unsupported(what) => write!(f, "raw p-code lowering does not support {what}"),
413 Self::DuplicateLabel(label) => write!(f, "duplicate p-code label `{label}`"),
414 Self::UnknownLabel(label) => write!(f, "unknown p-code label `{label}`"),
415 Self::InvalidDirectTarget => write!(f, "invalid direct p-code branch target"),
416 }
417 }
418}
419
420impl std::error::Error for PcodeLowerError {}
421
422#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
428pub struct LabelId(u32);
429
430impl LabelId {
431 pub const fn index(self) -> usize {
433 self.0 as usize
434 }
435
436 pub const fn from_index(index: usize) -> Self {
438 Self(index as u32)
439 }
440}
441
442#[derive(Debug, Clone, Default)]
449pub struct PcodePlan {
450 local_sizes: HashMap<LocalVarId, usize>,
453 labels: Vec<Box<str>>,
454 terminal: Vec<bool>,
457 label_ids: HashMap<Box<str>, LabelId>,
458 direct_branches: Vec<u64>,
459 direct_calls: Vec<u64>,
460}
461
462impl PcodePlan {
463 pub fn labels(&self) -> &[Box<str>] {
465 &self.labels
466 }
467
468 pub fn is_terminal(&self, label: LabelId) -> bool {
473 self.terminal[label.index()]
474 }
475
476 pub fn direct_branches(&self) -> &[u64] {
478 &self.direct_branches
479 }
480
481 pub fn direct_calls(&self) -> &[u64] {
483 &self.direct_calls
484 }
485
486 pub fn declare_label(&mut self, label: &str) -> LabelId {
493 if let Some(id) = self.label_ids.get(label) {
494 return *id;
495 }
496 let id = LabelId(self.labels.len() as u32);
497 self.labels.push(Box::from(label));
498 self.terminal.push(false);
499 self.label_ids.insert(Box::from(label), id);
500 id
501 }
502
503 pub fn declare_direct_branch(&mut self, address: u64) {
505 if !self.direct_branches.contains(&address) {
506 self.direct_branches.push(address);
507 }
508 }
509
510 pub fn declare_direct_call(&mut self, address: u64) {
512 if !self.direct_calls.contains(&address) {
513 self.direct_calls.push(address);
514 }
515 }
516
517 fn label_id(&self, label: &str) -> Option<LabelId> {
518 self.label_ids.get(label).copied()
519 }
520}
521
522pub trait PcodeSink {
528 fn op(&mut self, opcode: Opcode, output: Option<Varnode>, inputs: &[Varnode]);
531
532 fn label(&mut self, label: LabelId);
535
536 fn branch_label(&mut self, opcode: Opcode, label: LabelId, condition: Option<Varnode>);
540}
541
542#[derive(Debug, Default)]
545struct Collector {
546 ops: Vec<PcodeOp>,
547 label_ops: HashMap<LabelId, usize>,
548 fixups: Vec<(usize, LabelId)>,
549}
550
551impl Collector {
552 fn finish(mut self, plan: &PcodePlan) -> Result<Vec<PcodeOp>, PcodeLowerError> {
553 for (op_index, label) in &self.fixups {
554 let target = *self
555 .label_ops
556 .get(label)
557 .ok_or_else(|| PcodeLowerError::UnknownLabel(plan.labels[label.index()].clone()))?;
558 let relative = i64::try_from(target)
559 .ok()
560 .and_then(|target| {
561 i64::try_from(*op_index)
562 .ok()
563 .and_then(|source| target.checked_sub(source))
564 })
565 .ok_or(PcodeLowerError::UniqueSpaceOverflow)?;
566 self.ops[*op_index].inputs[0] = Varnode::constant(relative as u64, 8);
567 }
568 Ok(self.ops)
569 }
570}
571
572impl PcodeSink for Collector {
573 fn op(&mut self, opcode: Opcode, output: Option<Varnode>, inputs: &[Varnode]) {
574 self.ops.push(PcodeOp::new(opcode, output, inputs.to_vec()));
575 }
576
577 fn label(&mut self, label: LabelId) {
578 self.label_ops.insert(label, self.ops.len());
579 }
580
581 fn branch_label(&mut self, opcode: Opcode, label: LabelId, condition: Option<Varnode>) {
582 let mut inputs = Vec::with_capacity(1 + usize::from(condition.is_some()));
583 inputs.push(Varnode::constant(0, 8));
584 inputs.extend(condition);
585 self.fixups.push((self.ops.len(), label));
586 self.ops.push(PcodeOp::new(opcode, None, inputs));
587 }
588}
589
590pub fn plan_instruction(
596 ast: &PcodeAst,
597 context: &impl PcodeLoweringContext,
598) -> Result<PcodePlan, PcodeLowerError> {
599 let mut planner = Planner {
600 context,
601 plan: PcodePlan::default(),
602 };
603 planner.plan(ast);
604 Ok(planner.plan)
605}
606
607pub trait Width: Copy + Eq + std::fmt::Debug {
616 fn fixed(size: usize) -> Self;
618
619 fn size(self) -> Option<usize>;
623
624 fn operand(_table: TableId) -> Option<Self> {
631 None
632 }
633}
634
635impl Width for usize {
636 fn fixed(size: usize) -> Self {
637 size
638 }
639
640 fn size(self) -> Option<usize> {
641 Some(self)
642 }
643}
644
645#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
647pub enum SymbolicWidth {
648 Fixed(usize),
650 SameAs(TableId),
652}
653
654impl Width for SymbolicWidth {
655 fn fixed(size: usize) -> Self {
656 Self::Fixed(size)
657 }
658
659 fn size(self) -> Option<usize> {
660 match self {
661 Self::Fixed(size) => Some(size),
662 Self::SameAs(_) => None,
663 }
664 }
665
666 fn operand(table: TableId) -> Option<Self> {
667 Some(Self::SameAs(table))
668 }
669}
670
671pub type LocalSizes = HashMap<LocalVarId, usize>;
673
674#[derive(Debug, Clone, Default)]
676pub struct BodyWidths<W> {
677 pub widths: HashMap<LocalVarId, W>,
679 pub unsized_locals: Vec<LocalVarId>,
685}
686
687impl<W> BodyWidths<W> {
688 pub fn is_complete(&self) -> bool {
690 self.unsized_locals.is_empty()
691 }
692}
693
694pub fn resolve_body_widths<S, W: Width>(
701 statements: &[Ast<S>],
702 context: &impl PcodeLoweringContext,
703) -> BodyWidths<W> {
704 let widths: HashMap<LocalVarId, W> = SizeInference::run(context, statements);
705 let mut used = Vec::new();
706 live_locals(statements, &mut used);
707 used.sort_unstable_by_key(|id| id.0);
708 used.dedup();
709 let unsized_locals = used
710 .into_iter()
711 .filter(|id| !widths.contains_key(id))
712 .collect();
713 BodyWidths {
714 widths,
715 unsized_locals,
716 }
717}
718
719fn live_locals<S>(statements: &[Ast<S>], out: &mut Vec<LocalVarId>) {
721 fn walk_expr<S>(value: &Expression<S>, out: &mut Vec<LocalVarId>) {
722 match &value.ty {
723 ExpressionTy::Ident(Ident::Named(id)) => out.push(*id),
724 ExpressionTy::Ident(_) | ExpressionTy::SizedInt { .. } => {}
725 ExpressionTy::Load(load) => walk_expr(&load.ptr, out),
726 ExpressionTy::Range(range) => walk_expr(&range.value, out),
727 ExpressionTy::SubPieceLsb { src, .. } | ExpressionTy::SubPieceMsb { src, .. } => {
728 walk_expr(src, out);
729 }
730 ExpressionTy::FunctionCall { args, .. }
731 | ExpressionTy::PcodeOp { args, .. }
732 | ExpressionTy::MacroCall { args, .. }
733 | ExpressionTy::DeferredCall { args, .. } => {
734 args.iter().for_each(|arg| walk_expr(arg, out));
735 }
736 ExpressionTy::Unop(unop) => walk_expr(&unop.e, out),
737 ExpressionTy::Binop(binop) => {
738 walk_expr(&binop.lhs, out);
739 walk_expr(&binop.rhs, out);
740 }
741 }
742 }
743 fn walk_target<S>(target: &LabelOrNode<S>, out: &mut Vec<LocalVarId>) {
744 if let LabelOrNode::Expr(value) = target {
745 walk_expr(value, out);
746 }
747 }
748 for statement in statements {
749 match &statement.ty {
750 AstNode::Assignment { lhs, rhs, .. } => {
751 if let Ident::Named(id) = lhs {
752 out.push(*id);
753 }
754 walk_expr(rhs, out);
755 }
756 AstNode::LoadAssignment { lhs, rhs, .. } => {
757 walk_expr(&lhs.ptr, out);
758 walk_expr(rhs, out);
759 }
760 AstNode::RangeAssignment { lhs, rhs, .. } => {
761 walk_expr(&lhs.value, out);
762 walk_expr(rhs, out);
763 }
764 AstNode::Branch { target } | AstNode::Call { target } => walk_target(target, out),
765 AstNode::ConditionalBranch { target, condition } => {
766 walk_target(target, out);
767 walk_expr(condition, out);
768 }
769 AstNode::BranchIndirect { target: value }
770 | AstNode::CallIndirect { target: value }
771 | AstNode::Return { target: value }
772 | AstNode::Expression(value)
773 | AstNode::Export(value) => walk_expr(value, out),
774 AstNode::Label(_)
775 | AstNode::Build(_)
776 | AstNode::DeferredBuild(_)
777 | AstNode::DelaySlot(_) => {}
778 }
779 }
780}
781
782pub fn infer_local_sizes<S, W: Width>(
794 statements: &[Ast<S>],
795 context: &impl PcodeLoweringContext,
796) -> HashMap<LocalVarId, W> {
797 SizeInference::run(context, statements)
798}
799
800pub fn plan_instruction_with(
806 ast: &PcodeAst,
807 context: &impl PcodeLoweringContext,
808 local_sizes: LocalSizes,
809) -> Result<PcodePlan, PcodeLowerError> {
810 let mut planner = Planner {
811 context,
812 plan: PcodePlan {
813 local_sizes,
814 ..PcodePlan::default()
815 },
816 };
817 planner.plan_statements(ast);
818 Ok(planner.plan)
819}
820
821pub fn emit_instruction(
827 ast: &PcodeAst,
828 context: &impl PcodeLoweringContext,
829 plan: &PcodePlan,
830 sink: &mut impl PcodeSink,
831) -> Result<(), PcodeLowerError> {
832 Lowerer::new(context, plan, sink).emit_all(ast)
833}
834
835pub fn lower_instruction(
846 ast: &PcodeAst,
847 context: &impl PcodeLoweringContext,
848) -> Result<InstructionPcode, PcodeLowerError> {
849 Ok(InstructionPcode {
850 ops: collect_ops(ast, context)?,
851 })
852}
853
854fn collect_ops(
855 ast: &PcodeAst,
856 context: &impl PcodeLoweringContext,
857) -> Result<Vec<PcodeOp>, PcodeLowerError> {
858 let plan = plan_instruction(ast, context)?;
859 let mut collector = Collector::default();
860 emit_instruction(ast, context, &plan, &mut collector)?;
861 collector.finish(&plan)
862}
863
864pub fn lower_instruction_into<R>(
871 ast: &PcodeAst,
872 context: &impl PcodeLoweringContext,
873 sink: impl FnOnce(&[PcodeOp]) -> R,
874) -> Result<R, PcodeLowerError> {
875 let ops = collect_ops(ast, context)?;
876 Ok(sink(&ops))
877}
878
879impl InstructionPcode {
880 pub fn lower(
882 ast: &PcodeAst,
883 context: &impl PcodeLoweringContext,
884 ) -> Result<Self, PcodeLowerError> {
885 lower_instruction(ast, context)
886 }
887}
888
889struct Lowerer<'a, 'p, 's, C: PcodeLoweringContext + ?Sized, S: PcodeSink + ?Sized> {
890 context: &'a C,
891 plan: &'p PcodePlan,
892 sink: &'s mut S,
893 locals: HashMap<LocalVarId, Varnode>,
894 defined_labels: HashSet<LabelId>,
895 next_unique: u64,
896}
897
898impl<'a, 'p, 's, C: PcodeLoweringContext + ?Sized, S: PcodeSink + ?Sized>
899 Lowerer<'a, 'p, 's, C, S>
900{
901 fn new(context: &'a C, plan: &'p PcodePlan, sink: &'s mut S) -> Self {
902 Self {
903 context,
904 plan,
905 sink,
906 locals: HashMap::new(),
907 defined_labels: HashSet::new(),
908 next_unique: 0,
909 }
910 }
911
912 fn emit_all(mut self, ast: &PcodeAst) -> Result<(), PcodeLowerError> {
913 for statement in &ast.statements {
914 self.lower_statement(&statement.ty)?;
915 }
916 Ok(())
917 }
918
919 fn emit(&mut self, opcode: Opcode, output: Option<Varnode>, inputs: &[Varnode]) {
920 self.sink.op(opcode, output, inputs);
921 }
922
923 fn lower_statement(&mut self, statement: &AstNode) -> Result<(), PcodeLowerError> {
924 match statement {
925 AstNode::Assignment {
926 lhs: Ident::BitRange(id),
927 rhs,
928 ..
929 } => {
930 let info = self
931 .context
932 .bitrange_info(*id)
933 .ok_or(PcodeLowerError::Unsupported("an unknown named bit range"))?;
934 self.insert_range(info.storage, info.start, info.size, rhs)?;
935 }
936 AstNode::Assignment { lhs, size, rhs } => {
937 let output =
938 self.storage_for_ident(lhs.clone(), size.or_else(|| self.expr_size(rhs)))?;
939 self.lower_expr(rhs, Some(output))?;
940 }
941 AstNode::LoadAssignment { lhs, rhs, .. } => self.lower_store(lhs, rhs)?,
942 AstNode::RangeAssignment { lhs, rhs, .. } => self.lower_range_assignment(lhs, rhs)?,
943 AstNode::Build(_) => return Err(PcodeLowerError::InternalNode("build statement")),
944 AstNode::DelaySlot(_) => {
945 return Err(PcodeLowerError::InternalNode("delay-slot directive"));
946 }
947 AstNode::DeferredBuild(_) => {
948 return Err(PcodeLowerError::InternalNode("deferred build statement"));
949 }
950 AstNode::Label(label) => {
951 let id = self.label_id(label)?;
952 if !self.defined_labels.insert(id) {
953 return Err(PcodeLowerError::DuplicateLabel(label.clone()));
954 }
955 self.sink.label(id);
956 }
957 AstNode::Branch { target } => self.lower_direct_flow(Opcode::Branch, target, None)?,
958 AstNode::ConditionalBranch { condition, target } => {
959 let condition = self.lower_expr(condition, None)?;
960 self.lower_direct_flow(Opcode::CBranch, target, Some(condition))?;
961 }
962 AstNode::BranchIndirect { target } => {
963 self.lower_indirect_flow(Opcode::BranchInd, target)?
964 }
965 AstNode::Call { target } => self.lower_direct_flow(Opcode::Call, target, None)?,
966 AstNode::CallIndirect { target } => {
967 self.lower_indirect_flow(Opcode::CallInd, target)?
968 }
969 AstNode::Return { target } => self.lower_indirect_flow(Opcode::Return, target)?,
970 AstNode::Export(_) => return Err(PcodeLowerError::InternalNode("export statement")),
971 AstNode::Expression(expr) => self.lower_effect(expr)?,
972 }
973 Ok(())
974 }
975
976 fn lower_store(&mut self, load: &Load, rhs: &Expression) -> Result<(), PcodeLowerError> {
977 let space = self.load_space(load)?;
978 if space == SPACE_CONST {
979 return Err(PcodeLowerError::Unsupported("a store to constant space"));
980 }
981 let ptr = self.lower_expr(&load.ptr, None)?;
982 self.validate_pointer(space, ptr)?;
983 let value = match load.size {
984 Some(size) => self.lower_expr_with_size(rhs, size)?,
985 None => self.lower_expr(rhs, None)?,
986 };
987 if let Some(size) = load.size {
988 Self::checked_size(size)?;
989 if size != value.size {
990 return Err(PcodeLowerError::StoreSizeMismatch {
991 declared: size,
992 value: value.size,
993 });
994 }
995 }
996 self.emit(Opcode::Store, None, &[Self::space_id(space), ptr, value]);
997 Ok(())
998 }
999
1000 fn lower_direct_flow(
1001 &mut self,
1002 opcode: Opcode,
1003 target: &LabelOrNode,
1004 condition: Option<Varnode>,
1005 ) -> Result<(), PcodeLowerError> {
1006 let target = match target {
1007 LabelOrNode::Label(label) => {
1008 let id = self.label_id(label)?;
1012 self.sink.branch_label(opcode, id, condition);
1013 return Ok(());
1014 }
1015 LabelOrNode::Node(_) => {
1016 return Err(PcodeLowerError::InternalNode("unresolved branch target"));
1017 }
1018 LabelOrNode::Expr(expr) => self.direct_target(expr)?,
1019 };
1020 match condition {
1021 Some(condition) => self.emit(opcode, None, &[target, condition]),
1022 None => self.emit(opcode, None, &[target]),
1023 }
1024 Ok(())
1025 }
1026
1027 fn lower_indirect_flow(
1028 &mut self,
1029 opcode: Opcode,
1030 target: &Expression,
1031 ) -> Result<(), PcodeLowerError> {
1032 let target = self.lower_expr(target, None)?;
1033 self.emit(opcode, None, &[target]);
1034 Ok(())
1035 }
1036
1037 fn direct_target(&self, target: &Expression) -> Result<Varnode, PcodeLowerError> {
1038 let ExpressionTy::SizedInt { value, size } = target.ty else {
1039 return Err(PcodeLowerError::InvalidDirectTarget);
1040 };
1041 let size = size
1042 .or(target.size)
1043 .or_else(|| self.context.address_size(self.context.default_space()))
1044 .ok_or(PcodeLowerError::UnknownSize)?;
1045 Self::checked_size(size)?;
1046 Ok(Varnode::new(self.context.default_space(), value, size))
1047 }
1048
1049 fn lower_effect(&mut self, expr: &Expression) -> Result<(), PcodeLowerError> {
1050 match &expr.ty {
1051 ExpressionTy::PcodeOp { id, args } => {
1052 let inputs = self.lower_userop_inputs(*id, args)?;
1053 self.emit(Opcode::CallOther, None, &inputs);
1054 Ok(())
1055 }
1056 ExpressionTy::MacroCall { .. } => Err(PcodeLowerError::InternalNode("macro call")),
1057 ExpressionTy::DeferredCall { .. } => {
1058 Err(PcodeLowerError::InternalNode("deferred call"))
1059 }
1060 _ => Err(PcodeLowerError::Unsupported("a discarded value expression")),
1061 }
1062 }
1063
1064 fn lower_expr(
1065 &mut self,
1066 expr: &Expression,
1067 requested_output: Option<Varnode>,
1068 ) -> Result<Varnode, PcodeLowerError> {
1069 match &expr.ty {
1070 ExpressionTy::SizedInt { value, size } => {
1071 let input = Varnode::constant(
1076 *value,
1077 requested_output
1078 .map(|output| output.size)
1079 .or(*size)
1080 .or(expr.size)
1081 .ok_or(PcodeLowerError::UnknownSize)?,
1082 );
1083 self.copy_if_requested(input, requested_output)
1084 }
1085 ExpressionTy::Ident(Ident::BitRange(id)) => {
1086 self.lower_named_bitrange(*id, requested_output)
1087 }
1088 ExpressionTy::Ident(ident) => {
1089 let input = self.storage_for_ident(ident.clone(), expr.size)?;
1090 self.copy_if_requested(input, requested_output)
1091 }
1092 ExpressionTy::Load(load) => self.lower_load(expr, load, requested_output),
1093 ExpressionTy::SubPieceMsb { src, count } => {
1094 let input = self.lower_expr(src, None)?;
1095 let size = requested_output
1096 .map(|output| output.size)
1097 .or(expr.size)
1098 .unwrap_or_else(|| input.size.saturating_sub(*count));
1099 let output = self.output(requested_output, size)?;
1100 if *count >= input.size || size > input.size - count {
1101 return Err(PcodeLowerError::InvalidRange {
1102 start: count.saturating_mul(8),
1103 size: size.saturating_mul(8),
1104 storage_bits: input.size.saturating_mul(8),
1105 });
1106 }
1107 self.emit(
1108 Opcode::SubPiece,
1109 Some(output),
1110 &[input, Varnode::constant(*count as u64, 8)],
1111 );
1112 Ok(output)
1113 }
1114 ExpressionTy::SubPieceLsb { src, count } => {
1115 let input = self.lower_expr(src, None)?;
1116 if *count == 0 || *count > input.size {
1117 return Err(PcodeLowerError::InvalidRange {
1118 start: 0,
1119 size: count.saturating_mul(8),
1120 storage_bits: input.size.saturating_mul(8),
1121 });
1122 }
1123 let output = self.output(requested_output, *count)?;
1124 self.emit(
1125 Opcode::SubPiece,
1126 Some(output),
1127 &[input, Varnode::constant(0, 8)],
1128 );
1129 Ok(output)
1130 }
1131 ExpressionTy::Range(range) => self.lower_range(range, requested_output),
1132 ExpressionTy::FunctionCall { builtin, args } => {
1133 self.lower_builtin(expr, *builtin, args, requested_output)
1134 }
1135 ExpressionTy::PcodeOp { id, args } => {
1136 let size = requested_output
1137 .map(|output| output.size)
1138 .or(expr.size)
1139 .ok_or(PcodeLowerError::UnknownSize)?;
1140 let output = self.output(requested_output, size)?;
1141 let inputs = self.lower_userop_inputs(*id, args)?;
1142 self.emit(Opcode::CallOther, Some(output), &inputs);
1143 Ok(output)
1144 }
1145 ExpressionTy::MacroCall { .. } => Err(PcodeLowerError::InternalNode("macro call")),
1146 ExpressionTy::DeferredCall { .. } => {
1147 Err(PcodeLowerError::InternalNode("deferred call"))
1148 }
1149 ExpressionTy::Unop(unop) => self.lower_unop(expr, unop.op, &unop.e, requested_output),
1150 ExpressionTy::Binop(binop) => {
1151 self.lower_binop(expr, binop.op, &binop.lhs, &binop.rhs, requested_output)
1152 }
1153 }
1154 }
1155
1156 fn lower_load(
1157 &mut self,
1158 expr: &Expression,
1159 load: &Load,
1160 requested_output: Option<Varnode>,
1161 ) -> Result<Varnode, PcodeLowerError> {
1162 let space = self.load_space(load)?;
1163 let ptr = self.lower_expr(&load.ptr, None)?;
1164 if space != SPACE_CONST {
1165 self.validate_pointer(space, ptr)?;
1166 }
1167 let size = requested_output
1168 .map(|output| output.size)
1169 .or(load.size)
1170 .or(expr.size)
1171 .ok_or(PcodeLowerError::UnknownSize)?;
1172 if space == SPACE_CONST {
1173 if ptr.size != size {
1174 return Err(PcodeLowerError::Unsupported(
1175 "a constant-space load that changes width",
1176 ));
1177 }
1178 return self.copy_if_requested(ptr, requested_output);
1179 }
1180 let output = self.output(requested_output, size)?;
1181 self.emit(Opcode::Load, Some(output), &[Self::space_id(space), ptr]);
1182 Ok(output)
1183 }
1184
1185 fn lower_builtin(
1186 &mut self,
1187 expr: &Expression,
1188 builtin: Builtin,
1189 args: &[Expression],
1190 requested_output: Option<Varnode>,
1191 ) -> Result<Varnode, PcodeLowerError> {
1192 let opcode = match builtin {
1193 Builtin::Carry => Opcode::IntCarry,
1194 Builtin::Scarry => Opcode::IntSCarry,
1195 Builtin::Sborrow => Opcode::IntSBorrow,
1196 Builtin::Nan => Opcode::FloatNan,
1197 Builtin::Abs => Opcode::FloatAbs,
1198 Builtin::Sqrt => Opcode::FloatSqrt,
1199 Builtin::Floor => Opcode::FloatFloor,
1200 Builtin::Ceil => Opcode::FloatCeil,
1201 Builtin::Round => Opcode::FloatRound,
1202 Builtin::Int2Float => Opcode::FloatInt2Float,
1203 Builtin::Float2Float => Opcode::FloatFloat2Float,
1204 Builtin::Trunc => Opcode::FloatTrunc,
1205 Builtin::Zext => Opcode::IntZext,
1206 Builtin::Sext => Opcode::IntSext,
1207 Builtin::Popcount => Opcode::PopCount,
1208 Builtin::Lzcount => Opcode::LzCount,
1209 Builtin::Cpool => Opcode::CpoolRef,
1210 Builtin::NewObject => Opcode::New,
1211 };
1212 let size = requested_output
1213 .map(|output| output.size)
1214 .or(expr.size)
1215 .or(match builtin {
1216 Builtin::Carry | Builtin::Scarry | Builtin::Sborrow | Builtin::Nan => Some(1),
1217 _ => None,
1218 })
1219 .ok_or(PcodeLowerError::UnknownSize)?;
1220 let output = self.output(requested_output, size)?;
1221 let inputs = if matches!(builtin, Builtin::Carry | Builtin::Scarry | Builtin::Sborrow)
1226 && !args.is_empty()
1227 {
1228 let operand_size = args
1232 .iter()
1233 .find_map(|arg| self.expr_size(arg))
1234 .ok_or(PcodeLowerError::UnknownSize)?;
1235 args.iter()
1236 .map(|arg| self.lower_expr_with_size(arg, operand_size))
1237 .collect::<Result<Vec<_>, _>>()?
1238 } else {
1239 args.iter()
1240 .map(|arg| self.lower_expr(arg, None))
1241 .collect::<Result<Vec<_>, _>>()?
1242 };
1243 self.emit(opcode, Some(output), &inputs);
1244 Ok(output)
1245 }
1246
1247 fn lower_unop(
1248 &mut self,
1249 expr: &Expression,
1250 op: UnaryOperator,
1251 operand: &Expression,
1252 requested_output: Option<Varnode>,
1253 ) -> Result<Varnode, PcodeLowerError> {
1254 if let UnaryOperator::AddressOf(size) = op {
1255 if let ExpressionTy::SizedInt {
1258 value,
1259 size: literal_size,
1260 } = &operand.ty
1261 {
1262 let size = size
1263 .or(*literal_size)
1264 .or(operand.size)
1265 .ok_or(PcodeLowerError::UnknownSize)?;
1266 return self.copy_if_requested(Varnode::constant(*value, size), requested_output);
1267 }
1268 let storage = self.storage_from_expr(operand)?;
1269 let size = size
1270 .or_else(|| self.context.address_size(storage.space))
1271 .ok_or(PcodeLowerError::UnknownSize)?;
1272 return self
1273 .copy_if_requested(Varnode::constant(storage.offset, size), requested_output);
1274 }
1275 let opcode = match op {
1276 UnaryOperator::LogicalNot => Opcode::BoolNegate,
1277 UnaryOperator::BitwiseNot => Opcode::IntNegate,
1278 UnaryOperator::Minus => Opcode::Int2Comp,
1279 UnaryOperator::FloatMinus => Opcode::FloatNeg,
1280 UnaryOperator::AddressOf(_) => unreachable!(),
1281 };
1282 let size = requested_output
1287 .map(|output| output.size)
1288 .or(expr.size)
1289 .or_else(|| (op == UnaryOperator::LogicalNot).then_some(1))
1290 .or_else(|| self.expr_size(operand))
1291 .ok_or(PcodeLowerError::UnknownSize)?;
1292 let input = if self.expr_size(operand).is_some() {
1296 self.lower_expr(operand, None)?
1297 } else {
1298 self.lower_expr_with_size(operand, size)?
1299 };
1300 let output = self.output(requested_output, size)?;
1301 self.emit(opcode, Some(output), &[input]);
1302 Ok(output)
1303 }
1304
1305 fn lower_binop(
1306 &mut self,
1307 expr: &Expression,
1308 op: BinaryOperator,
1309 lhs: &Expression,
1310 rhs: &Expression,
1311 requested_output: Option<Varnode>,
1312 ) -> Result<Varnode, PcodeLowerError> {
1313 let (opcode, reverse) = binary_opcode(op);
1314 let is_boolean = op.is_comparison()
1320 || matches!(
1321 op,
1322 BinaryOperator::LogicalXor | BinaryOperator::LogicalAnd | BinaryOperator::LogicalOr
1323 );
1324 let input_size = if is_boolean {
1325 self.expr_size(lhs).or_else(|| self.expr_size(rhs))
1326 } else {
1327 requested_output
1328 .map(|output| output.size)
1329 .or(expr.size)
1330 .or_else(|| self.expr_size(lhs))
1331 .or_else(|| self.expr_size(rhs))
1332 };
1333 let mut inputs = match input_size {
1334 Some(size) => vec![
1335 self.lower_expr_with_size(lhs, size)?,
1336 self.lower_expr_with_size(rhs, size)?,
1337 ],
1338 None => vec![self.lower_expr(lhs, None)?, self.lower_expr(rhs, None)?],
1339 };
1340 if reverse {
1341 inputs.swap(0, 1);
1342 }
1343 let size = requested_output
1344 .map(|output| output.size)
1345 .or(expr.size)
1346 .or_else(|| op.is_comparison().then_some(1))
1347 .or(input_size)
1348 .ok_or(PcodeLowerError::UnknownSize)?;
1349 let output = self.output(requested_output, size)?;
1350 if (op.is_comparison()
1351 || matches!(
1352 op,
1353 BinaryOperator::LogicalXor | BinaryOperator::LogicalAnd | BinaryOperator::LogicalOr
1354 ))
1355 && output.size != 1
1356 {
1357 return Err(PcodeLowerError::InvalidBooleanSize(output.size));
1358 }
1359 if inputs[0].size != inputs[1].size {
1360 return Err(PcodeLowerError::InputSizeMismatch {
1361 operation: "binary operation",
1362 left: inputs[0].size,
1363 right: inputs[1].size,
1364 });
1365 }
1366 if !op.is_comparison()
1367 && !matches!(
1368 op,
1369 BinaryOperator::LogicalXor | BinaryOperator::LogicalAnd | BinaryOperator::LogicalOr
1370 )
1371 && output.size != inputs[0].size
1372 {
1373 return Err(PcodeLowerError::CopySizeMismatch {
1374 input: inputs[0].size,
1375 output: output.size,
1376 });
1377 }
1378 self.emit(opcode, Some(output), &inputs);
1379 Ok(output)
1380 }
1381
1382 fn lower_expr_with_size(
1391 &mut self,
1392 expr: &Expression,
1393 size: usize,
1394 ) -> Result<Varnode, PcodeLowerError> {
1395 if let ExpressionTy::SizedInt { value, .. } = &expr.ty {
1400 return Ok(Varnode::constant(*value, size));
1401 }
1402 if self.expr_size(expr) == Some(size) {
1403 return self.lower_expr(expr, None);
1404 }
1405 let output = self.allocate_unique(size)?;
1406 self.lower_expr(expr, Some(output))
1407 }
1408
1409 fn lower_range(
1410 &mut self,
1411 range: &Range,
1412 requested_output: Option<Varnode>,
1413 ) -> Result<Varnode, PcodeLowerError> {
1414 let input = self.lower_expr(&range.value, None)?;
1415 let (start, bits) = range_params(range)?;
1416 self.extract_range(input, start, bits, requested_output)
1417 }
1418
1419 fn lower_named_bitrange(
1420 &mut self,
1421 id: BitRangeFieldId,
1422 requested_output: Option<Varnode>,
1423 ) -> Result<Varnode, PcodeLowerError> {
1424 let info = self
1425 .context
1426 .bitrange_info(id)
1427 .ok_or(PcodeLowerError::Unsupported("an unknown named bit range"))?;
1428 self.extract_range(info.storage, info.start, info.size, requested_output)
1429 }
1430
1431 fn extract_range(
1432 &mut self,
1433 input: Varnode,
1434 start: usize,
1435 bits: usize,
1436 requested_output: Option<Varnode>,
1437 ) -> Result<Varnode, PcodeLowerError> {
1438 let result_size = Self::validate_range(input, start, bits)?;
1439 if let Some(output) = requested_output {
1440 if output.size != result_size {
1441 return Err(PcodeLowerError::CopySizeMismatch {
1442 input: result_size,
1443 output: output.size,
1444 });
1445 }
1446 }
1447 let shifted = self.allocate_unique(input.size)?;
1448 self.emit(
1449 Opcode::IntRight,
1450 Some(shifted),
1451 &[input, Varnode::constant(start as u64, input.size)],
1452 );
1453 let masked = self.allocate_unique(input.size)?;
1454 self.emit(
1455 Opcode::IntAnd,
1456 Some(masked),
1457 &[shifted, Varnode::constant(Self::mask(bits)?, input.size)],
1458 );
1459 let output = self.output(requested_output, result_size)?;
1460 self.emit(
1461 Opcode::SubPiece,
1462 Some(output),
1463 &[masked, Varnode::constant(0, 8)],
1464 );
1465 Ok(output)
1466 }
1467
1468 fn lower_range_assignment(
1469 &mut self,
1470 range: &Range,
1471 rhs: &Expression,
1472 ) -> Result<(), PcodeLowerError> {
1473 if let ExpressionTy::Load(load) = &range.value.ty {
1474 return self.lower_load_range_assignment(load, range, rhs);
1475 }
1476 let storage = self.storage_from_expr(&range.value)?;
1477 let (start, bits) = range_params(range)?;
1478 self.insert_range(storage, start, bits, rhs)
1479 }
1480
1481 fn lower_load_range_assignment(
1485 &mut self,
1486 load: &Load,
1487 range: &Range,
1488 rhs: &Expression,
1489 ) -> Result<(), PcodeLowerError> {
1490 let storage = self.lower_load(&range.value, load, None)?;
1491 let (start, bits) = range_params(range)?;
1492 Self::validate_range(storage, start, bits)?;
1493 if storage.size > 8 {
1494 return Err(PcodeLowerError::InvalidRange {
1495 start,
1496 size: bits,
1497 storage_bits: storage.size.saturating_mul(8),
1498 });
1499 }
1500 let value = self.lower_expr_with_size(rhs, bits.div_ceil(8))?;
1501 if value.size > storage.size {
1502 return Err(PcodeLowerError::InputSizeMismatch {
1503 operation: "bit-range assignment",
1504 left: storage.size,
1505 right: value.size,
1506 });
1507 }
1508 let extended = if value.size == storage.size {
1509 value
1510 } else {
1511 let output = self.allocate_unique(storage.size)?;
1512 self.emit(Opcode::IntZext, Some(output), &[value]);
1513 output
1514 };
1515 let inserted = self.allocate_unique(storage.size)?;
1516 self.emit(
1517 Opcode::IntAnd,
1518 Some(inserted),
1519 &[extended, Varnode::constant(Self::mask(bits)?, storage.size)],
1520 );
1521 let shifted = self.allocate_unique(storage.size)?;
1522 self.emit(
1523 Opcode::IntLeft,
1524 Some(shifted),
1525 &[inserted, Varnode::constant(start as u64, storage.size)],
1526 );
1527 let clear_mask = !(Self::mask(bits)? << start);
1528 let kept = self.allocate_unique(storage.size)?;
1529 self.emit(
1530 Opcode::IntAnd,
1531 Some(kept),
1532 &[storage, Varnode::constant(clear_mask, storage.size)],
1533 );
1534 let result = self.allocate_unique(storage.size)?;
1535 self.emit(Opcode::IntOr, Some(result), &[kept, shifted]);
1536
1537 let space = self.load_space(load)?;
1538 if space == SPACE_CONST {
1539 return Err(PcodeLowerError::Unsupported("a store to constant space"));
1540 }
1541 let ptr = self.lower_expr(&load.ptr, None)?;
1542 self.validate_pointer(space, ptr)?;
1543 self.emit(Opcode::Store, None, &[Self::space_id(space), ptr, result]);
1544 Ok(())
1545 }
1546
1547 fn insert_range(
1548 &mut self,
1549 storage: Varnode,
1550 start: usize,
1551 bits: usize,
1552 rhs: &Expression,
1553 ) -> Result<(), PcodeLowerError> {
1554 Self::validate_range(storage, start, bits)?;
1555 if storage.size > 8 {
1559 return Err(PcodeLowerError::InvalidRange {
1560 start,
1561 size: bits,
1562 storage_bits: storage.size.saturating_mul(8),
1563 });
1564 }
1565 let value = self.lower_expr_with_size(rhs, bits.div_ceil(8))?;
1568 if value.size > storage.size {
1569 return Err(PcodeLowerError::InputSizeMismatch {
1570 operation: "bit-range assignment",
1571 left: storage.size,
1572 right: value.size,
1573 });
1574 }
1575 let extended = if value.size == storage.size {
1576 value
1577 } else {
1578 let output = self.allocate_unique(storage.size)?;
1579 self.emit(Opcode::IntZext, Some(output), &[value]);
1580 output
1581 };
1582 let inserted = self.allocate_unique(storage.size)?;
1583 self.emit(
1584 Opcode::IntAnd,
1585 Some(inserted),
1586 &[extended, Varnode::constant(Self::mask(bits)?, storage.size)],
1587 );
1588 let shifted = self.allocate_unique(storage.size)?;
1589 self.emit(
1590 Opcode::IntLeft,
1591 Some(shifted),
1592 &[inserted, Varnode::constant(start as u64, storage.size)],
1593 );
1594 let clear_mask = !(Self::mask(bits)? << start);
1595 let kept = self.allocate_unique(storage.size)?;
1596 self.emit(
1597 Opcode::IntAnd,
1598 Some(kept),
1599 &[storage, Varnode::constant(clear_mask, storage.size)],
1600 );
1601 self.emit(Opcode::IntOr, Some(storage), &[kept, shifted]);
1602 Ok(())
1603 }
1604
1605 fn validate_range(
1606 storage: Varnode,
1607 start: usize,
1608 size: usize,
1609 ) -> Result<usize, PcodeLowerError> {
1610 let storage_bits = storage
1611 .size
1612 .checked_mul(8)
1613 .ok_or(PcodeLowerError::InvalidRange {
1614 start,
1615 size,
1616 storage_bits: usize::MAX,
1617 })?;
1618 if size == 0 || size > 64 || start.checked_add(size).is_none_or(|end| end > storage_bits) {
1619 return Err(PcodeLowerError::InvalidRange {
1620 start,
1621 size,
1622 storage_bits,
1623 });
1624 }
1625 Ok(size.div_ceil(8))
1626 }
1627
1628 fn mask(bits: usize) -> Result<u64, PcodeLowerError> {
1629 match bits {
1630 1..=63 => Ok((1u64 << bits) - 1),
1631 64 => Ok(u64::MAX),
1632 _ => Err(PcodeLowerError::InvalidRange {
1633 start: 0,
1634 size: bits,
1635 storage_bits: 64,
1636 }),
1637 }
1638 }
1639
1640 fn validate_pointer(&self, space: SpaceId, ptr: Varnode) -> Result<(), PcodeLowerError> {
1641 let expected = self
1642 .context
1643 .address_size(space)
1644 .ok_or(PcodeLowerError::UnresolvedSpace)?;
1645 Self::checked_size(expected)?;
1646 if ptr.size != expected {
1647 return Err(PcodeLowerError::AddressSizeMismatch {
1648 expected,
1649 actual: ptr.size,
1650 });
1651 }
1652 Ok(())
1653 }
1654
1655 fn storage_from_expr(&mut self, expr: &Expression) -> Result<Varnode, PcodeLowerError> {
1656 match &expr.ty {
1657 ExpressionTy::Ident(ident) => self.storage_for_ident(ident.clone(), expr.size),
1658 _ => Err(PcodeLowerError::Unsupported(
1659 "address-of a non-varnode expression",
1660 )),
1661 }
1662 }
1663
1664 fn storage_for_ident(
1665 &mut self,
1666 ident: Ident,
1667 size: Option<usize>,
1668 ) -> Result<Varnode, PcodeLowerError> {
1669 match ident {
1670 Ident::Register(id) => self
1671 .context
1672 .register_varnode(id)
1673 .ok_or(PcodeLowerError::UnknownRegister(id)),
1674 Ident::Named(id) => {
1675 let size = self.plan.local_sizes.get(&id).copied().or(size);
1676 if let Some(varnode) = self.locals.get(&id) {
1677 if let Some(size) = size
1678 && size != varnode.size
1679 {
1680 return Err(PcodeLowerError::CopySizeMismatch {
1681 input: varnode.size,
1682 output: size,
1683 });
1684 }
1685 return Ok(*varnode);
1686 }
1687 let varnode = self.allocate_unique(size.ok_or(PcodeLowerError::UnknownSize)?)?;
1688 self.locals.insert(id, varnode);
1689 Ok(varnode)
1690 }
1691 Ident::BitRange(_) => Err(PcodeLowerError::Unsupported("a named bit range")),
1692 Ident::Field(_) => Err(PcodeLowerError::UnresolvedIdentifier("field")),
1693 Ident::Table(_) => Err(PcodeLowerError::UnresolvedIdentifier("table")),
1694 Ident::Global(_) => Err(PcodeLowerError::UnresolvedIdentifier("global")),
1695 }
1696 }
1697
1698 fn lower_userop_inputs(
1699 &mut self,
1700 id: PCodeOpId,
1701 args: &[Expression],
1702 ) -> Result<Vec<Varnode>, PcodeLowerError> {
1703 let mut inputs = Vec::with_capacity(args.len() + 1);
1704 inputs.push(Varnode::constant(usize::from(id) as u64, 4));
1705 inputs.extend(
1706 args.iter()
1707 .map(|arg| self.lower_expr(arg, None))
1708 .collect::<Result<Vec<_>, _>>()?,
1709 );
1710 Ok(inputs)
1711 }
1712
1713 fn copy_if_requested(
1714 &mut self,
1715 input: Varnode,
1716 requested_output: Option<Varnode>,
1717 ) -> Result<Varnode, PcodeLowerError> {
1718 match requested_output {
1719 Some(output) if output != input && input.size == output.size => {
1720 self.emit(Opcode::Copy, Some(output), &[input]);
1721 Ok(output)
1722 }
1723 Some(output) if input.size < output.size => {
1724 self.emit(Opcode::IntZext, Some(output), &[input]);
1725 Ok(output)
1726 }
1727 Some(output) if input.size > output.size => {
1728 self.emit(
1729 Opcode::SubPiece,
1730 Some(output),
1731 &[input, Varnode::constant(0, 8)],
1732 );
1733 Ok(output)
1734 }
1735 Some(output) => Ok(output),
1736 None => Ok(input),
1737 }
1738 }
1739
1740 fn output(
1741 &mut self,
1742 requested_output: Option<Varnode>,
1743 size: usize,
1744 ) -> Result<Varnode, PcodeLowerError> {
1745 match requested_output {
1746 Some(output) => {
1747 Self::checked_size(output.size)?;
1748 Ok(output)
1749 }
1750 None => self.allocate_unique(size),
1751 }
1752 }
1753
1754 fn allocate_unique(&mut self, size: usize) -> Result<Varnode, PcodeLowerError> {
1755 Self::checked_size(size)?;
1756 let offset = self.next_unique;
1757 self.next_unique = self
1758 .next_unique
1759 .checked_add(size as u64)
1760 .ok_or(PcodeLowerError::UniqueSpaceOverflow)?;
1761 Ok(Varnode::new(self.context.unique_space(), offset, size))
1762 }
1763
1764 fn label_id(&self, label: &str) -> Result<LabelId, PcodeLowerError> {
1765 self.plan
1766 .label_id(label)
1767 .ok_or_else(|| PcodeLowerError::UnknownLabel(Box::from(label)))
1768 }
1769
1770 fn checked_size(size: usize) -> Result<(), PcodeLowerError> {
1771 if size == 0 {
1772 Err(PcodeLowerError::ZeroSize)
1773 } else {
1774 Ok(())
1775 }
1776 }
1777
1778 fn space_id(space: SpaceId) -> Varnode {
1779 Varnode::constant(usize::from(space) as u64, 4)
1780 }
1781}
1782
1783struct Planner<'a, C: PcodeLoweringContext + ?Sized> {
1789 context: &'a C,
1790 plan: PcodePlan,
1791}
1792
1793impl<'a, C: PcodeLoweringContext + ?Sized> Planner<'a, C> {
1794 fn plan(&mut self, ast: &PcodeAst) {
1795 self.plan.local_sizes = SizeInference::run(self.context, &ast.statements);
1796 self.plan_statements(ast);
1797 }
1798
1799 fn plan_statements(&mut self, ast: &PcodeAst) {
1802 for statement in &ast.statements {
1803 match &statement.ty {
1804 AstNode::Label(label) => {
1805 self.plan.declare_label(label);
1806 }
1807 AstNode::Branch { target } | AstNode::ConditionalBranch { target, .. } => {
1808 if let LabelOrNode::Expr(expr) = target
1811 && let Some(address) = self.direct_address(expr)
1812 {
1813 self.plan.declare_direct_branch(address);
1814 }
1815 }
1816 AstNode::Call { target } => {
1817 if let LabelOrNode::Expr(expr) = target
1818 && let Some(address) = self.direct_address(expr)
1819 {
1820 self.plan.declare_direct_call(address);
1821 }
1822 }
1823 _ => {}
1824 }
1825 }
1826
1827 for statement in ast.statements.iter().rev() {
1830 let AstNode::Label(label) = &statement.ty else {
1831 break;
1832 };
1833 if let Some(id) = self.plan.label_id(label) {
1834 self.plan.terminal[id.index()] = true;
1835 }
1836 }
1837 }
1838
1839 fn direct_address<S>(&self, target: &Expression<S>) -> Option<u64> {
1840 match target.ty {
1841 ExpressionTy::SizedInt { value, .. } => Some(value),
1842 _ => None,
1843 }
1844 }
1845}
1846
1847struct SizeInference<'a, C: PcodeLoweringContext + ?Sized, W: Width> {
1854 context: &'a C,
1855 sizes: HashMap<LocalVarId, W>,
1856}
1857
1858impl<'a, C: PcodeLoweringContext + ?Sized, W: Width> SizeInference<'a, C, W> {
1859 fn run<S>(context: &'a C, statements: &[Ast<S>]) -> HashMap<LocalVarId, W> {
1860 let mut inference = Self {
1861 context,
1862 sizes: HashMap::new(),
1863 };
1864 inference.infer(statements);
1865 inference.sizes
1866 }
1867
1868 fn infer<S>(&mut self, statements: &[Ast<S>]) {
1872 for _ in 0..=statements.len() {
1875 let before = self.sizes.len();
1876 for statement in statements {
1877 self.constrain_statement(&statement.ty);
1878 }
1879 if self.sizes.len() == before {
1880 break;
1881 }
1882 }
1883 }
1884
1885 fn constrain_statement<S>(&mut self, statement: &AstNode<S>) {
1886 match statement {
1887 AstNode::Assignment { lhs, size, rhs } => {
1888 let comparison_size =
1892 matches!(&rhs.ty, ExpressionTy::Binop(binop) if binop.op.is_comparison())
1893 .then_some(rhs.size)
1894 .flatten()
1895 .filter(|&size| size != 1)
1896 .map(W::fixed);
1897 let expected = (*size)
1898 .map(W::fixed)
1899 .or_else(|| self.storage_size(lhs))
1900 .or(comparison_size);
1901 let inferred = self.constrain_expr(rhs, expected);
1902 if let Ident::Named(id) = lhs
1903 && let Some(size) = expected.or(inferred)
1904 {
1905 self.sizes.entry(*id).or_insert(size);
1906 }
1907 }
1908 AstNode::LoadAssignment { lhs, rhs, .. } => {
1909 let space = self.load_space(lhs).ok();
1910 if let Some(space) = space {
1911 self.constrain_expr(&lhs.ptr, self.context.address_size(space).map(W::fixed));
1912 }
1913 self.constrain_expr(rhs, lhs.size.map(W::fixed));
1914 }
1915 AstNode::RangeAssignment { lhs, rhs, .. } => {
1916 if let Ok((_, bits)) = range_params(lhs) {
1917 self.constrain_expr(rhs, Some(W::fixed(bits.div_ceil(8))));
1918 }
1919 }
1920 AstNode::ConditionalBranch { condition, .. } => {
1921 self.constrain_expr(condition, Some(W::fixed(1)));
1922 }
1923 AstNode::BranchIndirect { target }
1924 | AstNode::CallIndirect { target }
1925 | AstNode::Return { target } => {
1926 self.constrain_expr(
1927 target,
1928 self.context
1929 .address_size(self.context.default_space())
1930 .map(W::fixed),
1931 );
1932 }
1933 AstNode::Expression(expr) => {
1934 self.constrain_expr(expr, None);
1935 }
1936 AstNode::Build(_)
1937 | AstNode::DelaySlot(_)
1938 | AstNode::DeferredBuild(_)
1939 | AstNode::Label(_)
1940 | AstNode::Branch { .. }
1941 | AstNode::Call { .. }
1942 | AstNode::Export(_) => {}
1943 }
1944 }
1945
1946 fn constrain_expr<S>(&mut self, expr: &Expression<S>, expected: Option<W>) -> Option<W> {
1950 match &expr.ty {
1951 ExpressionTy::SizedInt { .. } => expected,
1952 ExpressionTy::Ident(Ident::Named(id)) => {
1953 if let Some(&size) = self.sizes.get(id) {
1954 Some(size)
1955 } else if let Some(size) = expected {
1956 self.sizes.insert(*id, size);
1957 Some(size)
1958 } else {
1959 None
1960 }
1961 }
1962 ExpressionTy::Ident(ident) => self.storage_size(ident),
1963 ExpressionTy::Load(load) => {
1964 if let Ok(space) = self.load_space(load) {
1965 self.constrain_expr(&load.ptr, self.context.address_size(space).map(W::fixed));
1966 }
1967 load.size.map(W::fixed).or(expected)
1968 }
1969 ExpressionTy::SubPieceLsb { src, count } => {
1970 self.constrain_expr(src, None);
1971 Some(W::fixed(*count))
1972 }
1973 ExpressionTy::SubPieceMsb { src, count } => {
1974 let size = expected
1977 .or_else(|| Some(W::fixed(self.expr_size(src)?.size()?.checked_sub(*count)?)));
1978 let source = size
1979 .and_then(|size| size.size())
1980 .map(|size| W::fixed(size + count));
1981 self.constrain_expr(src, source);
1982 size
1983 }
1984 ExpressionTy::Range(range) => {
1985 let size = match range.size {
1986 RangeParam::Literal(bits) => Some(W::fixed(bits.div_ceil(8))),
1987 RangeParam::MacroArg(_) => expected,
1988 };
1989 self.constrain_expr(&range.value, None);
1990 size
1991 }
1992 ExpressionTy::FunctionCall { builtin, args } => {
1993 let boolean = matches!(
1994 builtin,
1995 Builtin::Carry | Builtin::Scarry | Builtin::Sborrow | Builtin::Nan
1996 );
1997 let size = boolean.then(|| W::fixed(1)).or(expected);
1998 let input_size = args.iter().find_map(|arg| self.constrain_expr(arg, None));
1999 if let Some(input_size) = input_size {
2000 for arg in args {
2001 self.constrain_expr(arg, Some(input_size));
2002 }
2003 }
2004 size
2005 }
2006 ExpressionTy::PcodeOp { args, .. } => {
2007 for arg in args {
2008 self.constrain_expr(arg, None);
2009 }
2010 expected
2011 }
2012 ExpressionTy::Unop(unop) => match unop.op {
2013 UnaryOperator::LogicalNot => {
2014 let size = self.constrain_expr(&unop.e, None);
2015 self.constrain_expr(&unop.e, size);
2016 Some(W::fixed(1))
2017 }
2018 UnaryOperator::AddressOf(size) => size.map(W::fixed).or_else(|| {
2019 self.storage_from_expr_size(&unop.e)
2020 .and_then(|storage| self.context.address_size(storage.space))
2021 .map(W::fixed)
2022 }),
2023 _ => {
2024 let size = expected.or_else(|| self.constrain_expr(&unop.e, None));
2025 self.constrain_expr(&unop.e, size);
2026 size
2027 }
2028 },
2029 ExpressionTy::Binop(binop) => {
2030 let boolean = binop.op.is_comparison()
2031 || matches!(
2032 binop.op,
2033 BinaryOperator::LogicalXor
2034 | BinaryOperator::LogicalAnd
2035 | BinaryOperator::LogicalOr
2036 );
2037 let input_size = self
2038 .constrain_expr(&binop.lhs, None)
2039 .or_else(|| self.constrain_expr(&binop.rhs, None));
2040 let input_size = if boolean {
2041 input_size
2042 } else {
2043 expected.or(input_size)
2044 };
2045 self.constrain_expr(&binop.lhs, input_size);
2046 self.constrain_expr(&binop.rhs, input_size);
2047 if boolean {
2048 Some(W::fixed(1))
2049 } else {
2050 input_size
2051 }
2052 }
2053 ExpressionTy::MacroCall { .. } | ExpressionTy::DeferredCall { .. } => expected,
2054 }
2055 }
2056}
2057
2058impl<'a, C: PcodeLoweringContext + ?Sized, W: Width> Sizing<W> for SizeInference<'a, C, W> {
2059 type Ctx = C;
2060
2061 fn context(&self) -> &C {
2062 self.context
2063 }
2064
2065 fn local_size(&self, id: &LocalVarId) -> Option<W> {
2066 self.sizes.get(id).copied()
2067 }
2068}
2069
2070impl<C: PcodeLoweringContext + ?Sized, S: PcodeSink + ?Sized> Sizing<usize>
2071 for Lowerer<'_, '_, '_, C, S>
2072{
2073 type Ctx = C;
2074
2075 fn context(&self) -> &C {
2076 self.context
2077 }
2078
2079 fn local_size(&self, id: &LocalVarId) -> Option<usize> {
2080 self.plan
2081 .local_sizes
2082 .get(id)
2083 .copied()
2084 .or_else(|| self.locals.get(id).map(|varnode| varnode.size))
2085 }
2086}
2087
2088trait Sizing<W: Width> {
2092 type Ctx: PcodeLoweringContext + ?Sized;
2093
2094 fn context(&self) -> &Self::Ctx;
2095
2096 fn local_size(&self, id: &LocalVarId) -> Option<W>;
2098
2099 fn expr_size<S>(&self, expr: &Expression<S>) -> Option<W> {
2100 expr.size.map(W::fixed).or(match &expr.ty {
2101 ExpressionTy::SizedInt { size, .. } => size.map(W::fixed),
2102 ExpressionTy::Ident(ident) => self.storage_size(ident),
2103 ExpressionTy::Load(load) => load.size.map(W::fixed),
2104 ExpressionTy::SubPieceLsb { count, .. } => Some(W::fixed(*count)),
2105 ExpressionTy::SubPieceMsb { src, count } => {
2106 Some(W::fixed(self.expr_size(src)?.size()?.checked_sub(*count)?))
2107 }
2108 ExpressionTy::Range(Range {
2109 size: RangeParam::Literal(bits),
2110 ..
2111 }) => Some(W::fixed(bits.div_ceil(8))),
2112 ExpressionTy::Range(Range {
2113 size: RangeParam::MacroArg(_),
2114 ..
2115 }) => None,
2116 ExpressionTy::FunctionCall {
2117 builtin: Builtin::Carry | Builtin::Scarry | Builtin::Sborrow | Builtin::Nan,
2118 ..
2119 } => Some(W::fixed(1)),
2120 ExpressionTy::FunctionCall { .. } => None,
2121 ExpressionTy::Unop(unop) if unop.op == UnaryOperator::LogicalNot => Some(W::fixed(1)),
2122 ExpressionTy::Unop(unop) => self.expr_size(&unop.e),
2123 ExpressionTy::Binop(binop) if binop.op.is_comparison() => Some(W::fixed(1)),
2124 ExpressionTy::Binop(binop) => self
2125 .expr_size(&binop.lhs)
2126 .or_else(|| self.expr_size(&binop.rhs)),
2127 ExpressionTy::PcodeOp { .. }
2128 | ExpressionTy::MacroCall { .. }
2129 | ExpressionTy::DeferredCall { .. } => None,
2130 })
2131 }
2132
2133 fn storage_size(&self, ident: &Ident) -> Option<W> {
2134 match ident {
2135 Ident::Register(id) => self
2136 .context()
2137 .register_varnode(*id)
2138 .map(|varnode| W::fixed(varnode.size)),
2139 Ident::BitRange(id) => self
2140 .context()
2141 .bitrange_info(*id)
2142 .map(|info| W::fixed(info.size.div_ceil(8))),
2143 Ident::Named(id) => self.local_size(id),
2144 Ident::Table(id) => W::operand(*id),
2147 Ident::Field(_) | Ident::Global(_) => None,
2148 }
2149 }
2150
2151 fn storage_from_expr_size<S>(&self, expr: &Expression<S>) -> Option<Varnode> {
2152 match &expr.ty {
2153 ExpressionTy::Ident(Ident::Register(id)) => self.context().register_varnode(*id),
2154 ExpressionTy::Ident(Ident::BitRange(id)) => {
2155 self.context().bitrange_info(*id).map(|info| info.storage)
2156 }
2157 _ => None,
2158 }
2159 }
2160
2161 fn load_space<S>(&self, load: &Load<S>) -> Result<SpaceId, PcodeLowerError> {
2162 match &load.space {
2163 None => Ok(self.context().default_space()),
2164 Some(crate::PcodeSpaceRef::Resolved(space)) => Ok(*space),
2165 Some(crate::PcodeSpaceRef::Deferred(_)) => Err(PcodeLowerError::UnresolvedSpace),
2166 }
2167 }
2168}
2169
2170fn range_params<S>(range: &Range<S>) -> Result<(usize, usize), PcodeLowerError> {
2174 let RangeParam::Literal(start) = range.start else {
2175 return Err(PcodeLowerError::UnresolvedRangeParameter);
2176 };
2177 let RangeParam::Literal(size) = range.size else {
2178 return Err(PcodeLowerError::UnresolvedRangeParameter);
2179 };
2180 Ok((start, size))
2181}
2182
2183fn binary_opcode(op: BinaryOperator) -> (Opcode, bool) {
2184 use BinaryOperator::*;
2185 match op {
2186 Mul => (Opcode::IntMult, false),
2187 Div => (Opcode::IntDiv, false),
2188 SignedDiv => (Opcode::IntSDiv, false),
2189 Mod => (Opcode::IntRem, false),
2190 SignedMod => (Opcode::IntSRem, false),
2191 FloatDiv => (Opcode::FloatDiv, false),
2192 FloatMul => (Opcode::FloatMult, false),
2193 Add => (Opcode::IntAdd, false),
2194 Sub => (Opcode::IntSub, false),
2195 FloatAdd => (Opcode::FloatAdd, false),
2196 FloatSub => (Opcode::FloatSub, false),
2197 LeftShift => (Opcode::IntLeft, false),
2198 RightShift => (Opcode::IntRight, false),
2199 SignedRightShift => (Opcode::IntSRight, false),
2200 SignedLessThan => (Opcode::IntSLess, false),
2201 SignedGreaterThan => (Opcode::IntSLess, true),
2202 SignedLessEqual => (Opcode::IntSLessEqual, false),
2203 SignedGreaterEqual => (Opcode::IntSLessEqual, true),
2204 LessEqual => (Opcode::IntLessEqual, false),
2205 GreaterEqual => (Opcode::IntLessEqual, true),
2206 LessThan => (Opcode::IntLess, false),
2207 GreaterThan => (Opcode::IntLess, true),
2208 FloatLessEqual => (Opcode::FloatLessEqual, false),
2209 FloatGreaterEqual => (Opcode::FloatLessEqual, true),
2210 FloatLessThan => (Opcode::FloatLess, false),
2211 FloatGreaterThan => (Opcode::FloatLess, true),
2212 Equal => (Opcode::IntEqual, false),
2213 NotEqual => (Opcode::IntNotEqual, false),
2214 FloatEqual => (Opcode::FloatEqual, false),
2215 FloatNotEqual => (Opcode::FloatNotEqual, false),
2216 LogicalXor => (Opcode::BoolXor, false),
2217 LogicalAnd => (Opcode::BoolAnd, false),
2218 LogicalOr => (Opcode::BoolOr, false),
2219 BitwiseXor => (Opcode::IntXor, false),
2220 BitwiseOr => (Opcode::IntOr, false),
2221 BitwiseAnd => (Opcode::IntAnd, false),
2222 }
2223}
2224
2225#[cfg(test)]
2226mod tests {
2227 use super::{
2228 BitRangeInfo, InstructionPcode, LabelId, LocalSizes, Opcode, PcodeLowerError,
2229 PcodeLoweringContext, PcodeOp, PcodeSink, Varnode, emit_instruction, lower_instruction,
2230 plan_instruction,
2231 };
2232 use crate::{
2233 Ast, AstNode, BinaryOperator, Binop, Expression, ExpressionTy, Ident, LabelOrNode, Load,
2234 LocalVarId, PCodeOpId, PcodeAst, PcodeSpaceRef, Range, RangeParam, RegisterId, SPACE_CONST,
2235 SpaceId,
2236 };
2237 use std::collections::HashMap;
2238
2239 struct Context;
2240
2241 impl PcodeLoweringContext for Context {
2242 fn default_space(&self) -> SpaceId {
2243 SpaceId::new(1)
2244 }
2245
2246 fn unique_space(&self) -> SpaceId {
2247 SpaceId::new(2)
2248 }
2249
2250 fn register_varnode(&self, id: RegisterId) -> Option<Varnode> {
2251 Some(Varnode::new(SpaceId::new(3), usize::from(id) as u64 * 4, 4))
2252 }
2253
2254 fn bitrange_info(&self, _id: crate::BitRangeFieldId) -> Option<BitRangeInfo> {
2255 None
2256 }
2257
2258 fn address_size(&self, space: SpaceId) -> Option<usize> {
2259 Some(if space == SpaceId::new(4) { 4 } else { 8 })
2260 }
2261 }
2262
2263 fn int(value: u64, size: usize) -> Expression {
2264 Expression {
2265 ty: ExpressionTy::SizedInt {
2266 value,
2267 size: Some(size),
2268 },
2269 size: Some(size),
2270 span: (),
2271 }
2272 }
2273
2274 fn ident(id: RegisterId) -> Expression {
2275 Expression {
2276 ty: ExpressionTy::Ident(Ident::Register(id)),
2277 size: Some(4),
2278 span: (),
2279 }
2280 }
2281
2282 fn ast(nodes: Vec<AstNode>) -> PcodeAst {
2283 PcodeAst {
2284 statements: nodes.into_iter().map(Ast::from).collect(),
2285 }
2286 }
2287
2288 #[test]
2289 fn varnodes_distinguish_constants_from_storage() {
2290 let constant = Varnode::constant(0x1234, 4);
2291 let storage = Varnode::new(SpaceId::new(2), 0x1234, 4);
2292 assert_eq!(constant.space, SPACE_CONST);
2293 assert!(constant.is_constant());
2294 assert!(!storage.is_constant());
2295 }
2296
2297 #[test]
2298 fn opcode_inventory_identifies_analysis_only_operations() {
2299 assert_eq!(Opcode::ALL.len(), 72);
2300 assert_eq!(Opcode::Copy.ghidra_id(), 1);
2301 assert_eq!(Opcode::FloatLessEqual.ghidra_id(), 44);
2302 assert_eq!(Opcode::FloatNan.ghidra_id(), 46);
2303 assert_eq!(Opcode::LzCount.ghidra_id(), 73);
2304 assert!(Opcode::ALL.contains(&Opcode::Load));
2305 assert!(Opcode::ALL.contains(&Opcode::LzCount));
2306 assert!(Opcode::Load.is_raw_instruction_op());
2307 for opcode in [
2308 Opcode::MultiEqual,
2309 Opcode::Indirect,
2310 Opcode::Cast,
2311 Opcode::PtrAdd,
2312 Opcode::PtrSub,
2313 Opcode::SegmentOp,
2314 Opcode::Insert,
2315 Opcode::Extract,
2316 ] {
2317 assert!(!opcode.is_raw_instruction_op());
2318 }
2319 }
2320
2321 #[test]
2322 fn flat_operations_preserve_input_order_and_round_trip() {
2323 let output = Varnode::new(SpaceId::new(1), 0, 4);
2324 let instruction = InstructionPcode {
2325 ops: vec![PcodeOp::new(
2326 Opcode::IntAdd,
2327 Some(output),
2328 vec![output, Varnode::constant(1, 4)],
2329 )],
2330 };
2331 assert!(!instruction.is_empty());
2332 let bytes =
2333 bincode::serde::encode_to_vec(&instruction, bincode::config::standard()).unwrap();
2334 let (decoded, _): (InstructionPcode, _) =
2335 bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).unwrap();
2336 assert_eq!(decoded, instruction);
2337 assert!(InstructionPcode::new().is_empty());
2338 }
2339
2340 #[test]
2341 fn lower_assignment_emits_direct_arithmetic_output() {
2342 let rhs = Expression {
2343 ty: ExpressionTy::Binop(Binop {
2344 op: BinaryOperator::Add,
2345 lhs: Box::new(ident(RegisterId::new(1))),
2346 rhs: Box::new(int(1, 4)),
2347 }),
2348 size: Some(4),
2349 span: (),
2350 };
2351 let output = Varnode::new(SpaceId::new(3), 0, 4);
2352 let input = Varnode::new(SpaceId::new(3), 4, 4);
2353 let pcode = lower_instruction(
2354 &ast(vec![AstNode::Assignment {
2355 lhs: Ident::Register(RegisterId::new(0)),
2356 size: None,
2357 rhs,
2358 }]),
2359 &Context,
2360 )
2361 .unwrap();
2362 assert_eq!(
2363 pcode.ops,
2364 vec![PcodeOp::new(
2365 Opcode::IntAdd,
2366 Some(output),
2367 vec![input, Varnode::constant(1, 4)],
2368 )]
2369 );
2370 }
2371
2372 #[test]
2373 fn lower_load_store_and_userop_use_ghidra_operand_order() {
2374 let ram = SpaceId::new(4);
2375 let pointer = ident(RegisterId::new(1));
2376 let load = Load {
2377 space: Some(PcodeSpaceRef::Resolved(ram)),
2378 size: Some(4),
2379 ptr: Box::new(pointer.clone()),
2380 };
2381 let pcode = lower_instruction(
2382 &ast(vec![
2383 AstNode::Assignment {
2384 lhs: Ident::Register(RegisterId::new(0)),
2385 size: None,
2386 rhs: Expression {
2387 ty: ExpressionTy::Load(load.clone()),
2388 size: Some(4),
2389 span: (),
2390 },
2391 },
2392 AstNode::LoadAssignment {
2393 lhs: load,
2394 size: None,
2395 rhs: int(9, 4),
2396 },
2397 AstNode::Expression(Expression {
2398 ty: ExpressionTy::PcodeOp {
2399 id: PCodeOpId::new(7),
2400 args: vec![int(2, 4)],
2401 },
2402 size: None,
2403 span: (),
2404 }),
2405 ]),
2406 &Context,
2407 )
2408 .unwrap();
2409 let r0 = Varnode::new(SpaceId::new(3), 0, 4);
2410 let r1 = Varnode::new(SpaceId::new(3), 4, 4);
2411 assert_eq!(
2412 pcode.ops,
2413 vec![
2414 PcodeOp::new(Opcode::Load, Some(r0), vec![Varnode::constant(4, 4), r1],),
2415 PcodeOp::new(
2416 Opcode::Store,
2417 None,
2418 vec![Varnode::constant(4, 4), r1, Varnode::constant(9, 4)],
2419 ),
2420 PcodeOp::new(
2421 Opcode::CallOther,
2422 None,
2423 vec![Varnode::constant(7, 4), Varnode::constant(2, 4)],
2424 ),
2425 ]
2426 );
2427 }
2428
2429 #[derive(Default)]
2431 struct Trace {
2432 events: Vec<String>,
2433 }
2434
2435 impl PcodeSink for Trace {
2436 fn op(&mut self, opcode: Opcode, output: Option<Varnode>, inputs: &[Varnode]) {
2437 self.events
2438 .push(format!("{opcode:?} {output:?} {inputs:?}"));
2439 }
2440
2441 fn label(&mut self, label: LabelId) {
2442 self.events.push(format!("label {}", label.index()));
2443 }
2444
2445 fn branch_label(&mut self, opcode: Opcode, label: LabelId, condition: Option<Varnode>) {
2446 self.events.push(format!(
2447 "{opcode:?} -> label {} {condition:?}",
2448 label.index()
2449 ));
2450 }
2451 }
2452
2453 fn branch_statements() -> Vec<AstNode> {
2454 vec![
2455 AstNode::ConditionalBranch {
2456 condition: ident(RegisterId::new(1)),
2457 target: LabelOrNode::Label("skip".into()),
2458 },
2459 AstNode::Call {
2460 target: LabelOrNode::Expr(int(0x2000, 8)),
2461 },
2462 AstNode::Branch {
2463 target: LabelOrNode::Expr(int(0x1000, 8)),
2464 },
2465 AstNode::Label("skip".into()),
2466 AstNode::Assignment {
2467 lhs: Ident::Register(RegisterId::new(1)),
2468 size: None,
2469 rhs: ident(RegisterId::new(2)),
2470 },
2471 ]
2472 }
2473
2474 #[test]
2475 fn local_widths_can_be_inferred_from_a_body_before_planning() {
2476 let statements = vec![
2477 AstNode::Assignment {
2478 lhs: Ident::Named(LocalVarId(0)),
2479 size: None,
2480 rhs: ident(RegisterId::new(1)),
2481 },
2482 AstNode::Assignment {
2483 lhs: Ident::Register(RegisterId::new(2)),
2484 size: None,
2485 rhs: ExpressionTy::Ident(Ident::Named(LocalVarId(0))).with_size(4),
2486 },
2487 ];
2488 let ast = ast(statements.clone());
2489
2490 let sizes = super::infer_local_sizes(&ast.statements, &Context);
2493 assert_eq!(sizes.get(&LocalVarId(0)), Some(&4));
2494
2495 let planned = super::plan_instruction_with(&ast, &Context, sizes).unwrap();
2496 let inferred = plan_instruction(&ast, &Context).unwrap();
2497 assert_eq!(planned.labels(), inferred.labels());
2498
2499 let pcode = lower_instruction(&ast, &Context).unwrap();
2502 assert_eq!(
2503 pcode.ops[0].output,
2504 Some(Varnode::new(SpaceId::new(2), 0, 4))
2505 );
2506 }
2507
2508 #[test]
2513 fn symbolic_inference_names_an_operand_width_instead_of_losing_it() {
2514 let table = crate::TableId::new(7);
2515 let statements = ast(vec![
2516 AstNode::Assignment {
2517 lhs: Ident::Named(LocalVarId(0)),
2518 size: None,
2519 rhs: Expression {
2520 ty: ExpressionTy::Ident(Ident::Table(table)),
2521 size: None,
2522 span: (),
2523 },
2524 },
2525 AstNode::Assignment {
2526 lhs: Ident::Register(RegisterId::new(1)),
2527 size: None,
2528 rhs: Expression {
2529 ty: ExpressionTy::Binop(Binop {
2530 op: BinaryOperator::Add,
2531 lhs: Box::new(
2532 ExpressionTy::Ident(Ident::Named(LocalVarId(0))).with_size(4),
2533 ),
2534 rhs: Box::new(ident(RegisterId::new(2))),
2535 }),
2536 size: None,
2537 span: (),
2538 },
2539 },
2540 ])
2541 .statements;
2542
2543 let symbolic: HashMap<LocalVarId, super::SymbolicWidth> =
2544 super::infer_local_sizes(&statements, &Context);
2545 assert_eq!(
2546 symbolic.get(&LocalVarId(0)),
2547 Some(&super::SymbolicWidth::SameAs(table))
2548 );
2549
2550 let concrete: LocalSizes = super::infer_local_sizes(&statements, &Context);
2554 assert_eq!(concrete.get(&LocalVarId(0)), Some(&4));
2555 }
2556
2557 #[test]
2558 fn plan_reports_labels_and_out_of_instruction_targets() {
2559 let plan = plan_instruction(&ast(branch_statements()), &Context).unwrap();
2560 assert_eq!(plan.labels(), &[Box::<str>::from("skip")]);
2561 assert_eq!(plan.direct_branches(), &[0x1000]);
2562 assert_eq!(plan.direct_calls(), &[0x2000]);
2563 }
2564
2565 #[test]
2566 fn streaming_emission_keeps_local_branch_targets_symbolic() {
2567 let ast = ast(branch_statements());
2568 let plan = plan_instruction(&ast, &Context).unwrap();
2569 let mut trace = Trace::default();
2570 emit_instruction(&ast, &Context, &plan, &mut trace).unwrap();
2571
2572 assert_eq!(
2573 trace.events[0],
2574 "CBranch -> label 0 Some(Varnode { space: SpaceId(3), offset: 4, size: 4 })"
2575 );
2576 assert_eq!(trace.events[3], "label 0");
2577 assert_eq!(trace.events.len(), 5);
2578
2579 let pcode = lower_instruction(&ast, &Context).unwrap();
2581 assert_eq!(pcode.ops[0].opcode, Opcode::CBranch);
2582 assert_eq!(pcode.ops[0].inputs[0], Varnode::constant(3, 8));
2583 }
2584
2585 #[test]
2586 fn plan_omits_unresolvable_direct_targets() {
2587 let plan = plan_instruction(
2588 &ast(vec![AstNode::Branch {
2589 target: LabelOrNode::Expr(ident(RegisterId::new(1))),
2590 }]),
2591 &Context,
2592 )
2593 .unwrap();
2594 assert!(plan.direct_branches().is_empty());
2595 assert_eq!(
2596 lower_instruction(
2597 &ast(vec![AstNode::Branch {
2598 target: LabelOrNode::Expr(ident(RegisterId::new(1))),
2599 }]),
2600 &Context,
2601 )
2602 .unwrap_err(),
2603 PcodeLowerError::InvalidDirectTarget
2604 );
2605 }
2606
2607 #[test]
2608 fn branching_to_an_undefined_label_is_rejected() {
2609 let error = lower_instruction(
2610 &ast(vec![AstNode::Branch {
2611 target: LabelOrNode::Label("missing".into()),
2612 }]),
2613 &Context,
2614 )
2615 .unwrap_err();
2616 assert_eq!(error, PcodeLowerError::UnknownLabel("missing".into()));
2617 }
2618
2619 #[test]
2620 fn duplicate_labels_are_rejected() {
2621 let error = lower_instruction(
2622 &ast(vec![
2623 AstNode::Label("here".into()),
2624 AstNode::Label("here".into()),
2625 ]),
2626 &Context,
2627 )
2628 .unwrap_err();
2629 assert_eq!(error, PcodeLowerError::DuplicateLabel("here".into()));
2630 }
2631
2632 #[test]
2633 fn lower_labels_are_pcode_relative_and_comparisons_reverse_greater_than() {
2634 let comparison = Expression {
2635 ty: ExpressionTy::Binop(Binop {
2636 op: BinaryOperator::GreaterThan,
2637 lhs: Box::new(ident(RegisterId::new(1))),
2638 rhs: Box::new(ident(RegisterId::new(2))),
2639 }),
2640 size: Some(1),
2641 span: (),
2642 };
2643 let pcode = lower_instruction(
2644 &ast(vec![
2645 AstNode::Label("loop".into()),
2646 AstNode::Assignment {
2647 lhs: Ident::Named(LocalVarId(0)),
2648 size: None,
2649 rhs: comparison,
2650 },
2651 AstNode::Branch {
2652 target: LabelOrNode::Label("loop".into()),
2653 },
2654 ]),
2655 &Context,
2656 )
2657 .unwrap();
2658 assert_eq!(pcode.ops.len(), 2);
2659 assert_eq!(pcode.ops[0].opcode, Opcode::IntLess);
2660 assert_eq!(
2661 pcode.ops[0].inputs,
2662 vec![
2663 Varnode::new(SpaceId::new(3), 8, 4),
2664 Varnode::new(SpaceId::new(3), 4, 4),
2665 ]
2666 );
2667 assert_eq!(
2668 pcode.ops[0].output,
2669 Some(Varnode::new(SpaceId::new(2), 0, 1))
2670 );
2671 assert_eq!(pcode.ops[1].opcode, Opcode::Branch);
2672 assert_eq!(pcode.ops[1].inputs, vec![Varnode::constant(u64::MAX, 8)]);
2673 }
2674
2675 #[test]
2676 fn lower_named_bitranges_as_raw_read_modify_write() {
2677 struct BitRangeContext;
2678 impl PcodeLoweringContext for BitRangeContext {
2679 fn default_space(&self) -> SpaceId {
2680 SpaceId::new(1)
2681 }
2682 fn unique_space(&self) -> SpaceId {
2683 SpaceId::new(2)
2684 }
2685 fn register_varnode(&self, id: RegisterId) -> Option<Varnode> {
2686 Some(Varnode::new(SpaceId::new(3), usize::from(id) as u64 * 4, 4))
2687 }
2688 fn bitrange_info(&self, id: crate::BitRangeFieldId) -> Option<BitRangeInfo> {
2689 (id == crate::BitRangeFieldId::new(0)).then_some(BitRangeInfo {
2690 storage: Varnode::new(SpaceId::new(3), 0, 4),
2691 start: 3,
2692 size: 5,
2693 })
2694 }
2695 fn address_size(&self, _space: SpaceId) -> Option<usize> {
2696 Some(4)
2697 }
2698 }
2699
2700 let read = lower_instruction(
2701 &ast(vec![AstNode::Assignment {
2702 lhs: Ident::Named(LocalVarId(0)),
2703 size: None,
2704 rhs: ExpressionTy::Ident(Ident::BitRange(crate::BitRangeFieldId::new(0)))
2705 .with_size(1),
2706 }]),
2707 &BitRangeContext,
2708 )
2709 .unwrap();
2710 assert_eq!(
2711 read.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
2712 vec![Opcode::IntRight, Opcode::IntAnd, Opcode::SubPiece]
2713 );
2714 assert_eq!(read.ops[0].inputs[1], Varnode::constant(3, 4));
2715 assert_eq!(read.ops[1].inputs[1], Varnode::constant(0x1f, 4));
2716
2717 let write = lower_instruction(
2718 &ast(vec![AstNode::Assignment {
2719 lhs: Ident::BitRange(crate::BitRangeFieldId::new(0)),
2720 size: None,
2721 rhs: int(0xff, 1),
2722 }]),
2723 &BitRangeContext,
2724 )
2725 .unwrap();
2726 assert_eq!(
2727 write.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
2728 vec![
2729 Opcode::IntZext,
2730 Opcode::IntAnd,
2731 Opcode::IntLeft,
2732 Opcode::IntAnd,
2733 Opcode::IntOr,
2734 ]
2735 );
2736 assert_eq!(
2737 write.ops.last().unwrap().output.unwrap().space,
2738 SpaceId::new(3)
2739 );
2740 assert_eq!(write.ops.last().unwrap().output.unwrap().offset, 0);
2741 }
2742
2743 #[test]
2744 fn lower_binary_operations_coerce_narrow_operands() {
2745 struct MixedWidthContext;
2746 impl PcodeLoweringContext for MixedWidthContext {
2747 fn default_space(&self) -> SpaceId {
2748 SpaceId::new(1)
2749 }
2750 fn unique_space(&self) -> SpaceId {
2751 SpaceId::new(2)
2752 }
2753 fn register_varnode(&self, id: RegisterId) -> Option<Varnode> {
2754 Some(Varnode::new(
2755 SpaceId::new(3),
2756 usize::from(id) as u64 * 2,
2757 if id == RegisterId::new(0) { 2 } else { 1 },
2758 ))
2759 }
2760 fn bitrange_info(&self, _id: crate::BitRangeFieldId) -> Option<BitRangeInfo> {
2761 None
2762 }
2763 fn address_size(&self, _space: SpaceId) -> Option<usize> {
2764 Some(8)
2765 }
2766 }
2767
2768 let pcode = lower_instruction(
2769 &ast(vec![AstNode::Assignment {
2770 lhs: Ident::Register(RegisterId::new(0)),
2771 size: None,
2772 rhs: ExpressionTy::Binop(Binop {
2773 op: BinaryOperator::LeftShift,
2774 lhs: Box::new(
2775 ExpressionTy::Ident(Ident::Register(RegisterId::new(0))).with_size(2),
2776 ),
2777 rhs: Box::new(
2778 ExpressionTy::Ident(Ident::Register(RegisterId::new(1))).with_size(1),
2779 ),
2780 })
2781 .with_size(2),
2782 }]),
2783 &MixedWidthContext,
2784 )
2785 .unwrap();
2786 assert_eq!(pcode.ops[0].opcode, Opcode::IntZext);
2787 assert_eq!(pcode.ops[0].output.unwrap().size, 2);
2788 assert_eq!(pcode.ops[1].opcode, Opcode::IntLeft);
2789 assert_eq!(pcode.ops[1].inputs[1].size, 2);
2790 }
2791
2792 #[test]
2793 fn lower_store_coerces_its_value_to_the_declared_width() {
2794 let pcode = lower_instruction(
2795 &ast(vec![AstNode::LoadAssignment {
2796 lhs: Load {
2797 space: None,
2798 size: Some(1),
2799 ptr: Box::new(int(0, 8)),
2800 },
2801 size: None,
2802 rhs: ident(RegisterId::new(0)),
2803 }]),
2804 &Context,
2805 )
2806 .unwrap();
2807 assert_eq!(pcode.ops[0].opcode, Opcode::SubPiece);
2808 assert_eq!(pcode.ops[0].output.unwrap().size, 1);
2809 assert_eq!(pcode.ops[1].opcode, Opcode::Store);
2810 assert_eq!(pcode.ops[1].inputs[2].size, 1);
2811 }
2812
2813 #[test]
2814 fn lower_range_assignment_supplies_its_rhs_width_to_a_userop() {
2815 let pcode = lower_instruction(
2816 &ast(vec![AstNode::RangeAssignment {
2817 lhs: Range {
2818 value: Box::new(ident(RegisterId::new(0))),
2819 start: RangeParam::Literal(0),
2820 size: RangeParam::Literal(8),
2821 },
2822 size: None,
2823 rhs: Expression {
2824 ty: ExpressionTy::PcodeOp {
2825 id: PCodeOpId::new(7),
2826 args: vec![],
2827 },
2828 size: None,
2829 span: (),
2830 },
2831 }]),
2832 &Context,
2833 )
2834 .unwrap();
2835 let userop = pcode
2836 .ops
2837 .iter()
2838 .find(|op| op.opcode == Opcode::CallOther)
2839 .expect("range-assignment user-op was emitted");
2840 assert_eq!(userop.output.unwrap().size, 1);
2841 }
2842
2843 #[test]
2844 fn lower_rejects_nodes_that_are_not_final_raw_pcode() {
2845 let error = lower_instruction(&ast(vec![AstNode::Build(crate::TableId::new(0))]), &Context)
2846 .unwrap_err();
2847 assert_eq!(error, PcodeLowerError::InternalNode("build statement"));
2848 let error = lower_instruction(
2849 &ast(vec![AstNode::Branch {
2850 target: LabelOrNode::Node("unresolved".into()),
2851 }]),
2852 &Context,
2853 )
2854 .unwrap_err();
2855 assert_eq!(
2856 error,
2857 PcodeLowerError::InternalNode("unresolved branch target")
2858 );
2859 }
2860
2861 #[test]
2862 fn lower_every_binary_operator_uses_its_raw_opcode() {
2863 use BinaryOperator::*;
2864 let cases = [
2865 (Mul, Opcode::IntMult, false),
2866 (Div, Opcode::IntDiv, false),
2867 (SignedDiv, Opcode::IntSDiv, false),
2868 (Mod, Opcode::IntRem, false),
2869 (SignedMod, Opcode::IntSRem, false),
2870 (FloatDiv, Opcode::FloatDiv, false),
2871 (FloatMul, Opcode::FloatMult, false),
2872 (Add, Opcode::IntAdd, false),
2873 (Sub, Opcode::IntSub, false),
2874 (FloatAdd, Opcode::FloatAdd, false),
2875 (FloatSub, Opcode::FloatSub, false),
2876 (LeftShift, Opcode::IntLeft, false),
2877 (RightShift, Opcode::IntRight, false),
2878 (SignedRightShift, Opcode::IntSRight, false),
2879 (SignedLessThan, Opcode::IntSLess, false),
2880 (SignedGreaterThan, Opcode::IntSLess, true),
2881 (SignedLessEqual, Opcode::IntSLessEqual, false),
2882 (SignedGreaterEqual, Opcode::IntSLessEqual, true),
2883 (LessEqual, Opcode::IntLessEqual, false),
2884 (GreaterEqual, Opcode::IntLessEqual, true),
2885 (LessThan, Opcode::IntLess, false),
2886 (GreaterThan, Opcode::IntLess, true),
2887 (FloatLessEqual, Opcode::FloatLessEqual, false),
2888 (FloatGreaterEqual, Opcode::FloatLessEqual, true),
2889 (FloatLessThan, Opcode::FloatLess, false),
2890 (FloatGreaterThan, Opcode::FloatLess, true),
2891 (Equal, Opcode::IntEqual, false),
2892 (NotEqual, Opcode::IntNotEqual, false),
2893 (FloatEqual, Opcode::FloatEqual, false),
2894 (FloatNotEqual, Opcode::FloatNotEqual, false),
2895 (LogicalXor, Opcode::BoolXor, false),
2896 (LogicalAnd, Opcode::BoolAnd, false),
2897 (LogicalOr, Opcode::BoolOr, false),
2898 (BitwiseXor, Opcode::IntXor, false),
2899 (BitwiseOr, Opcode::IntOr, false),
2900 (BitwiseAnd, Opcode::IntAnd, false),
2901 ];
2902 for (operator, opcode, reverse) in cases {
2903 assert_eq!(super::binary_opcode(operator), (opcode, reverse));
2904 }
2905 }
2906
2907 #[test]
2908 fn lower_every_builtin_and_unary_operator() {
2909 let builtins = [
2910 (crate::Builtin::Carry, Opcode::IntCarry),
2911 (crate::Builtin::Scarry, Opcode::IntSCarry),
2912 (crate::Builtin::Sborrow, Opcode::IntSBorrow),
2913 (crate::Builtin::Nan, Opcode::FloatNan),
2914 (crate::Builtin::Abs, Opcode::FloatAbs),
2915 (crate::Builtin::Sqrt, Opcode::FloatSqrt),
2916 (crate::Builtin::Floor, Opcode::FloatFloor),
2917 (crate::Builtin::Ceil, Opcode::FloatCeil),
2918 (crate::Builtin::Round, Opcode::FloatRound),
2919 (crate::Builtin::Int2Float, Opcode::FloatInt2Float),
2920 (crate::Builtin::Float2Float, Opcode::FloatFloat2Float),
2921 (crate::Builtin::Trunc, Opcode::FloatTrunc),
2922 (crate::Builtin::Zext, Opcode::IntZext),
2923 (crate::Builtin::Sext, Opcode::IntSext),
2924 (crate::Builtin::Popcount, Opcode::PopCount),
2925 (crate::Builtin::Lzcount, Opcode::LzCount),
2926 (crate::Builtin::Cpool, Opcode::CpoolRef),
2927 (crate::Builtin::NewObject, Opcode::New),
2928 ];
2929 for (builtin, opcode) in builtins {
2930 let expression = Expression {
2931 ty: ExpressionTy::FunctionCall {
2932 builtin,
2933 args: vec![int(1, 4)],
2934 },
2935 size: Some(4),
2936 span: (),
2937 };
2938 let pcode = lower_instruction(
2939 &ast(vec![AstNode::Assignment {
2940 lhs: Ident::Named(LocalVarId(0)),
2941 size: None,
2942 rhs: expression,
2943 }]),
2944 &Context,
2945 )
2946 .unwrap();
2947 assert_eq!(pcode.ops[0].opcode, opcode);
2948 }
2949 for (operator, opcode) in [
2950 (crate::UnaryOperator::LogicalNot, Opcode::BoolNegate),
2951 (crate::UnaryOperator::BitwiseNot, Opcode::IntNegate),
2952 (crate::UnaryOperator::Minus, Opcode::Int2Comp),
2953 (crate::UnaryOperator::FloatMinus, Opcode::FloatNeg),
2954 ] {
2955 let expression = Expression {
2956 ty: ExpressionTy::Unop(crate::Unop {
2957 op: operator,
2958 e: Box::new(ident(RegisterId::new(0))),
2959 }),
2960 size: Some(4),
2961 span: (),
2962 };
2963 let pcode = lower_instruction(
2964 &ast(vec![AstNode::Assignment {
2965 lhs: Ident::Named(LocalVarId(0)),
2966 size: None,
2967 rhs: expression,
2968 }]),
2969 &Context,
2970 )
2971 .unwrap();
2972 assert_eq!(pcode.ops[0].opcode, opcode);
2973 }
2974 }
2975
2976 #[test]
2977 fn lower_direct_and_indirect_control_flow() {
2978 let direct = lower_instruction(
2979 &ast(vec![AstNode::Call {
2980 target: LabelOrNode::Expr(int(0x1000, 8)),
2981 }]),
2982 &Context,
2983 )
2984 .unwrap();
2985 assert_eq!(
2986 direct.ops[0],
2987 PcodeOp::new(
2988 Opcode::Call,
2989 None,
2990 vec![Varnode::new(SpaceId::new(1), 0x1000, 8)]
2991 )
2992 );
2993 let indirect = lower_instruction(
2994 &ast(vec![
2995 AstNode::ConditionalBranch {
2996 condition: int(1, 1),
2997 target: LabelOrNode::Expr(int(0x2000, 8)),
2998 },
2999 AstNode::BranchIndirect {
3000 target: ident(RegisterId::new(0)),
3001 },
3002 AstNode::CallIndirect {
3003 target: ident(RegisterId::new(1)),
3004 },
3005 AstNode::Return {
3006 target: ident(RegisterId::new(2)),
3007 },
3008 ]),
3009 &Context,
3010 )
3011 .unwrap();
3012 assert_eq!(
3013 indirect.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
3014 vec![
3015 Opcode::CBranch,
3016 Opcode::BranchInd,
3017 Opcode::CallInd,
3018 Opcode::Return
3019 ]
3020 );
3021 assert_eq!(
3022 indirect.ops[0].inputs,
3023 vec![
3024 Varnode::new(SpaceId::new(1), 0x2000, 8),
3025 Varnode::constant(1, 1)
3026 ]
3027 );
3028 }
3029
3030 #[test]
3031 fn lowering_errors_are_displayable_and_typed() {
3032 let errors = [
3033 PcodeLowerError::UnknownSize,
3034 PcodeLowerError::ZeroSize,
3035 PcodeLowerError::CopySizeMismatch {
3036 input: 1,
3037 output: 2,
3038 },
3039 PcodeLowerError::InputSizeMismatch {
3040 operation: "operation",
3041 left: 1,
3042 right: 2,
3043 },
3044 PcodeLowerError::InvalidBooleanSize(2),
3045 PcodeLowerError::AddressSizeMismatch {
3046 expected: 4,
3047 actual: 8,
3048 },
3049 PcodeLowerError::StoreSizeMismatch {
3050 declared: 4,
3051 value: 8,
3052 },
3053 PcodeLowerError::InvalidRange {
3054 start: 0,
3055 size: 0,
3056 storage_bits: 32,
3057 },
3058 PcodeLowerError::UniqueSpaceOverflow,
3059 PcodeLowerError::UnknownRegister(RegisterId::new(9)),
3060 PcodeLowerError::UnresolvedIdentifier("field"),
3061 PcodeLowerError::UnresolvedSpace,
3062 PcodeLowerError::UnresolvedRangeParameter,
3063 PcodeLowerError::InternalNode("macro"),
3064 PcodeLowerError::Unsupported("range"),
3065 PcodeLowerError::DuplicateLabel("loop".into()),
3066 PcodeLowerError::UnknownLabel("loop".into()),
3067 PcodeLowerError::InvalidDirectTarget,
3068 ];
3069 for error in errors {
3070 assert!(!error.to_string().is_empty());
3071 }
3072 assert_eq!(
3073 InstructionPcode::lower(&ast(vec![]), &Context).unwrap(),
3074 InstructionPcode::new()
3075 );
3076 }
3077
3078 #[test]
3079 fn lower_rejects_invalid_raw_widths_and_ranges() {
3080 let range = |start, size| Expression {
3081 ty: ExpressionTy::Range(crate::Range {
3082 value: Box::new(ident(RegisterId::new(0))),
3083 start: crate::RangeParam::Literal(start),
3084 size: crate::RangeParam::Literal(size),
3085 }),
3086 size: None,
3087 span: (),
3088 };
3089 for (start, size) in [(0, 0), (0, 65), (31, 2)] {
3090 let error = lower_instruction(
3091 &ast(vec![AstNode::Assignment {
3092 lhs: Ident::Register(RegisterId::new(0)),
3093 size: None,
3094 rhs: range(start, size),
3095 }]),
3096 &Context,
3097 )
3098 .unwrap_err();
3099 assert!(matches!(error, PcodeLowerError::InvalidRange { .. }));
3100 }
3101
3102 let error = lower_instruction(
3103 &ast(vec![AstNode::Assignment {
3104 lhs: Ident::Named(LocalVarId(0)),
3105 size: None,
3106 rhs: Expression {
3107 ty: ExpressionTy::Range(crate::Range {
3108 value: Box::new(ident(RegisterId::new(0))),
3109 start: crate::RangeParam::MacroArg(LocalVarId(1)),
3110 size: crate::RangeParam::Literal(1),
3111 }),
3112 size: None,
3113 span: (),
3114 },
3115 }]),
3116 &Context,
3117 )
3118 .unwrap_err();
3119 assert_eq!(error, PcodeLowerError::UnresolvedRangeParameter);
3120
3121 let mismatch = ExpressionTy::Binop(Binop {
3122 op: BinaryOperator::Add,
3123 lhs: Box::new(ident(RegisterId::new(0))),
3124 rhs: Box::new(int(1, 1)),
3125 })
3126 .with_size(4);
3127 assert!(
3130 lower_instruction(
3131 &ast(vec![AstNode::Assignment {
3132 lhs: Ident::Named(LocalVarId(0)),
3133 size: None,
3134 rhs: mismatch,
3135 }]),
3136 &Context,
3137 )
3138 .is_ok()
3139 );
3140
3141 let comparison = ExpressionTy::Binop(Binop {
3142 op: BinaryOperator::Equal,
3143 lhs: Box::new(ident(RegisterId::new(0))),
3144 rhs: Box::new(ident(RegisterId::new(1))),
3145 })
3146 .with_size(4);
3147 assert_eq!(
3148 lower_instruction(
3149 &ast(vec![AstNode::Assignment {
3150 lhs: Ident::Named(LocalVarId(0)),
3151 size: None,
3152 rhs: comparison,
3153 }]),
3154 &Context,
3155 )
3156 .unwrap_err(),
3157 PcodeLowerError::InvalidBooleanSize(4)
3158 );
3159
3160 let bad_load = Load {
3161 space: Some(PcodeSpaceRef::Resolved(SpaceId::new(1))),
3162 size: Some(4),
3163 ptr: Box::new(ident(RegisterId::new(0))),
3164 };
3165 assert!(matches!(
3166 lower_instruction(
3167 &ast(vec![AstNode::Assignment {
3168 lhs: Ident::Named(LocalVarId(0)),
3169 size: Some(4),
3170 rhs: ExpressionTy::Load(bad_load.clone()).with_size(4),
3171 }]),
3172 &Context,
3173 ),
3174 Err(PcodeLowerError::AddressSizeMismatch { .. })
3175 ));
3176 assert!(matches!(
3177 lower_instruction(
3178 &ast(vec![AstNode::LoadAssignment {
3179 lhs: bad_load,
3180 size: None,
3181 rhs: int(1, 1),
3182 }]),
3183 &Context,
3184 ),
3185 Err(PcodeLowerError::AddressSizeMismatch { .. })
3186 ));
3187 }
3188
3189 #[test]
3190 fn lower_handles_subpieces_and_rejects_invalid_final_forms() {
3191 let lsb = Expression {
3192 ty: ExpressionTy::SubPieceLsb {
3193 src: Box::new(ident(RegisterId::new(0))),
3194 count: 2,
3195 },
3196 size: Some(2),
3197 span: (),
3198 };
3199 let msb = Expression {
3200 ty: ExpressionTy::SubPieceMsb {
3201 src: Box::new(ident(RegisterId::new(0))),
3202 count: 2,
3203 },
3204 size: Some(2),
3205 span: (),
3206 };
3207 let pcode = lower_instruction(
3208 &ast(vec![
3209 AstNode::Assignment {
3210 lhs: Ident::Named(LocalVarId(0)),
3211 size: None,
3212 rhs: lsb,
3213 },
3214 AstNode::Assignment {
3215 lhs: Ident::Named(LocalVarId(1)),
3216 size: None,
3217 rhs: msb,
3218 },
3219 ]),
3220 &Context,
3221 )
3222 .unwrap();
3223 assert_eq!(
3224 pcode.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
3225 vec![Opcode::SubPiece, Opcode::SubPiece]
3226 );
3227 assert_eq!(pcode.ops[0].inputs[1], Varnode::constant(0, 8));
3228 assert_eq!(pcode.ops[1].inputs[1], Varnode::constant(2, 8));
3229
3230 let range = Expression {
3231 ty: ExpressionTy::Range(crate::Range {
3232 value: Box::new(ident(RegisterId::new(0))),
3233 start: crate::RangeParam::Literal(0),
3234 size: crate::RangeParam::Literal(1),
3235 }),
3236 size: Some(1),
3237 span: (),
3238 };
3239 let pcode = lower_instruction(
3240 &ast(vec![AstNode::Assignment {
3241 lhs: Ident::Named(LocalVarId(0)),
3242 size: None,
3243 rhs: range,
3244 }]),
3245 &Context,
3246 )
3247 .unwrap();
3248 assert_eq!(
3249 pcode.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
3250 vec![Opcode::IntRight, Opcode::IntAnd, Opcode::SubPiece]
3251 );
3252 let error = lower_instruction(
3253 &ast(vec![
3254 AstNode::Label("same".into()),
3255 AstNode::Label("same".into()),
3256 ]),
3257 &Context,
3258 )
3259 .unwrap_err();
3260 assert_eq!(error, PcodeLowerError::DuplicateLabel("same".into()));
3261 let error = lower_instruction(
3262 &ast(vec![AstNode::Branch {
3263 target: LabelOrNode::Label("missing".into()),
3264 }]),
3265 &Context,
3266 )
3267 .unwrap_err();
3268 assert_eq!(error, PcodeLowerError::UnknownLabel("missing".into()));
3269 let error = lower_instruction(
3270 &ast(vec![AstNode::Branch {
3271 target: LabelOrNode::Expr(ident(RegisterId::new(0))),
3272 }]),
3273 &Context,
3274 )
3275 .unwrap_err();
3276 assert_eq!(error, PcodeLowerError::InvalidDirectTarget);
3277 }
3278}