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