1use crate::ssa::{SSABuilder, SideEffects};
3use crate::variable::Variable;
4use core::fmt::{self, Debug};
5use cranelift_codegen::cursor::{Cursor, FuncCursor};
6use cranelift_codegen::entity::{EntityRef, EntitySet, SecondaryMap};
7use cranelift_codegen::ir;
8use cranelift_codegen::ir::condcodes::IntCC;
9use cranelift_codegen::ir::{
10 types, AbiParam, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, ExtFuncData,
11 ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Inst, InstBuilder,
12 InstBuilderBase, InstructionData, JumpTable, JumpTableData, LibCall, MemFlags, RelSourceLoc,
13 SigRef, Signature, StackSlot, StackSlotData, Type, Value, ValueLabel, ValueLabelAssignments,
14 ValueLabelStart,
15};
16use cranelift_codegen::isa::TargetFrontendConfig;
17use cranelift_codegen::packed_option::PackedOption;
18
19#[derive(Default)]
25pub struct FunctionBuilderContext {
26 ssa: SSABuilder,
27 status: SecondaryMap<Block, BlockStatus>,
28 types: SecondaryMap<Variable, Type>,
29}
30
31pub struct FunctionBuilder<'a> {
33 pub func: &'a mut Function,
36
37 srcloc: ir::SourceLoc,
39
40 func_ctx: &'a mut FunctionBuilderContext,
41 position: PackedOption<Block>,
42}
43
44#[derive(Clone, Default, Eq, PartialEq)]
45enum BlockStatus {
46 #[default]
48 Empty,
49 Partial,
51 Filled,
53}
54
55impl FunctionBuilderContext {
56 pub fn new() -> Self {
59 Self::default()
60 }
61
62 fn clear(&mut self) {
63 self.ssa.clear();
64 self.status.clear();
65 self.types.clear();
66 }
67
68 fn is_empty(&self) -> bool {
69 self.ssa.is_empty() && self.status.is_empty() && self.types.is_empty()
70 }
71}
72
73pub struct FuncInstBuilder<'short, 'long: 'short> {
76 builder: &'short mut FunctionBuilder<'long>,
77 block: Block,
78}
79
80impl<'short, 'long> FuncInstBuilder<'short, 'long> {
81 fn new(builder: &'short mut FunctionBuilder<'long>, block: Block) -> Self {
82 Self { builder, block }
83 }
84}
85
86impl<'short, 'long> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long> {
87 fn data_flow_graph(&self) -> &DataFlowGraph {
88 &self.builder.func.dfg
89 }
90
91 fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
92 &mut self.builder.func.dfg
93 }
94
95 fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'short mut DataFlowGraph) {
99 self.builder.ensure_inserted_block();
101
102 let inst = self.builder.func.dfg.make_inst(data.clone());
103 self.builder.func.dfg.make_inst_results(inst, ctrl_typevar);
104 self.builder.func.layout.append_inst(inst, self.block);
105 if !self.builder.srcloc.is_default() {
106 self.builder.func.set_srcloc(inst, self.builder.srcloc);
107 }
108
109 match &self.builder.func.dfg.insts[inst] {
110 ir::InstructionData::Jump {
111 destination: dest, ..
112 } => {
113 let block = dest.block(&self.builder.func.dfg.value_lists);
116 self.builder.declare_successor(block, inst);
117 }
118
119 ir::InstructionData::Brif {
120 blocks: [branch_then, branch_else],
121 ..
122 } => {
123 let block_then = branch_then.block(&self.builder.func.dfg.value_lists);
124 let block_else = branch_else.block(&self.builder.func.dfg.value_lists);
125
126 self.builder.declare_successor(block_then, inst);
127 if block_then != block_else {
128 self.builder.declare_successor(block_else, inst);
129 }
130 }
131
132 ir::InstructionData::BranchTable { table, .. } => {
133 let pool = &self.builder.func.dfg.value_lists;
134
135 let mut unique = EntitySet::<Block>::new();
139 for dest_block in self
140 .builder
141 .func
142 .stencil
143 .dfg
144 .jump_tables
145 .get(*table)
146 .expect("you are referencing an undeclared jump table")
147 .all_branches()
148 {
149 let block = dest_block.block(pool);
150 if !unique.insert(block) {
151 continue;
152 }
153
154 self.builder
157 .func_ctx
158 .ssa
159 .declare_block_predecessor(block, inst);
160 }
161 }
162
163 inst => debug_assert!(!inst.opcode().is_branch()),
164 }
165
166 if data.opcode().is_terminator() {
167 self.builder.fill_current_block()
168 }
169 (inst, &mut self.builder.func.dfg)
170 }
171}
172
173#[derive(Debug, Copy, Clone, PartialEq, Eq)]
174pub enum UseVariableError {
176 UsedBeforeDeclared(Variable),
177}
178
179impl fmt::Display for UseVariableError {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 match self {
182 UseVariableError::UsedBeforeDeclared(variable) => {
183 write!(
184 f,
185 "variable {} was used before it was defined",
186 variable.index()
187 )?;
188 }
189 }
190 Ok(())
191 }
192}
193
194impl std::error::Error for UseVariableError {}
195
196#[derive(Debug, Copy, Clone, Eq, PartialEq)]
197pub enum DeclareVariableError {
199 DeclaredMultipleTimes(Variable),
200}
201
202impl std::error::Error for DeclareVariableError {}
203
204impl fmt::Display for DeclareVariableError {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 match self {
207 DeclareVariableError::DeclaredMultipleTimes(variable) => {
208 write!(
209 f,
210 "variable {} was declared multiple times",
211 variable.index()
212 )?;
213 }
214 }
215 Ok(())
216 }
217}
218
219#[derive(Debug, Copy, Clone, Eq, PartialEq)]
220pub enum DefVariableError {
222 TypeMismatch(Variable, Value),
228 DefinedBeforeDeclared(Variable),
231}
232
233impl fmt::Display for DefVariableError {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 match self {
236 DefVariableError::TypeMismatch(variable, value) => {
237 write!(
238 f,
239 "the types of variable {} and value {} are not the same.
240 The `Value` supplied to `def_var` must be of the same type as
241 the variable was declared to be of in `declare_var`.",
242 variable.index(),
243 value.as_u32()
244 )?;
245 }
246 DefVariableError::DefinedBeforeDeclared(variable) => {
247 write!(
248 f,
249 "the value of variabe {} was declared before it was defined",
250 variable.index()
251 )?;
252 }
253 }
254 Ok(())
255 }
256}
257
258impl<'a> FunctionBuilder<'a> {
292 pub fn new(func: &'a mut Function, func_ctx: &'a mut FunctionBuilderContext) -> Self {
295 debug_assert!(func_ctx.is_empty());
296 Self {
297 func,
298 srcloc: Default::default(),
299 func_ctx,
300 position: Default::default(),
301 }
302 }
303
304 pub fn current_block(&self) -> Option<Block> {
306 self.position.expand()
307 }
308
309 pub fn set_srcloc(&mut self, srcloc: ir::SourceLoc) {
311 self.srcloc = srcloc;
312 }
313
314 pub fn create_block(&mut self) -> Block {
316 let block = self.func.dfg.make_block();
317 self.func_ctx.ssa.declare_block(block);
318 block
319 }
320
321 pub fn set_cold_block(&mut self, block: Block) {
326 self.func.layout.set_cold(block);
327 }
328
329 pub fn insert_block_after(&mut self, block: Block, after: Block) {
331 self.func.layout.insert_block_after(block, after);
332 }
333
334 pub fn switch_to_block(&mut self, block: Block) {
342 debug_assert!(
344 self.position.is_none()
345 || self.is_unreachable()
346 || self.is_pristine(self.position.unwrap())
347 || self.is_filled(self.position.unwrap()),
348 "you have to fill your block before switching"
349 );
350 debug_assert!(
352 !self.is_filled(block),
353 "you cannot switch to a block which is already filled"
354 );
355
356 self.position = PackedOption::from(block);
358 }
359
360 pub fn seal_block(&mut self, block: Block) {
366 let side_effects = self.func_ctx.ssa.seal_block(block, self.func);
367 self.handle_ssa_side_effects(side_effects);
368 }
369
370 pub fn seal_all_blocks(&mut self) {
377 let side_effects = self.func_ctx.ssa.seal_all_blocks(self.func);
378 self.handle_ssa_side_effects(side_effects);
379 }
380
381 pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> {
385 if self.func_ctx.types[var] != types::INVALID {
386 return Err(DeclareVariableError::DeclaredMultipleTimes(var));
387 }
388 self.func_ctx.types[var] = ty;
389 Ok(())
390 }
391
392 pub fn declare_var(&mut self, var: Variable, ty: Type) {
395 self.try_declare_var(var, ty)
396 .unwrap_or_else(|_| panic!("the variable {:?} has been declared multiple times", var))
397 }
398
399 pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {
402 self.ensure_inserted_block();
407
408 let (val, side_effects) = {
409 let ty = *self
410 .func_ctx
411 .types
412 .get(var)
413 .ok_or(UseVariableError::UsedBeforeDeclared(var))?;
414 debug_assert_ne!(
415 ty,
416 types::INVALID,
417 "variable {:?} is used but its type has not been declared",
418 var
419 );
420 self.func_ctx
421 .ssa
422 .use_var(self.func, var, ty, self.position.unwrap())
423 };
424 self.handle_ssa_side_effects(side_effects);
425 Ok(val)
426 }
427
428 pub fn use_var(&mut self, var: Variable) -> Value {
431 self.try_use_var(var).unwrap_or_else(|_| {
432 panic!(
433 "variable {:?} is used but its type has not been declared",
434 var
435 )
436 })
437 }
438
439 pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
443 let var_ty = *self
444 .func_ctx
445 .types
446 .get(var)
447 .ok_or(DefVariableError::DefinedBeforeDeclared(var))?;
448 if var_ty != self.func.dfg.value_type(val) {
449 return Err(DefVariableError::TypeMismatch(var, val));
450 }
451
452 self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
453 Ok(())
454 }
455
456 pub fn def_var(&mut self, var: Variable, val: Value) {
459 self.try_def_var(var, val)
460 .unwrap_or_else(|error| match error {
461 DefVariableError::TypeMismatch(var, val) => {
462 panic!(
463 "declared type of variable {:?} doesn't match type of value {}",
464 var, val
465 );
466 }
467 DefVariableError::DefinedBeforeDeclared(var) => {
468 panic!(
469 "variable {:?} is used but its type has not been declared",
470 var
471 );
472 }
473 })
474 }
475
476 pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
480 if let Some(values_labels) = self.func.stencil.dfg.values_labels.as_mut() {
481 use alloc::collections::btree_map::Entry;
482
483 let start = ValueLabelStart {
484 from: RelSourceLoc::from_base_offset(self.func.params.base_srcloc(), self.srcloc),
485 label,
486 };
487
488 match values_labels.entry(val) {
489 Entry::Occupied(mut e) => match e.get_mut() {
490 ValueLabelAssignments::Starts(starts) => starts.push(start),
491 _ => panic!("Unexpected ValueLabelAssignments at this stage"),
492 },
493 Entry::Vacant(e) => {
494 e.insert(ValueLabelAssignments::Starts(vec![start]));
495 }
496 }
497 }
498 }
499
500 pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
502 self.func.create_jump_table(data)
503 }
504
505 pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
508 self.func.create_sized_stack_slot(data)
509 }
510
511 pub fn create_dynamic_stack_slot(&mut self, data: DynamicStackSlotData) -> DynamicStackSlot {
514 self.func.create_dynamic_stack_slot(data)
515 }
516
517 pub fn import_signature(&mut self, signature: Signature) -> SigRef {
519 self.func.import_signature(signature)
520 }
521
522 pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef {
524 self.func.import_function(data)
525 }
526
527 pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue {
529 self.func.create_global_value(data)
530 }
531
532 pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> {
535 let block = self
536 .position
537 .expect("Please call switch_to_block before inserting instructions");
538 FuncInstBuilder::new(self, block)
539 }
540
541 pub fn ensure_inserted_block(&mut self) {
543 let block = self.position.unwrap();
544 if self.is_pristine(block) {
545 if !self.func.layout.is_block_inserted(block) {
546 self.func.layout.append_block(block);
547 }
548 self.func_ctx.status[block] = BlockStatus::Partial;
549 } else {
550 debug_assert!(
551 !self.is_filled(block),
552 "you cannot add an instruction to a block already filled"
553 );
554 }
555 }
556
557 pub fn cursor(&mut self) -> FuncCursor {
562 self.ensure_inserted_block();
563 FuncCursor::new(self.func)
564 .with_srcloc(self.srcloc)
565 .at_bottom(self.position.unwrap())
566 }
567
568 pub fn append_block_params_for_function_params(&mut self, block: Block) {
572 debug_assert!(
573 !self.func_ctx.ssa.has_any_predecessors(block),
574 "block parameters for function parameters should only be added to the entry block"
575 );
576
577 debug_assert!(
580 self.is_pristine(block),
581 "You can't add block parameters after adding any instruction"
582 );
583
584 for argtyp in &self.func.stencil.signature.params {
585 self.func
586 .stencil
587 .dfg
588 .append_block_param(block, argtyp.value_type);
589 }
590 }
591
592 pub fn append_block_params_for_function_returns(&mut self, block: Block) {
596 debug_assert!(
599 self.is_pristine(block),
600 "You can't add block parameters after adding any instruction"
601 );
602
603 for argtyp in &self.func.stencil.signature.returns {
604 self.func
605 .stencil
606 .dfg
607 .append_block_param(block, argtyp.value_type);
608 }
609 }
610
611 pub fn finalize(self) {
616 #[cfg(debug_assertions)]
618 {
619 for block in self.func_ctx.status.keys() {
620 if !self.is_pristine(block) {
621 assert!(
622 self.func_ctx.ssa.is_sealed(block),
623 "FunctionBuilder finalized, but block {} is not sealed",
624 block,
625 );
626 assert!(
627 self.is_filled(block),
628 "FunctionBuilder finalized, but block {} is not filled",
629 block,
630 );
631 }
632 }
633 }
634
635 #[cfg(debug_assertions)]
637 {
638 for block in self.func_ctx.status.keys() {
640 if let Err((inst, msg)) = self.func.is_block_basic(block) {
641 let inst_str = self.func.dfg.display_inst(inst);
642 panic!(
643 "{} failed basic block invariants on {}: {}",
644 block, inst_str, msg
645 );
646 }
647 }
648 }
649
650 self.func_ctx.clear();
653 }
654}
655
656impl<'a> FunctionBuilder<'a> {
662 pub fn block_params(&self, block: Block) -> &[Value] {
665 self.func.dfg.block_params(block)
666 }
667
668 pub fn signature(&self, sigref: SigRef) -> Option<&Signature> {
670 self.func.dfg.signatures.get(sigref)
671 }
672
673 pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
679 debug_assert!(
680 self.is_pristine(block),
681 "You can't add block parameters after adding any instruction"
682 );
683 self.func.dfg.append_block_param(block, ty)
684 }
685
686 pub fn inst_results(&self, inst: Inst) -> &[Value] {
688 self.func.dfg.inst_results(inst)
689 }
690
691 pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {
696 let dfg = &mut self.func.dfg;
697 for block in dfg.insts[inst].branch_destination_mut(&mut dfg.jump_tables) {
698 if block.block(&dfg.value_lists) == old_block {
699 self.func_ctx.ssa.remove_block_predecessor(old_block, inst);
700 block.set_block(new_block, &mut dfg.value_lists);
701 self.func_ctx.ssa.declare_block_predecessor(new_block, inst);
702 }
703 }
704 }
705
706 pub fn is_unreachable(&self) -> bool {
710 let is_entry = match self.func.layout.entry_block() {
711 None => false,
712 Some(entry) => self.position.unwrap() == entry,
713 };
714 !is_entry
715 && self.func_ctx.ssa.is_sealed(self.position.unwrap())
716 && !self
717 .func_ctx
718 .ssa
719 .has_any_predecessors(self.position.unwrap())
720 }
721
722 fn is_pristine(&self, block: Block) -> bool {
725 self.func_ctx.status[block] == BlockStatus::Empty
726 }
727
728 fn is_filled(&self, block: Block) -> bool {
731 self.func_ctx.status[block] == BlockStatus::Filled
732 }
733}
734
735impl<'a> FunctionBuilder<'a> {
737 pub fn call_memcpy(
744 &mut self,
745 config: TargetFrontendConfig,
746 dest: Value,
747 src: Value,
748 size: Value,
749 ) {
750 let pointer_type = config.pointer_type();
751 let signature = {
752 let mut s = Signature::new(config.default_call_conv);
753 s.params.push(AbiParam::new(pointer_type));
754 s.params.push(AbiParam::new(pointer_type));
755 s.params.push(AbiParam::new(pointer_type));
756 self.import_signature(s)
757 };
758
759 let libc_memcpy = self.import_function(ExtFuncData {
760 name: ExternalName::LibCall(LibCall::Memcpy),
761 signature,
762 colocated: false,
763 });
764
765 self.ins().call(libc_memcpy, &[dest, src, size]);
766 }
767
768 pub fn emit_small_memory_copy(
777 &mut self,
778 config: TargetFrontendConfig,
779 dest: Value,
780 src: Value,
781 size: u64,
782 dest_align: u8,
783 src_align: u8,
784 non_overlapping: bool,
785 mut flags: MemFlags,
786 ) {
787 const THRESHOLD: u64 = 4;
789
790 if size == 0 {
791 return;
792 }
793
794 let access_size = greatest_divisible_power_of_two(size);
795 assert!(
796 access_size.is_power_of_two(),
797 "`size` is not a power of two"
798 );
799 assert!(
800 access_size >= u64::from(::core::cmp::min(src_align, dest_align)),
801 "`size` is smaller than `dest` and `src`'s alignment value."
802 );
803
804 let (access_size, int_type) = if access_size <= 8 {
805 (access_size, Type::int((access_size * 8) as u16).unwrap())
806 } else {
807 (8, types::I64)
808 };
809
810 let load_and_store_amount = size / access_size;
811
812 if load_and_store_amount > THRESHOLD {
813 let size_value = self.ins().iconst(config.pointer_type(), size as i64);
814 if non_overlapping {
815 self.call_memcpy(config, dest, src, size_value);
816 } else {
817 self.call_memmove(config, dest, src, size_value);
818 }
819 return;
820 }
821
822 if u64::from(src_align) >= access_size && u64::from(dest_align) >= access_size {
823 flags.set_aligned();
824 }
825
826 let registers: smallvec::SmallVec<[_; THRESHOLD as usize]> = (0..load_and_store_amount)
829 .map(|i| {
830 let offset = (access_size * i) as i32;
831 (self.ins().load(int_type, flags, src, offset), offset)
832 })
833 .collect();
834
835 for (value, offset) in registers {
836 self.ins().store(flags, value, dest, offset);
837 }
838 }
839
840 pub fn call_memset(
844 &mut self,
845 config: TargetFrontendConfig,
846 buffer: Value,
847 ch: Value,
848 size: Value,
849 ) {
850 let pointer_type = config.pointer_type();
851 let signature = {
852 let mut s = Signature::new(config.default_call_conv);
853 s.params.push(AbiParam::new(pointer_type));
854 s.params.push(AbiParam::new(types::I32));
855 s.params.push(AbiParam::new(pointer_type));
856 self.import_signature(s)
857 };
858
859 let libc_memset = self.import_function(ExtFuncData {
860 name: ExternalName::LibCall(LibCall::Memset),
861 signature,
862 colocated: false,
863 });
864
865 let ch = self.ins().uextend(types::I32, ch);
866 self.ins().call(libc_memset, &[buffer, ch, size]);
867 }
868
869 pub fn emit_small_memset(
873 &mut self,
874 config: TargetFrontendConfig,
875 buffer: Value,
876 ch: u8,
877 size: u64,
878 buffer_align: u8,
879 mut flags: MemFlags,
880 ) {
881 const THRESHOLD: u64 = 4;
883
884 if size == 0 {
885 return;
886 }
887
888 let access_size = greatest_divisible_power_of_two(size);
889 assert!(
890 access_size.is_power_of_two(),
891 "`size` is not a power of two"
892 );
893 assert!(
894 access_size >= u64::from(buffer_align),
895 "`size` is smaller than `dest` and `src`'s alignment value."
896 );
897
898 let (access_size, int_type) = if access_size <= 8 {
899 (access_size, Type::int((access_size * 8) as u16).unwrap())
900 } else {
901 (8, types::I64)
902 };
903
904 let load_and_store_amount = size / access_size;
905
906 if load_and_store_amount > THRESHOLD {
907 let ch = self.ins().iconst(types::I8, i64::from(ch));
908 let size = self.ins().iconst(config.pointer_type(), size as i64);
909 self.call_memset(config, buffer, ch, size);
910 } else {
911 if u64::from(buffer_align) >= access_size {
912 flags.set_aligned();
913 }
914
915 let ch = u64::from(ch);
916 let raw_value = if int_type == types::I64 {
917 ch * 0x0101010101010101_u64
918 } else if int_type == types::I32 {
919 ch * 0x01010101_u64
920 } else if int_type == types::I16 {
921 (ch << 8) | ch
922 } else {
923 assert_eq!(int_type, types::I8);
924 ch
925 };
926
927 let value = self.ins().iconst(int_type, raw_value as i64);
928 for i in 0..load_and_store_amount {
929 let offset = (access_size * i) as i32;
930 self.ins().store(flags, value, buffer, offset);
931 }
932 }
933 }
934
935 pub fn call_memmove(
940 &mut self,
941 config: TargetFrontendConfig,
942 dest: Value,
943 source: Value,
944 size: Value,
945 ) {
946 let pointer_type = config.pointer_type();
947 let signature = {
948 let mut s = Signature::new(config.default_call_conv);
949 s.params.push(AbiParam::new(pointer_type));
950 s.params.push(AbiParam::new(pointer_type));
951 s.params.push(AbiParam::new(pointer_type));
952 self.import_signature(s)
953 };
954
955 let libc_memmove = self.import_function(ExtFuncData {
956 name: ExternalName::LibCall(LibCall::Memmove),
957 signature,
958 colocated: false,
959 });
960
961 self.ins().call(libc_memmove, &[dest, source, size]);
962 }
963
964 pub fn call_memcmp(
973 &mut self,
974 config: TargetFrontendConfig,
975 left: Value,
976 right: Value,
977 size: Value,
978 ) -> Value {
979 let pointer_type = config.pointer_type();
980 let signature = {
981 let mut s = Signature::new(config.default_call_conv);
982 s.params.reserve(3);
983 s.params.push(AbiParam::new(pointer_type));
984 s.params.push(AbiParam::new(pointer_type));
985 s.params.push(AbiParam::new(pointer_type));
986 s.returns.push(AbiParam::new(types::I32));
987 self.import_signature(s)
988 };
989
990 let libc_memcmp = self.import_function(ExtFuncData {
991 name: ExternalName::LibCall(LibCall::Memcmp),
992 signature,
993 colocated: false,
994 });
995
996 let call = self.ins().call(libc_memcmp, &[left, right, size]);
997 self.func.dfg.first_result(call)
998 }
999
1000 pub fn emit_small_memory_compare(
1013 &mut self,
1014 config: TargetFrontendConfig,
1015 int_cc: IntCC,
1016 left: Value,
1017 right: Value,
1018 size: u64,
1019 left_align: std::num::NonZeroU8,
1020 right_align: std::num::NonZeroU8,
1021 flags: MemFlags,
1022 ) -> Value {
1023 use IntCC::*;
1024 let (zero_cc, empty_imm) = match int_cc {
1025 Equal => (Equal, 1),
1027 NotEqual => (NotEqual, 0),
1028
1029 UnsignedLessThan => (SignedLessThan, 0),
1030 UnsignedGreaterThanOrEqual => (SignedGreaterThanOrEqual, 1),
1031 UnsignedGreaterThan => (SignedGreaterThan, 0),
1032 UnsignedLessThanOrEqual => (SignedLessThanOrEqual, 1),
1033
1034 SignedLessThan
1035 | SignedGreaterThanOrEqual
1036 | SignedGreaterThan
1037 | SignedLessThanOrEqual => {
1038 panic!("Signed comparison {} not supported by memcmp", int_cc)
1039 }
1040 };
1041
1042 if size == 0 {
1043 return self.ins().iconst(types::I8, empty_imm);
1044 }
1045
1046 if let Some(small_type) = size.try_into().ok().and_then(Type::int_with_byte_size) {
1048 if let Equal | NotEqual = zero_cc {
1049 let mut left_flags = flags;
1050 if size == left_align.get() as u64 {
1051 left_flags.set_aligned();
1052 }
1053 let mut right_flags = flags;
1054 if size == right_align.get() as u64 {
1055 right_flags.set_aligned();
1056 }
1057 let left_val = self.ins().load(small_type, left_flags, left, 0);
1058 let right_val = self.ins().load(small_type, right_flags, right, 0);
1059 return self.ins().icmp(int_cc, left_val, right_val);
1060 } else if small_type == types::I8 {
1061 let mut aligned_flags = flags;
1066 aligned_flags.set_aligned();
1067 let left_val = self.ins().load(small_type, aligned_flags, left, 0);
1068 let right_val = self.ins().load(small_type, aligned_flags, right, 0);
1069 return self.ins().icmp(int_cc, left_val, right_val);
1070 }
1071 }
1072
1073 let pointer_type = config.pointer_type();
1074 let size = self.ins().iconst(pointer_type, size as i64);
1075 let cmp = self.call_memcmp(config, left, right, size);
1076 self.ins().icmp_imm(zero_cc, cmp, 0)
1077 }
1078}
1079
1080fn greatest_divisible_power_of_two(size: u64) -> u64 {
1081 (size as i64 & -(size as i64)) as u64
1082}
1083
1084impl<'a> FunctionBuilder<'a> {
1086 fn fill_current_block(&mut self) {
1088 self.func_ctx.status[self.position.unwrap()] = BlockStatus::Filled;
1089 }
1090
1091 fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) {
1092 self.func_ctx
1093 .ssa
1094 .declare_block_predecessor(dest_block, jump_inst);
1095 }
1096
1097 fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
1098 for modified_block in side_effects.instructions_added_to_blocks {
1099 if self.is_pristine(modified_block) {
1100 self.func_ctx.status[modified_block] = BlockStatus::Partial;
1101 }
1102 }
1103 }
1104}
1105
1106#[cfg(test)]
1107mod tests {
1108 use super::greatest_divisible_power_of_two;
1109 use crate::frontend::{
1110 DeclareVariableError, DefVariableError, FunctionBuilder, FunctionBuilderContext,
1111 UseVariableError,
1112 };
1113 use crate::Variable;
1114 use alloc::string::ToString;
1115 use cranelift_codegen::entity::EntityRef;
1116 use cranelift_codegen::ir::condcodes::IntCC;
1117 use cranelift_codegen::ir::{types::*, UserFuncName};
1118 use cranelift_codegen::ir::{AbiParam, Function, InstBuilder, MemFlags, Signature, Value};
1119 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
1120 use cranelift_codegen::settings;
1121 use cranelift_codegen::verifier::verify_function;
1122 use target_lexicon::PointerWidth;
1123
1124 fn sample_function(lazy_seal: bool) {
1125 let mut sig = Signature::new(CallConv::SystemV);
1126 sig.returns.push(AbiParam::new(I32));
1127 sig.params.push(AbiParam::new(I32));
1128
1129 let mut fn_ctx = FunctionBuilderContext::new();
1130 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1131 {
1132 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1133
1134 let block0 = builder.create_block();
1135 let block1 = builder.create_block();
1136 let block2 = builder.create_block();
1137 let block3 = builder.create_block();
1138 let x = Variable::new(0);
1139 let y = Variable::new(1);
1140 let z = Variable::new(2);
1141 builder.declare_var(x, I32);
1142 builder.declare_var(y, I32);
1143 builder.declare_var(z, I32);
1144 builder.append_block_params_for_function_params(block0);
1145
1146 builder.switch_to_block(block0);
1147 if !lazy_seal {
1148 builder.seal_block(block0);
1149 }
1150 {
1151 let tmp = builder.block_params(block0)[0]; builder.def_var(x, tmp);
1153 }
1154 {
1155 let tmp = builder.ins().iconst(I32, 2);
1156 builder.def_var(y, tmp);
1157 }
1158 {
1159 let arg1 = builder.use_var(x);
1160 let arg2 = builder.use_var(y);
1161 let tmp = builder.ins().iadd(arg1, arg2);
1162 builder.def_var(z, tmp);
1163 }
1164 builder.ins().jump(block1, &[]);
1165
1166 builder.switch_to_block(block1);
1167 {
1168 let arg1 = builder.use_var(y);
1169 let arg2 = builder.use_var(z);
1170 let tmp = builder.ins().iadd(arg1, arg2);
1171 builder.def_var(z, tmp);
1172 }
1173 {
1174 let arg = builder.use_var(y);
1175 builder.ins().brif(arg, block3, &[], block2, &[]);
1176 }
1177
1178 builder.switch_to_block(block2);
1179 if !lazy_seal {
1180 builder.seal_block(block2);
1181 }
1182 {
1183 let arg1 = builder.use_var(z);
1184 let arg2 = builder.use_var(x);
1185 let tmp = builder.ins().isub(arg1, arg2);
1186 builder.def_var(z, tmp);
1187 }
1188 {
1189 let arg = builder.use_var(y);
1190 builder.ins().return_(&[arg]);
1191 }
1192
1193 builder.switch_to_block(block3);
1194 if !lazy_seal {
1195 builder.seal_block(block3);
1196 }
1197
1198 {
1199 let arg1 = builder.use_var(y);
1200 let arg2 = builder.use_var(x);
1201 let tmp = builder.ins().isub(arg1, arg2);
1202 builder.def_var(y, tmp);
1203 }
1204 builder.ins().jump(block1, &[]);
1205 if !lazy_seal {
1206 builder.seal_block(block1);
1207 }
1208
1209 if lazy_seal {
1210 builder.seal_all_blocks();
1211 }
1212
1213 builder.finalize();
1214 }
1215
1216 let flags = settings::Flags::new(settings::builder());
1217 if let Err(errors) = verify_function(&func, &flags) {
1219 panic!("{}\n{}", func.display(), errors)
1220 }
1221 }
1222
1223 #[test]
1224 fn sample() {
1225 sample_function(false)
1226 }
1227
1228 #[test]
1229 fn sample_with_lazy_seal() {
1230 sample_function(true)
1231 }
1232
1233 #[track_caller]
1234 fn check(func: &Function, expected_ir: &str) {
1235 let actual_ir = func.display().to_string();
1236 assert!(
1237 expected_ir == actual_ir,
1238 "Expected:\n{}\nGot:\n{}",
1239 expected_ir,
1240 actual_ir
1241 );
1242 }
1243
1244 fn systemv_frontend_config() -> TargetFrontendConfig {
1246 TargetFrontendConfig {
1247 default_call_conv: CallConv::SystemV,
1248 pointer_width: PointerWidth::U64,
1249 }
1250 }
1251
1252 #[test]
1253 fn memcpy() {
1254 let frontend_config = systemv_frontend_config();
1255 let mut sig = Signature::new(frontend_config.default_call_conv);
1256 sig.returns.push(AbiParam::new(I32));
1257
1258 let mut fn_ctx = FunctionBuilderContext::new();
1259 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1260 {
1261 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1262
1263 let block0 = builder.create_block();
1264 let x = Variable::new(0);
1265 let y = Variable::new(1);
1266 let z = Variable::new(2);
1267 builder.declare_var(x, frontend_config.pointer_type());
1268 builder.declare_var(y, frontend_config.pointer_type());
1269 builder.declare_var(z, I32);
1270 builder.append_block_params_for_function_params(block0);
1271 builder.switch_to_block(block0);
1272
1273 let src = builder.use_var(x);
1274 let dest = builder.use_var(y);
1275 let size = builder.use_var(y);
1276 builder.call_memcpy(frontend_config, dest, src, size);
1277 builder.ins().return_(&[size]);
1278
1279 builder.seal_all_blocks();
1280 builder.finalize();
1281 }
1282
1283 check(
1284 &func,
1285 "function %sample() -> i32 system_v {
1286 sig0 = (i64, i64, i64) system_v
1287 fn0 = %Memcpy sig0
1288
1289block0:
1290 v3 = iconst.i64 0
1291 v1 -> v3
1292 v2 = iconst.i64 0
1293 v0 -> v2
1294 call fn0(v1, v0, v1) ; v1 = 0, v0 = 0, v1 = 0
1295 return v1 ; v1 = 0
1296}
1297",
1298 );
1299 }
1300
1301 #[test]
1302 fn small_memcpy() {
1303 let frontend_config = systemv_frontend_config();
1304 let mut sig = Signature::new(frontend_config.default_call_conv);
1305 sig.returns.push(AbiParam::new(I32));
1306
1307 let mut fn_ctx = FunctionBuilderContext::new();
1308 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1309 {
1310 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1311
1312 let block0 = builder.create_block();
1313 let x = Variable::new(0);
1314 let y = Variable::new(16);
1315 builder.declare_var(x, frontend_config.pointer_type());
1316 builder.declare_var(y, frontend_config.pointer_type());
1317 builder.append_block_params_for_function_params(block0);
1318 builder.switch_to_block(block0);
1319
1320 let src = builder.use_var(x);
1321 let dest = builder.use_var(y);
1322 let size = 8;
1323 builder.emit_small_memory_copy(
1324 frontend_config,
1325 dest,
1326 src,
1327 size,
1328 8,
1329 8,
1330 true,
1331 MemFlags::new(),
1332 );
1333 builder.ins().return_(&[dest]);
1334
1335 builder.seal_all_blocks();
1336 builder.finalize();
1337 }
1338
1339 check(
1340 &func,
1341 "function %sample() -> i32 system_v {
1342block0:
1343 v4 = iconst.i64 0
1344 v1 -> v4
1345 v3 = iconst.i64 0
1346 v0 -> v3
1347 v2 = load.i64 aligned v0 ; v0 = 0
1348 store aligned v2, v1 ; v1 = 0
1349 return v1 ; v1 = 0
1350}
1351",
1352 );
1353 }
1354
1355 #[test]
1356 fn not_so_small_memcpy() {
1357 let frontend_config = systemv_frontend_config();
1358 let mut sig = Signature::new(frontend_config.default_call_conv);
1359 sig.returns.push(AbiParam::new(I32));
1360
1361 let mut fn_ctx = FunctionBuilderContext::new();
1362 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1363 {
1364 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1365
1366 let block0 = builder.create_block();
1367 let x = Variable::new(0);
1368 let y = Variable::new(16);
1369 builder.declare_var(x, frontend_config.pointer_type());
1370 builder.declare_var(y, frontend_config.pointer_type());
1371 builder.append_block_params_for_function_params(block0);
1372 builder.switch_to_block(block0);
1373
1374 let src = builder.use_var(x);
1375 let dest = builder.use_var(y);
1376 let size = 8192;
1377 builder.emit_small_memory_copy(
1378 frontend_config,
1379 dest,
1380 src,
1381 size,
1382 8,
1383 8,
1384 true,
1385 MemFlags::new(),
1386 );
1387 builder.ins().return_(&[dest]);
1388
1389 builder.seal_all_blocks();
1390 builder.finalize();
1391 }
1392
1393 check(
1394 &func,
1395 "function %sample() -> i32 system_v {
1396 sig0 = (i64, i64, i64) system_v
1397 fn0 = %Memcpy sig0
1398
1399block0:
1400 v4 = iconst.i64 0
1401 v1 -> v4
1402 v3 = iconst.i64 0
1403 v0 -> v3
1404 v2 = iconst.i64 8192
1405 call fn0(v1, v0, v2) ; v1 = 0, v0 = 0, v2 = 8192
1406 return v1 ; v1 = 0
1407}
1408",
1409 );
1410 }
1411
1412 #[test]
1413 fn small_memset() {
1414 let frontend_config = systemv_frontend_config();
1415 let mut sig = Signature::new(frontend_config.default_call_conv);
1416 sig.returns.push(AbiParam::new(I32));
1417
1418 let mut fn_ctx = FunctionBuilderContext::new();
1419 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1420 {
1421 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1422
1423 let block0 = builder.create_block();
1424 let y = Variable::new(16);
1425 builder.declare_var(y, frontend_config.pointer_type());
1426 builder.append_block_params_for_function_params(block0);
1427 builder.switch_to_block(block0);
1428
1429 let dest = builder.use_var(y);
1430 let size = 8;
1431 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1432 builder.ins().return_(&[dest]);
1433
1434 builder.seal_all_blocks();
1435 builder.finalize();
1436 }
1437
1438 check(
1439 &func,
1440 "function %sample() -> i32 system_v {
1441block0:
1442 v2 = iconst.i64 0
1443 v0 -> v2
1444 v1 = iconst.i64 0x0101_0101_0101_0101
1445 store aligned v1, v0 ; v1 = 0x0101_0101_0101_0101, v0 = 0
1446 return v0 ; v0 = 0
1447}
1448",
1449 );
1450 }
1451
1452 #[test]
1453 fn not_so_small_memset() {
1454 let frontend_config = systemv_frontend_config();
1455 let mut sig = Signature::new(frontend_config.default_call_conv);
1456 sig.returns.push(AbiParam::new(I32));
1457
1458 let mut fn_ctx = FunctionBuilderContext::new();
1459 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1460 {
1461 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1462
1463 let block0 = builder.create_block();
1464 let y = Variable::new(16);
1465 builder.declare_var(y, frontend_config.pointer_type());
1466 builder.append_block_params_for_function_params(block0);
1467 builder.switch_to_block(block0);
1468
1469 let dest = builder.use_var(y);
1470 let size = 8192;
1471 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1472 builder.ins().return_(&[dest]);
1473
1474 builder.seal_all_blocks();
1475 builder.finalize();
1476 }
1477
1478 check(
1479 &func,
1480 "function %sample() -> i32 system_v {
1481 sig0 = (i64, i32, i64) system_v
1482 fn0 = %Memset sig0
1483
1484block0:
1485 v4 = iconst.i64 0
1486 v0 -> v4
1487 v1 = iconst.i8 1
1488 v2 = iconst.i64 8192
1489 v3 = uextend.i32 v1 ; v1 = 1
1490 call fn0(v0, v3, v2) ; v0 = 0, v2 = 8192
1491 return v0 ; v0 = 0
1492}
1493",
1494 );
1495 }
1496
1497 #[test]
1498 fn memcmp() {
1499 use core::str::FromStr;
1500 use cranelift_codegen::isa;
1501
1502 let shared_builder = settings::builder();
1503 let shared_flags = settings::Flags::new(shared_builder);
1504
1505 let triple =
1506 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1507
1508 let target = isa::lookup(triple)
1509 .ok()
1510 .map(|b| b.finish(shared_flags))
1511 .expect("This test requires x86_64 support.")
1512 .expect("Should be able to create backend with default flags");
1513
1514 let mut sig = Signature::new(target.default_call_conv());
1515 sig.returns.push(AbiParam::new(I32));
1516
1517 let mut fn_ctx = FunctionBuilderContext::new();
1518 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1519 {
1520 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1521
1522 let block0 = builder.create_block();
1523 let x = Variable::new(0);
1524 let y = Variable::new(1);
1525 let z = Variable::new(2);
1526 builder.declare_var(x, target.pointer_type());
1527 builder.declare_var(y, target.pointer_type());
1528 builder.declare_var(z, target.pointer_type());
1529 builder.append_block_params_for_function_params(block0);
1530 builder.switch_to_block(block0);
1531
1532 let left = builder.use_var(x);
1533 let right = builder.use_var(y);
1534 let size = builder.use_var(z);
1535 let cmp = builder.call_memcmp(target.frontend_config(), left, right, size);
1536 builder.ins().return_(&[cmp]);
1537
1538 builder.seal_all_blocks();
1539 builder.finalize();
1540 }
1541
1542 check(
1543 &func,
1544 "function %sample() -> i32 system_v {
1545 sig0 = (i64, i64, i64) -> i32 system_v
1546 fn0 = %Memcmp sig0
1547
1548block0:
1549 v6 = iconst.i64 0
1550 v2 -> v6
1551 v5 = iconst.i64 0
1552 v1 -> v5
1553 v4 = iconst.i64 0
1554 v0 -> v4
1555 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 0
1556 return v3
1557}
1558",
1559 );
1560 }
1561
1562 #[test]
1563 fn small_memcmp_zero_size() {
1564 let align_eight = std::num::NonZeroU8::new(8).unwrap();
1565 small_memcmp_helper(
1566 "
1567block0:
1568 v4 = iconst.i64 0
1569 v1 -> v4
1570 v3 = iconst.i64 0
1571 v0 -> v3
1572 v2 = iconst.i8 1
1573 return v2 ; v2 = 1",
1574 |builder, target, x, y| {
1575 builder.emit_small_memory_compare(
1576 target.frontend_config(),
1577 IntCC::UnsignedGreaterThanOrEqual,
1578 x,
1579 y,
1580 0,
1581 align_eight,
1582 align_eight,
1583 MemFlags::new(),
1584 )
1585 },
1586 );
1587 }
1588
1589 #[test]
1590 fn small_memcmp_byte_ugt() {
1591 let align_one = std::num::NonZeroU8::new(1).unwrap();
1592 small_memcmp_helper(
1593 "
1594block0:
1595 v6 = iconst.i64 0
1596 v1 -> v6
1597 v5 = iconst.i64 0
1598 v0 -> v5
1599 v2 = load.i8 aligned v0 ; v0 = 0
1600 v3 = load.i8 aligned v1 ; v1 = 0
1601 v4 = icmp ugt v2, v3
1602 return v4",
1603 |builder, target, x, y| {
1604 builder.emit_small_memory_compare(
1605 target.frontend_config(),
1606 IntCC::UnsignedGreaterThan,
1607 x,
1608 y,
1609 1,
1610 align_one,
1611 align_one,
1612 MemFlags::new(),
1613 )
1614 },
1615 );
1616 }
1617
1618 #[test]
1619 fn small_memcmp_aligned_eq() {
1620 let align_four = std::num::NonZeroU8::new(4).unwrap();
1621 small_memcmp_helper(
1622 "
1623block0:
1624 v6 = iconst.i64 0
1625 v1 -> v6
1626 v5 = iconst.i64 0
1627 v0 -> v5
1628 v2 = load.i32 aligned v0 ; v0 = 0
1629 v3 = load.i32 aligned v1 ; v1 = 0
1630 v4 = icmp eq v2, v3
1631 return v4",
1632 |builder, target, x, y| {
1633 builder.emit_small_memory_compare(
1634 target.frontend_config(),
1635 IntCC::Equal,
1636 x,
1637 y,
1638 4,
1639 align_four,
1640 align_four,
1641 MemFlags::new(),
1642 )
1643 },
1644 );
1645 }
1646
1647 #[test]
1648 fn small_memcmp_ipv6_ne() {
1649 let align_two = std::num::NonZeroU8::new(2).unwrap();
1650 small_memcmp_helper(
1651 "
1652block0:
1653 v6 = iconst.i64 0
1654 v1 -> v6
1655 v5 = iconst.i64 0
1656 v0 -> v5
1657 v2 = load.i128 v0 ; v0 = 0
1658 v3 = load.i128 v1 ; v1 = 0
1659 v4 = icmp ne v2, v3
1660 return v4",
1661 |builder, target, x, y| {
1662 builder.emit_small_memory_compare(
1663 target.frontend_config(),
1664 IntCC::NotEqual,
1665 x,
1666 y,
1667 16,
1668 align_two,
1669 align_two,
1670 MemFlags::new(),
1671 )
1672 },
1673 );
1674 }
1675
1676 #[test]
1677 fn small_memcmp_odd_size_uge() {
1678 let one = std::num::NonZeroU8::new(1).unwrap();
1679 small_memcmp_helper(
1680 "
1681 sig0 = (i64, i64, i64) -> i32 system_v
1682 fn0 = %Memcmp sig0
1683
1684block0:
1685 v6 = iconst.i64 0
1686 v1 -> v6
1687 v5 = iconst.i64 0
1688 v0 -> v5
1689 v2 = iconst.i64 3
1690 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 3
1691 v4 = icmp_imm sge v3, 0
1692 return v4",
1693 |builder, target, x, y| {
1694 builder.emit_small_memory_compare(
1695 target.frontend_config(),
1696 IntCC::UnsignedGreaterThanOrEqual,
1697 x,
1698 y,
1699 3,
1700 one,
1701 one,
1702 MemFlags::new(),
1703 )
1704 },
1705 );
1706 }
1707
1708 fn small_memcmp_helper(
1709 expected: &str,
1710 f: impl FnOnce(&mut FunctionBuilder, &dyn TargetIsa, Value, Value) -> Value,
1711 ) {
1712 use core::str::FromStr;
1713 use cranelift_codegen::isa;
1714
1715 let shared_builder = settings::builder();
1716 let shared_flags = settings::Flags::new(shared_builder);
1717
1718 let triple =
1719 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1720
1721 let target = isa::lookup(triple)
1722 .ok()
1723 .map(|b| b.finish(shared_flags))
1724 .expect("This test requires x86_64 support.")
1725 .expect("Should be able to create backend with default flags");
1726
1727 let mut sig = Signature::new(target.default_call_conv());
1728 sig.returns.push(AbiParam::new(I8));
1729
1730 let mut fn_ctx = FunctionBuilderContext::new();
1731 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1732 {
1733 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1734
1735 let block0 = builder.create_block();
1736 let x = Variable::new(0);
1737 let y = Variable::new(1);
1738 builder.declare_var(x, target.pointer_type());
1739 builder.declare_var(y, target.pointer_type());
1740 builder.append_block_params_for_function_params(block0);
1741 builder.switch_to_block(block0);
1742
1743 let left = builder.use_var(x);
1744 let right = builder.use_var(y);
1745 let ret = f(&mut builder, &*target, left, right);
1746 builder.ins().return_(&[ret]);
1747
1748 builder.seal_all_blocks();
1749 builder.finalize();
1750 }
1751
1752 check(
1753 &func,
1754 &format!("function %sample() -> i8 system_v {{{}\n}}\n", expected),
1755 );
1756 }
1757
1758 #[test]
1759 fn undef_vector_vars() {
1760 let mut sig = Signature::new(CallConv::SystemV);
1761 sig.returns.push(AbiParam::new(I8X16));
1762 sig.returns.push(AbiParam::new(I8X16));
1763 sig.returns.push(AbiParam::new(F32X4));
1764
1765 let mut fn_ctx = FunctionBuilderContext::new();
1766 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1767 {
1768 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1769
1770 let block0 = builder.create_block();
1771 let a = Variable::new(0);
1772 let b = Variable::new(1);
1773 let c = Variable::new(2);
1774 builder.declare_var(a, I8X16);
1775 builder.declare_var(b, I8X16);
1776 builder.declare_var(c, F32X4);
1777 builder.switch_to_block(block0);
1778
1779 let a = builder.use_var(a);
1780 let b = builder.use_var(b);
1781 let c = builder.use_var(c);
1782 builder.ins().return_(&[a, b, c]);
1783
1784 builder.seal_all_blocks();
1785 builder.finalize();
1786 }
1787
1788 check(
1789 &func,
1790 "function %sample() -> i8x16, i8x16, f32x4 system_v {
1791 const0 = 0x00000000000000000000000000000000
1792
1793block0:
1794 v5 = f32const 0.0
1795 v6 = splat.f32x4 v5 ; v5 = 0.0
1796 v2 -> v6
1797 v4 = vconst.i8x16 const0
1798 v1 -> v4
1799 v3 = vconst.i8x16 const0
1800 v0 -> v3
1801 return v0, v1, v2 ; v0 = const0, v1 = const0
1802}
1803",
1804 );
1805 }
1806
1807 #[test]
1808 fn test_greatest_divisible_power_of_two() {
1809 assert_eq!(64, greatest_divisible_power_of_two(64));
1810 assert_eq!(16, greatest_divisible_power_of_two(48));
1811 assert_eq!(8, greatest_divisible_power_of_two(24));
1812 assert_eq!(1, greatest_divisible_power_of_two(25));
1813 }
1814
1815 #[test]
1816 fn try_use_var() {
1817 let sig = Signature::new(CallConv::SystemV);
1818
1819 let mut fn_ctx = FunctionBuilderContext::new();
1820 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1821 {
1822 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1823
1824 let block0 = builder.create_block();
1825 builder.append_block_params_for_function_params(block0);
1826 builder.switch_to_block(block0);
1827
1828 assert_eq!(
1829 builder.try_use_var(Variable::from_u32(0)),
1830 Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))
1831 );
1832
1833 let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
1834
1835 assert_eq!(
1836 builder.try_def_var(Variable::from_u32(0), value),
1837 Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(
1838 0
1839 )))
1840 );
1841
1842 builder.declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32);
1843 assert_eq!(
1844 builder.try_declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32),
1845 Err(DeclareVariableError::DeclaredMultipleTimes(
1846 Variable::from_u32(0)
1847 ))
1848 );
1849 }
1850 }
1851}