1use std::ops::{Index, IndexMut};
30
31use rucc_base::{Idx, Symbol};
32use rucc_diag::Span;
33use rucc_target::Slot;
34
35use crate::inst::{
36 Abi, AbiList, AsmInfo, Block, BlockCall, BlockCallList, BlockData, Bulk, CallInfo, Def, Extra,
37 Imm, ImmList, Inst, InstData, InstLayout, MemInfo, Sig, Signature, SlotList, SwitchInfo,
38 VaInfo, Value, ValueData, ValueList,
39};
40use crate::module::{Linkage, Visibility};
41use crate::{Attrs, Facts, Flags, FloatPred, IntPred, MemOrder, Opcode, PrefetchHint, RmwOp, Type};
42
43#[derive(Debug)]
45pub struct Func {
46 pub name: Symbol,
48 pub linkage: Linkage,
50 pub visibility: Visibility,
52 pub section: Option<Symbol>,
55 pub align: Option<u32>,
62 pub attrs: Attrs,
65
66 values: Vec<ValueData>,
67 insts: Vec<InstData>,
68 inst_layout: Vec<InstLayout>,
69 inst_spans: Vec<Span>,
70 blocks: Vec<BlockData>,
71
72 value_pool: Vec<Value>,
73 block_calls: Vec<BlockCall>,
74 imms: Vec<Imm>,
75 mem: Vec<MemInfo>,
76 calls: Vec<CallInfo>,
77 abis: Vec<Abi>,
78 switches: Vec<SwitchInfo>,
79 asms: Vec<AsmInfo>,
80 slots: Vec<Slot>,
81 va_objects: Vec<VaInfo>,
82 signatures: Vec<Signature>,
83 facts: Vec<(Value, Facts)>,
84 labels: Vec<(Block, Symbol)>,
85
86 first_block: Option<Block>,
87 last_block: Option<Block>,
88}
89
90impl Func {
91 #[must_use]
98 pub fn new(name: Symbol, signature: Signature) -> Self {
99 Self {
100 name,
101 linkage: Linkage::External,
102 visibility: Visibility::Default,
103 section: None,
104 align: None,
105 attrs: Attrs::NONE,
106 values: Vec::new(),
107 insts: Vec::new(),
108 inst_layout: Vec::new(),
109 inst_spans: Vec::new(),
110 blocks: Vec::new(),
111 value_pool: Vec::new(),
112 block_calls: Vec::new(),
113 imms: Vec::new(),
114 mem: Vec::new(),
115 calls: Vec::new(),
116 abis: Vec::new(),
117 switches: Vec::new(),
118 asms: Vec::new(),
119 slots: Vec::new(),
120 va_objects: Vec::new(),
121 signatures: vec![signature],
122 facts: Vec::new(),
123 labels: Vec::new(),
124 first_block: None,
125 last_block: None,
126 }
127 }
128
129 #[must_use]
131 pub fn signature(&self) -> &Signature {
132 &self.signatures[0]
133 }
134
135 pub fn set_signature(&mut self, signature: Signature) {
145 self.signatures[0] = signature;
146 }
147
148 pub fn signatures(&self) -> impl Iterator<Item = &Signature> {
150 self.signatures.iter()
151 }
152
153 pub fn add_signature(&mut self, signature: Signature) -> Sig {
155 self.signatures.push(signature);
156 Idx::from_usize(self.signatures.len() - 1)
157 }
158
159 #[must_use]
164 pub fn entry(&self) -> Option<Block> {
165 self.first_block
166 }
167
168 #[must_use]
175 pub fn is_declaration(&self) -> bool {
176 self.first_block.is_none()
177 }
178
179 pub fn create_block(&mut self) -> Block {
183 let block = Idx::from_usize(self.blocks.len());
184 self.blocks.push(BlockData { prev: self.last_block, ..BlockData::default() });
185 match self.last_block {
186 Some(last) => self.blocks[last.index()].next = Some(block),
187 None => self.first_block = Some(block),
188 }
189 self.last_block = Some(block);
190 block
191 }
192
193 pub fn remove_block(&mut self, block: Block) {
206 assert!(self.first_block != Some(block), "the entry block is not removable");
207 let (prev, next) = (self.blocks[block.index()].prev, self.blocks[block.index()].next);
208 match prev {
209 Some(prev) => self.blocks[prev.index()].next = next,
210 None => self.first_block = next,
211 }
212 match next {
213 Some(next) => self.blocks[next.index()].prev = prev,
214 None => self.last_block = prev,
215 }
216 let insts: Vec<Inst> = self.insts(block).collect();
220 for inst in insts {
221 self.inst_layout[inst.index()] = InstLayout::default();
222 }
223 self.blocks[block.index()] = BlockData::default();
224 }
225
226 pub fn append_param(&mut self, block: Block, ty: Type) -> Value {
235 let index = u32::try_from(self.blocks[block.index()].params.len())
236 .expect("a block with four billion parameters");
237 let value = self.add_value(ValueData { ty, def: Def::Param { block, index } });
238 self.blocks[block.index()].params.push(value);
239 value
240 }
241
242 pub fn retain_params(&mut self, block: Block, mut keep: impl FnMut(Value) -> bool) {
254 let mut params = std::mem::take(&mut self.blocks[block.index()].params);
255 params.retain(|&value| keep(value));
256 for (index, &value) in params.iter().enumerate() {
257 let index = u32::try_from(index).expect("a block with four billion parameters");
258 self.values[value.index()].def = Def::Param { block, index };
259 }
260 self.blocks[block.index()].params = params;
261 }
262
263 pub fn retype(&mut self, value: Value, ty: Type) {
275 self.values[value.index()].ty = ty;
276 }
277
278 pub fn values(&self) -> impl Iterator<Item = Value> + use<'_> {
283 (0..self.values.len()).map(Idx::from_usize)
284 }
285
286 pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
288 std::iter::successors(self.first_block, move |&block| self.blocks[block.index()].next)
289 }
290
291 pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
293 std::iter::successors(self.blocks[block.index()].first, move |&inst| {
294 self.inst_layout[inst.index()].next
295 })
296 }
297
298 pub fn insts_backwards(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
304 std::iter::successors(self.blocks[block.index()].last, move |&inst| {
305 self.inst_layout[inst.index()].prev
306 })
307 }
308
309 #[must_use]
311 pub fn terminator(&self, block: Block) -> Option<Inst> {
312 self.blocks[block.index()].last.filter(|&inst| self.is_terminator(inst))
313 }
314
315 #[must_use]
321 pub fn is_terminator(&self, inst: Inst) -> bool {
322 let data = &self[inst];
323 match data.extra {
324 Extra::Asm(info) => {
325 data.opcode.is_terminator() || !self.asms[info.index()].targets.is_empty()
326 }
327 _ => data.opcode.is_terminator(),
328 }
329 }
330
331 pub fn create_inst(&mut self, mut data: InstData, results: &[Type], span: Span) -> Inst {
342 let inst = Idx::from_usize(self.insts.len());
343 data.results = u8::try_from(results.len()).expect("an instruction with too many results");
344 data.first_result = results.first().map(|_| Idx::from_usize(self.values.len()));
345 for (index, &ty) in results.iter().enumerate() {
346 let index = u8::try_from(index).expect("checked just above");
347 self.add_value(ValueData { ty, def: Def::Result { inst, index } });
348 }
349 self.insts.push(data);
350 self.inst_layout.push(InstLayout::default());
351 self.inst_spans.push(span);
352 inst
353 }
354
355 pub fn append_inst(&mut self, block: Block, inst: Inst) {
362 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
363 let last = self.blocks[block.index()].last;
364 self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
365 match last {
366 Some(last) => self.inst_layout[last.index()].next = Some(inst),
367 None => self.blocks[block.index()].first = Some(inst),
368 }
369 self.blocks[block.index()].last = Some(inst);
370 }
371
372 pub fn insert_before(&mut self, inst: Inst, before: Inst) {
378 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
379 let at = self.inst_layout[before.index()];
380 let block = at.block.expect("the instruction to insert before is not in a block");
381 self.inst_layout[inst.index()] =
382 InstLayout { block: Some(block), prev: at.prev, next: Some(before) };
383 self.inst_layout[before.index()].prev = Some(inst);
384 match at.prev {
385 Some(prev) => self.inst_layout[prev.index()].next = Some(inst),
386 None => self.blocks[block.index()].first = Some(inst),
387 }
388 }
389
390 pub fn insert_after(&mut self, inst: Inst, after: Inst) {
402 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
403 let at = self.inst_layout[after.index()];
404 let block = at.block.expect("the instruction to insert after is not in a block");
405 assert!(at.next.is_some(), "nothing goes after a terminator");
406 self.inst_layout[inst.index()] =
407 InstLayout { block: Some(block), prev: Some(after), next: at.next };
408 self.inst_layout[after.index()].next = Some(inst);
409 if let Some(next) = at.next {
410 self.inst_layout[next.index()].prev = Some(inst);
411 }
412 }
413
414 pub fn remove_inst(&mut self, inst: Inst) {
424 let at = self.inst_layout[inst.index()];
425 let block = at.block.expect("the instruction is not in a block");
426 match at.prev {
427 Some(prev) => self.inst_layout[prev.index()].next = at.next,
428 None => self.blocks[block.index()].first = at.next,
429 }
430 match at.next {
431 Some(next) => self.inst_layout[next.index()].prev = at.prev,
432 None => self.blocks[block.index()].last = at.prev,
433 }
434 self.inst_layout[inst.index()] = InstLayout::default();
435 }
436
437 #[must_use]
439 pub fn block_of(&self, inst: Inst) -> Option<Block> {
440 self.inst_layout[inst.index()].block
441 }
442
443 #[must_use]
455 pub fn mem_in(&self, inst: Inst) -> Option<Value> {
456 let args = &self[self[inst].args];
457 args.last().copied().filter(|&arg| self[arg].ty.is_mem())
458 }
459
460 #[must_use]
470 pub fn mem_out(&self, inst: Inst) -> Option<Value> {
471 self[inst].results().last().filter(|&result| self[result].ty.is_mem())
472 }
473
474 #[must_use]
484 pub fn bulk(&self, inst: Inst) -> Option<Bulk> {
485 if !matches!(self[inst].opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) {
486 return None;
487 }
488 let all = &self[self[inst].args];
489 let args = &all[..all.len() - usize::from(self.mem_in(inst).is_some())];
490 let [to, with, rest @ ..] = args else { return None };
491 Some(Bulk { to: *to, with: *with, length: rest.first().copied() })
492 }
493
494 #[must_use]
496 pub fn carries_mem(&self, inst: Inst) -> bool {
497 self.mem_in(inst).is_some() || self.mem_out(inst).is_some()
498 }
499
500 pub fn with_mem(&mut self, inst: Inst, incoming: Value) -> Inst {
516 assert!(self[incoming].ty.is_mem(), "the incoming version of memory is not memory");
517 assert!(self[inst].opcode.touches_memory(), "this does not touch memory");
518 assert!(self.mem_in(inst).is_none(), "this is already on the memory chain");
519 let data = self[inst];
520 let mut args = self[data.args].to_vec();
521 args.push(incoming);
522 let mut results: Vec<Type> = data.results().map(|result| self[result].ty).collect();
523 if data.opcode.writes_memory() {
524 results.push(Type::MEM);
525 }
526 let span = self.span(inst);
527 let args = self.push_values(&args);
528 self.create_inst(InstData { args, ..data }, &results, span)
529 }
530
531 pub fn without_mem(&mut self, inst: Inst) -> Inst {
546 assert!(self.carries_mem(inst), "this is not on the memory chain");
547 let data = self[inst];
548 let mut args = self[data.args].to_vec();
549 if self.mem_in(inst).is_some() {
550 args.pop();
551 }
552 let results: Vec<Type> =
553 data.results().map(|result| self[result].ty).filter(|ty| !ty.is_mem()).collect();
554 let span = self.span(inst);
555 let args = self.push_values(&args);
556 self.create_inst(InstData { args, ..data }, &results, span)
557 }
558
559 #[must_use]
561 pub fn span(&self, inst: Inst) -> Span {
562 self.inst_spans[inst.index()]
563 }
564
565 pub fn successors(&self, inst: Inst) -> impl Iterator<Item = BlockCall> + use<'_> {
570 self.block_calls[self.target_list(inst).as_usize_range()].iter().copied()
571 }
572
573 #[must_use]
580 pub fn target_list(&self, inst: Inst) -> BlockCallList {
581 match self[inst].extra {
582 Extra::Targets(targets) => targets,
583 Extra::Switch(info) => self.switches[info.index()].targets,
584 Extra::Asm(info) => self.asms[info.index()].targets,
585 _ => BlockCallList::EMPTY,
586 }
587 }
588
589 pub fn push_values(&mut self, values: &[Value]) -> ValueList {
593 let start = Idx::from_usize(self.value_pool.len());
594 self.value_pool.extend_from_slice(values);
595 ValueList::new(start, Idx::from_usize(self.value_pool.len()))
596 }
597
598 pub fn append_arg(&mut self, list: ValueList, value: Value) -> ValueList {
605 let range = list.as_usize_range();
606 if range.end == self.value_pool.len() {
607 self.value_pool.push(value);
608 return ValueList::new(Idx::from_usize(range.start), Idx::from_usize(range.end + 1));
609 }
610 let start = self.value_pool.len();
611 self.value_pool.extend_from_within(range);
612 self.value_pool.push(value);
613 ValueList::new(Idx::from_usize(start), Idx::from_usize(self.value_pool.len()))
614 }
615
616 pub fn rewrite(&mut self, list: ValueList, mut with: impl FnMut(Value) -> Value) {
621 for value in &mut self.value_pool[list.as_usize_range()] {
622 *value = with(*value);
623 }
624 }
625
626 pub fn push_block_calls(&mut self, calls: &[BlockCall]) -> BlockCallList {
628 let start = Idx::from_usize(self.block_calls.len());
629 self.block_calls.extend_from_slice(calls);
630 BlockCallList::new(start, Idx::from_usize(self.block_calls.len()))
631 }
632
633 pub fn set_block_call(&mut self, at: Idx<BlockCall>, call: BlockCall) {
635 self.block_calls[at.index()] = call;
636 }
637
638 pub fn push_imms(&mut self, imms: &[Imm]) -> ImmList {
640 let start = Idx::from_usize(self.imms.len());
641 self.imms.extend_from_slice(imms);
642 ImmList::new(start, Idx::from_usize(self.imms.len()))
643 }
644
645 pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
647 self.imms.push(imm);
648 Idx::from_usize(self.imms.len() - 1)
649 }
650
651 pub fn push_slots(&mut self, slots: &[Slot]) -> SlotList {
653 let start = Idx::from_usize(self.slots.len());
654 self.slots.extend_from_slice(slots);
655 SlotList::new(start, Idx::from_usize(self.slots.len()))
656 }
657
658 pub fn add_va_object(&mut self, info: VaInfo) -> Idx<VaInfo> {
660 self.va_objects.push(info);
661 Idx::from_usize(self.va_objects.len() - 1)
662 }
663
664 pub fn add_mem(&mut self, info: MemInfo) -> Idx<MemInfo> {
666 self.mem.push(info);
667 Idx::from_usize(self.mem.len() - 1)
668 }
669
670 pub fn push_abis(&mut self, abis: &[Abi]) -> AbiList {
672 let start = Idx::from_usize(self.abis.len());
673 self.abis.extend_from_slice(abis);
674 AbiList::new(start, Idx::from_usize(self.abis.len()))
675 }
676
677 pub fn add_call(&mut self, info: CallInfo) -> Idx<CallInfo> {
679 self.calls.push(info);
680 Idx::from_usize(self.calls.len() - 1)
681 }
682
683 pub fn add_switch(&mut self, info: SwitchInfo) -> Idx<SwitchInfo> {
685 self.switches.push(info);
686 Idx::from_usize(self.switches.len() - 1)
687 }
688
689 pub fn add_asm(&mut self, info: AsmInfo) -> Idx<AsmInfo> {
691 self.asms.push(info);
692 Idx::from_usize(self.asms.len() - 1)
693 }
694
695 #[must_use]
698 pub fn counts(&self) -> Counts {
699 Counts { values: self.values.len(), insts: self.insts.len(), blocks: self.blocks.len() }
700 }
701
702 #[must_use]
708 pub fn facts(&self, value: Value) -> Facts {
709 match self.facts.binary_search_by_key(&value.raw(), |&(at, _)| at.raw()) {
710 Ok(at) => self.facts[at].1,
711 Err(_) => Facts::NONE,
712 }
713 }
714
715 pub fn set_facts(&mut self, value: Value, facts: Facts) {
720 let found = self.facts.binary_search_by_key(&value.raw(), |&(at, _)| at.raw());
721 match (found, facts.is_empty()) {
722 (Ok(at), true) => drop(self.facts.remove(at)),
723 (Ok(at), false) => self.facts[at].1 = facts,
724 (Err(_), true) => {}
725 (Err(at), false) => self.facts.insert(at, (value, facts)),
726 }
727 }
728
729 pub fn known(&self) -> impl Iterator<Item = (Value, Facts)> + '_ {
731 self.facts.iter().copied()
732 }
733
734 pub fn name_block(&mut self, block: Block, name: Symbol) {
748 let found = self.labels.binary_search_by_key(&block.raw(), |&(at, _)| at.raw());
749 if let Err(at) = found {
750 self.labels.insert(at, (block, name));
751 }
752 }
753
754 #[must_use]
756 pub fn block_name(&self, block: Block) -> Option<Symbol> {
757 match self.labels.binary_search_by_key(&block.raw(), |&(at, _)| at.raw()) {
758 Ok(at) => Some(self.labels[at].1),
759 Err(_) => None,
760 }
761 }
762
763 pub fn named_blocks(&self) -> impl Iterator<Item = (Block, Symbol)> + '_ {
765 self.labels.iter().copied()
766 }
767
768 fn add_value(&mut self, data: ValueData) -> Value {
769 self.values.push(data);
770 Idx::from_usize(self.values.len() - 1)
771 }
772}
773
774#[derive(Clone, Copy, Debug, PartialEq, Eq)]
776pub struct Counts {
777 pub values: usize,
779 pub insts: usize,
781 pub blocks: usize,
783}
784
785impl Index<Value> for Func {
788 type Output = ValueData;
789
790 fn index(&self, value: Value) -> &ValueData {
791 &self.values[value.index()]
792 }
793}
794
795impl Index<Inst> for Func {
796 type Output = InstData;
797
798 fn index(&self, inst: Inst) -> &InstData {
799 &self.insts[inst.index()]
800 }
801}
802
803impl IndexMut<Inst> for Func {
804 fn index_mut(&mut self, inst: Inst) -> &mut InstData {
805 &mut self.insts[inst.index()]
806 }
807}
808
809impl Index<Block> for Func {
810 type Output = BlockData;
811
812 fn index(&self, block: Block) -> &BlockData {
813 &self.blocks[block.index()]
814 }
815}
816
817impl Index<Sig> for Func {
818 type Output = Signature;
819
820 fn index(&self, sig: Sig) -> &Signature {
821 &self.signatures[sig.index()]
822 }
823}
824
825impl Index<ValueList> for Func {
826 type Output = [Value];
827
828 fn index(&self, list: ValueList) -> &[Value] {
829 &self.value_pool[list.as_usize_range()]
830 }
831}
832
833impl Index<BlockCallList> for Func {
834 type Output = [BlockCall];
835
836 fn index(&self, list: BlockCallList) -> &[BlockCall] {
837 &self.block_calls[list.as_usize_range()]
838 }
839}
840
841impl Index<Idx<BlockCall>> for Func {
842 type Output = BlockCall;
843
844 fn index(&self, at: Idx<BlockCall>) -> &BlockCall {
845 &self.block_calls[at.index()]
846 }
847}
848
849impl Index<ImmList> for Func {
850 type Output = [Imm];
851
852 fn index(&self, list: ImmList) -> &[Imm] {
853 &self.imms[list.as_usize_range()]
854 }
855}
856
857impl Index<Idx<Imm>> for Func {
858 type Output = Imm;
859
860 fn index(&self, at: Idx<Imm>) -> &Imm {
861 &self.imms[at.index()]
862 }
863}
864
865impl Index<Idx<MemInfo>> for Func {
866 type Output = MemInfo;
867
868 fn index(&self, at: Idx<MemInfo>) -> &MemInfo {
869 &self.mem[at.index()]
870 }
871}
872
873impl Index<AbiList> for Func {
874 type Output = [Abi];
875
876 fn index(&self, list: AbiList) -> &[Abi] {
877 &self.abis[list.as_usize_range()]
878 }
879}
880
881impl Index<SlotList> for Func {
882 type Output = [Slot];
883
884 fn index(&self, list: SlotList) -> &[Slot] {
885 &self.slots[list.as_usize_range()]
886 }
887}
888
889impl Index<Idx<VaInfo>> for Func {
890 type Output = VaInfo;
891
892 fn index(&self, at: Idx<VaInfo>) -> &VaInfo {
893 &self.va_objects[at.index()]
894 }
895}
896
897impl Index<Idx<CallInfo>> for Func {
898 type Output = CallInfo;
899
900 fn index(&self, at: Idx<CallInfo>) -> &CallInfo {
901 &self.calls[at.index()]
902 }
903}
904
905impl Index<Idx<SwitchInfo>> for Func {
906 type Output = SwitchInfo;
907
908 fn index(&self, at: Idx<SwitchInfo>) -> &SwitchInfo {
909 &self.switches[at.index()]
910 }
911}
912
913impl Index<Idx<AsmInfo>> for Func {
914 type Output = AsmInfo;
915
916 fn index(&self, at: Idx<AsmInfo>) -> &AsmInfo {
917 &self.asms[at.index()]
918 }
919}
920
921#[derive(Debug)]
928pub struct Builder<'a> {
929 func: &'a mut Func,
930 block: Block,
931 span: Span,
932}
933
934impl<'a> Builder<'a> {
935 pub fn new(func: &'a mut Func, block: Block) -> Self {
937 Self { func, block, span: Span::DUMMY }
938 }
939
940 #[must_use]
942 pub fn at(mut self, span: Span) -> Self {
943 self.span = span;
944 self
945 }
946
947 pub fn set_span(&mut self, span: Span) {
949 self.span = span;
950 }
951
952 pub fn func(&mut self) -> &mut Func {
954 self.func
955 }
956
957 #[must_use]
959 pub fn block(&self) -> Block {
960 self.block
961 }
962
963 pub fn inst(&mut self, data: InstData, results: &[Type]) -> Inst {
965 let inst = self.func.create_inst(data, results, self.span);
966 self.func.append_inst(self.block, inst);
967 inst
968 }
969
970 pub fn value(&mut self, data: InstData, ty: Type) -> Value {
976 let inst = self.inst(data, &[ty]);
977 self.func[inst].first_result.expect("one result was asked for")
978 }
979
980 pub fn iconst(&mut self, ty: Type, value: i128) -> Value {
986 let imm = self.func.add_imm(Imm::int(value, ty.lane()));
987 self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::IConst) }, ty)
988 }
989
990 pub fn fconst(&mut self, ty: Type, bits: u128) -> Value {
992 let imm = self.func.add_imm(Imm::from_bits(bits));
993 self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::FConst) }, ty)
994 }
995
996 pub fn binary(&mut self, opcode: Opcode, lhs: Value, rhs: Value, flags: Flags) -> Value {
998 let ty = self.func[lhs].ty;
999 let args = self.func.push_values(&[lhs, rhs]);
1000 self.value(InstData { args, flags, ..InstData::new(opcode) }, ty)
1001 }
1002
1003 pub fn checked(&mut self, opcode: Opcode, lhs: Value, rhs: Value) -> (Value, Value) {
1016 let ty = self.func[lhs].ty;
1017 let args = self.func.push_values(&[lhs, rhs]);
1018 let results = [ty, ty.with_lane(Type::I1)];
1019 let inst = self.inst(InstData { args, ..InstData::new(opcode) }, &results);
1020 let mut answers = self.func[inst].results();
1021 let value = answers.next().expect("two results were asked for");
1022 let wrapped = answers.next().expect("two results were asked for");
1023 (value, wrapped)
1024 }
1025
1026 pub fn unary(&mut self, opcode: Opcode, arg: Value, ty: Type) -> Value {
1028 let args = self.func.push_values(&[arg]);
1029 self.value(InstData { args, ..InstData::new(opcode) }, ty)
1030 }
1031
1032 pub fn icmp(&mut self, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1034 let ty = self.func[lhs].ty.with_lane(Type::I1);
1035 let args = self.func.push_values(&[lhs, rhs]);
1036 self.value(
1037 InstData { args, extra: Extra::IntPred(pred), ..InstData::new(Opcode::ICmp) },
1038 ty,
1039 )
1040 }
1041
1042 pub fn select(&mut self, cond: Value, then: Value, other: Value) -> Value {
1048 let ty = self.func[then].ty;
1049 let args = self.func.push_values(&[cond, then, other]);
1050 self.value(InstData { args, ..InstData::new(Opcode::Select) }, ty)
1051 }
1052
1053 pub fn fcmp(&mut self, pred: FloatPred, lhs: Value, rhs: Value, flags: Flags) -> Value {
1055 let ty = self.func[lhs].ty.with_lane(Type::I1);
1056 let args = self.func.push_values(&[lhs, rhs]);
1057 self.value(
1058 InstData { args, flags, extra: Extra::FloatPred(pred), ..InstData::new(Opcode::FCmp) },
1059 ty,
1060 )
1061 }
1062
1063 pub fn mem_entry(&mut self) -> Value {
1067 self.value(InstData::new(Opcode::MemEntry), Type::MEM)
1068 }
1069
1070 pub fn load(&mut self, ty: Type, addr: Value, info: MemInfo, flags: Flags) -> Value {
1072 let mem = self.func.add_mem(info);
1073 let args = self.func.push_values(&[addr]);
1074 self.value(
1075 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Load) },
1076 ty,
1077 )
1078 }
1079
1080 pub fn store(&mut self, value: Value, addr: Value, info: MemInfo, flags: Flags) -> Inst {
1082 let mem = self.func.add_mem(info);
1083 let args = self.func.push_values(&[value, addr]);
1084 self.inst(
1085 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Store) },
1086 &[],
1087 )
1088 }
1089
1090 pub fn atomic_load(&mut self, ty: Type, addr: Value, info: MemInfo, flags: Flags) -> Value {
1098 let mem = self.func.add_mem(info);
1099 let args = self.func.push_values(&[addr]);
1100 self.value(
1101 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::AtomicLoad) },
1102 ty,
1103 )
1104 }
1105
1106 pub fn atomic_store(&mut self, value: Value, addr: Value, info: MemInfo, flags: Flags) -> Inst {
1108 let mem = self.func.add_mem(info);
1109 let args = self.func.push_values(&[value, addr]);
1110 self.inst(
1111 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::AtomicStore) },
1112 &[],
1113 )
1114 }
1115
1116 pub fn cmpxchg(
1123 &mut self,
1124 addr: Value,
1125 expected: Value,
1126 desired: Value,
1127 info: MemInfo,
1128 flags: Flags,
1129 ) -> (Value, Value) {
1130 let ty = self.func[expected].ty;
1131 let mem = self.func.add_mem(info);
1132 let args = self.func.push_values(&[addr, expected, desired]);
1133 let inst = self.inst(
1134 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Cmpxchg) },
1135 &[ty, Type::I1],
1136 );
1137 let results: Vec<Value> = self.func[inst].results().collect();
1138 let [old, exchanged] = results[..] else { unreachable!("two results were asked for") };
1139 (old, exchanged)
1140 }
1141
1142 pub fn atomic_rmw(
1150 &mut self,
1151 op: RmwOp,
1152 addr: Value,
1153 operand: Value,
1154 info: MemInfo,
1155 flags: Flags,
1156 ) -> Value {
1157 let ty = self.func[operand].ty;
1158 let mem = self.func.add_mem(info);
1159 let args = self.func.push_values(&[addr, operand]);
1160 self.value(
1161 InstData {
1162 args,
1163 flags,
1164 extra: Extra::Rmw(op, mem),
1165 ..InstData::new(Opcode::AtomicRmw)
1166 },
1167 ty,
1168 )
1169 }
1170
1171 pub fn fence(&mut self, order: MemOrder) -> Inst {
1173 self.inst(InstData { extra: Extra::Order(order), ..InstData::new(Opcode::Fence) }, &[])
1174 }
1175
1176 pub fn prefetch(&mut self, address: Value, hint: PrefetchHint) -> Inst {
1183 let args = self.func.push_values(&[address]);
1184 self.inst(
1185 InstData { args, extra: Extra::Prefetch(hint), ..InstData::new(Opcode::Prefetch) },
1186 &[],
1187 )
1188 }
1189
1190 pub fn jump(&mut self, target: Block, args: &[Value]) -> Inst {
1192 let call = self.block_call(target, args);
1193 let targets = self.func.push_block_calls(&[call]);
1194 self.inst(InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::Jump) }, &[])
1195 }
1196
1197 pub fn block_addr(&mut self, target: Block) -> Value {
1203 let call = self.block_call(target, &[]);
1204 let targets = self.func.push_block_calls(&[call]);
1205 self.value(
1206 InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::BlockAddr) },
1207 Type::PTR,
1208 )
1209 }
1210
1211 pub fn indirect_br(&mut self, addr: Value, targets: &[Block]) -> Inst {
1217 let calls: Vec<BlockCall> =
1218 targets.iter().map(|&target| self.block_call(target, &[])).collect();
1219 let targets = self.func.push_block_calls(&calls);
1220 let args = self.func.push_values(&[addr]);
1221 self.inst(
1222 InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::IndirectBr) },
1223 &[],
1224 )
1225 }
1226
1227 pub fn br_if(
1229 &mut self,
1230 cond: Value,
1231 then_block: Block,
1232 then_args: &[Value],
1233 else_block: Block,
1234 else_args: &[Value],
1235 ) -> Inst {
1236 let then_call = self.block_call(then_block, then_args);
1237 let else_call = self.block_call(else_block, else_args);
1238 let targets = self.func.push_block_calls(&[then_call, else_call]);
1239 let args = self.func.push_values(&[cond]);
1240 self.inst(
1241 InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::BrIf) },
1242 &[],
1243 )
1244 }
1245
1246 pub fn switch(&mut self, value: Value, default: Block, cases: &[(i128, Block)]) -> Inst {
1253 let ty = self.func[value].ty.lane();
1254 let mut calls = vec![self.block_call(default, &[])];
1255 let mut values = Vec::with_capacity(cases.len());
1256 for &(value, block) in cases {
1257 calls.push(self.block_call(block, &[]));
1258 values.push(Imm::int(value, ty));
1259 }
1260 let targets = self.func.push_block_calls(&calls);
1261 let cases = self.func.push_imms(&values);
1262 let info = self.func.add_switch(SwitchInfo { targets, cases });
1263 let args = self.func.push_values(&[value]);
1264 self.inst(
1265 InstData { args, extra: Extra::Switch(info), ..InstData::new(Opcode::Switch) },
1266 &[],
1267 )
1268 }
1269
1270 pub fn ret(&mut self, values: &[Value]) -> Inst {
1272 let args = self.func.push_values(values);
1273 self.inst(InstData { args, ..InstData::new(Opcode::Return) }, &[])
1274 }
1275
1276 pub fn unreachable(&mut self) -> Inst {
1278 self.inst(InstData::new(Opcode::Unreachable), &[])
1279 }
1280
1281 pub fn call(&mut self, callee: Symbol, signature: Sig, args: &[Value]) -> Inst {
1283 self.call_varargs(callee, signature, args, &[])
1284 }
1285
1286 pub fn call_varargs(
1292 &mut self,
1293 callee: Symbol,
1294 signature: Sig,
1295 args: &[Value],
1296 varargs: &[Abi],
1297 ) -> Inst {
1298 let varargs = self.func.push_abis(varargs);
1299 let info = self.func.add_call(CallInfo { callee: Some(callee), signature, varargs });
1300 let returns: Vec<Type> = self.func[signature].return_types().collect();
1301 let args = self.func.push_values(args);
1302 self.inst(
1303 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::Call) },
1304 &returns,
1305 )
1306 }
1307
1308 pub fn inline_asm(
1314 &mut self,
1315 info: AsmInfo,
1316 args: &[Value],
1317 results: &[Type],
1318 flags: Flags,
1319 ) -> Inst {
1320 let info = self.func.add_asm(info);
1321 let args = self.func.push_values(args);
1322 self.inst(
1323 InstData { args, flags, extra: Extra::Asm(info), ..InstData::new(Opcode::InlineAsm) },
1324 results,
1325 )
1326 }
1327
1328 fn block_call(&mut self, block: Block, args: &[Value]) -> BlockCall {
1329 BlockCall::new(block, self.func.push_values(args))
1330 }
1331}
1332
1333#[cfg(test)]
1334mod tests {
1335 use rucc_base::Interner;
1336
1337 use super::*;
1338 use crate::inst::BlockCallList;
1339 use crate::{MemOrder, Restrict};
1340
1341 fn sum() -> (Func, Block, Block, Block) {
1343 let mut names = Interner::new();
1344 let i32_ = Type::int(32);
1345 let mut func = Func::new(
1346 names.intern("sum"),
1347 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
1348 );
1349
1350 let entry = func.create_block();
1351 let n = func.append_param(entry, i32_);
1352 let header = func.create_block();
1353 let acc = func.append_param(header, i32_);
1354 let i = func.append_param(header, i32_);
1355 let exit = func.create_block();
1356 let result = func.append_param(exit, i32_);
1357
1358 let mut b = Builder::new(&mut func, entry);
1359 let zero = b.iconst(i32_, 0);
1360 let cmp = b.icmp(IntPred::Sle, n, zero);
1361 b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
1362
1363 let mut b = Builder::new(&mut func, header);
1364 let one = b.iconst(i32_, 1);
1365 let next = b.binary(Opcode::Add, i, one, Flags::NSW);
1366 let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
1367 let done = b.icmp(IntPred::Sge, next, n);
1368 b.br_if(done, exit, &[total], header, &[total, next]);
1369
1370 let mut b = Builder::new(&mut func, exit);
1371 b.ret(&[result]);
1372
1373 (func, entry, header, exit)
1374 }
1375
1376 #[test]
1377 fn the_blocks_come_back_in_the_order_they_were_made() {
1378 let (func, entry, header, exit) = sum();
1379 assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, header, exit]);
1380 assert_eq!(func.entry(), Some(entry));
1381 }
1382
1383 #[test]
1384 fn a_removed_block_is_gone_from_the_layout_and_so_is_what_was_in_it() {
1385 let (mut func, entry, header, exit) = sum();
1386 let inside: Vec<Inst> = func.insts(header).collect();
1387 func.remove_block(header);
1388 assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, exit]);
1389 assert_eq!(func.entry(), Some(entry));
1390 assert_eq!(func[entry].next, Some(exit));
1391 assert_eq!(func[exit].prev, Some(entry));
1392 assert!(inside.iter().all(|&inst| func.block_of(inst).is_none()));
1394 assert!(func.insts(header).next().is_none());
1395 }
1396
1397 #[test]
1398 fn each_block_holds_what_was_appended_to_it() {
1399 let (func, entry, header, exit) = sum();
1400 let opcodes =
1401 |block| func.insts(block).map(|inst| func[inst].opcode.name()).collect::<Vec<_>>();
1402 assert_eq!(opcodes(entry), ["iconst", "icmp", "br_if"]);
1403 assert_eq!(opcodes(header), ["iconst", "add", "add", "icmp", "br_if"]);
1404 assert_eq!(opcodes(exit), ["return"]);
1405 }
1406
1407 #[test]
1408 fn asm_ends_a_block_when_it_has_labels_and_not_otherwise() {
1409 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1412 let block = func.create_block();
1413 let plain = func.add_asm(AsmInfo {
1414 template: Symbol::from_raw(0),
1415 constraints: Symbol::from_raw(0),
1416 clobbers: Symbol::from_raw(0),
1417 targets: BlockCallList::EMPTY,
1418 });
1419 let call = BlockCall::to(block);
1420 let targets = func.push_block_calls(&[call]);
1421 let labelled = func.add_asm(AsmInfo {
1422 template: Symbol::from_raw(0),
1423 constraints: Symbol::from_raw(0),
1424 clobbers: Symbol::from_raw(0),
1425 targets,
1426 });
1427
1428 let mut make = |extra| {
1429 let data = InstData { extra, ..InstData::new(Opcode::InlineAsm) };
1430 func.create_inst(data, &[], Span::DUMMY)
1431 };
1432 let plain = make(Extra::Asm(plain));
1433 let labelled = make(Extra::Asm(labelled));
1434 assert!(!func.is_terminator(plain));
1435 assert!(func.is_terminator(labelled));
1436 }
1437
1438 #[test]
1439 fn every_block_ends_in_its_terminator() {
1440 let (func, entry, header, exit) = sum();
1441 for block in [entry, header, exit] {
1442 let last = func.terminator(block).expect("a terminator");
1443 assert_eq!(Some(last), func.insts(block).last());
1444 }
1445 }
1446
1447 #[test]
1448 fn a_branch_carries_the_arguments_the_block_takes() {
1449 let (func, entry, header, _) = sum();
1450 let br = func.terminator(entry).expect("a terminator");
1451 let calls: Vec<BlockCall> = func.successors(br).collect();
1452 assert_eq!(calls.len(), 2);
1453 assert_eq!(calls[1].block, header);
1455 assert_eq!(func[calls[1].args].len(), 2);
1456 assert_eq!(func[header].params.len(), 2);
1457 assert_eq!(func[calls[0].args].len(), 1);
1458 }
1459
1460 #[test]
1461 fn a_value_knows_what_defined_it() {
1462 let (func, entry, _, _) = sum();
1463 let first = func.insts(entry).next().expect("an instruction");
1464 let value = func[first].first_result.expect("a result");
1465 assert_eq!(func[value].def, Def::Result { inst: first, index: 0 });
1466 assert_eq!(func[value].ty, Type::int(32));
1467
1468 let param = func[entry].params[0];
1469 assert_eq!(func[param].def, Def::Param { block: entry, index: 0 });
1470 }
1471
1472 #[test]
1473 fn a_comparison_produces_one_bit() {
1474 let (func, entry, _, _) = sum();
1475 let cmp = func.insts(entry).nth(1).expect("the comparison");
1476 let value = func[cmp].first_result.expect("a result");
1477 assert_eq!(func[value].ty, Type::I1);
1478 assert_eq!(func[cmp].extra, Extra::IntPred(IntPred::Sle));
1479 }
1480
1481 #[test]
1482 fn flags_ride_along_on_the_instruction_that_was_given_them() {
1483 let (func, _, header, _) = sum();
1484 let add = func.insts(header).nth(1).expect("the addition");
1485 assert_eq!(func[add].flags, Flags::NSW);
1486 let cmp = func.insts(header).nth(3).expect("the comparison");
1487 assert_eq!(func[cmp].flags, Flags::NONE);
1488 }
1489
1490 #[test]
1491 fn removing_an_instruction_takes_it_out_of_the_middle() {
1492 let (mut func, _, header, _) = sum();
1493 let add = func.insts(header).nth(1).expect("the addition");
1494 func.remove_inst(add);
1495 let opcodes: Vec<&str> = func.insts(header).map(|inst| func[inst].opcode.name()).collect();
1496 assert_eq!(opcodes, ["iconst", "add", "icmp", "br_if"]);
1497 assert_eq!(func.block_of(add), None);
1498 }
1499
1500 #[test]
1501 fn removing_the_first_and_the_last_keeps_the_ends_right() {
1502 let (mut func, entry, _, _) = sum();
1503 let first = func.insts(entry).next().expect("an instruction");
1504 let last = func.terminator(entry).expect("a terminator");
1505 func.remove_inst(first);
1506 func.remove_inst(last);
1507 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1508 assert_eq!(opcodes, ["icmp"]);
1509 assert_eq!(func[entry].first, func[entry].last);
1510 }
1511
1512 #[test]
1513 fn removing_the_only_instruction_empties_the_block() {
1514 let (mut func, _, _, exit) = sum();
1515 let only = func.insts(exit).next().expect("an instruction");
1516 func.remove_inst(only);
1517 assert_eq!(func.insts(exit).count(), 0);
1518 assert_eq!(func[exit].first, None);
1519 assert_eq!(func[exit].last, None);
1520 }
1521
1522 #[test]
1523 fn inserting_before_puts_it_in_the_right_place() {
1524 let (mut func, entry, _, _) = sum();
1525 let cmp = func.insts(entry).nth(1).expect("the comparison");
1526 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1527 func.insert_before(made, cmp);
1528 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1529 assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
1530 }
1531
1532 #[test]
1533 fn inserting_before_the_first_makes_it_the_first() {
1534 let (mut func, entry, _, _) = sum();
1535 let first = func.insts(entry).next().expect("an instruction");
1536 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1537 func.insert_before(made, first);
1538 assert_eq!(func.insts(entry).next(), Some(made));
1539 assert_eq!(func[entry].first, Some(made));
1540 }
1541
1542 #[test]
1543 fn inserting_after_puts_it_in_the_right_place() {
1544 let (mut func, entry, _, _) = sum();
1545 let first = func.insts(entry).next().expect("an instruction");
1546 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1547 func.insert_after(made, first);
1548 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1549 assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
1550 assert_eq!(func[entry].first, Some(first));
1551 }
1552
1553 #[test]
1554 #[should_panic(expected = "nothing goes after a terminator")]
1555 fn inserting_after_the_terminator_is_refused() {
1556 let (mut func, entry, _, _) = sum();
1559 let last = func.insts(entry).last().expect("a terminator");
1560 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1561 func.insert_after(made, last);
1562 }
1563
1564 #[test]
1565 fn a_list_grows_in_place_while_it_is_the_last_thing_in_the_pool() {
1566 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1567 let block = func.create_block();
1568 let a = func.append_param(block, Type::int(32));
1569 let b = func.append_param(block, Type::int(32));
1570 let list = func.push_values(&[a]);
1571 let grown = func.append_arg(list, b);
1572 assert_eq!(func[grown], [a, b]);
1573 assert_eq!(grown.as_usize_range().start, list.as_usize_range().start);
1574 }
1575
1576 #[test]
1577 fn a_list_is_copied_when_something_is_behind_it() {
1578 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1579 let block = func.create_block();
1580 let a = func.append_param(block, Type::int(32));
1581 let b = func.append_param(block, Type::int(32));
1582 let list = func.push_values(&[a, a]);
1583 let behind = func.push_values(&[b]);
1584 let grown = func.append_arg(list, b);
1585 assert_eq!(func[grown], [a, a, b]);
1586 assert_eq!(func[list], [a, a], "the old run is still readable");
1587 assert_eq!(func[behind], [b], "and so is what was behind it");
1588 assert_ne!(grown.as_usize_range().start, list.as_usize_range().start);
1589 }
1590
1591 #[test]
1592 fn a_parameter_added_late_is_the_next_one_along() {
1593 let (mut func, entry, header, _) = sum();
1597 let extra = func.append_param(header, Type::int(32));
1598 assert_eq!(func[header].params.len(), 3);
1599 assert_eq!(func[extra].def, Def::Param { block: header, index: 2 });
1600
1601 let br = func.terminator(entry).expect("a terminator");
1602 let call = func.successors(br).nth(1).expect("the branch to the header");
1603 let grown = func.append_arg(call.args, extra);
1604 assert_eq!(func[grown].len(), 3);
1605 }
1606
1607 #[test]
1608 fn a_span_rides_along_with_the_instruction() {
1609 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1610 let block = func.create_block();
1611 let span = Span::new(10, 20);
1612 let mut b = Builder::new(&mut func, block).at(span);
1613 let value = b.iconst(Type::int(32), 7);
1614 let inst = match func[value].def {
1615 Def::Result { inst, .. } => inst,
1616 Def::Param { .. } => unreachable!("a constant is not a parameter"),
1617 };
1618 assert_eq!(func.span(inst), span);
1619 }
1620
1621 #[test]
1622 fn a_store_produces_nothing_and_a_load_produces_one_value() {
1623 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1624 let block = func.create_block();
1625 let addr = func.append_param(block, Type::PTR);
1626 let info = MemInfo {
1627 size: 4,
1628 align: 4,
1629 order: MemOrder::NotAtomic,
1630 tbaa: None,
1631 owns: 0,
1632 restrict: Restrict::NONE,
1633 };
1634 let mut b = Builder::new(&mut func, block);
1635 let value = b.load(Type::int(32), addr, info, Flags::NONE);
1636 let store = b.store(value, addr, info, Flags::VOLATILE);
1637 assert_eq!(func[store].results, 0);
1638 assert_eq!(func[store].flags, Flags::VOLATILE);
1639 assert_eq!(func[value].ty, Type::int(32));
1640 }
1641
1642 #[test]
1643 fn a_call_produces_what_its_signature_returns() {
1644 let mut names = Interner::new();
1645 let mut func = Func::new(names.intern("caller"), Signature::new());
1646 let sig = func.add_signature(
1647 Signature::new().with_params(&[Type::int(32)]).with_returns(&[Type::int(64)]),
1648 );
1649 let block = func.create_block();
1650 let arg = func.append_param(block, Type::int(32));
1651 let callee = names.intern("callee");
1652 let mut b = Builder::new(&mut func, block);
1653 let call = b.call(callee, sig, &[arg]);
1654 assert_eq!(func[call].results, 1);
1655 let value = func[call].first_result.expect("a result");
1656 assert_eq!(func[value].ty, Type::int(64));
1657 assert_eq!(func[call].extra, Extra::Call(Idx::new(0)));
1658 }
1659
1660 #[test]
1661 fn the_counts_are_what_was_made() {
1662 let (func, _, _, _) = sum();
1663 let counts = func.counts();
1664 assert_eq!(counts.blocks, 3);
1665 assert_eq!(counts.insts, 9);
1666 assert_eq!(counts.values, 4 + 6);
1669 }
1670
1671 #[test]
1672 #[should_panic(expected = "the instruction is in a block")]
1673 fn appending_an_instruction_twice_is_refused() {
1674 let (mut func, entry, _, _) = sum();
1675 let first = func.insts(entry).next().expect("an instruction");
1676 func.append_inst(entry, first);
1677 }
1678
1679 #[test]
1680 #[should_panic(expected = "the instruction is not in a block")]
1681 fn removing_an_instruction_twice_is_refused() {
1682 let (mut func, entry, _, _) = sum();
1683 let first = func.insts(entry).next().expect("an instruction");
1684 func.remove_inst(first);
1685 func.remove_inst(first);
1686 }
1687
1688 fn threaded() -> (Func, Inst, Inst) {
1690 let mut names = Interner::new();
1691 let i32_ = Type::int(32);
1692 let mut func = Func::new(
1693 names.intern("thread"),
1694 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1695 );
1696 let entry = func.create_block();
1697 let addr = func.append_param(entry, Type::PTR);
1698 let info = MemInfo {
1699 size: 4,
1700 align: 4,
1701 order: MemOrder::NotAtomic,
1702 tbaa: None,
1703 owns: 0,
1704 restrict: Restrict::NONE,
1705 };
1706
1707 let mut b = Builder::new(&mut func, entry);
1708 let start = b.mem_entry();
1709 let seven = b.iconst(i32_, 7);
1710 let store = b.store(seven, addr, info, Flags::NONE);
1711 let value = b.load(i32_, addr, info, Flags::NONE);
1712 let Def::Result { inst: load, .. } = func[value].def else {
1713 panic!("the load produced it");
1714 };
1715
1716 let store = func.with_mem(store, start);
1717 let after = func.mem_out(store).expect("a store makes a new version");
1718 let load = func.with_mem(load, after);
1719 (func, store, load)
1720 }
1721
1722 #[test]
1723 fn threading_memory_puts_it_last_and_leaves_everything_else_where_it_was() {
1724 let (func, store, load) = threaded();
1725 assert_eq!(func.mem_in(store), func.mem_out(store).map(|_| func[func[store].args][2]));
1726 assert_eq!(func[func[store].args].len(), 3);
1727 assert!(func.carries_mem(store));
1728 assert!(func.carries_mem(load));
1729
1730 assert_eq!(func[func[load].args][0], func[func.entry().expect("an entry")].params[0]);
1733 assert_eq!(func.mem_in(load), func.mem_out(store));
1734 assert_eq!(func.mem_out(load), None);
1735 }
1736
1737 #[test]
1738 #[should_panic(expected = "this is already on the memory chain")]
1739 fn threading_memory_through_the_same_instruction_twice_is_refused() {
1740 let (mut func, store, _) = threaded();
1741 let start = func.mem_in(store).expect("it was threaded");
1742 func.with_mem(store, start);
1743 }
1744
1745 fn copies() -> (Func, Inst, Inst) {
1748 let mut names = Interner::new();
1749 let i64_ = Type::int(64);
1750 let mut func = Func::new(
1751 names.intern("copies"),
1752 Signature::new().with_params(&[Type::PTR, Type::PTR, i64_]),
1753 );
1754 let entry = func.create_block();
1755 let to = func.append_param(entry, Type::PTR);
1756 let from = func.append_param(entry, Type::PTR);
1757 let length = func.append_param(entry, i64_);
1758 let info = MemInfo {
1759 size: 16,
1760 align: 4,
1761 order: MemOrder::NotAtomic,
1762 tbaa: None,
1763 owns: 0,
1764 restrict: Restrict::NONE,
1765 };
1766
1767 let mut b = Builder::new(&mut func, entry);
1768 let start = b.mem_entry();
1769 let mem = b.func().add_mem(info);
1770 let args = b.func().push_values(&[to, from]);
1771 let fixed =
1772 b.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1773 let mem = b.func().add_mem(MemInfo { size: 0, ..info });
1774 let args = b.func().push_values(&[to, from, length]);
1775 let computed =
1776 b.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1777 b.ret(&[]);
1778
1779 let fixed = func.with_mem(fixed, start);
1780 let after = func.mem_out(fixed).expect("a copy makes a new version");
1781 let computed = func.with_mem(computed, after);
1782 (func, fixed, computed)
1783 }
1784
1785 #[test]
1786 fn a_bulk_copy_hands_back_its_length_where_it_has_one_and_nothing_where_the_payload_has_it() {
1787 let (func, fixed, computed) = copies();
1788 let params = &func[func.entry().expect("an entry")].params;
1789 let [to, from, length] = params[..] else { panic!("three of them were appended") };
1790
1791 let bulk = func.bulk(fixed).expect("a memcpy is one");
1792 assert_eq!((bulk.to, bulk.with, bulk.length), (to, from, None));
1793
1794 let bulk = func.bulk(computed).expect("a memcpy is one");
1795 assert_eq!((bulk.to, bulk.with, bulk.length), (to, from, Some(length)));
1796 }
1797
1798 #[test]
1799 fn an_instruction_that_is_not_a_bulk_operation_is_not_taken_apart_as_one() {
1800 let (func, store, load) = threaded();
1801 assert_eq!(func.bulk(store), None);
1802 assert_eq!(func.bulk(load), None);
1803 }
1804}