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 pub fn push_binop(
971 &mut self,
972 op: Binop,
973 lhs: ValueId,
974 rhs: ValueId,
975 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
976 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
977 let size = op.is_comparison().then_some(1);
978 let local = self.push_binop_local(op, lhs, rhs, size);
979 self.insn_ref(local)
980 }
981
982 fn push_binop_local(
983 &mut self,
984 op: Binop,
985 lhs: LocalValueId,
986 rhs: LocalValueId,
987 size: Option<usize>,
988 ) -> LocalInsnId {
989 let lhs_size = self.lsize_of(lhs);
990 let rhs_size = self.lsize_of(rhs);
991 let operand_size = match (
992 lhs_size == rhs_size,
993 self.is_literal(lhs),
994 self.is_literal(rhs),
995 ) {
996 (true, _, _) => lhs_size,
997 (false, true, false) => rhs_size,
998 (false, false, true) => lhs_size,
999 (false, true, true) => lhs_size.max(rhs_size),
1000 (false, false, false) => lhs_size,
1004 };
1005 let lhs = self.coerce_literal_size(lhs, operand_size);
1006 let rhs = self.coerce_literal_size(rhs, operand_size);
1007 assert_eq!(
1008 self.lsize_of(lhs),
1009 self.lsize_of(rhs),
1010 "push_binop: operands must have equal size; emit an explicit cast first"
1011 );
1012
1013 let result_type = {
1015 let lhs_type = self.ltype_of(lhs);
1016 let rhs_type = self.ltype_of(rhs);
1017 self.shr().types.binop_result(lhs_type, op, rhs_type)
1018 };
1019
1020 let result_type = if let Some(forced_size) = size {
1022 let current_size = self.shr().types.size_of(result_type);
1023 if forced_size != current_size {
1024 self.shr().types.get_or_make_int(forced_size)
1025 } else {
1026 result_type
1027 }
1028 } else {
1029 result_type
1030 };
1031
1032 let result_type = if self.shr().types.space_of(result_type).is_none()
1038 && matches!(op, Binop::Int(IntBinop::Add | IntBinop::Sub))
1039 {
1040 match self.merge_space_ids(lhs, rhs) {
1041 Some(space)
1042 if !matches!(Space::from_id(self.shr(), space).ty, SpaceType::Register) =>
1043 {
1044 let size = self.shr().types.size_of(result_type);
1045 self.shr().types.get_or_make_space_address(size, space)
1046 }
1047 _ => result_type,
1048 }
1049 } else {
1050 result_type
1051 };
1052
1053 let result_type = if matches!(op, Binop::Int(IntBinop::Add | IntBinop::Sub))
1058 && self.shr().types.space_of(result_type).is_some()
1059 {
1060 let spaced = |b: &Self, v| {
1061 b.lspace_of(v)
1062 .is_some_and(|s| !matches!(Space::from_id(b.shr(), s).ty, SpaceType::Register))
1063 };
1064 if spaced(self, lhs) && spaced(self, rhs) {
1065 let size = self.shr().types.size_of(result_type);
1066 self.shr().types.get_or_make_int(size)
1067 } else {
1068 result_type
1069 }
1070 } else {
1071 result_type
1072 };
1073
1074 self.store_insn_with_type(Mnemonic::Binop(Binary { op, lhs, rhs }), result_type)
1075 }
1076
1077 binop_leaf!(push_mul, push_mul_local, Binop::Int(IntBinop::Mul), None);
1080 binop_leaf!(push_div, push_div_local, Binop::Int(IntBinop::Div), None);
1081 binop_leaf!(push_sdiv, push_sdiv_local, Binop::Int(IntBinop::Sdiv), None);
1082 binop_leaf!(push_mod, push_mod_local, Binop::Int(IntBinop::Rem), None);
1083 binop_leaf!(push_smod, push_smod_local, Binop::Int(IntBinop::Srem), None);
1084 binop_leaf!(push_add, push_add_local, Binop::Int(IntBinop::Add), None);
1085 binop_leaf!(push_sub, push_sub_local, Binop::Int(IntBinop::Sub), None);
1086
1087 binop_leaf!(
1090 push_fdiv,
1091 push_fdiv_local,
1092 Binop::Float(FloatBinop::Div),
1093 None
1094 );
1095 binop_leaf!(
1096 push_fmul,
1097 push_fmul_local,
1098 Binop::Float(FloatBinop::Mul),
1099 None
1100 );
1101 binop_leaf!(
1102 push_fadd,
1103 push_fadd_local,
1104 Binop::Float(FloatBinop::Add),
1105 None
1106 );
1107 binop_leaf!(
1108 push_fsub,
1109 push_fsub_local,
1110 Binop::Float(FloatBinop::Sub),
1111 None
1112 );
1113
1114 binop_leaf!(
1117 push_shl,
1118 push_shl_local,
1119 Binop::Int(IntBinop::ShiftLeft),
1120 None
1121 );
1122 binop_leaf!(
1123 push_shr,
1124 push_shr_local,
1125 Binop::Int(IntBinop::ShiftRight),
1126 None
1127 );
1128 binop_leaf!(
1129 push_sshr,
1130 push_sshr_local,
1131 Binop::Int(IntBinop::SShiftRight),
1132 None
1133 );
1134
1135 cmp_pair!(
1139 push_slt,
1140 push_slt_local,
1141 push_sgt,
1142 push_sgt_local,
1143 Binop::Int(IntBinop::SLess)
1144 );
1145 cmp_pair!(
1146 push_sle,
1147 push_sle_local,
1148 push_sge,
1149 push_sge_local,
1150 Binop::Int(IntBinop::SLessEqual)
1151 );
1152 cmp_pair!(
1153 push_lt,
1154 push_lt_local,
1155 push_gt,
1156 push_gt_local,
1157 Binop::Int(IntBinop::Less)
1158 );
1159 cmp_pair!(
1160 push_le,
1161 push_le_local,
1162 push_ge,
1163 push_ge_local,
1164 Binop::Int(IntBinop::LessEqual)
1165 );
1166
1167 cmp_pair!(
1170 push_flt,
1171 push_flt_local,
1172 push_fgt,
1173 push_fgt_local,
1174 Binop::Float(FloatBinop::Less)
1175 );
1176 cmp_pair!(
1177 push_fle,
1178 push_fle_local,
1179 push_fge,
1180 push_fge_local,
1181 Binop::Float(FloatBinop::LessEqual)
1182 );
1183
1184 binop_leaf!(push_eq, push_eq_local, Binop::Int(IntBinop::Equal), Some(1));
1187 binop_leaf!(
1188 push_ne,
1189 push_ne_local,
1190 Binop::Int(IntBinop::NotEqual),
1191 Some(1)
1192 );
1193 binop_leaf!(
1194 push_feq,
1195 push_feq_local,
1196 Binop::Float(FloatBinop::Equal),
1197 Some(1)
1198 );
1199 binop_leaf!(
1200 push_fne,
1201 push_fne_local,
1202 Binop::Float(FloatBinop::NotEqual),
1203 Some(1)
1204 );
1205
1206 pub fn push_bool_xor(
1211 &mut self,
1212 lhs: ValueId,
1213 rhs: ValueId,
1214 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1215 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1216 let local = self.push_bool_xor_local(lhs, rhs);
1217 self.insn_ref(local)
1218 }
1219
1220 pub fn push_bool_xor_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1222 debug_assert!(
1223 self.both_bool(lhs, rhs),
1224 "push_bool_xor: operands must be bool"
1225 );
1226 self.push_binop_local(Binop::Int(IntBinop::Xor), lhs, rhs, None)
1227 }
1228
1229 pub fn push_bool_and(
1231 &mut self,
1232 lhs: ValueId,
1233 rhs: ValueId,
1234 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1235 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1236 let local = self.push_bool_and_local(lhs, rhs);
1237 self.insn_ref(local)
1238 }
1239
1240 pub fn push_bool_and_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1242 debug_assert!(
1243 self.both_bool(lhs, rhs),
1244 "push_bool_and: operands must be bool"
1245 );
1246 self.push_binop_local(Binop::Int(IntBinop::And), lhs, rhs, None)
1247 }
1248
1249 pub fn push_bool_or(
1251 &mut self,
1252 lhs: ValueId,
1253 rhs: ValueId,
1254 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1255 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1256 let local = self.push_bool_or_local(lhs, rhs);
1257 self.insn_ref(local)
1258 }
1259
1260 pub fn push_bool_or_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1262 debug_assert!(
1263 self.both_bool(lhs, rhs),
1264 "push_bool_or: operands must be bool"
1265 );
1266 self.push_binop_local(Binop::Int(IntBinop::Or), lhs, rhs, None)
1267 }
1268
1269 fn both_bool(&self, lhs: LocalValueId, rhs: LocalValueId) -> bool {
1271 let is_bool = |v: LocalValueId| {
1272 self.lstored_type_of(v)
1273 .is_some_and(|t| self.shr().types.is_bool(t))
1274 };
1275 is_bool(lhs) && is_bool(rhs)
1276 }
1277
1278 binop_leaf!(
1279 push_bit_xor,
1280 push_bit_xor_local,
1281 Binop::Int(IntBinop::Xor),
1282 None
1283 );
1284 binop_leaf!(
1285 push_bit_or,
1286 push_bit_or_local,
1287 Binop::Int(IntBinop::Or),
1288 None
1289 );
1290 binop_leaf!(
1291 push_bit_and,
1292 push_bit_and_local,
1293 Binop::Int(IntBinop::And),
1294 None
1295 );
1296
1297 pub fn push_is_nan(&mut self, src: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1300 let src = self.loc(src);
1301 let local = self.push_is_nan_local(src);
1302 self.insn_ref(local)
1303 }
1304
1305 pub fn push_is_nan_local(&mut self, src: LocalValueId) -> LocalInsnId {
1307 assert!(
1308 !matches!(src, LocalValueId::Varnode(_)),
1309 "push_is_nan: varnode operand not allowed"
1310 );
1311 self.store_insn(Mnemonic::IsFloatNaN(IsFloatNaN { src }), 1)
1312 }
1313
1314 unop_leaf!(push_abs, push_abs_local, Unop::FloatAbs);
1315 unop_leaf!(push_sqrt, push_sqrt_local, Unop::FloatSqrt);
1316 unop_leaf!(push_floor, push_floor_local, Unop::FloatFloor);
1317 unop_leaf!(push_ceil, push_ceil_local, Unop::FloatCeil);
1318 unop_leaf!(push_round, push_round_local, Unop::FloatRound);
1319
1320 conv_leaf!(
1321 push_int_to_float,
1322 push_int_to_float_local,
1323 "push_int_to_float: varnode operand not allowed",
1324 IntToFloat
1325 );
1326 conv_leaf!(
1327 push_float_to_float,
1328 push_float_to_float_local,
1329 "push_float_to_float: varnode operand not allowed",
1330 FloatToFloat
1331 );
1332 conv_leaf!(
1333 push_trunc,
1334 push_trunc_local,
1335 "push_trunc: varnode operand not allowed",
1336 FloatToInt
1337 );
1338 conv_leaf!(
1339 push_zext,
1340 push_zext_local,
1341 "push_zext: varnode operand not allowed",
1342 Zext
1343 );
1344 conv_leaf!(
1345 push_sext,
1346 push_sext_local,
1347 "push_sext: varnode operand not allowed",
1348 Sext
1349 );
1350
1351 pub fn push_tuple(
1356 &mut self,
1357 fields: Vec<ValueId>,
1358 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1359 let fields = self.loc_vec(fields);
1360 let local = self.push_tuple_local(fields);
1361 self.insn_ref(local)
1362 }
1363
1364 pub fn push_tuple_local(&mut self, fields: Vec<LocalValueId>) -> LocalInsnId {
1366 let named_fields = fields
1367 .into_iter()
1368 .enumerate()
1369 .map(|(i, value)| (format!("field{}", i + 1), value))
1370 .collect();
1371 self.push_named_tuple_local(named_fields)
1372 }
1373
1374 pub fn push_named_tuple(
1376 &mut self,
1377 fields: Vec<(String, ValueId)>,
1378 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1379 let fields = fields
1380 .into_iter()
1381 .map(|(name, v)| (name, self.loc(v)))
1382 .collect();
1383 let local = self.push_named_tuple_local(fields);
1384 self.insn_ref(local)
1385 }
1386
1387 pub fn push_named_tuple_local(&mut self, fields: Vec<(String, LocalValueId)>) -> LocalInsnId {
1389 let field_types: Vec<TypeId> = fields.iter().map(|(_, f)| self.ltype_of(*f)).collect();
1390 let aggregate_fields = fields
1391 .iter()
1392 .zip(field_types)
1393 .map(|((name, _), type_id)| AggregateField::new(name.clone(), type_id))
1394 .collect();
1395 let ty = self
1396 .shr()
1397 .types
1398 .get_or_make_named_aggregate(aggregate_fields);
1399 self.push_named_tuple_local_with_type(fields, ty)
1400 }
1401
1402 pub fn push_named_tuple_with_type(
1406 &mut self,
1407 fields: Vec<(String, ValueId)>,
1408 ty: TypeId,
1409 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1410 let fields = fields
1411 .into_iter()
1412 .map(|(name, value)| (name, self.loc(value)))
1413 .collect();
1414 let local = self.push_named_tuple_local_with_type(fields, ty);
1415 self.insn_ref(local)
1416 }
1417
1418 pub fn push_named_tuple_local_with_type(
1420 &mut self,
1421 fields: Vec<(String, LocalValueId)>,
1422 ty: TypeId,
1423 ) -> LocalInsnId {
1424 debug_assert_eq!(
1425 self.shr().types.aggregate_fields(ty).map(<[_]>::len),
1426 Some(fields.len()),
1427 "explicit tuple type must declare every tuple field"
1428 );
1429 debug_assert!(fields.iter().enumerate().all(|(index, (name, value))| {
1430 self.shr()
1431 .types
1432 .aggregate_fields(ty)
1433 .and_then(|declared| declared.get(index))
1434 .is_some_and(|declared| {
1435 declared.name == *name && declared.type_id == self.ltype_of(*value)
1436 })
1437 }));
1438 let values = fields.into_iter().map(|(_, value)| value).collect();
1439 self.store_insn_with_type(Mnemonic::Tuple(Tuple { fields: values }), ty)
1440 }
1441
1442 pub fn push_extract(
1445 &mut self,
1446 agg: ValueId,
1447 index: usize,
1448 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1449 let agg = self.loc(agg);
1450 let local = self.push_extract_local(agg, index);
1451 self.insn_ref(local)
1452 }
1453
1454 pub fn push_extract_local(&mut self, agg: LocalValueId, index: usize) -> LocalInsnId {
1456 let agg_ty = self.ltype_of(agg);
1457 let ty = self
1458 .shr()
1459 .types
1460 .field_type(agg_ty, index)
1461 .expect("push_extract: agg is not an aggregate with that field index");
1462 self.store_insn_with_type(Mnemonic::Extract(Extract { agg, index }), ty)
1463 }
1464
1465 pub fn push_map(
1478 &mut self,
1479 body: impl Into<Callee>,
1480 src: ValueId,
1481 captures: Vec<ValueId>,
1482 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1483 let (src, captures) = (self.loc(src), self.loc_vec(captures));
1484 let local = self.push_map_local(body, src, captures);
1485 self.insn_ref(local)
1486 }
1487
1488 pub fn push_map_local(
1490 &mut self,
1491 body: impl Into<Callee>,
1492 src: LocalValueId,
1493 captures: Vec<LocalValueId>,
1494 ) -> LocalInsnId {
1495 let body = body.into();
1496 let src_ty = self.ltype_of(src);
1497 let seq = self.shr().types.seq_of(src_ty);
1500 let ret_ty = body.real().and_then(|body| self.map_body_return_type(body));
1501 let ty = match (seq, ret_ty) {
1502 (Some((_, len, is_list)), Some(rt)) => {
1503 self.shr().types.get_or_make_seq(rt, len, is_list)
1504 }
1505 _ => src_ty,
1506 };
1507 self.push_map_typed_local(body, src, captures, ty)
1508 }
1509
1510 pub fn push_map_typed(
1514 &mut self,
1515 body: impl Into<Callee>,
1516 src: ValueId,
1517 captures: Vec<ValueId>,
1518 result_type: TypeId,
1519 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1520 let (src, captures) = (self.loc(src), self.loc_vec(captures));
1521 let local = self.push_map_typed_local(body, src, captures, result_type);
1522 self.insn_ref(local)
1523 }
1524
1525 pub fn push_map_typed_local(
1527 &mut self,
1528 body: impl Into<Callee>,
1529 src: LocalValueId,
1530 captures: Vec<LocalValueId>,
1531 result_type: TypeId,
1532 ) -> LocalInsnId {
1533 self.store_insn_with_type(
1534 Mnemonic::Map(Map {
1535 body: body.into(),
1536 src,
1537 captures,
1538 }),
1539 result_type,
1540 )
1541 }
1542
1543 pub fn push_scan(
1555 &mut self,
1556 body: impl Into<Callee>,
1557 init: ValueId,
1558 src: ValueId,
1559 captures: Vec<ValueId>,
1560 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1561 let (init, src, captures) = (self.loc(init), self.loc(src), self.loc_vec(captures));
1562 let local = self.push_scan_local(body, init, src, captures);
1563 self.insn_ref(local)
1564 }
1565
1566 pub fn push_scan_local(
1568 &mut self,
1569 body: impl Into<Callee>,
1570 init: LocalValueId,
1571 src: LocalValueId,
1572 captures: Vec<LocalValueId>,
1573 ) -> LocalInsnId {
1574 let body = body.into();
1575 let src_ty = self.ltype_of(src);
1576 let seq = self.shr().types.seq_of(src_ty);
1579 let ret_ty = body.real().and_then(|body| self.map_body_return_type(body));
1580 let ty = match (seq, ret_ty) {
1581 (Some((_, len, is_list)), Some(rt)) => {
1582 self.shr().types.get_or_make_seq(rt, len, is_list)
1583 }
1584 _ => src_ty,
1585 };
1586 self.push_scan_typed_local(body, init, src, captures, ty)
1587 }
1588
1589 pub fn push_scan_typed(
1592 &mut self,
1593 body: impl Into<Callee>,
1594 init: ValueId,
1595 src: ValueId,
1596 captures: Vec<ValueId>,
1597 result_type: TypeId,
1598 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1599 let (init, src, captures) = (self.loc(init), self.loc(src), self.loc_vec(captures));
1600 let local = self.push_scan_typed_local(body, init, src, captures, result_type);
1601 self.insn_ref(local)
1602 }
1603
1604 pub fn push_scan_typed_local(
1606 &mut self,
1607 body: impl Into<Callee>,
1608 init: LocalValueId,
1609 src: LocalValueId,
1610 captures: Vec<LocalValueId>,
1611 result_type: TypeId,
1612 ) -> LocalInsnId {
1613 self.store_insn_with_type(
1614 Mnemonic::Scan(Scan {
1615 body: body.into(),
1616 init,
1617 src,
1618 captures,
1619 }),
1620 result_type,
1621 )
1622 }
1623
1624 pub fn push_apply(
1628 &mut self,
1629 target: impl Into<Callee>,
1630 args: Vec<ValueId>,
1631 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1632 let args = self.loc_vec(args);
1633 let local = self.push_apply_local(target, args);
1634 self.insn_ref(local)
1635 }
1636
1637 pub fn push_apply_local(
1639 &mut self,
1640 target: impl Into<Callee>,
1641 args: Vec<LocalValueId>,
1642 ) -> LocalInsnId {
1643 let target = target.into();
1644 let ty = target
1645 .real()
1646 .and_then(|target| self.lambda_return_type(target))
1647 .unwrap_or_else(|| {
1648 args.first()
1649 .map(|&arg| self.ltype_of(arg))
1650 .unwrap_or_else(|| self.shr().types.get_or_make_int(0))
1651 });
1652 self.store_insn_with_type(Mnemonic::Apply(Apply { target, args }), ty)
1653 }
1654
1655 fn map_body_return_type(&self, body: FunctionId) -> Option<TypeId> {
1659 if self.body.try_id() != Some(body) {
1660 return None;
1661 }
1662 let root = self.body.root_id()?;
1663 self.body.blocks[root].instructions.iter().find_map(|&i| {
1664 match self.body.insns[i].mnemonic() {
1665 Mnemonic::Return(r) => r.value.and_then(|v| self.lstored_type_of(v)),
1666 _ => None,
1667 }
1668 })
1669 }
1670
1671 fn lambda_return_type(&self, body: FunctionId) -> Option<TypeId> {
1673 if self.body.try_id() != Some(body) {
1674 return None;
1675 }
1676 self.body
1677 .roster
1678 .iter()
1679 .flat_map(|&b| self.body.blocks[b].instructions.iter().copied())
1680 .find_map(|i| match self.body.insns[i].mnemonic() {
1681 Mnemonic::ReturnValue(r) => self.lstored_type_of(r.value),
1682 _ => None,
1683 })
1684 }
1685
1686 pub fn push_gep(
1692 &mut self,
1693 base: ValueId,
1694 offset: usize,
1695 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1696 let base = self.loc(base);
1697 let local = self.push_gep_local(base, offset);
1698 self.insn_ref(local)
1699 }
1700
1701 pub fn push_gep_local(&mut self, base: LocalValueId, offset: usize) -> LocalInsnId {
1703 let base_ty = self.ltype_of(base);
1704 let types = &self.shr().types;
1705 let ptr_width = types.size_of(base_ty);
1706 let pointee = types
1707 .pointee_of(base_ty)
1708 .expect("push_gep: base is not a struct pointer");
1709 let field_ty = types
1710 .field_by_offset(pointee, offset)
1711 .map(|(_, field)| field.type_id)
1712 .expect("push_gep: no field at that offset in the pointee struct");
1713 let ty = self
1714 .shr()
1715 .types
1716 .get_or_make_struct_pointer(ptr_width, field_ty);
1717 self.store_insn_with_type(Mnemonic::Gep(Gep { base, offset }), ty)
1718 }
1719
1720 pub fn push_gep_field(
1724 &mut self,
1725 base: ValueId,
1726 name: &str,
1727 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1728 let base = self.loc(base);
1729 let local = self.push_gep_field_local(base, name);
1730 self.insn_ref(local)
1731 }
1732
1733 pub fn push_gep_field_local(&mut self, base: LocalValueId, name: &str) -> LocalInsnId {
1735 let base_ty = self.ltype_of(base);
1736 let types = &self.shr().types;
1737 let pointee = types
1738 .pointee_of(base_ty)
1739 .expect("push_gep_field: base is not a struct pointer");
1740 let offset = types
1741 .aggregate_fields(pointee)
1742 .and_then(|fields| fields.iter().find(|f| f.name == name))
1743 .map(|f| f.offset)
1744 .expect("push_gep_field: pointee struct has no field of that name");
1745 self.push_gep_local(base, offset)
1746 }
1747
1748 pub fn push_popcount(
1749 &mut self,
1750 src: ValueId,
1751 size: usize,
1752 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1753 let src = self.loc(src);
1754 let local = self.push_popcount_local(src, size);
1755 self.insn_ref(local)
1756 }
1757
1758 pub fn push_popcount_local(&mut self, src: LocalValueId, size: usize) -> LocalInsnId {
1760 assert!(
1761 !matches!(src, LocalValueId::Varnode(_)),
1762 "push_popcount: varnode operand not allowed"
1763 );
1764 self.store_insn(Mnemonic::PopCount(PopCount { src }), size)
1765 }
1766
1767 pub fn push_lzcount(
1768 &mut self,
1769 src: ValueId,
1770 size: usize,
1771 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1772 let src = self.loc(src);
1773 let local = self.push_lzcount_local(src, size);
1774 self.insn_ref(local)
1775 }
1776
1777 pub fn push_lzcount_local(&mut self, src: LocalValueId, size: usize) -> LocalInsnId {
1779 assert!(
1780 !matches!(src, LocalValueId::Varnode(_)),
1781 "push_lzcount: varnode operand not allowed"
1782 );
1783 self.store_insn(Mnemonic::LzCount(LzCount { src }), size)
1784 }
1785
1786 pub fn push_carry(
1787 &mut self,
1788 lhs: ValueId,
1789 rhs: ValueId,
1790 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1791 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1792 let local = self.push_carry_local(lhs, rhs);
1793 self.insn_ref(local)
1794 }
1795
1796 pub fn push_carry_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1798 assert!(
1799 !matches!(lhs, LocalValueId::Varnode(_)) && !matches!(rhs, LocalValueId::Varnode(_)),
1800 "push_carry: varnode operand not allowed"
1801 );
1802 self.store_insn(Mnemonic::Carry(Carry { lhs, rhs }), 1)
1803 }
1804
1805 pub fn push_scarry(
1806 &mut self,
1807 lhs: ValueId,
1808 rhs: ValueId,
1809 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1810 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1811 let local = self.push_scarry_local(lhs, rhs);
1812 self.insn_ref(local)
1813 }
1814
1815 pub fn push_scarry_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1817 assert!(
1818 !matches!(lhs, LocalValueId::Varnode(_)) && !matches!(rhs, LocalValueId::Varnode(_)),
1819 "push_scarry: varnode operand not allowed"
1820 );
1821 self.store_insn(Mnemonic::SCarry(SCarry { lhs, rhs }), 1)
1822 }
1823
1824 pub fn push_sborrow(
1825 &mut self,
1826 lhs: ValueId,
1827 rhs: ValueId,
1828 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1829 let (lhs, rhs) = (self.loc(lhs), self.loc(rhs));
1830 let local = self.push_sborrow_local(lhs, rhs);
1831 self.insn_ref(local)
1832 }
1833
1834 pub fn push_sborrow_local(&mut self, lhs: LocalValueId, rhs: LocalValueId) -> LocalInsnId {
1836 assert!(
1837 !matches!(lhs, LocalValueId::Varnode(_)) && !matches!(rhs, LocalValueId::Varnode(_)),
1838 "push_sborrow: varnode operand not allowed"
1839 );
1840 self.store_insn(Mnemonic::SBorrow(SBorrow { lhs, rhs }), 1)
1841 }
1842
1843 pub fn push_pcode_op(
1844 &mut self,
1845 id: PCodeOpId,
1846 args: Vec<ValueId>,
1847 dst: Option<ValueId>,
1848 size: usize,
1849 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1850 let args = self.loc_vec(args);
1851 let dst = dst.map(|d| self.loc(d));
1852 let local = self.push_pcode_op_local(id, args, dst, size);
1853 self.insn_ref(local)
1854 }
1855
1856 pub fn push_pcode_op_local(
1858 &mut self,
1859 id: PCodeOpId,
1860 args: Vec<LocalValueId>,
1861 dst: Option<LocalValueId>,
1862 size: usize,
1863 ) -> LocalInsnId {
1864 let args = args
1865 .into_iter()
1866 .map(|arg| self.ensure_local_local(arg))
1867 .collect::<Vec<_>>();
1868
1869 self.store_insn(Mnemonic::PCodeOp(PCodeOp { id, args, dst }), size)
1870 }
1871
1872 #[track_caller]
1880 pub fn push_intrinsic(
1881 &mut self,
1882 id: IntrinsicId,
1883 args: Vec<ValueId>,
1884 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1885 let args = self.loc_vec(args);
1886 let local = self.push_intrinsic_local(id, args);
1887 self.insn_ref(local)
1888 }
1889
1890 #[track_caller]
1892 pub fn push_intrinsic_local(
1893 &mut self,
1894 id: IntrinsicId,
1895 args: Vec<LocalValueId>,
1896 ) -> LocalInsnId {
1897 let desc = id.desc();
1898 assert_eq!(
1899 args.len(),
1900 desc.arity(),
1901 "intrinsic `{}` expects {} args, got {}",
1902 desc.name(),
1903 desc.arity(),
1904 args.len()
1905 );
1906
1907 let args = args
1908 .into_iter()
1909 .map(|arg| self.ensure_local_local(arg))
1910 .collect::<Vec<_>>();
1911
1912 let arg_types = args
1913 .iter()
1914 .map(|&arg| self.ltype_of(arg))
1915 .collect::<Vec<_>>();
1916 let type_id = desc.result_type(&self.shr().types, &arg_types);
1917
1918 self.store_insn_with_type(Mnemonic::Intrinsic(IntrinsicApp { id, args }), type_id)
1919 }
1920
1921 pub fn push_copy(
1929 &mut self,
1930 src: ValueId,
1931 dst: impl Into<ValueId>,
1932 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
1933 let src = self.loc(src);
1934 let dst = self.loc(dst.into());
1935 let local = self.push_copy_local(src, dst);
1936 self.insn_ref(local)
1937 }
1938
1939 pub fn push_copy_local(&mut self, src: LocalValueId, dst: LocalValueId) -> LocalInsnId {
1941 let (size, space, name) = match dst {
1942 LocalValueId::Varnode(vid) => {
1943 let node = Varnode::from_id(self.shr(), vid);
1944 (
1945 node.size(),
1946 LocalMemorySpaceId::Shared(node.space().id),
1947 node.name().map(str::to_owned),
1948 )
1949 }
1950 LocalValueId::Temp(tlocal) => {
1951 let temp = &self.body.temps[tlocal];
1952 (
1953 temp.size,
1954 LocalMemorySpaceId::Temp(temp.space),
1955 temp.name.as_deref().map(str::to_owned),
1956 )
1957 }
1958 _ => panic!("copy destination must be a varnode or body-local temporary"),
1959 };
1960
1961 const LANE_SIZE: usize = 8;
1962
1963 if size > LANE_SIZE {
1964 let num_lanes = size.div_ceil(LANE_SIZE);
1965 let mut first_id = None;
1966
1967 for lane in 0..num_lanes {
1968 let offset = lane * LANE_SIZE;
1969 let lane_size = cmp::min(LANE_SIZE, size - offset);
1970
1971 let src_lane = self
1972 .get_range_local(src, offset..offset + lane_size)
1973 .expect("lane range in bounds");
1974 let src_lane = self.ensure_local_local(src_lane);
1975
1976 let dst_lane = self
1977 .get_range_local(dst, offset..offset + lane_size)
1978 .expect("lane range in bounds");
1979
1980 let id = self.store_insn(
1981 Mnemonic::Store(Store {
1982 src: src_lane,
1983 ptr: dst_lane,
1984 space,
1985 size: lane_size,
1986 }),
1987 0,
1988 );
1989
1990 if let Some(name) = &name {
1991 let name = Cow::Owned(format!("{}_lane{lane}", name.to_lowercase()));
1992 let _ = self.rename_insn_local(id, name);
1993 }
1994
1995 first_id.get_or_insert(id);
1996 }
1997
1998 first_id.unwrap()
1999 } else {
2000 let src = self.ensure_local_local(src);
2001 let id = self.store_insn(
2003 Mnemonic::Store(Store {
2004 src,
2005 ptr: dst,
2006 space,
2007 size,
2008 }),
2009 0,
2010 );
2011
2012 if let Some(name) = &name {
2014 let lowered = name.to_lowercase();
2015 let name = self.body.names.unique(Cow::Owned(lowered));
2016 self.rename_insn_local(id, name)
2017 .expect("This name was deduplicated");
2018 }
2019
2020 id
2021 }
2022 }
2023
2024 #[track_caller]
2025 pub fn push_store(
2026 &mut self,
2027 src: ValueId,
2028 ptr: ValueId,
2029 space: impl Into<LocalMemorySpaceId>,
2030 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2031 let (src, ptr) = (self.loc(src), self.loc(ptr));
2032 let local = self.push_store_local(src, ptr, space);
2033 self.insn_ref(local)
2034 }
2035
2036 #[track_caller]
2038 pub fn push_store_local(
2039 &mut self,
2040 src: LocalValueId,
2041 ptr: LocalValueId,
2042 space: impl Into<LocalMemorySpaceId>,
2043 ) -> LocalInsnId {
2044 let space = space.into();
2045 let src = self.ensure_local_local(src);
2046 let size = self.lsize_of(src);
2047
2048 match ptr {
2049 LocalValueId::Varnode(id) => {
2050 let varnode = Varnode::from_id(self.shr(), id);
2051 if varnode.space().id != space {
2052 panic!(
2053 "push_store: ptr is a varnode but its space {:?} does not match the store space {:?}; \
2054 call ensure_local on the ptr first",
2055 varnode.space().id,
2056 space
2057 );
2058 }
2059 }
2060
2061 LocalValueId::Instruction(local) => {
2062 self.set_insn_space_local(local, space);
2063 }
2064
2065 _ => {}
2066 }
2067
2068 self.store_insn(
2069 Mnemonic::Store(Store {
2070 src,
2071 ptr,
2072 space,
2073 size,
2074 }),
2075 0,
2076 )
2077 }
2078
2079 pub fn push_param(&mut self, size: usize) -> BlockParamId {
2083 let local = self.push_param_local(size);
2084 crate::value::block_param::BlockParamId::new(self.func(), local)
2085 }
2086
2087 pub fn push_param_local(&mut self, size: usize) -> crate::value::LocalParamId {
2089 let block = self.block;
2090 let index = self.body.blocks[block].params.len();
2091 let type_id = self.shared.types.get_or_make_int(size);
2092 let local = self.body.params.push(BlockParam {
2093 index,
2094 type_id,
2095 parent: Some(block),
2096 name: None,
2097 origin: None,
2098 });
2099 self.body.blocks[block].params.push(local);
2100 local
2101 }
2102
2103 pub fn finalize(mut self, target: BlockId) {
2106 self.finalize_local(target.local)
2107 }
2108
2109 pub fn finalize_local(&mut self, target: LocalBlockId) {
2111 if !self.is_terminated() {
2112 let branch = self.push_branch_local(target);
2113 if let Some(address) = self.address {
2114 self.body.insns[branch].set_address(address);
2115 }
2116 }
2117 }
2118
2119 fn add_cfg_edge_local(&mut self, from: LocalBlockId, to: LocalBlockId) {
2122 let edge_id = self
2123 .body
2124 .edges
2125 .push(crate::value::block::cfg::EdgeData { from, to });
2126 self.body.blocks[from].edges.insert(edge_id);
2127 self.body.blocks[to].edges.insert(edge_id);
2128 }
2129
2130 pub fn push_branch(&mut self, target: BlockId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2133 let local = self.push_branch_local(target.local);
2134 self.insn_ref(local)
2135 }
2136
2137 pub fn push_branch_local(&mut self, target: LocalBlockId) -> LocalInsnId {
2139 self.push_branch_with_args_local(target, vec![])
2140 }
2141
2142 pub fn push_branch_with_args(
2144 &mut self,
2145 target: BlockId,
2146 args: Vec<ValueId>,
2147 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2148 let args = self.loc_vec(args);
2149 let local = self.push_branch_with_args_local(target.local, args);
2150 self.insn_ref(local)
2151 }
2152
2153 pub fn push_branch_with_args_local(
2155 &mut self,
2156 target: LocalBlockId,
2157 args: Vec<LocalValueId>,
2158 ) -> LocalInsnId {
2159 let current = self.block;
2160 self.add_cfg_edge_local(current, target);
2161 let id = self.store_insn(Mnemonic::Branch(Branch { target, args }), 0);
2162 self.is_terminated = true;
2163 id
2164 }
2165
2166 pub fn push_cbranch(
2167 &mut self,
2168 condition: ValueId,
2169 target: BlockId,
2170 fallthrough: BlockId,
2171 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2172 self.push_cbranch_with_args(condition, target, vec![], fallthrough, vec![])
2173 }
2174
2175 pub fn push_cbranch_local(
2177 &mut self,
2178 condition: LocalValueId,
2179 target: LocalBlockId,
2180 fallthrough: LocalBlockId,
2181 ) -> LocalInsnId {
2182 self.push_cbranch_with_args_local(condition, target, vec![], fallthrough, vec![])
2183 }
2184
2185 pub fn push_cbranch_with_args(
2187 &mut self,
2188 condition: ValueId,
2189 target: BlockId,
2190 target_args: Vec<ValueId>,
2191 fallthrough: BlockId,
2192 fallthrough_args: Vec<ValueId>,
2193 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2194 let condition = self.loc(condition);
2195 let target_args = self.loc_vec(target_args);
2196 let fallthrough_args = self.loc_vec(fallthrough_args);
2197 let local = self.push_cbranch_with_args_local(
2198 condition,
2199 target.local,
2200 target_args,
2201 fallthrough.local,
2202 fallthrough_args,
2203 );
2204 self.insn_ref(local)
2205 }
2206
2207 pub fn push_cbranch_with_args_local(
2210 &mut self,
2211 condition: LocalValueId,
2212 target: LocalBlockId,
2213 target_args: Vec<LocalValueId>,
2214 fallthrough: LocalBlockId,
2215 fallthrough_args: Vec<LocalValueId>,
2216 ) -> LocalInsnId {
2217 assert!(
2218 !matches!(condition, LocalValueId::Varnode(_)),
2219 "push_cbranch: varnode condition not allowed; load the value first"
2220 );
2221 let current = self.block;
2222 self.add_cfg_edge_local(current, target);
2223 self.add_cfg_edge_local(current, fallthrough);
2224 let id = self.store_insn(
2225 Mnemonic::CBranch(CBranch {
2226 success_block: target,
2227 success_args: target_args,
2228 condition,
2229 failure_block: fallthrough,
2230 failure_args: fallthrough_args,
2231 }),
2232 0,
2233 );
2234 self.is_terminated = true;
2235 id
2236 }
2237
2238 pub fn push_switch(
2241 &mut self,
2242 scrutinee: ValueId,
2243 cases: Vec<(u64, BlockId, Vec<ValueId>)>,
2244 default: Option<(BlockId, Vec<ValueId>)>,
2245 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2246 let scrutinee = self.loc(scrutinee);
2247 let cases = cases
2248 .into_iter()
2249 .map(|(value, target, args)| (value, target.local, self.loc_vec(args)))
2250 .collect();
2251 let default = default.map(|(target, args)| (target.local, self.loc_vec(args)));
2252 let local = self.push_switch_local(scrutinee, cases, default);
2253 self.insn_ref(local)
2254 }
2255
2256 pub fn push_switch_local(
2258 &mut self,
2259 scrutinee: LocalValueId,
2260 cases: Vec<(u64, LocalBlockId, Vec<LocalValueId>)>,
2261 default: Option<(LocalBlockId, Vec<LocalValueId>)>,
2262 ) -> LocalInsnId {
2263 assert!(
2264 !matches!(scrutinee, LocalValueId::Varnode(_)),
2265 "push_switch: varnode scrutinee not allowed; load the value first"
2266 );
2267 let current = self.block;
2268 for &(_, target, _) in &cases {
2269 self.add_cfg_edge_local(current, target);
2270 }
2271 if let Some((target, _)) = &default {
2272 self.add_cfg_edge_local(current, *target);
2273 }
2274 let (default_block, default_args) = match default {
2275 Some((target, args)) => (Some(target), args),
2276 None => (None, Vec::new()),
2277 };
2278 let id = self.store_insn(
2279 Mnemonic::Switch(Switch {
2280 scrutinee,
2281 cases: cases
2282 .into_iter()
2283 .map(|(value, target, args)| SwitchArm {
2284 value,
2285 target,
2286 args,
2287 })
2288 .collect(),
2289 default: default_block,
2290 default_args,
2291 }),
2292 0,
2293 );
2294 self.is_terminated = true;
2295 id
2296 }
2297
2298 pub fn push_branchind(&mut self, ptr: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2299 let ptr = self.loc(ptr);
2300 let local = self.push_branchind_local(ptr);
2301 self.insn_ref(local)
2302 }
2303
2304 pub fn push_branchind_local(&mut self, ptr: LocalValueId) -> LocalInsnId {
2306 let id = self.store_insn(Mnemonic::BranchInd(BranchInd { ptr }), 0);
2307 self.is_terminated = true;
2308 id
2309 }
2310
2311 pub fn push_call(
2312 &mut self,
2313 target: impl Into<Callee>,
2314 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2315 self.push_call_with_args(target, vec![])
2316 }
2317
2318 pub fn push_call_with_args(
2319 &mut self,
2320 target: impl Into<Callee>,
2321 args: Vec<ValueId>,
2322 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2323 let args = self.loc_vec(args);
2324 let local = self.push_call_with_args_local(target, args);
2325 self.insn_ref(local)
2326 }
2327
2328 pub fn push_call_local(&mut self, target: impl Into<Callee>) -> LocalInsnId {
2330 self.push_call_with_args_local(target, vec![])
2331 }
2332
2333 pub fn push_call_with_args_local(
2335 &mut self,
2336 target: impl Into<Callee>,
2337 args: Vec<LocalValueId>,
2338 ) -> LocalInsnId {
2339 let target = target.into();
2340 let id = self.store_insn(
2341 Mnemonic::Call(Call {
2342 target,
2343 args,
2344 clobbers: vec![],
2345 tag: Default::default(),
2346 }),
2347 0,
2348 );
2349 self.is_terminated = true;
2350 id
2351 }
2352
2353 pub fn push_tail_call(
2358 &mut self,
2359 target: impl Into<Callee>,
2360 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2361 self.push_tail_call_with_args(target, vec![])
2362 }
2363
2364 pub fn push_tail_call_with_args(
2365 &mut self,
2366 target: impl Into<Callee>,
2367 args: Vec<ValueId>,
2368 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2369 let args = self.loc_vec(args);
2370 let local = self.push_tail_call_with_args_local(target, args);
2371 self.insn_ref(local)
2372 }
2373
2374 pub fn push_tail_call_local(&mut self, target: impl Into<Callee>) -> LocalInsnId {
2376 self.push_tail_call_with_args_local(target, vec![])
2377 }
2378
2379 pub fn push_tail_call_with_args_local(
2382 &mut self,
2383 target: impl Into<Callee>,
2384 args: Vec<LocalValueId>,
2385 ) -> LocalInsnId {
2386 let target = target.into();
2387 let id = self.store_insn(Mnemonic::TailCall(TailCall { target, args }), 0);
2388 self.is_terminated = true;
2389 id
2390 }
2391
2392 pub fn push_call_ind(&mut self, ptr: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2393 self.push_call_ind_with_args(ptr, vec![])
2394 }
2395
2396 pub fn push_call_ind_with_args(
2397 &mut self,
2398 ptr: ValueId,
2399 args: Vec<ValueId>,
2400 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2401 let (ptr, args) = (self.loc(ptr), self.loc_vec(args));
2402 let local = self.push_call_ind_with_args_local(ptr, args);
2403 self.insn_ref(local)
2404 }
2405
2406 pub fn push_call_ind_local(&mut self, ptr: LocalValueId) -> LocalInsnId {
2408 self.push_call_ind_with_args_local(ptr, vec![])
2409 }
2410
2411 pub fn push_call_ind_with_args_local(
2414 &mut self,
2415 ptr: LocalValueId,
2416 args: Vec<LocalValueId>,
2417 ) -> LocalInsnId {
2418 let id = self.store_insn(Mnemonic::CallInd(CallInd { ptr, args }), 0);
2419 self.is_terminated = true;
2420 id
2421 }
2422
2423 pub fn push_return(&mut self, ptr: ValueId) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2424 let ptr = self.loc(ptr);
2425 let local = self.push_return_local(ptr);
2426 self.insn_ref(local)
2427 }
2428
2429 pub fn push_return_local(&mut self, ptr: LocalValueId) -> LocalInsnId {
2431 self.push_return_at_local(None, ptr)
2432 }
2433
2434 pub fn push_return_with_value(
2435 &mut self,
2436 value: ValueId,
2437 ptr: ValueId,
2438 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2439 let (value, ptr) = (self.loc(value), self.loc(ptr));
2440 let local = self.push_return_at_local(Some(value), ptr);
2441 self.insn_ref(local)
2442 }
2443
2444 pub fn push_return_with_value_local(
2447 &mut self,
2448 value: LocalValueId,
2449 ptr: LocalValueId,
2450 ) -> LocalInsnId {
2451 self.push_return_at_local(Some(value), ptr)
2452 }
2453
2454 fn push_return_at_local(
2455 &mut self,
2456 value: Option<LocalValueId>,
2457 ptr: LocalValueId,
2458 ) -> LocalInsnId {
2459 let id = self.store_insn(Mnemonic::Return(Return { ptr, value }), 0);
2460 self.is_terminated = true;
2461 id
2462 }
2463
2464 pub fn push_return_value(
2465 &mut self,
2466 value: ValueId,
2467 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2468 let value = self.loc(value);
2469 let local = self.push_return_value_local(value);
2470 self.insn_ref(local)
2471 }
2472
2473 pub fn push_bad_insn(&mut self) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2476 let local = self.push_bad_insn_local();
2477 self.insn_ref(local)
2478 }
2479
2480 pub fn push_bad_insn_local(&mut self) -> LocalInsnId {
2482 let id = self.store_insn(Mnemonic::BadInsn(crate::value::insn::BadInsn), 0);
2483 self.is_terminated = true;
2484 id
2485 }
2486
2487 pub fn push_return_value_local(&mut self, value: LocalValueId) -> LocalInsnId {
2489 let id = self.store_insn(Mnemonic::ReturnValue(ReturnValue { value }), 0);
2490 self.is_terminated = true;
2491 id
2492 }
2493
2494 pub fn push_assert(
2498 &mut self,
2499 condition: ValueId,
2500 ) -> InstructionRef<'str, '_, BodyView<'_, 'str>> {
2501 let condition = self.loc(condition);
2502 let local = self.push_assert_local(condition);
2503 self.insn_ref(local)
2504 }
2505
2506 pub fn push_assert_local(&mut self, condition: LocalValueId) -> LocalInsnId {
2508 self.store_insn(Mnemonic::Assert(Assert { condition }), 0)
2509 }
2510}
2511
2512#[cfg(test)]
2513mod tests {
2514 use wazabin_qcode_macro::qcode;
2515
2516 use super::*;
2517 use crate::{context::Context, value::ModuleView};
2518
2519 #[test]
2520 fn checked_builder_matches_module_builder() {
2521 use crate::value::{
2522 FunctionId, FunctionRef, block::BasicBlock, function::FunctionBody,
2523 util::body_mut::BodyMut,
2524 };
2525
2526 fn body<'str>(b: &mut Builder<'str, '_>) {
2531 let c1 = b.shr().get_const(7, 8);
2532 let c2 = b.shr().get_const(9, 8);
2533 let sum = b.push_add(c1, c2).id();
2534 let _doubled = b.push_add(sum, sum).id();
2535 let lbl = b.get_or_make_local_label("next".into());
2536 b.push_branch(lbl);
2537 }
2538
2539 type BSnap = Vec<(String, Vec<String>, Vec<String>)>;
2542 fn snap(ctx: &Context, fid: FunctionId) -> BSnap {
2543 FunctionRef::from_id(ctx, fid)
2544 .blocks()
2545 .map(|blk| {
2546 let name = blk.name().unwrap_or("?").to_string();
2547 let insns: Vec<String> = blk
2548 .instructions()
2549 .map(|i| format!("{:?}", i.mnemonic()))
2550 .collect();
2551 let mut succ: Vec<String> = blk
2552 .successors()
2553 .map(|(_, s)| {
2554 BasicBlock::from_id(ctx, s)
2555 .name()
2556 .unwrap_or("?")
2557 .to_string()
2558 })
2559 .collect();
2560 succ.sort();
2561 (name, insns, succ)
2562 })
2563 .collect()
2564 }
2565
2566 let mut ctx_a = Context::new();
2568 let fid_a = FunctionBody::make(&mut ctx_a, "foo".into()).unwrap().id;
2569 let entry_a = FunctionBody::from_id_mut(&mut ctx_a, fid_a).make_root().id;
2570 {
2571 let mut b = ctx_a.builder(entry_a);
2572 body(&mut b);
2573 }
2574 let snap_a = snap(&ctx_a, fid_a);
2575 assert!(
2576 snap_a.iter().any(|(_, i, _)| !i.is_empty()),
2577 "sanity: built IR"
2578 );
2579
2580 let mut ctx_b = Context::new();
2582 let fid_b = FunctionBody::make(&mut ctx_b, "foo".into()).unwrap().id;
2583 let entry_b = FunctionBody::from_id_mut(&mut ctx_b, fid_b).make_root().id;
2584 {
2585 let mut host = BodyMut::new(&mut ctx_b.bodies[fid_b], &ctx_b.shared, &ctx_b.interfaces);
2586 let mut b = host.builder(entry_b);
2587 body(&mut b);
2588 }
2589 let snap_b = snap(&ctx_b, fid_b);
2590
2591 assert_eq!(
2592 snap_a, snap_b,
2593 "a body built through a checked-out builder must match the module-built body"
2594 );
2595 }
2596
2597 #[test]
2602 fn address_arithmetic_inherits_pointer_space() {
2603 use crate::value::function::FunctionBody;
2604
2605 let mut ctx = Context::new();
2606 let fid = FunctionBody::make(&mut ctx, "f".into()).unwrap().id;
2607 let entry = FunctionBody::from_id_mut(&mut ctx, fid).make_root().id;
2608
2609 let ram = ctx.shared.default_space;
2610 let ptr_ty = ctx.shared.types.get_or_make_space_address(8, ram);
2611 let ram_mem = ctx.shared.types.space_of(ptr_ty);
2612
2613 let (addr_a, addr_b) = {
2615 let mut b = ctx.builder(entry);
2616 let c1 = b.shr().get_const(0x1000, 8);
2617 let c2 = b.shr().get_const(0x2000, 8);
2618 (b.push_add(c1, c1).id(), b.push_add(c2, c2).id())
2619 };
2620 for v in [addr_a, addr_b] {
2621 if let ValueId::Instruction(i) = v {
2622 crate::value::Instruction::from_id_mut(&mut ctx, i).set_type(ptr_ty);
2623 }
2624 }
2625
2626 let (add_ptr_lit, add_lit_ptr, add_ptr_ptr, add_int_lit) = {
2627 let mut b = ctx.builder(entry);
2628 let k = b.shr().get_const(4, 8);
2629 let zero = b.shr().get_const(0, 8);
2630 (
2631 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(), )
2636 };
2637
2638 let space_of = |v: ValueId| ctx.shared.types.space_of(ctx.type_of(v));
2639 assert_eq!(
2640 space_of(add_ptr_lit),
2641 ram_mem,
2642 "ptr + literal keeps the pointer's space (rule 1)"
2643 );
2644 assert_eq!(
2645 space_of(add_lit_ptr),
2646 ram_mem,
2647 "literal + ptr keeps the pointer's space (rule 1, commutative)"
2648 );
2649 assert_eq!(
2650 space_of(add_ptr_ptr),
2651 None,
2652 "ptr + ptr is ambiguous and drops to a plain integer (rule 2)"
2653 );
2654 assert_eq!(
2655 space_of(add_int_lit),
2656 None,
2657 "int + literal carries no space"
2658 );
2659
2660 assert_eq!(
2664 space_of(addr_a),
2665 ram_mem,
2666 "the pointer operand a fold would return still carries its space"
2667 );
2668 }
2669
2670 #[test]
2674 fn detached_body_builds_through_local_verbs() {
2675 let ctx = Context::new();
2676 let mut body = FunctionBody::detached();
2677 assert_eq!(body.try_id(), None, "sanity: body starts detached");
2678
2679 let entry = body.push_block_local(BasicBlock::detached());
2681 let target = body.push_block_local(BasicBlock::detached());
2682 body.set_root_id(Some(entry));
2683
2684 let c1 = ctx.shared.get_const(7, 8).strip_func();
2685 let c2 = ctx.shared.get_const(9, 8).strip_func();
2686
2687 {
2688 let mut b = Builder::new_local(&mut body, &ctx.shared, &ctx.interfaces, entry);
2689 let sum = b.push_add_local(c1, c2);
2690 let sum = LocalValueId::Instruction(sum);
2691 let doubled = b.push_add_local(sum, sum);
2692 let _cmp = b.push_eq_local(LocalValueId::Instruction(doubled), c2);
2693 b.push_branch_local(target);
2694 assert!(b.is_terminated());
2695 b.switch_to_block_local(target);
2696 let ret = b.push_return_value_local(sum);
2697 let _ = ret;
2698 }
2699
2700 assert_eq!(body.blocks[entry].instructions.len(), 4);
2702 assert_eq!(body.blocks[target].instructions.len(), 1);
2703 let last = *body.blocks[entry].instructions.last().unwrap();
2704 assert!(body.insns[last].mnemonic().is_terminator());
2705
2706 assert_eq!(body.try_id(), None);
2708 }
2709
2710 #[test]
2713 fn map_over_a_list_yields_a_list() {
2714 use crate::value::{FunctionBody, insn::Return};
2715
2716 let mut ctx = Context::new();
2717 let i8 = ctx.shared.types.get_or_make_int(1);
2718
2719 let body = FunctionBody::make(&mut ctx, "body".into()).unwrap().id;
2721 let broot = FunctionBody::from_id_mut(&mut ctx, body).make_root().id;
2722 let bp = BasicBlock::from_id_mut(&mut ctx, broot).push_param(1).id;
2723 let dummy = ctx.get_const(0, 8).id();
2724 let ret = InstructionRef::from_mnemonic_with_type(
2725 &mut ctx,
2726 body,
2727 Mnemonic::Return(Return {
2728 ptr: dummy.localize(body),
2729 value: Some(ValueId::BlockParam(bp).localize(body)),
2730 }),
2731 i8,
2732 )
2733 .id;
2734 BasicBlock::from_id_mut(&mut ctx, broot).push_insn(ret);
2735
2736 let host = FunctionBody::make(&mut ctx, "host".into()).unwrap().id;
2738 let hentry = FunctionBody::from_id_mut(&mut ctx, host).make_root().id;
2739 let list_ty = ctx.shared.types.get_or_make_list(i8, 4);
2740 let src_pid = BasicBlock::from_id_mut(&mut ctx, hentry).push_param(4).id;
2741 ctx.block_param_mut(src_pid).type_id = list_ty;
2742 let src = ValueId::BlockParam(src_pid);
2743
2744 let map_ty = {
2745 let mut b = ctx.builder(hentry);
2746 b.push_map(body, src, Vec::new()).type_id()
2747 };
2748
2749 assert_eq!(
2750 ctx.shared.types.array_of(map_ty),
2751 None,
2752 "map of a list is not an array"
2753 );
2754 assert_eq!(
2755 ctx.shared.types.list_of(map_ty),
2756 Some((i8, Some(4))),
2757 "map of List<i8> (bound 4) is List<i8> (bound 4)"
2758 );
2759 }
2760
2761 #[test]
2762 fn cfg_branch_adds_one_node_and_one_edge() {
2763 let mut ctx = Context::new();
2764 qcode!(
2765 ctx,
2766 "
2767 <entry>
2768 goto <done>;
2769 <done>
2770 goto <0x1001>;
2771 "
2772 );
2773 assert_eq!(ctx.block_ids().len(), 3);
2775 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 2);
2776 }
2777
2778 #[test]
2779 fn cfg_cbranch_adds_two_edges() {
2780 let mut ctx = Context::new();
2781 qcode!(
2782 ctx,
2783 "
2784 varnode i8 cond;
2785
2786 <entry>
2787 %c = load(cond:1, cond);
2788 if %c goto <then_lbl> else goto <else_lbl>;
2789
2790 <then_lbl>
2791 goto <0x1001>;
2792
2793 <else_lbl>
2794 goto <0x1001>;
2795 "
2796 );
2797 assert_eq!(ctx.block_ids().len(), 4);
2800 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 4);
2801 }
2802
2803 #[test]
2804 fn cfg_branchind_adds_node_but_no_outgoing_edge() {
2805 let mut ctx = Context::new();
2806 qcode!(
2807 ctx,
2808 "
2809 <entry>
2810 local i64 ptr;
2811 goto [ptr];
2812 "
2813 );
2814
2815 assert_eq!(ctx.block_ids().len(), 1);
2816 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 0);
2817
2818 assert_eq!(BasicBlock::from_id(&ctx, entry).successors().count(), 0);
2819 }
2820
2821 #[test]
2822 fn cfg_return_adds_node_but_no_outgoing_edge() {
2823 let mut ctx = Context::new();
2824 qcode!(
2825 ctx,
2826 "
2827 <entry>
2828 local i64 ptr;
2829 return at ptr;
2830 "
2831 );
2832 assert_eq!(ctx.block_ids().len(), 1);
2833 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 0);
2834 assert_eq!(BasicBlock::from_id(&ctx, entry).successors().count(), 0);
2835 }
2836
2837 #[test]
2838 fn cfg_multi_block_qcode_program() {
2839 let mut ctx = Context::new();
2840 qcode!(
2841 ctx,
2842 "
2843 varnode i32 v;
2844
2845 <entry>
2846 goto <body>;
2847
2848 <body>
2849 i64 %v0 = i64 &v + i64 1;
2850 goto <0x1001>;
2851 "
2852 );
2853
2854 assert_eq!(ctx.block_ids().len(), 3);
2857 assert_eq!(ctx.functions().flat_map(|f| f.edge_ids()).count(), 2);
2858 }
2859
2860 #[test]
2861 fn test_builder_finalize() {
2862 let mut ctx = Context::new();
2865 let block_id = {
2866 let __f = ctx.anon_function();
2867 ctx.get_or_make_block(0, __f)
2868 };
2869 let target = ctx.get_or_make_block(0x1000, block_id.func);
2870
2871 {
2872 let builder = ctx.builder(block_id);
2873 builder.finalize(target);
2874 }
2875 }
2876
2877 #[test]
2878 fn test_named_temp_duplicate() {
2879 let mut ctx = Context::new();
2880 let mut builder = ctx.builder_at(0x1000);
2881
2882 let value = builder.make_named_temp("dup".into(), 4);
2883 let other_value = builder.make_named_temp("dup".into(), 4);
2884 let target = builder.current_block();
2885 builder.finalize(target);
2886
2887 assert_eq!(
2888 TempRef::new(ModuleView::new(&ctx), value).name(),
2889 Some("dup")
2890 );
2891 assert_eq!(
2892 TempRef::new(ModuleView::new(&ctx), other_value).name(),
2893 Some("dup_1")
2894 );
2895 }
2896
2897 #[test]
2898 fn same_label_temps_in_different_functions_are_isolated() {
2899 let mut ctx = Context::new();
2900
2901 let first = {
2902 let mut builder = ctx.builder_at(0x1000);
2903 let temp = builder.make_temp_labeled(7, 4);
2904 let target = builder.current_block();
2905 builder.finalize(target);
2906 temp
2907 };
2908 let second = {
2909 let mut builder = ctx.builder_at(0x2000);
2910 let temp = builder.make_temp_labeled(7, 4);
2911 let target = builder.current_block();
2912 builder.finalize(target);
2913 temp
2914 };
2915
2916 assert_ne!(first, second);
2917 let first = TempRef::new(ModuleView::new(&ctx), first);
2918 let second = TempRef::new(ModuleView::new(&ctx), second);
2919 assert_eq!((first.label(), second.label()), (Some(7), Some(7)));
2920 assert_ne!(first.space().id, second.space().id);
2921 }
2922
2923 #[test]
2924 fn qcode_local_decl_creates_named_temp() {
2925 let mut ctx = Context::new();
2926
2927 qcode!(ctx, "varnode i64 ptr; <block> goto <0x1001>;");
2928
2929 let ptr = Varnode::from_id(&ctx, ptr);
2930
2931 assert_eq!(ptr.size(), 8);
2932 assert_eq!(ptr.name(), Some("ptr"));
2933 }
2934
2935 #[test]
2936 fn qcode_standalone_local_decl_creates_named_temp() {
2937 let mut ctx = Context::new();
2938 qcode!(ctx, "varnode i64 ptr; <block> goto <0x1001>;");
2939
2940 let ptr = Varnode::from_id(&ctx, ptr);
2941
2942 assert_eq!(ptr.size(), 8);
2943 assert_eq!(ptr.name(), Some("ptr"));
2944 }
2945
2946 #[test]
2947 fn qcode_varnode_decl_before_entry_block() {
2948 let mut ctx = Context::new();
2949 qcode!(ctx, "varnode i64 ptr; <block> goto <0x1001>;");
2950
2951 let ptr = Varnode::from_id(&ctx, ptr);
2952
2953 assert_eq!(ptr.size(), 8);
2954 assert_eq!(ptr.name(), Some("ptr"));
2955 }
2956
2957 #[test]
2958 fn push_param_via_builder_visible_on_block() {
2959 let mut ctx = Context::new();
2960 let block_id = {
2961 let __f = ctx.anon_function();
2962 ctx.get_or_make_block(0x1000, __f)
2963 };
2964 let mut builder = ctx.builder(block_id);
2965
2966 let p0 = builder.push_param(8);
2967 let p0_id = p0;
2968 let p1 = builder.push_param(4);
2969 let p1_id = p1;
2970
2971 drop(builder);
2972
2973 let block = BasicBlock::from_id(&ctx, block_id);
2974 assert_eq!(block.num_params(), 2);
2975 let param_ids: Vec<_> = block.params().map(|p| p.id).collect();
2976 assert_eq!(param_ids, [p0_id, p1_id]);
2977 assert_eq!(block.instruction_ids().len(), 0);
2978 }
2979
2980 #[test]
2981 fn push_branch_with_args_via_builder() {
2982 let mut ctx = Context::new();
2983 let f = ctx.anon_function();
2985 let src_id = ctx.get_or_make_block(0x1000, f);
2986 let dst_id = ctx.get_or_make_block(0x2000, f);
2987
2988 let param_val = BasicBlock::from_id_mut(&mut ctx, dst_id).push_param(8).id();
2989
2990 {
2991 let mut builder = ctx.builder(src_id);
2992 builder.push_branch_with_args(dst_id, vec![param_val]);
2993 }
2994
2995 let block = BasicBlock::from_id(&ctx, src_id);
2996 let last = block.iter().last().expect("branch was added");
2997 let crate::value::insn::Mnemonic::Branch(branch) = last.mnemonic() else {
2998 panic!("expected branch");
2999 };
3000 assert_eq!(branch.target, dst_id.local);
3001 assert_eq!(branch.args.len(), 1);
3002 assert_eq!(branch.args[0], param_val.strip_func());
3003 }
3004
3005 #[test]
3006 fn test_builder_adds_address_to_qcode() {
3007 let mut ctx = Context::new();
3008 let id_42 = ctx.get_const(42, 8).id();
3009
3010 let not_insn_id = {
3011 let source = ctx.builder_at(0x1000).current_block();
3012 let target = ctx.get_or_make_block(0x1001, source.func);
3013 let mut builder = ctx.builder(source);
3014 builder.set_address(0x1000);
3015 let not_insn_id = builder.push_bit_negate(id_42).id;
3016 builder.finalize(target);
3017
3018 not_insn_id
3019 };
3020
3021 let insn = Instruction::from_id(&ctx, not_insn_id);
3022
3023 assert_eq!(insn.address().unwrap(), 0x1000);
3024 }
3025
3026 #[test]
3027 fn builder_at_materializes_root_in_registered_function_arena() {
3028 let mut ctx = Context::new();
3029 let func = FunctionBody::make_at_addr(&mut ctx, 0x2000, None).id;
3030 assert!(FunctionBody::from_id(&ctx, func).root().is_none());
3031
3032 let block = {
3033 let builder = ctx.builder_at(0x2000);
3034 builder.current_block()
3035 };
3036
3037 assert_eq!(block.func, func);
3038 assert_eq!(
3039 FunctionBody::from_id(&ctx, func).root().map(|root| root.id),
3040 Some(block)
3041 );
3042 assert!(FunctionBody::from_name(&ctx, "blk_2000").is_none());
3043 }
3044
3045 #[test]
3046 fn append_after_terminated_block_panic_includes_current_address() {
3047 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3048 let mut ctx = Context::new();
3049 let value = ctx.get_const(0, 1).id();
3050 let source = ctx.builder_at(0x4010).current_block();
3051 let target = ctx.get_or_make_block(0x4020, source.func);
3052 let mut builder = ctx.builder(source);
3053
3054 builder.push_branch(target);
3055 builder.set_address(0x4015);
3056 builder.push_bit_negate(value);
3057 }));
3058
3059 let panic = result.expect_err("append should panic after a terminator");
3060 let message = panic
3061 .downcast_ref::<String>()
3062 .map(String::as_str)
3063 .or_else(|| panic.downcast_ref::<&'static str>().copied())
3064 .expect("panic should carry a string message");
3065
3066 assert!(
3067 message.contains("cannot append instruction to a terminated block at 0x4015"),
3068 "unexpected panic message: {message}"
3069 );
3070 }
3071
3072 #[test]
3073 fn push_copy_supports_partial_final_lane() {
3074 let mut ctx = Context::new();
3075 let block_id = {
3076 let __f = ctx.anon_function();
3077 ctx.get_or_make_block(0x1000, __f)
3078 };
3079
3080 {
3081 let target = ctx.get_or_make_block(0x1001, block_id.func);
3082 let mut builder = ctx.builder(block_id);
3083 let src = builder.make_named_temp("src".into(), 9);
3084 let dst = builder.make_named_temp("dst".into(), 9);
3085 builder.push_copy(src.into(), dst);
3086 builder.finalize(target);
3087 }
3088
3089 let store_sizes = BasicBlock::from_id(&ctx, block_id)
3090 .iter()
3091 .filter_map(|insn| match insn.mnemonic() {
3092 Mnemonic::Store(store) => Some(store.size),
3093 _ => None,
3094 })
3095 .collect::<Vec<_>>();
3096 assert_eq!(store_sizes, [8, 1]);
3097 }
3098
3099 #[test]
3102 fn insert_point_to_start_prepends_before_existing_instruction() {
3103 let mut ctx = Context::new();
3104 let block_id = {
3105 let __f = ctx.anon_function();
3106 ctx.get_or_make_block(0x1000, __f)
3107 };
3108 let val = ctx.get_const(0, 8).id();
3109
3110 let existing_id = {
3111 let mut b = ctx.builder(block_id);
3112
3113 b.push_bit_negate(val).id
3114 };
3115
3116 let prepended_id = {
3117 let mut b = ctx.builder(block_id);
3118 b.set_insert_point_to_start();
3119 b.push_bit_negate(val).id
3120 };
3121
3122 let ids = BasicBlock::from_id(&ctx, block_id).instruction_ids();
3123 assert_eq!(ids, [prepended_id, existing_id]);
3124 }
3125
3126 #[test]
3127 fn multiple_pushes_with_insert_point_to_start_preserve_push_order() {
3128 let mut ctx = Context::new();
3129 let block_id = {
3130 let __f = ctx.anon_function();
3131 ctx.get_or_make_block(0x1000, __f)
3132 };
3133 let val = ctx.get_const(0, 8).id();
3134
3135 let existing_id = {
3136 let mut b = ctx.builder(block_id);
3137
3138 b.push_bit_negate(val).id
3139 };
3140
3141 let (id0, id1, id2) = {
3142 let mut b = ctx.builder(block_id);
3143 b.set_insert_point_to_start();
3144 (
3145 b.push_bit_negate(val).id,
3146 b.push_bit_negate(val).id,
3147 b.push_bit_negate(val).id,
3148 )
3149 };
3150
3151 let ids = BasicBlock::from_id(&ctx, block_id).instruction_ids();
3152 assert_eq!(ids, [id0, id1, id2, existing_id]);
3153 }
3154
3155 #[test]
3156 fn insert_point_before_existing_instruction_inserts_before_target() {
3157 let mut ctx = Context::new();
3158 let block_id = {
3159 let __f = ctx.anon_function();
3160 ctx.get_or_make_block(0x1000, __f)
3161 };
3162 let val = ctx.get_const(0, 8).id();
3163
3164 let (first_id, target_id) = {
3165 let mut b = ctx.builder(block_id);
3166 (b.push_bit_negate(val).id, b.push_bit_negate(val).id)
3167 };
3168
3169 let (inserted0, inserted1) = {
3170 let mut b = ctx.builder(block_id);
3171 b.set_insert_point_before(target_id);
3172 (b.push_bit_negate(val).id, b.push_bit_negate(val).id)
3173 };
3174
3175 let ids = BasicBlock::from_id(&ctx, block_id).instruction_ids();
3176 assert_eq!(ids, [first_id, inserted0, inserted1, target_id]);
3177 }
3178
3179 #[test]
3180 fn insert_point_to_start_allows_push_into_terminated_block() {
3181 let mut ctx = Context::new();
3182 qcode!(ctx, "<entry> goto <0x1001>;");
3183
3184 let val = ctx.get_const(1, 1).id();
3185 let new_id = {
3186 let mut b = ctx.builder(entry);
3187 b.set_insert_point_to_start();
3188 b.push_bit_negate(val).id
3189 };
3190
3191 let block = BasicBlock::from_id(&ctx, entry);
3192 assert_eq!(block.instruction_ids()[0], new_id);
3193 assert!(block.is_terminated());
3195 }
3196
3197 #[test]
3198 fn set_insert_point_to_end_restores_append_mode() {
3199 let mut ctx = Context::new();
3200 let block_id = {
3201 let __f = ctx.anon_function();
3202 ctx.get_or_make_block(0x1000, __f)
3203 };
3204 let val = ctx.get_const(0, 8).id();
3205
3206 let (first_id, middle_id, last_id) = {
3207 let mut b = ctx.builder(block_id);
3208 let first = b.push_bit_negate(val).id; b.set_insert_point_to_start();
3210 let middle = b.push_bit_negate(val).id; b.set_insert_point_to_end();
3212 let last = b.push_bit_negate(val).id; (first, middle, last)
3214 };
3215
3216 let ids = BasicBlock::from_id(&ctx, block_id).instruction_ids();
3217 assert_eq!(ids, [middle_id, first_id, last_id]);
3218 }
3219}