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 patchable: false,
849 });
850
851 self.ins().call(libc_memcpy, &[dest, src, size]);
852 }
853
854 pub fn emit_small_memory_copy(
863 &mut self,
864 config: TargetFrontendConfig,
865 dest: Value,
866 src: Value,
867 size: u64,
868 dest_align: u8,
869 src_align: u8,
870 non_overlapping: bool,
871 mut flags: MemFlags,
872 ) {
873 const THRESHOLD: u64 = 4;
875
876 if size == 0 {
877 return;
878 }
879
880 let access_size = greatest_divisible_power_of_two(size);
881 assert!(
882 access_size.is_power_of_two(),
883 "`size` is not a power of two"
884 );
885 assert!(
886 access_size >= u64::from(::core::cmp::min(src_align, dest_align)),
887 "`size` is smaller than `dest` and `src`'s alignment value."
888 );
889
890 let (access_size, int_type) = if access_size <= 8 {
891 (access_size, Type::int((access_size * 8) as u16).unwrap())
892 } else {
893 (8, types::I64)
894 };
895
896 let load_and_store_amount = size / access_size;
897
898 if load_and_store_amount > THRESHOLD {
899 let size_value = self.ins().iconst(config.pointer_type(), size as i64);
900 if non_overlapping {
901 self.call_memcpy(config, dest, src, size_value);
902 } else {
903 self.call_memmove(config, dest, src, size_value);
904 }
905 return;
906 }
907
908 if u64::from(src_align) >= access_size && u64::from(dest_align) >= access_size {
909 flags.set_aligned();
910 }
911
912 let registers: smallvec::SmallVec<[_; THRESHOLD as usize]> = (0..load_and_store_amount)
915 .map(|i| {
916 let offset = (access_size * i) as i32;
917 (self.ins().load(int_type, flags, src, offset), offset)
918 })
919 .collect();
920
921 for (value, offset) in registers {
922 self.ins().store(flags, value, dest, offset);
923 }
924 }
925
926 pub fn call_memset(
930 &mut self,
931 config: TargetFrontendConfig,
932 buffer: Value,
933 ch: Value,
934 size: Value,
935 ) {
936 let pointer_type = config.pointer_type();
937 let signature = {
938 let mut s = Signature::new(config.default_call_conv);
939 s.params.push(AbiParam::new(pointer_type));
940 s.params.push(AbiParam::new(types::I32));
941 s.params.push(AbiParam::new(pointer_type));
942 s.returns.push(AbiParam::new(pointer_type));
943 self.import_signature(s)
944 };
945
946 let libc_memset = self.import_function(ExtFuncData {
947 name: ExternalName::LibCall(LibCall::Memset),
948 signature,
949 colocated: false,
950 patchable: false,
951 });
952
953 let ch = self.ins().uextend(types::I32, ch);
954 self.ins().call(libc_memset, &[buffer, ch, size]);
955 }
956
957 pub fn emit_small_memset(
961 &mut self,
962 config: TargetFrontendConfig,
963 buffer: Value,
964 ch: u8,
965 size: u64,
966 buffer_align: u8,
967 mut flags: MemFlags,
968 ) {
969 const THRESHOLD: u64 = 4;
971
972 if size == 0 {
973 return;
974 }
975
976 let access_size = greatest_divisible_power_of_two(size);
977 assert!(
978 access_size.is_power_of_two(),
979 "`size` is not a power of two"
980 );
981 assert!(
982 access_size >= u64::from(buffer_align),
983 "`size` is smaller than `dest` and `src`'s alignment value."
984 );
985
986 let (access_size, int_type) = if access_size <= 8 {
987 (access_size, Type::int((access_size * 8) as u16).unwrap())
988 } else {
989 (8, types::I64)
990 };
991
992 let load_and_store_amount = size / access_size;
993
994 if load_and_store_amount > THRESHOLD {
995 let ch = self.ins().iconst(types::I8, i64::from(ch));
996 let size = self.ins().iconst(config.pointer_type(), size as i64);
997 self.call_memset(config, buffer, ch, size);
998 } else {
999 if u64::from(buffer_align) >= access_size {
1000 flags.set_aligned();
1001 }
1002
1003 let ch = u64::from(ch);
1004 let raw_value = if int_type == types::I64 {
1005 ch * 0x0101010101010101_u64
1006 } else if int_type == types::I32 {
1007 ch * 0x01010101_u64
1008 } else if int_type == types::I16 {
1009 (ch << 8) | ch
1010 } else {
1011 assert_eq!(int_type, types::I8);
1012 ch
1013 };
1014
1015 let value = self.ins().iconst(int_type, raw_value as i64);
1016 for i in 0..load_and_store_amount {
1017 let offset = (access_size * i) as i32;
1018 self.ins().store(flags, value, buffer, offset);
1019 }
1020 }
1021 }
1022
1023 pub fn call_memmove(
1028 &mut self,
1029 config: TargetFrontendConfig,
1030 dest: Value,
1031 source: Value,
1032 size: Value,
1033 ) {
1034 let pointer_type = config.pointer_type();
1035 let signature = {
1036 let mut s = Signature::new(config.default_call_conv);
1037 s.params.push(AbiParam::new(pointer_type));
1038 s.params.push(AbiParam::new(pointer_type));
1039 s.params.push(AbiParam::new(pointer_type));
1040 s.returns.push(AbiParam::new(pointer_type));
1041 self.import_signature(s)
1042 };
1043
1044 let libc_memmove = self.import_function(ExtFuncData {
1045 name: ExternalName::LibCall(LibCall::Memmove),
1046 signature,
1047 colocated: false,
1048 patchable: false,
1049 });
1050
1051 self.ins().call(libc_memmove, &[dest, source, size]);
1052 }
1053
1054 pub fn call_memcmp(
1063 &mut self,
1064 config: TargetFrontendConfig,
1065 left: Value,
1066 right: Value,
1067 size: Value,
1068 ) -> Value {
1069 let pointer_type = config.pointer_type();
1070 let signature = {
1071 let mut s = Signature::new(config.default_call_conv);
1072 s.params.reserve(3);
1073 s.params.push(AbiParam::new(pointer_type));
1074 s.params.push(AbiParam::new(pointer_type));
1075 s.params.push(AbiParam::new(pointer_type));
1076 s.returns.push(AbiParam::new(types::I32));
1077 self.import_signature(s)
1078 };
1079
1080 let libc_memcmp = self.import_function(ExtFuncData {
1081 name: ExternalName::LibCall(LibCall::Memcmp),
1082 signature,
1083 colocated: false,
1084 patchable: false,
1085 });
1086
1087 let call = self.ins().call(libc_memcmp, &[left, right, size]);
1088 self.func.dfg.first_result(call)
1089 }
1090
1091 pub fn emit_small_memory_compare(
1104 &mut self,
1105 config: TargetFrontendConfig,
1106 int_cc: IntCC,
1107 left: Value,
1108 right: Value,
1109 size: u64,
1110 left_align: std::num::NonZeroU8,
1111 right_align: std::num::NonZeroU8,
1112 flags: MemFlags,
1113 ) -> Value {
1114 use IntCC::*;
1115 let (zero_cc, empty_imm) = match int_cc {
1116 Equal => (Equal, 1),
1118 NotEqual => (NotEqual, 0),
1119
1120 UnsignedLessThan => (SignedLessThan, 0),
1121 UnsignedGreaterThanOrEqual => (SignedGreaterThanOrEqual, 1),
1122 UnsignedGreaterThan => (SignedGreaterThan, 0),
1123 UnsignedLessThanOrEqual => (SignedLessThanOrEqual, 1),
1124
1125 SignedLessThan
1126 | SignedGreaterThanOrEqual
1127 | SignedGreaterThan
1128 | SignedLessThanOrEqual => {
1129 panic!("Signed comparison {int_cc} not supported by memcmp")
1130 }
1131 };
1132
1133 if size == 0 {
1134 return self.ins().iconst(types::I8, empty_imm);
1135 }
1136
1137 if let Some(small_type) = size.try_into().ok().and_then(Type::int_with_byte_size) {
1139 if let Equal | NotEqual = zero_cc {
1140 let mut left_flags = flags;
1141 if size == left_align.get() as u64 {
1142 left_flags.set_aligned();
1143 }
1144 let mut right_flags = flags;
1145 if size == right_align.get() as u64 {
1146 right_flags.set_aligned();
1147 }
1148 let left_val = self.ins().load(small_type, left_flags, left, 0);
1149 let right_val = self.ins().load(small_type, right_flags, right, 0);
1150 return self.ins().icmp(int_cc, left_val, right_val);
1151 } else if small_type == types::I8 {
1152 let mut aligned_flags = flags;
1157 aligned_flags.set_aligned();
1158 let left_val = self.ins().load(small_type, aligned_flags, left, 0);
1159 let right_val = self.ins().load(small_type, aligned_flags, right, 0);
1160 return self.ins().icmp(int_cc, left_val, right_val);
1161 }
1162 }
1163
1164 let pointer_type = config.pointer_type();
1165 let size = self.ins().iconst(pointer_type, size as i64);
1166 let cmp = self.call_memcmp(config, left, right, size);
1167 self.ins().icmp_imm(zero_cc, cmp, 0)
1168 }
1169}
1170
1171fn greatest_divisible_power_of_two(size: u64) -> u64 {
1172 (size as i64 & -(size as i64)) as u64
1173}
1174
1175impl<'a> FunctionBuilder<'a> {
1177 fn fill_current_block(&mut self) {
1179 self.func_ctx.status[self.position.unwrap()] = BlockStatus::Filled;
1180 }
1181
1182 fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) {
1183 self.func_ctx
1184 .ssa
1185 .declare_block_predecessor(dest_block, jump_inst);
1186 }
1187
1188 fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
1189 let SideEffects {
1190 instructions_added_to_blocks,
1191 } = side_effects;
1192
1193 for modified_block in instructions_added_to_blocks {
1194 if self.is_pristine(modified_block) {
1195 self.func_ctx.status[modified_block] = BlockStatus::Partial;
1196 }
1197 }
1198 }
1199}
1200
1201#[cfg(test)]
1202mod tests {
1203 use super::greatest_divisible_power_of_two;
1204 use crate::Variable;
1205 use crate::frontend::{
1206 DefVariableError, FunctionBuilder, FunctionBuilderContext, UseVariableError,
1207 };
1208 use alloc::string::ToString;
1209 use cranelift_codegen::ir::condcodes::IntCC;
1210 use cranelift_codegen::ir::{
1211 AbiParam, BlockCall, ExceptionTableData, ExtFuncData, ExternalName, Function, InstBuilder,
1212 MemFlags, Signature, UserExternalName, UserFuncName, Value, types::*,
1213 };
1214 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
1215 use cranelift_codegen::settings;
1216 use cranelift_codegen::verifier::verify_function;
1217 use target_lexicon::PointerWidth;
1218
1219 fn sample_function(lazy_seal: bool) {
1220 let mut sig = Signature::new(CallConv::SystemV);
1221 sig.returns.push(AbiParam::new(I32));
1222 sig.params.push(AbiParam::new(I32));
1223
1224 let mut fn_ctx = FunctionBuilderContext::new();
1225 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1226 {
1227 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1228
1229 let block0 = builder.create_block();
1230 let block1 = builder.create_block();
1231 let block2 = builder.create_block();
1232 let block3 = builder.create_block();
1233 let x = builder.declare_var(I32);
1234 let y = builder.declare_var(I32);
1235 let z = builder.declare_var(I32);
1236
1237 builder.append_block_params_for_function_params(block0);
1238
1239 builder.switch_to_block(block0);
1240 if !lazy_seal {
1241 builder.seal_block(block0);
1242 }
1243 {
1244 let tmp = builder.block_params(block0)[0]; builder.def_var(x, tmp);
1246 }
1247 {
1248 let tmp = builder.ins().iconst(I32, 2);
1249 builder.def_var(y, tmp);
1250 }
1251 {
1252 let arg1 = builder.use_var(x);
1253 let arg2 = builder.use_var(y);
1254 let tmp = builder.ins().iadd(arg1, arg2);
1255 builder.def_var(z, tmp);
1256 }
1257 builder.ins().jump(block1, &[]);
1258
1259 builder.switch_to_block(block1);
1260 {
1261 let arg1 = builder.use_var(y);
1262 let arg2 = builder.use_var(z);
1263 let tmp = builder.ins().iadd(arg1, arg2);
1264 builder.def_var(z, tmp);
1265 }
1266 {
1267 let arg = builder.use_var(y);
1268 builder.ins().brif(arg, block3, &[], block2, &[]);
1269 }
1270
1271 builder.switch_to_block(block2);
1272 if !lazy_seal {
1273 builder.seal_block(block2);
1274 }
1275 {
1276 let arg1 = builder.use_var(z);
1277 let arg2 = builder.use_var(x);
1278 let tmp = builder.ins().isub(arg1, arg2);
1279 builder.def_var(z, tmp);
1280 }
1281 {
1282 let arg = builder.use_var(y);
1283 builder.ins().return_(&[arg]);
1284 }
1285
1286 builder.switch_to_block(block3);
1287 if !lazy_seal {
1288 builder.seal_block(block3);
1289 }
1290
1291 {
1292 let arg1 = builder.use_var(y);
1293 let arg2 = builder.use_var(x);
1294 let tmp = builder.ins().isub(arg1, arg2);
1295 builder.def_var(y, tmp);
1296 }
1297 builder.ins().jump(block1, &[]);
1298 if !lazy_seal {
1299 builder.seal_block(block1);
1300 }
1301
1302 if lazy_seal {
1303 builder.seal_all_blocks();
1304 }
1305
1306 builder.finalize();
1307 }
1308
1309 let flags = settings::Flags::new(settings::builder());
1310 if let Err(errors) = verify_function(&func, &flags) {
1312 panic!("{}\n{}", func.display(), errors)
1313 }
1314 }
1315
1316 #[test]
1317 fn sample() {
1318 sample_function(false)
1319 }
1320
1321 #[test]
1322 fn sample_with_lazy_seal() {
1323 sample_function(true)
1324 }
1325
1326 #[track_caller]
1327 fn check(func: &Function, expected_ir: &str) {
1328 let expected_ir = expected_ir.trim();
1329 let actual_ir = func.display().to_string();
1330 let actual_ir = actual_ir.trim();
1331 assert!(
1332 expected_ir == actual_ir,
1333 "Expected:\n{expected_ir}\nGot:\n{actual_ir}"
1334 );
1335 }
1336
1337 fn systemv_frontend_config() -> TargetFrontendConfig {
1339 TargetFrontendConfig {
1340 default_call_conv: CallConv::SystemV,
1341 pointer_width: PointerWidth::U64,
1342 page_size_align_log2: 12,
1343 }
1344 }
1345
1346 #[test]
1347 fn memcpy() {
1348 let frontend_config = systemv_frontend_config();
1349 let mut sig = Signature::new(frontend_config.default_call_conv);
1350 sig.returns.push(AbiParam::new(I32));
1351
1352 let mut fn_ctx = FunctionBuilderContext::new();
1353 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1354 {
1355 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1356
1357 let block0 = builder.create_block();
1358 let x = builder.declare_var(frontend_config.pointer_type());
1359 let y = builder.declare_var(frontend_config.pointer_type());
1360 let _z = builder.declare_var(I32);
1361
1362 builder.append_block_params_for_function_params(block0);
1363 builder.switch_to_block(block0);
1364
1365 let src = builder.use_var(x);
1366 let dest = builder.use_var(y);
1367 let size = builder.use_var(y);
1368 builder.call_memcpy(frontend_config, dest, src, size);
1369 builder.ins().return_(&[size]);
1370
1371 builder.seal_all_blocks();
1372 builder.finalize();
1373 }
1374
1375 check(
1376 &func,
1377 "function %sample() -> i32 system_v {
1378 sig0 = (i64, i64, i64) -> i64 system_v
1379 fn0 = %Memcpy sig0
1380
1381block0:
1382 v4 = iconst.i64 0
1383 v1 -> v4
1384 v3 = iconst.i64 0
1385 v0 -> v3
1386 v2 = call fn0(v1, v0, v1) ; v1 = 0, v0 = 0, v1 = 0
1387 return v1 ; v1 = 0
1388}
1389",
1390 );
1391 }
1392
1393 #[test]
1394 fn small_memcpy() {
1395 let frontend_config = systemv_frontend_config();
1396 let mut sig = Signature::new(frontend_config.default_call_conv);
1397 sig.returns.push(AbiParam::new(I32));
1398
1399 let mut fn_ctx = FunctionBuilderContext::new();
1400 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1401 {
1402 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1403
1404 let block0 = builder.create_block();
1405 let x = builder.declare_var(frontend_config.pointer_type());
1406 let y = builder.declare_var(frontend_config.pointer_type());
1407
1408 builder.append_block_params_for_function_params(block0);
1409 builder.switch_to_block(block0);
1410
1411 let src = builder.use_var(x);
1412 let dest = builder.use_var(y);
1413 let size = 8;
1414 builder.emit_small_memory_copy(
1415 frontend_config,
1416 dest,
1417 src,
1418 size,
1419 8,
1420 8,
1421 true,
1422 MemFlags::new(),
1423 );
1424 builder.ins().return_(&[dest]);
1425
1426 builder.seal_all_blocks();
1427 builder.finalize();
1428 }
1429
1430 check(
1431 &func,
1432 "function %sample() -> i32 system_v {
1433block0:
1434 v4 = iconst.i64 0
1435 v1 -> v4
1436 v3 = iconst.i64 0
1437 v0 -> v3
1438 v2 = load.i64 aligned v0 ; v0 = 0
1439 store aligned v2, v1 ; v1 = 0
1440 return v1 ; v1 = 0
1441}
1442",
1443 );
1444 }
1445
1446 #[test]
1447 fn not_so_small_memcpy() {
1448 let frontend_config = systemv_frontend_config();
1449 let mut sig = Signature::new(frontend_config.default_call_conv);
1450 sig.returns.push(AbiParam::new(I32));
1451
1452 let mut fn_ctx = FunctionBuilderContext::new();
1453 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1454 {
1455 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1456
1457 let block0 = builder.create_block();
1458 let x = builder.declare_var(frontend_config.pointer_type());
1459 let y = builder.declare_var(frontend_config.pointer_type());
1460 builder.append_block_params_for_function_params(block0);
1461 builder.switch_to_block(block0);
1462
1463 let src = builder.use_var(x);
1464 let dest = builder.use_var(y);
1465 let size = 8192;
1466 builder.emit_small_memory_copy(
1467 frontend_config,
1468 dest,
1469 src,
1470 size,
1471 8,
1472 8,
1473 true,
1474 MemFlags::new(),
1475 );
1476 builder.ins().return_(&[dest]);
1477
1478 builder.seal_all_blocks();
1479 builder.finalize();
1480 }
1481
1482 check(
1483 &func,
1484 "function %sample() -> i32 system_v {
1485 sig0 = (i64, i64, i64) -> i64 system_v
1486 fn0 = %Memcpy sig0
1487
1488block0:
1489 v5 = iconst.i64 0
1490 v1 -> v5
1491 v4 = iconst.i64 0
1492 v0 -> v4
1493 v2 = iconst.i64 8192
1494 v3 = call fn0(v1, v0, v2) ; v1 = 0, v0 = 0, v2 = 8192
1495 return v1 ; v1 = 0
1496}
1497",
1498 );
1499 }
1500
1501 #[test]
1502 fn small_memset() {
1503 let frontend_config = systemv_frontend_config();
1504 let mut sig = Signature::new(frontend_config.default_call_conv);
1505 sig.returns.push(AbiParam::new(I32));
1506
1507 let mut fn_ctx = FunctionBuilderContext::new();
1508 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1509 {
1510 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1511
1512 let block0 = builder.create_block();
1513 let y = builder.declare_var(frontend_config.pointer_type());
1514 builder.append_block_params_for_function_params(block0);
1515 builder.switch_to_block(block0);
1516
1517 let dest = builder.use_var(y);
1518 let size = 8;
1519 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1520 builder.ins().return_(&[dest]);
1521
1522 builder.seal_all_blocks();
1523 builder.finalize();
1524 }
1525
1526 check(
1527 &func,
1528 "function %sample() -> i32 system_v {
1529block0:
1530 v2 = iconst.i64 0
1531 v0 -> v2
1532 v1 = iconst.i64 0x0101_0101_0101_0101
1533 store aligned v1, v0 ; v1 = 0x0101_0101_0101_0101, v0 = 0
1534 return v0 ; v0 = 0
1535}
1536",
1537 );
1538 }
1539
1540 #[test]
1541 fn not_so_small_memset() {
1542 let frontend_config = systemv_frontend_config();
1543 let mut sig = Signature::new(frontend_config.default_call_conv);
1544 sig.returns.push(AbiParam::new(I32));
1545
1546 let mut fn_ctx = FunctionBuilderContext::new();
1547 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1548 {
1549 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1550
1551 let block0 = builder.create_block();
1552 let y = builder.declare_var(frontend_config.pointer_type());
1553 builder.append_block_params_for_function_params(block0);
1554 builder.switch_to_block(block0);
1555
1556 let dest = builder.use_var(y);
1557 let size = 8192;
1558 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1559 builder.ins().return_(&[dest]);
1560
1561 builder.seal_all_blocks();
1562 builder.finalize();
1563 }
1564
1565 check(
1566 &func,
1567 "function %sample() -> i32 system_v {
1568 sig0 = (i64, i32, i64) -> i64 system_v
1569 fn0 = %Memset sig0
1570
1571block0:
1572 v5 = iconst.i64 0
1573 v0 -> v5
1574 v1 = iconst.i8 1
1575 v2 = iconst.i64 8192
1576 v3 = uextend.i32 v1 ; v1 = 1
1577 v4 = call fn0(v0, v3, v2) ; v0 = 0, v2 = 8192
1578 return v0 ; v0 = 0
1579}
1580",
1581 );
1582 }
1583
1584 #[test]
1585 fn memcmp() {
1586 use core::str::FromStr;
1587 use cranelift_codegen::isa;
1588
1589 let shared_builder = settings::builder();
1590 let shared_flags = settings::Flags::new(shared_builder);
1591
1592 let triple =
1593 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1594
1595 let target = isa::lookup(triple)
1596 .ok()
1597 .map(|b| b.finish(shared_flags))
1598 .expect("This test requires x86_64 support.")
1599 .expect("Should be able to create backend with default flags");
1600
1601 let mut sig = Signature::new(target.default_call_conv());
1602 sig.returns.push(AbiParam::new(I32));
1603
1604 let mut fn_ctx = FunctionBuilderContext::new();
1605 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1606 {
1607 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1608
1609 let block0 = builder.create_block();
1610 let x = builder.declare_var(target.pointer_type());
1611 let y = builder.declare_var(target.pointer_type());
1612 let z = builder.declare_var(target.pointer_type());
1613 builder.append_block_params_for_function_params(block0);
1614 builder.switch_to_block(block0);
1615
1616 let left = builder.use_var(x);
1617 let right = builder.use_var(y);
1618 let size = builder.use_var(z);
1619 let cmp = builder.call_memcmp(target.frontend_config(), left, right, size);
1620 builder.ins().return_(&[cmp]);
1621
1622 builder.seal_all_blocks();
1623 builder.finalize();
1624 }
1625
1626 check(
1627 &func,
1628 "function %sample() -> i32 system_v {
1629 sig0 = (i64, i64, i64) -> i32 system_v
1630 fn0 = %Memcmp sig0
1631
1632block0:
1633 v6 = iconst.i64 0
1634 v2 -> v6
1635 v5 = iconst.i64 0
1636 v1 -> v5
1637 v4 = iconst.i64 0
1638 v0 -> v4
1639 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 0
1640 return v3
1641}
1642",
1643 );
1644 }
1645
1646 #[test]
1647 fn small_memcmp_zero_size() {
1648 let align_eight = std::num::NonZeroU8::new(8).unwrap();
1649 small_memcmp_helper(
1650 "
1651block0:
1652 v4 = iconst.i64 0
1653 v1 -> v4
1654 v3 = iconst.i64 0
1655 v0 -> v3
1656 v2 = iconst.i8 1
1657 return v2 ; v2 = 1",
1658 |builder, target, x, y| {
1659 builder.emit_small_memory_compare(
1660 target.frontend_config(),
1661 IntCC::UnsignedGreaterThanOrEqual,
1662 x,
1663 y,
1664 0,
1665 align_eight,
1666 align_eight,
1667 MemFlags::new(),
1668 )
1669 },
1670 );
1671 }
1672
1673 #[test]
1674 fn small_memcmp_byte_ugt() {
1675 let align_one = std::num::NonZeroU8::new(1).unwrap();
1676 small_memcmp_helper(
1677 "
1678block0:
1679 v6 = iconst.i64 0
1680 v1 -> v6
1681 v5 = iconst.i64 0
1682 v0 -> v5
1683 v2 = load.i8 aligned v0 ; v0 = 0
1684 v3 = load.i8 aligned v1 ; v1 = 0
1685 v4 = icmp ugt v2, v3
1686 return v4",
1687 |builder, target, x, y| {
1688 builder.emit_small_memory_compare(
1689 target.frontend_config(),
1690 IntCC::UnsignedGreaterThan,
1691 x,
1692 y,
1693 1,
1694 align_one,
1695 align_one,
1696 MemFlags::new(),
1697 )
1698 },
1699 );
1700 }
1701
1702 #[test]
1703 fn small_memcmp_aligned_eq() {
1704 let align_four = std::num::NonZeroU8::new(4).unwrap();
1705 small_memcmp_helper(
1706 "
1707block0:
1708 v6 = iconst.i64 0
1709 v1 -> v6
1710 v5 = iconst.i64 0
1711 v0 -> v5
1712 v2 = load.i32 aligned v0 ; v0 = 0
1713 v3 = load.i32 aligned v1 ; v1 = 0
1714 v4 = icmp eq v2, v3
1715 return v4",
1716 |builder, target, x, y| {
1717 builder.emit_small_memory_compare(
1718 target.frontend_config(),
1719 IntCC::Equal,
1720 x,
1721 y,
1722 4,
1723 align_four,
1724 align_four,
1725 MemFlags::new(),
1726 )
1727 },
1728 );
1729 }
1730
1731 #[test]
1732 fn small_memcmp_ipv6_ne() {
1733 let align_two = std::num::NonZeroU8::new(2).unwrap();
1734 small_memcmp_helper(
1735 "
1736block0:
1737 v6 = iconst.i64 0
1738 v1 -> v6
1739 v5 = iconst.i64 0
1740 v0 -> v5
1741 v2 = load.i128 v0 ; v0 = 0
1742 v3 = load.i128 v1 ; v1 = 0
1743 v4 = icmp ne v2, v3
1744 return v4",
1745 |builder, target, x, y| {
1746 builder.emit_small_memory_compare(
1747 target.frontend_config(),
1748 IntCC::NotEqual,
1749 x,
1750 y,
1751 16,
1752 align_two,
1753 align_two,
1754 MemFlags::new(),
1755 )
1756 },
1757 );
1758 }
1759
1760 #[test]
1761 fn small_memcmp_odd_size_uge() {
1762 let one = std::num::NonZeroU8::new(1).unwrap();
1763 small_memcmp_helper(
1764 "
1765 sig0 = (i64, i64, i64) -> i32 system_v
1766 fn0 = %Memcmp sig0
1767
1768block0:
1769 v6 = iconst.i64 0
1770 v1 -> v6
1771 v5 = iconst.i64 0
1772 v0 -> v5
1773 v2 = iconst.i64 3
1774 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 3
1775 v4 = icmp_imm sge v3, 0
1776 return v4",
1777 |builder, target, x, y| {
1778 builder.emit_small_memory_compare(
1779 target.frontend_config(),
1780 IntCC::UnsignedGreaterThanOrEqual,
1781 x,
1782 y,
1783 3,
1784 one,
1785 one,
1786 MemFlags::new(),
1787 )
1788 },
1789 );
1790 }
1791
1792 fn small_memcmp_helper(
1793 expected: &str,
1794 f: impl FnOnce(&mut FunctionBuilder, &dyn TargetIsa, Value, Value) -> Value,
1795 ) {
1796 use core::str::FromStr;
1797 use cranelift_codegen::isa;
1798
1799 let shared_builder = settings::builder();
1800 let shared_flags = settings::Flags::new(shared_builder);
1801
1802 let triple =
1803 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1804
1805 let target = isa::lookup(triple)
1806 .ok()
1807 .map(|b| b.finish(shared_flags))
1808 .expect("This test requires x86_64 support.")
1809 .expect("Should be able to create backend with default flags");
1810
1811 let mut sig = Signature::new(target.default_call_conv());
1812 sig.returns.push(AbiParam::new(I8));
1813
1814 let mut fn_ctx = FunctionBuilderContext::new();
1815 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1816 {
1817 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1818
1819 let block0 = builder.create_block();
1820 let x = builder.declare_var(target.pointer_type());
1821 let y = builder.declare_var(target.pointer_type());
1822 builder.append_block_params_for_function_params(block0);
1823 builder.switch_to_block(block0);
1824
1825 let left = builder.use_var(x);
1826 let right = builder.use_var(y);
1827 let ret = f(&mut builder, &*target, left, right);
1828 builder.ins().return_(&[ret]);
1829
1830 builder.seal_all_blocks();
1831 builder.finalize();
1832 }
1833
1834 check(
1835 &func,
1836 &format!("function %sample() -> i8 system_v {{{expected}\n}}\n"),
1837 );
1838 }
1839
1840 #[test]
1841 fn undef_vector_vars() {
1842 let mut sig = Signature::new(CallConv::SystemV);
1843 sig.returns.push(AbiParam::new(I8X16));
1844 sig.returns.push(AbiParam::new(I8X16));
1845 sig.returns.push(AbiParam::new(F32X4));
1846
1847 let mut fn_ctx = FunctionBuilderContext::new();
1848 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1849 {
1850 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1851
1852 let block0 = builder.create_block();
1853 let a = builder.declare_var(I8X16);
1854 let b = builder.declare_var(I8X16);
1855 let c = builder.declare_var(F32X4);
1856 builder.switch_to_block(block0);
1857
1858 let a = builder.use_var(a);
1859 let b = builder.use_var(b);
1860 let c = builder.use_var(c);
1861 builder.ins().return_(&[a, b, c]);
1862
1863 builder.seal_all_blocks();
1864 builder.finalize();
1865 }
1866
1867 check(
1868 &func,
1869 "function %sample() -> i8x16, i8x16, f32x4 system_v {
1870 const0 = 0x00000000000000000000000000000000
1871
1872block0:
1873 v5 = f32const 0.0
1874 v6 = splat.f32x4 v5 ; v5 = 0.0
1875 v2 -> v6
1876 v4 = vconst.i8x16 const0
1877 v1 -> v4
1878 v3 = vconst.i8x16 const0
1879 v0 -> v3
1880 return v0, v1, v2 ; v0 = const0, v1 = const0
1881}
1882",
1883 );
1884 }
1885
1886 #[test]
1887 fn test_greatest_divisible_power_of_two() {
1888 assert_eq!(64, greatest_divisible_power_of_two(64));
1889 assert_eq!(16, greatest_divisible_power_of_two(48));
1890 assert_eq!(8, greatest_divisible_power_of_two(24));
1891 assert_eq!(1, greatest_divisible_power_of_two(25));
1892 }
1893
1894 #[test]
1895 fn try_use_var() {
1896 let sig = Signature::new(CallConv::SystemV);
1897
1898 let mut fn_ctx = FunctionBuilderContext::new();
1899 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1900 {
1901 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1902
1903 let block0 = builder.create_block();
1904 builder.append_block_params_for_function_params(block0);
1905 builder.switch_to_block(block0);
1906
1907 assert_eq!(
1908 builder.try_use_var(Variable::from_u32(0)),
1909 Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))
1910 );
1911
1912 let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
1913
1914 assert_eq!(
1915 builder.try_def_var(Variable::from_u32(0), value),
1916 Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(
1917 0
1918 )))
1919 );
1920 }
1921 }
1922
1923 #[test]
1924 fn test_builder_with_iconst_and_negative_constant() {
1925 let sig = Signature::new(CallConv::SystemV);
1926 let mut fn_ctx = FunctionBuilderContext::new();
1927 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1928
1929 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1930
1931 let block0 = builder.create_block();
1932 builder.switch_to_block(block0);
1933 builder.ins().iconst(I32, -1);
1934 builder.ins().return_(&[]);
1935
1936 builder.seal_all_blocks();
1937 builder.finalize();
1938
1939 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
1940 let ctx = cranelift_codegen::Context::for_function(func);
1941 ctx.verify(&flags).expect("should be valid");
1942
1943 check(
1944 &ctx.func,
1945 "function %sample() system_v {
1946block0:
1947 v0 = iconst.i32 -1
1948 return
1949}",
1950 );
1951 }
1952
1953 #[test]
1954 fn try_call() {
1955 let mut sig = Signature::new(CallConv::SystemV);
1956 sig.params.push(AbiParam::new(I8));
1957 sig.returns.push(AbiParam::new(I32));
1958 let mut fn_ctx = FunctionBuilderContext::new();
1959 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1960
1961 let sig0 = func.import_signature(Signature::new(CallConv::SystemV));
1962 let name = func.declare_imported_user_function(UserExternalName::new(0, 0));
1963 let fn0 = func.import_function(ExtFuncData {
1964 name: ExternalName::User(name),
1965 signature: sig0,
1966 colocated: false,
1967 patchable: false,
1968 });
1969
1970 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1971
1972 let block0 = builder.create_block();
1973 let block1 = builder.create_block();
1974 let block2 = builder.create_block();
1975 let block3 = builder.create_block();
1976
1977 let my_var = builder.declare_var(I32);
1978
1979 builder.switch_to_block(block0);
1980 let branch_val = builder.append_block_param(block0, I8);
1981 builder.ins().brif(branch_val, block1, &[], block2, &[]);
1982
1983 builder.switch_to_block(block1);
1984 let one = builder.ins().iconst(I32, 1);
1985 builder.def_var(my_var, one);
1986
1987 let normal_return = BlockCall::new(block3, [], &mut builder.func.dfg.value_lists);
1988 let exception_table = builder
1989 .func
1990 .dfg
1991 .exception_tables
1992 .push(ExceptionTableData::new(sig0, normal_return, []));
1993 builder.ins().try_call(fn0, &[], exception_table);
1994
1995 builder.switch_to_block(block2);
1996 let two = builder.ins().iconst(I32, 2);
1997 builder.def_var(my_var, two);
1998
1999 let normal_return = BlockCall::new(block3, [], &mut builder.func.dfg.value_lists);
2000 let exception_table = builder
2001 .func
2002 .dfg
2003 .exception_tables
2004 .push(ExceptionTableData::new(sig0, normal_return, []));
2005 builder.ins().try_call(fn0, &[], exception_table);
2006
2007 builder.switch_to_block(block3);
2008 let ret_val = builder.use_var(my_var);
2009 builder.ins().return_(&[ret_val]);
2010
2011 builder.seal_all_blocks();
2012 builder.finalize();
2013
2014 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
2015 let ctx = cranelift_codegen::Context::for_function(func);
2016 ctx.verify(&flags).expect("should be valid");
2017
2018 check(
2019 &ctx.func,
2020 "function %sample(i8) -> i32 system_v {
2021 sig0 = () system_v
2022 fn0 = u0:0 sig0
2023
2024block0(v0: i8):
2025 brif v0, block1, block2
2026
2027block1:
2028 v1 = iconst.i32 1
2029 try_call fn0(), sig0, block3(v1), [] ; v1 = 1
2030
2031block2:
2032 v2 = iconst.i32 2
2033 try_call fn0(), sig0, block3(v2), [] ; v2 = 2
2034
2035block3(v3: i32):
2036 return v3
2037}",
2038 );
2039 }
2040}