1use crate::ssa::{SSABuilder, SideEffects};
3use crate::variable::Variable;
4use alloc::vec::Vec;
5use core::fmt::{self, Debug};
6use cranelift_codegen::cursor::{Cursor, CursorPosition, FuncCursor};
7use cranelift_codegen::entity::{EntityRef, EntitySet, PrimaryMap, SecondaryMap};
8use cranelift_codegen::ir;
9use cranelift_codegen::ir::condcodes::IntCC;
10use cranelift_codegen::ir::{
11 AbiParam, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, ExtFuncData,
12 ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Inst, InstBuilder,
13 InstBuilderBase, InstructionData, JumpTable, JumpTableData, LibCall, MemFlags, RelSourceLoc,
14 SigRef, Signature, StackSlot, StackSlotData, Type, Value, ValueLabel, ValueLabelAssignments,
15 ValueLabelStart, types,
16};
17use cranelift_codegen::isa::TargetFrontendConfig;
18use cranelift_codegen::packed_option::PackedOption;
19use cranelift_codegen::traversals::Dfs;
20use smallvec::SmallVec;
21
22mod safepoints;
23
24#[derive(Default)]
30pub struct FunctionBuilderContext {
31 ssa: SSABuilder,
32 status: SecondaryMap<Block, BlockStatus>,
33 variables: PrimaryMap<Variable, Type>,
34 stack_map_vars: EntitySet<Variable>,
35 stack_map_values: EntitySet<Value>,
36 safepoints: safepoints::SafepointSpiller,
37}
38
39pub struct FunctionBuilder<'a> {
41 pub func: &'a mut Function,
44
45 srcloc: ir::SourceLoc,
47
48 func_ctx: &'a mut FunctionBuilderContext,
49 position: PackedOption<Block>,
50}
51
52#[derive(Clone, Default, Eq, PartialEq)]
53enum BlockStatus {
54 #[default]
56 Empty,
57 Partial,
59 Filled,
61}
62
63impl FunctionBuilderContext {
64 pub fn new() -> Self {
67 Self::default()
68 }
69
70 fn clear(&mut self) {
71 let FunctionBuilderContext {
72 ssa,
73 status,
74 variables,
75 stack_map_vars,
76 stack_map_values,
77 safepoints,
78 } = self;
79 ssa.clear();
80 status.clear();
81 variables.clear();
82 stack_map_values.clear();
83 stack_map_vars.clear();
84 safepoints.clear();
85 }
86
87 fn is_empty(&self) -> bool {
88 self.ssa.is_empty() && self.status.is_empty() && self.variables.is_empty()
89 }
90}
91
92pub struct FuncInstBuilder<'short, 'long: 'short> {
95 builder: &'short mut FunctionBuilder<'long>,
96 block: Block,
97}
98
99impl<'short, 'long> FuncInstBuilder<'short, 'long> {
100 fn new(builder: &'short mut FunctionBuilder<'long>, block: Block) -> Self {
101 Self { builder, block }
102 }
103}
104
105impl<'short, 'long> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long> {
106 fn data_flow_graph(&self) -> &DataFlowGraph {
107 &self.builder.func.dfg
108 }
109
110 fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
111 &mut self.builder.func.dfg
112 }
113
114 fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'short mut DataFlowGraph) {
118 self.builder.ensure_inserted_block();
120
121 let inst = self.builder.func.dfg.make_inst(data);
122 self.builder.func.dfg.make_inst_results(inst, ctrl_typevar);
123 self.builder.func.layout.append_inst(inst, self.block);
124 if !self.builder.srcloc.is_default() {
125 self.builder.func.set_srcloc(inst, self.builder.srcloc);
126 }
127
128 match &self.builder.func.dfg.insts[inst] {
129 ir::InstructionData::Jump {
130 destination: dest, ..
131 } => {
132 let block = dest.block(&self.builder.func.dfg.value_lists);
135 self.builder.declare_successor(block, inst);
136 }
137
138 ir::InstructionData::Brif {
139 blocks: [branch_then, branch_else],
140 ..
141 } => {
142 let block_then = branch_then.block(&self.builder.func.dfg.value_lists);
143 let block_else = branch_else.block(&self.builder.func.dfg.value_lists);
144
145 self.builder.declare_successor(block_then, inst);
146 if block_then != block_else {
147 self.builder.declare_successor(block_else, inst);
148 }
149 }
150
151 ir::InstructionData::BranchTable { table, .. } => {
152 let pool = &self.builder.func.dfg.value_lists;
153
154 let mut unique = EntitySet::<Block>::new();
158 for dest_block in self
159 .builder
160 .func
161 .stencil
162 .dfg
163 .jump_tables
164 .get(*table)
165 .expect("you are referencing an undeclared jump table")
166 .all_branches()
167 {
168 let block = dest_block.block(pool);
169 if !unique.insert(block) {
170 continue;
171 }
172
173 self.builder
176 .func_ctx
177 .ssa
178 .declare_block_predecessor(block, inst);
179 }
180 }
181
182 ir::InstructionData::TryCall { exception, .. }
183 | ir::InstructionData::TryCallIndirect { exception, .. } => {
184 let pool = &self.builder.func.dfg.value_lists;
185
186 let mut unique = EntitySet::<Block>::new();
190 for dest_block in self
191 .builder
192 .func
193 .stencil
194 .dfg
195 .exception_tables
196 .get(*exception)
197 .expect("you are referencing an undeclared exception table")
198 .all_branches()
199 {
200 let block = dest_block.block(pool);
201 if !unique.insert(block) {
202 continue;
203 }
204
205 self.builder
208 .func_ctx
209 .ssa
210 .declare_block_predecessor(block, inst);
211 }
212 }
213
214 inst => assert!(!inst.opcode().is_branch()),
215 }
216
217 if data.opcode().is_terminator() {
218 self.builder.fill_current_block()
219 }
220 (inst, &mut self.builder.func.dfg)
221 }
222}
223
224#[derive(Debug, Copy, Clone, PartialEq, Eq)]
225pub enum UseVariableError {
227 UsedBeforeDeclared(Variable),
228}
229
230impl fmt::Display for UseVariableError {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 match self {
233 UseVariableError::UsedBeforeDeclared(variable) => {
234 write!(
235 f,
236 "variable {} was used before it was defined",
237 variable.index()
238 )?;
239 }
240 }
241 Ok(())
242 }
243}
244
245impl std::error::Error for UseVariableError {}
246
247#[derive(Debug, Copy, Clone, Eq, PartialEq)]
248pub enum DefVariableError {
250 TypeMismatch(Variable, Value),
256 DefinedBeforeDeclared(Variable),
259}
260
261impl fmt::Display for DefVariableError {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 match self {
264 DefVariableError::TypeMismatch(variable, value) => {
265 write!(
266 f,
267 "the types of variable {} and value {} are not the same.
268 The `Value` supplied to `def_var` must be of the same type as
269 the variable was declared to be of in `declare_var`.",
270 variable.index(),
271 value.as_u32()
272 )?;
273 }
274 DefVariableError::DefinedBeforeDeclared(variable) => {
275 write!(
276 f,
277 "the value of variable {} was declared before it was defined",
278 variable.index()
279 )?;
280 }
281 }
282 Ok(())
283 }
284}
285
286impl<'a> FunctionBuilder<'a> {
319 pub fn new(func: &'a mut Function, func_ctx: &'a mut FunctionBuilderContext) -> Self {
322 debug_assert!(func_ctx.is_empty());
323 Self {
324 func,
325 srcloc: Default::default(),
326 func_ctx,
327 position: Default::default(),
328 }
329 }
330
331 pub fn current_block(&self) -> Option<Block> {
333 self.position.expand()
334 }
335
336 pub fn set_srcloc(&mut self, srcloc: ir::SourceLoc) {
338 self.srcloc = srcloc;
339 }
340
341 pub fn srcloc(&self) -> ir::SourceLoc {
343 self.srcloc
344 }
345
346 pub fn create_block(&mut self) -> Block {
348 let block = self.func.dfg.make_block();
349 self.func_ctx.ssa.declare_block(block);
350 block
351 }
352
353 pub fn set_cold_block(&mut self, block: Block) {
358 self.func.layout.set_cold(block);
359 }
360
361 pub fn insert_block_after(&mut self, block: Block, after: Block) {
363 self.func.layout.insert_block_after(block, after);
364 }
365
366 pub fn switch_to_block(&mut self, block: Block) {
374 log::trace!("switch to {block:?}");
375
376 debug_assert!(
378 self.position.is_none()
379 || self.is_unreachable()
380 || self.is_pristine(self.position.unwrap())
381 || self.is_filled(self.position.unwrap()),
382 "you have to fill your block before switching"
383 );
384 debug_assert!(
386 !self.is_filled(block),
387 "you cannot switch to a block which is already filled"
388 );
389
390 self.position = PackedOption::from(block);
392 }
393
394 pub fn seal_block(&mut self, block: Block) {
400 let side_effects = self.func_ctx.ssa.seal_block(block, self.func);
401 self.handle_ssa_side_effects(side_effects);
402 }
403
404 pub fn seal_all_blocks(&mut self) {
411 let side_effects = self.func_ctx.ssa.seal_all_blocks(self.func);
412 self.handle_ssa_side_effects(side_effects);
413 }
414
415 pub fn declare_var(&mut self, ty: Type) -> Variable {
421 self.func_ctx.variables.push(ty)
422 }
423
424 pub fn declare_var_needs_stack_map(&mut self, var: Variable) {
440 log::trace!("declare_var_needs_stack_map({var:?})");
441 let ty = self.func_ctx.variables[var];
442 assert!(ty != types::INVALID);
443 assert!(ty.bytes() <= 16);
444 self.func_ctx.stack_map_vars.insert(var);
445 }
446
447 pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {
450 self.ensure_inserted_block();
455
456 let (val, side_effects) = {
457 let ty = *self
458 .func_ctx
459 .variables
460 .get(var)
461 .ok_or(UseVariableError::UsedBeforeDeclared(var))?;
462 debug_assert_ne!(
463 ty,
464 types::INVALID,
465 "variable {var:?} is used but its type has not been declared"
466 );
467 self.func_ctx
468 .ssa
469 .use_var(self.func, var, ty, self.position.unwrap())
470 };
471 self.handle_ssa_side_effects(side_effects);
472
473 Ok(val)
474 }
475
476 pub fn use_var(&mut self, var: Variable) -> Value {
479 self.try_use_var(var).unwrap_or_else(|_| {
480 panic!("variable {var:?} is used but its type has not been declared")
481 })
482 }
483
484 pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
488 log::trace!("try_def_var: {var:?} = {val:?}");
489
490 let var_ty = *self
491 .func_ctx
492 .variables
493 .get(var)
494 .ok_or(DefVariableError::DefinedBeforeDeclared(var))?;
495 if var_ty != self.func.dfg.value_type(val) {
496 return Err(DefVariableError::TypeMismatch(var, val));
497 }
498
499 self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
500 Ok(())
501 }
502
503 pub fn def_var(&mut self, var: Variable, val: Value) {
506 self.try_def_var(var, val)
507 .unwrap_or_else(|error| match error {
508 DefVariableError::TypeMismatch(var, val) => {
509 panic!("declared type of variable {var:?} doesn't match type of value {val}");
510 }
511 DefVariableError::DefinedBeforeDeclared(var) => {
512 panic!("variable {var:?} is used but its type has not been declared");
513 }
514 })
515 }
516
517 pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
522 if let Some(values_labels) = self.func.stencil.dfg.values_labels.as_mut() {
523 use alloc::collections::btree_map::Entry;
524
525 let start = ValueLabelStart {
526 from: RelSourceLoc::from_base_offset(self.func.params.base_srcloc(), self.srcloc),
527 label,
528 };
529
530 match values_labels.entry(val) {
531 Entry::Occupied(mut e) => match e.get_mut() {
532 ValueLabelAssignments::Starts(starts) => starts.push(start),
533 _ => panic!("Unexpected ValueLabelAssignments at this stage"),
534 },
535 Entry::Vacant(e) => {
536 e.insert(ValueLabelAssignments::Starts(vec![start]));
537 }
538 }
539 }
540 }
541
542 pub fn declare_value_needs_stack_map(&mut self, val: Value) {
555 log::trace!("declare_value_needs_stack_map({val:?})");
556
557 let size = self.func.dfg.value_type(val).bytes();
559 assert!(size <= 16);
560 assert!(size.is_power_of_two());
561
562 self.func_ctx.stack_map_values.insert(val);
563 }
564
565 pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
567 self.func.create_jump_table(data)
568 }
569
570 pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
573 self.func.create_sized_stack_slot(data)
574 }
575
576 pub fn create_dynamic_stack_slot(&mut self, data: DynamicStackSlotData) -> DynamicStackSlot {
581 self.func.create_dynamic_stack_slot(data)
582 }
583
584 pub fn import_signature(&mut self, signature: Signature) -> SigRef {
586 self.func.import_signature(signature)
587 }
588
589 pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef {
591 self.func.import_function(data)
592 }
593
594 pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue {
596 self.func.create_global_value(data)
597 }
598
599 pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> {
602 let block = self
603 .position
604 .expect("Please call switch_to_block before inserting instructions");
605 FuncInstBuilder::new(self, block)
606 }
607
608 pub fn ensure_inserted_block(&mut self) {
610 let block = self.position.unwrap();
611 if self.is_pristine(block) {
612 if !self.func.layout.is_block_inserted(block) {
613 self.func.layout.append_block(block);
614 }
615 self.func_ctx.status[block] = BlockStatus::Partial;
616 } else {
617 debug_assert!(
618 !self.is_filled(block),
619 "you cannot add an instruction to a block already filled"
620 );
621 }
622 }
623
624 pub fn cursor(&mut self) -> FuncCursor<'_> {
629 self.ensure_inserted_block();
630 FuncCursor::new(self.func)
631 .with_srcloc(self.srcloc)
632 .at_bottom(self.position.unwrap())
633 }
634
635 pub fn append_block_params_for_function_params(&mut self, block: Block) {
639 debug_assert!(
640 !self.func_ctx.ssa.has_any_predecessors(block),
641 "block parameters for function parameters should only be added to the entry block"
642 );
643
644 debug_assert!(
647 self.is_pristine(block),
648 "You can't add block parameters after adding any instruction"
649 );
650
651 for argtyp in &self.func.stencil.signature.params {
652 self.func
653 .stencil
654 .dfg
655 .append_block_param(block, argtyp.value_type);
656 }
657 }
658
659 pub fn append_block_params_for_function_returns(&mut self, block: Block) {
663 debug_assert!(
666 self.is_pristine(block),
667 "You can't add block parameters after adding any instruction"
668 );
669
670 for argtyp in &self.func.stencil.signature.returns {
671 self.func
672 .stencil
673 .dfg
674 .append_block_param(block, argtyp.value_type);
675 }
676 }
677
678 pub fn finalize(mut self) {
683 #[cfg(debug_assertions)]
685 {
686 for block in self.func_ctx.status.keys() {
687 if !self.is_pristine(block) {
688 assert!(
689 self.func_ctx.ssa.is_sealed(block),
690 "FunctionBuilder finalized, but block {block} is not sealed",
691 );
692 assert!(
693 self.is_filled(block),
694 "FunctionBuilder finalized, but block {block} is not filled",
695 );
696 }
697 }
698 }
699
700 #[cfg(debug_assertions)]
702 {
703 for block in self.func_ctx.status.keys() {
705 if let Err((inst, msg)) = self.func.is_block_basic(block) {
706 let inst_str = self.func.dfg.display_inst(inst);
707 panic!("{block} failed basic block invariants on {inst_str}: {msg}");
708 }
709 }
710 }
711
712 for var in self.func_ctx.stack_map_vars.iter() {
715 for val in self.func_ctx.ssa.values_for_var(var) {
716 log::trace!("propagating needs-stack-map from {var:?} to {val:?}");
717 debug_assert_eq!(self.func.dfg.value_type(val), self.func_ctx.variables[var]);
718 self.func_ctx.stack_map_values.insert(val);
719 }
720 }
721
722 if !self.func_ctx.stack_map_values.is_empty() {
726 self.func_ctx
727 .safepoints
728 .run(&mut self.func, &self.func_ctx.stack_map_values);
729 }
730
731 self.func_ctx.clear();
734 }
735}
736
737impl<'a> FunctionBuilder<'a> {
743 pub fn block_params(&self, block: Block) -> &[Value] {
746 self.func.dfg.block_params(block)
747 }
748
749 pub fn signature(&self, sigref: SigRef) -> Option<&Signature> {
752 self.func.dfg.signatures.get(sigref)
753 }
754
755 pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
761 debug_assert!(
762 self.is_pristine(block),
763 "You can't add block parameters after adding any instruction"
764 );
765 self.func.dfg.append_block_param(block, ty)
766 }
767
768 pub fn inst_results(&self, inst: Inst) -> &[Value] {
770 self.func.dfg.inst_results(inst)
771 }
772
773 pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {
778 let dfg = &mut self.func.dfg;
779 for block in
780 dfg.insts[inst].branch_destination_mut(&mut dfg.jump_tables, &mut dfg.exception_tables)
781 {
782 if block.block(&dfg.value_lists) == old_block {
783 self.func_ctx.ssa.remove_block_predecessor(old_block, inst);
784 block.set_block(new_block, &mut dfg.value_lists);
785 self.func_ctx.ssa.declare_block_predecessor(new_block, inst);
786 }
787 }
788 }
789
790 pub fn is_unreachable(&self) -> bool {
794 let is_entry = match self.func.layout.entry_block() {
795 None => false,
796 Some(entry) => self.position.unwrap() == entry,
797 };
798 !is_entry
799 && self.func_ctx.ssa.is_sealed(self.position.unwrap())
800 && !self
801 .func_ctx
802 .ssa
803 .has_any_predecessors(self.position.unwrap())
804 }
805
806 fn is_pristine(&self, block: Block) -> bool {
809 self.func_ctx.status[block] == BlockStatus::Empty
810 }
811
812 fn is_filled(&self, block: Block) -> bool {
815 self.func_ctx.status[block] == BlockStatus::Filled
816 }
817}
818
819impl<'a> FunctionBuilder<'a> {
821 pub fn call_memcpy(
828 &mut self,
829 config: TargetFrontendConfig,
830 dest: Value,
831 src: Value,
832 size: Value,
833 ) {
834 let pointer_type = config.pointer_type();
835 let signature = {
836 let mut s = Signature::new(config.default_call_conv);
837 s.params.push(AbiParam::new(pointer_type));
838 s.params.push(AbiParam::new(pointer_type));
839 s.params.push(AbiParam::new(pointer_type));
840 s.returns.push(AbiParam::new(pointer_type));
841 self.import_signature(s)
842 };
843
844 let libc_memcpy = self.import_function(ExtFuncData {
845 name: ExternalName::LibCall(LibCall::Memcpy),
846 signature,
847 colocated: false,
848 });
849
850 self.ins().call(libc_memcpy, &[dest, src, size]);
851 }
852
853 pub fn emit_small_memory_copy(
862 &mut self,
863 config: TargetFrontendConfig,
864 dest: Value,
865 src: Value,
866 size: u64,
867 dest_align: u8,
868 src_align: u8,
869 non_overlapping: bool,
870 mut flags: MemFlags,
871 ) {
872 const THRESHOLD: u64 = 4;
874
875 if size == 0 {
876 return;
877 }
878
879 let access_size = greatest_divisible_power_of_two(size);
880 assert!(
881 access_size.is_power_of_two(),
882 "`size` is not a power of two"
883 );
884 assert!(
885 access_size >= u64::from(::core::cmp::min(src_align, dest_align)),
886 "`size` is smaller than `dest` and `src`'s alignment value."
887 );
888
889 let (access_size, int_type) = if access_size <= 8 {
890 (access_size, Type::int((access_size * 8) as u16).unwrap())
891 } else {
892 (8, types::I64)
893 };
894
895 let load_and_store_amount = size / access_size;
896
897 if load_and_store_amount > THRESHOLD {
898 let size_value = self.ins().iconst(config.pointer_type(), size as i64);
899 if non_overlapping {
900 self.call_memcpy(config, dest, src, size_value);
901 } else {
902 self.call_memmove(config, dest, src, size_value);
903 }
904 return;
905 }
906
907 if u64::from(src_align) >= access_size && u64::from(dest_align) >= access_size {
908 flags.set_aligned();
909 }
910
911 let registers: smallvec::SmallVec<[_; THRESHOLD as usize]> = (0..load_and_store_amount)
914 .map(|i| {
915 let offset = (access_size * i) as i32;
916 (self.ins().load(int_type, flags, src, offset), offset)
917 })
918 .collect();
919
920 for (value, offset) in registers {
921 self.ins().store(flags, value, dest, offset);
922 }
923 }
924
925 pub fn call_memset(
929 &mut self,
930 config: TargetFrontendConfig,
931 buffer: Value,
932 ch: Value,
933 size: Value,
934 ) {
935 let pointer_type = config.pointer_type();
936 let signature = {
937 let mut s = Signature::new(config.default_call_conv);
938 s.params.push(AbiParam::new(pointer_type));
939 s.params.push(AbiParam::new(types::I32));
940 s.params.push(AbiParam::new(pointer_type));
941 s.returns.push(AbiParam::new(pointer_type));
942 self.import_signature(s)
943 };
944
945 let libc_memset = self.import_function(ExtFuncData {
946 name: ExternalName::LibCall(LibCall::Memset),
947 signature,
948 colocated: false,
949 });
950
951 let ch = self.ins().uextend(types::I32, ch);
952 self.ins().call(libc_memset, &[buffer, ch, size]);
953 }
954
955 pub fn emit_small_memset(
959 &mut self,
960 config: TargetFrontendConfig,
961 buffer: Value,
962 ch: u8,
963 size: u64,
964 buffer_align: u8,
965 mut flags: MemFlags,
966 ) {
967 const THRESHOLD: u64 = 4;
969
970 if size == 0 {
971 return;
972 }
973
974 let access_size = greatest_divisible_power_of_two(size);
975 assert!(
976 access_size.is_power_of_two(),
977 "`size` is not a power of two"
978 );
979 assert!(
980 access_size >= u64::from(buffer_align),
981 "`size` is smaller than `dest` and `src`'s alignment value."
982 );
983
984 let (access_size, int_type) = if access_size <= 8 {
985 (access_size, Type::int((access_size * 8) as u16).unwrap())
986 } else {
987 (8, types::I64)
988 };
989
990 let load_and_store_amount = size / access_size;
991
992 if load_and_store_amount > THRESHOLD {
993 let ch = self.ins().iconst(types::I8, i64::from(ch));
994 let size = self.ins().iconst(config.pointer_type(), size as i64);
995 self.call_memset(config, buffer, ch, size);
996 } else {
997 if u64::from(buffer_align) >= access_size {
998 flags.set_aligned();
999 }
1000
1001 let ch = u64::from(ch);
1002 let raw_value = if int_type == types::I64 {
1003 ch * 0x0101010101010101_u64
1004 } else if int_type == types::I32 {
1005 ch * 0x01010101_u64
1006 } else if int_type == types::I16 {
1007 (ch << 8) | ch
1008 } else {
1009 assert_eq!(int_type, types::I8);
1010 ch
1011 };
1012
1013 let value = self.ins().iconst(int_type, raw_value as i64);
1014 for i in 0..load_and_store_amount {
1015 let offset = (access_size * i) as i32;
1016 self.ins().store(flags, value, buffer, offset);
1017 }
1018 }
1019 }
1020
1021 pub fn call_memmove(
1026 &mut self,
1027 config: TargetFrontendConfig,
1028 dest: Value,
1029 source: Value,
1030 size: Value,
1031 ) {
1032 let pointer_type = config.pointer_type();
1033 let signature = {
1034 let mut s = Signature::new(config.default_call_conv);
1035 s.params.push(AbiParam::new(pointer_type));
1036 s.params.push(AbiParam::new(pointer_type));
1037 s.params.push(AbiParam::new(pointer_type));
1038 s.returns.push(AbiParam::new(pointer_type));
1039 self.import_signature(s)
1040 };
1041
1042 let libc_memmove = self.import_function(ExtFuncData {
1043 name: ExternalName::LibCall(LibCall::Memmove),
1044 signature,
1045 colocated: false,
1046 });
1047
1048 self.ins().call(libc_memmove, &[dest, source, size]);
1049 }
1050
1051 pub fn call_memcmp(
1060 &mut self,
1061 config: TargetFrontendConfig,
1062 left: Value,
1063 right: Value,
1064 size: Value,
1065 ) -> Value {
1066 let pointer_type = config.pointer_type();
1067 let signature = {
1068 let mut s = Signature::new(config.default_call_conv);
1069 s.params.reserve(3);
1070 s.params.push(AbiParam::new(pointer_type));
1071 s.params.push(AbiParam::new(pointer_type));
1072 s.params.push(AbiParam::new(pointer_type));
1073 s.returns.push(AbiParam::new(types::I32));
1074 self.import_signature(s)
1075 };
1076
1077 let libc_memcmp = self.import_function(ExtFuncData {
1078 name: ExternalName::LibCall(LibCall::Memcmp),
1079 signature,
1080 colocated: false,
1081 });
1082
1083 let call = self.ins().call(libc_memcmp, &[left, right, size]);
1084 self.func.dfg.first_result(call)
1085 }
1086
1087 pub fn emit_small_memory_compare(
1100 &mut self,
1101 config: TargetFrontendConfig,
1102 int_cc: IntCC,
1103 left: Value,
1104 right: Value,
1105 size: u64,
1106 left_align: std::num::NonZeroU8,
1107 right_align: std::num::NonZeroU8,
1108 flags: MemFlags,
1109 ) -> Value {
1110 use IntCC::*;
1111 let (zero_cc, empty_imm) = match int_cc {
1112 Equal => (Equal, 1),
1114 NotEqual => (NotEqual, 0),
1115
1116 UnsignedLessThan => (SignedLessThan, 0),
1117 UnsignedGreaterThanOrEqual => (SignedGreaterThanOrEqual, 1),
1118 UnsignedGreaterThan => (SignedGreaterThan, 0),
1119 UnsignedLessThanOrEqual => (SignedLessThanOrEqual, 1),
1120
1121 SignedLessThan
1122 | SignedGreaterThanOrEqual
1123 | SignedGreaterThan
1124 | SignedLessThanOrEqual => {
1125 panic!("Signed comparison {int_cc} not supported by memcmp")
1126 }
1127 };
1128
1129 if size == 0 {
1130 return self.ins().iconst(types::I8, empty_imm);
1131 }
1132
1133 if let Some(small_type) = size.try_into().ok().and_then(Type::int_with_byte_size) {
1135 if let Equal | NotEqual = zero_cc {
1136 let mut left_flags = flags;
1137 if size == left_align.get() as u64 {
1138 left_flags.set_aligned();
1139 }
1140 let mut right_flags = flags;
1141 if size == right_align.get() as u64 {
1142 right_flags.set_aligned();
1143 }
1144 let left_val = self.ins().load(small_type, left_flags, left, 0);
1145 let right_val = self.ins().load(small_type, right_flags, right, 0);
1146 return self.ins().icmp(int_cc, left_val, right_val);
1147 } else if small_type == types::I8 {
1148 let mut aligned_flags = flags;
1153 aligned_flags.set_aligned();
1154 let left_val = self.ins().load(small_type, aligned_flags, left, 0);
1155 let right_val = self.ins().load(small_type, aligned_flags, right, 0);
1156 return self.ins().icmp(int_cc, left_val, right_val);
1157 }
1158 }
1159
1160 let pointer_type = config.pointer_type();
1161 let size = self.ins().iconst(pointer_type, size as i64);
1162 let cmp = self.call_memcmp(config, left, right, size);
1163 self.ins().icmp_imm(zero_cc, cmp, 0)
1164 }
1165}
1166
1167fn greatest_divisible_power_of_two(size: u64) -> u64 {
1168 (size as i64 & -(size as i64)) as u64
1169}
1170
1171impl<'a> FunctionBuilder<'a> {
1173 fn fill_current_block(&mut self) {
1175 self.func_ctx.status[self.position.unwrap()] = BlockStatus::Filled;
1176 }
1177
1178 fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) {
1179 self.func_ctx
1180 .ssa
1181 .declare_block_predecessor(dest_block, jump_inst);
1182 }
1183
1184 fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
1185 let SideEffects {
1186 instructions_added_to_blocks,
1187 } = side_effects;
1188
1189 for modified_block in instructions_added_to_blocks {
1190 if self.is_pristine(modified_block) {
1191 self.func_ctx.status[modified_block] = BlockStatus::Partial;
1192 }
1193 }
1194 }
1195}
1196
1197#[cfg(test)]
1198mod tests {
1199 use super::greatest_divisible_power_of_two;
1200 use crate::Variable;
1201 use crate::frontend::{
1202 DefVariableError, FunctionBuilder, FunctionBuilderContext, UseVariableError,
1203 };
1204 use alloc::string::ToString;
1205 use cranelift_codegen::ir::condcodes::IntCC;
1206 use cranelift_codegen::ir::{
1207 AbiParam, BlockCall, ExceptionTableData, ExtFuncData, ExternalName, Function, InstBuilder,
1208 MemFlags, Signature, UserExternalName, UserFuncName, Value, types::*,
1209 };
1210 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
1211 use cranelift_codegen::settings;
1212 use cranelift_codegen::verifier::verify_function;
1213 use target_lexicon::PointerWidth;
1214
1215 fn sample_function(lazy_seal: bool) {
1216 let mut sig = Signature::new(CallConv::SystemV);
1217 sig.returns.push(AbiParam::new(I32));
1218 sig.params.push(AbiParam::new(I32));
1219
1220 let mut fn_ctx = FunctionBuilderContext::new();
1221 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1222 {
1223 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1224
1225 let block0 = builder.create_block();
1226 let block1 = builder.create_block();
1227 let block2 = builder.create_block();
1228 let block3 = builder.create_block();
1229 let x = builder.declare_var(I32);
1230 let y = builder.declare_var(I32);
1231 let z = builder.declare_var(I32);
1232
1233 builder.append_block_params_for_function_params(block0);
1234
1235 builder.switch_to_block(block0);
1236 if !lazy_seal {
1237 builder.seal_block(block0);
1238 }
1239 {
1240 let tmp = builder.block_params(block0)[0]; builder.def_var(x, tmp);
1242 }
1243 {
1244 let tmp = builder.ins().iconst(I32, 2);
1245 builder.def_var(y, tmp);
1246 }
1247 {
1248 let arg1 = builder.use_var(x);
1249 let arg2 = builder.use_var(y);
1250 let tmp = builder.ins().iadd(arg1, arg2);
1251 builder.def_var(z, tmp);
1252 }
1253 builder.ins().jump(block1, &[]);
1254
1255 builder.switch_to_block(block1);
1256 {
1257 let arg1 = builder.use_var(y);
1258 let arg2 = builder.use_var(z);
1259 let tmp = builder.ins().iadd(arg1, arg2);
1260 builder.def_var(z, tmp);
1261 }
1262 {
1263 let arg = builder.use_var(y);
1264 builder.ins().brif(arg, block3, &[], block2, &[]);
1265 }
1266
1267 builder.switch_to_block(block2);
1268 if !lazy_seal {
1269 builder.seal_block(block2);
1270 }
1271 {
1272 let arg1 = builder.use_var(z);
1273 let arg2 = builder.use_var(x);
1274 let tmp = builder.ins().isub(arg1, arg2);
1275 builder.def_var(z, tmp);
1276 }
1277 {
1278 let arg = builder.use_var(y);
1279 builder.ins().return_(&[arg]);
1280 }
1281
1282 builder.switch_to_block(block3);
1283 if !lazy_seal {
1284 builder.seal_block(block3);
1285 }
1286
1287 {
1288 let arg1 = builder.use_var(y);
1289 let arg2 = builder.use_var(x);
1290 let tmp = builder.ins().isub(arg1, arg2);
1291 builder.def_var(y, tmp);
1292 }
1293 builder.ins().jump(block1, &[]);
1294 if !lazy_seal {
1295 builder.seal_block(block1);
1296 }
1297
1298 if lazy_seal {
1299 builder.seal_all_blocks();
1300 }
1301
1302 builder.finalize();
1303 }
1304
1305 let flags = settings::Flags::new(settings::builder());
1306 if let Err(errors) = verify_function(&func, &flags) {
1308 panic!("{}\n{}", func.display(), errors)
1309 }
1310 }
1311
1312 #[test]
1313 fn sample() {
1314 sample_function(false)
1315 }
1316
1317 #[test]
1318 fn sample_with_lazy_seal() {
1319 sample_function(true)
1320 }
1321
1322 #[track_caller]
1323 fn check(func: &Function, expected_ir: &str) {
1324 let expected_ir = expected_ir.trim();
1325 let actual_ir = func.display().to_string();
1326 let actual_ir = actual_ir.trim();
1327 assert!(
1328 expected_ir == actual_ir,
1329 "Expected:\n{expected_ir}\nGot:\n{actual_ir}"
1330 );
1331 }
1332
1333 fn systemv_frontend_config() -> TargetFrontendConfig {
1335 TargetFrontendConfig {
1336 default_call_conv: CallConv::SystemV,
1337 pointer_width: PointerWidth::U64,
1338 page_size_align_log2: 12,
1339 }
1340 }
1341
1342 #[test]
1343 fn memcpy() {
1344 let frontend_config = systemv_frontend_config();
1345 let mut sig = Signature::new(frontend_config.default_call_conv);
1346 sig.returns.push(AbiParam::new(I32));
1347
1348 let mut fn_ctx = FunctionBuilderContext::new();
1349 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1350 {
1351 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1352
1353 let block0 = builder.create_block();
1354 let x = builder.declare_var(frontend_config.pointer_type());
1355 let y = builder.declare_var(frontend_config.pointer_type());
1356 let _z = builder.declare_var(I32);
1357
1358 builder.append_block_params_for_function_params(block0);
1359 builder.switch_to_block(block0);
1360
1361 let src = builder.use_var(x);
1362 let dest = builder.use_var(y);
1363 let size = builder.use_var(y);
1364 builder.call_memcpy(frontend_config, dest, src, size);
1365 builder.ins().return_(&[size]);
1366
1367 builder.seal_all_blocks();
1368 builder.finalize();
1369 }
1370
1371 check(
1372 &func,
1373 "function %sample() -> i32 system_v {
1374 sig0 = (i64, i64, i64) -> i64 system_v
1375 fn0 = %Memcpy sig0
1376
1377block0:
1378 v4 = iconst.i64 0
1379 v1 -> v4
1380 v3 = iconst.i64 0
1381 v0 -> v3
1382 v2 = call fn0(v1, v0, v1) ; v1 = 0, v0 = 0, v1 = 0
1383 return v1 ; v1 = 0
1384}
1385",
1386 );
1387 }
1388
1389 #[test]
1390 fn small_memcpy() {
1391 let frontend_config = systemv_frontend_config();
1392 let mut sig = Signature::new(frontend_config.default_call_conv);
1393 sig.returns.push(AbiParam::new(I32));
1394
1395 let mut fn_ctx = FunctionBuilderContext::new();
1396 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1397 {
1398 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1399
1400 let block0 = builder.create_block();
1401 let x = builder.declare_var(frontend_config.pointer_type());
1402 let y = builder.declare_var(frontend_config.pointer_type());
1403
1404 builder.append_block_params_for_function_params(block0);
1405 builder.switch_to_block(block0);
1406
1407 let src = builder.use_var(x);
1408 let dest = builder.use_var(y);
1409 let size = 8;
1410 builder.emit_small_memory_copy(
1411 frontend_config,
1412 dest,
1413 src,
1414 size,
1415 8,
1416 8,
1417 true,
1418 MemFlags::new(),
1419 );
1420 builder.ins().return_(&[dest]);
1421
1422 builder.seal_all_blocks();
1423 builder.finalize();
1424 }
1425
1426 check(
1427 &func,
1428 "function %sample() -> i32 system_v {
1429block0:
1430 v4 = iconst.i64 0
1431 v1 -> v4
1432 v3 = iconst.i64 0
1433 v0 -> v3
1434 v2 = load.i64 aligned v0 ; v0 = 0
1435 store aligned v2, v1 ; v1 = 0
1436 return v1 ; v1 = 0
1437}
1438",
1439 );
1440 }
1441
1442 #[test]
1443 fn not_so_small_memcpy() {
1444 let frontend_config = systemv_frontend_config();
1445 let mut sig = Signature::new(frontend_config.default_call_conv);
1446 sig.returns.push(AbiParam::new(I32));
1447
1448 let mut fn_ctx = FunctionBuilderContext::new();
1449 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1450 {
1451 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1452
1453 let block0 = builder.create_block();
1454 let x = builder.declare_var(frontend_config.pointer_type());
1455 let y = builder.declare_var(frontend_config.pointer_type());
1456 builder.append_block_params_for_function_params(block0);
1457 builder.switch_to_block(block0);
1458
1459 let src = builder.use_var(x);
1460 let dest = builder.use_var(y);
1461 let size = 8192;
1462 builder.emit_small_memory_copy(
1463 frontend_config,
1464 dest,
1465 src,
1466 size,
1467 8,
1468 8,
1469 true,
1470 MemFlags::new(),
1471 );
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, i64, i64) -> i64 system_v
1482 fn0 = %Memcpy sig0
1483
1484block0:
1485 v5 = iconst.i64 0
1486 v1 -> v5
1487 v4 = iconst.i64 0
1488 v0 -> v4
1489 v2 = iconst.i64 8192
1490 v3 = call fn0(v1, v0, v2) ; v1 = 0, v0 = 0, v2 = 8192
1491 return v1 ; v1 = 0
1492}
1493",
1494 );
1495 }
1496
1497 #[test]
1498 fn small_memset() {
1499 let frontend_config = systemv_frontend_config();
1500 let mut sig = Signature::new(frontend_config.default_call_conv);
1501 sig.returns.push(AbiParam::new(I32));
1502
1503 let mut fn_ctx = FunctionBuilderContext::new();
1504 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1505 {
1506 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1507
1508 let block0 = builder.create_block();
1509 let y = builder.declare_var(frontend_config.pointer_type());
1510 builder.append_block_params_for_function_params(block0);
1511 builder.switch_to_block(block0);
1512
1513 let dest = builder.use_var(y);
1514 let size = 8;
1515 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1516 builder.ins().return_(&[dest]);
1517
1518 builder.seal_all_blocks();
1519 builder.finalize();
1520 }
1521
1522 check(
1523 &func,
1524 "function %sample() -> i32 system_v {
1525block0:
1526 v2 = iconst.i64 0
1527 v0 -> v2
1528 v1 = iconst.i64 0x0101_0101_0101_0101
1529 store aligned v1, v0 ; v1 = 0x0101_0101_0101_0101, v0 = 0
1530 return v0 ; v0 = 0
1531}
1532",
1533 );
1534 }
1535
1536 #[test]
1537 fn not_so_small_memset() {
1538 let frontend_config = systemv_frontend_config();
1539 let mut sig = Signature::new(frontend_config.default_call_conv);
1540 sig.returns.push(AbiParam::new(I32));
1541
1542 let mut fn_ctx = FunctionBuilderContext::new();
1543 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1544 {
1545 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1546
1547 let block0 = builder.create_block();
1548 let y = builder.declare_var(frontend_config.pointer_type());
1549 builder.append_block_params_for_function_params(block0);
1550 builder.switch_to_block(block0);
1551
1552 let dest = builder.use_var(y);
1553 let size = 8192;
1554 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1555 builder.ins().return_(&[dest]);
1556
1557 builder.seal_all_blocks();
1558 builder.finalize();
1559 }
1560
1561 check(
1562 &func,
1563 "function %sample() -> i32 system_v {
1564 sig0 = (i64, i32, i64) -> i64 system_v
1565 fn0 = %Memset sig0
1566
1567block0:
1568 v5 = iconst.i64 0
1569 v0 -> v5
1570 v1 = iconst.i8 1
1571 v2 = iconst.i64 8192
1572 v3 = uextend.i32 v1 ; v1 = 1
1573 v4 = call fn0(v0, v3, v2) ; v0 = 0, v2 = 8192
1574 return v0 ; v0 = 0
1575}
1576",
1577 );
1578 }
1579
1580 #[test]
1581 fn memcmp() {
1582 use core::str::FromStr;
1583 use cranelift_codegen::isa;
1584
1585 let shared_builder = settings::builder();
1586 let shared_flags = settings::Flags::new(shared_builder);
1587
1588 let triple =
1589 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1590
1591 let target = isa::lookup(triple)
1592 .ok()
1593 .map(|b| b.finish(shared_flags))
1594 .expect("This test requires x86_64 support.")
1595 .expect("Should be able to create backend with default flags");
1596
1597 let mut sig = Signature::new(target.default_call_conv());
1598 sig.returns.push(AbiParam::new(I32));
1599
1600 let mut fn_ctx = FunctionBuilderContext::new();
1601 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1602 {
1603 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1604
1605 let block0 = builder.create_block();
1606 let x = builder.declare_var(target.pointer_type());
1607 let y = builder.declare_var(target.pointer_type());
1608 let z = builder.declare_var(target.pointer_type());
1609 builder.append_block_params_for_function_params(block0);
1610 builder.switch_to_block(block0);
1611
1612 let left = builder.use_var(x);
1613 let right = builder.use_var(y);
1614 let size = builder.use_var(z);
1615 let cmp = builder.call_memcmp(target.frontend_config(), left, right, size);
1616 builder.ins().return_(&[cmp]);
1617
1618 builder.seal_all_blocks();
1619 builder.finalize();
1620 }
1621
1622 check(
1623 &func,
1624 "function %sample() -> i32 system_v {
1625 sig0 = (i64, i64, i64) -> i32 system_v
1626 fn0 = %Memcmp sig0
1627
1628block0:
1629 v6 = iconst.i64 0
1630 v2 -> v6
1631 v5 = iconst.i64 0
1632 v1 -> v5
1633 v4 = iconst.i64 0
1634 v0 -> v4
1635 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 0
1636 return v3
1637}
1638",
1639 );
1640 }
1641
1642 #[test]
1643 fn small_memcmp_zero_size() {
1644 let align_eight = std::num::NonZeroU8::new(8).unwrap();
1645 small_memcmp_helper(
1646 "
1647block0:
1648 v4 = iconst.i64 0
1649 v1 -> v4
1650 v3 = iconst.i64 0
1651 v0 -> v3
1652 v2 = iconst.i8 1
1653 return v2 ; v2 = 1",
1654 |builder, target, x, y| {
1655 builder.emit_small_memory_compare(
1656 target.frontend_config(),
1657 IntCC::UnsignedGreaterThanOrEqual,
1658 x,
1659 y,
1660 0,
1661 align_eight,
1662 align_eight,
1663 MemFlags::new(),
1664 )
1665 },
1666 );
1667 }
1668
1669 #[test]
1670 fn small_memcmp_byte_ugt() {
1671 let align_one = std::num::NonZeroU8::new(1).unwrap();
1672 small_memcmp_helper(
1673 "
1674block0:
1675 v6 = iconst.i64 0
1676 v1 -> v6
1677 v5 = iconst.i64 0
1678 v0 -> v5
1679 v2 = load.i8 aligned v0 ; v0 = 0
1680 v3 = load.i8 aligned v1 ; v1 = 0
1681 v4 = icmp ugt v2, v3
1682 return v4",
1683 |builder, target, x, y| {
1684 builder.emit_small_memory_compare(
1685 target.frontend_config(),
1686 IntCC::UnsignedGreaterThan,
1687 x,
1688 y,
1689 1,
1690 align_one,
1691 align_one,
1692 MemFlags::new(),
1693 )
1694 },
1695 );
1696 }
1697
1698 #[test]
1699 fn small_memcmp_aligned_eq() {
1700 let align_four = std::num::NonZeroU8::new(4).unwrap();
1701 small_memcmp_helper(
1702 "
1703block0:
1704 v6 = iconst.i64 0
1705 v1 -> v6
1706 v5 = iconst.i64 0
1707 v0 -> v5
1708 v2 = load.i32 aligned v0 ; v0 = 0
1709 v3 = load.i32 aligned v1 ; v1 = 0
1710 v4 = icmp eq v2, v3
1711 return v4",
1712 |builder, target, x, y| {
1713 builder.emit_small_memory_compare(
1714 target.frontend_config(),
1715 IntCC::Equal,
1716 x,
1717 y,
1718 4,
1719 align_four,
1720 align_four,
1721 MemFlags::new(),
1722 )
1723 },
1724 );
1725 }
1726
1727 #[test]
1728 fn small_memcmp_ipv6_ne() {
1729 let align_two = std::num::NonZeroU8::new(2).unwrap();
1730 small_memcmp_helper(
1731 "
1732block0:
1733 v6 = iconst.i64 0
1734 v1 -> v6
1735 v5 = iconst.i64 0
1736 v0 -> v5
1737 v2 = load.i128 v0 ; v0 = 0
1738 v3 = load.i128 v1 ; v1 = 0
1739 v4 = icmp ne v2, v3
1740 return v4",
1741 |builder, target, x, y| {
1742 builder.emit_small_memory_compare(
1743 target.frontend_config(),
1744 IntCC::NotEqual,
1745 x,
1746 y,
1747 16,
1748 align_two,
1749 align_two,
1750 MemFlags::new(),
1751 )
1752 },
1753 );
1754 }
1755
1756 #[test]
1757 fn small_memcmp_odd_size_uge() {
1758 let one = std::num::NonZeroU8::new(1).unwrap();
1759 small_memcmp_helper(
1760 "
1761 sig0 = (i64, i64, i64) -> i32 system_v
1762 fn0 = %Memcmp sig0
1763
1764block0:
1765 v6 = iconst.i64 0
1766 v1 -> v6
1767 v5 = iconst.i64 0
1768 v0 -> v5
1769 v2 = iconst.i64 3
1770 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 3
1771 v4 = icmp_imm sge v3, 0
1772 return v4",
1773 |builder, target, x, y| {
1774 builder.emit_small_memory_compare(
1775 target.frontend_config(),
1776 IntCC::UnsignedGreaterThanOrEqual,
1777 x,
1778 y,
1779 3,
1780 one,
1781 one,
1782 MemFlags::new(),
1783 )
1784 },
1785 );
1786 }
1787
1788 fn small_memcmp_helper(
1789 expected: &str,
1790 f: impl FnOnce(&mut FunctionBuilder, &dyn TargetIsa, Value, Value) -> Value,
1791 ) {
1792 use core::str::FromStr;
1793 use cranelift_codegen::isa;
1794
1795 let shared_builder = settings::builder();
1796 let shared_flags = settings::Flags::new(shared_builder);
1797
1798 let triple =
1799 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1800
1801 let target = isa::lookup(triple)
1802 .ok()
1803 .map(|b| b.finish(shared_flags))
1804 .expect("This test requires x86_64 support.")
1805 .expect("Should be able to create backend with default flags");
1806
1807 let mut sig = Signature::new(target.default_call_conv());
1808 sig.returns.push(AbiParam::new(I8));
1809
1810 let mut fn_ctx = FunctionBuilderContext::new();
1811 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1812 {
1813 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1814
1815 let block0 = builder.create_block();
1816 let x = builder.declare_var(target.pointer_type());
1817 let y = builder.declare_var(target.pointer_type());
1818 builder.append_block_params_for_function_params(block0);
1819 builder.switch_to_block(block0);
1820
1821 let left = builder.use_var(x);
1822 let right = builder.use_var(y);
1823 let ret = f(&mut builder, &*target, left, right);
1824 builder.ins().return_(&[ret]);
1825
1826 builder.seal_all_blocks();
1827 builder.finalize();
1828 }
1829
1830 check(
1831 &func,
1832 &format!("function %sample() -> i8 system_v {{{expected}\n}}\n"),
1833 );
1834 }
1835
1836 #[test]
1837 fn undef_vector_vars() {
1838 let mut sig = Signature::new(CallConv::SystemV);
1839 sig.returns.push(AbiParam::new(I8X16));
1840 sig.returns.push(AbiParam::new(I8X16));
1841 sig.returns.push(AbiParam::new(F32X4));
1842
1843 let mut fn_ctx = FunctionBuilderContext::new();
1844 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1845 {
1846 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1847
1848 let block0 = builder.create_block();
1849 let a = builder.declare_var(I8X16);
1850 let b = builder.declare_var(I8X16);
1851 let c = builder.declare_var(F32X4);
1852 builder.switch_to_block(block0);
1853
1854 let a = builder.use_var(a);
1855 let b = builder.use_var(b);
1856 let c = builder.use_var(c);
1857 builder.ins().return_(&[a, b, c]);
1858
1859 builder.seal_all_blocks();
1860 builder.finalize();
1861 }
1862
1863 check(
1864 &func,
1865 "function %sample() -> i8x16, i8x16, f32x4 system_v {
1866 const0 = 0x00000000000000000000000000000000
1867
1868block0:
1869 v5 = f32const 0.0
1870 v6 = splat.f32x4 v5 ; v5 = 0.0
1871 v2 -> v6
1872 v4 = vconst.i8x16 const0
1873 v1 -> v4
1874 v3 = vconst.i8x16 const0
1875 v0 -> v3
1876 return v0, v1, v2 ; v0 = const0, v1 = const0
1877}
1878",
1879 );
1880 }
1881
1882 #[test]
1883 fn test_greatest_divisible_power_of_two() {
1884 assert_eq!(64, greatest_divisible_power_of_two(64));
1885 assert_eq!(16, greatest_divisible_power_of_two(48));
1886 assert_eq!(8, greatest_divisible_power_of_two(24));
1887 assert_eq!(1, greatest_divisible_power_of_two(25));
1888 }
1889
1890 #[test]
1891 fn try_use_var() {
1892 let sig = Signature::new(CallConv::SystemV);
1893
1894 let mut fn_ctx = FunctionBuilderContext::new();
1895 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1896 {
1897 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1898
1899 let block0 = builder.create_block();
1900 builder.append_block_params_for_function_params(block0);
1901 builder.switch_to_block(block0);
1902
1903 assert_eq!(
1904 builder.try_use_var(Variable::from_u32(0)),
1905 Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))
1906 );
1907
1908 let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
1909
1910 assert_eq!(
1911 builder.try_def_var(Variable::from_u32(0), value),
1912 Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(
1913 0
1914 )))
1915 );
1916 }
1917 }
1918
1919 #[test]
1920 fn test_builder_with_iconst_and_negative_constant() {
1921 let sig = Signature::new(CallConv::SystemV);
1922 let mut fn_ctx = FunctionBuilderContext::new();
1923 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1924
1925 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1926
1927 let block0 = builder.create_block();
1928 builder.switch_to_block(block0);
1929 builder.ins().iconst(I32, -1);
1930 builder.ins().return_(&[]);
1931
1932 builder.seal_all_blocks();
1933 builder.finalize();
1934
1935 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
1936 let ctx = cranelift_codegen::Context::for_function(func);
1937 ctx.verify(&flags).expect("should be valid");
1938
1939 check(
1940 &ctx.func,
1941 "function %sample() system_v {
1942block0:
1943 v0 = iconst.i32 -1
1944 return
1945}",
1946 );
1947 }
1948
1949 #[test]
1950 fn try_call() {
1951 let mut sig = Signature::new(CallConv::SystemV);
1952 sig.params.push(AbiParam::new(I8));
1953 sig.returns.push(AbiParam::new(I32));
1954 let mut fn_ctx = FunctionBuilderContext::new();
1955 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1956
1957 let sig0 = func.import_signature(Signature::new(CallConv::SystemV));
1958 let name = func.declare_imported_user_function(UserExternalName::new(0, 0));
1959 let fn0 = func.import_function(ExtFuncData {
1960 name: ExternalName::User(name),
1961 signature: sig0,
1962 colocated: false,
1963 });
1964
1965 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1966
1967 let block0 = builder.create_block();
1968 let block1 = builder.create_block();
1969 let block2 = builder.create_block();
1970 let block3 = builder.create_block();
1971
1972 let my_var = builder.declare_var(I32);
1973
1974 builder.switch_to_block(block0);
1975 let branch_val = builder.append_block_param(block0, I8);
1976 builder.ins().brif(branch_val, block1, &[], block2, &[]);
1977
1978 builder.switch_to_block(block1);
1979 let one = builder.ins().iconst(I32, 1);
1980 builder.def_var(my_var, one);
1981
1982 let normal_return = BlockCall::new(block3, [], &mut builder.func.dfg.value_lists);
1983 let exception_table = builder
1984 .func
1985 .dfg
1986 .exception_tables
1987 .push(ExceptionTableData::new(sig0, normal_return, []));
1988 builder.ins().try_call(fn0, &[], exception_table);
1989
1990 builder.switch_to_block(block2);
1991 let two = builder.ins().iconst(I32, 2);
1992 builder.def_var(my_var, two);
1993
1994 let normal_return = BlockCall::new(block3, [], &mut builder.func.dfg.value_lists);
1995 let exception_table = builder
1996 .func
1997 .dfg
1998 .exception_tables
1999 .push(ExceptionTableData::new(sig0, normal_return, []));
2000 builder.ins().try_call(fn0, &[], exception_table);
2001
2002 builder.switch_to_block(block3);
2003 let ret_val = builder.use_var(my_var);
2004 builder.ins().return_(&[ret_val]);
2005
2006 builder.seal_all_blocks();
2007 builder.finalize();
2008
2009 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
2010 let ctx = cranelift_codegen::Context::for_function(func);
2011 ctx.verify(&flags).expect("should be valid");
2012
2013 check(
2014 &ctx.func,
2015 "function %sample(i8) -> i32 system_v {
2016 sig0 = () system_v
2017 fn0 = u0:0 sig0
2018
2019block0(v0: i8):
2020 brif v0, block1, block2
2021
2022block1:
2023 v1 = iconst.i32 1
2024 try_call fn0(), sig0, block3(v1), [] ; v1 = 1
2025
2026block2:
2027 v2 = iconst.i32 2
2028 try_call fn0(), sig0, block3(v2), [] ; v2 = 2
2029
2030block3(v3: i32):
2031 return v3
2032}",
2033 );
2034 }
2035}