1use crate::{
13 Ast, AstNode, BinaryOperator, BitRangeFieldId, Builtin, Expression, ExpressionTy, Ident,
14 LabelOrNode, LocalVarId, PCodeOpId, PcodeAst, RangeParam, RegisterId, SPACE_CONST, SpaceId,
15 TableId, UnaryOperator,
16 streaming::{ExprKind, ExprNode, LoadNode, LoadSpace, RangeNode, StmtKind, TargetNode},
17};
18use serde::{Deserialize, Serialize};
19use std::{
20 collections::{HashMap, HashSet},
21 fmt,
22};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub struct Varnode {
31 pub space: SpaceId,
33 pub offset: u64,
35 pub size: usize,
37}
38
39impl Varnode {
40 pub const fn new(space: SpaceId, offset: u64, size: usize) -> Self {
42 Self {
43 space,
44 offset,
45 size,
46 }
47 }
48
49 pub const fn constant(value: u64, size: usize) -> Self {
51 Self::new(SPACE_CONST, value, size)
52 }
53
54 pub fn is_constant(self) -> bool {
56 self.space == SPACE_CONST
57 }
58}
59
60#[repr(u8)]
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
68pub enum Opcode {
69 Copy = 1,
70 Load,
71 Store,
72 Branch,
73 CBranch,
74 BranchInd,
75 Call,
76 CallInd,
77 CallOther,
78 Return,
79 IntEqual,
80 IntNotEqual,
81 IntSLess,
82 IntSLessEqual,
83 IntLess,
84 IntLessEqual,
85 IntZext,
86 IntSext,
87 IntAdd,
88 IntSub,
89 IntCarry,
90 IntSCarry,
91 IntSBorrow,
92 Int2Comp,
93 IntNegate,
94 IntXor,
95 IntAnd,
96 IntOr,
97 IntLeft,
98 IntRight,
99 IntSRight,
100 IntMult,
101 IntDiv,
102 IntSDiv,
103 IntRem,
104 IntSRem,
105 BoolNegate,
106 BoolXor,
107 BoolAnd,
108 BoolOr,
109 FloatEqual,
110 FloatNotEqual,
111 FloatLess,
112 FloatLessEqual,
113 FloatNan = 46,
115 FloatAdd,
116 FloatDiv,
117 FloatMult,
118 FloatSub,
119 FloatNeg,
120 FloatAbs,
121 FloatSqrt,
122 FloatInt2Float,
123 FloatFloat2Float,
124 FloatTrunc,
125 FloatCeil,
126 FloatFloor,
127 FloatRound,
128 MultiEqual,
129 Indirect,
130 Piece,
131 SubPiece,
132 Cast,
133 PtrAdd,
134 PtrSub,
135 SegmentOp,
136 CpoolRef,
137 New,
138 Insert,
139 Extract,
140 PopCount,
141 LzCount,
142}
143
144impl Opcode {
145 pub const ALL: &[Self] = &[
147 Self::Copy,
148 Self::Load,
149 Self::Store,
150 Self::Branch,
151 Self::CBranch,
152 Self::BranchInd,
153 Self::Call,
154 Self::CallInd,
155 Self::CallOther,
156 Self::Return,
157 Self::IntEqual,
158 Self::IntNotEqual,
159 Self::IntSLess,
160 Self::IntSLessEqual,
161 Self::IntLess,
162 Self::IntLessEqual,
163 Self::IntZext,
164 Self::IntSext,
165 Self::IntAdd,
166 Self::IntSub,
167 Self::IntCarry,
168 Self::IntSCarry,
169 Self::IntSBorrow,
170 Self::Int2Comp,
171 Self::IntNegate,
172 Self::IntXor,
173 Self::IntAnd,
174 Self::IntOr,
175 Self::IntLeft,
176 Self::IntRight,
177 Self::IntSRight,
178 Self::IntMult,
179 Self::IntDiv,
180 Self::IntSDiv,
181 Self::IntRem,
182 Self::IntSRem,
183 Self::BoolNegate,
184 Self::BoolXor,
185 Self::BoolAnd,
186 Self::BoolOr,
187 Self::FloatEqual,
188 Self::FloatNotEqual,
189 Self::FloatLess,
190 Self::FloatLessEqual,
191 Self::FloatNan,
192 Self::FloatAdd,
193 Self::FloatDiv,
194 Self::FloatMult,
195 Self::FloatSub,
196 Self::FloatNeg,
197 Self::FloatAbs,
198 Self::FloatSqrt,
199 Self::FloatInt2Float,
200 Self::FloatFloat2Float,
201 Self::FloatTrunc,
202 Self::FloatCeil,
203 Self::FloatFloor,
204 Self::FloatRound,
205 Self::MultiEqual,
206 Self::Indirect,
207 Self::Piece,
208 Self::SubPiece,
209 Self::Cast,
210 Self::PtrAdd,
211 Self::PtrSub,
212 Self::SegmentOp,
213 Self::CpoolRef,
214 Self::New,
215 Self::Insert,
216 Self::Extract,
217 Self::PopCount,
218 Self::LzCount,
219 ];
220
221 pub const fn ghidra_id(self) -> u8 {
223 self as u8
224 }
225
226 pub const fn is_raw_instruction_op(self) -> bool {
232 !matches!(
233 self,
234 Self::MultiEqual
235 | Self::Indirect
236 | Self::Cast
237 | Self::PtrAdd
238 | Self::PtrSub
239 | Self::SegmentOp
240 | Self::Insert
241 | Self::Extract
242 )
243 }
244}
245
246#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
248pub struct PcodeOp {
249 pub opcode: Opcode,
251 pub output: Option<Varnode>,
253 pub inputs: Vec<Varnode>,
255}
256
257impl PcodeOp {
258 pub fn new(opcode: Opcode, output: Option<Varnode>, inputs: Vec<Varnode>) -> Self {
260 Self {
261 opcode,
262 output,
263 inputs,
264 }
265 }
266}
267
268#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
270pub struct InstructionPcode {
271 pub ops: Vec<PcodeOp>,
273}
274
275impl InstructionPcode {
276 pub const fn new() -> Self {
278 Self { ops: Vec::new() }
279 }
280
281 pub fn is_empty(&self) -> bool {
283 self.ops.is_empty()
284 }
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
292pub struct BitRangeInfo {
293 pub storage: Varnode,
295 pub start: usize,
297 pub size: usize,
299}
300
301pub trait PcodeLoweringContext {
307 fn default_space(&self) -> SpaceId;
309 fn unique_space(&self) -> SpaceId;
311 fn register_varnode(&self, id: RegisterId) -> Option<Varnode>;
313 fn bitrange_info(&self, id: BitRangeFieldId) -> Option<BitRangeInfo>;
315 fn address_size(&self, space: SpaceId) -> Option<usize>;
317}
318
319#[derive(Debug, Clone, PartialEq, Eq)]
321pub enum PcodeLowerError {
322 UnknownSize,
324 ZeroSize,
326 CopySizeMismatch { input: usize, output: usize },
328 InputSizeMismatch {
330 operation: &'static str,
331 left: usize,
332 right: usize,
333 },
334 InvalidBooleanSize(usize),
336 AddressSizeMismatch { expected: usize, actual: usize },
338 StoreSizeMismatch { declared: usize, value: usize },
340 InvalidRange {
342 start: usize,
343 size: usize,
344 storage_bits: usize,
345 },
346 UniqueSpaceOverflow,
348 UnknownRegister(RegisterId),
350 UnresolvedIdentifier(&'static str),
352 UnresolvedSpace,
354 UnresolvedRangeParameter,
356 InternalNode(&'static str),
358 Unsupported(&'static str),
360 DuplicateLabel(Box<str>),
362 UnknownLabel(Box<str>),
364 InvalidDirectTarget,
366}
367
368impl fmt::Display for PcodeLowerError {
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 match self {
371 Self::UnknownSize => write!(f, "p-code expression has no known byte width"),
372 Self::ZeroSize => write!(f, "p-code varnodes cannot have zero byte width"),
373 Self::CopySizeMismatch { input, output } => write!(
374 f,
375 "p-code COPY requires equal widths, got {input} and {output} bytes"
376 ),
377 Self::InputSizeMismatch {
378 operation,
379 left,
380 right,
381 } => write!(
382 f,
383 "p-code {operation} requires equal input widths, got {left} and {right} bytes"
384 ),
385 Self::InvalidBooleanSize(size) => {
386 write!(
387 f,
388 "p-code comparison and boolean outputs must be one byte, got {size}"
389 )
390 }
391 Self::AddressSizeMismatch { expected, actual } => write!(
392 f,
393 "p-code memory pointer requires {expected} bytes, got {actual}"
394 ),
395 Self::StoreSizeMismatch { declared, value } => write!(
396 f,
397 "p-code STORE declares {declared} bytes but receives {value}"
398 ),
399 Self::InvalidRange {
400 start,
401 size,
402 storage_bits,
403 } => write!(
404 f,
405 "invalid bit range [{start}, {size}] for {storage_bits}-bit storage"
406 ),
407 Self::UniqueSpaceOverflow => write!(f, "unique-space temporary allocation overflowed"),
408 Self::UnknownRegister(id) => write!(f, "unknown register {}", usize::from(*id)),
409 Self::UnresolvedIdentifier(kind) => {
410 write!(f, "unresolved {kind} reached p-code lowering")
411 }
412 Self::UnresolvedSpace => write!(f, "unresolved address space reached p-code lowering"),
413 Self::UnresolvedRangeParameter => {
414 write!(f, "unresolved range parameter reached p-code lowering")
415 }
416 Self::InternalNode(kind) => write!(f, "unexpanded {kind} reached p-code lowering"),
417 Self::Unsupported(what) => write!(f, "raw p-code lowering does not support {what}"),
418 Self::DuplicateLabel(label) => write!(f, "duplicate p-code label `{label}`"),
419 Self::UnknownLabel(label) => write!(f, "unknown p-code label `{label}`"),
420 Self::InvalidDirectTarget => write!(f, "invalid direct p-code branch target"),
421 }
422 }
423}
424
425impl std::error::Error for PcodeLowerError {}
426
427#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
433pub struct LabelId(u32);
434
435impl LabelId {
436 pub const fn index(self) -> usize {
438 self.0 as usize
439 }
440
441 pub const fn from_index(index: usize) -> Self {
443 Self(index as u32)
444 }
445}
446
447#[derive(Debug, Clone, Default)]
454pub struct PcodePlan {
455 local_sizes: HashMap<LocalVarId, usize>,
458 labels: Vec<Box<str>>,
462 terminal: Vec<bool>,
465 direct_branches: Vec<u64>,
466 direct_calls: Vec<u64>,
467}
468
469impl PcodePlan {
470 pub fn labels(&self) -> &[Box<str>] {
472 &self.labels
473 }
474
475 pub fn is_terminal(&self, label: LabelId) -> bool {
480 self.terminal[label.index()]
481 }
482
483 pub fn direct_branches(&self) -> &[u64] {
485 &self.direct_branches
486 }
487
488 pub fn direct_calls(&self) -> &[u64] {
490 &self.direct_calls
491 }
492
493 pub fn declare_label(&mut self, label: &str) -> LabelId {
500 if let Some(id) = self.label_id(label) {
501 return id;
502 }
503 let id = LabelId(self.labels.len() as u32);
504 self.labels.push(Box::from(label));
505 self.terminal.push(false);
506 id
507 }
508
509 pub fn declare_direct_branch(&mut self, address: u64) {
511 if !self.direct_branches.contains(&address) {
512 self.direct_branches.push(address);
513 }
514 }
515
516 pub fn declare_direct_call(&mut self, address: u64) {
518 if !self.direct_calls.contains(&address) {
519 self.direct_calls.push(address);
520 }
521 }
522
523 fn label_id(&self, label: &str) -> Option<LabelId> {
524 self.labels
525 .iter()
526 .position(|name| **name == *label)
527 .map(|index| LabelId(index as u32))
528 }
529}
530
531pub trait PcodeSink {
537 fn op(&mut self, opcode: Opcode, output: Option<Varnode>, inputs: &[Varnode]);
540
541 fn label(&mut self, label: LabelId);
544
545 fn branch_label(&mut self, opcode: Opcode, label: LabelId, condition: Option<Varnode>);
549}
550
551#[derive(Debug, Default)]
554struct Collector {
555 ops: Vec<PcodeOp>,
556 label_ops: HashMap<LabelId, usize>,
557 fixups: Vec<(usize, LabelId)>,
558}
559
560impl Collector {
561 fn finish(mut self, plan: &PcodePlan) -> Result<Vec<PcodeOp>, PcodeLowerError> {
562 for (op_index, label) in &self.fixups {
563 let target = *self
564 .label_ops
565 .get(label)
566 .ok_or_else(|| PcodeLowerError::UnknownLabel(plan.labels[label.index()].clone()))?;
567 let relative = i64::try_from(target)
568 .ok()
569 .and_then(|target| {
570 i64::try_from(*op_index)
571 .ok()
572 .and_then(|source| target.checked_sub(source))
573 })
574 .ok_or(PcodeLowerError::UniqueSpaceOverflow)?;
575 self.ops[*op_index].inputs[0] = Varnode::constant(relative as u64, 8);
576 }
577 Ok(self.ops)
578 }
579}
580
581impl PcodeSink for Collector {
582 fn op(&mut self, opcode: Opcode, output: Option<Varnode>, inputs: &[Varnode]) {
583 self.ops.push(PcodeOp::new(opcode, output, inputs.to_vec()));
584 }
585
586 fn label(&mut self, label: LabelId) {
587 self.label_ops.insert(label, self.ops.len());
588 }
589
590 fn branch_label(&mut self, opcode: Opcode, label: LabelId, condition: Option<Varnode>) {
591 let mut inputs = Vec::with_capacity(1 + usize::from(condition.is_some()));
592 inputs.push(Varnode::constant(0, 8));
593 inputs.extend(condition);
594 self.fixups.push((self.ops.len(), label));
595 self.ops.push(PcodeOp::new(opcode, None, inputs));
596 }
597}
598
599pub fn plan_instruction(
605 ast: &PcodeAst,
606 context: &impl PcodeLoweringContext,
607) -> Result<PcodePlan, PcodeLowerError> {
608 let local_sizes = SizeInference::run(context, &ast.statements);
609 plan_instruction_with(ast, context, local_sizes)
610}
611
612pub trait Width: Copy + Eq + std::fmt::Debug {
621 fn fixed(size: usize) -> Self;
623
624 fn size(self) -> Option<usize>;
628
629 fn operand(_table: TableId) -> Option<Self> {
636 None
637 }
638}
639
640impl Width for usize {
641 fn fixed(size: usize) -> Self {
642 size
643 }
644
645 fn size(self) -> Option<usize> {
646 Some(self)
647 }
648}
649
650#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
652pub enum SymbolicWidth {
653 Fixed(usize),
655 SameAs(TableId),
657}
658
659impl Width for SymbolicWidth {
660 fn fixed(size: usize) -> Self {
661 Self::Fixed(size)
662 }
663
664 fn size(self) -> Option<usize> {
665 match self {
666 Self::Fixed(size) => Some(size),
667 Self::SameAs(_) => None,
668 }
669 }
670
671 fn operand(table: TableId) -> Option<Self> {
672 Some(Self::SameAs(table))
673 }
674}
675
676pub type LocalSizes = HashMap<LocalVarId, usize>;
678
679#[derive(Debug, Clone, Default)]
681pub struct BodyWidths<W> {
682 pub widths: HashMap<LocalVarId, W>,
684 pub unsized_locals: Vec<LocalVarId>,
690}
691
692impl<W> BodyWidths<W> {
693 pub fn is_complete(&self) -> bool {
695 self.unsized_locals.is_empty()
696 }
697}
698
699pub fn resolve_body_widths<S, W: Width>(
706 statements: &[Ast<S>],
707 context: &impl PcodeLoweringContext,
708) -> BodyWidths<W> {
709 let widths: HashMap<LocalVarId, W> = SizeInference::run(context, statements);
710 let mut used = Vec::new();
711 live_locals(statements, &mut used);
712 used.sort_unstable_by_key(|id| id.0);
713 used.dedup();
714 let unsized_locals = used
715 .into_iter()
716 .filter(|id| !widths.contains_key(id))
717 .collect();
718 BodyWidths {
719 widths,
720 unsized_locals,
721 }
722}
723
724fn live_locals<S>(statements: &[Ast<S>], out: &mut Vec<LocalVarId>) {
726 fn walk_expr<S>(value: &Expression<S>, out: &mut Vec<LocalVarId>) {
727 match &value.ty {
728 ExpressionTy::Ident(Ident::Named(id)) => out.push(*id),
729 ExpressionTy::Ident(_) | ExpressionTy::SizedInt { .. } => {}
730 ExpressionTy::Load(load) => walk_expr(&load.ptr, out),
731 ExpressionTy::Range(range) => walk_expr(&range.value, out),
732 ExpressionTy::SubPieceLsb { src, .. } | ExpressionTy::SubPieceMsb { src, .. } => {
733 walk_expr(src, out);
734 }
735 ExpressionTy::FunctionCall { args, .. }
736 | ExpressionTy::PcodeOp { args, .. }
737 | ExpressionTy::MacroCall { args, .. }
738 | ExpressionTy::DeferredCall { args, .. } => {
739 args.iter().for_each(|arg| walk_expr(arg, out));
740 }
741 ExpressionTy::Unop(unop) => walk_expr(&unop.e, out),
742 ExpressionTy::Binop(binop) => {
743 walk_expr(&binop.lhs, out);
744 walk_expr(&binop.rhs, out);
745 }
746 }
747 }
748 fn walk_target<S>(target: &LabelOrNode<S>, out: &mut Vec<LocalVarId>) {
749 if let LabelOrNode::Expr(value) = target {
750 walk_expr(value, out);
751 }
752 }
753 for statement in statements {
754 match &statement.ty {
755 AstNode::Assignment { lhs, rhs, .. } => {
756 if let Ident::Named(id) = lhs {
757 out.push(*id);
758 }
759 walk_expr(rhs, out);
760 }
761 AstNode::LoadAssignment { lhs, rhs, .. } => {
762 walk_expr(&lhs.ptr, out);
763 walk_expr(rhs, out);
764 }
765 AstNode::RangeAssignment { lhs, rhs, .. } => {
766 walk_expr(&lhs.value, out);
767 walk_expr(rhs, out);
768 }
769 AstNode::Branch { target } | AstNode::Call { target } => walk_target(target, out),
770 AstNode::ConditionalBranch { target, condition } => {
771 walk_target(target, out);
772 walk_expr(condition, out);
773 }
774 AstNode::BranchIndirect { target: value }
775 | AstNode::CallIndirect { target: value }
776 | AstNode::Return { target: value }
777 | AstNode::Expression(value)
778 | AstNode::Export(value) => walk_expr(value, out),
779 AstNode::Label(_)
780 | AstNode::Build(_)
781 | AstNode::DeferredBuild(_)
782 | AstNode::DelaySlot(_) => {}
783 }
784 }
785}
786
787pub fn infer_local_sizes<S, W: Width>(
799 statements: &[Ast<S>],
800 context: &impl PcodeLoweringContext,
801) -> HashMap<LocalVarId, W> {
802 SizeInference::run(context, statements)
803}
804
805pub fn plan_instruction_with(
811 ast: &PcodeAst,
812 _context: &impl PcodeLoweringContext,
815 local_sizes: LocalSizes,
816) -> Result<PcodePlan, PcodeLowerError> {
817 let mut planner = Planner::new();
818 for statement in &ast.statements {
819 planner.statement(StmtKind::from(&statement.ty));
820 }
821 Ok(planner.finish(local_sizes))
822}
823
824pub fn emit_instruction(
830 ast: &PcodeAst,
831 context: &impl PcodeLoweringContext,
832 plan: &PcodePlan,
833 sink: &mut impl PcodeSink,
834) -> Result<(), PcodeLowerError> {
835 let mut emitter = Emitter::new(context, plan, sink);
836 for statement in &ast.statements {
837 emitter.statement(StmtKind::from(&statement.ty))?;
838 }
839 Ok(())
840}
841
842pub fn lower_instruction(
853 ast: &PcodeAst,
854 context: &impl PcodeLoweringContext,
855) -> Result<InstructionPcode, PcodeLowerError> {
856 Ok(InstructionPcode {
857 ops: collect_ops(ast, context)?,
858 })
859}
860
861fn collect_ops(
862 ast: &PcodeAst,
863 context: &impl PcodeLoweringContext,
864) -> Result<Vec<PcodeOp>, PcodeLowerError> {
865 let plan = plan_instruction(ast, context)?;
866 let mut collector = Collector::default();
867 emit_instruction(ast, context, &plan, &mut collector)?;
868 collector.finish(&plan)
869}
870
871pub fn lower_instruction_into<R>(
878 ast: &PcodeAst,
879 context: &impl PcodeLoweringContext,
880 sink: impl FnOnce(&[PcodeOp]) -> R,
881) -> Result<R, PcodeLowerError> {
882 let ops = collect_ops(ast, context)?;
883 Ok(sink(&ops))
884}
885
886impl InstructionPcode {
887 pub fn lower(
889 ast: &PcodeAst,
890 context: &impl PcodeLoweringContext,
891 ) -> Result<Self, PcodeLowerError> {
892 lower_instruction(ast, context)
893 }
894}
895
896pub struct Emitter<'a, 'p, 's, C: PcodeLoweringContext + ?Sized, S: PcodeSink + ?Sized> {
906 context: &'a C,
907 plan: &'p PcodePlan,
908 sink: &'s mut S,
909 locals: HashMap<LocalVarId, Varnode>,
910 defined_labels: HashSet<LabelId>,
911 next_unique: u64,
912}
913
914impl<'a, 'p, 's, C: PcodeLoweringContext + ?Sized, S: PcodeSink + ?Sized>
915 Emitter<'a, 'p, 's, C, S>
916{
917 pub fn new(context: &'a C, plan: &'p PcodePlan, sink: &'s mut S) -> Self {
919 Self {
920 context,
921 plan,
922 sink,
923 locals: HashMap::with_capacity(plan.local_sizes.len()),
927 defined_labels: HashSet::with_capacity(plan.labels.len()),
928 next_unique: 0,
929 }
930 }
931
932 pub fn statement<E: ExprNode>(
937 &mut self,
938 statement: StmtKind<'_, E>,
939 ) -> Result<(), PcodeLowerError> {
940 self.lower_statement(statement)
941 }
942
943 fn emit(&mut self, opcode: Opcode, output: Option<Varnode>, inputs: &[Varnode]) {
944 self.sink.op(opcode, output, inputs);
945 }
946
947 fn lower_statement<E: ExprNode>(
948 &mut self,
949 statement: StmtKind<'_, E>,
950 ) -> Result<(), PcodeLowerError> {
951 match statement {
952 StmtKind::Assignment {
953 lhs: Ident::BitRange(id),
954 rhs,
955 ..
956 } => {
957 let info = self
958 .context
959 .bitrange_info(id)
960 .ok_or(PcodeLowerError::Unsupported("an unknown named bit range"))?;
961 self.insert_range(info.storage, info.start, info.size, rhs)?;
962 }
963 StmtKind::Assignment { lhs, size, rhs } => {
964 let output = self.storage_for_ident(lhs, size.or_else(|| self.expr_size(rhs)))?;
965 self.lower_expr(rhs, Some(output))?;
966 }
967 StmtKind::LoadAssignment { load, rhs, .. } => self.lower_store(load, rhs)?,
968 StmtKind::RangeAssignment { range, rhs, .. } => {
969 self.lower_range_assignment(range, rhs)?
970 }
971 StmtKind::Internal(node) => return Err(PcodeLowerError::InternalNode(node)),
972 StmtKind::Label(label) => {
973 let id = self.label_id(&label)?;
974 if !self.defined_labels.insert(id) {
975 return Err(PcodeLowerError::DuplicateLabel(Box::from(&*label)));
976 }
977 self.sink.label(id);
978 }
979 StmtKind::Branch { target } => self.lower_direct_flow(Opcode::Branch, target, None)?,
980 StmtKind::ConditionalBranch { condition, target } => {
981 let condition = self.lower_expr(condition, None)?;
982 self.lower_direct_flow(Opcode::CBranch, target, Some(condition))?;
983 }
984 StmtKind::BranchIndirect { target } => {
985 self.lower_indirect_flow(Opcode::BranchInd, target)?
986 }
987 StmtKind::Call { target } => self.lower_direct_flow(Opcode::Call, target, None)?,
988 StmtKind::CallIndirect { target } => {
989 self.lower_indirect_flow(Opcode::CallInd, target)?
990 }
991 StmtKind::Return { target } => self.lower_indirect_flow(Opcode::Return, target)?,
992 StmtKind::Expression(expr) => self.lower_effect(expr)?,
993 }
994 Ok(())
995 }
996
997 fn lower_store<E: ExprNode>(
998 &mut self,
999 load: LoadNode<'_, E>,
1000 rhs: E,
1001 ) -> Result<(), PcodeLowerError> {
1002 let space = self.load_space(&load)?;
1003 if space == SPACE_CONST {
1004 return Err(PcodeLowerError::Unsupported("a store to constant space"));
1005 }
1006 let ptr = self.lower_expr(load.ptr, None)?;
1007 self.validate_pointer(space, ptr)?;
1008 let value = match load.size {
1009 Some(size) => self.lower_expr_with_size(rhs, size)?,
1010 None => self.lower_expr(rhs, None)?,
1011 };
1012 if let Some(size) = load.size {
1013 Self::checked_size(size)?;
1014 if size != value.size {
1015 return Err(PcodeLowerError::StoreSizeMismatch {
1016 declared: size,
1017 value: value.size,
1018 });
1019 }
1020 }
1021 self.emit(Opcode::Store, None, &[Self::space_id(space), ptr, value]);
1022 Ok(())
1023 }
1024
1025 fn lower_direct_flow<E: ExprNode>(
1026 &mut self,
1027 opcode: Opcode,
1028 target: TargetNode<'_, E>,
1029 condition: Option<Varnode>,
1030 ) -> Result<(), PcodeLowerError> {
1031 let target = match target {
1032 TargetNode::Label(label) => {
1033 let id = self.label_id(&label)?;
1037 self.sink.branch_label(opcode, id, condition);
1038 return Ok(());
1039 }
1040 TargetNode::Node(_) => {
1041 return Err(PcodeLowerError::InternalNode("unresolved branch target"));
1042 }
1043 TargetNode::Expr(expr) => self.direct_target(expr)?,
1044 };
1045 match condition {
1046 Some(condition) => self.emit(opcode, None, &[target, condition]),
1047 None => self.emit(opcode, None, &[target]),
1048 }
1049 Ok(())
1050 }
1051
1052 fn lower_indirect_flow<E: ExprNode>(
1053 &mut self,
1054 opcode: Opcode,
1055 target: E,
1056 ) -> Result<(), PcodeLowerError> {
1057 let target = self.lower_expr(target, None)?;
1058 self.emit(opcode, None, &[target]);
1059 Ok(())
1060 }
1061
1062 fn direct_target<E: ExprNode>(&self, target: E) -> Result<Varnode, PcodeLowerError> {
1063 let ExprKind::SizedInt { value, size } = target.kind() else {
1064 return Err(PcodeLowerError::InvalidDirectTarget);
1065 };
1066 let size = size
1067 .or(target.size())
1068 .or_else(|| self.context.address_size(self.context.default_space()))
1069 .ok_or(PcodeLowerError::UnknownSize)?;
1070 Self::checked_size(size)?;
1071 Ok(Varnode::new(self.context.default_space(), value, size))
1072 }
1073
1074 fn lower_effect<E: ExprNode>(&mut self, expr: E) -> Result<(), PcodeLowerError> {
1075 match expr.kind() {
1076 ExprKind::PcodeOp { id, args } => {
1077 let inputs = self.lower_userop_inputs::<E>(id, args)?;
1078 self.emit(Opcode::CallOther, None, &inputs);
1079 Ok(())
1080 }
1081 ExprKind::Internal(node) => Err(PcodeLowerError::InternalNode(node)),
1082 _ => Err(PcodeLowerError::Unsupported("a discarded value expression")),
1083 }
1084 }
1085
1086 fn lower_expr<E: ExprNode>(
1087 &mut self,
1088 expr: E,
1089 requested_output: Option<Varnode>,
1090 ) -> Result<Varnode, PcodeLowerError> {
1091 match expr.kind() {
1092 ExprKind::SizedInt { value, size } => {
1093 let input = Varnode::constant(
1098 value,
1099 requested_output
1100 .map(|output| output.size)
1101 .or(size)
1102 .or(expr.size())
1103 .ok_or(PcodeLowerError::UnknownSize)?,
1104 );
1105 self.copy_if_requested(input, requested_output)
1106 }
1107 ExprKind::Ident(Ident::BitRange(id)) => self.lower_named_bitrange(id, requested_output),
1108 ExprKind::Ident(ident) => {
1109 let input = self.storage_for_ident(ident, expr.size())?;
1110 self.copy_if_requested(input, requested_output)
1111 }
1112 ExprKind::Load(load) => self.lower_load(expr, load, requested_output),
1113 ExprKind::SubPieceMsb { src, count } => {
1114 let input = self.lower_expr(src, None)?;
1115 let size = requested_output
1116 .map(|output| output.size)
1117 .or(expr.size())
1118 .unwrap_or_else(|| input.size.saturating_sub(count));
1119 let output = self.output(requested_output, size)?;
1120 if count >= input.size || size > input.size - count {
1121 return Err(PcodeLowerError::InvalidRange {
1122 start: count.saturating_mul(8),
1123 size: size.saturating_mul(8),
1124 storage_bits: input.size.saturating_mul(8),
1125 });
1126 }
1127 self.emit(
1128 Opcode::SubPiece,
1129 Some(output),
1130 &[input, Varnode::constant(count as u64, 8)],
1131 );
1132 Ok(output)
1133 }
1134 ExprKind::SubPieceLsb { src, count } => {
1135 let input = self.lower_expr(src, None)?;
1136 if count == 0 || count > input.size {
1137 return Err(PcodeLowerError::InvalidRange {
1138 start: 0,
1139 size: count.saturating_mul(8),
1140 storage_bits: input.size.saturating_mul(8),
1141 });
1142 }
1143 let output = self.output(requested_output, count)?;
1144 self.emit(
1145 Opcode::SubPiece,
1146 Some(output),
1147 &[input, Varnode::constant(0, 8)],
1148 );
1149 Ok(output)
1150 }
1151 ExprKind::Range(range) => self.lower_range(range, requested_output),
1152 ExprKind::FunctionCall { builtin, args } => {
1153 self.lower_builtin(expr, builtin, args, requested_output)
1154 }
1155 ExprKind::PcodeOp { id, args } => {
1156 let size = requested_output
1157 .map(|output| output.size)
1158 .or(expr.size())
1159 .ok_or(PcodeLowerError::UnknownSize)?;
1160 let output = self.output(requested_output, size)?;
1161 let inputs = self.lower_userop_inputs::<E>(id, args)?;
1162 self.emit(Opcode::CallOther, Some(output), &inputs);
1163 Ok(output)
1164 }
1165 ExprKind::Internal(node) => Err(PcodeLowerError::InternalNode(node)),
1166 ExprKind::Unop { op, e } => self.lower_unop(expr, op, e, requested_output),
1167 ExprKind::Binop { op, lhs, rhs } => {
1168 self.lower_binop(expr, op, lhs, rhs, requested_output)
1169 }
1170 }
1171 }
1172
1173 fn lower_load<E: ExprNode>(
1174 &mut self,
1175 expr: E,
1176 load: LoadNode<'_, E>,
1177 requested_output: Option<Varnode>,
1178 ) -> Result<Varnode, PcodeLowerError> {
1179 let space = self.load_space(&load)?;
1180 let ptr = self.lower_expr(load.ptr, None)?;
1181 if space != SPACE_CONST {
1182 self.validate_pointer(space, ptr)?;
1183 }
1184 let size = requested_output
1185 .map(|output| output.size)
1186 .or(load.size)
1187 .or(expr.size())
1188 .ok_or(PcodeLowerError::UnknownSize)?;
1189 if space == SPACE_CONST {
1190 if ptr.size != size {
1191 return Err(PcodeLowerError::Unsupported(
1192 "a constant-space load that changes width",
1193 ));
1194 }
1195 return self.copy_if_requested(ptr, requested_output);
1196 }
1197 let output = self.output(requested_output, size)?;
1198 self.emit(Opcode::Load, Some(output), &[Self::space_id(space), ptr]);
1199 Ok(output)
1200 }
1201
1202 fn lower_builtin<E: ExprNode>(
1203 &mut self,
1204 expr: E,
1205 builtin: Builtin,
1206 args: E::Args,
1207 requested_output: Option<Varnode>,
1208 ) -> Result<Varnode, PcodeLowerError> {
1209 let opcode = match builtin {
1210 Builtin::Carry => Opcode::IntCarry,
1211 Builtin::Scarry => Opcode::IntSCarry,
1212 Builtin::Sborrow => Opcode::IntSBorrow,
1213 Builtin::Nan => Opcode::FloatNan,
1214 Builtin::Abs => Opcode::FloatAbs,
1215 Builtin::Sqrt => Opcode::FloatSqrt,
1216 Builtin::Floor => Opcode::FloatFloor,
1217 Builtin::Ceil => Opcode::FloatCeil,
1218 Builtin::Round => Opcode::FloatRound,
1219 Builtin::Int2Float => Opcode::FloatInt2Float,
1220 Builtin::Float2Float => Opcode::FloatFloat2Float,
1221 Builtin::Trunc => Opcode::FloatTrunc,
1222 Builtin::Zext => Opcode::IntZext,
1223 Builtin::Sext => Opcode::IntSext,
1224 Builtin::Popcount => Opcode::PopCount,
1225 Builtin::Lzcount => Opcode::LzCount,
1226 Builtin::Cpool => Opcode::CpoolRef,
1227 Builtin::NewObject => Opcode::New,
1228 };
1229 let size = requested_output
1230 .map(|output| output.size)
1231 .or(expr.size())
1232 .or_else(|| match builtin {
1233 Builtin::Carry | Builtin::Scarry | Builtin::Sborrow | Builtin::Nan => Some(1),
1234 Builtin::Abs | Builtin::Sqrt | Builtin::Floor | Builtin::Ceil | Builtin::Round => {
1237 args.clone().next().and_then(|arg| self.expr_size(arg))
1238 }
1239 _ => None,
1240 })
1241 .ok_or(PcodeLowerError::UnknownSize)?;
1242 let output = self.output(requested_output, size)?;
1243 let mut inputs = Inputs::default();
1248 if matches!(builtin, Builtin::Carry | Builtin::Scarry | Builtin::Sborrow) && args.len() != 0
1249 {
1250 let operand_size = args
1254 .clone()
1255 .find_map(|arg| self.expr_size(arg))
1256 .ok_or(PcodeLowerError::UnknownSize)?;
1257 for arg in args {
1258 inputs.push(self.lower_expr_with_size(arg, operand_size)?);
1259 }
1260 } else {
1261 for arg in args {
1262 inputs.push(self.lower_expr(arg, None)?);
1263 }
1264 }
1265 self.emit(opcode, Some(output), &inputs);
1266 Ok(output)
1267 }
1268
1269 fn lower_unop<E: ExprNode>(
1270 &mut self,
1271 expr: E,
1272 op: UnaryOperator,
1273 operand: E,
1274 requested_output: Option<Varnode>,
1275 ) -> Result<Varnode, PcodeLowerError> {
1276 if let UnaryOperator::AddressOf(size) = op {
1277 if let ExprKind::SizedInt {
1280 value,
1281 size: literal_size,
1282 } = operand.kind()
1283 {
1284 let size = size
1285 .or(literal_size)
1286 .or(operand.size())
1287 .ok_or(PcodeLowerError::UnknownSize)?;
1288 return self.copy_if_requested(Varnode::constant(value, size), requested_output);
1289 }
1290 let storage = self.storage_from_expr(operand)?;
1291 let size = size
1292 .or_else(|| self.context.address_size(storage.space))
1293 .ok_or(PcodeLowerError::UnknownSize)?;
1294 return self
1295 .copy_if_requested(Varnode::constant(storage.offset, size), requested_output);
1296 }
1297 let opcode = match op {
1298 UnaryOperator::LogicalNot => Opcode::BoolNegate,
1299 UnaryOperator::BitwiseNot => Opcode::IntNegate,
1300 UnaryOperator::Minus => Opcode::Int2Comp,
1301 UnaryOperator::FloatMinus => Opcode::FloatNeg,
1302 UnaryOperator::AddressOf(_) => unreachable!(),
1303 };
1304 let size = requested_output
1309 .map(|output| output.size)
1310 .or(expr.size())
1311 .or_else(|| (op == UnaryOperator::LogicalNot).then_some(1))
1312 .or_else(|| self.expr_size(operand))
1313 .ok_or(PcodeLowerError::UnknownSize)?;
1314 let input = if self.expr_size(operand).is_some() {
1318 self.lower_expr(operand, None)?
1319 } else {
1320 self.lower_expr_with_size(operand, size)?
1321 };
1322 let output = self.output(requested_output, size)?;
1323 self.emit(opcode, Some(output), &[input]);
1324 Ok(output)
1325 }
1326
1327 fn lower_binop<E: ExprNode>(
1328 &mut self,
1329 expr: E,
1330 op: BinaryOperator,
1331 lhs: E,
1332 rhs: E,
1333 requested_output: Option<Varnode>,
1334 ) -> Result<Varnode, PcodeLowerError> {
1335 let (opcode, reverse) = binary_opcode(op);
1336 let is_boolean = op.is_comparison()
1342 || matches!(
1343 op,
1344 BinaryOperator::LogicalXor | BinaryOperator::LogicalAnd | BinaryOperator::LogicalOr
1345 );
1346 let input_size = if is_boolean {
1347 self.expr_size(lhs).or_else(|| self.expr_size(rhs))
1348 } else {
1349 requested_output
1350 .map(|output| output.size)
1351 .or(expr.size())
1352 .or_else(|| self.expr_size(lhs))
1353 .or_else(|| self.expr_size(rhs))
1354 };
1355 let mut inputs = match input_size {
1356 Some(size) => [
1357 self.lower_expr_with_size(lhs, size)?,
1358 self.lower_expr_with_size(rhs, size)?,
1359 ],
1360 None => [self.lower_expr(lhs, None)?, self.lower_expr(rhs, None)?],
1361 };
1362 if reverse {
1363 inputs.swap(0, 1);
1364 }
1365 let size = requested_output
1366 .map(|output| output.size)
1367 .or(expr.size())
1368 .or_else(|| op.is_comparison().then_some(1))
1369 .or(input_size)
1370 .ok_or(PcodeLowerError::UnknownSize)?;
1371 let output = self.output(requested_output, size)?;
1372 if is_boolean && output.size != 1 {
1373 return Err(PcodeLowerError::InvalidBooleanSize(output.size));
1374 }
1375 if inputs[0].size != inputs[1].size {
1376 return Err(PcodeLowerError::InputSizeMismatch {
1377 operation: "binary operation",
1378 left: inputs[0].size,
1379 right: inputs[1].size,
1380 });
1381 }
1382 if !is_boolean && output.size != inputs[0].size {
1383 return Err(PcodeLowerError::CopySizeMismatch {
1384 input: inputs[0].size,
1385 output: output.size,
1386 });
1387 }
1388 self.emit(opcode, Some(output), &inputs);
1389 Ok(output)
1390 }
1391
1392 fn lower_expr_with_size<E: ExprNode>(
1401 &mut self,
1402 expr: E,
1403 size: usize,
1404 ) -> Result<Varnode, PcodeLowerError> {
1405 if let ExprKind::SizedInt { value, .. } = expr.kind() {
1410 return Ok(Varnode::constant(value, size));
1411 }
1412 if self.expr_size(expr) == Some(size) {
1413 return self.lower_expr(expr, None);
1414 }
1415 let output = self.allocate_unique(size)?;
1416 self.lower_expr(expr, Some(output))
1417 }
1418
1419 fn lower_range<E: ExprNode>(
1420 &mut self,
1421 range: RangeNode<E>,
1422 requested_output: Option<Varnode>,
1423 ) -> Result<Varnode, PcodeLowerError> {
1424 let input = self.lower_expr(range.value, None)?;
1425 let (start, bits) = range_params(&range)?;
1426 self.extract_range(input, start, bits, requested_output)
1427 }
1428
1429 fn lower_named_bitrange(
1430 &mut self,
1431 id: BitRangeFieldId,
1432 requested_output: Option<Varnode>,
1433 ) -> Result<Varnode, PcodeLowerError> {
1434 let info = self
1435 .context
1436 .bitrange_info(id)
1437 .ok_or(PcodeLowerError::Unsupported("an unknown named bit range"))?;
1438 self.extract_range(info.storage, info.start, info.size, requested_output)
1439 }
1440
1441 fn extract_range(
1442 &mut self,
1443 input: Varnode,
1444 start: usize,
1445 bits: usize,
1446 requested_output: Option<Varnode>,
1447 ) -> Result<Varnode, PcodeLowerError> {
1448 let result_size = Self::validate_range(input, start, bits)?;
1449 if let Some(output) = requested_output {
1450 if output.size != result_size {
1451 return Err(PcodeLowerError::CopySizeMismatch {
1452 input: result_size,
1453 output: output.size,
1454 });
1455 }
1456 }
1457 let shifted = self.allocate_unique(input.size)?;
1458 self.emit(
1459 Opcode::IntRight,
1460 Some(shifted),
1461 &[input, Varnode::constant(start as u64, input.size)],
1462 );
1463 let masked = self.allocate_unique(input.size)?;
1464 self.emit(
1465 Opcode::IntAnd,
1466 Some(masked),
1467 &[shifted, Varnode::constant(Self::mask(bits)?, input.size)],
1468 );
1469 let output = self.output(requested_output, result_size)?;
1470 self.emit(
1471 Opcode::SubPiece,
1472 Some(output),
1473 &[masked, Varnode::constant(0, 8)],
1474 );
1475 Ok(output)
1476 }
1477
1478 fn lower_range_assignment<E: ExprNode>(
1479 &mut self,
1480 range: RangeNode<E>,
1481 rhs: E,
1482 ) -> Result<(), PcodeLowerError> {
1483 if let ExprKind::Load(load) = range.value.kind() {
1484 return self.lower_load_range_assignment(load, range, rhs);
1485 }
1486 let storage = self.storage_from_expr(range.value)?;
1487 let (start, bits) = range_params(&range)?;
1488 self.insert_range(storage, start, bits, rhs)
1489 }
1490
1491 fn lower_load_range_assignment<E: ExprNode>(
1495 &mut self,
1496 load: LoadNode<'_, E>,
1497 range: RangeNode<E>,
1498 rhs: E,
1499 ) -> Result<(), PcodeLowerError> {
1500 let storage = self.lower_load(range.value, load, None)?;
1501 let (start, bits) = range_params(&range)?;
1502 Self::validate_range(storage, start, bits)?;
1503 if storage.size > 8 {
1504 let Some(lane) = Self::aligned_lane(storage, start, bits) else {
1507 return Err(PcodeLowerError::InvalidRange {
1508 start,
1509 size: bits,
1510 storage_bits: storage.size.saturating_mul(8),
1511 });
1512 };
1513 let value = self.lower_expr_with_size(rhs, lane.size)?;
1514 if value.size != lane.size {
1515 return Err(PcodeLowerError::CopySizeMismatch {
1516 input: value.size,
1517 output: lane.size,
1518 });
1519 }
1520 let space = self.load_space(&load)?;
1521 if space == SPACE_CONST {
1522 return Err(PcodeLowerError::Unsupported("a store to constant space"));
1523 }
1524 let ptr = self.lower_expr(load.ptr, None)?;
1525 self.validate_pointer(space, ptr)?;
1526 let lane_ptr = if start == 0 {
1527 ptr
1528 } else {
1529 let output = self.allocate_unique(ptr.size)?;
1530 self.emit(
1531 Opcode::IntAdd,
1532 Some(output),
1533 &[ptr, Varnode::constant((start / 8) as u64, ptr.size)],
1534 );
1535 output
1536 };
1537 self.emit(
1538 Opcode::Store,
1539 None,
1540 &[Self::space_id(space), lane_ptr, value],
1541 );
1542 return Ok(());
1543 }
1544 let value = self.lower_expr_with_size(rhs, bits.div_ceil(8))?;
1545 if value.size > storage.size {
1546 return Err(PcodeLowerError::InputSizeMismatch {
1547 operation: "bit-range assignment",
1548 left: storage.size,
1549 right: value.size,
1550 });
1551 }
1552 let extended = if value.size == storage.size {
1553 value
1554 } else {
1555 let output = self.allocate_unique(storage.size)?;
1556 self.emit(Opcode::IntZext, Some(output), &[value]);
1557 output
1558 };
1559 let inserted = self.allocate_unique(storage.size)?;
1560 self.emit(
1561 Opcode::IntAnd,
1562 Some(inserted),
1563 &[extended, Varnode::constant(Self::mask(bits)?, storage.size)],
1564 );
1565 let shifted = self.allocate_unique(storage.size)?;
1566 self.emit(
1567 Opcode::IntLeft,
1568 Some(shifted),
1569 &[inserted, Varnode::constant(start as u64, storage.size)],
1570 );
1571 let clear_mask = !(Self::mask(bits)? << start);
1572 let kept = self.allocate_unique(storage.size)?;
1573 self.emit(
1574 Opcode::IntAnd,
1575 Some(kept),
1576 &[storage, Varnode::constant(clear_mask, storage.size)],
1577 );
1578 let result = self.allocate_unique(storage.size)?;
1579 self.emit(Opcode::IntOr, Some(result), &[kept, shifted]);
1580
1581 let space = self.load_space(&load)?;
1582 if space == SPACE_CONST {
1583 return Err(PcodeLowerError::Unsupported("a store to constant space"));
1584 }
1585 let ptr = self.lower_expr(load.ptr, None)?;
1586 self.validate_pointer(space, ptr)?;
1587 self.emit(Opcode::Store, None, &[Self::space_id(space), ptr, result]);
1588 Ok(())
1589 }
1590
1591 fn insert_range<E: ExprNode>(
1592 &mut self,
1593 storage: Varnode,
1594 start: usize,
1595 bits: usize,
1596 rhs: E,
1597 ) -> Result<(), PcodeLowerError> {
1598 Self::validate_range(storage, start, bits)?;
1599 if storage.size > 8 {
1605 let Some(lane) = Self::aligned_lane(storage, start, bits) else {
1606 return Err(PcodeLowerError::InvalidRange {
1607 start,
1608 size: bits,
1609 storage_bits: storage.size.saturating_mul(8),
1610 });
1611 };
1612 let value = self.lower_expr_with_size(rhs, lane.size)?;
1613 if value.size != lane.size {
1614 return Err(PcodeLowerError::CopySizeMismatch {
1615 input: value.size,
1616 output: lane.size,
1617 });
1618 }
1619 self.emit(Opcode::Copy, Some(lane), &[value]);
1620 return Ok(());
1621 }
1622 let value = self.lower_expr_with_size(rhs, bits.div_ceil(8))?;
1625 if value.size > storage.size {
1626 return Err(PcodeLowerError::InputSizeMismatch {
1627 operation: "bit-range assignment",
1628 left: storage.size,
1629 right: value.size,
1630 });
1631 }
1632 let extended = if value.size == storage.size {
1633 value
1634 } else {
1635 let output = self.allocate_unique(storage.size)?;
1636 self.emit(Opcode::IntZext, Some(output), &[value]);
1637 output
1638 };
1639 let inserted = self.allocate_unique(storage.size)?;
1640 self.emit(
1641 Opcode::IntAnd,
1642 Some(inserted),
1643 &[extended, Varnode::constant(Self::mask(bits)?, storage.size)],
1644 );
1645 let shifted = self.allocate_unique(storage.size)?;
1646 self.emit(
1647 Opcode::IntLeft,
1648 Some(shifted),
1649 &[inserted, Varnode::constant(start as u64, storage.size)],
1650 );
1651 let clear_mask = !(Self::mask(bits)? << start);
1652 let kept = self.allocate_unique(storage.size)?;
1653 self.emit(
1654 Opcode::IntAnd,
1655 Some(kept),
1656 &[storage, Varnode::constant(clear_mask, storage.size)],
1657 );
1658 self.emit(Opcode::IntOr, Some(storage), &[kept, shifted]);
1659 Ok(())
1660 }
1661
1662 fn aligned_lane(storage: Varnode, start: usize, bits: usize) -> Option<Varnode> {
1665 if start % 8 != 0 || bits % 8 != 0 {
1666 return None;
1667 }
1668 Some(Varnode::new(
1669 storage.space,
1670 storage.offset + (start / 8) as u64,
1671 bits / 8,
1672 ))
1673 }
1674
1675 fn validate_range(
1676 storage: Varnode,
1677 start: usize,
1678 size: usize,
1679 ) -> Result<usize, PcodeLowerError> {
1680 let storage_bits = storage
1681 .size
1682 .checked_mul(8)
1683 .ok_or(PcodeLowerError::InvalidRange {
1684 start,
1685 size,
1686 storage_bits: usize::MAX,
1687 })?;
1688 if size == 0 || size > 64 || start.checked_add(size).is_none_or(|end| end > storage_bits) {
1689 return Err(PcodeLowerError::InvalidRange {
1690 start,
1691 size,
1692 storage_bits,
1693 });
1694 }
1695 Ok(size.div_ceil(8))
1696 }
1697
1698 fn mask(bits: usize) -> Result<u64, PcodeLowerError> {
1699 match bits {
1700 1..=63 => Ok((1u64 << bits) - 1),
1701 64 => Ok(u64::MAX),
1702 _ => Err(PcodeLowerError::InvalidRange {
1703 start: 0,
1704 size: bits,
1705 storage_bits: 64,
1706 }),
1707 }
1708 }
1709
1710 fn validate_pointer(&self, space: SpaceId, ptr: Varnode) -> Result<(), PcodeLowerError> {
1711 let expected = self
1712 .context
1713 .address_size(space)
1714 .ok_or(PcodeLowerError::UnresolvedSpace)?;
1715 Self::checked_size(expected)?;
1716 if ptr.size != expected {
1717 return Err(PcodeLowerError::AddressSizeMismatch {
1718 expected,
1719 actual: ptr.size,
1720 });
1721 }
1722 Ok(())
1723 }
1724
1725 fn storage_from_expr<E: ExprNode>(&mut self, expr: E) -> Result<Varnode, PcodeLowerError> {
1726 match expr.kind() {
1727 ExprKind::Ident(ident) => self.storage_for_ident(ident, expr.size()),
1728 _ => Err(PcodeLowerError::Unsupported(
1729 "address-of a non-varnode expression",
1730 )),
1731 }
1732 }
1733
1734 fn storage_for_ident(
1735 &mut self,
1736 ident: Ident,
1737 size: Option<usize>,
1738 ) -> Result<Varnode, PcodeLowerError> {
1739 match ident {
1740 Ident::Register(id) => self
1741 .context
1742 .register_varnode(id)
1743 .ok_or(PcodeLowerError::UnknownRegister(id)),
1744 Ident::Named(id) => {
1745 let size = self.plan.local_sizes.get(&id).copied().or(size);
1746 if let Some(varnode) = self.locals.get(&id) {
1747 if let Some(size) = size
1748 && size != varnode.size
1749 {
1750 return Err(PcodeLowerError::CopySizeMismatch {
1751 input: varnode.size,
1752 output: size,
1753 });
1754 }
1755 return Ok(*varnode);
1756 }
1757 let varnode = self.allocate_unique(size.ok_or(PcodeLowerError::UnknownSize)?)?;
1758 self.locals.insert(id, varnode);
1759 Ok(varnode)
1760 }
1761 Ident::BitRange(_) => Err(PcodeLowerError::Unsupported("a named bit range")),
1762 Ident::Field(_) => Err(PcodeLowerError::UnresolvedIdentifier("field")),
1763 Ident::Table(_) => Err(PcodeLowerError::UnresolvedIdentifier("table")),
1764 Ident::Global(_) => Err(PcodeLowerError::UnresolvedIdentifier("global")),
1765 }
1766 }
1767
1768 fn lower_userop_inputs<E: ExprNode>(
1769 &mut self,
1770 id: PCodeOpId,
1771 args: E::Args,
1772 ) -> Result<Inputs, PcodeLowerError> {
1773 let mut inputs = Inputs::default();
1774 inputs.push(Varnode::constant(usize::from(id) as u64, 4));
1775 for arg in args {
1776 inputs.push(self.lower_expr(arg, None)?);
1777 }
1778 Ok(inputs)
1779 }
1780
1781 fn copy_if_requested(
1782 &mut self,
1783 input: Varnode,
1784 requested_output: Option<Varnode>,
1785 ) -> Result<Varnode, PcodeLowerError> {
1786 match requested_output {
1787 Some(output) if output != input && input.size == output.size => {
1788 self.emit(Opcode::Copy, Some(output), &[input]);
1789 Ok(output)
1790 }
1791 Some(output) if input.size < output.size => {
1792 self.emit(Opcode::IntZext, Some(output), &[input]);
1793 Ok(output)
1794 }
1795 Some(output) if input.size > output.size => {
1796 self.emit(
1797 Opcode::SubPiece,
1798 Some(output),
1799 &[input, Varnode::constant(0, 8)],
1800 );
1801 Ok(output)
1802 }
1803 Some(output) => Ok(output),
1804 None => Ok(input),
1805 }
1806 }
1807
1808 fn output(
1809 &mut self,
1810 requested_output: Option<Varnode>,
1811 size: usize,
1812 ) -> Result<Varnode, PcodeLowerError> {
1813 match requested_output {
1814 Some(output) => {
1815 Self::checked_size(output.size)?;
1816 Ok(output)
1817 }
1818 None => self.allocate_unique(size),
1819 }
1820 }
1821
1822 fn allocate_unique(&mut self, size: usize) -> Result<Varnode, PcodeLowerError> {
1823 Self::checked_size(size)?;
1824 let offset = self.next_unique;
1825 self.next_unique = self
1826 .next_unique
1827 .checked_add(size as u64)
1828 .ok_or(PcodeLowerError::UniqueSpaceOverflow)?;
1829 Ok(Varnode::new(self.context.unique_space(), offset, size))
1830 }
1831
1832 fn label_id(&self, label: &str) -> Result<LabelId, PcodeLowerError> {
1833 self.plan
1834 .label_id(label)
1835 .ok_or_else(|| PcodeLowerError::UnknownLabel(Box::from(label)))
1836 }
1837
1838 fn checked_size(size: usize) -> Result<(), PcodeLowerError> {
1839 if size == 0 {
1840 Err(PcodeLowerError::ZeroSize)
1841 } else {
1842 Ok(())
1843 }
1844 }
1845
1846 fn space_id(space: SpaceId) -> Varnode {
1847 Varnode::constant(usize::from(space) as u64, 4)
1848 }
1849}
1850
1851struct Inputs {
1857 inline: [Varnode; Inputs::INLINE],
1858 len: usize,
1859 spilled: Vec<Varnode>,
1860}
1861
1862impl Default for Inputs {
1863 fn default() -> Self {
1864 Self {
1865 inline: [Varnode::constant(0, 1); Self::INLINE],
1866 len: 0,
1867 spilled: Vec::new(),
1868 }
1869 }
1870}
1871
1872impl Inputs {
1873 const INLINE: usize = 6;
1874
1875 fn push(&mut self, input: Varnode) {
1876 if self.len < Self::INLINE {
1877 self.inline[self.len] = input;
1878 } else {
1879 if self.len == Self::INLINE {
1880 self.spilled.extend_from_slice(&self.inline);
1881 }
1882 self.spilled.push(input);
1883 }
1884 self.len += 1;
1885 }
1886}
1887
1888impl std::ops::Deref for Inputs {
1889 type Target = [Varnode];
1890
1891 fn deref(&self) -> &[Varnode] {
1892 if self.len <= Self::INLINE {
1893 &self.inline[..self.len]
1894 } else {
1895 &self.spilled
1896 }
1897 }
1898}
1899
1900#[derive(Default)]
1909pub struct Planner {
1910 plan: PcodePlan,
1911}
1912
1913impl Planner {
1914 pub fn new() -> Self {
1916 Self {
1917 plan: PcodePlan::default(),
1918 }
1919 }
1920
1921 pub fn statement<E: ExprNode>(&mut self, statement: StmtKind<'_, E>) {
1925 match statement {
1926 StmtKind::Label(label) => {
1927 let id = self.plan.declare_label(&label);
1931 self.plan.terminal[id.index()] = true;
1932 return;
1933 }
1934 StmtKind::Branch { target } | StmtKind::ConditionalBranch { target, .. } => {
1935 if let TargetNode::Expr(expr) = target
1938 && let Some(address) = Self::direct_address(expr)
1939 {
1940 self.plan.declare_direct_branch(address);
1941 }
1942 }
1943 StmtKind::Call { target } => {
1944 if let TargetNode::Expr(expr) = target
1945 && let Some(address) = Self::direct_address(expr)
1946 {
1947 self.plan.declare_direct_call(address);
1948 }
1949 }
1950 _ => {}
1951 }
1952 self.end_terminal_run();
1953 }
1954
1955 pub fn opaque_statement(&mut self) {
1960 self.end_terminal_run();
1961 }
1962
1963 fn end_terminal_run(&mut self) {
1965 self.plan.terminal.iter_mut().for_each(|flag| *flag = false);
1966 }
1967
1968 pub fn finish(mut self, local_sizes: LocalSizes) -> PcodePlan {
1976 self.plan.local_sizes = local_sizes;
1977 self.plan
1978 }
1979
1980 fn direct_address<E: ExprNode>(target: E) -> Option<u64> {
1981 match target.kind() {
1982 ExprKind::SizedInt { value, .. } => Some(value),
1983 _ => None,
1984 }
1985 }
1986}
1987
1988pub struct SizeInference<'a, C: PcodeLoweringContext + ?Sized, W: Width> {
2003 context: &'a C,
2004 sizes: HashMap<LocalVarId, W>,
2005 progressed: bool,
2007}
2008
2009impl<'a, C: PcodeLoweringContext + ?Sized, W: Width> SizeInference<'a, C, W> {
2010 pub fn new(context: &'a C) -> Self {
2012 Self {
2013 context,
2014 sizes: HashMap::new(),
2015 progressed: false,
2016 }
2017 }
2018
2019 pub fn run<S>(context: &'a C, statements: &[Ast<S>]) -> HashMap<LocalVarId, W> {
2021 let mut inference = Self::new(context);
2022 for _ in 0..=statements.len() {
2025 for statement in statements {
2026 inference.statement(StmtKind::from(&statement.ty));
2027 }
2028 if !inference.progressed() {
2029 break;
2030 }
2031 }
2032 inference.finish()
2033 }
2034
2035 pub fn statement<E: ExprNode>(&mut self, statement: StmtKind<'_, E>) {
2037 self.constrain_statement(statement);
2038 }
2039
2040 pub fn progressed(&mut self) -> bool {
2043 std::mem::take(&mut self.progressed)
2044 }
2045
2046 pub fn finish(self) -> HashMap<LocalVarId, W> {
2048 self.sizes
2049 }
2050
2051 fn found(&mut self, id: LocalVarId, size: W) {
2052 if let std::collections::hash_map::Entry::Vacant(entry) = self.sizes.entry(id) {
2053 entry.insert(size);
2054 self.progressed = true;
2055 }
2056 }
2057
2058 fn constrain_statement<E: ExprNode>(&mut self, statement: StmtKind<'_, E>) {
2059 match statement {
2060 StmtKind::Assignment { lhs, size, rhs } => {
2061 let comparison_size =
2065 matches!(rhs.kind(), ExprKind::Binop { op, .. } if op.is_comparison())
2066 .then(|| rhs.size())
2067 .flatten()
2068 .filter(|&size| size != 1)
2069 .map(W::fixed);
2070 let expected = size
2071 .map(W::fixed)
2072 .or_else(|| self.storage_size(&lhs))
2073 .or(comparison_size);
2074 let inferred = self.constrain_expr(rhs, expected);
2075 if let Ident::Named(id) = lhs
2076 && let Some(size) = expected.or(inferred)
2077 {
2078 self.found(id, size);
2079 }
2080 }
2081 StmtKind::LoadAssignment { load, rhs, .. } => {
2082 let space = self.load_space(&load).ok();
2083 if let Some(space) = space {
2084 self.constrain_expr(load.ptr, self.context.address_size(space).map(W::fixed));
2085 }
2086 self.constrain_expr(rhs, load.size.map(W::fixed));
2087 }
2088 StmtKind::RangeAssignment { range, rhs, .. } => {
2089 if let Ok((_, bits)) = range_params(&range) {
2090 self.constrain_expr(rhs, Some(W::fixed(bits.div_ceil(8))));
2091 }
2092 }
2093 StmtKind::ConditionalBranch { condition, .. } => {
2094 self.constrain_expr(condition, Some(W::fixed(1)));
2095 }
2096 StmtKind::BranchIndirect { target }
2097 | StmtKind::CallIndirect { target }
2098 | StmtKind::Return { target } => {
2099 self.constrain_expr(
2100 target,
2101 self.context
2102 .address_size(self.context.default_space())
2103 .map(W::fixed),
2104 );
2105 }
2106 StmtKind::Expression(expr) => {
2107 self.constrain_expr(expr, None);
2108 }
2109 StmtKind::Internal(_)
2110 | StmtKind::Label(_)
2111 | StmtKind::Branch { .. }
2112 | StmtKind::Call { .. } => {}
2113 }
2114 }
2115
2116 fn constrain_expr<E: ExprNode>(&mut self, expr: E, expected: Option<W>) -> Option<W> {
2120 match expr.kind() {
2121 ExprKind::SizedInt { .. } => expected,
2122 ExprKind::Ident(Ident::Named(id)) => {
2123 if let Some(&size) = self.sizes.get(&id) {
2124 Some(size)
2125 } else if let Some(size) = expected {
2126 self.found(id, size);
2127 Some(size)
2128 } else {
2129 None
2130 }
2131 }
2132 ExprKind::Ident(ident) => self.storage_size(&ident),
2133 ExprKind::Load(load) => {
2134 if let Ok(space) = self.load_space(&load) {
2135 self.constrain_expr(load.ptr, self.context.address_size(space).map(W::fixed));
2136 }
2137 load.size.map(W::fixed).or(expected)
2138 }
2139 ExprKind::SubPieceLsb { src, count } => {
2140 self.constrain_expr(src, None);
2141 Some(W::fixed(count))
2142 }
2143 ExprKind::SubPieceMsb { src, count } => {
2144 let size = expected
2147 .or_else(|| Some(W::fixed(self.expr_size(src)?.size()?.checked_sub(count)?)));
2148 let source = size
2149 .and_then(|size| size.size())
2150 .map(|size| W::fixed(size + count));
2151 self.constrain_expr(src, source);
2152 size
2153 }
2154 ExprKind::Range(range) => {
2155 let size = match range.size {
2156 RangeParam::Literal(bits) => Some(W::fixed(bits.div_ceil(8))),
2157 RangeParam::MacroArg(_) => expected,
2158 };
2159 self.constrain_expr(range.value, None);
2160 size
2161 }
2162 ExprKind::FunctionCall { builtin, args } => {
2163 let boolean = matches!(
2164 builtin,
2165 Builtin::Carry | Builtin::Scarry | Builtin::Sborrow | Builtin::Nan
2166 );
2167 let size = boolean.then(|| W::fixed(1)).or(expected);
2168 let input_size = args.clone().find_map(|arg| self.constrain_expr(arg, None));
2169 if let Some(input_size) = input_size {
2170 for arg in args {
2171 self.constrain_expr(arg, Some(input_size));
2172 }
2173 }
2174 size
2175 }
2176 ExprKind::PcodeOp { args, .. } => {
2177 for arg in args {
2178 self.constrain_expr(arg, None);
2179 }
2180 expected
2181 }
2182 ExprKind::Unop { op, e } => match op {
2183 UnaryOperator::LogicalNot => {
2184 let size = self.constrain_expr(e, None);
2185 self.constrain_expr(e, size);
2186 Some(W::fixed(1))
2187 }
2188 UnaryOperator::AddressOf(size) => size.map(W::fixed).or_else(|| {
2189 self.storage_from_expr_size(e)
2190 .and_then(|storage| self.context.address_size(storage.space))
2191 .map(W::fixed)
2192 }),
2193 _ => {
2194 let size = expected.or_else(|| self.constrain_expr(e, None));
2195 self.constrain_expr(e, size);
2196 size
2197 }
2198 },
2199 ExprKind::Binop { op, lhs, rhs } => {
2200 let boolean = op.is_comparison()
2201 || matches!(
2202 op,
2203 BinaryOperator::LogicalXor
2204 | BinaryOperator::LogicalAnd
2205 | BinaryOperator::LogicalOr
2206 );
2207 let input_size = self
2208 .constrain_expr(lhs, None)
2209 .or_else(|| self.constrain_expr(rhs, None));
2210 let input_size = if boolean {
2211 input_size
2212 } else {
2213 expected.or(input_size)
2214 };
2215 self.constrain_expr(lhs, input_size);
2216 self.constrain_expr(rhs, input_size);
2217 if boolean {
2218 Some(W::fixed(1))
2219 } else {
2220 input_size
2221 }
2222 }
2223 ExprKind::Internal(_) => expected,
2224 }
2225 }
2226}
2227
2228impl<'a, C: PcodeLoweringContext + ?Sized, W: Width> Sizing<W> for SizeInference<'a, C, W> {
2229 type Ctx = C;
2230
2231 fn context(&self) -> &C {
2232 self.context
2233 }
2234
2235 fn local_size(&self, id: &LocalVarId) -> Option<W> {
2236 self.sizes.get(id).copied()
2237 }
2238}
2239
2240impl<C: PcodeLoweringContext + ?Sized, S: PcodeSink + ?Sized> Sizing<usize>
2241 for Emitter<'_, '_, '_, C, S>
2242{
2243 type Ctx = C;
2244
2245 fn context(&self) -> &C {
2246 self.context
2247 }
2248
2249 fn local_size(&self, id: &LocalVarId) -> Option<usize> {
2250 self.plan
2251 .local_sizes
2252 .get(id)
2253 .copied()
2254 .or_else(|| self.locals.get(id).map(|varnode| varnode.size))
2255 }
2256}
2257
2258trait Sizing<W: Width> {
2262 type Ctx: PcodeLoweringContext + ?Sized;
2263
2264 fn context(&self) -> &Self::Ctx;
2265
2266 fn local_size(&self, id: &LocalVarId) -> Option<W>;
2268
2269 fn expr_size<E: ExprNode>(&self, expr: E) -> Option<W> {
2270 expr.size().map(W::fixed).or(match expr.kind() {
2271 ExprKind::SizedInt { size, .. } => size.map(W::fixed),
2272 ExprKind::Ident(ident) => self.storage_size(&ident),
2273 ExprKind::Load(load) => load.size.map(W::fixed),
2274 ExprKind::SubPieceLsb { count, .. } => Some(W::fixed(count)),
2275 ExprKind::SubPieceMsb { src, count } => {
2276 Some(W::fixed(self.expr_size(src)?.size()?.checked_sub(count)?))
2277 }
2278 ExprKind::Range(RangeNode {
2279 size: RangeParam::Literal(bits),
2280 ..
2281 }) => Some(W::fixed(bits.div_ceil(8))),
2282 ExprKind::Range(RangeNode {
2283 size: RangeParam::MacroArg(_),
2284 ..
2285 }) => None,
2286 ExprKind::FunctionCall {
2287 builtin: Builtin::Carry | Builtin::Scarry | Builtin::Sborrow | Builtin::Nan,
2288 ..
2289 } => Some(W::fixed(1)),
2290 ExprKind::FunctionCall { .. } => None,
2291 ExprKind::Unop {
2292 op: UnaryOperator::LogicalNot,
2293 ..
2294 } => Some(W::fixed(1)),
2295 ExprKind::Unop { e, .. } => self.expr_size(e),
2296 ExprKind::Binop { op, .. } if op.is_comparison() => Some(W::fixed(1)),
2297 ExprKind::Binop { lhs, rhs, .. } => self.expr_size(lhs).or_else(|| self.expr_size(rhs)),
2298 ExprKind::PcodeOp { .. } | ExprKind::Internal(_) => None,
2299 })
2300 }
2301
2302 fn storage_size(&self, ident: &Ident) -> Option<W> {
2303 match ident {
2304 Ident::Register(id) => self
2305 .context()
2306 .register_varnode(*id)
2307 .map(|varnode| W::fixed(varnode.size)),
2308 Ident::BitRange(id) => self
2309 .context()
2310 .bitrange_info(*id)
2311 .map(|info| W::fixed(info.size.div_ceil(8))),
2312 Ident::Named(id) => self.local_size(id),
2313 Ident::Table(id) => W::operand(*id),
2316 Ident::Field(_) | Ident::Global(_) => None,
2317 }
2318 }
2319
2320 fn storage_from_expr_size<E: ExprNode>(&self, expr: E) -> Option<Varnode> {
2321 match expr.kind() {
2322 ExprKind::Ident(Ident::Register(id)) => self.context().register_varnode(id),
2323 ExprKind::Ident(Ident::BitRange(id)) => {
2324 self.context().bitrange_info(id).map(|info| info.storage)
2325 }
2326 _ => None,
2327 }
2328 }
2329
2330 fn load_space<E>(&self, load: &LoadNode<'_, E>) -> Result<SpaceId, PcodeLowerError> {
2331 match load.space {
2332 LoadSpace::Default => Ok(self.context().default_space()),
2333 LoadSpace::Resolved(space) => Ok(space),
2334 LoadSpace::Deferred(_) => Err(PcodeLowerError::UnresolvedSpace),
2335 }
2336 }
2337}
2338
2339fn range_params<E>(range: &RangeNode<E>) -> Result<(usize, usize), PcodeLowerError> {
2343 let RangeParam::Literal(start) = range.start else {
2344 return Err(PcodeLowerError::UnresolvedRangeParameter);
2345 };
2346 let RangeParam::Literal(size) = range.size else {
2347 return Err(PcodeLowerError::UnresolvedRangeParameter);
2348 };
2349 Ok((start, size))
2350}
2351
2352fn binary_opcode(op: BinaryOperator) -> (Opcode, bool) {
2353 use BinaryOperator::*;
2354 match op {
2355 Mul => (Opcode::IntMult, false),
2356 Div => (Opcode::IntDiv, false),
2357 SignedDiv => (Opcode::IntSDiv, false),
2358 Mod => (Opcode::IntRem, false),
2359 SignedMod => (Opcode::IntSRem, false),
2360 FloatDiv => (Opcode::FloatDiv, false),
2361 FloatMul => (Opcode::FloatMult, false),
2362 Add => (Opcode::IntAdd, false),
2363 Sub => (Opcode::IntSub, false),
2364 FloatAdd => (Opcode::FloatAdd, false),
2365 FloatSub => (Opcode::FloatSub, false),
2366 LeftShift => (Opcode::IntLeft, false),
2367 RightShift => (Opcode::IntRight, false),
2368 SignedRightShift => (Opcode::IntSRight, false),
2369 SignedLessThan => (Opcode::IntSLess, false),
2370 SignedGreaterThan => (Opcode::IntSLess, true),
2371 SignedLessEqual => (Opcode::IntSLessEqual, false),
2372 SignedGreaterEqual => (Opcode::IntSLessEqual, true),
2373 LessEqual => (Opcode::IntLessEqual, false),
2374 GreaterEqual => (Opcode::IntLessEqual, true),
2375 LessThan => (Opcode::IntLess, false),
2376 GreaterThan => (Opcode::IntLess, true),
2377 FloatLessEqual => (Opcode::FloatLessEqual, false),
2378 FloatGreaterEqual => (Opcode::FloatLessEqual, true),
2379 FloatLessThan => (Opcode::FloatLess, false),
2380 FloatGreaterThan => (Opcode::FloatLess, true),
2381 Equal => (Opcode::IntEqual, false),
2382 NotEqual => (Opcode::IntNotEqual, false),
2383 FloatEqual => (Opcode::FloatEqual, false),
2384 FloatNotEqual => (Opcode::FloatNotEqual, false),
2385 LogicalXor => (Opcode::BoolXor, false),
2386 LogicalAnd => (Opcode::BoolAnd, false),
2387 LogicalOr => (Opcode::BoolOr, false),
2388 BitwiseXor => (Opcode::IntXor, false),
2389 BitwiseOr => (Opcode::IntOr, false),
2390 BitwiseAnd => (Opcode::IntAnd, false),
2391 }
2392}
2393
2394#[cfg(test)]
2395mod tests {
2396 use super::{
2397 BitRangeInfo, InstructionPcode, LabelId, LocalSizes, Opcode, PcodeLowerError,
2398 PcodeLoweringContext, PcodeOp, PcodeSink, Varnode, emit_instruction, lower_instruction,
2399 plan_instruction,
2400 };
2401 use crate::{
2402 Ast, AstNode, BinaryOperator, Binop, Expression, ExpressionTy, Ident, LabelOrNode, Load,
2403 LocalVarId, PCodeOpId, PcodeAst, PcodeSpaceRef, Range, RangeParam, RegisterId, SPACE_CONST,
2404 SpaceId,
2405 };
2406 use std::collections::HashMap;
2407
2408 struct Context;
2409
2410 impl PcodeLoweringContext for Context {
2411 fn default_space(&self) -> SpaceId {
2412 SpaceId::new(1)
2413 }
2414
2415 fn unique_space(&self) -> SpaceId {
2416 SpaceId::new(2)
2417 }
2418
2419 fn register_varnode(&self, id: RegisterId) -> Option<Varnode> {
2420 Some(Varnode::new(SpaceId::new(3), usize::from(id) as u64 * 4, 4))
2421 }
2422
2423 fn bitrange_info(&self, _id: crate::BitRangeFieldId) -> Option<BitRangeInfo> {
2424 None
2425 }
2426
2427 fn address_size(&self, space: SpaceId) -> Option<usize> {
2428 Some(if space == SpaceId::new(4) { 4 } else { 8 })
2429 }
2430 }
2431
2432 fn int(value: u64, size: usize) -> Expression {
2433 Expression {
2434 ty: ExpressionTy::SizedInt {
2435 value,
2436 size: Some(size),
2437 },
2438 size: Some(size),
2439 span: (),
2440 }
2441 }
2442
2443 fn ident(id: RegisterId) -> Expression {
2444 Expression {
2445 ty: ExpressionTy::Ident(Ident::Register(id)),
2446 size: Some(4),
2447 span: (),
2448 }
2449 }
2450
2451 fn ast(nodes: Vec<AstNode>) -> PcodeAst {
2452 PcodeAst {
2453 statements: nodes.into_iter().map(Ast::from).collect(),
2454 }
2455 }
2456
2457 #[test]
2458 fn varnodes_distinguish_constants_from_storage() {
2459 let constant = Varnode::constant(0x1234, 4);
2460 let storage = Varnode::new(SpaceId::new(2), 0x1234, 4);
2461 assert_eq!(constant.space, SPACE_CONST);
2462 assert!(constant.is_constant());
2463 assert!(!storage.is_constant());
2464 }
2465
2466 #[test]
2467 fn opcode_inventory_identifies_analysis_only_operations() {
2468 assert_eq!(Opcode::ALL.len(), 72);
2469 assert_eq!(Opcode::Copy.ghidra_id(), 1);
2470 assert_eq!(Opcode::FloatLessEqual.ghidra_id(), 44);
2471 assert_eq!(Opcode::FloatNan.ghidra_id(), 46);
2472 assert_eq!(Opcode::LzCount.ghidra_id(), 73);
2473 assert!(Opcode::ALL.contains(&Opcode::Load));
2474 assert!(Opcode::ALL.contains(&Opcode::LzCount));
2475 assert!(Opcode::Load.is_raw_instruction_op());
2476 for opcode in [
2477 Opcode::MultiEqual,
2478 Opcode::Indirect,
2479 Opcode::Cast,
2480 Opcode::PtrAdd,
2481 Opcode::PtrSub,
2482 Opcode::SegmentOp,
2483 Opcode::Insert,
2484 Opcode::Extract,
2485 ] {
2486 assert!(!opcode.is_raw_instruction_op());
2487 }
2488 }
2489
2490 #[test]
2491 fn flat_operations_preserve_input_order_and_round_trip() {
2492 let output = Varnode::new(SpaceId::new(1), 0, 4);
2493 let instruction = InstructionPcode {
2494 ops: vec![PcodeOp::new(
2495 Opcode::IntAdd,
2496 Some(output),
2497 vec![output, Varnode::constant(1, 4)],
2498 )],
2499 };
2500 assert!(!instruction.is_empty());
2501 let bytes =
2502 bincode::serde::encode_to_vec(&instruction, bincode::config::standard()).unwrap();
2503 let (decoded, _): (InstructionPcode, _) =
2504 bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).unwrap();
2505 assert_eq!(decoded, instruction);
2506 assert!(InstructionPcode::new().is_empty());
2507 }
2508
2509 #[test]
2510 fn lower_assignment_emits_direct_arithmetic_output() {
2511 let rhs = Expression {
2512 ty: ExpressionTy::Binop(Binop {
2513 op: BinaryOperator::Add,
2514 lhs: Box::new(ident(RegisterId::new(1))),
2515 rhs: Box::new(int(1, 4)),
2516 }),
2517 size: Some(4),
2518 span: (),
2519 };
2520 let output = Varnode::new(SpaceId::new(3), 0, 4);
2521 let input = Varnode::new(SpaceId::new(3), 4, 4);
2522 let pcode = lower_instruction(
2523 &ast(vec![AstNode::Assignment {
2524 lhs: Ident::Register(RegisterId::new(0)),
2525 size: None,
2526 rhs,
2527 }]),
2528 &Context,
2529 )
2530 .unwrap();
2531 assert_eq!(
2532 pcode.ops,
2533 vec![PcodeOp::new(
2534 Opcode::IntAdd,
2535 Some(output),
2536 vec![input, Varnode::constant(1, 4)],
2537 )]
2538 );
2539 }
2540
2541 #[test]
2542 fn lower_load_store_and_userop_use_ghidra_operand_order() {
2543 let ram = SpaceId::new(4);
2544 let pointer = ident(RegisterId::new(1));
2545 let load = Load {
2546 space: Some(PcodeSpaceRef::Resolved(ram)),
2547 size: Some(4),
2548 ptr: Box::new(pointer.clone()),
2549 };
2550 let pcode = lower_instruction(
2551 &ast(vec![
2552 AstNode::Assignment {
2553 lhs: Ident::Register(RegisterId::new(0)),
2554 size: None,
2555 rhs: Expression {
2556 ty: ExpressionTy::Load(load.clone()),
2557 size: Some(4),
2558 span: (),
2559 },
2560 },
2561 AstNode::LoadAssignment {
2562 lhs: load,
2563 size: None,
2564 rhs: int(9, 4),
2565 },
2566 AstNode::Expression(Expression {
2567 ty: ExpressionTy::PcodeOp {
2568 id: PCodeOpId::new(7),
2569 args: vec![int(2, 4)],
2570 },
2571 size: None,
2572 span: (),
2573 }),
2574 ]),
2575 &Context,
2576 )
2577 .unwrap();
2578 let r0 = Varnode::new(SpaceId::new(3), 0, 4);
2579 let r1 = Varnode::new(SpaceId::new(3), 4, 4);
2580 assert_eq!(
2581 pcode.ops,
2582 vec![
2583 PcodeOp::new(Opcode::Load, Some(r0), vec![Varnode::constant(4, 4), r1],),
2584 PcodeOp::new(
2585 Opcode::Store,
2586 None,
2587 vec![Varnode::constant(4, 4), r1, Varnode::constant(9, 4)],
2588 ),
2589 PcodeOp::new(
2590 Opcode::CallOther,
2591 None,
2592 vec![Varnode::constant(7, 4), Varnode::constant(2, 4)],
2593 ),
2594 ]
2595 );
2596 }
2597
2598 #[derive(Default)]
2600 struct Trace {
2601 events: Vec<String>,
2602 }
2603
2604 impl PcodeSink for Trace {
2605 fn op(&mut self, opcode: Opcode, output: Option<Varnode>, inputs: &[Varnode]) {
2606 self.events
2607 .push(format!("{opcode:?} {output:?} {inputs:?}"));
2608 }
2609
2610 fn label(&mut self, label: LabelId) {
2611 self.events.push(format!("label {}", label.index()));
2612 }
2613
2614 fn branch_label(&mut self, opcode: Opcode, label: LabelId, condition: Option<Varnode>) {
2615 self.events.push(format!(
2616 "{opcode:?} -> label {} {condition:?}",
2617 label.index()
2618 ));
2619 }
2620 }
2621
2622 fn branch_statements() -> Vec<AstNode> {
2623 vec![
2624 AstNode::ConditionalBranch {
2625 condition: ident(RegisterId::new(1)),
2626 target: LabelOrNode::Label("skip".into()),
2627 },
2628 AstNode::Call {
2629 target: LabelOrNode::Expr(int(0x2000, 8)),
2630 },
2631 AstNode::Branch {
2632 target: LabelOrNode::Expr(int(0x1000, 8)),
2633 },
2634 AstNode::Label("skip".into()),
2635 AstNode::Assignment {
2636 lhs: Ident::Register(RegisterId::new(1)),
2637 size: None,
2638 rhs: ident(RegisterId::new(2)),
2639 },
2640 ]
2641 }
2642
2643 #[test]
2644 fn local_widths_can_be_inferred_from_a_body_before_planning() {
2645 let statements = vec![
2646 AstNode::Assignment {
2647 lhs: Ident::Named(LocalVarId(0)),
2648 size: None,
2649 rhs: ident(RegisterId::new(1)),
2650 },
2651 AstNode::Assignment {
2652 lhs: Ident::Register(RegisterId::new(2)),
2653 size: None,
2654 rhs: ExpressionTy::Ident(Ident::Named(LocalVarId(0))).with_size(4),
2655 },
2656 ];
2657 let ast = ast(statements.clone());
2658
2659 let sizes = super::infer_local_sizes(&ast.statements, &Context);
2662 assert_eq!(sizes.get(&LocalVarId(0)), Some(&4));
2663
2664 let planned = super::plan_instruction_with(&ast, &Context, sizes).unwrap();
2665 let inferred = plan_instruction(&ast, &Context).unwrap();
2666 assert_eq!(planned.labels(), inferred.labels());
2667
2668 let pcode = lower_instruction(&ast, &Context).unwrap();
2671 assert_eq!(
2672 pcode.ops[0].output,
2673 Some(Varnode::new(SpaceId::new(2), 0, 4))
2674 );
2675 }
2676
2677 #[test]
2682 fn symbolic_inference_names_an_operand_width_instead_of_losing_it() {
2683 let table = crate::TableId::new(7);
2684 let statements = ast(vec![
2685 AstNode::Assignment {
2686 lhs: Ident::Named(LocalVarId(0)),
2687 size: None,
2688 rhs: Expression {
2689 ty: ExpressionTy::Ident(Ident::Table(table)),
2690 size: None,
2691 span: (),
2692 },
2693 },
2694 AstNode::Assignment {
2695 lhs: Ident::Register(RegisterId::new(1)),
2696 size: None,
2697 rhs: Expression {
2698 ty: ExpressionTy::Binop(Binop {
2699 op: BinaryOperator::Add,
2700 lhs: Box::new(
2701 ExpressionTy::Ident(Ident::Named(LocalVarId(0))).with_size(4),
2702 ),
2703 rhs: Box::new(ident(RegisterId::new(2))),
2704 }),
2705 size: None,
2706 span: (),
2707 },
2708 },
2709 ])
2710 .statements;
2711
2712 let symbolic: HashMap<LocalVarId, super::SymbolicWidth> =
2713 super::infer_local_sizes(&statements, &Context);
2714 assert_eq!(
2715 symbolic.get(&LocalVarId(0)),
2716 Some(&super::SymbolicWidth::SameAs(table))
2717 );
2718
2719 let concrete: LocalSizes = super::infer_local_sizes(&statements, &Context);
2723 assert_eq!(concrete.get(&LocalVarId(0)), Some(&4));
2724 }
2725
2726 #[test]
2727 fn plan_reports_labels_and_out_of_instruction_targets() {
2728 let plan = plan_instruction(&ast(branch_statements()), &Context).unwrap();
2729 assert_eq!(plan.labels(), &[Box::<str>::from("skip")]);
2730 assert_eq!(plan.direct_branches(), &[0x1000]);
2731 assert_eq!(plan.direct_calls(), &[0x2000]);
2732 }
2733
2734 #[test]
2735 fn streaming_emission_keeps_local_branch_targets_symbolic() {
2736 let ast = ast(branch_statements());
2737 let plan = plan_instruction(&ast, &Context).unwrap();
2738 let mut trace = Trace::default();
2739 emit_instruction(&ast, &Context, &plan, &mut trace).unwrap();
2740
2741 assert_eq!(
2742 trace.events[0],
2743 "CBranch -> label 0 Some(Varnode { space: SpaceId(3), offset: 4, size: 4 })"
2744 );
2745 assert_eq!(trace.events[3], "label 0");
2746 assert_eq!(trace.events.len(), 5);
2747
2748 let pcode = lower_instruction(&ast, &Context).unwrap();
2750 assert_eq!(pcode.ops[0].opcode, Opcode::CBranch);
2751 assert_eq!(pcode.ops[0].inputs[0], Varnode::constant(3, 8));
2752 }
2753
2754 #[test]
2755 fn plan_omits_unresolvable_direct_targets() {
2756 let plan = plan_instruction(
2757 &ast(vec![AstNode::Branch {
2758 target: LabelOrNode::Expr(ident(RegisterId::new(1))),
2759 }]),
2760 &Context,
2761 )
2762 .unwrap();
2763 assert!(plan.direct_branches().is_empty());
2764 assert_eq!(
2765 lower_instruction(
2766 &ast(vec![AstNode::Branch {
2767 target: LabelOrNode::Expr(ident(RegisterId::new(1))),
2768 }]),
2769 &Context,
2770 )
2771 .unwrap_err(),
2772 PcodeLowerError::InvalidDirectTarget
2773 );
2774 }
2775
2776 #[test]
2777 fn branching_to_an_undefined_label_is_rejected() {
2778 let error = lower_instruction(
2779 &ast(vec![AstNode::Branch {
2780 target: LabelOrNode::Label("missing".into()),
2781 }]),
2782 &Context,
2783 )
2784 .unwrap_err();
2785 assert_eq!(error, PcodeLowerError::UnknownLabel("missing".into()));
2786 }
2787
2788 #[test]
2789 fn duplicate_labels_are_rejected() {
2790 let error = lower_instruction(
2791 &ast(vec![
2792 AstNode::Label("here".into()),
2793 AstNode::Label("here".into()),
2794 ]),
2795 &Context,
2796 )
2797 .unwrap_err();
2798 assert_eq!(error, PcodeLowerError::DuplicateLabel("here".into()));
2799 }
2800
2801 #[test]
2802 fn lower_labels_are_pcode_relative_and_comparisons_reverse_greater_than() {
2803 let comparison = Expression {
2804 ty: ExpressionTy::Binop(Binop {
2805 op: BinaryOperator::GreaterThan,
2806 lhs: Box::new(ident(RegisterId::new(1))),
2807 rhs: Box::new(ident(RegisterId::new(2))),
2808 }),
2809 size: Some(1),
2810 span: (),
2811 };
2812 let pcode = lower_instruction(
2813 &ast(vec![
2814 AstNode::Label("loop".into()),
2815 AstNode::Assignment {
2816 lhs: Ident::Named(LocalVarId(0)),
2817 size: None,
2818 rhs: comparison,
2819 },
2820 AstNode::Branch {
2821 target: LabelOrNode::Label("loop".into()),
2822 },
2823 ]),
2824 &Context,
2825 )
2826 .unwrap();
2827 assert_eq!(pcode.ops.len(), 2);
2828 assert_eq!(pcode.ops[0].opcode, Opcode::IntLess);
2829 assert_eq!(
2830 pcode.ops[0].inputs,
2831 vec![
2832 Varnode::new(SpaceId::new(3), 8, 4),
2833 Varnode::new(SpaceId::new(3), 4, 4),
2834 ]
2835 );
2836 assert_eq!(
2837 pcode.ops[0].output,
2838 Some(Varnode::new(SpaceId::new(2), 0, 1))
2839 );
2840 assert_eq!(pcode.ops[1].opcode, Opcode::Branch);
2841 assert_eq!(pcode.ops[1].inputs, vec![Varnode::constant(u64::MAX, 8)]);
2842 }
2843
2844 #[test]
2845 fn lower_named_bitranges_as_raw_read_modify_write() {
2846 struct BitRangeContext;
2847 impl PcodeLoweringContext for BitRangeContext {
2848 fn default_space(&self) -> SpaceId {
2849 SpaceId::new(1)
2850 }
2851 fn unique_space(&self) -> SpaceId {
2852 SpaceId::new(2)
2853 }
2854 fn register_varnode(&self, id: RegisterId) -> Option<Varnode> {
2855 Some(Varnode::new(SpaceId::new(3), usize::from(id) as u64 * 4, 4))
2856 }
2857 fn bitrange_info(&self, id: crate::BitRangeFieldId) -> Option<BitRangeInfo> {
2858 (id == crate::BitRangeFieldId::new(0)).then_some(BitRangeInfo {
2859 storage: Varnode::new(SpaceId::new(3), 0, 4),
2860 start: 3,
2861 size: 5,
2862 })
2863 }
2864 fn address_size(&self, _space: SpaceId) -> Option<usize> {
2865 Some(4)
2866 }
2867 }
2868
2869 let read = lower_instruction(
2870 &ast(vec![AstNode::Assignment {
2871 lhs: Ident::Named(LocalVarId(0)),
2872 size: None,
2873 rhs: ExpressionTy::Ident(Ident::BitRange(crate::BitRangeFieldId::new(0)))
2874 .with_size(1),
2875 }]),
2876 &BitRangeContext,
2877 )
2878 .unwrap();
2879 assert_eq!(
2880 read.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
2881 vec![Opcode::IntRight, Opcode::IntAnd, Opcode::SubPiece]
2882 );
2883 assert_eq!(read.ops[0].inputs[1], Varnode::constant(3, 4));
2884 assert_eq!(read.ops[1].inputs[1], Varnode::constant(0x1f, 4));
2885
2886 let write = lower_instruction(
2887 &ast(vec![AstNode::Assignment {
2888 lhs: Ident::BitRange(crate::BitRangeFieldId::new(0)),
2889 size: None,
2890 rhs: int(0xff, 1),
2891 }]),
2892 &BitRangeContext,
2893 )
2894 .unwrap();
2895 assert_eq!(
2896 write.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
2897 vec![
2898 Opcode::IntZext,
2899 Opcode::IntAnd,
2900 Opcode::IntLeft,
2901 Opcode::IntAnd,
2902 Opcode::IntOr,
2903 ]
2904 );
2905 assert_eq!(
2906 write.ops.last().unwrap().output.unwrap().space,
2907 SpaceId::new(3)
2908 );
2909 assert_eq!(write.ops.last().unwrap().output.unwrap().offset, 0);
2910 }
2911
2912 #[test]
2913 fn lower_binary_operations_coerce_narrow_operands() {
2914 struct MixedWidthContext;
2915 impl PcodeLoweringContext for MixedWidthContext {
2916 fn default_space(&self) -> SpaceId {
2917 SpaceId::new(1)
2918 }
2919 fn unique_space(&self) -> SpaceId {
2920 SpaceId::new(2)
2921 }
2922 fn register_varnode(&self, id: RegisterId) -> Option<Varnode> {
2923 Some(Varnode::new(
2924 SpaceId::new(3),
2925 usize::from(id) as u64 * 2,
2926 if id == RegisterId::new(0) { 2 } else { 1 },
2927 ))
2928 }
2929 fn bitrange_info(&self, _id: crate::BitRangeFieldId) -> Option<BitRangeInfo> {
2930 None
2931 }
2932 fn address_size(&self, _space: SpaceId) -> Option<usize> {
2933 Some(8)
2934 }
2935 }
2936
2937 let pcode = lower_instruction(
2938 &ast(vec![AstNode::Assignment {
2939 lhs: Ident::Register(RegisterId::new(0)),
2940 size: None,
2941 rhs: ExpressionTy::Binop(Binop {
2942 op: BinaryOperator::LeftShift,
2943 lhs: Box::new(
2944 ExpressionTy::Ident(Ident::Register(RegisterId::new(0))).with_size(2),
2945 ),
2946 rhs: Box::new(
2947 ExpressionTy::Ident(Ident::Register(RegisterId::new(1))).with_size(1),
2948 ),
2949 })
2950 .with_size(2),
2951 }]),
2952 &MixedWidthContext,
2953 )
2954 .unwrap();
2955 assert_eq!(pcode.ops[0].opcode, Opcode::IntZext);
2956 assert_eq!(pcode.ops[0].output.unwrap().size, 2);
2957 assert_eq!(pcode.ops[1].opcode, Opcode::IntLeft);
2958 assert_eq!(pcode.ops[1].inputs[1].size, 2);
2959 }
2960
2961 #[test]
2962 fn lower_store_coerces_its_value_to_the_declared_width() {
2963 let pcode = lower_instruction(
2964 &ast(vec![AstNode::LoadAssignment {
2965 lhs: Load {
2966 space: None,
2967 size: Some(1),
2968 ptr: Box::new(int(0, 8)),
2969 },
2970 size: None,
2971 rhs: ident(RegisterId::new(0)),
2972 }]),
2973 &Context,
2974 )
2975 .unwrap();
2976 assert_eq!(pcode.ops[0].opcode, Opcode::SubPiece);
2977 assert_eq!(pcode.ops[0].output.unwrap().size, 1);
2978 assert_eq!(pcode.ops[1].opcode, Opcode::Store);
2979 assert_eq!(pcode.ops[1].inputs[2].size, 1);
2980 }
2981
2982 #[test]
2983 fn lower_range_assignment_supplies_its_rhs_width_to_a_userop() {
2984 let pcode = lower_instruction(
2985 &ast(vec![AstNode::RangeAssignment {
2986 lhs: Range {
2987 value: Box::new(ident(RegisterId::new(0))),
2988 start: RangeParam::Literal(0),
2989 size: RangeParam::Literal(8),
2990 },
2991 size: None,
2992 rhs: Expression {
2993 ty: ExpressionTy::PcodeOp {
2994 id: PCodeOpId::new(7),
2995 args: vec![],
2996 },
2997 size: None,
2998 span: (),
2999 },
3000 }]),
3001 &Context,
3002 )
3003 .unwrap();
3004 let userop = pcode
3005 .ops
3006 .iter()
3007 .find(|op| op.opcode == Opcode::CallOther)
3008 .expect("range-assignment user-op was emitted");
3009 assert_eq!(userop.output.unwrap().size, 1);
3010 }
3011
3012 struct WideContext;
3014
3015 impl PcodeLoweringContext for WideContext {
3016 fn default_space(&self) -> SpaceId {
3017 SpaceId::new(1)
3018 }
3019
3020 fn unique_space(&self) -> SpaceId {
3021 SpaceId::new(2)
3022 }
3023
3024 fn register_varnode(&self, id: RegisterId) -> Option<Varnode> {
3025 if usize::from(id) == 9 {
3026 Some(Varnode::new(SpaceId::new(3), 0x100, 16))
3027 } else {
3028 Some(Varnode::new(SpaceId::new(3), usize::from(id) as u64 * 4, 4))
3029 }
3030 }
3031
3032 fn bitrange_info(&self, _id: crate::BitRangeFieldId) -> Option<BitRangeInfo> {
3033 None
3034 }
3035
3036 fn address_size(&self, _space: SpaceId) -> Option<usize> {
3037 Some(8)
3038 }
3039 }
3040
3041 fn wide_lane_assignment(start: usize, size: usize, rhs: Expression) -> AstNode {
3042 AstNode::RangeAssignment {
3043 lhs: Range {
3044 value: Box::new(Expression {
3045 ty: ExpressionTy::Ident(Ident::Register(RegisterId::new(9))),
3046 size: Some(16),
3047 span: (),
3048 }),
3049 start: RangeParam::Literal(start),
3050 size: RangeParam::Literal(size),
3051 },
3052 size: None,
3053 rhs,
3054 }
3055 }
3056
3057 #[test]
3058 fn lower_writes_an_aligned_lane_of_wide_storage_as_its_sub_varnode() {
3059 let pcode = lower_instruction(
3061 &ast(vec![wide_lane_assignment(
3062 32,
3063 32,
3064 ident(RegisterId::new(0)),
3065 )]),
3066 &WideContext,
3067 )
3068 .unwrap();
3069 assert_eq!(pcode.ops.len(), 1);
3070 assert_eq!(pcode.ops[0].opcode, Opcode::Copy);
3071 assert_eq!(
3072 pcode.ops[0].output,
3073 Some(Varnode::new(SpaceId::new(3), 0x104, 4))
3074 );
3075 assert_eq!(
3076 pcode.ops[0].inputs,
3077 vec![Varnode::new(SpaceId::new(3), 0, 4)]
3078 );
3079 }
3080
3081 #[test]
3082 fn lower_writes_an_aligned_lane_of_a_wide_memory_operand_as_a_narrow_store() {
3083 let pcode = lower_instruction(
3085 &ast(vec![AstNode::RangeAssignment {
3086 lhs: Range {
3087 value: Box::new(Expression {
3088 ty: ExpressionTy::Load(Load {
3089 space: None,
3090 size: Some(16),
3091 ptr: Box::new(int(0x2000, 8)),
3092 }),
3093 size: Some(16),
3094 span: (),
3095 }),
3096 start: RangeParam::Literal(64),
3097 size: RangeParam::Literal(32),
3098 },
3099 size: None,
3100 rhs: ident(RegisterId::new(0)),
3101 }]),
3102 &WideContext,
3103 )
3104 .unwrap();
3105 let store = pcode.ops.last().unwrap();
3106 assert_eq!(store.opcode, Opcode::Store);
3107 assert_eq!(store.inputs[2], Varnode::new(SpaceId::new(3), 0, 4));
3108 let add = pcode
3109 .ops
3110 .iter()
3111 .find(|op| op.opcode == Opcode::IntAdd)
3112 .expect("the lane pointer is offset from the operand pointer");
3113 assert_eq!(add.inputs[1], Varnode::constant(8, 8));
3114 assert_eq!(store.inputs[1], add.output.unwrap());
3115 }
3116
3117 #[test]
3118 fn lower_still_rejects_an_unaligned_lane_of_wide_storage() {
3119 for (start, size) in [(4, 32), (0, 12), (127, 1)] {
3120 let error = lower_instruction(
3121 &ast(vec![wide_lane_assignment(start, size, int(1, 1))]),
3122 &WideContext,
3123 )
3124 .unwrap_err();
3125 assert!(
3126 matches!(error, PcodeLowerError::InvalidRange { .. }),
3127 "{start},{size}"
3128 );
3129 }
3130 }
3131
3132 #[test]
3133 fn lower_gives_width_preserving_float_builtins_their_operand_width() {
3134 let pcode = lower_instruction(
3136 &ast(vec![AstNode::Assignment {
3137 lhs: Ident::Register(RegisterId::new(0)),
3138 size: None,
3139 rhs: Expression {
3140 ty: ExpressionTy::FunctionCall {
3141 builtin: crate::Builtin::Trunc,
3142 args: vec![Expression {
3143 ty: ExpressionTy::FunctionCall {
3144 builtin: crate::Builtin::Round,
3145 args: vec![Expression {
3146 ty: ExpressionTy::Range(crate::Range {
3147 value: Box::new(ident(RegisterId::new(1))),
3148 start: crate::RangeParam::Literal(0),
3149 size: crate::RangeParam::Literal(32),
3150 }),
3151 size: Some(4),
3152 span: (),
3153 }],
3154 },
3155 size: None,
3156 span: (),
3157 }],
3158 },
3159 size: None,
3160 span: (),
3161 },
3162 }]),
3163 &Context,
3164 )
3165 .unwrap();
3166 let round = pcode
3167 .ops
3168 .iter()
3169 .find(|op| op.opcode == Opcode::FloatRound)
3170 .expect("round was emitted");
3171 assert_eq!(round.output.unwrap().size, 4);
3172 assert_eq!(pcode.ops.last().unwrap().opcode, Opcode::FloatTrunc);
3173 assert_eq!(pcode.ops.last().unwrap().output.unwrap().size, 4);
3174 }
3175
3176 #[test]
3177 fn lower_rejects_nodes_that_are_not_final_raw_pcode() {
3178 let error = lower_instruction(&ast(vec![AstNode::Build(crate::TableId::new(0))]), &Context)
3179 .unwrap_err();
3180 assert_eq!(error, PcodeLowerError::InternalNode("build statement"));
3181 let error = lower_instruction(
3182 &ast(vec![AstNode::Branch {
3183 target: LabelOrNode::Node("unresolved".into()),
3184 }]),
3185 &Context,
3186 )
3187 .unwrap_err();
3188 assert_eq!(
3189 error,
3190 PcodeLowerError::InternalNode("unresolved branch target")
3191 );
3192 }
3193
3194 #[test]
3195 fn lower_every_binary_operator_uses_its_raw_opcode() {
3196 use BinaryOperator::*;
3197 let cases = [
3198 (Mul, Opcode::IntMult, false),
3199 (Div, Opcode::IntDiv, false),
3200 (SignedDiv, Opcode::IntSDiv, false),
3201 (Mod, Opcode::IntRem, false),
3202 (SignedMod, Opcode::IntSRem, false),
3203 (FloatDiv, Opcode::FloatDiv, false),
3204 (FloatMul, Opcode::FloatMult, false),
3205 (Add, Opcode::IntAdd, false),
3206 (Sub, Opcode::IntSub, false),
3207 (FloatAdd, Opcode::FloatAdd, false),
3208 (FloatSub, Opcode::FloatSub, false),
3209 (LeftShift, Opcode::IntLeft, false),
3210 (RightShift, Opcode::IntRight, false),
3211 (SignedRightShift, Opcode::IntSRight, false),
3212 (SignedLessThan, Opcode::IntSLess, false),
3213 (SignedGreaterThan, Opcode::IntSLess, true),
3214 (SignedLessEqual, Opcode::IntSLessEqual, false),
3215 (SignedGreaterEqual, Opcode::IntSLessEqual, true),
3216 (LessEqual, Opcode::IntLessEqual, false),
3217 (GreaterEqual, Opcode::IntLessEqual, true),
3218 (LessThan, Opcode::IntLess, false),
3219 (GreaterThan, Opcode::IntLess, true),
3220 (FloatLessEqual, Opcode::FloatLessEqual, false),
3221 (FloatGreaterEqual, Opcode::FloatLessEqual, true),
3222 (FloatLessThan, Opcode::FloatLess, false),
3223 (FloatGreaterThan, Opcode::FloatLess, true),
3224 (Equal, Opcode::IntEqual, false),
3225 (NotEqual, Opcode::IntNotEqual, false),
3226 (FloatEqual, Opcode::FloatEqual, false),
3227 (FloatNotEqual, Opcode::FloatNotEqual, false),
3228 (LogicalXor, Opcode::BoolXor, false),
3229 (LogicalAnd, Opcode::BoolAnd, false),
3230 (LogicalOr, Opcode::BoolOr, false),
3231 (BitwiseXor, Opcode::IntXor, false),
3232 (BitwiseOr, Opcode::IntOr, false),
3233 (BitwiseAnd, Opcode::IntAnd, false),
3234 ];
3235 for (operator, opcode, reverse) in cases {
3236 assert_eq!(super::binary_opcode(operator), (opcode, reverse));
3237 }
3238 }
3239
3240 #[test]
3241 fn lower_every_builtin_and_unary_operator() {
3242 let builtins = [
3243 (crate::Builtin::Carry, Opcode::IntCarry),
3244 (crate::Builtin::Scarry, Opcode::IntSCarry),
3245 (crate::Builtin::Sborrow, Opcode::IntSBorrow),
3246 (crate::Builtin::Nan, Opcode::FloatNan),
3247 (crate::Builtin::Abs, Opcode::FloatAbs),
3248 (crate::Builtin::Sqrt, Opcode::FloatSqrt),
3249 (crate::Builtin::Floor, Opcode::FloatFloor),
3250 (crate::Builtin::Ceil, Opcode::FloatCeil),
3251 (crate::Builtin::Round, Opcode::FloatRound),
3252 (crate::Builtin::Int2Float, Opcode::FloatInt2Float),
3253 (crate::Builtin::Float2Float, Opcode::FloatFloat2Float),
3254 (crate::Builtin::Trunc, Opcode::FloatTrunc),
3255 (crate::Builtin::Zext, Opcode::IntZext),
3256 (crate::Builtin::Sext, Opcode::IntSext),
3257 (crate::Builtin::Popcount, Opcode::PopCount),
3258 (crate::Builtin::Lzcount, Opcode::LzCount),
3259 (crate::Builtin::Cpool, Opcode::CpoolRef),
3260 (crate::Builtin::NewObject, Opcode::New),
3261 ];
3262 for (builtin, opcode) in builtins {
3263 let expression = Expression {
3264 ty: ExpressionTy::FunctionCall {
3265 builtin,
3266 args: vec![int(1, 4)],
3267 },
3268 size: Some(4),
3269 span: (),
3270 };
3271 let pcode = lower_instruction(
3272 &ast(vec![AstNode::Assignment {
3273 lhs: Ident::Named(LocalVarId(0)),
3274 size: None,
3275 rhs: expression,
3276 }]),
3277 &Context,
3278 )
3279 .unwrap();
3280 assert_eq!(pcode.ops[0].opcode, opcode);
3281 }
3282 for (operator, opcode) in [
3283 (crate::UnaryOperator::LogicalNot, Opcode::BoolNegate),
3284 (crate::UnaryOperator::BitwiseNot, Opcode::IntNegate),
3285 (crate::UnaryOperator::Minus, Opcode::Int2Comp),
3286 (crate::UnaryOperator::FloatMinus, Opcode::FloatNeg),
3287 ] {
3288 let expression = Expression {
3289 ty: ExpressionTy::Unop(crate::Unop {
3290 op: operator,
3291 e: Box::new(ident(RegisterId::new(0))),
3292 }),
3293 size: Some(4),
3294 span: (),
3295 };
3296 let pcode = lower_instruction(
3297 &ast(vec![AstNode::Assignment {
3298 lhs: Ident::Named(LocalVarId(0)),
3299 size: None,
3300 rhs: expression,
3301 }]),
3302 &Context,
3303 )
3304 .unwrap();
3305 assert_eq!(pcode.ops[0].opcode, opcode);
3306 }
3307 }
3308
3309 #[test]
3310 fn lower_direct_and_indirect_control_flow() {
3311 let direct = lower_instruction(
3312 &ast(vec![AstNode::Call {
3313 target: LabelOrNode::Expr(int(0x1000, 8)),
3314 }]),
3315 &Context,
3316 )
3317 .unwrap();
3318 assert_eq!(
3319 direct.ops[0],
3320 PcodeOp::new(
3321 Opcode::Call,
3322 None,
3323 vec![Varnode::new(SpaceId::new(1), 0x1000, 8)]
3324 )
3325 );
3326 let indirect = lower_instruction(
3327 &ast(vec![
3328 AstNode::ConditionalBranch {
3329 condition: int(1, 1),
3330 target: LabelOrNode::Expr(int(0x2000, 8)),
3331 },
3332 AstNode::BranchIndirect {
3333 target: ident(RegisterId::new(0)),
3334 },
3335 AstNode::CallIndirect {
3336 target: ident(RegisterId::new(1)),
3337 },
3338 AstNode::Return {
3339 target: ident(RegisterId::new(2)),
3340 },
3341 ]),
3342 &Context,
3343 )
3344 .unwrap();
3345 assert_eq!(
3346 indirect.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
3347 vec![
3348 Opcode::CBranch,
3349 Opcode::BranchInd,
3350 Opcode::CallInd,
3351 Opcode::Return
3352 ]
3353 );
3354 assert_eq!(
3355 indirect.ops[0].inputs,
3356 vec![
3357 Varnode::new(SpaceId::new(1), 0x2000, 8),
3358 Varnode::constant(1, 1)
3359 ]
3360 );
3361 }
3362
3363 #[test]
3364 fn lowering_errors_are_displayable_and_typed() {
3365 let errors = [
3366 PcodeLowerError::UnknownSize,
3367 PcodeLowerError::ZeroSize,
3368 PcodeLowerError::CopySizeMismatch {
3369 input: 1,
3370 output: 2,
3371 },
3372 PcodeLowerError::InputSizeMismatch {
3373 operation: "operation",
3374 left: 1,
3375 right: 2,
3376 },
3377 PcodeLowerError::InvalidBooleanSize(2),
3378 PcodeLowerError::AddressSizeMismatch {
3379 expected: 4,
3380 actual: 8,
3381 },
3382 PcodeLowerError::StoreSizeMismatch {
3383 declared: 4,
3384 value: 8,
3385 },
3386 PcodeLowerError::InvalidRange {
3387 start: 0,
3388 size: 0,
3389 storage_bits: 32,
3390 },
3391 PcodeLowerError::UniqueSpaceOverflow,
3392 PcodeLowerError::UnknownRegister(RegisterId::new(9)),
3393 PcodeLowerError::UnresolvedIdentifier("field"),
3394 PcodeLowerError::UnresolvedSpace,
3395 PcodeLowerError::UnresolvedRangeParameter,
3396 PcodeLowerError::InternalNode("macro"),
3397 PcodeLowerError::Unsupported("range"),
3398 PcodeLowerError::DuplicateLabel("loop".into()),
3399 PcodeLowerError::UnknownLabel("loop".into()),
3400 PcodeLowerError::InvalidDirectTarget,
3401 ];
3402 for error in errors {
3403 assert!(!error.to_string().is_empty());
3404 }
3405 assert_eq!(
3406 InstructionPcode::lower(&ast(vec![]), &Context).unwrap(),
3407 InstructionPcode::new()
3408 );
3409 }
3410
3411 #[test]
3412 fn lower_rejects_invalid_raw_widths_and_ranges() {
3413 let range = |start, size| Expression {
3414 ty: ExpressionTy::Range(crate::Range {
3415 value: Box::new(ident(RegisterId::new(0))),
3416 start: crate::RangeParam::Literal(start),
3417 size: crate::RangeParam::Literal(size),
3418 }),
3419 size: None,
3420 span: (),
3421 };
3422 for (start, size) in [(0, 0), (0, 65), (31, 2)] {
3423 let error = lower_instruction(
3424 &ast(vec![AstNode::Assignment {
3425 lhs: Ident::Register(RegisterId::new(0)),
3426 size: None,
3427 rhs: range(start, size),
3428 }]),
3429 &Context,
3430 )
3431 .unwrap_err();
3432 assert!(matches!(error, PcodeLowerError::InvalidRange { .. }));
3433 }
3434
3435 let error = lower_instruction(
3436 &ast(vec![AstNode::Assignment {
3437 lhs: Ident::Named(LocalVarId(0)),
3438 size: None,
3439 rhs: Expression {
3440 ty: ExpressionTy::Range(crate::Range {
3441 value: Box::new(ident(RegisterId::new(0))),
3442 start: crate::RangeParam::MacroArg(LocalVarId(1)),
3443 size: crate::RangeParam::Literal(1),
3444 }),
3445 size: None,
3446 span: (),
3447 },
3448 }]),
3449 &Context,
3450 )
3451 .unwrap_err();
3452 assert_eq!(error, PcodeLowerError::UnresolvedRangeParameter);
3453
3454 let mismatch = ExpressionTy::Binop(Binop {
3455 op: BinaryOperator::Add,
3456 lhs: Box::new(ident(RegisterId::new(0))),
3457 rhs: Box::new(int(1, 1)),
3458 })
3459 .with_size(4);
3460 assert!(
3463 lower_instruction(
3464 &ast(vec![AstNode::Assignment {
3465 lhs: Ident::Named(LocalVarId(0)),
3466 size: None,
3467 rhs: mismatch,
3468 }]),
3469 &Context,
3470 )
3471 .is_ok()
3472 );
3473
3474 let comparison = ExpressionTy::Binop(Binop {
3475 op: BinaryOperator::Equal,
3476 lhs: Box::new(ident(RegisterId::new(0))),
3477 rhs: Box::new(ident(RegisterId::new(1))),
3478 })
3479 .with_size(4);
3480 assert_eq!(
3481 lower_instruction(
3482 &ast(vec![AstNode::Assignment {
3483 lhs: Ident::Named(LocalVarId(0)),
3484 size: None,
3485 rhs: comparison,
3486 }]),
3487 &Context,
3488 )
3489 .unwrap_err(),
3490 PcodeLowerError::InvalidBooleanSize(4)
3491 );
3492
3493 let bad_load = Load {
3494 space: Some(PcodeSpaceRef::Resolved(SpaceId::new(1))),
3495 size: Some(4),
3496 ptr: Box::new(ident(RegisterId::new(0))),
3497 };
3498 assert!(matches!(
3499 lower_instruction(
3500 &ast(vec![AstNode::Assignment {
3501 lhs: Ident::Named(LocalVarId(0)),
3502 size: Some(4),
3503 rhs: ExpressionTy::Load(bad_load.clone()).with_size(4),
3504 }]),
3505 &Context,
3506 ),
3507 Err(PcodeLowerError::AddressSizeMismatch { .. })
3508 ));
3509 assert!(matches!(
3510 lower_instruction(
3511 &ast(vec![AstNode::LoadAssignment {
3512 lhs: bad_load,
3513 size: None,
3514 rhs: int(1, 1),
3515 }]),
3516 &Context,
3517 ),
3518 Err(PcodeLowerError::AddressSizeMismatch { .. })
3519 ));
3520 }
3521
3522 #[test]
3523 fn lower_handles_subpieces_and_rejects_invalid_final_forms() {
3524 let lsb = Expression {
3525 ty: ExpressionTy::SubPieceLsb {
3526 src: Box::new(ident(RegisterId::new(0))),
3527 count: 2,
3528 },
3529 size: Some(2),
3530 span: (),
3531 };
3532 let msb = Expression {
3533 ty: ExpressionTy::SubPieceMsb {
3534 src: Box::new(ident(RegisterId::new(0))),
3535 count: 2,
3536 },
3537 size: Some(2),
3538 span: (),
3539 };
3540 let pcode = lower_instruction(
3541 &ast(vec![
3542 AstNode::Assignment {
3543 lhs: Ident::Named(LocalVarId(0)),
3544 size: None,
3545 rhs: lsb,
3546 },
3547 AstNode::Assignment {
3548 lhs: Ident::Named(LocalVarId(1)),
3549 size: None,
3550 rhs: msb,
3551 },
3552 ]),
3553 &Context,
3554 )
3555 .unwrap();
3556 assert_eq!(
3557 pcode.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
3558 vec![Opcode::SubPiece, Opcode::SubPiece]
3559 );
3560 assert_eq!(pcode.ops[0].inputs[1], Varnode::constant(0, 8));
3561 assert_eq!(pcode.ops[1].inputs[1], Varnode::constant(2, 8));
3562
3563 let range = Expression {
3564 ty: ExpressionTy::Range(crate::Range {
3565 value: Box::new(ident(RegisterId::new(0))),
3566 start: crate::RangeParam::Literal(0),
3567 size: crate::RangeParam::Literal(1),
3568 }),
3569 size: Some(1),
3570 span: (),
3571 };
3572 let pcode = lower_instruction(
3573 &ast(vec![AstNode::Assignment {
3574 lhs: Ident::Named(LocalVarId(0)),
3575 size: None,
3576 rhs: range,
3577 }]),
3578 &Context,
3579 )
3580 .unwrap();
3581 assert_eq!(
3582 pcode.ops.iter().map(|op| op.opcode).collect::<Vec<_>>(),
3583 vec![Opcode::IntRight, Opcode::IntAnd, Opcode::SubPiece]
3584 );
3585 let error = lower_instruction(
3586 &ast(vec![
3587 AstNode::Label("same".into()),
3588 AstNode::Label("same".into()),
3589 ]),
3590 &Context,
3591 )
3592 .unwrap_err();
3593 assert_eq!(error, PcodeLowerError::DuplicateLabel("same".into()));
3594 let error = lower_instruction(
3595 &ast(vec![AstNode::Branch {
3596 target: LabelOrNode::Label("missing".into()),
3597 }]),
3598 &Context,
3599 )
3600 .unwrap_err();
3601 assert_eq!(error, PcodeLowerError::UnknownLabel("missing".into()));
3602 let error = lower_instruction(
3603 &ast(vec![AstNode::Branch {
3604 target: LabelOrNode::Expr(ident(RegisterId::new(0))),
3605 }]),
3606 &Context,
3607 )
3608 .unwrap_err();
3609 assert_eq!(error, PcodeLowerError::InvalidDirectTarget);
3610 }
3611
3612 #[test]
3613 fn deferred_space_is_rejected_at_lowering() {
3614 let load = Expression {
3617 ty: ExpressionTy::Load(Load {
3618 space: Some(PcodeSpaceRef::Deferred("segment".into())),
3619 size: Some(4),
3620 ptr: Box::new(int(0x10, 8)),
3621 }),
3622 size: Some(4),
3623 span: (),
3624 };
3625 let error = lower_instruction(
3626 &ast(vec![AstNode::Assignment {
3627 lhs: Ident::Register(RegisterId::new(0)),
3628 size: None,
3629 rhs: load.clone(),
3630 }]),
3631 &Context,
3632 )
3633 .unwrap_err();
3634 assert_eq!(error, PcodeLowerError::UnresolvedSpace);
3635 let ExpressionTy::Load(store) = load.ty else {
3636 unreachable!()
3637 };
3638 let error = lower_instruction(
3639 &ast(vec![AstNode::LoadAssignment {
3640 lhs: store,
3641 size: None,
3642 rhs: ident(RegisterId::new(0)),
3643 }]),
3644 &Context,
3645 )
3646 .unwrap_err();
3647 assert_eq!(error, PcodeLowerError::UnresolvedSpace);
3648 }
3649}