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, SecondaryMap};
8use cranelift_codegen::ir;
9use cranelift_codegen::ir::condcodes::IntCC;
10use cranelift_codegen::ir::{
11 types, 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,
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 types: SecondaryMap<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 types,
75 stack_map_vars,
76 stack_map_values,
77 safepoints,
78 } = self;
79 ssa.clear();
80 status.clear();
81 types.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.types.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 inst => debug_assert!(!inst.opcode().is_branch()),
183 }
184
185 if data.opcode().is_terminator() {
186 self.builder.fill_current_block()
187 }
188 (inst, &mut self.builder.func.dfg)
189 }
190}
191
192#[derive(Debug, Copy, Clone, PartialEq, Eq)]
193pub enum UseVariableError {
195 UsedBeforeDeclared(Variable),
196}
197
198impl fmt::Display for UseVariableError {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 match self {
201 UseVariableError::UsedBeforeDeclared(variable) => {
202 write!(
203 f,
204 "variable {} was used before it was defined",
205 variable.index()
206 )?;
207 }
208 }
209 Ok(())
210 }
211}
212
213impl std::error::Error for UseVariableError {}
214
215#[derive(Debug, Copy, Clone, Eq, PartialEq)]
216pub enum DeclareVariableError {
218 DeclaredMultipleTimes(Variable),
219}
220
221impl std::error::Error for DeclareVariableError {}
222
223impl fmt::Display for DeclareVariableError {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 match self {
226 DeclareVariableError::DeclaredMultipleTimes(variable) => {
227 write!(
228 f,
229 "variable {} was declared multiple times",
230 variable.index()
231 )?;
232 }
233 }
234 Ok(())
235 }
236}
237
238#[derive(Debug, Copy, Clone, Eq, PartialEq)]
239pub enum DefVariableError {
241 TypeMismatch(Variable, Value),
247 DefinedBeforeDeclared(Variable),
250}
251
252impl fmt::Display for DefVariableError {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 match self {
255 DefVariableError::TypeMismatch(variable, value) => {
256 write!(
257 f,
258 "the types of variable {} and value {} are not the same.
259 The `Value` supplied to `def_var` must be of the same type as
260 the variable was declared to be of in `declare_var`.",
261 variable.index(),
262 value.as_u32()
263 )?;
264 }
265 DefVariableError::DefinedBeforeDeclared(variable) => {
266 write!(
267 f,
268 "the value of variable {} was declared before it was defined",
269 variable.index()
270 )?;
271 }
272 }
273 Ok(())
274 }
275}
276
277impl<'a> FunctionBuilder<'a> {
310 pub fn new(func: &'a mut Function, func_ctx: &'a mut FunctionBuilderContext) -> Self {
313 debug_assert!(func_ctx.is_empty());
314 Self {
315 func,
316 srcloc: Default::default(),
317 func_ctx,
318 position: Default::default(),
319 }
320 }
321
322 pub fn current_block(&self) -> Option<Block> {
324 self.position.expand()
325 }
326
327 pub fn set_srcloc(&mut self, srcloc: ir::SourceLoc) {
329 self.srcloc = srcloc;
330 }
331
332 pub fn create_block(&mut self) -> Block {
334 let block = self.func.dfg.make_block();
335 self.func_ctx.ssa.declare_block(block);
336 block
337 }
338
339 pub fn set_cold_block(&mut self, block: Block) {
344 self.func.layout.set_cold(block);
345 }
346
347 pub fn insert_block_after(&mut self, block: Block, after: Block) {
349 self.func.layout.insert_block_after(block, after);
350 }
351
352 pub fn switch_to_block(&mut self, block: Block) {
360 log::trace!("switch to {block:?}");
361
362 debug_assert!(
364 self.position.is_none()
365 || self.is_unreachable()
366 || self.is_pristine(self.position.unwrap())
367 || self.is_filled(self.position.unwrap()),
368 "you have to fill your block before switching"
369 );
370 debug_assert!(
372 !self.is_filled(block),
373 "you cannot switch to a block which is already filled"
374 );
375
376 self.position = PackedOption::from(block);
378 }
379
380 pub fn seal_block(&mut self, block: Block) {
386 let side_effects = self.func_ctx.ssa.seal_block(block, self.func);
387 self.handle_ssa_side_effects(side_effects);
388 }
389
390 pub fn seal_all_blocks(&mut self) {
397 let side_effects = self.func_ctx.ssa.seal_all_blocks(self.func);
398 self.handle_ssa_side_effects(side_effects);
399 }
400
401 pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> {
411 if self.func_ctx.types[var] != types::INVALID {
412 return Err(DeclareVariableError::DeclaredMultipleTimes(var));
413 }
414 self.func_ctx.types[var] = ty;
415 Ok(())
416 }
417
418 pub fn declare_var(&mut self, var: Variable, ty: Type) {
424 self.try_declare_var(var, ty)
425 .unwrap_or_else(|_| panic!("the variable {var:?} has been declared multiple times"))
426 }
427
428 pub fn declare_var_needs_stack_map(&mut self, var: Variable) {
443 log::trace!("declare_var_needs_stack_map({var:?})");
444 let ty = self.func_ctx.types[var];
445 assert!(ty != types::INVALID);
446 assert!(ty.bytes() <= 16);
447 self.func_ctx.stack_map_vars.insert(var);
448 }
449
450 pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {
453 self.ensure_inserted_block();
458
459 let (val, side_effects) = {
460 let ty = *self
461 .func_ctx
462 .types
463 .get(var)
464 .ok_or(UseVariableError::UsedBeforeDeclared(var))?;
465 debug_assert_ne!(
466 ty,
467 types::INVALID,
468 "variable {var:?} is used but its type has not been declared"
469 );
470 self.func_ctx
471 .ssa
472 .use_var(self.func, var, ty, self.position.unwrap())
473 };
474 self.handle_ssa_side_effects(side_effects);
475
476 Ok(val)
477 }
478
479 pub fn use_var(&mut self, var: Variable) -> Value {
482 self.try_use_var(var).unwrap_or_else(|_| {
483 panic!("variable {var:?} is used but its type has not been declared")
484 })
485 }
486
487 pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
491 log::trace!("try_def_var: {var:?} = {val:?}");
492
493 let var_ty = *self
494 .func_ctx
495 .types
496 .get(var)
497 .ok_or(DefVariableError::DefinedBeforeDeclared(var))?;
498 if var_ty != self.func.dfg.value_type(val) {
499 return Err(DefVariableError::TypeMismatch(var, val));
500 }
501
502 self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
503 Ok(())
504 }
505
506 pub fn def_var(&mut self, var: Variable, val: Value) {
509 self.try_def_var(var, val)
510 .unwrap_or_else(|error| match error {
511 DefVariableError::TypeMismatch(var, val) => {
512 panic!("declared type of variable {var:?} doesn't match type of value {val}");
513 }
514 DefVariableError::DefinedBeforeDeclared(var) => {
515 panic!("variable {var:?} is used but its type has not been declared");
516 }
517 })
518 }
519
520 pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
525 if let Some(values_labels) = self.func.stencil.dfg.values_labels.as_mut() {
526 use alloc::collections::btree_map::Entry;
527
528 let start = ValueLabelStart {
529 from: RelSourceLoc::from_base_offset(self.func.params.base_srcloc(), self.srcloc),
530 label,
531 };
532
533 match values_labels.entry(val) {
534 Entry::Occupied(mut e) => match e.get_mut() {
535 ValueLabelAssignments::Starts(starts) => starts.push(start),
536 _ => panic!("Unexpected ValueLabelAssignments at this stage"),
537 },
538 Entry::Vacant(e) => {
539 e.insert(ValueLabelAssignments::Starts(vec![start]));
540 }
541 }
542 }
543 }
544
545 pub fn declare_value_needs_stack_map(&mut self, val: Value) {
557 log::trace!("declare_value_needs_stack_map({val:?})");
558
559 let size = self.func.dfg.value_type(val).bytes();
561 assert!(size <= 16);
562 assert!(size.is_power_of_two());
563
564 self.func_ctx.stack_map_values.insert(val);
565 }
566
567 pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
569 self.func.create_jump_table(data)
570 }
571
572 pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
575 self.func.create_sized_stack_slot(data)
576 }
577
578 pub fn create_dynamic_stack_slot(&mut self, data: DynamicStackSlotData) -> DynamicStackSlot {
583 self.func.create_dynamic_stack_slot(data)
584 }
585
586 pub fn import_signature(&mut self, signature: Signature) -> SigRef {
588 self.func.import_signature(signature)
589 }
590
591 pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef {
593 self.func.import_function(data)
594 }
595
596 pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue {
598 self.func.create_global_value(data)
599 }
600
601 pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> {
604 let block = self
605 .position
606 .expect("Please call switch_to_block before inserting instructions");
607 FuncInstBuilder::new(self, block)
608 }
609
610 pub fn ensure_inserted_block(&mut self) {
612 let block = self.position.unwrap();
613 if self.is_pristine(block) {
614 if !self.func.layout.is_block_inserted(block) {
615 self.func.layout.append_block(block);
616 }
617 self.func_ctx.status[block] = BlockStatus::Partial;
618 } else {
619 debug_assert!(
620 !self.is_filled(block),
621 "you cannot add an instruction to a block already filled"
622 );
623 }
624 }
625
626 pub fn cursor(&mut self) -> FuncCursor {
631 self.ensure_inserted_block();
632 FuncCursor::new(self.func)
633 .with_srcloc(self.srcloc)
634 .at_bottom(self.position.unwrap())
635 }
636
637 pub fn append_block_params_for_function_params(&mut self, block: Block) {
641 debug_assert!(
642 !self.func_ctx.ssa.has_any_predecessors(block),
643 "block parameters for function parameters should only be added to the entry block"
644 );
645
646 debug_assert!(
649 self.is_pristine(block),
650 "You can't add block parameters after adding any instruction"
651 );
652
653 for argtyp in &self.func.stencil.signature.params {
654 self.func
655 .stencil
656 .dfg
657 .append_block_param(block, argtyp.value_type);
658 }
659 }
660
661 pub fn append_block_params_for_function_returns(&mut self, block: Block) {
665 debug_assert!(
668 self.is_pristine(block),
669 "You can't add block parameters after adding any instruction"
670 );
671
672 for argtyp in &self.func.stencil.signature.returns {
673 self.func
674 .stencil
675 .dfg
676 .append_block_param(block, argtyp.value_type);
677 }
678 }
679
680 pub fn finalize(mut self) {
685 #[cfg(debug_assertions)]
687 {
688 for block in self.func_ctx.status.keys() {
689 if !self.is_pristine(block) {
690 assert!(
691 self.func_ctx.ssa.is_sealed(block),
692 "FunctionBuilder finalized, but block {block} is not sealed",
693 );
694 assert!(
695 self.is_filled(block),
696 "FunctionBuilder finalized, but block {block} is not filled",
697 );
698 }
699 }
700 }
701
702 #[cfg(debug_assertions)]
704 {
705 for block in self.func_ctx.status.keys() {
707 if let Err((inst, msg)) = self.func.is_block_basic(block) {
708 let inst_str = self.func.dfg.display_inst(inst);
709 panic!("{block} failed basic block invariants on {inst_str}: {msg}");
710 }
711 }
712 }
713
714 for var in self.func_ctx.stack_map_vars.iter() {
717 for val in self.func_ctx.ssa.values_for_var(var) {
718 log::trace!("propagating needs-stack-map from {var:?} to {val:?}");
719 debug_assert_eq!(self.func.dfg.value_type(val), self.func_ctx.types[var]);
720 self.func_ctx.stack_map_values.insert(val);
721 }
722 }
723
724 if !self.func_ctx.stack_map_values.is_empty() {
728 self.func_ctx
729 .safepoints
730 .run(&mut self.func, &self.func_ctx.stack_map_values);
731 }
732
733 self.func_ctx.clear();
736 }
737}
738
739impl<'a> FunctionBuilder<'a> {
745 pub fn block_params(&self, block: Block) -> &[Value] {
748 self.func.dfg.block_params(block)
749 }
750
751 pub fn signature(&self, sigref: SigRef) -> Option<&Signature> {
754 self.func.dfg.signatures.get(sigref)
755 }
756
757 pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
763 debug_assert!(
764 self.is_pristine(block),
765 "You can't add block parameters after adding any instruction"
766 );
767 self.func.dfg.append_block_param(block, ty)
768 }
769
770 pub fn inst_results(&self, inst: Inst) -> &[Value] {
772 self.func.dfg.inst_results(inst)
773 }
774
775 pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {
780 let dfg = &mut self.func.dfg;
781 for block in dfg.insts[inst].branch_destination_mut(&mut dfg.jump_tables) {
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::frontend::{
1201 DeclareVariableError, DefVariableError, FunctionBuilder, FunctionBuilderContext,
1202 UseVariableError,
1203 };
1204 use crate::Variable;
1205 use alloc::string::ToString;
1206 use cranelift_codegen::entity::EntityRef;
1207 use cranelift_codegen::ir::condcodes::IntCC;
1208 use cranelift_codegen::ir::{types::*, UserFuncName};
1209 use cranelift_codegen::ir::{AbiParam, Function, InstBuilder, MemFlags, Signature, Value};
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 = Variable::new(0);
1230 let y = Variable::new(1);
1231 let z = Variable::new(2);
1232 builder.declare_var(x, I32);
1233 builder.declare_var(y, I32);
1234 builder.declare_var(z, I32);
1235 builder.append_block_params_for_function_params(block0);
1236
1237 builder.switch_to_block(block0);
1238 if !lazy_seal {
1239 builder.seal_block(block0);
1240 }
1241 {
1242 let tmp = builder.block_params(block0)[0]; builder.def_var(x, tmp);
1244 }
1245 {
1246 let tmp = builder.ins().iconst(I32, 2);
1247 builder.def_var(y, tmp);
1248 }
1249 {
1250 let arg1 = builder.use_var(x);
1251 let arg2 = builder.use_var(y);
1252 let tmp = builder.ins().iadd(arg1, arg2);
1253 builder.def_var(z, tmp);
1254 }
1255 builder.ins().jump(block1, &[]);
1256
1257 builder.switch_to_block(block1);
1258 {
1259 let arg1 = builder.use_var(y);
1260 let arg2 = builder.use_var(z);
1261 let tmp = builder.ins().iadd(arg1, arg2);
1262 builder.def_var(z, tmp);
1263 }
1264 {
1265 let arg = builder.use_var(y);
1266 builder.ins().brif(arg, block3, &[], block2, &[]);
1267 }
1268
1269 builder.switch_to_block(block2);
1270 if !lazy_seal {
1271 builder.seal_block(block2);
1272 }
1273 {
1274 let arg1 = builder.use_var(z);
1275 let arg2 = builder.use_var(x);
1276 let tmp = builder.ins().isub(arg1, arg2);
1277 builder.def_var(z, tmp);
1278 }
1279 {
1280 let arg = builder.use_var(y);
1281 builder.ins().return_(&[arg]);
1282 }
1283
1284 builder.switch_to_block(block3);
1285 if !lazy_seal {
1286 builder.seal_block(block3);
1287 }
1288
1289 {
1290 let arg1 = builder.use_var(y);
1291 let arg2 = builder.use_var(x);
1292 let tmp = builder.ins().isub(arg1, arg2);
1293 builder.def_var(y, tmp);
1294 }
1295 builder.ins().jump(block1, &[]);
1296 if !lazy_seal {
1297 builder.seal_block(block1);
1298 }
1299
1300 if lazy_seal {
1301 builder.seal_all_blocks();
1302 }
1303
1304 builder.finalize();
1305 }
1306
1307 let flags = settings::Flags::new(settings::builder());
1308 if let Err(errors) = verify_function(&func, &flags) {
1310 panic!("{}\n{}", func.display(), errors)
1311 }
1312 }
1313
1314 #[test]
1315 fn sample() {
1316 sample_function(false)
1317 }
1318
1319 #[test]
1320 fn sample_with_lazy_seal() {
1321 sample_function(true)
1322 }
1323
1324 #[track_caller]
1325 fn check(func: &Function, expected_ir: &str) {
1326 let expected_ir = expected_ir.trim();
1327 let actual_ir = func.display().to_string();
1328 let actual_ir = actual_ir.trim();
1329 assert!(
1330 expected_ir == actual_ir,
1331 "Expected:\n{expected_ir}\nGot:\n{actual_ir}"
1332 );
1333 }
1334
1335 fn systemv_frontend_config() -> TargetFrontendConfig {
1337 TargetFrontendConfig {
1338 default_call_conv: CallConv::SystemV,
1339 pointer_width: PointerWidth::U64,
1340 page_size_align_log2: 12,
1341 }
1342 }
1343
1344 #[test]
1345 fn memcpy() {
1346 let frontend_config = systemv_frontend_config();
1347 let mut sig = Signature::new(frontend_config.default_call_conv);
1348 sig.returns.push(AbiParam::new(I32));
1349
1350 let mut fn_ctx = FunctionBuilderContext::new();
1351 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1352 {
1353 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1354
1355 let block0 = builder.create_block();
1356 let x = Variable::new(0);
1357 let y = Variable::new(1);
1358 let z = Variable::new(2);
1359 builder.declare_var(x, frontend_config.pointer_type());
1360 builder.declare_var(y, frontend_config.pointer_type());
1361 builder.declare_var(z, I32);
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 = Variable::new(0);
1406 let y = Variable::new(16);
1407 builder.declare_var(x, frontend_config.pointer_type());
1408 builder.declare_var(y, frontend_config.pointer_type());
1409 builder.append_block_params_for_function_params(block0);
1410 builder.switch_to_block(block0);
1411
1412 let src = builder.use_var(x);
1413 let dest = builder.use_var(y);
1414 let size = 8;
1415 builder.emit_small_memory_copy(
1416 frontend_config,
1417 dest,
1418 src,
1419 size,
1420 8,
1421 8,
1422 true,
1423 MemFlags::new(),
1424 );
1425 builder.ins().return_(&[dest]);
1426
1427 builder.seal_all_blocks();
1428 builder.finalize();
1429 }
1430
1431 check(
1432 &func,
1433 "function %sample() -> i32 system_v {
1434block0:
1435 v4 = iconst.i64 0
1436 v1 -> v4
1437 v3 = iconst.i64 0
1438 v0 -> v3
1439 v2 = load.i64 aligned v0 ; v0 = 0
1440 store aligned v2, v1 ; v1 = 0
1441 return v1 ; v1 = 0
1442}
1443",
1444 );
1445 }
1446
1447 #[test]
1448 fn not_so_small_memcpy() {
1449 let frontend_config = systemv_frontend_config();
1450 let mut sig = Signature::new(frontend_config.default_call_conv);
1451 sig.returns.push(AbiParam::new(I32));
1452
1453 let mut fn_ctx = FunctionBuilderContext::new();
1454 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1455 {
1456 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1457
1458 let block0 = builder.create_block();
1459 let x = Variable::new(0);
1460 let y = Variable::new(16);
1461 builder.declare_var(x, frontend_config.pointer_type());
1462 builder.declare_var(y, frontend_config.pointer_type());
1463 builder.append_block_params_for_function_params(block0);
1464 builder.switch_to_block(block0);
1465
1466 let src = builder.use_var(x);
1467 let dest = builder.use_var(y);
1468 let size = 8192;
1469 builder.emit_small_memory_copy(
1470 frontend_config,
1471 dest,
1472 src,
1473 size,
1474 8,
1475 8,
1476 true,
1477 MemFlags::new(),
1478 );
1479 builder.ins().return_(&[dest]);
1480
1481 builder.seal_all_blocks();
1482 builder.finalize();
1483 }
1484
1485 check(
1486 &func,
1487 "function %sample() -> i32 system_v {
1488 sig0 = (i64, i64, i64) -> i64 system_v
1489 fn0 = %Memcpy sig0
1490
1491block0:
1492 v5 = iconst.i64 0
1493 v1 -> v5
1494 v4 = iconst.i64 0
1495 v0 -> v4
1496 v2 = iconst.i64 8192
1497 v3 = call fn0(v1, v0, v2) ; v1 = 0, v0 = 0, v2 = 8192
1498 return v1 ; v1 = 0
1499}
1500",
1501 );
1502 }
1503
1504 #[test]
1505 fn small_memset() {
1506 let frontend_config = systemv_frontend_config();
1507 let mut sig = Signature::new(frontend_config.default_call_conv);
1508 sig.returns.push(AbiParam::new(I32));
1509
1510 let mut fn_ctx = FunctionBuilderContext::new();
1511 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1512 {
1513 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1514
1515 let block0 = builder.create_block();
1516 let y = Variable::new(16);
1517 builder.declare_var(y, frontend_config.pointer_type());
1518 builder.append_block_params_for_function_params(block0);
1519 builder.switch_to_block(block0);
1520
1521 let dest = builder.use_var(y);
1522 let size = 8;
1523 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1524 builder.ins().return_(&[dest]);
1525
1526 builder.seal_all_blocks();
1527 builder.finalize();
1528 }
1529
1530 check(
1531 &func,
1532 "function %sample() -> i32 system_v {
1533block0:
1534 v2 = iconst.i64 0
1535 v0 -> v2
1536 v1 = iconst.i64 0x0101_0101_0101_0101
1537 store aligned v1, v0 ; v1 = 0x0101_0101_0101_0101, v0 = 0
1538 return v0 ; v0 = 0
1539}
1540",
1541 );
1542 }
1543
1544 #[test]
1545 fn not_so_small_memset() {
1546 let frontend_config = systemv_frontend_config();
1547 let mut sig = Signature::new(frontend_config.default_call_conv);
1548 sig.returns.push(AbiParam::new(I32));
1549
1550 let mut fn_ctx = FunctionBuilderContext::new();
1551 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1552 {
1553 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1554
1555 let block0 = builder.create_block();
1556 let y = Variable::new(16);
1557 builder.declare_var(y, frontend_config.pointer_type());
1558 builder.append_block_params_for_function_params(block0);
1559 builder.switch_to_block(block0);
1560
1561 let dest = builder.use_var(y);
1562 let size = 8192;
1563 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1564 builder.ins().return_(&[dest]);
1565
1566 builder.seal_all_blocks();
1567 builder.finalize();
1568 }
1569
1570 check(
1571 &func,
1572 "function %sample() -> i32 system_v {
1573 sig0 = (i64, i32, i64) -> i64 system_v
1574 fn0 = %Memset sig0
1575
1576block0:
1577 v5 = iconst.i64 0
1578 v0 -> v5
1579 v1 = iconst.i8 1
1580 v2 = iconst.i64 8192
1581 v3 = uextend.i32 v1 ; v1 = 1
1582 v4 = call fn0(v0, v3, v2) ; v0 = 0, v2 = 8192
1583 return v0 ; v0 = 0
1584}
1585",
1586 );
1587 }
1588
1589 #[test]
1590 fn memcmp() {
1591 use core::str::FromStr;
1592 use cranelift_codegen::isa;
1593
1594 let shared_builder = settings::builder();
1595 let shared_flags = settings::Flags::new(shared_builder);
1596
1597 let triple =
1598 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1599
1600 let target = isa::lookup(triple)
1601 .ok()
1602 .map(|b| b.finish(shared_flags))
1603 .expect("This test requires x86_64 support.")
1604 .expect("Should be able to create backend with default flags");
1605
1606 let mut sig = Signature::new(target.default_call_conv());
1607 sig.returns.push(AbiParam::new(I32));
1608
1609 let mut fn_ctx = FunctionBuilderContext::new();
1610 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1611 {
1612 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1613
1614 let block0 = builder.create_block();
1615 let x = Variable::new(0);
1616 let y = Variable::new(1);
1617 let z = Variable::new(2);
1618 builder.declare_var(x, target.pointer_type());
1619 builder.declare_var(y, target.pointer_type());
1620 builder.declare_var(z, target.pointer_type());
1621 builder.append_block_params_for_function_params(block0);
1622 builder.switch_to_block(block0);
1623
1624 let left = builder.use_var(x);
1625 let right = builder.use_var(y);
1626 let size = builder.use_var(z);
1627 let cmp = builder.call_memcmp(target.frontend_config(), left, right, size);
1628 builder.ins().return_(&[cmp]);
1629
1630 builder.seal_all_blocks();
1631 builder.finalize();
1632 }
1633
1634 check(
1635 &func,
1636 "function %sample() -> i32 system_v {
1637 sig0 = (i64, i64, i64) -> i32 system_v
1638 fn0 = %Memcmp sig0
1639
1640block0:
1641 v6 = iconst.i64 0
1642 v2 -> v6
1643 v5 = iconst.i64 0
1644 v1 -> v5
1645 v4 = iconst.i64 0
1646 v0 -> v4
1647 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 0
1648 return v3
1649}
1650",
1651 );
1652 }
1653
1654 #[test]
1655 fn small_memcmp_zero_size() {
1656 let align_eight = std::num::NonZeroU8::new(8).unwrap();
1657 small_memcmp_helper(
1658 "
1659block0:
1660 v4 = iconst.i64 0
1661 v1 -> v4
1662 v3 = iconst.i64 0
1663 v0 -> v3
1664 v2 = iconst.i8 1
1665 return v2 ; v2 = 1",
1666 |builder, target, x, y| {
1667 builder.emit_small_memory_compare(
1668 target.frontend_config(),
1669 IntCC::UnsignedGreaterThanOrEqual,
1670 x,
1671 y,
1672 0,
1673 align_eight,
1674 align_eight,
1675 MemFlags::new(),
1676 )
1677 },
1678 );
1679 }
1680
1681 #[test]
1682 fn small_memcmp_byte_ugt() {
1683 let align_one = std::num::NonZeroU8::new(1).unwrap();
1684 small_memcmp_helper(
1685 "
1686block0:
1687 v6 = iconst.i64 0
1688 v1 -> v6
1689 v5 = iconst.i64 0
1690 v0 -> v5
1691 v2 = load.i8 aligned v0 ; v0 = 0
1692 v3 = load.i8 aligned v1 ; v1 = 0
1693 v4 = icmp ugt v2, v3
1694 return v4",
1695 |builder, target, x, y| {
1696 builder.emit_small_memory_compare(
1697 target.frontend_config(),
1698 IntCC::UnsignedGreaterThan,
1699 x,
1700 y,
1701 1,
1702 align_one,
1703 align_one,
1704 MemFlags::new(),
1705 )
1706 },
1707 );
1708 }
1709
1710 #[test]
1711 fn small_memcmp_aligned_eq() {
1712 let align_four = std::num::NonZeroU8::new(4).unwrap();
1713 small_memcmp_helper(
1714 "
1715block0:
1716 v6 = iconst.i64 0
1717 v1 -> v6
1718 v5 = iconst.i64 0
1719 v0 -> v5
1720 v2 = load.i32 aligned v0 ; v0 = 0
1721 v3 = load.i32 aligned v1 ; v1 = 0
1722 v4 = icmp eq v2, v3
1723 return v4",
1724 |builder, target, x, y| {
1725 builder.emit_small_memory_compare(
1726 target.frontend_config(),
1727 IntCC::Equal,
1728 x,
1729 y,
1730 4,
1731 align_four,
1732 align_four,
1733 MemFlags::new(),
1734 )
1735 },
1736 );
1737 }
1738
1739 #[test]
1740 fn small_memcmp_ipv6_ne() {
1741 let align_two = std::num::NonZeroU8::new(2).unwrap();
1742 small_memcmp_helper(
1743 "
1744block0:
1745 v6 = iconst.i64 0
1746 v1 -> v6
1747 v5 = iconst.i64 0
1748 v0 -> v5
1749 v2 = load.i128 v0 ; v0 = 0
1750 v3 = load.i128 v1 ; v1 = 0
1751 v4 = icmp ne v2, v3
1752 return v4",
1753 |builder, target, x, y| {
1754 builder.emit_small_memory_compare(
1755 target.frontend_config(),
1756 IntCC::NotEqual,
1757 x,
1758 y,
1759 16,
1760 align_two,
1761 align_two,
1762 MemFlags::new(),
1763 )
1764 },
1765 );
1766 }
1767
1768 #[test]
1769 fn small_memcmp_odd_size_uge() {
1770 let one = std::num::NonZeroU8::new(1).unwrap();
1771 small_memcmp_helper(
1772 "
1773 sig0 = (i64, i64, i64) -> i32 system_v
1774 fn0 = %Memcmp sig0
1775
1776block0:
1777 v6 = iconst.i64 0
1778 v1 -> v6
1779 v5 = iconst.i64 0
1780 v0 -> v5
1781 v2 = iconst.i64 3
1782 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 3
1783 v4 = icmp_imm sge v3, 0
1784 return v4",
1785 |builder, target, x, y| {
1786 builder.emit_small_memory_compare(
1787 target.frontend_config(),
1788 IntCC::UnsignedGreaterThanOrEqual,
1789 x,
1790 y,
1791 3,
1792 one,
1793 one,
1794 MemFlags::new(),
1795 )
1796 },
1797 );
1798 }
1799
1800 fn small_memcmp_helper(
1801 expected: &str,
1802 f: impl FnOnce(&mut FunctionBuilder, &dyn TargetIsa, Value, Value) -> Value,
1803 ) {
1804 use core::str::FromStr;
1805 use cranelift_codegen::isa;
1806
1807 let shared_builder = settings::builder();
1808 let shared_flags = settings::Flags::new(shared_builder);
1809
1810 let triple =
1811 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1812
1813 let target = isa::lookup(triple)
1814 .ok()
1815 .map(|b| b.finish(shared_flags))
1816 .expect("This test requires x86_64 support.")
1817 .expect("Should be able to create backend with default flags");
1818
1819 let mut sig = Signature::new(target.default_call_conv());
1820 sig.returns.push(AbiParam::new(I8));
1821
1822 let mut fn_ctx = FunctionBuilderContext::new();
1823 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1824 {
1825 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1826
1827 let block0 = builder.create_block();
1828 let x = Variable::new(0);
1829 let y = Variable::new(1);
1830 builder.declare_var(x, target.pointer_type());
1831 builder.declare_var(y, target.pointer_type());
1832 builder.append_block_params_for_function_params(block0);
1833 builder.switch_to_block(block0);
1834
1835 let left = builder.use_var(x);
1836 let right = builder.use_var(y);
1837 let ret = f(&mut builder, &*target, left, right);
1838 builder.ins().return_(&[ret]);
1839
1840 builder.seal_all_blocks();
1841 builder.finalize();
1842 }
1843
1844 check(
1845 &func,
1846 &format!("function %sample() -> i8 system_v {{{expected}\n}}\n"),
1847 );
1848 }
1849
1850 #[test]
1851 fn undef_vector_vars() {
1852 let mut sig = Signature::new(CallConv::SystemV);
1853 sig.returns.push(AbiParam::new(I8X16));
1854 sig.returns.push(AbiParam::new(I8X16));
1855 sig.returns.push(AbiParam::new(F32X4));
1856
1857 let mut fn_ctx = FunctionBuilderContext::new();
1858 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1859 {
1860 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1861
1862 let block0 = builder.create_block();
1863 let a = Variable::new(0);
1864 let b = Variable::new(1);
1865 let c = Variable::new(2);
1866 builder.declare_var(a, I8X16);
1867 builder.declare_var(b, I8X16);
1868 builder.declare_var(c, F32X4);
1869 builder.switch_to_block(block0);
1870
1871 let a = builder.use_var(a);
1872 let b = builder.use_var(b);
1873 let c = builder.use_var(c);
1874 builder.ins().return_(&[a, b, c]);
1875
1876 builder.seal_all_blocks();
1877 builder.finalize();
1878 }
1879
1880 check(
1881 &func,
1882 "function %sample() -> i8x16, i8x16, f32x4 system_v {
1883 const0 = 0x00000000000000000000000000000000
1884
1885block0:
1886 v5 = f32const 0.0
1887 v6 = splat.f32x4 v5 ; v5 = 0.0
1888 v2 -> v6
1889 v4 = vconst.i8x16 const0
1890 v1 -> v4
1891 v3 = vconst.i8x16 const0
1892 v0 -> v3
1893 return v0, v1, v2 ; v0 = const0, v1 = const0
1894}
1895",
1896 );
1897 }
1898
1899 #[test]
1900 fn test_greatest_divisible_power_of_two() {
1901 assert_eq!(64, greatest_divisible_power_of_two(64));
1902 assert_eq!(16, greatest_divisible_power_of_two(48));
1903 assert_eq!(8, greatest_divisible_power_of_two(24));
1904 assert_eq!(1, greatest_divisible_power_of_two(25));
1905 }
1906
1907 #[test]
1908 fn try_use_var() {
1909 let sig = Signature::new(CallConv::SystemV);
1910
1911 let mut fn_ctx = FunctionBuilderContext::new();
1912 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1913 {
1914 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1915
1916 let block0 = builder.create_block();
1917 builder.append_block_params_for_function_params(block0);
1918 builder.switch_to_block(block0);
1919
1920 assert_eq!(
1921 builder.try_use_var(Variable::from_u32(0)),
1922 Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))
1923 );
1924
1925 let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
1926
1927 assert_eq!(
1928 builder.try_def_var(Variable::from_u32(0), value),
1929 Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(
1930 0
1931 )))
1932 );
1933
1934 builder.declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32);
1935 assert_eq!(
1936 builder.try_declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32),
1937 Err(DeclareVariableError::DeclaredMultipleTimes(
1938 Variable::from_u32(0)
1939 ))
1940 );
1941 }
1942 }
1943
1944 #[test]
1945 fn test_builder_with_iconst_and_negative_constant() {
1946 let sig = Signature::new(CallConv::SystemV);
1947 let mut fn_ctx = FunctionBuilderContext::new();
1948 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1949
1950 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1951
1952 let block0 = builder.create_block();
1953 builder.switch_to_block(block0);
1954 builder.ins().iconst(I32, -1);
1955 builder.ins().return_(&[]);
1956
1957 builder.seal_all_blocks();
1958 builder.finalize();
1959
1960 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
1961 let ctx = cranelift_codegen::Context::for_function(func);
1962 ctx.verify(&flags).expect("should be valid");
1963
1964 check(
1965 &ctx.func,
1966 "function %sample() system_v {
1967block0:
1968 v0 = iconst.i32 -1
1969 return
1970}",
1971 );
1972 }
1973}