1use std::collections::{HashMap, HashSet};
30use std::ops::{Index, IndexMut};
31
32use rucc_base::{Idx, Symbol};
33use rucc_diag::Span;
34use rucc_target::Slot;
35
36use crate::inst::{
37 Abi, AbiList, AsmInfo, Block, BlockCall, BlockCallList, BlockData, Bulk, CallInfo, Def, Extra,
38 Imm, ImmList, Inst, InstData, InstLayout, MemInfo, Sig, Signature, SlotList, SwitchInfo,
39 VaInfo, Value, ValueData, ValueList,
40};
41use crate::module::{Linkage, Visibility};
42use crate::{Attrs, Facts, Flags, FloatPred, IntPred, MemOrder, Opcode, PrefetchHint, RmwOp, Type};
43
44#[derive(Debug)]
46pub struct Func {
47 pub name: Symbol,
49 pub spelled: Option<Symbol>,
62 pub linkage: Linkage,
64 pub visibility: Visibility,
66 pub section: Option<Symbol>,
69 pub align: Option<u32>,
76 pub attrs: Attrs,
79 pub declared: Span,
95
96 values: Vec<ValueData>,
97 insts: Vec<InstData>,
98 inst_layout: Vec<InstLayout>,
99 inst_spans: Vec<Span>,
100 blocks: Vec<BlockData>,
101
102 value_pool: Vec<Value>,
103 block_calls: Vec<BlockCall>,
104 imms: Vec<Imm>,
105 mem: Vec<MemInfo>,
106 calls: Vec<CallInfo>,
107 abis: Vec<Abi>,
108 switches: Vec<SwitchInfo>,
109 asms: Vec<AsmInfo>,
110 slots: Vec<Slot>,
111 va_objects: Vec<VaInfo>,
112 signatures: Vec<Signature>,
113 facts: Vec<(Value, Facts)>,
114 labels: Vec<(Block, Symbol)>,
115 mem_decls: Vec<(Idx<MemInfo>, u32)>,
116 value_decls: Vec<(Value, u32)>,
117 value_starts: Vec<(Value, Start)>,
118 anchors: HashSet<Inst>,
121 unplaced: HashMap<Inst, Vec<(Value, Start)>>,
124 arg_groups: HashMap<Inst, Vec<u32>>,
132 gone: HashMap<Inst, (Block, Option<Inst>)>,
135
136 first_block: Option<Block>,
137 last_block: Option<Block>,
138}
139
140impl Func {
141 #[must_use]
148 pub fn new(name: Symbol, signature: Signature) -> Self {
149 Self {
150 name,
151 spelled: None,
152 linkage: Linkage::External,
153 visibility: Visibility::Default,
154 section: None,
155 align: None,
156 attrs: Attrs::NONE,
157 declared: Span::DUMMY,
158 values: Vec::new(),
159 insts: Vec::new(),
160 inst_layout: Vec::new(),
161 inst_spans: Vec::new(),
162 blocks: Vec::new(),
163 value_pool: Vec::new(),
164 block_calls: Vec::new(),
165 imms: Vec::new(),
166 mem: Vec::new(),
167 calls: Vec::new(),
168 abis: Vec::new(),
169 switches: Vec::new(),
170 asms: Vec::new(),
171 slots: Vec::new(),
172 va_objects: Vec::new(),
173 signatures: vec![signature],
174 facts: Vec::new(),
175 labels: Vec::new(),
176 mem_decls: Vec::new(),
177 value_decls: Vec::new(),
178 value_starts: Vec::new(),
179 anchors: HashSet::new(),
180 unplaced: HashMap::new(),
181 arg_groups: HashMap::new(),
182 gone: HashMap::new(),
183 first_block: None,
184 last_block: None,
185 }
186 }
187
188 #[must_use]
190 pub fn signature(&self) -> &Signature {
191 &self.signatures[0]
192 }
193
194 pub fn set_signature(&mut self, signature: Signature) {
206 self.signatures[0] = signature;
207 }
208
209 pub fn signatures(&self) -> impl Iterator<Item = &Signature> {
211 self.signatures.iter()
212 }
213
214 pub fn add_signature(&mut self, signature: Signature) -> Sig {
216 self.signatures.push(signature);
217 Idx::from_usize(self.signatures.len() - 1)
218 }
219
220 #[must_use]
225 pub fn entry(&self) -> Option<Block> {
226 self.first_block
227 }
228
229 #[must_use]
236 pub fn is_declaration(&self) -> bool {
237 self.first_block.is_none()
238 }
239
240 pub fn create_block(&mut self) -> Block {
244 let block = Idx::from_usize(self.blocks.len());
245 self.blocks.push(BlockData { prev: self.last_block, ..BlockData::default() });
246 match self.last_block {
247 Some(last) => self.blocks[last.index()].next = Some(block),
248 None => self.first_block = Some(block),
249 }
250 self.last_block = Some(block);
251 block
252 }
253
254 pub fn remove_block(&mut self, block: Block) {
267 assert!(self.first_block != Some(block), "the entry block is not removable");
268 let (prev, next) = (self.blocks[block.index()].prev, self.blocks[block.index()].next);
269 match prev {
270 Some(prev) => self.blocks[prev.index()].next = next,
271 None => self.first_block = next,
272 }
273 match next {
274 Some(next) => self.blocks[next.index()].prev = prev,
275 None => self.last_block = prev,
276 }
277 let insts: Vec<Inst> = self.insts(block).collect();
281 for inst in insts {
282 self.inst_layout[inst.index()] = InstLayout::default();
283 }
284 self.blocks[block.index()] = BlockData::default();
285 }
286
287 pub fn append_param(&mut self, block: Block, ty: Type) -> Value {
296 let index = u32::try_from(self.blocks[block.index()].params.len())
297 .expect("a block with four billion parameters");
298 let value = self.add_value(ValueData { ty, def: Def::Param { block, index } });
299 self.blocks[block.index()].params.push(value);
300 value
301 }
302
303 pub fn retain_params(&mut self, block: Block, mut keep: impl FnMut(Value) -> bool) {
315 let mut params = std::mem::take(&mut self.blocks[block.index()].params);
316 let mut dropped = Vec::new();
317 params.retain(|&value| {
318 let kept = keep(value);
319 if !kept {
320 dropped.push(value);
321 }
322 kept
323 });
324 for value in dropped {
327 self.held_from(value, block, None);
328 }
329 for (index, &value) in params.iter().enumerate() {
330 let index = u32::try_from(index).expect("a block with four billion parameters");
331 self.values[value.index()].def = Def::Param { block, index };
332 }
333 self.blocks[block.index()].params = params;
334 }
335
336 pub fn retype(&mut self, value: Value, ty: Type) {
348 self.values[value.index()].ty = ty;
349 }
350
351 pub fn values(&self) -> impl Iterator<Item = Value> + use<'_> {
356 (0..self.values.len()).map(Idx::from_usize)
357 }
358
359 pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
361 std::iter::successors(self.first_block, move |&block| self.blocks[block.index()].next)
362 }
363
364 pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
366 std::iter::successors(self.blocks[block.index()].first, move |&inst| {
367 self.inst_layout[inst.index()].next
368 })
369 }
370
371 pub fn insts_backwards(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
377 std::iter::successors(self.blocks[block.index()].last, move |&inst| {
378 self.inst_layout[inst.index()].prev
379 })
380 }
381
382 #[must_use]
384 pub fn terminator(&self, block: Block) -> Option<Inst> {
385 self.blocks[block.index()].last.filter(|&inst| self.is_terminator(inst))
386 }
387
388 #[must_use]
394 pub fn is_terminator(&self, inst: Inst) -> bool {
395 let data = &self[inst];
396 match data.extra {
397 Extra::Asm(info) => {
398 data.opcode.is_terminator() || !self.asms[info.index()].targets.is_empty()
399 }
400 _ => data.opcode.is_terminator(),
401 }
402 }
403
404 pub fn create_inst(&mut self, mut data: InstData, results: &[Type], span: Span) -> Inst {
415 let inst = Idx::from_usize(self.insts.len());
416 data.results = u8::try_from(results.len()).expect("an instruction with too many results");
417 data.first_result = results.first().map(|_| Idx::from_usize(self.values.len()));
418 for (index, &ty) in results.iter().enumerate() {
419 let index = u8::try_from(index).expect("checked just above");
420 self.add_value(ValueData { ty, def: Def::Result { inst, index } });
421 }
422 self.insts.push(data);
423 self.inst_layout.push(InstLayout::default());
424 self.inst_spans.push(span);
425 inst
426 }
427
428 pub fn drop_results(&mut self, inst: Inst, drop: u8) {
444 let data = &self.insts[inst.index()];
445 assert!(drop <= data.results, "the instruction does not produce that many results");
446 let left = data.results - drop;
447 let first = data.first_result.map_or(0, Idx::raw) + u32::from(drop);
448 let data = &mut self.insts[inst.index()];
449 data.results = left;
450 data.first_result = (left > 0).then(|| Value::new(first));
451 for offset in 0..u32::from(left) {
452 let index = u8::try_from(offset).expect("no more than the count it came from");
453 let value = Value::new(first + offset);
454 self.values[value.index()].def = Def::Result { inst, index };
455 }
456 }
457
458 pub fn append_inst(&mut self, block: Block, inst: Inst) {
465 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
466 let last = self.blocks[block.index()].last;
467 self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
468 match last {
469 Some(last) => self.inst_layout[last.index()].next = Some(inst),
470 None => self.blocks[block.index()].first = Some(inst),
471 }
472 self.blocks[block.index()].last = Some(inst);
473 self.placed(inst);
474 }
475
476 pub fn insert_before(&mut self, inst: Inst, before: Inst) {
482 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
483 let at = self.inst_layout[before.index()];
484 let block = at.block.expect("the instruction to insert before is not in a block");
485 self.inst_layout[inst.index()] =
486 InstLayout { block: Some(block), prev: at.prev, next: Some(before) };
487 self.inst_layout[before.index()].prev = Some(inst);
488 match at.prev {
489 Some(prev) => self.inst_layout[prev.index()].next = Some(inst),
490 None => self.blocks[block.index()].first = Some(inst),
491 }
492 self.placed(inst);
493 }
494
495 pub fn insert_after(&mut self, inst: Inst, after: Inst) {
507 assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
508 let at = self.inst_layout[after.index()];
509 let block = at.block.expect("the instruction to insert after is not in a block");
510 assert!(at.next.is_some(), "nothing goes after a terminator");
511 self.inst_layout[inst.index()] =
512 InstLayout { block: Some(block), prev: Some(after), next: at.next };
513 self.inst_layout[after.index()].next = Some(inst);
514 if let Some(next) = at.next {
515 self.inst_layout[next.index()].prev = Some(inst);
516 }
517 self.placed(inst);
518 }
519
520 pub fn remove_inst(&mut self, inst: Inst) {
530 let at = self.inst_layout[inst.index()];
531 let block = at.block.expect("the instruction is not in a block");
532 match at.prev {
533 Some(prev) => self.inst_layout[prev.index()].next = at.next,
534 None => self.blocks[block.index()].first = at.next,
535 }
536 match at.next {
537 Some(next) => self.inst_layout[next.index()].prev = at.prev,
538 None => self.blocks[block.index()].last = at.prev,
539 }
540 self.inst_layout[inst.index()] = InstLayout::default();
541 if self.anchors.contains(&inst) {
546 self.gone.insert(inst, (block, at.prev));
547 self.anchors.extend(at.prev);
548 }
549 let mut made = Vec::new();
556 for value in self.insts[inst.index()].results() {
557 for decl in self.held_from(value, block, at.prev) {
558 made.push((value, Start { decl, block, after: at.prev }));
559 }
560 }
561 if !made.is_empty() {
562 self.unplaced.insert(inst, made);
563 }
564 }
565
566 #[must_use]
569 pub fn is_placed(&self, block: Block) -> bool {
570 let data = &self.blocks[block.index()];
571 self.first_block == Some(block) || data.prev.is_some()
572 }
573
574 #[must_use]
576 pub fn block_of(&self, inst: Inst) -> Option<Block> {
577 self.inst_layout[inst.index()].block
578 }
579
580 #[must_use]
592 pub fn mem_in(&self, inst: Inst) -> Option<Value> {
593 let args = &self[self[inst].args];
594 args.last().copied().filter(|&arg| self[arg].ty.is_mem())
595 }
596
597 #[must_use]
607 pub fn mem_out(&self, inst: Inst) -> Option<Value> {
608 self[inst].results().last().filter(|&result| self[result].ty.is_mem())
609 }
610
611 #[must_use]
621 pub fn bulk(&self, inst: Inst) -> Option<Bulk> {
622 if !matches!(self[inst].opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) {
623 return None;
624 }
625 let all = &self[self[inst].args];
626 let args = &all[..all.len() - usize::from(self.mem_in(inst).is_some())];
627 let [to, with, rest @ ..] = args else { return None };
628 Some(Bulk { to: *to, with: *with, length: rest.first().copied() })
629 }
630
631 #[must_use]
633 pub fn carries_mem(&self, inst: Inst) -> bool {
634 self.mem_in(inst).is_some() || self.mem_out(inst).is_some()
635 }
636
637 pub fn with_mem(&mut self, inst: Inst, incoming: Value) -> Inst {
653 assert!(self[incoming].ty.is_mem(), "the incoming version of memory is not memory");
654 assert!(self[inst].opcode.touches_memory(), "this does not touch memory");
655 assert!(self.mem_in(inst).is_none(), "this is already on the memory chain");
656 let data = self[inst];
657 let mut args = self[data.args].to_vec();
658 args.push(incoming);
659 let mut results: Vec<Type> = data.results().map(|result| self[result].ty).collect();
660 if data.opcode.writes_memory() {
661 results.push(Type::MEM);
662 }
663 let span = self.span(inst);
664 let args = self.push_values(&args);
665 self.create_inst(InstData { args, ..data }, &results, span)
666 }
667
668 pub fn without_mem(&mut self, inst: Inst) -> Inst {
683 assert!(self.carries_mem(inst), "this is not on the memory chain");
684 let data = self[inst];
685 let mut args = self[data.args].to_vec();
686 if self.mem_in(inst).is_some() {
687 args.pop();
688 }
689 let results: Vec<Type> =
690 data.results().map(|result| self[result].ty).filter(|ty| !ty.is_mem()).collect();
691 let span = self.span(inst);
692 let args = self.push_values(&args);
693 self.create_inst(InstData { args, ..data }, &results, span)
694 }
695
696 #[must_use]
698 pub fn span(&self, inst: Inst) -> Span {
699 self.inst_spans[inst.index()]
700 }
701
702 pub fn successors(&self, inst: Inst) -> impl Iterator<Item = BlockCall> + use<'_> {
707 self.block_calls[self.target_list(inst).as_usize_range()].iter().copied()
708 }
709
710 #[must_use]
717 pub fn target_list(&self, inst: Inst) -> BlockCallList {
718 match self[inst].extra {
719 Extra::Targets(targets) => targets,
720 Extra::Switch(info) => self.switches[info.index()].targets,
721 Extra::Asm(info) => self.asms[info.index()].targets,
722 _ => BlockCallList::EMPTY,
723 }
724 }
725
726 pub fn push_values(&mut self, values: &[Value]) -> ValueList {
730 let start = Idx::from_usize(self.value_pool.len());
731 self.value_pool.extend_from_slice(values);
732 ValueList::new(start, Idx::from_usize(self.value_pool.len()))
733 }
734
735 pub fn append_arg(&mut self, list: ValueList, value: Value) -> ValueList {
742 let range = list.as_usize_range();
743 if range.end == self.value_pool.len() {
744 self.value_pool.push(value);
745 return ValueList::new(Idx::from_usize(range.start), Idx::from_usize(range.end + 1));
746 }
747 let start = self.value_pool.len();
748 self.value_pool.extend_from_within(range);
749 self.value_pool.push(value);
750 ValueList::new(Idx::from_usize(start), Idx::from_usize(self.value_pool.len()))
751 }
752
753 pub fn rewrite(&mut self, list: ValueList, mut with: impl FnMut(Value) -> Value) {
758 for value in &mut self.value_pool[list.as_usize_range()] {
759 *value = with(*value);
760 }
761 }
762
763 pub fn push_block_calls(&mut self, calls: &[BlockCall]) -> BlockCallList {
765 let start = Idx::from_usize(self.block_calls.len());
766 self.block_calls.extend_from_slice(calls);
767 BlockCallList::new(start, Idx::from_usize(self.block_calls.len()))
768 }
769
770 pub fn set_block_call(&mut self, at: Idx<BlockCall>, call: BlockCall) {
772 self.block_calls[at.index()] = call;
773 }
774
775 pub fn push_imms(&mut self, imms: &[Imm]) -> ImmList {
777 let start = Idx::from_usize(self.imms.len());
778 self.imms.extend_from_slice(imms);
779 ImmList::new(start, Idx::from_usize(self.imms.len()))
780 }
781
782 pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
784 self.imms.push(imm);
785 Idx::from_usize(self.imms.len() - 1)
786 }
787
788 pub fn push_slots(&mut self, slots: &[Slot]) -> SlotList {
790 let start = Idx::from_usize(self.slots.len());
791 self.slots.extend_from_slice(slots);
792 SlotList::new(start, Idx::from_usize(self.slots.len()))
793 }
794
795 pub fn add_va_object(&mut self, info: VaInfo) -> Idx<VaInfo> {
797 self.va_objects.push(info);
798 Idx::from_usize(self.va_objects.len() - 1)
799 }
800
801 pub fn add_mem(&mut self, info: MemInfo) -> Idx<MemInfo> {
803 self.mem.push(info);
804 Idx::from_usize(self.mem.len() - 1)
805 }
806
807 pub fn push_abis(&mut self, abis: &[Abi]) -> AbiList {
809 let start = Idx::from_usize(self.abis.len());
810 self.abis.extend_from_slice(abis);
811 AbiList::new(start, Idx::from_usize(self.abis.len()))
812 }
813
814 pub fn add_call(&mut self, info: CallInfo) -> Idx<CallInfo> {
816 self.calls.push(info);
817 Idx::from_usize(self.calls.len() - 1)
818 }
819
820 pub fn set_arg_groups(&mut self, call: Inst, groups: Vec<u32>) {
822 self.arg_groups.insert(call, groups);
823 }
824
825 #[must_use]
830 pub fn arg_groups(&self, call: Inst) -> Option<&[u32]> {
831 self.arg_groups.get(&call).map(Vec::as_slice)
832 }
833
834 pub fn add_switch(&mut self, info: SwitchInfo) -> Idx<SwitchInfo> {
836 self.switches.push(info);
837 Idx::from_usize(self.switches.len() - 1)
838 }
839
840 pub fn add_asm(&mut self, info: AsmInfo) -> Idx<AsmInfo> {
842 self.asms.push(info);
843 Idx::from_usize(self.asms.len() - 1)
844 }
845
846 #[must_use]
849 pub fn counts(&self) -> Counts {
850 Counts { values: self.values.len(), insts: self.insts.len(), blocks: self.blocks.len() }
851 }
852
853 #[must_use]
859 pub fn facts(&self, value: Value) -> Facts {
860 match self.facts.binary_search_by_key(&value.raw(), |&(at, _)| at.raw()) {
861 Ok(at) => self.facts[at].1,
862 Err(_) => Facts::NONE,
863 }
864 }
865
866 pub fn set_facts(&mut self, value: Value, facts: Facts) {
871 let found = self.facts.binary_search_by_key(&value.raw(), |&(at, _)| at.raw());
872 match (found, facts.is_empty()) {
873 (Ok(at), true) => drop(self.facts.remove(at)),
874 (Ok(at), false) => self.facts[at].1 = facts,
875 (Err(_), true) => {}
876 (Err(at), false) => self.facts.insert(at, (value, facts)),
877 }
878 }
879
880 pub fn known(&self) -> impl Iterator<Item = (Value, Facts)> + '_ {
882 self.facts.iter().copied()
883 }
884
885 pub fn name_block(&mut self, block: Block, name: Symbol) {
899 let found = self.labels.binary_search_by_key(&block.raw(), |&(at, _)| at.raw());
900 if let Err(at) = found {
901 self.labels.insert(at, (block, name));
902 }
903 }
904
905 #[must_use]
907 pub fn block_name(&self, block: Block) -> Option<Symbol> {
908 match self.labels.binary_search_by_key(&block.raw(), |&(at, _)| at.raw()) {
909 Ok(at) => Some(self.labels[at].1),
910 Err(_) => None,
911 }
912 }
913
914 pub fn named_blocks(&self) -> impl Iterator<Item = (Block, Symbol)> + '_ {
916 self.labels.iter().copied()
917 }
918
919 pub fn declare_mem(&mut self, mem: Idx<MemInfo>, decl: u32) {
934 let found = self.mem_decls.binary_search_by_key(&mem.raw(), |&(at, _)| at.raw());
935 if let Err(at) = found {
936 self.mem_decls.insert(at, (mem, decl));
937 }
938 }
939
940 #[must_use]
943 pub fn mem_decl(&self, mem: Idx<MemInfo>) -> Option<u32> {
944 match self.mem_decls.binary_search_by_key(&mem.raw(), |&(at, _)| at.raw()) {
945 Ok(at) => Some(self.mem_decls[at].1),
946 Err(_) => None,
947 }
948 }
949
950 pub fn declare_value(&mut self, value: Value, decl: u32) {
967 let key = (value.raw(), decl);
968 let found = self.value_decls.binary_search_by_key(&key, |&(at, decl)| (at.raw(), decl));
969 if let Err(at) = found {
970 self.value_decls.insert(at, (value, decl));
971 }
972 }
973
974 pub fn value_decls(&self, value: Value) -> impl Iterator<Item = u32> + '_ {
980 let at = self.value_decls.partition_point(|&(held, _)| held.raw() < value.raw());
981 self.value_decls[at..]
982 .iter()
983 .take_while(move |&&(held, _)| held == value)
984 .map(|&(_, decl)| decl)
985 }
986
987 pub fn rename_value(&mut self, from: Value, to: Value) {
1000 if from == to {
1001 return;
1002 }
1003 match self.values[from.index()].def {
1006 Def::Result { inst, .. } => {
1007 let at = self.inst_layout[inst.index()];
1008 if let Some(block) = at.block {
1009 self.held_from(from, block, at.prev);
1010 }
1011 }
1012 Def::Param { block, index } => {
1013 let param = usize::try_from(index)
1014 .ok()
1015 .and_then(|index| self.blocks[block.index()].params.get(index).copied());
1016 if param == Some(from) {
1017 self.held_from(from, block, None);
1018 }
1019 }
1020 }
1021 let at = self.value_decls.partition_point(|&(held, _)| held.raw() < from.raw());
1022 let end = at + self.value_decls[at..].iter().take_while(|&&(held, _)| held == from).count();
1023 let moving: Vec<u32> = self.value_decls.drain(at..end).map(|(_, decl)| decl).collect();
1024 for decl in moving {
1025 self.declare_value(to, decl);
1026 }
1027 let at = self.value_starts.partition_point(|&(held, _)| held.raw() < from.raw());
1028 let end =
1029 at + self.value_starts[at..].iter().take_while(|&&(held, _)| held == from).count();
1030 let moving: Vec<Start> = self.value_starts.drain(at..end).map(|(_, start)| start).collect();
1031 for start in moving {
1032 self.declare_value_from(to, start);
1033 }
1034 }
1035
1036 pub fn declare_value_from(&mut self, value: Value, start: Start) {
1049 let at = self.value_starts.partition_point(|&(held, _)| held.raw() <= value.raw());
1050 if !self.value_starts[..at]
1051 .iter()
1052 .rev()
1053 .take_while(|&&(held, _)| held == value)
1054 .any(|&(_, have)| have == start)
1055 {
1056 self.value_starts.insert(at, (value, start));
1057 self.anchors.extend(start.after);
1058 }
1059 }
1060
1061 pub fn value_starts(&self, value: Value) -> impl Iterator<Item = Start> + '_ {
1064 let at = self.value_starts.partition_point(|&(held, _)| held.raw() < value.raw());
1065 self.value_starts[at..]
1066 .iter()
1067 .take_while(move |&&(held, _)| held == value)
1068 .map(|&(_, start)| start)
1069 }
1070
1071 pub fn move_starts(&mut self, from: Value, into: Value, which: &[Start]) {
1079 if from == into {
1080 return;
1081 }
1082 let at = self.value_starts.partition_point(|&(held, _)| held.raw() < from.raw());
1083 let end =
1084 at + self.value_starts[at..].iter().take_while(|&&(held, _)| held == from).count();
1085 let (moving, staying): (Vec<Start>, Vec<Start>) = self
1086 .value_starts
1087 .drain(at..end)
1088 .map(|(_, start)| start)
1089 .partition(|start| which.contains(start));
1090 let at = self.value_starts.partition_point(|&(held, _)| held.raw() < from.raw());
1091 self.value_starts.splice(at..at, staying.into_iter().map(|start| (from, start)));
1092 for start in moving {
1093 self.declare_value_from(into, start);
1094 }
1095 }
1096
1097 fn held_from(&mut self, value: Value, block: Block, after: Option<Inst>) -> Vec<u32> {
1106 let at = self.value_decls.partition_point(|&(held, _)| held.raw() < value.raw());
1107 let end =
1108 at + self.value_decls[at..].iter().take_while(|&&(held, _)| held == value).count();
1109 let moving: Vec<u32> = self.value_decls.drain(at..end).map(|(_, decl)| decl).collect();
1110 for &decl in &moving {
1111 self.declare_value_from(value, Start { decl, block, after });
1112 }
1113 moving
1114 }
1115
1116 fn placed(&mut self, inst: Inst) {
1119 if self.unplaced.is_empty() {
1120 return;
1121 }
1122 let Some(made) = self.unplaced.remove(&inst) else { return };
1123 for (value, start) in made {
1124 let at = self.value_starts.partition_point(|&(held, _)| held.raw() < value.raw());
1125 let found = self.value_starts[at..]
1126 .iter()
1127 .take_while(|&&(held, _)| held == value)
1128 .position(|&(_, have)| have == start);
1129 if let Some(found) = found {
1132 self.value_starts.remove(at + found);
1133 self.declare_value(value, start.decl);
1134 }
1135 }
1136 }
1137
1138 #[must_use]
1147 pub fn start_place(&self, start: Start) -> Option<(Block, Option<Inst>)> {
1148 let (mut block, mut after) = (start.block, start.after);
1149 loop {
1150 let Some(inst) = after else {
1151 return self.is_placed(block).then_some((block, None));
1152 };
1153 if let Some(now) = self.block_of(inst) {
1154 return Some((now, Some(inst)));
1155 }
1156 (block, after) = *self.gone.get(&inst)?;
1157 }
1158 }
1159
1160 pub fn carry_starts(&mut self, from: Block, into: Block, after: Option<Inst>) {
1167 let unplaced = self.unplaced.values_mut().flatten().map(|(_, start)| start);
1168 for start in self.value_starts.iter_mut().map(|(_, start)| start).chain(unplaced) {
1169 if start.block == from && start.after.is_none() {
1170 *start = Start { block: into, after, ..*start };
1171 }
1172 }
1173 for place in self.gone.values_mut() {
1174 if *place == (from, None) {
1175 *place = (into, after);
1176 }
1177 }
1178 self.anchors.extend(after);
1179 }
1180
1181 fn add_value(&mut self, data: ValueData) -> Value {
1182 self.values.push(data);
1183 Idx::from_usize(self.values.len() - 1)
1184 }
1185}
1186
1187#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1194pub struct Start {
1195 pub decl: u32,
1197 pub block: Block,
1199 pub after: Option<Inst>,
1201}
1202
1203#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1205pub struct Counts {
1206 pub values: usize,
1208 pub insts: usize,
1210 pub blocks: usize,
1212}
1213
1214impl Index<Value> for Func {
1217 type Output = ValueData;
1218
1219 fn index(&self, value: Value) -> &ValueData {
1220 &self.values[value.index()]
1221 }
1222}
1223
1224impl Index<Inst> for Func {
1225 type Output = InstData;
1226
1227 fn index(&self, inst: Inst) -> &InstData {
1228 &self.insts[inst.index()]
1229 }
1230}
1231
1232impl IndexMut<Inst> for Func {
1233 fn index_mut(&mut self, inst: Inst) -> &mut InstData {
1234 &mut self.insts[inst.index()]
1235 }
1236}
1237
1238impl Index<Block> for Func {
1239 type Output = BlockData;
1240
1241 fn index(&self, block: Block) -> &BlockData {
1242 &self.blocks[block.index()]
1243 }
1244}
1245
1246impl Index<Sig> for Func {
1247 type Output = Signature;
1248
1249 fn index(&self, sig: Sig) -> &Signature {
1250 &self.signatures[sig.index()]
1251 }
1252}
1253
1254impl Index<ValueList> for Func {
1255 type Output = [Value];
1256
1257 fn index(&self, list: ValueList) -> &[Value] {
1258 &self.value_pool[list.as_usize_range()]
1259 }
1260}
1261
1262impl Index<BlockCallList> for Func {
1263 type Output = [BlockCall];
1264
1265 fn index(&self, list: BlockCallList) -> &[BlockCall] {
1266 &self.block_calls[list.as_usize_range()]
1267 }
1268}
1269
1270impl Index<Idx<BlockCall>> for Func {
1271 type Output = BlockCall;
1272
1273 fn index(&self, at: Idx<BlockCall>) -> &BlockCall {
1274 &self.block_calls[at.index()]
1275 }
1276}
1277
1278impl Index<ImmList> for Func {
1279 type Output = [Imm];
1280
1281 fn index(&self, list: ImmList) -> &[Imm] {
1282 &self.imms[list.as_usize_range()]
1283 }
1284}
1285
1286impl Index<Idx<Imm>> for Func {
1287 type Output = Imm;
1288
1289 fn index(&self, at: Idx<Imm>) -> &Imm {
1290 &self.imms[at.index()]
1291 }
1292}
1293
1294impl Index<Idx<MemInfo>> for Func {
1295 type Output = MemInfo;
1296
1297 fn index(&self, at: Idx<MemInfo>) -> &MemInfo {
1298 &self.mem[at.index()]
1299 }
1300}
1301
1302impl Index<AbiList> for Func {
1303 type Output = [Abi];
1304
1305 fn index(&self, list: AbiList) -> &[Abi] {
1306 &self.abis[list.as_usize_range()]
1307 }
1308}
1309
1310impl Index<SlotList> for Func {
1311 type Output = [Slot];
1312
1313 fn index(&self, list: SlotList) -> &[Slot] {
1314 &self.slots[list.as_usize_range()]
1315 }
1316}
1317
1318impl Index<Idx<VaInfo>> for Func {
1319 type Output = VaInfo;
1320
1321 fn index(&self, at: Idx<VaInfo>) -> &VaInfo {
1322 &self.va_objects[at.index()]
1323 }
1324}
1325
1326impl Index<Idx<CallInfo>> for Func {
1327 type Output = CallInfo;
1328
1329 fn index(&self, at: Idx<CallInfo>) -> &CallInfo {
1330 &self.calls[at.index()]
1331 }
1332}
1333
1334impl Index<Idx<SwitchInfo>> for Func {
1335 type Output = SwitchInfo;
1336
1337 fn index(&self, at: Idx<SwitchInfo>) -> &SwitchInfo {
1338 &self.switches[at.index()]
1339 }
1340}
1341
1342impl Index<Idx<AsmInfo>> for Func {
1343 type Output = AsmInfo;
1344
1345 fn index(&self, at: Idx<AsmInfo>) -> &AsmInfo {
1346 &self.asms[at.index()]
1347 }
1348}
1349
1350#[derive(Debug)]
1357pub struct Builder<'a> {
1358 func: &'a mut Func,
1359 block: Block,
1360 span: Span,
1361}
1362
1363impl<'a> Builder<'a> {
1364 pub fn new(func: &'a mut Func, block: Block) -> Self {
1366 Self { func, block, span: Span::DUMMY }
1367 }
1368
1369 #[must_use]
1371 pub fn at(mut self, span: Span) -> Self {
1372 self.span = span;
1373 self
1374 }
1375
1376 pub fn set_span(&mut self, span: Span) {
1378 self.span = span;
1379 }
1380
1381 pub fn func(&mut self) -> &mut Func {
1383 self.func
1384 }
1385
1386 #[must_use]
1388 pub fn block(&self) -> Block {
1389 self.block
1390 }
1391
1392 pub fn inst(&mut self, data: InstData, results: &[Type]) -> Inst {
1394 let inst = self.func.create_inst(data, results, self.span);
1395 self.func.append_inst(self.block, inst);
1396 inst
1397 }
1398
1399 pub fn value(&mut self, data: InstData, ty: Type) -> Value {
1405 let inst = self.inst(data, &[ty]);
1406 self.func[inst].first_result.expect("one result was asked for")
1407 }
1408
1409 pub fn iconst(&mut self, ty: Type, value: i128) -> Value {
1415 let imm = self.func.add_imm(Imm::int(value, ty.lane()));
1416 self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::IConst) }, ty)
1417 }
1418
1419 pub fn fconst(&mut self, ty: Type, bits: u128) -> Value {
1421 let imm = self.func.add_imm(Imm::from_bits(bits));
1422 self.value(InstData { extra: Extra::Imm(imm), ..InstData::new(Opcode::FConst) }, ty)
1423 }
1424
1425 pub fn binary(&mut self, opcode: Opcode, lhs: Value, rhs: Value, flags: Flags) -> Value {
1427 let ty = self.func[lhs].ty;
1428 let args = self.func.push_values(&[lhs, rhs]);
1429 self.value(InstData { args, flags, ..InstData::new(opcode) }, ty)
1430 }
1431
1432 pub fn checked(&mut self, opcode: Opcode, lhs: Value, rhs: Value) -> (Value, Value) {
1445 let ty = self.func[lhs].ty;
1446 let args = self.func.push_values(&[lhs, rhs]);
1447 let results = [ty, ty.with_lane(Type::I1)];
1448 let inst = self.inst(InstData { args, ..InstData::new(opcode) }, &results);
1449 let mut answers = self.func[inst].results();
1450 let value = answers.next().expect("two results were asked for");
1451 let wrapped = answers.next().expect("two results were asked for");
1452 (value, wrapped)
1453 }
1454
1455 pub fn unary(&mut self, opcode: Opcode, arg: Value, ty: Type) -> Value {
1457 let args = self.func.push_values(&[arg]);
1458 self.value(InstData { args, ..InstData::new(opcode) }, ty)
1459 }
1460
1461 pub fn icmp(&mut self, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1463 let ty = self.func[lhs].ty.with_lane(Type::I1);
1464 let args = self.func.push_values(&[lhs, rhs]);
1465 self.value(
1466 InstData { args, extra: Extra::IntPred(pred), ..InstData::new(Opcode::ICmp) },
1467 ty,
1468 )
1469 }
1470
1471 pub fn select(&mut self, cond: Value, then: Value, other: Value) -> Value {
1477 let ty = self.func[then].ty;
1478 let args = self.func.push_values(&[cond, then, other]);
1479 self.value(InstData { args, ..InstData::new(Opcode::Select) }, ty)
1480 }
1481
1482 pub fn fcmp(&mut self, pred: FloatPred, lhs: Value, rhs: Value, flags: Flags) -> Value {
1484 let ty = self.func[lhs].ty.with_lane(Type::I1);
1485 let args = self.func.push_values(&[lhs, rhs]);
1486 self.value(
1487 InstData { args, flags, extra: Extra::FloatPred(pred), ..InstData::new(Opcode::FCmp) },
1488 ty,
1489 )
1490 }
1491
1492 pub fn mem_entry(&mut self) -> Value {
1496 self.value(InstData::new(Opcode::MemEntry), Type::MEM)
1497 }
1498
1499 pub fn load(&mut self, ty: Type, addr: Value, info: MemInfo, flags: Flags) -> Value {
1501 let mem = self.func.add_mem(info);
1502 let args = self.func.push_values(&[addr]);
1503 self.value(
1504 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Load) },
1505 ty,
1506 )
1507 }
1508
1509 pub fn store(&mut self, value: Value, addr: Value, info: MemInfo, flags: Flags) -> Inst {
1511 let mem = self.func.add_mem(info);
1512 let args = self.func.push_values(&[value, addr]);
1513 self.inst(
1514 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Store) },
1515 &[],
1516 )
1517 }
1518
1519 pub fn atomic_load(&mut self, ty: Type, addr: Value, info: MemInfo, flags: Flags) -> Value {
1527 let mem = self.func.add_mem(info);
1528 let args = self.func.push_values(&[addr]);
1529 self.value(
1530 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::AtomicLoad) },
1531 ty,
1532 )
1533 }
1534
1535 pub fn atomic_store(&mut self, value: Value, addr: Value, info: MemInfo, flags: Flags) -> Inst {
1537 let mem = self.func.add_mem(info);
1538 let args = self.func.push_values(&[value, addr]);
1539 self.inst(
1540 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::AtomicStore) },
1541 &[],
1542 )
1543 }
1544
1545 pub fn cmpxchg(
1552 &mut self,
1553 addr: Value,
1554 expected: Value,
1555 desired: Value,
1556 info: MemInfo,
1557 flags: Flags,
1558 ) -> (Value, Value) {
1559 let ty = self.func[expected].ty;
1560 let mem = self.func.add_mem(info);
1561 let args = self.func.push_values(&[addr, expected, desired]);
1562 let inst = self.inst(
1563 InstData { args, flags, extra: Extra::Mem(mem), ..InstData::new(Opcode::Cmpxchg) },
1564 &[ty, Type::I1],
1565 );
1566 let results: Vec<Value> = self.func[inst].results().collect();
1567 let [old, exchanged] = results[..] else { unreachable!("two results were asked for") };
1568 (old, exchanged)
1569 }
1570
1571 pub fn atomic_rmw(
1579 &mut self,
1580 op: RmwOp,
1581 addr: Value,
1582 operand: Value,
1583 info: MemInfo,
1584 flags: Flags,
1585 ) -> Value {
1586 let ty = self.func[operand].ty;
1587 let mem = self.func.add_mem(info);
1588 let args = self.func.push_values(&[addr, operand]);
1589 self.value(
1590 InstData {
1591 args,
1592 flags,
1593 extra: Extra::Rmw(op, mem),
1594 ..InstData::new(Opcode::AtomicRmw)
1595 },
1596 ty,
1597 )
1598 }
1599
1600 pub fn fence(&mut self, order: MemOrder) -> Inst {
1602 self.inst(InstData { extra: Extra::Order(order), ..InstData::new(Opcode::Fence) }, &[])
1603 }
1604
1605 pub fn prefetch(&mut self, address: Value, hint: PrefetchHint) -> Inst {
1612 let args = self.func.push_values(&[address]);
1613 self.inst(
1614 InstData { args, extra: Extra::Prefetch(hint), ..InstData::new(Opcode::Prefetch) },
1615 &[],
1616 )
1617 }
1618
1619 pub fn jump(&mut self, target: Block, args: &[Value]) -> Inst {
1621 let call = self.block_call(target, args);
1622 let targets = self.func.push_block_calls(&[call]);
1623 self.inst(InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::Jump) }, &[])
1624 }
1625
1626 pub fn block_addr(&mut self, target: Block) -> Value {
1632 let call = self.block_call(target, &[]);
1633 let targets = self.func.push_block_calls(&[call]);
1634 self.value(
1635 InstData { extra: Extra::Targets(targets), ..InstData::new(Opcode::BlockAddr) },
1636 Type::PTR,
1637 )
1638 }
1639
1640 pub fn indirect_br(&mut self, addr: Value, targets: &[Block]) -> Inst {
1646 let calls: Vec<BlockCall> =
1647 targets.iter().map(|&target| self.block_call(target, &[])).collect();
1648 let targets = self.func.push_block_calls(&calls);
1649 let args = self.func.push_values(&[addr]);
1650 self.inst(
1651 InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::IndirectBr) },
1652 &[],
1653 )
1654 }
1655
1656 pub fn br_if(
1658 &mut self,
1659 cond: Value,
1660 then_block: Block,
1661 then_args: &[Value],
1662 else_block: Block,
1663 else_args: &[Value],
1664 ) -> Inst {
1665 let then_call = self.block_call(then_block, then_args);
1666 let else_call = self.block_call(else_block, else_args);
1667 let targets = self.func.push_block_calls(&[then_call, else_call]);
1668 let args = self.func.push_values(&[cond]);
1669 self.inst(
1670 InstData { args, extra: Extra::Targets(targets), ..InstData::new(Opcode::BrIf) },
1671 &[],
1672 )
1673 }
1674
1675 pub fn switch(&mut self, value: Value, default: Block, cases: &[(i128, Block)]) -> Inst {
1682 let ty = self.func[value].ty.lane();
1683 let mut calls = vec![self.block_call(default, &[])];
1684 let mut values = Vec::with_capacity(cases.len());
1685 for &(value, block) in cases {
1686 calls.push(self.block_call(block, &[]));
1687 values.push(Imm::int(value, ty));
1688 }
1689 let targets = self.func.push_block_calls(&calls);
1690 let cases = self.func.push_imms(&values);
1691 let info = self.func.add_switch(SwitchInfo { targets, cases });
1692 let args = self.func.push_values(&[value]);
1693 self.inst(
1694 InstData { args, extra: Extra::Switch(info), ..InstData::new(Opcode::Switch) },
1695 &[],
1696 )
1697 }
1698
1699 pub fn ret(&mut self, values: &[Value]) -> Inst {
1701 let args = self.func.push_values(values);
1702 self.inst(InstData { args, ..InstData::new(Opcode::Return) }, &[])
1703 }
1704
1705 pub fn unreachable(&mut self) -> Inst {
1707 self.inst(InstData::new(Opcode::Unreachable), &[])
1708 }
1709
1710 pub fn call(&mut self, callee: Symbol, signature: Sig, args: &[Value]) -> Inst {
1712 self.call_varargs(callee, signature, args, &[])
1713 }
1714
1715 pub fn call_varargs(
1721 &mut self,
1722 callee: Symbol,
1723 signature: Sig,
1724 args: &[Value],
1725 varargs: &[Abi],
1726 ) -> Inst {
1727 let varargs = self.func.push_abis(varargs);
1728 let info = self.func.add_call(CallInfo { callee: Some(callee), signature, varargs });
1729 let returns: Vec<Type> = self.func[signature].return_types().collect();
1730 let args = self.func.push_values(args);
1731 self.inst(
1732 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::Call) },
1733 &returns,
1734 )
1735 }
1736
1737 pub fn inline_asm(
1743 &mut self,
1744 info: AsmInfo,
1745 args: &[Value],
1746 results: &[Type],
1747 flags: Flags,
1748 ) -> Inst {
1749 let info = self.func.add_asm(info);
1750 let args = self.func.push_values(args);
1751 self.inst(
1752 InstData { args, flags, extra: Extra::Asm(info), ..InstData::new(Opcode::InlineAsm) },
1753 results,
1754 )
1755 }
1756
1757 fn block_call(&mut self, block: Block, args: &[Value]) -> BlockCall {
1758 BlockCall::new(block, self.func.push_values(args))
1759 }
1760}
1761
1762#[cfg(test)]
1763mod tests {
1764 use rucc_base::Interner;
1765
1766 use super::*;
1767 use crate::inst::BlockCallList;
1768 use crate::{MemOrder, Restrict};
1769
1770 fn sum() -> (Func, Block, Block, Block) {
1772 let mut names = Interner::new();
1773 let i32_ = Type::int(32);
1774 let mut func = Func::new(
1775 names.intern("sum"),
1776 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
1777 );
1778
1779 let entry = func.create_block();
1780 let n = func.append_param(entry, i32_);
1781 let header = func.create_block();
1782 let acc = func.append_param(header, i32_);
1783 let i = func.append_param(header, i32_);
1784 let exit = func.create_block();
1785 let result = func.append_param(exit, i32_);
1786
1787 let mut b = Builder::new(&mut func, entry);
1788 let zero = b.iconst(i32_, 0);
1789 let cmp = b.icmp(IntPred::Sle, n, zero);
1790 b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
1791
1792 let mut b = Builder::new(&mut func, header);
1793 let one = b.iconst(i32_, 1);
1794 let next = b.binary(Opcode::Add, i, one, Flags::NSW);
1795 let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
1796 let done = b.icmp(IntPred::Sge, next, n);
1797 b.br_if(done, exit, &[total], header, &[total, next]);
1798
1799 let mut b = Builder::new(&mut func, exit);
1800 b.ret(&[result]);
1801
1802 (func, entry, header, exit)
1803 }
1804
1805 #[test]
1806 fn the_blocks_come_back_in_the_order_they_were_made() {
1807 let (func, entry, header, exit) = sum();
1808 assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, header, exit]);
1809 assert_eq!(func.entry(), Some(entry));
1810 }
1811
1812 #[test]
1813 fn a_removed_block_is_gone_from_the_layout_and_so_is_what_was_in_it() {
1814 let (mut func, entry, header, exit) = sum();
1815 let inside: Vec<Inst> = func.insts(header).collect();
1816 func.remove_block(header);
1817 assert_eq!(func.blocks().collect::<Vec<_>>(), [entry, exit]);
1818 assert_eq!(func.entry(), Some(entry));
1819 assert_eq!(func[entry].next, Some(exit));
1820 assert_eq!(func[exit].prev, Some(entry));
1821 assert!(inside.iter().all(|&inst| func.block_of(inst).is_none()));
1823 assert!(func.insts(header).next().is_none());
1824 }
1825
1826 #[test]
1827 fn each_block_holds_what_was_appended_to_it() {
1828 let (func, entry, header, exit) = sum();
1829 let opcodes =
1830 |block| func.insts(block).map(|inst| func[inst].opcode.name()).collect::<Vec<_>>();
1831 assert_eq!(opcodes(entry), ["iconst", "icmp", "br_if"]);
1832 assert_eq!(opcodes(header), ["iconst", "add", "add", "icmp", "br_if"]);
1833 assert_eq!(opcodes(exit), ["return"]);
1834 }
1835
1836 #[test]
1837 fn dropping_the_results_in_front_moves_the_first_one_along_and_renumbers_the_rest() {
1838 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1843 let types = [Type::int(32), Type::int(64), Type::MEM];
1844 let inst = func.create_inst(InstData::new(Opcode::Call), &types, Span::DUMMY);
1845 let before: Vec<Value> = func[inst].results().collect();
1846 func.drop_results(inst, 1);
1847 assert_eq!(func[inst].results().collect::<Vec<_>>(), before[1..]);
1848 assert_eq!(func[before[1]].def, Def::Result { inst, index: 0 });
1849 assert_eq!(func[before[2]].def, Def::Result { inst, index: 1 });
1850 assert_eq!(func.mem_out(inst), Some(before[2]));
1851 }
1852
1853 #[test]
1854 fn dropping_every_result_leaves_an_instruction_that_produces_nothing() {
1855 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1856 let inst = func.create_inst(InstData::new(Opcode::Call), &[Type::int(32)], Span::DUMMY);
1857 func.drop_results(inst, 1);
1858 assert_eq!(func[inst].results().count(), 0);
1859 assert_eq!(func[inst].first_result, None);
1860 }
1861
1862 #[test]
1863 fn asm_ends_a_block_when_it_has_labels_and_not_otherwise() {
1864 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
1867 let block = func.create_block();
1868 let plain = func.add_asm(AsmInfo {
1869 template: Symbol::from_raw(0),
1870 constraints: Symbol::from_raw(0),
1871 clobbers: Symbol::from_raw(0),
1872 targets: BlockCallList::EMPTY,
1873 });
1874 let call = BlockCall::to(block);
1875 let targets = func.push_block_calls(&[call]);
1876 let labelled = func.add_asm(AsmInfo {
1877 template: Symbol::from_raw(0),
1878 constraints: Symbol::from_raw(0),
1879 clobbers: Symbol::from_raw(0),
1880 targets,
1881 });
1882
1883 let mut make = |extra| {
1884 let data = InstData { extra, ..InstData::new(Opcode::InlineAsm) };
1885 func.create_inst(data, &[], Span::DUMMY)
1886 };
1887 let plain = make(Extra::Asm(plain));
1888 let labelled = make(Extra::Asm(labelled));
1889 assert!(!func.is_terminator(plain));
1890 assert!(func.is_terminator(labelled));
1891 }
1892
1893 #[test]
1894 fn every_block_ends_in_its_terminator() {
1895 let (func, entry, header, exit) = sum();
1896 for block in [entry, header, exit] {
1897 let last = func.terminator(block).expect("a terminator");
1898 assert_eq!(Some(last), func.insts(block).last());
1899 }
1900 }
1901
1902 #[test]
1903 fn a_branch_carries_the_arguments_the_block_takes() {
1904 let (func, entry, header, _) = sum();
1905 let br = func.terminator(entry).expect("a terminator");
1906 let calls: Vec<BlockCall> = func.successors(br).collect();
1907 assert_eq!(calls.len(), 2);
1908 assert_eq!(calls[1].block, header);
1910 assert_eq!(func[calls[1].args].len(), 2);
1911 assert_eq!(func[header].params.len(), 2);
1912 assert_eq!(func[calls[0].args].len(), 1);
1913 }
1914
1915 #[test]
1916 fn a_value_knows_what_defined_it() {
1917 let (func, entry, _, _) = sum();
1918 let first = func.insts(entry).next().expect("an instruction");
1919 let value = func[first].first_result.expect("a result");
1920 assert_eq!(func[value].def, Def::Result { inst: first, index: 0 });
1921 assert_eq!(func[value].ty, Type::int(32));
1922
1923 let param = func[entry].params[0];
1924 assert_eq!(func[param].def, Def::Param { block: entry, index: 0 });
1925 }
1926
1927 #[test]
1928 fn a_comparison_produces_one_bit() {
1929 let (func, entry, _, _) = sum();
1930 let cmp = func.insts(entry).nth(1).expect("the comparison");
1931 let value = func[cmp].first_result.expect("a result");
1932 assert_eq!(func[value].ty, Type::I1);
1933 assert_eq!(func[cmp].extra, Extra::IntPred(IntPred::Sle));
1934 }
1935
1936 #[test]
1937 fn flags_ride_along_on_the_instruction_that_was_given_them() {
1938 let (func, _, header, _) = sum();
1939 let add = func.insts(header).nth(1).expect("the addition");
1940 assert_eq!(func[add].flags, Flags::NSW);
1941 let cmp = func.insts(header).nth(3).expect("the comparison");
1942 assert_eq!(func[cmp].flags, Flags::NONE);
1943 }
1944
1945 #[test]
1946 fn removing_an_instruction_takes_it_out_of_the_middle() {
1947 let (mut func, _, header, _) = sum();
1948 let add = func.insts(header).nth(1).expect("the addition");
1949 func.remove_inst(add);
1950 let opcodes: Vec<&str> = func.insts(header).map(|inst| func[inst].opcode.name()).collect();
1951 assert_eq!(opcodes, ["iconst", "add", "icmp", "br_if"]);
1952 assert_eq!(func.block_of(add), None);
1953 }
1954
1955 #[test]
1956 fn removing_the_first_and_the_last_keeps_the_ends_right() {
1957 let (mut func, entry, _, _) = sum();
1958 let first = func.insts(entry).next().expect("an instruction");
1959 let last = func.terminator(entry).expect("a terminator");
1960 func.remove_inst(first);
1961 func.remove_inst(last);
1962 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1963 assert_eq!(opcodes, ["icmp"]);
1964 assert_eq!(func[entry].first, func[entry].last);
1965 }
1966
1967 #[test]
1968 fn removing_the_only_instruction_empties_the_block() {
1969 let (mut func, _, _, exit) = sum();
1970 let only = func.insts(exit).next().expect("an instruction");
1971 func.remove_inst(only);
1972 assert_eq!(func.insts(exit).count(), 0);
1973 assert_eq!(func[exit].first, None);
1974 assert_eq!(func[exit].last, None);
1975 }
1976
1977 #[test]
1978 fn inserting_before_puts_it_in_the_right_place() {
1979 let (mut func, entry, _, _) = sum();
1980 let cmp = func.insts(entry).nth(1).expect("the comparison");
1981 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1982 func.insert_before(made, cmp);
1983 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
1984 assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
1985 }
1986
1987 #[test]
1988 fn inserting_before_the_first_makes_it_the_first() {
1989 let (mut func, entry, _, _) = sum();
1990 let first = func.insts(entry).next().expect("an instruction");
1991 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
1992 func.insert_before(made, first);
1993 assert_eq!(func.insts(entry).next(), Some(made));
1994 assert_eq!(func[entry].first, Some(made));
1995 }
1996
1997 #[test]
1998 fn inserting_after_puts_it_in_the_right_place() {
1999 let (mut func, entry, _, _) = sum();
2000 let first = func.insts(entry).next().expect("an instruction");
2001 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
2002 func.insert_after(made, first);
2003 let opcodes: Vec<&str> = func.insts(entry).map(|inst| func[inst].opcode.name()).collect();
2004 assert_eq!(opcodes, ["iconst", "unreachable", "icmp", "br_if"]);
2005 assert_eq!(func[entry].first, Some(first));
2006 }
2007
2008 #[test]
2009 #[should_panic(expected = "nothing goes after a terminator")]
2010 fn inserting_after_the_terminator_is_refused() {
2011 let (mut func, entry, _, _) = sum();
2014 let last = func.insts(entry).last().expect("a terminator");
2015 let made = func.create_inst(InstData::new(Opcode::Unreachable), &[], Span::DUMMY);
2016 func.insert_after(made, last);
2017 }
2018
2019 #[test]
2020 fn a_list_grows_in_place_while_it_is_the_last_thing_in_the_pool() {
2021 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2022 let block = func.create_block();
2023 let a = func.append_param(block, Type::int(32));
2024 let b = func.append_param(block, Type::int(32));
2025 let list = func.push_values(&[a]);
2026 let grown = func.append_arg(list, b);
2027 assert_eq!(func[grown], [a, b]);
2028 assert_eq!(grown.as_usize_range().start, list.as_usize_range().start);
2029 }
2030
2031 #[test]
2032 fn a_list_is_copied_when_something_is_behind_it() {
2033 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2034 let block = func.create_block();
2035 let a = func.append_param(block, Type::int(32));
2036 let b = func.append_param(block, Type::int(32));
2037 let list = func.push_values(&[a, a]);
2038 let behind = func.push_values(&[b]);
2039 let grown = func.append_arg(list, b);
2040 assert_eq!(func[grown], [a, a, b]);
2041 assert_eq!(func[list], [a, a], "the old run is still readable");
2042 assert_eq!(func[behind], [b], "and so is what was behind it");
2043 assert_ne!(grown.as_usize_range().start, list.as_usize_range().start);
2044 }
2045
2046 #[test]
2047 fn a_parameter_added_late_is_the_next_one_along() {
2048 let (mut func, entry, header, _) = sum();
2052 let extra = func.append_param(header, Type::int(32));
2053 assert_eq!(func[header].params.len(), 3);
2054 assert_eq!(func[extra].def, Def::Param { block: header, index: 2 });
2055
2056 let br = func.terminator(entry).expect("a terminator");
2057 let call = func.successors(br).nth(1).expect("the branch to the header");
2058 let grown = func.append_arg(call.args, extra);
2059 assert_eq!(func[grown].len(), 3);
2060 }
2061
2062 #[test]
2063 fn a_span_rides_along_with_the_instruction() {
2064 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2065 let block = func.create_block();
2066 let span = Span::new(10, 20);
2067 let mut b = Builder::new(&mut func, block).at(span);
2068 let value = b.iconst(Type::int(32), 7);
2069 let inst = match func[value].def {
2070 Def::Result { inst, .. } => inst,
2071 Def::Param { .. } => unreachable!("a constant is not a parameter"),
2072 };
2073 assert_eq!(func.span(inst), span);
2074 }
2075
2076 #[test]
2077 fn a_store_produces_nothing_and_a_load_produces_one_value() {
2078 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2079 let block = func.create_block();
2080 let addr = func.append_param(block, Type::PTR);
2081 let info = MemInfo {
2082 size: 4,
2083 align: 4,
2084 order: MemOrder::NotAtomic,
2085 tbaa: None,
2086 owns: 0,
2087 restrict: Restrict::NONE,
2088 };
2089 let mut b = Builder::new(&mut func, block);
2090 let value = b.load(Type::int(32), addr, info, Flags::NONE);
2091 let store = b.store(value, addr, info, Flags::VOLATILE);
2092 assert_eq!(func[store].results, 0);
2093 assert_eq!(func[store].flags, Flags::VOLATILE);
2094 assert_eq!(func[value].ty, Type::int(32));
2095 }
2096
2097 #[test]
2098 fn memory_remembers_which_declaration_asked_for_it_and_which_did_not() {
2099 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2100 let info = MemInfo {
2101 size: 4,
2102 align: 4,
2103 order: MemOrder::NotAtomic,
2104 tbaa: None,
2105 owns: 0,
2106 restrict: Restrict::NONE,
2107 };
2108 let first = func.add_mem(info);
2109 let second = func.add_mem(info);
2110 let third = func.add_mem(info);
2111
2112 func.declare_mem(third, 7);
2115 func.declare_mem(first, 2);
2116 func.declare_mem(first, 9);
2118
2119 assert_eq!(func.mem_decl(first), Some(2));
2120 assert_eq!(func.mem_decl(third), Some(7));
2121 assert_eq!(func.mem_decl(second), None);
2123 }
2124
2125 #[test]
2131 fn a_value_says_which_declarations_it_is_and_a_rename_carries_them_over() {
2132 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2133 let block = func.create_block();
2134 let first = func.append_param(block, Type::int(32));
2135 let second = func.append_param(block, Type::int(32));
2136 let third = func.append_param(block, Type::int(32));
2137
2138 func.declare_value(third, 7);
2141 func.declare_value(first, 2);
2142 func.declare_value(first, 2);
2144
2145 assert_eq!(func.value_decls(first).collect::<Vec<u32>>(), vec![2]);
2146 assert_eq!(func.value_decls(third).collect::<Vec<u32>>(), vec![7]);
2147 assert_eq!(func.value_decls(second).count(), 0);
2149
2150 func.rename_value(third, first);
2154 assert_eq!(func.value_decls(first).collect::<Vec<u32>>(), vec![2]);
2155 assert_eq!(
2156 func.value_starts(first).collect::<Vec<Start>>(),
2157 vec![Start { decl: 7, block, after: None }]
2158 );
2159 assert_eq!(func.value_decls(third).count(), 0);
2160
2161 func.rename_value(second, third);
2163 assert_eq!(func.value_decls(third).count(), 0);
2164 }
2165
2166 #[test]
2169 fn a_start_part_of_the_way_through_is_kept_and_a_rename_carries_it_over() {
2170 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2171 let block = func.create_block();
2172 let first = func.append_param(block, Type::int(32));
2173 let second = func.append_param(block, Type::int(32));
2174 let one = Builder::new(&mut func, block).iconst(Type::int(32), 1);
2175 let inst = func.insts(block).next().expect("the constant");
2176
2177 let top = Start { decl: 4, block, after: None };
2178 let later = Start { decl: 4, block, after: Some(inst) };
2179 func.declare_value_from(first, later);
2180 func.declare_value_from(first, top);
2181 func.declare_value_from(first, later);
2183 assert_eq!(func.value_starts(first).collect::<Vec<_>>(), vec![later, top]);
2184 assert_eq!(func.value_starts(one).count(), 0);
2185
2186 func.rename_value(first, second);
2187 assert_eq!(func.value_starts(second).collect::<Vec<_>>(), vec![later, top]);
2188 assert_eq!(func.value_starts(first).count(), 0);
2189 }
2190
2191 fn places(func: &Func, value: Value) -> Vec<Option<(Block, Option<Inst>)>> {
2193 func.value_starts(value).map(|start| func.start_place(start)).collect()
2194 }
2195
2196 #[test]
2199 fn a_start_after_an_instruction_that_is_taken_out_stays_where_it_was() {
2200 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2201 let block = func.create_block();
2202 let value = func.append_param(block, Type::int(32));
2203 let mut build = Builder::new(&mut func, block);
2204 build.iconst(Type::int(32), 1);
2205 build.iconst(Type::int(32), 2);
2206 let [one, two]: [Inst; 2] = func.insts(block).collect::<Vec<_>>().try_into().expect("two");
2207 func.declare_value_from(value, Start { decl: 4, block, after: Some(two) });
2208
2209 func.remove_inst(two);
2210 assert_eq!(places(&func, value), [Some((block, Some(one)))]);
2211 let data = func[one];
2212 let fresh = func.create_inst(data, &[Type::int(32)], Span::DUMMY);
2213 func.insert_before(fresh, one);
2214 func.remove_inst(one);
2215 assert_eq!(places(&func, value), [Some((block, Some(fresh)))]);
2216 func.remove_inst(fresh);
2217 assert_eq!(places(&func, value), [Some((block, None))]);
2218 }
2219
2220 #[test]
2223 fn a_start_after_an_instruction_a_pass_moves_goes_with_it() {
2224 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2225 let top = func.create_block();
2226 let below = func.create_block();
2227 let value = func.append_param(top, Type::int(32));
2228 let mut build = Builder::new(&mut func, below);
2229 build.iconst(Type::int(32), 1);
2230 build.iconst(Type::int(32), 2);
2231 let [one, two]: [Inst; 2] = func.insts(below).collect::<Vec<_>>().try_into().expect("two");
2232 func.declare_value_from(value, Start { decl: 4, block: below, after: Some(two) });
2233
2234 for inst in [one, two] {
2236 func.remove_inst(inst);
2237 func.append_inst(top, inst);
2238 }
2239 func.remove_block(below);
2240 assert_eq!(places(&func, value), [Some((top, Some(two)))]);
2241 }
2242
2243 #[test]
2246 fn a_start_at_the_top_of_a_block_that_is_merged_away_is_carried_over() {
2247 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2248 let top = func.create_block();
2249 let below = func.create_block();
2250 let value = func.append_param(top, Type::int(32));
2251 Builder::new(&mut func, top).iconst(Type::int(32), 1);
2252 Builder::new(&mut func, below).iconst(Type::int(32), 2);
2253 let last = func.insts(top).next();
2254 func.declare_value_from(value, Start { decl: 4, block: below, after: None });
2255
2256 for inst in func.insts(below).collect::<Vec<_>>() {
2257 func.remove_inst(inst);
2258 func.append_inst(top, inst);
2259 }
2260 func.carry_starts(below, top, last);
2261 func.remove_block(below);
2262 assert_eq!(places(&func, value), [Some((top, last))]);
2263 }
2264
2265 #[test]
2268 fn a_name_on_a_value_whose_instruction_goes_becomes_a_start_and_comes_back_if_it_returns() {
2269 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2270 let block = func.create_block();
2271 let mut build = Builder::new(&mut func, block);
2272 let kept = build.iconst(Type::int(32), 1);
2273 let moved = build.iconst(Type::int(32), 2);
2274 let first = func.insts(block).next().expect("one");
2275 let second = func.insts(block).nth(1).expect("two");
2276 func.declare_value(kept, 4);
2277 func.declare_value(moved, 5);
2278
2279 func.remove_inst(first);
2280 assert_eq!(func.value_decls(kept).count(), 0);
2281 assert_eq!(places(&func, kept), [Some((block, None))]);
2282
2283 func.remove_inst(second);
2284 func.append_inst(block, second);
2285 assert_eq!(func.value_decls(moved).collect::<Vec<_>>(), [5]);
2286 assert_eq!(func.value_starts(moved).count(), 0);
2287 }
2288
2289 #[test]
2292 fn a_rename_into_an_earlier_value_starts_the_declaration_where_the_later_one_was() {
2293 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2294 let block = func.create_block();
2295 let mut build = Builder::new(&mut func, block);
2296 let early = build.iconst(Type::int(32), 1);
2297 build.iconst(Type::int(32), 3);
2298 let late = build.iconst(Type::int(32), 1);
2299 let middle = func.insts(block).nth(1).expect("three");
2300 func.declare_value(late, 4);
2301
2302 func.rename_value(late, early);
2303 assert_eq!(func.value_decls(early).count(), 0);
2304 assert_eq!(places(&func, early), [Some((block, Some(middle)))]);
2305 }
2306
2307 #[test]
2309 fn moving_starts_moves_the_ones_asked_for_and_no_others() {
2310 let mut func = Func::new(Symbol::from_raw(0), Signature::new());
2311 let block = func.create_block();
2312 let mut build = Builder::new(&mut func, block);
2313 let from = build.iconst(Type::int(32), 1);
2314 let into = build.iconst(Type::int(32), 2);
2315 let first = func.insts(block).next();
2316 let starts: Vec<Start> = (5..8).map(|decl| Start { decl, block, after: first }).collect();
2317 for &start in &starts {
2318 func.declare_value_from(from, start);
2319 }
2320
2321 func.move_starts(from, into, &starts[1..2]);
2322 assert_eq!(func.value_starts(from).collect::<Vec<Start>>(), [starts[0], starts[2]]);
2323 assert_eq!(func.value_starts(into).collect::<Vec<Start>>(), [starts[1]]);
2324 }
2325
2326 #[test]
2327 fn a_call_produces_what_its_signature_returns() {
2328 let mut names = Interner::new();
2329 let mut func = Func::new(names.intern("caller"), Signature::new());
2330 let sig = func.add_signature(
2331 Signature::new().with_params(&[Type::int(32)]).with_returns(&[Type::int(64)]),
2332 );
2333 let block = func.create_block();
2334 let arg = func.append_param(block, Type::int(32));
2335 let callee = names.intern("callee");
2336 let mut b = Builder::new(&mut func, block);
2337 let call = b.call(callee, sig, &[arg]);
2338 assert_eq!(func[call].results, 1);
2339 let value = func[call].first_result.expect("a result");
2340 assert_eq!(func[value].ty, Type::int(64));
2341 assert_eq!(func[call].extra, Extra::Call(Idx::new(0)));
2342 }
2343
2344 #[test]
2345 fn the_counts_are_what_was_made() {
2346 let (func, _, _, _) = sum();
2347 let counts = func.counts();
2348 assert_eq!(counts.blocks, 3);
2349 assert_eq!(counts.insts, 9);
2350 assert_eq!(counts.values, 4 + 6);
2353 }
2354
2355 #[test]
2356 #[should_panic(expected = "the instruction is in a block")]
2357 fn appending_an_instruction_twice_is_refused() {
2358 let (mut func, entry, _, _) = sum();
2359 let first = func.insts(entry).next().expect("an instruction");
2360 func.append_inst(entry, first);
2361 }
2362
2363 #[test]
2364 #[should_panic(expected = "the instruction is not in a block")]
2365 fn removing_an_instruction_twice_is_refused() {
2366 let (mut func, entry, _, _) = sum();
2367 let first = func.insts(entry).next().expect("an instruction");
2368 func.remove_inst(first);
2369 func.remove_inst(first);
2370 }
2371
2372 fn threaded() -> (Func, Inst, Inst) {
2374 let mut names = Interner::new();
2375 let i32_ = Type::int(32);
2376 let mut func = Func::new(
2377 names.intern("thread"),
2378 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
2379 );
2380 let entry = func.create_block();
2381 let addr = func.append_param(entry, Type::PTR);
2382 let info = MemInfo {
2383 size: 4,
2384 align: 4,
2385 order: MemOrder::NotAtomic,
2386 tbaa: None,
2387 owns: 0,
2388 restrict: Restrict::NONE,
2389 };
2390
2391 let mut b = Builder::new(&mut func, entry);
2392 let start = b.mem_entry();
2393 let seven = b.iconst(i32_, 7);
2394 let store = b.store(seven, addr, info, Flags::NONE);
2395 let value = b.load(i32_, addr, info, Flags::NONE);
2396 let Def::Result { inst: load, .. } = func[value].def else {
2397 panic!("the load produced it");
2398 };
2399
2400 let store = func.with_mem(store, start);
2401 let after = func.mem_out(store).expect("a store makes a new version");
2402 let load = func.with_mem(load, after);
2403 (func, store, load)
2404 }
2405
2406 #[test]
2407 fn threading_memory_puts_it_last_and_leaves_everything_else_where_it_was() {
2408 let (func, store, load) = threaded();
2409 assert_eq!(func.mem_in(store), func.mem_out(store).map(|_| func[func[store].args][2]));
2410 assert_eq!(func[func[store].args].len(), 3);
2411 assert!(func.carries_mem(store));
2412 assert!(func.carries_mem(load));
2413
2414 assert_eq!(func[func[load].args][0], func[func.entry().expect("an entry")].params[0]);
2417 assert_eq!(func.mem_in(load), func.mem_out(store));
2418 assert_eq!(func.mem_out(load), None);
2419 }
2420
2421 #[test]
2422 #[should_panic(expected = "this is already on the memory chain")]
2423 fn threading_memory_through_the_same_instruction_twice_is_refused() {
2424 let (mut func, store, _) = threaded();
2425 let start = func.mem_in(store).expect("it was threaded");
2426 func.with_mem(store, start);
2427 }
2428
2429 fn copies() -> (Func, Inst, Inst) {
2432 let mut names = Interner::new();
2433 let i64_ = Type::int(64);
2434 let mut func = Func::new(
2435 names.intern("copies"),
2436 Signature::new().with_params(&[Type::PTR, Type::PTR, i64_]),
2437 );
2438 let entry = func.create_block();
2439 let to = func.append_param(entry, Type::PTR);
2440 let from = func.append_param(entry, Type::PTR);
2441 let length = func.append_param(entry, i64_);
2442 let info = MemInfo {
2443 size: 16,
2444 align: 4,
2445 order: MemOrder::NotAtomic,
2446 tbaa: None,
2447 owns: 0,
2448 restrict: Restrict::NONE,
2449 };
2450
2451 let mut b = Builder::new(&mut func, entry);
2452 let start = b.mem_entry();
2453 let mem = b.func().add_mem(info);
2454 let args = b.func().push_values(&[to, from]);
2455 let fixed =
2456 b.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
2457 let mem = b.func().add_mem(MemInfo { size: 0, ..info });
2458 let args = b.func().push_values(&[to, from, length]);
2459 let computed =
2460 b.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
2461 b.ret(&[]);
2462
2463 let fixed = func.with_mem(fixed, start);
2464 let after = func.mem_out(fixed).expect("a copy makes a new version");
2465 let computed = func.with_mem(computed, after);
2466 (func, fixed, computed)
2467 }
2468
2469 #[test]
2470 fn a_bulk_copy_hands_back_its_length_where_it_has_one_and_nothing_where_the_payload_has_it() {
2471 let (func, fixed, computed) = copies();
2472 let params = &func[func.entry().expect("an entry")].params;
2473 let [to, from, length] = params[..] else { panic!("three of them were appended") };
2474
2475 let bulk = func.bulk(fixed).expect("a memcpy is one");
2476 assert_eq!((bulk.to, bulk.with, bulk.length), (to, from, None));
2477
2478 let bulk = func.bulk(computed).expect("a memcpy is one");
2479 assert_eq!((bulk.to, bulk.with, bulk.length), (to, from, Some(length)));
2480 }
2481
2482 #[test]
2483 fn an_instruction_that_is_not_a_bulk_operation_is_not_taken_apart_as_one() {
2484 let (func, store, load) = threaded();
2485 assert_eq!(func.bulk(store), None);
2486 assert_eq!(func.bulk(load), None);
2487 }
2488}