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 pub declared: Span,
81
82 values: Vec<ValueData>,
83 insts: Vec<InstData>,
84 inst_layout: Vec<InstLayout>,
85 inst_spans: Vec<Span>,
86 blocks: Vec<BlockData>,
87
88 value_pool: Vec<Value>,
89 block_calls: Vec<BlockCall>,
90 imms: Vec<Imm>,
91 mem: Vec<MemInfo>,
92 calls: Vec<CallInfo>,
93 abis: Vec<Abi>,
94 switches: Vec<SwitchInfo>,
95 asms: Vec<AsmInfo>,
96 slots: Vec<Slot>,
97 va_objects: Vec<VaInfo>,
98 signatures: Vec<Signature>,
99 facts: Vec<(Value, Facts)>,
100 labels: Vec<(Block, Symbol)>,
101
102 first_block: Option<Block>,
103 last_block: Option<Block>,
104}
105
106impl Func {
107 #[must_use]
114 pub fn new(name: Symbol, signature: Signature) -> Self {
115 Self {
116 name,
117 linkage: Linkage::External,
118 visibility: Visibility::Default,
119 section: None,
120 align: None,
121 attrs: Attrs::NONE,
122 declared: Span::DUMMY,
123 values: Vec::new(),
124 insts: Vec::new(),
125 inst_layout: Vec::new(),
126 inst_spans: Vec::new(),
127 blocks: Vec::new(),
128 value_pool: Vec::new(),
129 block_calls: Vec::new(),
130 imms: Vec::new(),
131 mem: Vec::new(),
132 calls: Vec::new(),
133 abis: Vec::new(),
134 switches: Vec::new(),
135 asms: Vec::new(),
136 slots: Vec::new(),
137 va_objects: Vec::new(),
138 signatures: vec![signature],
139 facts: Vec::new(),
140 labels: Vec::new(),
141 first_block: None,
142 last_block: None,
143 }
144 }
145
146 #[must_use]
148 pub fn signature(&self) -> &Signature {
149 &self.signatures[0]
150 }
151
152 pub fn set_signature(&mut self, signature: Signature) {
164 self.signatures[0] = signature;
165 }
166
167 pub fn signatures(&self) -> impl Iterator<Item = &Signature> {
169 self.signatures.iter()
170 }
171
172 pub fn add_signature(&mut self, signature: Signature) -> Sig {
174 self.signatures.push(signature);
175 Idx::from_usize(self.signatures.len() - 1)
176 }
177
178 #[must_use]
183 pub fn entry(&self) -> Option<Block> {
184 self.first_block
185 }
186
187 #[must_use]
194 pub fn is_declaration(&self) -> bool {
195 self.first_block.is_none()
196 }
197
198 pub fn create_block(&mut self) -> Block {
202 let block = Idx::from_usize(self.blocks.len());
203 self.blocks.push(BlockData { prev: self.last_block, ..BlockData::default() });
204 match self.last_block {
205 Some(last) => self.blocks[last.index()].next = Some(block),
206 None => self.first_block = Some(block),
207 }
208 self.last_block = Some(block);
209 block
210 }
211
212 pub fn remove_block(&mut self, block: Block) {
225 assert!(self.first_block != Some(block), "the entry block is not removable");
226 let (prev, next) = (self.blocks[block.index()].prev, self.blocks[block.index()].next);
227 match prev {
228 Some(prev) => self.blocks[prev.index()].next = next,
229 None => self.first_block = next,
230 }
231 match next {
232 Some(next) => self.blocks[next.index()].prev = prev,
233 None => self.last_block = prev,
234 }
235 let insts: Vec<Inst> = self.insts(block).collect();
239 for inst in insts {
240 self.inst_layout[inst.index()] = InstLayout::default();
241 }
242 self.blocks[block.index()] = BlockData::default();
243 }
244
245 pub fn append_param(&mut self, block: Block, ty: Type) -> Value {
254 let index = u32::try_from(self.blocks[block.index()].params.len())
255 .expect("a block with four billion parameters");
256 let value = self.add_value(ValueData { ty, def: Def::Param { block, index } });
257 self.blocks[block.index()].params.push(value);
258 value
259 }
260
261 pub fn retain_params(&mut self, block: Block, mut keep: impl FnMut(Value) -> bool) {
273 let mut params = std::mem::take(&mut self.blocks[block.index()].params);
274 params.retain(|&value| keep(value));
275 for (index, &value) in params.iter().enumerate() {
276 let index = u32::try_from(index).expect("a block with four billion parameters");
277 self.values[value.index()].def = Def::Param { block, index };
278 }
279 self.blocks[block.index()].params = params;
280 }
281
282 pub fn retype(&mut self, value: Value, ty: Type) {
294 self.values[value.index()].ty = ty;
295 }
296
297 pub fn values(&self) -> impl Iterator<Item = Value> + use<'_> {
302 (0..self.values.len()).map(Idx::from_usize)
303 }
304
305 pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
307 std::iter::successors(self.first_block, move |&block| self.blocks[block.index()].next)
308 }
309
310 pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
312 std::iter::successors(self.blocks[block.index()].first, move |&inst| {
313 self.inst_layout[inst.index()].next
314 })
315 }
316
317 pub fn insts_backwards(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
323 std::iter::successors(self.blocks[block.index()].last, move |&inst| {
324 self.inst_layout[inst.index()].prev
325 })
326 }
327
328 #[must_use]
330 pub fn terminator(&self, block: Block) -> Option<Inst> {
331 self.blocks[block.index()].last.filter(|&inst| self.is_terminator(inst))
332 }
333
334 #[must_use]
340 pub fn is_terminator(&self, inst: Inst) -> bool {
341 let data = &self[inst];
342 match data.extra {
343 Extra::Asm(info) => {
344 data.opcode.is_terminator() || !self.asms[info.index()].targets.is_empty()
345 }
346 _ => data.opcode.is_terminator(),
347 }
348 }
349
350 pub fn create_inst(&mut self, mut data: InstData, results: &[Type], span: Span) -> Inst {
361 let inst = Idx::from_usize(self.insts.len());
362 data.results = u8::try_from(results.len()).expect("an instruction with too many results");
363 data.first_result = results.first().map(|_| Idx::from_usize(self.values.len()));
364 for (index, &ty) in results.iter().enumerate() {
365 let index = u8::try_from(index).expect("checked just above");
366 self.add_value(ValueData { ty, def: Def::Result { inst, index } });
367 }
368 self.insts.push(data);
369 self.inst_layout.push(InstLayout::default());
370 self.inst_spans.push(span);
371 inst
372 }
373
374 pub fn drop_results(&mut self, inst: Inst, drop: u8) {
390 let data = &self.insts[inst.index()];
391 assert!(drop <= data.results, "the instruction does not produce that many results");
392 let left = data.results - drop;
393 let first = data.first_result.map_or(0, Idx::raw) + u32::from(drop);
394 let data = &mut self.insts[inst.index()];
395 data.results = left;
396 data.first_result = (left > 0).then(|| Value::new(first));
397 for offset in 0..u32::from(left) {
398 let index = u8::try_from(offset).expect("no more than the count it came from");
399 let value = Value::new(first + offset);
400 self.values[value.index()].def = Def::Result { inst, index };
401 }
402 }
403
404 pub fn append_inst(&mut self, block: Block, inst: Inst) {
411 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
412 let last = self.blocks[block.index()].last;
413 self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
414 match last {
415 Some(last) => self.inst_layout[last.index()].next = Some(inst),
416 None => self.blocks[block.index()].first = Some(inst),
417 }
418 self.blocks[block.index()].last = Some(inst);
419 }
420
421 pub fn insert_before(&mut self, inst: Inst, before: Inst) {
427 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
428 let at = self.inst_layout[before.index()];
429 let block = at.block.expect("the instruction to insert before is not in a block");
430 self.inst_layout[inst.index()] =
431 InstLayout { block: Some(block), prev: at.prev, next: Some(before) };
432 self.inst_layout[before.index()].prev = Some(inst);
433 match at.prev {
434 Some(prev) => self.inst_layout[prev.index()].next = Some(inst),
435 None => self.blocks[block.index()].first = Some(inst),
436 }
437 }
438
439 pub fn insert_after(&mut self, inst: Inst, after: Inst) {
451 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
452 let at = self.inst_layout[after.index()];
453 let block = at.block.expect("the instruction to insert after is not in a block");
454 assert!(at.next.is_some(), "nothing goes after a terminator");
455 self.inst_layout[inst.index()] =
456 InstLayout { block: Some(block), prev: Some(after), next: at.next };
457 self.inst_layout[after.index()].next = Some(inst);
458 if let Some(next) = at.next {
459 self.inst_layout[next.index()].prev = Some(inst);
460 }
461 }
462
463 pub fn remove_inst(&mut self, inst: Inst) {
473 let at = self.inst_layout[inst.index()];
474 let block = at.block.expect("the instruction is not in a block");
475 match at.prev {
476 Some(prev) => self.inst_layout[prev.index()].next = at.next,
477 None => self.blocks[block.index()].first = at.next,
478 }
479 match at.next {
480 Some(next) => self.inst_layout[next.index()].prev = at.prev,
481 None => self.blocks[block.index()].last = at.prev,
482 }
483 self.inst_layout[inst.index()] = InstLayout::default();
484 }
485
486 #[must_use]
488 pub fn block_of(&self, inst: Inst) -> Option<Block> {
489 self.inst_layout[inst.index()].block
490 }
491
492 #[must_use]
504 pub fn mem_in(&self, inst: Inst) -> Option<Value> {
505 let args = &self[self[inst].args];
506 args.last().copied().filter(|&arg| self[arg].ty.is_mem())
507 }
508
509 #[must_use]
519 pub fn mem_out(&self, inst: Inst) -> Option<Value> {
520 self[inst].results().last().filter(|&result| self[result].ty.is_mem())
521 }
522
523 #[must_use]
533 pub fn bulk(&self, inst: Inst) -> Option<Bulk> {
534 if !matches!(self[inst].opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) {
535 return None;
536 }
537 let all = &self[self[inst].args];
538 let args = &all[..all.len() - usize::from(self.mem_in(inst).is_some())];
539 let [to, with, rest @ ..] = args else { return None };
540 Some(Bulk { to: *to, with: *with, length: rest.first().copied() })
541 }
542
543 #[must_use]
545 pub fn carries_mem(&self, inst: Inst) -> bool {
546 self.mem_in(inst).is_some() || self.mem_out(inst).is_some()
547 }
548
549 pub fn with_mem(&mut self, inst: Inst, incoming: Value) -> Inst {
565 assert!(self[incoming].ty.is_mem(), "the incoming version of memory is not memory");
566 assert!(self[inst].opcode.touches_memory(), "this does not touch memory");
567 assert!(self.mem_in(inst).is_none(), "this is already on the memory chain");
568 let data = self[inst];
569 let mut args = self[data.args].to_vec();
570 args.push(incoming);
571 let mut results: Vec<Type> = data.results().map(|result| self[result].ty).collect();
572 if data.opcode.writes_memory() {
573 results.push(Type::MEM);
574 }
575 let span = self.span(inst);
576 let args = self.push_values(&args);
577 self.create_inst(InstData { args, ..data }, &results, span)
578 }
579
580 pub fn without_mem(&mut self, inst: Inst) -> Inst {
595 assert!(self.carries_mem(inst), "this is not on the memory chain");
596 let data = self[inst];
597 let mut args = self[data.args].to_vec();
598 if self.mem_in(inst).is_some() {
599 args.pop();
600 }
601 let results: Vec<Type> =
602 data.results().map(|result| self[result].ty).filter(|ty| !ty.is_mem()).collect();
603 let span = self.span(inst);
604 let args = self.push_values(&args);
605 self.create_inst(InstData { args, ..data }, &results, span)
606 }
607
608 #[must_use]
610 pub fn span(&self, inst: Inst) -> Span {
611 self.inst_spans[inst.index()]
612 }
613
614 pub fn successors(&self, inst: Inst) -> impl Iterator<Item = BlockCall> + use<'_> {
619 self.block_calls[self.target_list(inst).as_usize_range()].iter().copied()
620 }
621
622 #[must_use]
629 pub fn target_list(&self, inst: Inst) -> BlockCallList {
630 match self[inst].extra {
631 Extra::Targets(targets) => targets,
632 Extra::Switch(info) => self.switches[info.index()].targets,
633 Extra::Asm(info) => self.asms[info.index()].targets,
634 _ => BlockCallList::EMPTY,
635 }
636 }
637
638 pub fn push_values(&mut self, values: &[Value]) -> ValueList {
642 let start = Idx::from_usize(self.value_pool.len());
643 self.value_pool.extend_from_slice(values);
644 ValueList::new(start, Idx::from_usize(self.value_pool.len()))
645 }
646
647 pub fn append_arg(&mut self, list: ValueList, value: Value) -> ValueList {
654 let range = list.as_usize_range();
655 if range.end == self.value_pool.len() {
656 self.value_pool.push(value);
657 return ValueList::new(Idx::from_usize(range.start), Idx::from_usize(range.end + 1));
658 }
659 let start = self.value_pool.len();
660 self.value_pool.extend_from_within(range);
661 self.value_pool.push(value);
662 ValueList::new(Idx::from_usize(start), Idx::from_usize(self.value_pool.len()))
663 }
664
665 pub fn rewrite(&mut self, list: ValueList, mut with: impl FnMut(Value) -> Value) {
670 for value in &mut self.value_pool[list.as_usize_range()] {
671 *value = with(*value);
672 }
673 }
674
675 pub fn push_block_calls(&mut self, calls: &[BlockCall]) -> BlockCallList {
677 let start = Idx::from_usize(self.block_calls.len());
678 self.block_calls.extend_from_slice(calls);
679 BlockCallList::new(start, Idx::from_usize(self.block_calls.len()))
680 }
681
682 pub fn set_block_call(&mut self, at: Idx<BlockCall>, call: BlockCall) {
684 self.block_calls[at.index()] = call;
685 }
686
687 pub fn push_imms(&mut self, imms: &[Imm]) -> ImmList {
689 let start = Idx::from_usize(self.imms.len());
690 self.imms.extend_from_slice(imms);
691 ImmList::new(start, Idx::from_usize(self.imms.len()))
692 }
693
694 pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
696 self.imms.push(imm);
697 Idx::from_usize(self.imms.len() - 1)
698 }
699
700 pub fn push_slots(&mut self, slots: &[Slot]) -> SlotList {
702 let start = Idx::from_usize(self.slots.len());
703 self.slots.extend_from_slice(slots);
704 SlotList::new(start, Idx::from_usize(self.slots.len()))
705 }
706
707 pub fn add_va_object(&mut self, info: VaInfo) -> Idx<VaInfo> {
709 self.va_objects.push(info);
710 Idx::from_usize(self.va_objects.len() - 1)
711 }
712
713 pub fn add_mem(&mut self, info: MemInfo) -> Idx<MemInfo> {
715 self.mem.push(info);
716 Idx::from_usize(self.mem.len() - 1)
717 }
718
719 pub fn push_abis(&mut self, abis: &[Abi]) -> AbiList {
721 let start = Idx::from_usize(self.abis.len());
722 self.abis.extend_from_slice(abis);
723 AbiList::new(start, Idx::from_usize(self.abis.len()))
724 }
725
726 pub fn add_call(&mut self, info: CallInfo) -> Idx<CallInfo> {
728 self.calls.push(info);
729 Idx::from_usize(self.calls.len() - 1)
730 }
731
732 pub fn add_switch(&mut self, info: SwitchInfo) -> Idx<SwitchInfo> {
734 self.switches.push(info);
735 Idx::from_usize(self.switches.len() - 1)
736 }
737
738 pub fn add_asm(&mut self, info: AsmInfo) -> Idx<AsmInfo> {
740 self.asms.push(info);
741 Idx::from_usize(self.asms.len() - 1)
742 }
743
744 #[must_use]
747 pub fn counts(&self) -> Counts {
748 Counts { values: self.values.len(), insts: self.insts.len(), blocks: self.blocks.len() }
749 }
750
751 #[must_use]
757 pub fn facts(&self, value: Value) -> Facts {
758 match self.facts.binary_search_by_key(&value.raw(), |&(at, _)| at.raw()) {
759 Ok(at) => self.facts[at].1,
760 Err(_) => Facts::NONE,
761 }
762 }
763
764 pub fn set_facts(&mut self, value: Value, facts: Facts) {
769 let found = self.facts.binary_search_by_key(&value.raw(), |&(at, _)| at.raw());
770 match (found, facts.is_empty()) {
771 (Ok(at), true) => drop(self.facts.remove(at)),
772 (Ok(at), false) => self.facts[at].1 = facts,
773 (Err(_), true) => {}
774 (Err(at), false) => self.facts.insert(at, (value, facts)),
775 }
776 }
777
778 pub fn known(&self) -> impl Iterator<Item = (Value, Facts)> + '_ {
780 self.facts.iter().copied()
781 }
782
783 pub fn name_block(&mut self, block: Block, name: Symbol) {
797 let found = self.labels.binary_search_by_key(&block.raw(), |&(at, _)| at.raw());
798 if let Err(at) = found {
799 self.labels.insert(at, (block, name));
800 }
801 }
802
803 #[must_use]
805 pub fn block_name(&self, block: Block) -> Option<Symbol> {
806 match self.labels.binary_search_by_key(&block.raw(), |&(at, _)| at.raw()) {
807 Ok(at) => Some(self.labels[at].1),
808 Err(_) => None,
809 }
810 }
811
812 pub fn named_blocks(&self) -> impl Iterator<Item = (Block, Symbol)> + '_ {
814 self.labels.iter().copied()
815 }
816
817 fn add_value(&mut self, data: ValueData) -> Value {
818 self.values.push(data);
819 Idx::from_usize(self.values.len() - 1)
820 }
821}
822
823#[derive(Clone, Copy, Debug, PartialEq, Eq)]
825pub struct Counts {
826 pub values: usize,
828 pub insts: usize,
830 pub blocks: usize,
832}
833
834impl Index<Value> for Func {
837 type Output = ValueData;
838
839 fn index(&self, value: Value) -> &ValueData {
840 &self.values[value.index()]
841 }
842}
843
844impl Index<Inst> for Func {
845 type Output = InstData;
846
847 fn index(&self, inst: Inst) -> &InstData {
848 &self.insts[inst.index()]
849 }
850}
851
852impl IndexMut<Inst> for Func {
853 fn index_mut(&mut self, inst: Inst) -> &mut InstData {
854 &mut self.insts[inst.index()]
855 }
856}
857
858impl Index<Block> for Func {
859 type Output = BlockData;
860
861 fn index(&self, block: Block) -> &BlockData {
862 &self.blocks[block.index()]
863 }
864}
865
866impl Index<Sig> for Func {
867 type Output = Signature;
868
869 fn index(&self, sig: Sig) -> &Signature {
870 &self.signatures[sig.index()]
871 }
872}
873
874impl Index<ValueList> for Func {
875 type Output = [Value];
876
877 fn index(&self, list: ValueList) -> &[Value] {
878 &self.value_pool[list.as_usize_range()]
879 }
880}
881
882impl Index<BlockCallList> for Func {
883 type Output = [BlockCall];
884
885 fn index(&self, list: BlockCallList) -> &[BlockCall] {
886 &self.block_calls[list.as_usize_range()]
887 }
888}
889
890impl Index<Idx<BlockCall>> for Func {
891 type Output = BlockCall;
892
893 fn index(&self, at: Idx<BlockCall>) -> &BlockCall {
894 &self.block_calls[at.index()]
895 }
896}
897
898impl Index<ImmList> for Func {
899 type Output = [Imm];
900
901 fn index(&self, list: ImmList) -> &[Imm] {
902 &self.imms[list.as_usize_range()]
903 }
904}
905
906impl Index<Idx<Imm>> for Func {
907 type Output = Imm;
908
909 fn index(&self, at: Idx<Imm>) -> &Imm {
910 &self.imms[at.index()]
911 }
912}
913
914impl Index<Idx<MemInfo>> for Func {
915 type Output = MemInfo;
916
917 fn index(&self, at: Idx<MemInfo>) -> &MemInfo {
918 &self.mem[at.index()]
919 }
920}
921
922impl Index<AbiList> for Func {
923 type Output = [Abi];
924
925 fn index(&self, list: AbiList) -> &[Abi] {
926 &self.abis[list.as_usize_range()]
927 }
928}
929
930impl Index<SlotList> for Func {
931 type Output = [Slot];
932
933 fn index(&self, list: SlotList) -> &[Slot] {
934 &self.slots[list.as_usize_range()]
935 }
936}
937
938impl Index<Idx<VaInfo>> for Func {
939 type Output = VaInfo;
940
941 fn index(&self, at: Idx<VaInfo>) -> &VaInfo {
942 &self.va_objects[at.index()]
943 }
944}
945
946impl Index<Idx<CallInfo>> for Func {
947 type Output = CallInfo;
948
949 fn index(&self, at: Idx<CallInfo>) -> &CallInfo {
950 &self.calls[at.index()]
951 }
952}
953
954impl Index<Idx<SwitchInfo>> for Func {
955 type Output = SwitchInfo;
956
957 fn index(&self, at: Idx<SwitchInfo>) -> &SwitchInfo {
958 &self.switches[at.index()]
959 }
960}
961
962impl Index<Idx<AsmInfo>> for Func {
963 type Output = AsmInfo;
964
965 fn index(&self, at: Idx<AsmInfo>) -> &AsmInfo {
966 &self.asms[at.index()]
967 }
968}
969
970#[derive(Debug)]
977pub struct Builder<'a> {
978 func: &'a mut Func,
979 block: Block,
980 span: Span,
981}
982
983impl<'a> Builder<'a> {
984 pub fn new(func: &'a mut Func, block: Block) -> Self {
986 Self { func, block, span: Span::DUMMY }
987 }
988
989 #[must_use]
991 pub fn at(mut self, span: Span) -> Self {
992 self.span = span;
993 self
994 }
995
996 pub fn set_span(&mut self, span: Span) {
998 self.span = span;
999 }
1000
1001 pub fn func(&mut self) -> &mut Func {
1003 self.func
1004 }
1005
1006 #[must_use]
1008 pub fn block(&self) -> Block {
1009 self.block
1010 }
1011
1012 pub fn inst(&mut self, data: InstData, results: &[Type]) -> Inst {
1014 let inst = self.func.create_inst(data, results, self.span);
1015 self.func.append_inst(self.block, inst);
1016 inst
1017 }
1018
1019 pub fn value(&mut self, data: InstData, ty: Type) -> Value {
1025 let inst = self.inst(data, &[ty]);
1026 self.func[inst].first_result.expect("one result was asked for")
1027 }
1028
1029 pub fn iconst(&mut self, ty: Type, value: i128) -> Value {
1035 let imm = self.func.add_imm(Imm::int(value, ty.lane()));
1036 self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::IConst) }, ty)
1037 }
1038
1039 pub fn fconst(&mut self, ty: Type, bits: u128) -> Value {
1041 let imm = self.func.add_imm(Imm::from_bits(bits));
1042 self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::FConst) }, ty)
1043 }
1044
1045 pub fn binary(&mut self, opcode: Opcode, lhs: Value, rhs: Value, flags: Flags) -> Value {
1047 let ty = self.func[lhs].ty;
1048 let args = self.func.push_values(&[lhs, rhs]);
1049 self.value(InstData { args, flags, ..InstData::new(opcode) }, ty)
1050 }
1051
1052 pub fn checked(&mut self, opcode: Opcode, lhs: Value, rhs: Value) -> (Value, Value) {
1065 let ty = self.func[lhs].ty;
1066 let args = self.func.push_values(&[lhs, rhs]);
1067 let results = [ty, ty.with_lane(Type::I1)];
1068 let inst = self.inst(InstData { args, ..InstData::new(opcode) }, &results);
1069 let mut answers = self.func[inst].results();
1070 let value = answers.next().expect("two results were asked for");
1071 let wrapped = answers.next().expect("two results were asked for");
1072 (value, wrapped)
1073 }
1074
1075 pub fn unary(&mut self, opcode: Opcode, arg: Value, ty: Type) -> Value {
1077 let args = self.func.push_values(&[arg]);
1078 self.value(InstData { args, ..InstData::new(opcode) }, ty)
1079 }
1080
1081 pub fn icmp(&mut self, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1083 let ty = self.func[lhs].ty.with_lane(Type::I1);
1084 let args = self.func.push_values(&[lhs, rhs]);
1085 self.value(
1086 InstData { args, extra: Extra::IntPred(pred), ..InstData::new(Opcode::ICmp) },
1087 ty,
1088 )
1089 }
1090
1091 pub fn select(&mut self, cond: Value, then: Value, other: Value) -> Value {
1097 let ty = self.func[then].ty;
1098 let args = self.func.push_values(&[cond, then, other]);
1099 self.value(InstData { args, ..InstData::new(Opcode::Select) }, ty)
1100 }
1101
1102 pub fn fcmp(&mut self, pred: FloatPred, lhs: Value, rhs: Value, flags: Flags) -> Value {
1104 let ty = self.func[lhs].ty.with_lane(Type::I1);
1105 let args = self.func.push_values(&[lhs, rhs]);
1106 self.value(
1107 InstData { args, flags, extra: Extra::FloatPred(pred), ..InstData::new(Opcode::FCmp) },
1108 ty,
1109 )
1110 }
1111
1112 pub fn mem_entry(&mut self) -> Value {
1116 self.value(InstData::new(Opcode::MemEntry), Type::MEM)
1117 }
1118
1119 pub fn load(&mut self, ty: Type, addr: Value, info: MemInfo, flags: Flags) -> Value {
1121 let mem = self.func.add_mem(info);
1122 let args = self.func.push_values(&[addr]);
1123 self.value(
1124 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Load) },
1125 ty,
1126 )
1127 }
1128
1129 pub fn store(&mut self, value: Value, addr: Value, info: MemInfo, flags: Flags) -> Inst {
1131 let mem = self.func.add_mem(info);
1132 let args = self.func.push_values(&[value, addr]);
1133 self.inst(
1134 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Store) },
1135 &[],
1136 )
1137 }
1138
1139 pub fn atomic_load(&mut self, ty: Type, addr: Value, info: MemInfo, flags: Flags) -> Value {
1147 let mem = self.func.add_mem(info);
1148 let args = self.func.push_values(&[addr]);
1149 self.value(
1150 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::AtomicLoad) },
1151 ty,
1152 )
1153 }
1154
1155 pub fn atomic_store(&mut self, value: Value, addr: Value, info: MemInfo, flags: Flags) -> Inst {
1157 let mem = self.func.add_mem(info);
1158 let args = self.func.push_values(&[value, addr]);
1159 self.inst(
1160 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::AtomicStore) },
1161 &[],
1162 )
1163 }
1164
1165 pub fn cmpxchg(
1172 &mut self,
1173 addr: Value,
1174 expected: Value,
1175 desired: Value,
1176 info: MemInfo,
1177 flags: Flags,
1178 ) -> (Value, Value) {
1179 let ty = self.func[expected].ty;
1180 let mem = self.func.add_mem(info);
1181 let args = self.func.push_values(&[addr, expected, desired]);
1182 let inst = self.inst(
1183 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Cmpxchg) },
1184 &[ty, Type::I1],
1185 );
1186 let results: Vec<Value> = self.func[inst].results().collect();
1187 let [old, exchanged] = results[..] else { unreachable!("two results were asked for") };
1188 (old, exchanged)
1189 }
1190
1191 pub fn atomic_rmw(
1199 &mut self,
1200 op: RmwOp,
1201 addr: Value,
1202 operand: Value,
1203 info: MemInfo,
1204 flags: Flags,
1205 ) -> Value {
1206 let ty = self.func[operand].ty;
1207 let mem = self.func.add_mem(info);
1208 let args = self.func.push_values(&[addr, operand]);
1209 self.value(
1210 InstData {
1211 args,
1212 flags,
1213 extra: Extra::Rmw(op, mem),
1214 ..InstData::new(Opcode::AtomicRmw)
1215 },
1216 ty,
1217 )
1218 }
1219
1220 pub fn fence(&mut self, order: MemOrder) -> Inst {
1222 self.inst(InstData { extra: Extra::Order(order), ..InstData::new(Opcode::Fence) }, &[])
1223 }
1224
1225 pub fn prefetch(&mut self, address: Value, hint: PrefetchHint) -> Inst {
1232 let args = self.func.push_values(&[address]);
1233 self.inst(
1234 InstData { args, extra: Extra::Prefetch(hint), ..InstData::new(Opcode::Prefetch) },
1235 &[],
1236 )
1237 }
1238
1239 pub fn jump(&mut self, target: Block, args: &[Value]) -> Inst {
1241 let call = self.block_call(target, args);
1242 let targets = self.func.push_block_calls(&[call]);
1243 self.inst(InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::Jump) }, &[])
1244 }
1245
1246 pub fn block_addr(&mut self, target: Block) -> Value {
1252 let call = self.block_call(target, &[]);
1253 let targets = self.func.push_block_calls(&[call]);
1254 self.value(
1255 InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::BlockAddr) },
1256 Type::PTR,
1257 )
1258 }
1259
1260 pub fn indirect_br(&mut self, addr: Value, targets: &[Block]) -> Inst {
1266 let calls: Vec<BlockCall> =
1267 targets.iter().map(|&target| self.block_call(target, &[])).collect();
1268 let targets = self.func.push_block_calls(&calls);
1269 let args = self.func.push_values(&[addr]);
1270 self.inst(
1271 InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::IndirectBr) },
1272 &[],
1273 )
1274 }
1275
1276 pub fn br_if(
1278 &mut self,
1279 cond: Value,
1280 then_block: Block,
1281 then_args: &[Value],
1282 else_block: Block,
1283 else_args: &[Value],
1284 ) -> Inst {
1285 let then_call = self.block_call(then_block, then_args);
1286 let else_call = self.block_call(else_block, else_args);
1287 let targets = self.func.push_block_calls(&[then_call, else_call]);
1288 let args = self.func.push_values(&[cond]);
1289 self.inst(
1290 InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::BrIf) },
1291 &[],
1292 )
1293 }
1294
1295 pub fn switch(&mut self, value: Value, default: Block, cases: &[(i128, Block)]) -> Inst {
1302 let ty = self.func[value].ty.lane();
1303 let mut calls = vec![self.block_call(default, &[])];
1304 let mut values = Vec::with_capacity(cases.len());
1305 for &(value, block) in cases {
1306 calls.push(self.block_call(block, &[]));
1307 values.push(Imm::int(value, ty));
1308 }
1309 let targets = self.func.push_block_calls(&calls);
1310 let cases = self.func.push_imms(&values);
1311 let info = self.func.add_switch(SwitchInfo { targets, cases });
1312 let args = self.func.push_values(&[value]);
1313 self.inst(
1314 InstData { args, extra: Extra::Switch(info), ..InstData::new(Opcode::Switch) },
1315 &[],
1316 )
1317 }
1318
1319 pub fn ret(&mut self, values: &[Value]) -> Inst {
1321 let args = self.func.push_values(values);
1322 self.inst(InstData { args, ..InstData::new(Opcode::Return) }, &[])
1323 }
1324
1325 pub fn unreachable(&mut self) -> Inst {
1327 self.inst(InstData::new(Opcode::Unreachable), &[])
1328 }
1329
1330 pub fn call(&mut self, callee: Symbol, signature: Sig, args: &[Value]) -> Inst {
1332 self.call_varargs(callee, signature, args, &[])
1333 }
1334
1335 pub fn call_varargs(
1341 &mut self,
1342 callee: Symbol,
1343 signature: Sig,
1344 args: &[Value],
1345 varargs: &[Abi],
1346 ) -> Inst {
1347 let varargs = self.func.push_abis(varargs);
1348 let info = self.func.add_call(CallInfo { callee: Some(callee), signature, varargs });
1349 let returns: Vec<Type> = self.func[signature].return_types().collect();
1350 let args = self.func.push_values(args);
1351 self.inst(
1352 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::Call) },
1353 &returns,
1354 )
1355 }
1356
1357 pub fn inline_asm(
1363 &mut self,
1364 info: AsmInfo,
1365 args: &[Value],
1366 results: &[Type],
1367 flags: Flags,
1368 ) -> Inst {
1369 let info = self.func.add_asm(info);
1370 let args = self.func.push_values(args);
1371 self.inst(
1372 InstData { args, flags, extra: Extra::Asm(info), ..InstData::new(Opcode::InlineAsm) },
1373 results,
1374 )
1375 }
1376
1377 fn block_call(&mut self, block: Block, args: &[Value]) -> BlockCall {
1378 BlockCall::new(block, self.func.push_values(args))
1379 }
1380}
1381
1382#[cfg(test)]
1383mod tests {
1384 use rucc_base::Interner;
1385
1386 use super::*;
1387 use crate::inst::BlockCallList;
1388 use crate::{MemOrder, Restrict};
1389
1390 fn sum() -> (Func, Block, Block, Block) {
1392 let mut names = Interner::new();
1393 let i32_ = Type::int(32);
1394 let mut func = Func::new(
1395 names.intern("sum"),
1396 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
1397 );
1398
1399 let entry = func.create_block();
1400 let n = func.append_param(entry, i32_);
1401 let header = func.create_block();
1402 let acc = func.append_param(header, i32_);
1403 let i = func.append_param(header, i32_);
1404 let exit = func.create_block();
1405 let result = func.append_param(exit, i32_);
1406
1407 let mut b = Builder::new(&mut func, entry);
1408 let zero = b.iconst(i32_, 0);
1409 let cmp = b.icmp(IntPred::Sle, n, zero);
1410 b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
1411
1412 let mut b = Builder::new(&mut func, header);
1413 let one = b.iconst(i32_, 1);
1414 let next = b.binary(Opcode::Add, i, one, Flags::NSW);
1415 let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
1416 let done = b.icmp(IntPred::Sge, next, n);
1417 b.br_if(done, exit, &[total], header, &[total, next]);
1418
1419 let mut b = Builder::new(&mut func, exit);
1420 b.ret(&[result]);
1421
1422 (func, entry, header, exit)
1423 }
1424
1425 #[test]
1426 fn the_blocks_come_back_in_the_order_they_were_made() {
1427 let (func, entry, header, exit) = sum();
1428 assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, header, exit]);
1429 assert_eq!(func.entry(), Some(entry));
1430 }
1431
1432 #[test]
1433 fn a_removed_block_is_gone_from_the_layout_and_so_is_what_was_in_it() {
1434 let (mut func, entry, header, exit) = sum();
1435 let inside: Vec<Inst> = func.insts(header).collect();
1436 func.remove_block(header);
1437 assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, exit]);
1438 assert_eq!(func.entry(), Some(entry));
1439 assert_eq!(func[entry].next, Some(exit));
1440 assert_eq!(func[exit].prev, Some(entry));
1441 assert!(inside.iter().all(|&inst| func.block_of(inst).is_none()));
1443 assert!(func.insts(header).next().is_none());
1444 }
1445
1446 #[test]
1447 fn each_block_holds_what_was_appended_to_it() {
1448 let (func, entry, header, exit) = sum();
1449 let opcodes =
1450 |block| func.insts(block).map(|inst| func[inst].opcode.name()).collect::<Vec<_>>();
1451 assert_eq!(opcodes(entry), ["iconst", "icmp", "br_if"]);
1452 assert_eq!(opcodes(header), ["iconst", "add", "add", "icmp", "br_if"]);
1453 assert_eq!(opcodes(exit), ["return"]);
1454 }
1455
1456 #[test]
1457 fn dropping_the_results_in_front_moves_the_first_one_along_and_renumbers_the_rest() {
1458 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1463 let types = [Type::int(32), Type::int(64), Type::MEM];
1464 let inst = func.create_inst(InstData::new(Opcode::Call), &types, Span::DUMMY);
1465 let before: Vec<Value> = func[inst].results().collect();
1466 func.drop_results(inst, 1);
1467 assert_eq!(func[inst].results().collect::<Vec<_>>(), before[1..]);
1468 assert_eq!(func[before[1]].def, Def::Result { inst, index: 0 });
1469 assert_eq!(func[before[2]].def, Def::Result { inst, index: 1 });
1470 assert_eq!(func.mem_out(inst), Some(before[2]));
1471 }
1472
1473 #[test]
1474 fn dropping_every_result_leaves_an_instruction_that_produces_nothing() {
1475 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1476 let inst = func.create_inst(InstData::new(Opcode::Call), &[Type::int(32)], Span::DUMMY);
1477 func.drop_results(inst, 1);
1478 assert_eq!(func[inst].results().count(), 0);
1479 assert_eq!(func[inst].first_result, None);
1480 }
1481
1482 #[test]
1483 fn asm_ends_a_block_when_it_has_labels_and_not_otherwise() {
1484 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1487 let block = func.create_block();
1488 let plain = func.add_asm(AsmInfo {
1489 template: Symbol::from_raw(0),
1490 constraints: Symbol::from_raw(0),
1491 clobbers: Symbol::from_raw(0),
1492 targets: BlockCallList::EMPTY,
1493 });
1494 let call = BlockCall::to(block);
1495 let targets = func.push_block_calls(&[call]);
1496 let labelled = func.add_asm(AsmInfo {
1497 template: Symbol::from_raw(0),
1498 constraints: Symbol::from_raw(0),
1499 clobbers: Symbol::from_raw(0),
1500 targets,
1501 });
1502
1503 let mut make = |extra| {
1504 let data = InstData { extra, ..InstData::new(Opcode::InlineAsm) };
1505 func.create_inst(data, &[], Span::DUMMY)
1506 };
1507 let plain = make(Extra::Asm(plain));
1508 let labelled = make(Extra::Asm(labelled));
1509 assert!(!func.is_terminator(plain));
1510 assert!(func.is_terminator(labelled));
1511 }
1512
1513 #[test]
1514 fn every_block_ends_in_its_terminator() {
1515 let (func, entry, header, exit) = sum();
1516 for block in [entry, header, exit] {
1517 let last = func.terminator(block).expect("a terminator");
1518 assert_eq!(Some(last), func.insts(block).last());
1519 }
1520 }
1521
1522 #[test]
1523 fn a_branch_carries_the_arguments_the_block_takes() {
1524 let (func, entry, header, _) = sum();
1525 let br = func.terminator(entry).expect("a terminator");
1526 let calls: Vec<BlockCall> = func.successors(br).collect();
1527 assert_eq!(calls.len(), 2);
1528 assert_eq!(calls[1].block, header);
1530 assert_eq!(func[calls[1].args].len(), 2);
1531 assert_eq!(func[header].params.len(), 2);
1532 assert_eq!(func[calls[0].args].len(), 1);
1533 }
1534
1535 #[test]
1536 fn a_value_knows_what_defined_it() {
1537 let (func, entry, _, _) = sum();
1538 let first = func.insts(entry).next().expect("an instruction");
1539 let value = func[first].first_result.expect("a result");
1540 assert_eq!(func[value].def, Def::Result { inst: first, index: 0 });
1541 assert_eq!(func[value].ty, Type::int(32));
1542
1543 let param = func[entry].params[0];
1544 assert_eq!(func[param].def, Def::Param { block: entry, index: 0 });
1545 }
1546
1547 #[test]
1548 fn a_comparison_produces_one_bit() {
1549 let (func, entry, _, _) = sum();
1550 let cmp = func.insts(entry).nth(1).expect("the comparison");
1551 let value = func[cmp].first_result.expect("a result");
1552 assert_eq!(func[value].ty, Type::I1);
1553 assert_eq!(func[cmp].extra, Extra::IntPred(IntPred::Sle));
1554 }
1555
1556 #[test]
1557 fn flags_ride_along_on_the_instruction_that_was_given_them() {
1558 let (func, _, header, _) = sum();
1559 let add = func.insts(header).nth(1).expect("the addition");
1560 assert_eq!(func[add].flags, Flags::NSW);
1561 let cmp = func.insts(header).nth(3).expect("the comparison");
1562 assert_eq!(func[cmp].flags, Flags::NONE);
1563 }
1564
1565 #[test]
1566 fn removing_an_instruction_takes_it_out_of_the_middle() {
1567 let (mut func, _, header, _) = sum();
1568 let add = func.insts(header).nth(1).expect("the addition");
1569 func.remove_inst(add);
1570 let opcodes: Vec<&str> = func.insts(header).map(|inst| func[inst].opcode.name()).collect();
1571 assert_eq!(opcodes, ["iconst", "add", "icmp", "br_if"]);
1572 assert_eq!(func.block_of(add), None);
1573 }
1574
1575 #[test]
1576 fn removing_the_first_and_the_last_keeps_the_ends_right() {
1577 let (mut func, entry, _, _) = sum();
1578 let first = func.insts(entry).next().expect("an instruction");
1579 let last = func.terminator(entry).expect("a terminator");
1580 func.remove_inst(first);
1581 func.remove_inst(last);
1582 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1583 assert_eq!(opcodes, ["icmp"]);
1584 assert_eq!(func[entry].first, func[entry].last);
1585 }
1586
1587 #[test]
1588 fn removing_the_only_instruction_empties_the_block() {
1589 let (mut func, _, _, exit) = sum();
1590 let only = func.insts(exit).next().expect("an instruction");
1591 func.remove_inst(only);
1592 assert_eq!(func.insts(exit).count(), 0);
1593 assert_eq!(func[exit].first, None);
1594 assert_eq!(func[exit].last, None);
1595 }
1596
1597 #[test]
1598 fn inserting_before_puts_it_in_the_right_place() {
1599 let (mut func, entry, _, _) = sum();
1600 let cmp = func.insts(entry).nth(1).expect("the comparison");
1601 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1602 func.insert_before(made, cmp);
1603 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1604 assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
1605 }
1606
1607 #[test]
1608 fn inserting_before_the_first_makes_it_the_first() {
1609 let (mut func, entry, _, _) = sum();
1610 let first = func.insts(entry).next().expect("an instruction");
1611 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1612 func.insert_before(made, first);
1613 assert_eq!(func.insts(entry).next(), Some(made));
1614 assert_eq!(func[entry].first, Some(made));
1615 }
1616
1617 #[test]
1618 fn inserting_after_puts_it_in_the_right_place() {
1619 let (mut func, entry, _, _) = sum();
1620 let first = func.insts(entry).next().expect("an instruction");
1621 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1622 func.insert_after(made, first);
1623 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1624 assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
1625 assert_eq!(func[entry].first, Some(first));
1626 }
1627
1628 #[test]
1629 #[should_panic(expected = "nothing goes after a terminator")]
1630 fn inserting_after_the_terminator_is_refused() {
1631 let (mut func, entry, _, _) = sum();
1634 let last = func.insts(entry).last().expect("a terminator");
1635 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1636 func.insert_after(made, last);
1637 }
1638
1639 #[test]
1640 fn a_list_grows_in_place_while_it_is_the_last_thing_in_the_pool() {
1641 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1642 let block = func.create_block();
1643 let a = func.append_param(block, Type::int(32));
1644 let b = func.append_param(block, Type::int(32));
1645 let list = func.push_values(&[a]);
1646 let grown = func.append_arg(list, b);
1647 assert_eq!(func[grown], [a, b]);
1648 assert_eq!(grown.as_usize_range().start, list.as_usize_range().start);
1649 }
1650
1651 #[test]
1652 fn a_list_is_copied_when_something_is_behind_it() {
1653 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1654 let block = func.create_block();
1655 let a = func.append_param(block, Type::int(32));
1656 let b = func.append_param(block, Type::int(32));
1657 let list = func.push_values(&[a, a]);
1658 let behind = func.push_values(&[b]);
1659 let grown = func.append_arg(list, b);
1660 assert_eq!(func[grown], [a, a, b]);
1661 assert_eq!(func[list], [a, a], "the old run is still readable");
1662 assert_eq!(func[behind], [b], "and so is what was behind it");
1663 assert_ne!(grown.as_usize_range().start, list.as_usize_range().start);
1664 }
1665
1666 #[test]
1667 fn a_parameter_added_late_is_the_next_one_along() {
1668 let (mut func, entry, header, _) = sum();
1672 let extra = func.append_param(header, Type::int(32));
1673 assert_eq!(func[header].params.len(), 3);
1674 assert_eq!(func[extra].def, Def::Param { block: header, index: 2 });
1675
1676 let br = func.terminator(entry).expect("a terminator");
1677 let call = func.successors(br).nth(1).expect("the branch to the header");
1678 let grown = func.append_arg(call.args, extra);
1679 assert_eq!(func[grown].len(), 3);
1680 }
1681
1682 #[test]
1683 fn a_span_rides_along_with_the_instruction() {
1684 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1685 let block = func.create_block();
1686 let span = Span::new(10, 20);
1687 let mut b = Builder::new(&mut func, block).at(span);
1688 let value = b.iconst(Type::int(32), 7);
1689 let inst = match func[value].def {
1690 Def::Result { inst, .. } => inst,
1691 Def::Param { .. } => unreachable!("a constant is not a parameter"),
1692 };
1693 assert_eq!(func.span(inst), span);
1694 }
1695
1696 #[test]
1697 fn a_store_produces_nothing_and_a_load_produces_one_value() {
1698 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1699 let block = func.create_block();
1700 let addr = func.append_param(block, Type::PTR);
1701 let info = MemInfo {
1702 size: 4,
1703 align: 4,
1704 order: MemOrder::NotAtomic,
1705 tbaa: None,
1706 owns: 0,
1707 restrict: Restrict::NONE,
1708 };
1709 let mut b = Builder::new(&mut func, block);
1710 let value = b.load(Type::int(32), addr, info, Flags::NONE);
1711 let store = b.store(value, addr, info, Flags::VOLATILE);
1712 assert_eq!(func[store].results, 0);
1713 assert_eq!(func[store].flags, Flags::VOLATILE);
1714 assert_eq!(func[value].ty, Type::int(32));
1715 }
1716
1717 #[test]
1718 fn a_call_produces_what_its_signature_returns() {
1719 let mut names = Interner::new();
1720 let mut func = Func::new(names.intern("caller"), Signature::new());
1721 let sig = func.add_signature(
1722 Signature::new().with_params(&[Type::int(32)]).with_returns(&[Type::int(64)]),
1723 );
1724 let block = func.create_block();
1725 let arg = func.append_param(block, Type::int(32));
1726 let callee = names.intern("callee");
1727 let mut b = Builder::new(&mut func, block);
1728 let call = b.call(callee, sig, &[arg]);
1729 assert_eq!(func[call].results, 1);
1730 let value = func[call].first_result.expect("a result");
1731 assert_eq!(func[value].ty, Type::int(64));
1732 assert_eq!(func[call].extra, Extra::Call(Idx::new(0)));
1733 }
1734
1735 #[test]
1736 fn the_counts_are_what_was_made() {
1737 let (func, _, _, _) = sum();
1738 let counts = func.counts();
1739 assert_eq!(counts.blocks, 3);
1740 assert_eq!(counts.insts, 9);
1741 assert_eq!(counts.values, 4 + 6);
1744 }
1745
1746 #[test]
1747 #[should_panic(expected = "the instruction is in a block")]
1748 fn appending_an_instruction_twice_is_refused() {
1749 let (mut func, entry, _, _) = sum();
1750 let first = func.insts(entry).next().expect("an instruction");
1751 func.append_inst(entry, first);
1752 }
1753
1754 #[test]
1755 #[should_panic(expected = "the instruction is not in a block")]
1756 fn removing_an_instruction_twice_is_refused() {
1757 let (mut func, entry, _, _) = sum();
1758 let first = func.insts(entry).next().expect("an instruction");
1759 func.remove_inst(first);
1760 func.remove_inst(first);
1761 }
1762
1763 fn threaded() -> (Func, Inst, Inst) {
1765 let mut names = Interner::new();
1766 let i32_ = Type::int(32);
1767 let mut func = Func::new(
1768 names.intern("thread"),
1769 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1770 );
1771 let entry = func.create_block();
1772 let addr = func.append_param(entry, Type::PTR);
1773 let info = MemInfo {
1774 size: 4,
1775 align: 4,
1776 order: MemOrder::NotAtomic,
1777 tbaa: None,
1778 owns: 0,
1779 restrict: Restrict::NONE,
1780 };
1781
1782 let mut b = Builder::new(&mut func, entry);
1783 let start = b.mem_entry();
1784 let seven = b.iconst(i32_, 7);
1785 let store = b.store(seven, addr, info, Flags::NONE);
1786 let value = b.load(i32_, addr, info, Flags::NONE);
1787 let Def::Result { inst: load, .. } = func[value].def else {
1788 panic!("the load produced it");
1789 };
1790
1791 let store = func.with_mem(store, start);
1792 let after = func.mem_out(store).expect("a store makes a new version");
1793 let load = func.with_mem(load, after);
1794 (func, store, load)
1795 }
1796
1797 #[test]
1798 fn threading_memory_puts_it_last_and_leaves_everything_else_where_it_was() {
1799 let (func, store, load) = threaded();
1800 assert_eq!(func.mem_in(store), func.mem_out(store).map(|_| func[func[store].args][2]));
1801 assert_eq!(func[func[store].args].len(), 3);
1802 assert!(func.carries_mem(store));
1803 assert!(func.carries_mem(load));
1804
1805 assert_eq!(func[func[load].args][0], func[func.entry().expect("an entry")].params[0]);
1808 assert_eq!(func.mem_in(load), func.mem_out(store));
1809 assert_eq!(func.mem_out(load), None);
1810 }
1811
1812 #[test]
1813 #[should_panic(expected = "this is already on the memory chain")]
1814 fn threading_memory_through_the_same_instruction_twice_is_refused() {
1815 let (mut func, store, _) = threaded();
1816 let start = func.mem_in(store).expect("it was threaded");
1817 func.with_mem(store, start);
1818 }
1819
1820 fn copies() -> (Func, Inst, Inst) {
1823 let mut names = Interner::new();
1824 let i64_ = Type::int(64);
1825 let mut func = Func::new(
1826 names.intern("copies"),
1827 Signature::new().with_params(&[Type::PTR, Type::PTR, i64_]),
1828 );
1829 let entry = func.create_block();
1830 let to = func.append_param(entry, Type::PTR);
1831 let from = func.append_param(entry, Type::PTR);
1832 let length = func.append_param(entry, i64_);
1833 let info = MemInfo {
1834 size: 16,
1835 align: 4,
1836 order: MemOrder::NotAtomic,
1837 tbaa: None,
1838 owns: 0,
1839 restrict: Restrict::NONE,
1840 };
1841
1842 let mut b = Builder::new(&mut func, entry);
1843 let start = b.mem_entry();
1844 let mem = b.func().add_mem(info);
1845 let args = b.func().push_values(&[to, from]);
1846 let fixed =
1847 b.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1848 let mem = b.func().add_mem(MemInfo { size: 0, ..info });
1849 let args = b.func().push_values(&[to, from, length]);
1850 let computed =
1851 b.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1852 b.ret(&[]);
1853
1854 let fixed = func.with_mem(fixed, start);
1855 let after = func.mem_out(fixed).expect("a copy makes a new version");
1856 let computed = func.with_mem(computed, after);
1857 (func, fixed, computed)
1858 }
1859
1860 #[test]
1861 fn a_bulk_copy_hands_back_its_length_where_it_has_one_and_nothing_where_the_payload_has_it() {
1862 let (func, fixed, computed) = copies();
1863 let params = &func[func.entry().expect("an entry")].params;
1864 let [to, from, length] = params[..] else { panic!("three of them were appended") };
1865
1866 let bulk = func.bulk(fixed).expect("a memcpy is one");
1867 assert_eq!((bulk.to, bulk.with, bulk.length), (to, from, None));
1868
1869 let bulk = func.bulk(computed).expect("a memcpy is one");
1870 assert_eq!((bulk.to, bulk.with, bulk.length), (to, from, Some(length)));
1871 }
1872
1873 #[test]
1874 fn an_instruction_that_is_not_a_bulk_operation_is_not_taken_apart_as_one() {
1875 let (func, store, load) = threaded();
1876 assert_eq!(func.bulk(store), None);
1877 assert_eq!(func.bulk(load), None);
1878 }
1879}