1use std::{borrow::Cow, cmp};
42
43use rustc_hash::FxHashMap as HashMap;
44
45use crate::{
46 space::{LocalMemorySpaceId, SPACE_CONST, Space, SpaceId, SpaceType},
47 types::{AggregateField, TypeId},
48 value::{
49 BodyView, FunctionBody, Instruction, LocalBlockId, LocalValueId, Temp, TempId, TempSpace,
50 ValueId, ValueRef,
51 block::{BasicBlock, BlockId},
52 block_param::{BlockParam, BlockParamId},
53 function::FunctionId,
54 insn::{
55 Apply, Assert, Binary, Binop, Branch, BranchInd, CBranch, Call, CallInd, Callee, Carry,
56 Extract, FloatBinop, FloatToFloat, FloatToInt, Gep, InstructionId, InstructionRef,
57 IntBinop, IntToFloat, IntrinsicApp, IntrinsicId, IsFloatNaN, Load, LocalInsnId,
58 LzCount, Map, Mnemonic, PCodeOp, PCodeOpId, PopCount, Range, Return, ReturnValue,
59 SBorrow, SCarry, Scan, Sext, Store, Switch, SwitchArm, TailCall, Tuple, Unary, Unop,
60 Zext,
61 },
62 varnode::Varnode,
63 },
64};
65
66#[cfg(test)]
67use crate::value::TempRef;
68
69pub struct Builder<'str, 'ctx> {
73 body: &'ctx mut FunctionBody<'str>,
74 shared: &'ctx crate::context::Shared<'str>,
75 interfaces:
76 &'ctx jstd::registry::Registry<FunctionId, crate::value::function::FunctionInterface<'str>>,
77
78 pub(crate) block: LocalBlockId,
83
84 namespace: HashMap<Cow<'str, str>, ValueId>,
86
87 local_labels: HashMap<Cow<'str, str>, LocalBlockId>,
89
90 address: Option<u64>,
92
93 pub(crate) is_terminated: bool,
96
97 insert_point: Option<usize>,
103}
104
105macro_rules! cmp_pair {
108 ($fwd:ident, $fwd_local:ident, $rev:ident, $rev_local:ident, $op:expr) => {
109 pub fn $fwd(
110 &mut self,
111 lhs: ValueId,
112 rhs: ValueId,
113 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
114 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
115 let local = self.$fwd_local(lhs, rhs);
116 self.insn_ref(local)
117 }
118 pub fn $fwd_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
119 self.push_binop_local($op, lhs, rhs, Some(1))
120 }
121 pub fn $rev(
122 &mut self,
123 lhs: ValueId,
124 rhs: ValueId,
125 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
126 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
127 let local = self.$rev_local(lhs, rhs);
128 self.insn_ref(local)
129 }
130 pub fn $rev_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
131 self.push_binop_local($op, rhs, lhs, Some(1))
132 }
133 };
134}
135
136macro_rules! unop_leaf {
139 ($(#[$m:meta])* $name:ident, $lname:ident, $op:expr) => {
140 $(#[$m])*
141 pub fn $name(&mut self, src: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
142 let src = self.loc(src);
143 let local = self.$lname(src);
144 self.insn_ref(local)
145 }
146 pub fn $lname(&mut self, src: LocalValueId) -> LocalInsnId {
148 self.push_unop_local($op, src)
149 }
150 };
151}
152
153macro_rules! binop_leaf {
157 ($(#[$m:meta])* $name:ident, $lname:ident, $op:expr, $size:expr) => {
158 $(#[$m])*
159 pub fn $name(
160 &mut self,
161 lhs: ValueId,
162 rhs: ValueId,
163 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
164 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
165 let local = self.$lname(lhs, rhs);
166 self.insn_ref(local)
167 }
168 pub fn $lname(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
170 self.push_binop_local($op, lhs, rhs, $size)
171 }
172 };
173}
174
175macro_rules! conv_leaf {
179 ($name:ident, $lname:ident, $err:literal, $variant:ident) => {
180 pub fn $name(
181 &mut self,
182 src: ValueId,
183 size: usize,
184 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
185 let src = self.loc(src);
186 let local = self.$lname(src, size);
187 self.insn_ref(local)
188 }
189 pub fn $lname(&mut self, src: LocalValueId, size: usize) -> LocalInsnId {
191 assert!(!matches!(src, LocalValueId::Varnode(_)), $err);
192 self.store_insn(Mnemonic::$variant($variant { src, size }), size)
193 }
194 };
195}
196
197impl<'str, 'ctx> Builder<'str, 'ctx> {
198 fn fresh_temp_space(&mut self, name: Option<&str>) -> crate::value::TempSpaceId {
199 let (word_size, addr_size) = {
200 let default = self.shr().space(self.shr().default_space);
201 (default.word_size, default.addr_size)
202 };
203 self.body
204 .push_temp_space(TempSpace::new(name, word_size, addr_size))
205 }
206
207 pub fn make_temp(&mut self, size: usize) -> TempId {
209 let space = self.fresh_temp_space(None);
210 self.body.push_temp(Temp::new(0, size, space.local))
211 }
212
213 pub fn make_named_temp(&mut self, name: Cow<'str, str>, size: usize) -> TempId {
215 let unique = self.body.names.unique(name);
216 let space = self.fresh_temp_space(Some(unique.as_ref()));
217 self.body
218 .push_temp(Temp::new(0, size, space.local).with_name(unique))
219 }
220
221 pub fn make_temp_labeled(&mut self, label: u32, size: usize) -> TempId {
223 let space = self.fresh_temp_space(None);
224 let mut temp = Temp::new(0, size, space.local);
225 temp.label = Some(label);
226 self.body.push_temp(temp)
227 }
228
229 pub fn new(
234 body: &'ctx mut FunctionBody<'str>,
235 shared: &'ctx crate::context::Shared<'str>,
236 interfaces: &'ctx jstd::registry::Registry<
237 FunctionId,
238 crate::value::function::FunctionInterface<'str>,
239 >,
240 block: BlockId,
241 ) -> Self {
242 assert_eq!(
243 body.id(),
244 block.func,
245 "Builder block must belong to its body"
246 );
247 Self::new_local(body, shared, interfaces, block.local)
248 }
249
250 pub fn new_local(
256 body: &'ctx mut FunctionBody<'str>,
257 shared: &'ctx crate::context::Shared<'str>,
258 interfaces: &'ctx jstd::registry::Registry<
259 FunctionId,
260 crate::value::function::FunctionInterface<'str>,
261 >,
262 block: LocalBlockId,
263 ) -> Self {
264 let is_terminated = body.blocks[block]
265 .instructions
266 .last()
267 .is_some_and(|&i| body.insns[i].mnemonic().is_terminator());
268 Self {
269 body,
270 shared,
271 interfaces,
272 is_terminated,
273 block,
274 namespace: HashMap::default(),
275 local_labels: HashMap::default(),
276 address: None,
277 insert_point: None,
278 }
279 }
280
281 #[inline]
285 fn func(&self) -> FunctionId {
286 self.body.id()
287 }
288
289 fn insn_ref(&self, local: LocalInsnId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
291 let id = InstructionId::new(self.func(), local);
292 InstructionRef::new(self.view(), id)
293 }
294
295 pub fn view(&self) -> BodyView<'_, 'str> {
298 BodyView::new(&*self.body, self.shared, self.interfaces)
299 }
300
301 pub fn is_terminated(&self) -> bool {
303 self.block_is_terminated(self.block)
304 }
305
306 fn block_is_terminated(&self, block: LocalBlockId) -> bool {
309 self.body.blocks[block]
310 .instructions
311 .last()
312 .is_some_and(|&i| self.body.insns[i].mnemonic().is_terminator())
313 }
314
315 pub fn set_address(&mut self, addr: u64) {
317 self.address = Some(addr);
318 }
319
320 pub fn clear_address(&mut self) {
322 self.address = None;
323 }
324
325 pub fn set_insert_point_to_start(&mut self) {
336 self.insert_point = Some(0);
337 }
338
339 pub fn set_insert_point_before(&mut self, before_id: InstructionId) {
348 let index = self.body.blocks[self.block]
349 .instructions
350 .iter()
351 .position(|&id| id == before_id.local)
352 .expect("before_id not found in block");
353 self.insert_point = Some(index);
354 }
355
356 pub fn set_insert_point_to_end(&mut self) {
358 self.insert_point = None;
359 }
360
361 pub fn get_range(
363 &mut self,
364 src: ValueId,
365 range: std::ops::Range<usize>,
366 ) -> Option<ValueRef<'str, '_, BodyView<'_, 'str>>> {
367 let src = self.loc(src);
368 let dst = self.get_range_local(src, range)?;
369 Some(self.get_value(dst.qualify(self.func())))
370 }
371
372 pub fn get_range_local(
376 &mut self,
377 src: LocalValueId,
378 range: std::ops::Range<usize>,
379 ) -> Option<LocalValueId> {
380 if range.is_empty() {
381 return None;
382 }
383
384 let dst = match src {
385 LocalValueId::Literal(lit) => {
386 let value = self.shr().values.literals[lit].value;
387 let id = self.shr().get_const(value, range.len());
388 id.strip_func()
389 }
390
391 LocalValueId::Varnode(vid) => {
392 let size = Varnode::from_id(self.shr(), vid).size();
393 if range.end > size {
394 return None;
395 }
396 let local = self.store_insn(
397 Mnemonic::Range(Range {
398 src,
399 start: range.start,
400 size: range.len(),
401 }),
402 range.len(),
403 );
404 LocalValueId::Instruction(local)
405 }
406
407 LocalValueId::Temp(tlocal) => {
408 let (address, size, space) = {
409 let temp = &self.body.temps[tlocal];
410 (temp.address, temp.size, temp.space)
411 };
412 if range.end > size {
413 return None;
414 }
415 let temp = Temp::new(address + range.start as i64, range.len(), space);
416 let local = self.body.temps.push(temp);
417 LocalValueId::Temp(local)
418 }
419
420 LocalValueId::Instruction(_) => {
421 let size = self.lsize_of(src);
422 if range.end > size {
423 return None;
424 }
425 let local = self.store_insn(
426 Mnemonic::Range(Range {
427 src,
428 start: range.start,
429 size: range.len(),
430 }),
431 range.len(),
432 );
433 LocalValueId::Instruction(local)
434 }
435
436 _ => return None,
438 };
439
440 Some(dst)
441 }
442
443 pub fn push_range(
448 &mut self,
449 src: ValueId,
450 start: usize,
451 size: usize,
452 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
453 let local = self.push_range_local(self.loc(src), start, size);
454 self.insn_ref(local)
455 }
456
457 pub fn push_range_local(
459 &mut self,
460 src: LocalValueId,
461 start: usize,
462 size: usize,
463 ) -> LocalInsnId {
464 self.store_insn(Mnemonic::Range(Range { src, start, size }), size)
465 }
466
467 pub fn remove_alias(&mut self, name: &str) {
469 self.namespace.remove(name);
470 }
471
472 pub fn set_alias(&mut self, name: Cow<'str, str>, id: ValueId) {
475 self.namespace.insert(name, id);
476 }
477
478 pub fn switch_to_block(&mut self, block: BlockId) {
479 self.switch_to_block_local(block.local);
480 }
481
482 pub fn switch_to_block_local(&mut self, block: LocalBlockId) {
484 self.block = block;
485 self.is_terminated = self.block_is_terminated(block);
486 }
487
488 pub fn current_block(&self) -> BlockId {
490 BlockId::new(self.func(), self.block)
491 }
492
493 pub fn try_get_value(&self, name: &str) -> Option<ValueRef<'str, '_, BodyView<'_, 'str>>> {
495 self.namespace.get(name).map(|&id| self.get_value(id))
496 }
497
498 pub fn shr(&self) -> &crate::context::Shared<'str> {
500 self.shared
501 }
502
503 fn set_insn_space_local(&mut self, local: LocalInsnId, space: LocalMemorySpaceId) {
509 if space.shared().is_some_and(|space| {
510 matches!(Space::from_id(self.shr(), space).ty, SpaceType::Register)
511 }) {
512 return;
513 }
514 let qualified = match space {
515 LocalMemorySpaceId::Shared(id) => crate::space::MemorySpaceId::Shared(id),
516 LocalMemorySpaceId::Temp(t) => match self.body.try_id() {
517 Some(func) => {
518 crate::space::MemorySpaceId::Temp(crate::value::TempSpaceId::new(func, t))
519 }
520 None => return,
521 },
522 };
523 let cur_type = self.body.insns[local].type_id;
524 let size = self.shr().types.size_of(cur_type);
525 let type_id = self.shr().types.get_or_make_space_address(size, qualified);
526 self.body.insns[local].type_id = type_id;
527 }
528
529 fn rename_insn_local(
534 &mut self,
535 local: LocalInsnId,
536 name: Cow<'str, str>,
537 ) -> crate::error::Result<()> {
538 if self.body.try_id().is_some() {
539 let id = InstructionId::new(self.func(), local);
540 let old = self.body.insns[local].name.clone();
541 self.body.register_local_name(
542 self.shared,
543 ValueId::Instruction(id),
544 name.clone(),
545 old.as_deref(),
546 )?;
547 }
548 self.body.insns[local].name = Some(name);
549 Ok(())
550 }
551
552 pub(crate) fn rename_insn(
555 &mut self,
556 id: InstructionId,
557 name: Cow<'str, str>,
558 ) -> crate::error::Result<()> {
559 self.rename_insn_local(id.local, name)
560 }
561
562 #[track_caller]
569 pub fn push_mnemonic_with_type(
570 &mut self,
571 mnemonic: Mnemonic,
572 type_id: TypeId,
573 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
574 let local = self.store_insn_with_type(mnemonic, type_id);
575 self.insn_ref(local)
576 }
577
578 #[track_caller]
581 fn store_insn(&mut self, mnemonic: Mnemonic, size: usize) -> LocalInsnId {
582 let type_id = self.shr().types.get_or_make_int(size);
583 self.store_insn_with_type(mnemonic, type_id)
584 }
585
586 #[track_caller]
591 fn store_insn_with_type(&mut self, mnemonic: Mnemonic, type_id: TypeId) -> LocalInsnId {
592 if self.is_terminated && self.insert_point.is_none() {
593 let block_address = self.body.blocks[self.block].address;
594 if let Some(address) = self.address.or(block_address) {
595 panic!("cannot append instruction to a terminated block at {address:#x}");
596 }
597 panic!("cannot append instruction to a terminated block");
598 }
599
600 let block = self.block;
601 let insn = Instruction::new(type_id, mnemonic);
602 let args = insn.mnemonic().args();
609 let local = self.body.insns.push(insn);
610 for arg in args {
611 self.body.users.entry(arg).or_default().push(local);
612 }
613
614 if let Some(address) = self.address {
615 self.body.insns[local].set_address(address);
616 }
617
618 match self.insert_point {
619 None => {
620 self.body.insns[local].parent = Some(block);
621 self.body.blocks[block].instructions.push(local);
622 }
623 Some(ref mut pos) => {
624 let index = *pos;
625 self.body.insns[local].parent = Some(block);
626 self.body.blocks[block].instructions.insert(index, local);
627 *pos += 1;
628 }
629 }
630
631 local
632 }
633
634 fn get_value(&self, id: ValueId) -> ValueRef<'str, '_, BodyView<'_, 'str>> {
635 ValueRef::from_view(self.view(), id)
639 }
640
641 fn loc(&self, id: ValueId) -> LocalValueId {
645 id.localize(self.func())
646 }
647
648 fn loc_vec(&self, ids: Vec<ValueId>) -> Vec<LocalValueId> {
650 let func = self.func();
651 ids.into_iter().map(|v| v.localize(func)).collect()
652 }
653
654 pub(crate) fn stored_type_of(&self, id: ValueId) -> Option<TypeId> {
657 self.lstored_type_of(self.loc(id))
658 }
659
660 fn ltype_of(&self, id: LocalValueId) -> TypeId {
663 self.body.local_type_of(self.shared, id)
664 }
665
666 fn lstored_type_of(&self, id: LocalValueId) -> Option<TypeId> {
669 self.body.local_stored_type_of(self.shared, id)
670 }
671
672 fn lsize_of(&self, id: LocalValueId) -> usize {
674 self.shr().types.size_of(self.ltype_of(id))
675 }
676
677 fn lspace_of(&self, id: LocalValueId) -> Option<SpaceId> {
681 match id {
682 LocalValueId::Varnode(vid) => Some(Varnode::from_id(self.shr(), vid).space().id),
683 LocalValueId::Instruction(local) => {
684 let ty = self.body.insns[local].type_id;
685 self.shr().types.space_of(ty).and_then(|m| m.shared())
686 }
687 _ => None,
688 }
689 }
690
691 pub(crate) fn set_insn_type(&mut self, id: InstructionId, type_id: TypeId) {
692 self.body.insn_mut(id).type_id = type_id;
693 }
694
695 pub(crate) fn constrain_param_size(&mut self, id: BlockParamId, size: usize) {
696 self.body.block_param_mut(id).type_id = self.shared.types.get_or_make_int(size);
697 }
698
699 pub fn set_param_type(&mut self, id: BlockParamId, type_id: TypeId) {
700 self.body.block_param_mut(id).type_id = type_id;
701 }
702
703 pub(crate) fn add_cfg_edge(&mut self, from: BlockId, to: BlockId) {
704 self.body.add_cfg_edge(from, to);
705 }
706
707 fn merge_space_ids(&self, lhs: LocalValueId, rhs: LocalValueId) -> Option<SpaceId> {
713 match (self.lspace_of(lhs), self.lspace_of(rhs)) {
714 (Some(space), None) | (None, Some(space)) => Some(space),
718 _ => None,
719 }
720 }
721
722 fn is_literal(&self, id: LocalValueId) -> bool {
723 matches!(id, LocalValueId::Literal(_))
724 }
725
726 fn coerce_literal_size(&mut self, id: LocalValueId, size: usize) -> LocalValueId {
727 let LocalValueId::Literal(lit_id) = id else {
728 return id;
729 };
730 let literal = self.shr().values.literals[lit_id].clone();
731 let current_size = self.shr().types.size_of(literal.type_id);
732 if current_size == size || literal.symbolic.is_some() {
733 return id;
734 }
735 self.shr().get_const(literal.value, size).strip_func()
736 }
737
738 pub fn get_or_make_local_label(&mut self, name: Cow<'str, str>) -> BlockId {
739 if let Some(&local) = self.local_labels.get(name.as_ref()) {
740 return BlockId::new(self.func(), local);
741 }
742 let unique_name = self.body.names.unique(name.clone());
749 let id = self.body.push_block(BasicBlock::detached());
750 self.body
751 .register_local_name(
752 self.shared,
753 ValueId::BasicBlock(id),
754 unique_name.clone(),
755 None,
756 )
757 .expect("name was deduplicated");
758 self.body.block_mut(id).set_name(Some(unique_name));
759 self.local_labels.insert(name, id.local);
760 id
761 }
762
763 pub fn get_or_make_local_temp_space(&mut self, name: &str) -> LocalMemorySpaceId {
767 let (word_size, addr_size) = {
768 let default = self.shr().space(self.shr().default_space);
769 (default.word_size, default.addr_size)
770 };
771 let body = &mut *self.body;
772 for index in 0..body.temp_spaces.len() {
773 let local = crate::value::LocalTempSpaceId::from(index);
774 if body.temp_spaces[local].name.as_deref() == Some(name) {
775 return LocalMemorySpaceId::Temp(local);
776 }
777 }
778 let id = body.push_temp_space(TempSpace::new(Some(name), word_size, addr_size));
779 LocalMemorySpaceId::Temp(id.local)
780 }
781
782 pub fn ensure_local(&mut self, src: ValueId) -> ValueId {
786 let src = self.loc(src);
787 self.ensure_local_local(src).qualify(self.func())
788 }
789
790 pub fn ensure_local_local(&mut self, src: LocalValueId) -> LocalValueId {
794 match src {
795 LocalValueId::Varnode(vid) => {
796 let node = Varnode::from_id(self.shr(), vid);
797 let size = node.size();
798 let space = node.space().id;
799 let name = node.name().map(str::to_owned);
800 let id = self.push_load_local::<false>(src, size, space);
801
802 if let (Some(name), LocalValueId::Instruction(local)) = (name, id) {
804 let unique = self.body.names.unique(name.to_lowercase().into());
805 self.rename_insn_local(local, unique)
806 .expect("This name was deduplicated");
807 }
808
809 id
810 }
811
812 LocalValueId::Temp(tlocal) => {
813 let (size, space, name) = {
814 let temp = &self.body.temps[tlocal];
815 (
816 temp.size,
817 LocalMemorySpaceId::Temp(temp.space),
818 temp.name.clone(),
819 )
820 };
821 let id = self.push_load_local::<false>(src, size, space);
822 let LocalValueId::Instruction(local) = id else {
823 unreachable!("non-constant temporary load creates an instruction");
824 };
825
826 if let Some(name) = name {
827 let unique = self.body.names.unique(Cow::Owned(name.to_lowercase()));
828 self.rename_insn_local(local, unique)
829 .expect("temporary load name was deduplicated");
830 }
831
832 id
833 }
834
835 _ => src,
836 }
837 }
838
839 #[track_caller]
846 pub fn push_load<const CHECK_LOCAL: bool>(
847 &mut self,
848 src: ValueId,
849 size: usize,
850 space: impl Into<LocalMemorySpaceId>,
851 ) -> ValueRef<'str, '_, BodyView<'_, 'str>> {
852 let src = self.loc(src);
853 let id = self.push_load_local::<CHECK_LOCAL>(src, size, space);
854 self.get_value(id.qualify(self.func()))
855 }
856
857 #[track_caller]
860 pub fn push_load_local<const CHECK_LOCAL: bool>(
861 &mut self,
862 mut src: LocalValueId,
863 size: usize,
864 space: impl Into<LocalMemorySpaceId>,
865 ) -> LocalValueId {
866 let space = space.into();
867 if CHECK_LOCAL {
868 src = self.ensure_local_local(src);
869 }
870
871 if space == SPACE_CONST {
872 match src {
873 LocalValueId::Literal(lit) => {
874 let value = self.shr().values.literals[lit].value;
875 let id = self.shr().get_const(value, size);
876 id.strip_func()
877 }
878
879 _ => panic!("Expected literal value for CONST space load"),
880 }
881 } else {
882 match src {
887 LocalValueId::Varnode(id) => {
888 let varnode = Varnode::from_id(self.shr(), id);
889 if varnode.space().id != space {
890 panic!(
891 "push_load: ptr is a varnode but its space {:?} does not match the load space {:?}; \
892 call ensure_local on the ptr first",
893 varnode.space().id,
894 space
895 );
896 }
897 }
898
899 LocalValueId::Instruction(local) => {
900 self.set_insn_space_local(local, space);
901 }
902
903 _ => {}
904 }
905
906 let local = self.store_insn(
907 Mnemonic::Load(Load {
908 ptr: src,
909 space,
910 size,
911 }),
912 size,
913 );
914 LocalValueId::Instruction(local)
915 }
916 }
917
918 fn push_unop_local(&mut self, op: Unop, src: LocalValueId) -> LocalInsnId {
921 assert!(
922 !matches!(src, LocalValueId::Varnode(_)),
923 "push_unop: varnode operand is not allowed; use ensure_local or &name addressof syntax"
924 );
925 let size = self.lsize_of(src);
926 self.store_insn(Mnemonic::Unop(Unary { op, src }), size)
927 }
928
929 pub fn push_bool_not(&mut self, src: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
931 let src = self.loc(src);
932 let local = self.push_bool_not_local(src);
933 self.insn_ref(local)
934 }
935
936 pub fn push_bool_not_local(&mut self, src: LocalValueId) -> LocalInsnId {
938 debug_assert!(
939 self.lstored_type_of(src)
940 .is_some_and(|t| self.shr().types.is_bool(t)),
941 "push_bool_not: operand must be bool-typed"
942 );
943 let f = self.shr().get_bool_const(false).strip_func();
944 self.push_binop_local(Binop::Int(IntBinop::Equal), src, f, Some(1))
945 }
946
947 unop_leaf!(
948 push_bit_negate,
950 push_bit_negate_local,
951 Unop::IntNot
952 );
953
954 unop_leaf!(
955 push_neg,
957 push_neg_local,
958 Unop::IntNegate
959 );
960
961 unop_leaf!(
962 push_fneg,
964 push_fneg_local,
965 Unop::FloatNegate
966 );
967
968 fn push_binop_local(
969 &mut self,
970 op: Binop,
971 lhs: LocalValueId,
972 rhs: LocalValueId,
973 size: Option<usize>,
974 ) -> LocalInsnId {
975 let lhs_size = self.lsize_of(lhs);
976 let rhs_size = self.lsize_of(rhs);
977 let operand_size = match (
978 lhs_size == rhs_size,
979 self.is_literal(lhs),
980 self.is_literal(rhs),
981 ) {
982 (true, _, _) => lhs_size,
983 (false, true, false) => rhs_size,
984 (false, false, true) => lhs_size,
985 (false, true, true) => lhs_size.max(rhs_size),
986 (false, false, false) => lhs_size,
990 };
991 let lhs = self.coerce_literal_size(lhs, operand_size);
992 let rhs = self.coerce_literal_size(rhs, operand_size);
993 assert_eq!(
994 self.lsize_of(lhs),
995 self.lsize_of(rhs),
996 "push_binop: operands must have equal size; emit an explicit cast first"
997 );
998
999 let result_type = {
1001 let lhs_type = self.ltype_of(lhs);
1002 let rhs_type = self.ltype_of(rhs);
1003 self.shr().types.binop_result(lhs_type, op, rhs_type)
1004 };
1005
1006 let result_type = if let Some(forced_size) = size {
1008 let current_size = self.shr().types.size_of(result_type);
1009 if forced_size != current_size {
1010 self.shr().types.get_or_make_int(forced_size)
1011 } else {
1012 result_type
1013 }
1014 } else {
1015 result_type
1016 };
1017
1018 let result_type = if self.shr().types.space_of(result_type).is_none()
1024 && matches!(op, Binop::Int(IntBinop::Add | IntBinop::Sub))
1025 {
1026 match self.merge_space_ids(lhs, rhs) {
1027 Some(space)
1028 if !matches!(Space::from_id(self.shr(), space).ty, SpaceType::Register) =>
1029 {
1030 let size = self.shr().types.size_of(result_type);
1031 self.shr().types.get_or_make_space_address(size, space)
1032 }
1033 _ => result_type,
1034 }
1035 } else {
1036 result_type
1037 };
1038
1039 let result_type = if matches!(op, Binop::Int(IntBinop::Add | IntBinop::Sub))
1044 && self.shr().types.space_of(result_type).is_some()
1045 {
1046 let spaced = |b: &Self, v| {
1047 b.lspace_of(v)
1048 .is_some_and(|s| !matches!(Space::from_id(b.shr(), s).ty, SpaceType::Register))
1049 };
1050 if spaced(self, lhs) && spaced(self, rhs) {
1051 let size = self.shr().types.size_of(result_type);
1052 self.shr().types.get_or_make_int(size)
1053 } else {
1054 result_type
1055 }
1056 } else {
1057 result_type
1058 };
1059
1060 self.store_insn_with_type(Mnemonic::Binop(Binary { op, lhs, rhs }), result_type)
1061 }
1062
1063 binop_leaf!(push_mul, push_mul_local, Binop::Int(IntBinop::Mul), None);
1066 binop_leaf!(push_div, push_div_local, Binop::Int(IntBinop::Div), None);
1067 binop_leaf!(push_sdiv, push_sdiv_local, Binop::Int(IntBinop::Sdiv), None);
1068 binop_leaf!(push_mod, push_mod_local, Binop::Int(IntBinop::Rem), None);
1069 binop_leaf!(push_smod, push_smod_local, Binop::Int(IntBinop::Srem), None);
1070 binop_leaf!(push_add, push_add_local, Binop::Int(IntBinop::Add), None);
1071 binop_leaf!(push_sub, push_sub_local, Binop::Int(IntBinop::Sub), None);
1072
1073 binop_leaf!(
1076 push_fdiv,
1077 push_fdiv_local,
1078 Binop::Float(FloatBinop::Div),
1079 None
1080 );
1081 binop_leaf!(
1082 push_fmul,
1083 push_fmul_local,
1084 Binop::Float(FloatBinop::Mul),
1085 None
1086 );
1087 binop_leaf!(
1088 push_fadd,
1089 push_fadd_local,
1090 Binop::Float(FloatBinop::Add),
1091 None
1092 );
1093 binop_leaf!(
1094 push_fsub,
1095 push_fsub_local,
1096 Binop::Float(FloatBinop::Sub),
1097 None
1098 );
1099
1100 binop_leaf!(
1103 push_shl,
1104 push_shl_local,
1105 Binop::Int(IntBinop::ShiftLeft),
1106 None
1107 );
1108 binop_leaf!(
1109 push_shr,
1110 push_shr_local,
1111 Binop::Int(IntBinop::ShiftRight),
1112 None
1113 );
1114 binop_leaf!(
1115 push_sshr,
1116 push_sshr_local,
1117 Binop::Int(IntBinop::SShiftRight),
1118 None
1119 );
1120
1121 cmp_pair!(
1125 push_slt,
1126 push_slt_local,
1127 push_sgt,
1128 push_sgt_local,
1129 Binop::Int(IntBinop::SLess)
1130 );
1131 cmp_pair!(
1132 push_sle,
1133 push_sle_local,
1134 push_sge,
1135 push_sge_local,
1136 Binop::Int(IntBinop::SLessEqual)
1137 );
1138 cmp_pair!(
1139 push_lt,
1140 push_lt_local,
1141 push_gt,
1142 push_gt_local,
1143 Binop::Int(IntBinop::Less)
1144 );
1145 cmp_pair!(
1146 push_le,
1147 push_le_local,
1148 push_ge,
1149 push_ge_local,
1150 Binop::Int(IntBinop::LessEqual)
1151 );
1152
1153 cmp_pair!(
1156 push_flt,
1157 push_flt_local,
1158 push_fgt,
1159 push_fgt_local,
1160 Binop::Float(FloatBinop::Less)
1161 );
1162 cmp_pair!(
1163 push_fle,
1164 push_fle_local,
1165 push_fge,
1166 push_fge_local,
1167 Binop::Float(FloatBinop::LessEqual)
1168 );
1169
1170 binop_leaf!(push_eq, push_eq_local, Binop::Int(IntBinop::Equal), Some(1));
1173 binop_leaf!(
1174 push_ne,
1175 push_ne_local,
1176 Binop::Int(IntBinop::NotEqual),
1177 Some(1)
1178 );
1179 binop_leaf!(
1180 push_feq,
1181 push_feq_local,
1182 Binop::Float(FloatBinop::Equal),
1183 Some(1)
1184 );
1185 binop_leaf!(
1186 push_fne,
1187 push_fne_local,
1188 Binop::Float(FloatBinop::NotEqual),
1189 Some(1)
1190 );
1191
1192 pub fn push_bool_xor(
1197 &mut self,
1198 lhs: ValueId,
1199 rhs: ValueId,
1200 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1201 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1202 let local = self.push_bool_xor_local(lhs, rhs);
1203 self.insn_ref(local)
1204 }
1205
1206 pub fn push_bool_xor_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1208 debug_assert!(
1209 self.both_bool(lhs, rhs),
1210 "push_bool_xor: operands must be bool"
1211 );
1212 self.push_binop_local(Binop::Int(IntBinop::Xor), lhs, rhs, None)
1213 }
1214
1215 pub fn push_bool_and(
1217 &mut self,
1218 lhs: ValueId,
1219 rhs: ValueId,
1220 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1221 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1222 let local = self.push_bool_and_local(lhs, rhs);
1223 self.insn_ref(local)
1224 }
1225
1226 pub fn push_bool_and_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1228 debug_assert!(
1229 self.both_bool(lhs, rhs),
1230 "push_bool_and: operands must be bool"
1231 );
1232 self.push_binop_local(Binop::Int(IntBinop::And), lhs, rhs, None)
1233 }
1234
1235 pub fn push_bool_or(
1237 &mut self,
1238 lhs: ValueId,
1239 rhs: ValueId,
1240 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1241 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1242 let local = self.push_bool_or_local(lhs, rhs);
1243 self.insn_ref(local)
1244 }
1245
1246 pub fn push_bool_or_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1248 debug_assert!(
1249 self.both_bool(lhs, rhs),
1250 "push_bool_or: operands must be bool"
1251 );
1252 self.push_binop_local(Binop::Int(IntBinop::Or), lhs, rhs, None)
1253 }
1254
1255 fn both_bool(&self, lhs: LocalValueId, rhs: LocalValueId) -> bool {
1257 let is_bool = |v: LocalValueId| {
1258 self.lstored_type_of(v)
1259 .is_some_and(|t| self.shr().types.is_bool(t))
1260 };
1261 is_bool(lhs) && is_bool(rhs)
1262 }
1263
1264 binop_leaf!(
1265 push_bit_xor,
1266 push_bit_xor_local,
1267 Binop::Int(IntBinop::Xor),
1268 None
1269 );
1270 binop_leaf!(
1271 push_bit_or,
1272 push_bit_or_local,
1273 Binop::Int(IntBinop::Or),
1274 None
1275 );
1276 binop_leaf!(
1277 push_bit_and,
1278 push_bit_and_local,
1279 Binop::Int(IntBinop::And),
1280 None
1281 );
1282
1283 pub fn push_is_nan(&mut self, src: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1286 let src = self.loc(src);
1287 let local = self.push_is_nan_local(src);
1288 self.insn_ref(local)
1289 }
1290
1291 pub fn push_is_nan_local(&mut self, src: LocalValueId) -> LocalInsnId {
1293 assert!(
1294 !matches!(src, LocalValueId::Varnode(_)),
1295 "push_is_nan: varnode operand not allowed"
1296 );
1297 self.store_insn(Mnemonic::IsFloatNaN(IsFloatNaN { src }), 1)
1298 }
1299
1300 unop_leaf!(push_abs, push_abs_local, Unop::FloatAbs);
1301 unop_leaf!(push_sqrt, push_sqrt_local, Unop::FloatSqrt);
1302 unop_leaf!(push_floor, push_floor_local, Unop::FloatFloor);
1303 unop_leaf!(push_ceil, push_ceil_local, Unop::FloatCeil);
1304 unop_leaf!(push_round, push_round_local, Unop::FloatRound);
1305
1306 conv_leaf!(
1307 push_int_to_float,
1308 push_int_to_float_local,
1309 "push_int_to_float: varnode operand not allowed",
1310 IntToFloat
1311 );
1312 conv_leaf!(
1313 push_float_to_float,
1314 push_float_to_float_local,
1315 "push_float_to_float: varnode operand not allowed",
1316 FloatToFloat
1317 );
1318 conv_leaf!(
1319 push_trunc,
1320 push_trunc_local,
1321 "push_trunc: varnode operand not allowed",
1322 FloatToInt
1323 );
1324 conv_leaf!(
1325 push_zext,
1326 push_zext_local,
1327 "push_zext: varnode operand not allowed",
1328 Zext
1329 );
1330 conv_leaf!(
1331 push_sext,
1332 push_sext_local,
1333 "push_sext: varnode operand not allowed",
1334 Sext
1335 );
1336
1337 pub fn push_tuple(
1342 &mut self,
1343 fields: Vec<ValueId>,
1344 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1345 let fields = self.loc_vec(fields);
1346 let local = self.push_tuple_local(fields);
1347 self.insn_ref(local)
1348 }
1349
1350 pub fn push_tuple_local(&mut self, fields: Vec<LocalValueId>) -> LocalInsnId {
1352 let named_fields = fields
1353 .into_iter()
1354 .enumerate()
1355 .map(|(i, value)| (format!("field{}", i + 1), value))
1356 .collect();
1357 self.push_named_tuple_local(named_fields)
1358 }
1359
1360 pub fn push_named_tuple(
1362 &mut self,
1363 fields: Vec<(String, ValueId)>,
1364 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1365 let fields = fields
1366 .into_iter()
1367 .map(|(name, v)| (name, self.loc(v)))
1368 .collect();
1369 let local = self.push_named_tuple_local(fields);
1370 self.insn_ref(local)
1371 }
1372
1373 pub fn push_named_tuple_local(&mut self, fields: Vec<(String, LocalValueId)>) -> LocalInsnId {
1375 let field_types: Vec<TypeId> = fields.iter().map(|(_, f)| self.ltype_of(*f)).collect();
1376 let aggregate_fields = fields
1377 .iter()
1378 .zip(field_types)
1379 .map(|((name, _), type_id)| AggregateField::new(name.clone(), type_id))
1380 .collect();
1381 let ty = self
1382 .shr()
1383 .types
1384 .get_or_make_named_aggregate(aggregate_fields);
1385 self.push_named_tuple_local_with_type(fields, ty)
1386 }
1387
1388 pub fn push_named_tuple_with_type(
1392 &mut self,
1393 fields: Vec<(String, ValueId)>,
1394 ty: TypeId,
1395 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1396 let fields = fields
1397 .into_iter()
1398 .map(|(name, value)| (name, self.loc(value)))
1399 .collect();
1400 let local = self.push_named_tuple_local_with_type(fields, ty);
1401 self.insn_ref(local)
1402 }
1403
1404 pub fn push_named_tuple_local_with_type(
1406 &mut self,
1407 fields: Vec<(String, LocalValueId)>,
1408 ty: TypeId,
1409 ) -> LocalInsnId {
1410 debug_assert_eq!(
1411 self.shr().types.aggregate_fields(ty).map(<[_]>::len),
1412 Some(fields.len()),
1413 "explicit tuple type must declare every tuple field"
1414 );
1415 debug_assert!(fields.iter().enumerate().all(|(index, (name, value))| {
1416 self.shr()
1417 .types
1418 .aggregate_fields(ty)
1419 .and_then(|declared| declared.get(index))
1420 .is_some_and(|declared| {
1421 declared.name == *name && declared.type_id == self.ltype_of(*value)
1422 })
1423 }));
1424 let values = fields.into_iter().map(|(_, value)| value).collect();
1425 self.store_insn_with_type(Mnemonic::Tuple(Tuple { fields: values }), ty)
1426 }
1427
1428 pub fn push_extract(
1431 &mut self,
1432 agg: ValueId,
1433 index: usize,
1434 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1435 let agg = self.loc(agg);
1436 let local = self.push_extract_local(agg, index);
1437 self.insn_ref(local)
1438 }
1439
1440 pub fn push_extract_local(&mut self, agg: LocalValueId, index: usize) -> LocalInsnId {
1442 let agg_ty = self.ltype_of(agg);
1443 let ty = self
1444 .shr()
1445 .types
1446 .field_type(agg_ty, index)
1447 .expect("push_extract: agg is not an aggregate with that field index");
1448 self.store_insn_with_type(Mnemonic::Extract(Extract { agg, index }), ty)
1449 }
1450
1451 pub fn push_map(
1464 &mut self,
1465 body: impl Into<Callee>,
1466 src: ValueId,
1467 captures: Vec<ValueId>,
1468 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1469 let (src, captures) = (self.loc(src), self.loc_vec(captures));
1470 let local = self.push_map_local(body, src, captures);
1471 self.insn_ref(local)
1472 }
1473
1474 pub fn push_map_local(
1476 &mut self,
1477 body: impl Into<Callee>,
1478 src: LocalValueId,
1479 captures: Vec<LocalValueId>,
1480 ) -> LocalInsnId {
1481 let body = body.into();
1482 let src_ty = self.ltype_of(src);
1483 let seq = self.shr().types.seq_of(src_ty);
1486 let ret_ty = body.real().and_then(|body| self.map_body_return_type(body));
1487 let ty = match (seq, ret_ty) {
1488 (Some((_, len, is_list)), Some(rt)) => {
1489 self.shr().types.get_or_make_seq(rt, len, is_list)
1490 }
1491 _ => src_ty,
1492 };
1493 self.push_map_typed_local(body, src, captures, ty)
1494 }
1495
1496 pub fn push_map_typed(
1500 &mut self,
1501 body: impl Into<Callee>,
1502 src: ValueId,
1503 captures: Vec<ValueId>,
1504 result_type: TypeId,
1505 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1506 let (src, captures) = (self.loc(src), self.loc_vec(captures));
1507 let local = self.push_map_typed_local(body, src, captures, result_type);
1508 self.insn_ref(local)
1509 }
1510
1511 pub fn push_map_typed_local(
1513 &mut self,
1514 body: impl Into<Callee>,
1515 src: LocalValueId,
1516 captures: Vec<LocalValueId>,
1517 result_type: TypeId,
1518 ) -> LocalInsnId {
1519 self.store_insn_with_type(
1520 Mnemonic::Map(Map {
1521 body: body.into(),
1522 src,
1523 captures,
1524 }),
1525 result_type,
1526 )
1527 }
1528
1529 pub fn push_scan(
1541 &mut self,
1542 body: impl Into<Callee>,
1543 init: ValueId,
1544 src: ValueId,
1545 captures: Vec<ValueId>,
1546 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1547 let (init, src, captures) = (self.loc(init), self.loc(src), self.loc_vec(captures));
1548 let local = self.push_scan_local(body, init, src, captures);
1549 self.insn_ref(local)
1550 }
1551
1552 pub fn push_scan_local(
1554 &mut self,
1555 body: impl Into<Callee>,
1556 init: LocalValueId,
1557 src: LocalValueId,
1558 captures: Vec<LocalValueId>,
1559 ) -> LocalInsnId {
1560 let body = body.into();
1561 let src_ty = self.ltype_of(src);
1562 let seq = self.shr().types.seq_of(src_ty);
1565 let ret_ty = body.real().and_then(|body| self.map_body_return_type(body));
1566 let ty = match (seq, ret_ty) {
1567 (Some((_, len, is_list)), Some(rt)) => {
1568 self.shr().types.get_or_make_seq(rt, len, is_list)
1569 }
1570 _ => src_ty,
1571 };
1572 self.push_scan_typed_local(body, init, src, captures, ty)
1573 }
1574
1575 pub fn push_scan_typed(
1578 &mut self,
1579 body: impl Into<Callee>,
1580 init: ValueId,
1581 src: ValueId,
1582 captures: Vec<ValueId>,
1583 result_type: TypeId,
1584 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1585 let (init, src, captures) = (self.loc(init), self.loc(src), self.loc_vec(captures));
1586 let local = self.push_scan_typed_local(body, init, src, captures, result_type);
1587 self.insn_ref(local)
1588 }
1589
1590 pub fn push_scan_typed_local(
1592 &mut self,
1593 body: impl Into<Callee>,
1594 init: LocalValueId,
1595 src: LocalValueId,
1596 captures: Vec<LocalValueId>,
1597 result_type: TypeId,
1598 ) -> LocalInsnId {
1599 self.store_insn_with_type(
1600 Mnemonic::Scan(Scan {
1601 body: body.into(),
1602 init,
1603 src,
1604 captures,
1605 }),
1606 result_type,
1607 )
1608 }
1609
1610 pub fn push_apply(
1614 &mut self,
1615 target: impl Into<Callee>,
1616 args: Vec<ValueId>,
1617 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1618 let args = self.loc_vec(args);
1619 let local = self.push_apply_local(target, args);
1620 self.insn_ref(local)
1621 }
1622
1623 pub fn push_apply_local(
1625 &mut self,
1626 target: impl Into<Callee>,
1627 args: Vec<LocalValueId>,
1628 ) -> LocalInsnId {
1629 let target = target.into();
1630 let ty = target
1631 .real()
1632 .and_then(|target| self.lambda_return_type(target))
1633 .unwrap_or_else(|| {
1634 args.first()
1635 .map(|&arg| self.ltype_of(arg))
1636 .unwrap_or_else(|| self.shr().types.get_or_make_int(0))
1637 });
1638 self.store_insn_with_type(Mnemonic::Apply(Apply { target, args }), ty)
1639 }
1640
1641 fn map_body_return_type(&self, body: FunctionId) -> Option<TypeId> {
1645 if self.body.try_id() != Some(body) {
1646 return None;
1647 }
1648 let root = self.body.root_id()?;
1649 self.body.blocks[root].instructions.iter().find_map(|&i| {
1650 match self.body.insns[i].mnemonic() {
1651 Mnemonic::Return(r) => r.value.and_then(|v| self.lstored_type_of(v)),
1652 _ => None,
1653 }
1654 })
1655 }
1656
1657 fn lambda_return_type(&self, body: FunctionId) -> Option<TypeId> {
1659 if self.body.try_id() != Some(body) {
1660 return None;
1661 }
1662 self.body
1663 .roster
1664 .iter()
1665 .flat_map(|&b| self.body.blocks[b].instructions.iter().copied())
1666 .find_map(|i| match self.body.insns[i].mnemonic() {
1667 Mnemonic::ReturnValue(r) => self.lstored_type_of(r.value),
1668 _ => None,
1669 })
1670 }
1671
1672 pub fn push_gep(
1678 &mut self,
1679 base: ValueId,
1680 offset: usize,
1681 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1682 let base = self.loc(base);
1683 let local = self.push_gep_local(base, offset);
1684 self.insn_ref(local)
1685 }
1686
1687 pub fn push_gep_local(&mut self, base: LocalValueId, offset: usize) -> LocalInsnId {
1689 let base_ty = self.ltype_of(base);
1690 let types = &self.shr().types;
1691 let ptr_width = types.size_of(base_ty);
1692 let pointee = types
1693 .pointee_of(base_ty)
1694 .expect("push_gep: base is not a struct pointer");
1695 let field_ty = types
1696 .field_by_offset(pointee, offset)
1697 .map(|(_, field)| field.type_id)
1698 .expect("push_gep: no field at that offset in the pointee struct");
1699 let ty = self
1700 .shr()
1701 .types
1702 .get_or_make_struct_pointer(ptr_width, field_ty);
1703 self.store_insn_with_type(Mnemonic::Gep(Gep { base, offset }), ty)
1704 }
1705
1706 pub fn push_gep_field(
1710 &mut self,
1711 base: ValueId,
1712 name: &str,
1713 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1714 let base = self.loc(base);
1715 let local = self.push_gep_field_local(base, name);
1716 self.insn_ref(local)
1717 }
1718
1719 pub fn push_gep_field_local(&mut self, base: LocalValueId, name: &str) -> LocalInsnId {
1721 let base_ty = self.ltype_of(base);
1722 let types = &self.shr().types;
1723 let pointee = types
1724 .pointee_of(base_ty)
1725 .expect("push_gep_field: base is not a struct pointer");
1726 let offset = types
1727 .aggregate_fields(pointee)
1728 .and_then(|fields| fields.iter().find(|f| f.name == name))
1729 .map(|f| f.offset)
1730 .expect("push_gep_field: pointee struct has no field of that name");
1731 self.push_gep_local(base, offset)
1732 }
1733
1734 pub fn push_popcount(
1735 &mut self,
1736 src: ValueId,
1737 size: usize,
1738 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1739 let src = self.loc(src);
1740 let local = self.push_popcount_local(src, size);
1741 self.insn_ref(local)
1742 }
1743
1744 pub fn push_popcount_local(&mut self, src: LocalValueId, size: usize) -> LocalInsnId {
1746 assert!(
1747 !matches!(src, LocalValueId::Varnode(_)),
1748 "push_popcount: varnode operand not allowed"
1749 );
1750 self.store_insn(Mnemonic::PopCount(PopCount { src }), size)
1751 }
1752
1753 pub fn push_lzcount(
1754 &mut self,
1755 src: ValueId,
1756 size: usize,
1757 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1758 let src = self.loc(src);
1759 let local = self.push_lzcount_local(src, size);
1760 self.insn_ref(local)
1761 }
1762
1763 pub fn push_lzcount_local(&mut self, src: LocalValueId, size: usize) -> LocalInsnId {
1765 assert!(
1766 !matches!(src, LocalValueId::Varnode(_)),
1767 "push_lzcount: varnode operand not allowed"
1768 );
1769 self.store_insn(Mnemonic::LzCount(LzCount { src }), size)
1770 }
1771
1772 pub fn push_carry(
1773 &mut self,
1774 lhs: ValueId,
1775 rhs: ValueId,
1776 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1777 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1778 let local = self.push_carry_local(lhs, rhs);
1779 self.insn_ref(local)
1780 }
1781
1782 pub fn push_carry_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1784 assert!(
1785 !matches!(lhs, LocalValueId::Varnode(_)) && !matches!(rhs, LocalValueId::Varnode(_)),
1786 "push_carry: varnode operand not allowed"
1787 );
1788 self.store_insn(Mnemonic::Carry(Carry { lhs, rhs }), 1)
1789 }
1790
1791 pub fn push_scarry(
1792 &mut self,
1793 lhs: ValueId,
1794 rhs: ValueId,
1795 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1796 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1797 let local = self.push_scarry_local(lhs, rhs);
1798 self.insn_ref(local)
1799 }
1800
1801 pub fn push_scarry_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1803 assert!(
1804 !matches!(lhs, LocalValueId::Varnode(_)) && !matches!(rhs, LocalValueId::Varnode(_)),
1805 "push_scarry: varnode operand not allowed"
1806 );
1807 self.store_insn(Mnemonic::SCarry(SCarry { lhs, rhs }), 1)
1808 }
1809
1810 pub fn push_sborrow(
1811 &mut self,
1812 lhs: ValueId,
1813 rhs: ValueId,
1814 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1815 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1816 let local = self.push_sborrow_local(lhs, rhs);
1817 self.insn_ref(local)
1818 }
1819
1820 pub fn push_sborrow_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1822 assert!(
1823 !matches!(lhs, LocalValueId::Varnode(_)) && !matches!(rhs, LocalValueId::Varnode(_)),
1824 "push_sborrow: varnode operand not allowed"
1825 );
1826 self.store_insn(Mnemonic::SBorrow(SBorrow { lhs, rhs }), 1)
1827 }
1828
1829 pub fn push_pcode_op(
1830 &mut self,
1831 id: PCodeOpId,
1832 args: Vec<ValueId>,
1833 dst: Option<ValueId>,
1834 size: usize,
1835 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1836 let args = self.loc_vec(args);
1837 let dst = dst.map(|d| self.loc(d));
1838 let local = self.push_pcode_op_local(id, args, dst, size);
1839 self.insn_ref(local)
1840 }
1841
1842 pub fn push_pcode_op_local(
1844 &mut self,
1845 id: PCodeOpId,
1846 args: Vec<LocalValueId>,
1847 dst: Option<LocalValueId>,
1848 size: usize,
1849 ) -> LocalInsnId {
1850 let args = args
1851 .into_iter()
1852 .map(|arg| self.ensure_local_local(arg))
1853 .collect::<Vec<_>>();
1854
1855 self.store_insn(Mnemonic::PCodeOp(PCodeOp { id, args, dst }), size)
1856 }
1857
1858 #[track_caller]
1866 pub fn push_intrinsic(
1867 &mut self,
1868 id: IntrinsicId,
1869 args: Vec<ValueId>,
1870 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1871 let args = self.loc_vec(args);
1872 let local = self.push_intrinsic_local(id, args);
1873 self.insn_ref(local)
1874 }
1875
1876 #[track_caller]
1878 pub fn push_intrinsic_local(
1879 &mut self,
1880 id: IntrinsicId,
1881 args: Vec<LocalValueId>,
1882 ) -> LocalInsnId {
1883 let desc = id.desc();
1884 assert_eq!(
1885 args.len(),
1886 desc.arity(),
1887 "intrinsic `{}` expects {} args, got {}",
1888 desc.name(),
1889 desc.arity(),
1890 args.len()
1891 );
1892
1893 let args = args
1894 .into_iter()
1895 .map(|arg| self.ensure_local_local(arg))
1896 .collect::<Vec<_>>();
1897
1898 let arg_types = args
1899 .iter()
1900 .map(|&arg| self.ltype_of(arg))
1901 .collect::<Vec<_>>();
1902 let type_id = desc.result_type(&self.shr().types, &arg_types);
1903
1904 self.store_insn_with_type(Mnemonic::Intrinsic(IntrinsicApp { id, args }), type_id)
1905 }
1906
1907 pub fn push_copy(
1915 &mut self,
1916 src: ValueId,
1917 dst: impl Into<ValueId>,
1918 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1919 let src = self.loc(src);
1920 let dst = self.loc(dst.into());
1921 let local = self.push_copy_local(src, dst);
1922 self.insn_ref(local)
1923 }
1924
1925 pub fn push_copy_local(&mut self, src: LocalValueId, dst: LocalValueId) -> LocalInsnId {
1927 let (size, space, name) = match dst {
1928 LocalValueId::Varnode(vid) => {
1929 let node = Varnode::from_id(self.shr(), vid);
1930 (
1931 node.size(),
1932 LocalMemorySpaceId::Shared(node.space().id),
1933 node.name().map(str::to_owned),
1934 )
1935 }
1936 LocalValueId::Temp(tlocal) => {
1937 let temp = &self.body.temps[tlocal];
1938 (
1939 temp.size,
1940 LocalMemorySpaceId::Temp(temp.space),
1941 temp.name.as_deref().map(str::to_owned),
1942 )
1943 }
1944 _ => panic!("copy destination must be a varnode or body-local temporary"),
1945 };
1946
1947 const LANE_SIZE: usize = 8;
1948
1949 if size > LANE_SIZE {
1950 let num_lanes = size.div_ceil(LANE_SIZE);
1951 let mut first_id = None;
1952
1953 for lane in 0..num_lanes {
1954 let offset = lane * LANE_SIZE;
1955 let lane_size = cmp::min(LANE_SIZE, size - offset);
1956
1957 let src_lane = self
1958 .get_range_local(src, offset..offset + lane_size)
1959 .expect("lane range in bounds");
1960 let src_lane = self.ensure_local_local(src_lane);
1961
1962 let dst_lane = self
1963 .get_range_local(dst, offset..offset + lane_size)
1964 .expect("lane range in bounds");
1965
1966 let id = self.store_insn(
1967 Mnemonic::Store(Store {
1968 src: src_lane,
1969 ptr: dst_lane,
1970 space,
1971 size: lane_size,
1972 }),
1973 0,
1974 );
1975
1976 if let Some(name) = &name {
1977 let name = Cow::Owned(format!("{}_lane{lane}", name.to_lowercase()));
1978 let _ = self.rename_insn_local(id, name);
1979 }
1980
1981 first_id.get_or_insert(id);
1982 }
1983
1984 first_id.unwrap()
1985 } else {
1986 let src = self.ensure_local_local(src);
1987 let id = self.store_insn(
1989 Mnemonic::Store(Store {
1990 src,
1991 ptr: dst,
1992 space,
1993 size,
1994 }),
1995 0,
1996 );
1997
1998 if let Some(name) = &name {
2000 let lowered = name.to_lowercase();
2001 let name = self.body.names.unique(Cow::Owned(lowered));
2002 self.rename_insn_local(id, name)
2003 .expect("This name was deduplicated");
2004 }
2005
2006 id
2007 }
2008 }
2009
2010 #[track_caller]
2011 pub fn push_store(
2012 &mut self,
2013 src: ValueId,
2014 ptr: ValueId,
2015 space: impl Into<LocalMemorySpaceId>,
2016 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2017 let (src, ptr) = (self.loc(src), self.loc(ptr));
2018 let local = self.push_store_local(src, ptr, space);
2019 self.insn_ref(local)
2020 }
2021
2022 #[track_caller]
2024 pub fn push_store_local(
2025 &mut self,
2026 src: LocalValueId,
2027 ptr: LocalValueId,
2028 space: impl Into<LocalMemorySpaceId>,
2029 ) -> LocalInsnId {
2030 let space = space.into();
2031 let src = self.ensure_local_local(src);
2032 let size = self.lsize_of(src);
2033
2034 match ptr {
2035 LocalValueId::Varnode(id) => {
2036 let varnode = Varnode::from_id(self.shr(), id);
2037 if varnode.space().id != space {
2038 panic!(
2039 "push_store: ptr is a varnode but its space {:?} does not match the store space {:?}; \
2040 call ensure_local on the ptr first",
2041 varnode.space().id,
2042 space
2043 );
2044 }
2045 }
2046
2047 LocalValueId::Instruction(local) => {
2048 self.set_insn_space_local(local, space);
2049 }
2050
2051 _ => {}
2052 }
2053
2054 self.store_insn(
2055 Mnemonic::Store(Store {
2056 src,
2057 ptr,
2058 space,
2059 size,
2060 }),
2061 0,
2062 )
2063 }
2064
2065 pub fn push_param(&mut self, size: usize) -> BlockParamId {
2069 let local = self.push_param_local(size);
2070 crate::value::block_param::BlockParamId::new(self.func(), local)
2071 }
2072
2073 pub fn push_param_local(&mut self, size: usize) -> crate::value::LocalParamId {
2075 let block = self.block;
2076 let index = self.body.blocks[block].params.len();
2077 let type_id = self.shared.types.get_or_make_int(size);
2078 let local = self.body.params.push(BlockParam {
2079 index,
2080 type_id,
2081 parent: Some(block),
2082 name: None,
2083 origin: None,
2084 });
2085 self.body.blocks[block].params.push(local);
2086 local
2087 }
2088
2089 pub fn finalize(mut self, target: BlockId) {
2092 self.finalize_local(target.local)
2093 }
2094
2095 pub fn finalize_local(&mut self, target: LocalBlockId) {
2097 if !self.is_terminated() {
2098 let branch = self.push_branch_local(target);
2099 if let Some(address) = self.address {
2100 self.body.insns[branch].set_address(address);
2101 }
2102 }
2103 }
2104
2105 fn add_cfg_edge_local(&mut self, from: LocalBlockId, to: LocalBlockId) {
2108 let edge_id = self
2109 .body
2110 .edges
2111 .push(crate::value::block::cfg::EdgeData { from, to });
2112 self.body.blocks[from].edges.insert(edge_id);
2113 self.body.blocks[to].edges.insert(edge_id);
2114 }
2115
2116 pub fn push_branch(&mut self, target: BlockId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2119 let local = self.push_branch_local(target.local);
2120 self.insn_ref(local)
2121 }
2122
2123 pub fn push_branch_local(&mut self, target: LocalBlockId) -> LocalInsnId {
2125 self.push_branch_with_args_local(target, vec![])
2126 }
2127
2128 pub fn push_branch_with_args(
2130 &mut self,
2131 target: BlockId,
2132 args: Vec<ValueId>,
2133 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2134 let args = self.loc_vec(args);
2135 let local = self.push_branch_with_args_local(target.local, args);
2136 self.insn_ref(local)
2137 }
2138
2139 pub fn push_branch_with_args_local(
2141 &mut self,
2142 target: LocalBlockId,
2143 args: Vec<LocalValueId>,
2144 ) -> LocalInsnId {
2145 let current = self.block;
2146 self.add_cfg_edge_local(current, target);
2147 let id = self.store_insn(Mnemonic::Branch(Branch { target, args }), 0);
2148 self.is_terminated = true;
2149 id
2150 }
2151
2152 pub fn push_cbranch(
2153 &mut self,
2154 condition: ValueId,
2155 target: BlockId,
2156 fallthrough: BlockId,
2157 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2158 self.push_cbranch_with_args(condition, target, vec![], fallthrough, vec![])
2159 }
2160
2161 pub fn push_cbranch_local(
2163 &mut self,
2164 condition: LocalValueId,
2165 target: LocalBlockId,
2166 fallthrough: LocalBlockId,
2167 ) -> LocalInsnId {
2168 self.push_cbranch_with_args_local(condition, target, vec![], fallthrough, vec![])
2169 }
2170
2171 pub fn push_cbranch_with_args(
2173 &mut self,
2174 condition: ValueId,
2175 target: BlockId,
2176 target_args: Vec<ValueId>,
2177 fallthrough: BlockId,
2178 fallthrough_args: Vec<ValueId>,
2179 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2180 let condition = self.loc(condition);
2181 let target_args = self.loc_vec(target_args);
2182 let fallthrough_args = self.loc_vec(fallthrough_args);
2183 let local = self.push_cbranch_with_args_local(
2184 condition,
2185 target.local,
2186 target_args,
2187 fallthrough.local,
2188 fallthrough_args,
2189 );
2190 self.insn_ref(local)
2191 }
2192
2193 pub fn push_cbranch_with_args_local(
2196 &mut self,
2197 condition: LocalValueId,
2198 target: LocalBlockId,
2199 target_args: Vec<LocalValueId>,
2200 fallthrough: LocalBlockId,
2201 fallthrough_args: Vec<LocalValueId>,
2202 ) -> LocalInsnId {
2203 assert!(
2204 !matches!(condition, LocalValueId::Varnode(_)),
2205 "push_cbranch: varnode condition not allowed; load the value first"
2206 );
2207 let current = self.block;
2208 self.add_cfg_edge_local(current, target);
2209 self.add_cfg_edge_local(current, fallthrough);
2210 let id = self.store_insn(
2211 Mnemonic::CBranch(CBranch {
2212 success_block: target,
2213 success_args: target_args,
2214 condition,
2215 failure_block: fallthrough,
2216 failure_args: fallthrough_args,
2217 }),
2218 0,
2219 );
2220 self.is_terminated = true;
2221 id
2222 }
2223
2224 pub fn push_switch(
2227 &mut self,
2228 scrutinee: ValueId,
2229 cases: Vec<(u64, BlockId, Vec<ValueId>)>,
2230 default: Option<(BlockId, Vec<ValueId>)>,
2231 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2232 let scrutinee = self.loc(scrutinee);
2233 let cases = cases
2234 .into_iter()
2235 .map(|(value, target, args)| (value, target.local, self.loc_vec(args)))
2236 .collect();
2237 let default = default.map(|(target, args)| (target.local, self.loc_vec(args)));
2238 let local = self.push_switch_local(scrutinee, cases, default);
2239 self.insn_ref(local)
2240 }
2241
2242 pub fn push_switch_local(
2244 &mut self,
2245 scrutinee: LocalValueId,
2246 cases: Vec<(u64, LocalBlockId, Vec<LocalValueId>)>,
2247 default: Option<(LocalBlockId, Vec<LocalValueId>)>,
2248 ) -> LocalInsnId {
2249 assert!(
2250 !matches!(scrutinee, LocalValueId::Varnode(_)),
2251 "push_switch: varnode scrutinee not allowed; load the value first"
2252 );
2253 let current = self.block;
2254 for &(_, target, _) in &cases {
2255 self.add_cfg_edge_local(current, target);
2256 }
2257 if let Some((target, _)) = &default {
2258 self.add_cfg_edge_local(current, *target);
2259 }
2260 let (default_block, default_args) = match default {
2261 Some((target, args)) => (Some(target), args),
2262 None => (None, Vec::new()),
2263 };
2264 let id = self.store_insn(
2265 Mnemonic::Switch(Switch {
2266 scrutinee,
2267 cases: cases
2268 .into_iter()
2269 .map(|(value, target, args)| SwitchArm {
2270 value,
2271 target,
2272 args,
2273 })
2274 .collect(),
2275 default: default_block,
2276 default_args,
2277 }),
2278 0,
2279 );
2280 self.is_terminated = true;
2281 id
2282 }
2283
2284 pub fn push_branchind(&mut self, ptr: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2285 let ptr = self.loc(ptr);
2286 let local = self.push_branchind_local(ptr);
2287 self.insn_ref(local)
2288 }
2289
2290 pub fn push_branchind_local(&mut self, ptr: LocalValueId) -> LocalInsnId {
2292 let id = self.store_insn(Mnemonic::BranchInd(BranchInd { ptr }), 0);
2293 self.is_terminated = true;
2294 id
2295 }
2296
2297 pub fn push_call(
2298 &mut self,
2299 target: impl Into<Callee>,
2300 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2301 self.push_call_with_args(target, vec![])
2302 }
2303
2304 pub fn push_call_with_args(
2305 &mut self,
2306 target: impl Into<Callee>,
2307 args: Vec<ValueId>,
2308 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2309 let args = self.loc_vec(args);
2310 let local = self.push_call_with_args_local(target, args);
2311 self.insn_ref(local)
2312 }
2313
2314 pub fn push_call_local(&mut self, target: impl Into<Callee>) -> LocalInsnId {
2316 self.push_call_with_args_local(target, vec![])
2317 }
2318
2319 pub fn push_call_with_args_local(
2321 &mut self,
2322 target: impl Into<Callee>,
2323 args: Vec<LocalValueId>,
2324 ) -> LocalInsnId {
2325 let target = target.into();
2326 let id = self.store_insn(
2327 Mnemonic::Call(Call {
2328 target,
2329 args,
2330 clobbers: vec![],
2331 tag: Default::default(),
2332 }),
2333 0,
2334 );
2335 self.is_terminated = true;
2336 id
2337 }
2338
2339 pub fn push_tail_call(
2344 &mut self,
2345 target: impl Into<Callee>,
2346 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2347 self.push_tail_call_with_args(target, vec![])
2348 }
2349
2350 pub fn push_tail_call_with_args(
2351 &mut self,
2352 target: impl Into<Callee>,
2353 args: Vec<ValueId>,
2354 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2355 let args = self.loc_vec(args);
2356 let local = self.push_tail_call_with_args_local(target, args);
2357 self.insn_ref(local)
2358 }
2359
2360 pub fn push_tail_call_local(&mut self, target: impl Into<Callee>) -> LocalInsnId {
2362 self.push_tail_call_with_args_local(target, vec![])
2363 }
2364
2365 pub fn push_tail_call_with_args_local(
2368 &mut self,
2369 target: impl Into<Callee>,
2370 args: Vec<LocalValueId>,
2371 ) -> LocalInsnId {
2372 let target = target.into();
2373 let id = self.store_insn(Mnemonic::TailCall(TailCall { target, args }), 0);
2374 self.is_terminated = true;
2375 id
2376 }
2377
2378 pub fn push_call_ind(&mut self, ptr: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2379 self.push_call_ind_with_args(ptr, vec![])
2380 }
2381
2382 pub fn push_call_ind_with_args(
2383 &mut self,
2384 ptr: ValueId,
2385 args: Vec<ValueId>,
2386 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2387 let (ptr, args) = (self.loc(ptr), self.loc_vec(args));
2388 let local = self.push_call_ind_with_args_local(ptr, args);
2389 self.insn_ref(local)
2390 }
2391
2392 pub fn push_call_ind_local(&mut self, ptr: LocalValueId) -> LocalInsnId {
2394 self.push_call_ind_with_args_local(ptr, vec![])
2395 }
2396
2397 pub fn push_call_ind_with_args_local(
2400 &mut self,
2401 ptr: LocalValueId,
2402 args: Vec<LocalValueId>,
2403 ) -> LocalInsnId {
2404 let id = self.store_insn(Mnemonic::CallInd(CallInd { ptr, args }), 0);
2405 self.is_terminated = true;
2406 id
2407 }
2408
2409 pub fn push_return(&mut self, ptr: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2410 let ptr = self.loc(ptr);
2411 let local = self.push_return_local(ptr);
2412 self.insn_ref(local)
2413 }
2414
2415 pub fn push_return_local(&mut self, ptr: LocalValueId) -> LocalInsnId {
2417 self.push_return_at_local(None, ptr)
2418 }
2419
2420 pub fn push_return_with_value(
2421 &mut self,
2422 value: ValueId,
2423 ptr: ValueId,
2424 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2425 let (value, ptr) = (self.loc(value), self.loc(ptr));
2426 let local = self.push_return_at_local(Some(value), ptr);
2427 self.insn_ref(local)
2428 }
2429
2430 pub fn push_return_with_value_local(
2433 &mut self,
2434 value: LocalValueId,
2435 ptr: LocalValueId,
2436 ) -> LocalInsnId {
2437 self.push_return_at_local(Some(value), ptr)
2438 }
2439
2440 fn push_return_at_local(
2441 &mut self,
2442 value: Option<LocalValueId>,
2443 ptr: LocalValueId,
2444 ) -> LocalInsnId {
2445 let id = self.store_insn(Mnemonic::Return(Return { ptr, value }), 0);
2446 self.is_terminated = true;
2447 id
2448 }
2449
2450 pub fn push_return_value(
2451 &mut self,
2452 value: ValueId,
2453 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2454 let value = self.loc(value);
2455 let local = self.push_return_value_local(value);
2456 self.insn_ref(local)
2457 }
2458
2459 pub fn push_bad_insn(&mut self) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2462 let local = self.push_bad_insn_local();
2463 self.insn_ref(local)
2464 }
2465
2466 pub fn push_bad_insn_local(&mut self) -> LocalInsnId {
2468 let id = self.store_insn(Mnemonic::BadInsn(crate::value::insn::BadInsn), 0);
2469 self.is_terminated = true;
2470 id
2471 }
2472
2473 pub fn push_return_value_local(&mut self, value: LocalValueId) -> LocalInsnId {
2475 let id = self.store_insn(Mnemonic::ReturnValue(ReturnValue { value }), 0);
2476 self.is_terminated = true;
2477 id
2478 }
2479
2480 pub fn push_assert(
2484 &mut self,
2485 condition: ValueId,
2486 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2487 let condition = self.loc(condition);
2488 let local = self.push_assert_local(condition);
2489 self.insn_ref(local)
2490 }
2491
2492 pub fn push_assert_local(&mut self, condition: LocalValueId) -> LocalInsnId {
2494 self.store_insn(Mnemonic::Assert(Assert { condition }), 0)
2495 }
2496}
2497
2498#[cfg(test)]
2499mod tests {
2500 use wazabin_qcode_macro::qcode;
2501
2502 use super::*;
2503 use crate::{context::Context, value::ModuleView};
2504
2505 #[test]
2506 fn checked_builder_matches_module_builder() {
2507 use crate::value::{
2508 FunctionId, FunctionRef, block::BasicBlock, function::FunctionBody,
2509 util::body_mut::BodyMut,
2510 };
2511
2512 fn body<'str>(b: &mut Builder<'str, '_>) {
2517 let c1 = b.shr().get_const(7, 8);
2518 let c2 = b.shr().get_const(9, 8);
2519 let sum = b.push_add(c1, c2).id();
2520 let _doubled = b.push_add(sum, sum).id();
2521 let lbl = b.get_or_make_local_label("next".into());
2522 b.push_branch(lbl);
2523 }
2524
2525 type BSnap = Vec<(String, Vec<String>, Vec<String>)>;
2528 fn snap(ctx: &Context, fid: FunctionId) -> BSnap {
2529 FunctionRef::from_id(ctx, fid)
2530 .blocks()
2531 .map(|blk| {
2532 let name = blk.name().unwrap_or("?").to_string();
2533 let insns: Vec<String> = blk
2534 .instructions()
2535 .map(|i| format!("{:?}", i.mnemonic()))
2536 .collect();
2537 let mut succ: Vec<String> = blk
2538 .successors()
2539 .map(|(_, s)| {
2540 BasicBlock::from_id(ctx, s)
2541 .name()
2542 .unwrap_or("?")
2543 .to_string()
2544 })
2545 .collect();
2546 succ.sort();
2547 (name, insns, succ)
2548 })
2549 .collect()
2550 }
2551
2552 let mut ctx_a = Context::new();
2554 let fid_a = FunctionBody::make(&mut ctx_a, "foo".into()).unwrap().id;
2555 let entry_a = FunctionBody::from_id_mut(&mut ctx_a, fid_a).make_root().id;
2556 {
2557 let mut b = ctx_a.builder(entry_a);
2558 body(&mut b);
2559 }
2560 let snap_a = snap(&ctx_a, fid_a);
2561 assert!(
2562 snap_a.iter().any(|(_, i, _)| !i.is_empty()),
2563 "sanity: built IR"
2564 );
2565
2566 let mut ctx_b = Context::new();
2568 let fid_b = FunctionBody::make(&mut ctx_b, "foo".into()).unwrap().id;
2569 let entry_b = FunctionBody::from_id_mut(&mut ctx_b, fid_b).make_root().id;
2570 {
2571 let mut host = BodyMut::new(&mut ctx_b.bodies[fid_b], &ctx_b.shared, &ctx_b.interfaces);
2572 let mut b = host.builder(entry_b);
2573 body(&mut b);
2574 }
2575 let snap_b = snap(&ctx_b, fid_b);
2576
2577 assert_eq!(
2578 snap_a, snap_b,
2579 "a body built through a checked-out builder must match the module-built body"
2580 );
2581 }
2582
2583 #[test]
2588 fn address_arithmetic_inherits_pointer_space() {
2589 use crate::value::function::FunctionBody;
2590
2591 let mut ctx = Context::new();
2592 let fid = FunctionBody::make(&mut ctx, "f".into()).unwrap().id;
2593 let entry = FunctionBody::from_id_mut(&mut ctx, fid).make_root().id;
2594
2595 let ram = ctx.shared.default_space;
2596 let ptr_ty = ctx.shared.types.get_or_make_space_address(8, ram);
2597 let ram_mem = ctx.shared.types.space_of(ptr_ty);
2598
2599 let (addr_a, addr_b) = {
2601 let mut b = ctx.builder(entry);
2602 let c1 = b.shr().get_const(0x1000, 8);
2603 let c2 = b.shr().get_const(0x2000, 8);
2604 (b.push_add(c1, c1).id(), b.push_add(c2, c2).id())
2605 };
2606 for v in [addr_a, addr_b] {
2607 if let ValueId::Instruction(i) = v {
2608 crate::value::Instruction::from_id_mut(&mut ctx, i).set_type(ptr_ty);
2609 }
2610 }
2611
2612 let (add_ptr_lit, add_lit_ptr, add_ptr_ptr, add_int_lit) = {
2613 let mut b = ctx.builder(entry);
2614 let k = b.shr().get_const(4, 8);
2615 let zero = b.shr().get_const(0, 8);
2616 (
2617 b.push_add(addr_a, k).id(), b.push_add(k, addr_a).id(), b.push_add(addr_a, addr_b).id(), b.push_add(k, zero).id(), )
2622 };
2623
2624 let space_of = |v: ValueId| ctx.shared.types.space_of(ctx.type_of(v));
2625 assert_eq!(
2626 space_of(add_ptr_lit),
2627 ram_mem,
2628 "ptr + literal keeps the pointer's space (rule 1)"
2629 );
2630 assert_eq!(
2631 space_of(add_lit_ptr),
2632 ram_mem,
2633 "literal + ptr keeps the pointer's space (rule 1, commutative)"
2634 );
2635 assert_eq!(
2636 space_of(add_ptr_ptr),
2637 None,
2638 "ptr + ptr is ambiguous and drops to a plain integer (rule 2)"
2639 );
2640 assert_eq!(
2641 space_of(add_int_lit),
2642 None,
2643 "int + literal carries no space"
2644 );
2645
2646 assert_eq!(
2650 space_of(addr_a),
2651 ram_mem,
2652 "the pointer operand a fold would return still carries its space"
2653 );
2654 }
2655
2656 #[test]
2660 fn detached_body_builds_through_local_verbs() {
2661 let ctx = Context::new();
2662 let mut body = FunctionBody::detached();
2663 assert_eq!(body.try_id(), None, "sanity: body starts detached");
2664
2665 let entry = body.push_block_local(BasicBlock::detached());
2667 let target = body.push_block_local(BasicBlock::detached());
2668 body.set_root_id(Some(entry));
2669
2670 let c1 = ctx.shared.get_const(7, 8).strip_func();
2671 let c2 = ctx.shared.get_const(9, 8).strip_func();
2672
2673 {
2674 let mut b = Builder::new_local(&mut body, &ctx.shared, &ctx.interfaces, entry);
2675 let sum = b.push_add_local(c1, c2);
2676 let sum = LocalValueId::Instruction(sum);
2677 let doubled = b.push_add_local(sum, sum);
2678 let _cmp = b.push_eq_local(LocalValueId::Instruction(doubled), c2);
2679 b.push_branch_local(target);
2680 assert!(b.is_terminated());
2681 b.switch_to_block_local(target);
2682 let ret = b.push_return_value_local(sum);
2683 let _ = ret;
2684 }
2685
2686 assert_eq!(body.blocks[entry].instructions.len(), 4);
2688 assert_eq!(body.blocks[target].instructions.len(), 1);
2689 let last = *body.blocks[entry].instructions.last().unwrap();
2690 assert!(body.insns[last].mnemonic().is_terminator());
2691
2692 assert_eq!(body.try_id(), None);
2694 }
2695
2696 #[test]
2699 fn map_over_a_list_yields_a_list() {
2700 use crate::value::{FunctionBody, insn::Return};
2701
2702 let mut ctx = Context::new();
2703 let i8 = ctx.shared.types.get_or_make_int(1);
2704
2705 let body = FunctionBody::make(&mut ctx, "body".into()).unwrap().id;
2707 let broot = FunctionBody::from_id_mut(&mut ctx, body).make_root().id;
2708 let bp = BasicBlock::from_id_mut(&mut ctx, broot).push_param(1).id;
2709 let dummy = ctx.get_const(0, 8).id();
2710 let ret = InstructionRef::from_mnemonic_with_type(
2711 &mut ctx,
2712 body,
2713 Mnemonic::Return(Return {
2714 ptr: dummy.localize(body),
2715 value: Some(ValueId::BlockParam(bp).localize(body)),
2716 }),
2717 i8,
2718 )
2719 .id;
2720 BasicBlock::from_id_mut(&mut ctx, broot).push_insn(ret);
2721
2722 let host = FunctionBody::make(&mut ctx, "host".into()).unwrap().id;
2724 let hentry = FunctionBody::from_id_mut(&mut ctx, host).make_root().id;
2725 let list_ty = ctx.shared.types.get_or_make_list(i8, 4);
2726 let src_pid = BasicBlock::from_id_mut(&mut ctx, hentry).push_param(4).id;
2727 ctx.block_param_mut(src_pid).type_id = list_ty;
2728 let src = ValueId::BlockParam(src_pid);
2729
2730 let map_ty = {
2731 let mut b = ctx.builder(hentry);
2732 b.push_map(body, src, Vec::new()).type_id()
2733 };
2734
2735 assert_eq!(
2736 ctx.shared.types.array_of(map_ty),
2737 None,
2738 "map of a list is not an array"
2739 );
2740 assert_eq!(
2741 ctx.shared.types.list_of(map_ty),
2742 Some((i8, Some(4))),
2743 "map of List<i8> (bound 4) is List<i8> (bound 4)"
2744 );
2745 }
2746
2747 #[test]
2748 fn cfg_branch_adds_one_node_and_one_edge() {
2749 let mut ctx = Context::new();
2750 qcode!(
2751 ctx,
2752 "
2753 <entry>
2754 goto <done>;
2755 <done>
2756 goto <0x1001>;
2757 "
2758 );
2759 assert_eq!(ctx.block_ids().len(), 3);
2761 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 2);
2762 }
2763
2764 #[test]
2765 fn cfg_cbranch_adds_two_edges() {
2766 let mut ctx = Context::new();
2767 qcode!(
2768 ctx,
2769 "
2770 varnode i8 cond;
2771
2772 <entry>
2773 %c = load(cond:1, cond);
2774 if %c goto <then_lbl> else goto <else_lbl>;
2775
2776 <then_lbl>
2777 goto <0x1001>;
2778
2779 <else_lbl>
2780 goto <0x1001>;
2781 "
2782 );
2783 assert_eq!(ctx.block_ids().len(), 4);
2786 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 4);
2787 }
2788
2789 #[test]
2790 fn cfg_branchind_adds_node_but_no_outgoing_edge() {
2791 let mut ctx = Context::new();
2792 qcode!(
2793 ctx,
2794 "
2795 <entry>
2796 local i64 ptr;
2797 goto [ptr];
2798 "
2799 );
2800
2801 assert_eq!(ctx.block_ids().len(), 1);
2802 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 0);
2803
2804 assert_eq!(BasicBlock::from_id(&ctx, entry).successors().count(), 0);
2805 }
2806
2807 #[test]
2808 fn cfg_return_adds_node_but_no_outgoing_edge() {
2809 let mut ctx = Context::new();
2810 qcode!(
2811 ctx,
2812 "
2813 <entry>
2814 local i64 ptr;
2815 return at ptr;
2816 "
2817 );
2818 assert_eq!(ctx.block_ids().len(), 1);
2819 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 0);
2820 assert_eq!(BasicBlock::from_id(&ctx, entry).successors().count(), 0);
2821 }
2822
2823 #[test]
2824 fn cfg_multi_block_qcode_program() {
2825 let mut ctx = Context::new();
2826 qcode!(
2827 ctx,
2828 "
2829 varnode i32 v;
2830
2831 <entry>
2832 goto <body>;
2833
2834 <body>
2835 i64 %v0 = i64 &v + i64 1;
2836 goto <0x1001>;
2837 "
2838 );
2839
2840 assert_eq!(ctx.block_ids().len(), 3);
2843 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 2);
2844 }
2845
2846 #[test]
2847 fn test_builder_finalize() {
2848 let mut ctx = Context::new();
2851 let block_id = {
2852 let __f = ctx.anon_function();
2853 ctx.get_or_make_block(0, __f)
2854 };
2855 let target = ctx.get_or_make_block(0x1000, block_id.func);
2856
2857 {
2858 let builder = ctx.builder(block_id);
2859 builder.finalize(target);
2860 }
2861 }
2862
2863 #[test]
2864 fn test_named_temp_duplicate() {
2865 let mut ctx = Context::new();
2866 let mut builder = ctx.builder_at(0x1000);
2867
2868 let value = builder.make_named_temp("dup".into(), 4);
2869 let other_value = builder.make_named_temp("dup".into(), 4);
2870 let target = builder.current_block();
2871 builder.finalize(target);
2872
2873 assert_eq!(
2874 TempRef::new(ModuleView::new(&ctx), value).name(),
2875 Some("dup")
2876 );
2877 assert_eq!(
2878 TempRef::new(ModuleView::new(&ctx), other_value).name(),
2879 Some("dup_1")
2880 );
2881 }
2882
2883 #[test]
2884 fn same_label_temps_in_different_functions_are_isolated() {
2885 let mut ctx = Context::new();
2886
2887 let first = {
2888 let mut builder = ctx.builder_at(0x1000);
2889 let temp = builder.make_temp_labeled(7, 4);
2890 let target = builder.current_block();
2891 builder.finalize(target);
2892 temp
2893 };
2894 let second = {
2895 let mut builder = ctx.builder_at(0x2000);
2896 let temp = builder.make_temp_labeled(7, 4);
2897 let target = builder.current_block();
2898 builder.finalize(target);
2899 temp
2900 };
2901
2902 assert_ne!(first, second);
2903 let first = TempRef::new(ModuleView::new(&ctx), first);
2904 let second = TempRef::new(ModuleView::new(&ctx), second);
2905 assert_eq!((first.label(), second.label()), (Some(7), Some(7)));
2906 assert_ne!(first.space().id, second.space().id);
2907 }
2908
2909 #[test]
2910 fn qcode_local_decl_creates_named_temp() {
2911 let mut ctx = Context::new();
2912
2913 qcode!(ctx, "varnode i64 ptr; <block> goto <0x1001>;");
2914
2915 let ptr = Varnode::from_id(&ctx, ptr);
2916
2917 assert_eq!(ptr.size(), 8);
2918 assert_eq!(ptr.name(), Some("ptr"));
2919 }
2920
2921 #[test]
2922 fn qcode_standalone_local_decl_creates_named_temp() {
2923 let mut ctx = Context::new();
2924 qcode!(ctx, "varnode i64 ptr; <block> goto <0x1001>;");
2925
2926 let ptr = Varnode::from_id(&ctx, ptr);
2927
2928 assert_eq!(ptr.size(), 8);
2929 assert_eq!(ptr.name(), Some("ptr"));
2930 }
2931
2932 #[test]
2933 fn qcode_varnode_decl_before_entry_block() {
2934 let mut ctx = Context::new();
2935 qcode!(ctx, "varnode i64 ptr; <block> goto <0x1001>;");
2936
2937 let ptr = Varnode::from_id(&ctx, ptr);
2938
2939 assert_eq!(ptr.size(), 8);
2940 assert_eq!(ptr.name(), Some("ptr"));
2941 }
2942
2943 #[test]
2944 fn push_param_via_builder_visible_on_block() {
2945 let mut ctx = Context::new();
2946 let block_id = {
2947 let __f = ctx.anon_function();
2948 ctx.get_or_make_block(0x1000, __f)
2949 };
2950 let mut builder = ctx.builder(block_id);
2951
2952 let p0 = builder.push_param(8);
2953 let p0_id = p0;
2954 let p1 = builder.push_param(4);
2955 let p1_id = p1;
2956
2957 drop(builder);
2958
2959 let block = BasicBlock::from_id(&ctx, block_id);
2960 assert_eq!(block.num_params(), 2);
2961 let param_ids: Vec<_> = block.params().map(|p| p.id).collect();
2962 assert_eq!(param_ids, [p0_id, p1_id]);
2963 assert_eq!(block.instruction_ids().len(), 0);
2964 }
2965
2966 #[test]
2967 fn push_branch_with_args_via_builder() {
2968 let mut ctx = Context::new();
2969 let f = ctx.anon_function();
2971 let src_id = ctx.get_or_make_block(0x1000, f);
2972 let dst_id = ctx.get_or_make_block(0x2000, f);
2973
2974 let param_val = BasicBlock::from_id_mut(&mut ctx, dst_id).push_param(8).id();
2975
2976 {
2977 let mut builder = ctx.builder(src_id);
2978 builder.push_branch_with_args(dst_id, vec![param_val]);
2979 }
2980
2981 let block = BasicBlock::from_id(&ctx, src_id);
2982 let last = block.iter().last().expect("branch was added");
2983 let crate::value::insn::Mnemonic::Branch(branch) = last.mnemonic() else {
2984 panic!("expected branch");
2985 };
2986 assert_eq!(branch.target, dst_id.local);
2987 assert_eq!(branch.args.len(), 1);
2988 assert_eq!(branch.args[0], param_val.strip_func());
2989 }
2990
2991 #[test]
2992 fn test_builder_adds_address_to_qcode() {
2993 let mut ctx = Context::new();
2994 let id_42 = ctx.get_const(42, 8).id();
2995
2996 let not_insn_id = {
2997 let source = ctx.builder_at(0x1000).current_block();
2998 let target = ctx.get_or_make_block(0x1001, source.func);
2999 let mut builder = ctx.builder(source);
3000 builder.set_address(0x1000);
3001 let not_insn_id = builder.push_bit_negate(id_42).id;
3002 builder.finalize(target);
3003
3004 not_insn_id
3005 };
3006
3007 let insn = Instruction::from_id(&ctx, not_insn_id);
3008
3009 assert_eq!(insn.address().unwrap(), 0x1000);
3010 }
3011
3012 #[test]
3013 fn builder_at_materializes_root_in_registered_function_arena() {
3014 let mut ctx = Context::new();
3015 let func = FunctionBody::make_at_addr(&mut ctx, 0x2000, None).id;
3016 assert!(FunctionBody::from_id(&ctx, func).root().is_none());
3017
3018 let block = {
3019 let builder = ctx.builder_at(0x2000);
3020 builder.current_block()
3021 };
3022
3023 assert_eq!(block.func, func);
3024 assert_eq!(
3025 FunctionBody::from_id(&ctx, func).root().map(|root| root.id),
3026 Some(block)
3027 );
3028 assert!(FunctionBody::from_name(&ctx, "blk_2000").is_none());
3029 }
3030
3031 #[test]
3032 fn append_after_terminated_block_panic_includes_current_address() {
3033 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3034 let mut ctx = Context::new();
3035 let value = ctx.get_const(0, 1).id();
3036 let source = ctx.builder_at(0x4010).current_block();
3037 let target = ctx.get_or_make_block(0x4020, source.func);
3038 let mut builder = ctx.builder(source);
3039
3040 builder.push_branch(target);
3041 builder.set_address(0x4015);
3042 builder.push_bit_negate(value);
3043 }));
3044
3045 let panic = result.expect_err("append should panic after a terminator");
3046 let message = panic
3047 .downcast_ref::<String>()
3048 .map(String::as_str)
3049 .or_else(|| panic.downcast_ref::<&'static str>().copied())
3050 .expect("panic should carry a string message");
3051
3052 assert!(
3053 message.contains("cannot append instruction to a terminated block at 0x4015"),
3054 "unexpected panic message: {message}"
3055 );
3056 }
3057
3058 #[test]
3059 fn push_copy_supports_partial_final_lane() {
3060 let mut ctx = Context::new();
3061 let block_id = {
3062 let __f = ctx.anon_function();
3063 ctx.get_or_make_block(0x1000, __f)
3064 };
3065
3066 {
3067 let target = ctx.get_or_make_block(0x1001, block_id.func);
3068 let mut builder = ctx.builder(block_id);
3069 let src = builder.make_named_temp("src".into(), 9);
3070 let dst = builder.make_named_temp("dst".into(), 9);
3071 builder.push_copy(src.into(), dst);
3072 builder.finalize(target);
3073 }
3074
3075 let store_sizes = BasicBlock::from_id(&ctx, block_id)
3076 .iter()
3077 .filter_map(|insn| match insn.mnemonic() {
3078 Mnemonic::Store(store) => Some(store.size),
3079 _ => None,
3080 })
3081 .collect::<Vec<_>>();
3082 assert_eq!(store_sizes, [8, 1]);
3083 }
3084
3085 #[test]
3088 fn insert_point_to_start_prepends_before_existing_instruction() {
3089 let mut ctx = Context::new();
3090 let block_id = {
3091 let __f = ctx.anon_function();
3092 ctx.get_or_make_block(0x1000, __f)
3093 };
3094 let val = ctx.get_const(0, 8).id();
3095
3096 let existing_id = {
3097 let mut b = ctx.builder(block_id);
3098
3099 b.push_bit_negate(val).id
3100 };
3101
3102 let prepended_id = {
3103 let mut b = ctx.builder(block_id);
3104 b.set_insert_point_to_start();
3105 b.push_bit_negate(val).id
3106 };
3107
3108 let ids = BasicBlock::from_id(&ctx, block_id).instruction_ids();
3109 assert_eq!(ids, [prepended_id, existing_id]);
3110 }
3111
3112 #[test]
3113 fn multiple_pushes_with_insert_point_to_start_preserve_push_order() {
3114 let mut ctx = Context::new();
3115 let block_id = {
3116 let __f = ctx.anon_function();
3117 ctx.get_or_make_block(0x1000, __f)
3118 };
3119 let val = ctx.get_const(0, 8).id();
3120
3121 let existing_id = {
3122 let mut b = ctx.builder(block_id);
3123
3124 b.push_bit_negate(val).id
3125 };
3126
3127 let (id0, id1, id2) = {
3128 let mut b = ctx.builder(block_id);
3129 b.set_insert_point_to_start();
3130 (
3131 b.push_bit_negate(val).id,
3132 b.push_bit_negate(val).id,
3133 b.push_bit_negate(val).id,
3134 )
3135 };
3136
3137 let ids = BasicBlock::from_id(&ctx, block_id).instruction_ids();
3138 assert_eq!(ids, [id0, id1, id2, existing_id]);
3139 }
3140
3141 #[test]
3142 fn insert_point_before_existing_instruction_inserts_before_target() {
3143 let mut ctx = Context::new();
3144 let block_id = {
3145 let __f = ctx.anon_function();
3146 ctx.get_or_make_block(0x1000, __f)
3147 };
3148 let val = ctx.get_const(0, 8).id();
3149
3150 let (first_id, target_id) = {
3151 let mut b = ctx.builder(block_id);
3152 (b.push_bit_negate(val).id, b.push_bit_negate(val).id)
3153 };
3154
3155 let (inserted0, inserted1) = {
3156 let mut b = ctx.builder(block_id);
3157 b.set_insert_point_before(target_id);
3158 (b.push_bit_negate(val).id, b.push_bit_negate(val).id)
3159 };
3160
3161 let ids = BasicBlock::from_id(&ctx, block_id).instruction_ids();
3162 assert_eq!(ids, [first_id, inserted0, inserted1, target_id]);
3163 }
3164
3165 #[test]
3166 fn insert_point_to_start_allows_push_into_terminated_block() {
3167 let mut ctx = Context::new();
3168 qcode!(ctx, "<entry> goto <0x1001>;");
3169
3170 let val = ctx.get_const(1, 1).id();
3171 let new_id = {
3172 let mut b = ctx.builder(entry);
3173 b.set_insert_point_to_start();
3174 b.push_bit_negate(val).id
3175 };
3176
3177 let block = BasicBlock::from_id(&ctx, entry);
3178 assert_eq!(block.instruction_ids()[0], new_id);
3179 assert!(block.is_terminated());
3181 }
3182
3183 #[test]
3184 fn set_insert_point_to_end_restores_append_mode() {
3185 let mut ctx = Context::new();
3186 let block_id = {
3187 let __f = ctx.anon_function();
3188 ctx.get_or_make_block(0x1000, __f)
3189 };
3190 let val = ctx.get_const(0, 8).id();
3191
3192 let (first_id, middle_id, last_id) = {
3193 let mut b = ctx.builder(block_id);
3194 let first = b.push_bit_negate(val).id; b.set_insert_point_to_start();
3196 let middle = b.push_bit_negate(val).id; b.set_insert_point_to_end();
3198 let last = b.push_bit_negate(val).id; (first, middle, last)
3200 };
3201
3202 let ids = BasicBlock::from_id(&ctx, block_id).instruction_ids();
3203 assert_eq!(ids, [middle_id, first_id, last_id]);
3204 }
3205}