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 if self.func_ctx.stack_map_vars.contains(var) {
479 self.declare_value_needs_stack_map(val);
480 }
481
482 Ok(val)
483 }
484
485 pub fn use_var(&mut self, var: Variable) -> Value {
488 self.try_use_var(var).unwrap_or_else(|_| {
489 panic!("variable {var:?} is used but its type has not been declared")
490 })
491 }
492
493 pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
497 let var_ty = *self
498 .func_ctx
499 .types
500 .get(var)
501 .ok_or(DefVariableError::DefinedBeforeDeclared(var))?;
502 if var_ty != self.func.dfg.value_type(val) {
503 return Err(DefVariableError::TypeMismatch(var, val));
504 }
505
506 if self.func_ctx.stack_map_vars.contains(var) {
508 self.declare_value_needs_stack_map(val);
509 }
510
511 self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
512 Ok(())
513 }
514
515 pub fn def_var(&mut self, var: Variable, val: Value) {
518 self.try_def_var(var, val)
519 .unwrap_or_else(|error| match error {
520 DefVariableError::TypeMismatch(var, val) => {
521 panic!("declared type of variable {var:?} doesn't match type of value {val}");
522 }
523 DefVariableError::DefinedBeforeDeclared(var) => {
524 panic!("variable {var:?} is used but its type has not been declared");
525 }
526 })
527 }
528
529 pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
534 if let Some(values_labels) = self.func.stencil.dfg.values_labels.as_mut() {
535 use alloc::collections::btree_map::Entry;
536
537 let start = ValueLabelStart {
538 from: RelSourceLoc::from_base_offset(self.func.params.base_srcloc(), self.srcloc),
539 label,
540 };
541
542 match values_labels.entry(val) {
543 Entry::Occupied(mut e) => match e.get_mut() {
544 ValueLabelAssignments::Starts(starts) => starts.push(start),
545 _ => panic!("Unexpected ValueLabelAssignments at this stage"),
546 },
547 Entry::Vacant(e) => {
548 e.insert(ValueLabelAssignments::Starts(vec![start]));
549 }
550 }
551 }
552 }
553
554 pub fn declare_value_needs_stack_map(&mut self, val: Value) {
566 log::trace!("declare_value_needs_stack_map({val:?})");
567
568 let size = self.func.dfg.value_type(val).bytes();
570 assert!(size <= 16);
571 assert!(size.is_power_of_two());
572
573 self.func_ctx.stack_map_values.insert(val);
574 }
575
576 pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
578 self.func.create_jump_table(data)
579 }
580
581 pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
584 self.func.create_sized_stack_slot(data)
585 }
586
587 pub fn create_dynamic_stack_slot(&mut self, data: DynamicStackSlotData) -> DynamicStackSlot {
592 self.func.create_dynamic_stack_slot(data)
593 }
594
595 pub fn import_signature(&mut self, signature: Signature) -> SigRef {
597 self.func.import_signature(signature)
598 }
599
600 pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef {
602 self.func.import_function(data)
603 }
604
605 pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue {
607 self.func.create_global_value(data)
608 }
609
610 pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> {
613 let block = self
614 .position
615 .expect("Please call switch_to_block before inserting instructions");
616 FuncInstBuilder::new(self, block)
617 }
618
619 pub fn ensure_inserted_block(&mut self) {
621 let block = self.position.unwrap();
622 if self.is_pristine(block) {
623 if !self.func.layout.is_block_inserted(block) {
624 self.func.layout.append_block(block);
625 }
626 self.func_ctx.status[block] = BlockStatus::Partial;
627 } else {
628 debug_assert!(
629 !self.is_filled(block),
630 "you cannot add an instruction to a block already filled"
631 );
632 }
633 }
634
635 pub fn cursor(&mut self) -> FuncCursor {
640 self.ensure_inserted_block();
641 FuncCursor::new(self.func)
642 .with_srcloc(self.srcloc)
643 .at_bottom(self.position.unwrap())
644 }
645
646 pub fn append_block_params_for_function_params(&mut self, block: Block) {
650 debug_assert!(
651 !self.func_ctx.ssa.has_any_predecessors(block),
652 "block parameters for function parameters should only be added to the entry block"
653 );
654
655 debug_assert!(
658 self.is_pristine(block),
659 "You can't add block parameters after adding any instruction"
660 );
661
662 for argtyp in &self.func.stencil.signature.params {
663 self.func
664 .stencil
665 .dfg
666 .append_block_param(block, argtyp.value_type);
667 }
668 }
669
670 pub fn append_block_params_for_function_returns(&mut self, block: Block) {
674 debug_assert!(
677 self.is_pristine(block),
678 "You can't add block parameters after adding any instruction"
679 );
680
681 for argtyp in &self.func.stencil.signature.returns {
682 self.func
683 .stencil
684 .dfg
685 .append_block_param(block, argtyp.value_type);
686 }
687 }
688
689 pub fn finalize(mut self) {
694 #[cfg(debug_assertions)]
696 {
697 for block in self.func_ctx.status.keys() {
698 if !self.is_pristine(block) {
699 assert!(
700 self.func_ctx.ssa.is_sealed(block),
701 "FunctionBuilder finalized, but block {block} is not sealed",
702 );
703 assert!(
704 self.is_filled(block),
705 "FunctionBuilder finalized, but block {block} is not filled",
706 );
707 }
708 }
709 }
710
711 #[cfg(debug_assertions)]
713 {
714 for block in self.func_ctx.status.keys() {
716 if let Err((inst, msg)) = self.func.is_block_basic(block) {
717 let inst_str = self.func.dfg.display_inst(inst);
718 panic!("{block} failed basic block invariants on {inst_str}: {msg}");
719 }
720 }
721 }
722
723 if !self.func_ctx.stack_map_values.is_empty() {
724 self.func_ctx
725 .safepoints
726 .run(&mut self.func, &self.func_ctx.stack_map_values);
727 }
728
729 self.func_ctx.clear();
732 }
733}
734
735impl<'a> FunctionBuilder<'a> {
741 pub fn block_params(&self, block: Block) -> &[Value] {
744 self.func.dfg.block_params(block)
745 }
746
747 pub fn signature(&self, sigref: SigRef) -> Option<&Signature> {
750 self.func.dfg.signatures.get(sigref)
751 }
752
753 pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
759 debug_assert!(
760 self.is_pristine(block),
761 "You can't add block parameters after adding any instruction"
762 );
763 self.func.dfg.append_block_param(block, ty)
764 }
765
766 pub fn inst_results(&self, inst: Inst) -> &[Value] {
768 self.func.dfg.inst_results(inst)
769 }
770
771 pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {
776 let dfg = &mut self.func.dfg;
777 for block in dfg.insts[inst].branch_destination_mut(&mut dfg.jump_tables) {
778 if block.block(&dfg.value_lists) == old_block {
779 self.func_ctx.ssa.remove_block_predecessor(old_block, inst);
780 block.set_block(new_block, &mut dfg.value_lists);
781 self.func_ctx.ssa.declare_block_predecessor(new_block, inst);
782 }
783 }
784 }
785
786 pub fn is_unreachable(&self) -> bool {
790 let is_entry = match self.func.layout.entry_block() {
791 None => false,
792 Some(entry) => self.position.unwrap() == entry,
793 };
794 !is_entry
795 && self.func_ctx.ssa.is_sealed(self.position.unwrap())
796 && !self
797 .func_ctx
798 .ssa
799 .has_any_predecessors(self.position.unwrap())
800 }
801
802 fn is_pristine(&self, block: Block) -> bool {
805 self.func_ctx.status[block] == BlockStatus::Empty
806 }
807
808 fn is_filled(&self, block: Block) -> bool {
811 self.func_ctx.status[block] == BlockStatus::Filled
812 }
813}
814
815impl<'a> FunctionBuilder<'a> {
817 pub fn call_memcpy(
824 &mut self,
825 config: TargetFrontendConfig,
826 dest: Value,
827 src: Value,
828 size: Value,
829 ) {
830 let pointer_type = config.pointer_type();
831 let signature = {
832 let mut s = Signature::new(config.default_call_conv);
833 s.params.push(AbiParam::new(pointer_type));
834 s.params.push(AbiParam::new(pointer_type));
835 s.params.push(AbiParam::new(pointer_type));
836 s.returns.push(AbiParam::new(pointer_type));
837 self.import_signature(s)
838 };
839
840 let libc_memcpy = self.import_function(ExtFuncData {
841 name: ExternalName::LibCall(LibCall::Memcpy),
842 signature,
843 colocated: false,
844 });
845
846 self.ins().call(libc_memcpy, &[dest, src, size]);
847 }
848
849 pub fn emit_small_memory_copy(
858 &mut self,
859 config: TargetFrontendConfig,
860 dest: Value,
861 src: Value,
862 size: u64,
863 dest_align: u8,
864 src_align: u8,
865 non_overlapping: bool,
866 mut flags: MemFlags,
867 ) {
868 const THRESHOLD: u64 = 4;
870
871 if size == 0 {
872 return;
873 }
874
875 let access_size = greatest_divisible_power_of_two(size);
876 assert!(
877 access_size.is_power_of_two(),
878 "`size` is not a power of two"
879 );
880 assert!(
881 access_size >= u64::from(::core::cmp::min(src_align, dest_align)),
882 "`size` is smaller than `dest` and `src`'s alignment value."
883 );
884
885 let (access_size, int_type) = if access_size <= 8 {
886 (access_size, Type::int((access_size * 8) as u16).unwrap())
887 } else {
888 (8, types::I64)
889 };
890
891 let load_and_store_amount = size / access_size;
892
893 if load_and_store_amount > THRESHOLD {
894 let size_value = self.ins().iconst(config.pointer_type(), size as i64);
895 if non_overlapping {
896 self.call_memcpy(config, dest, src, size_value);
897 } else {
898 self.call_memmove(config, dest, src, size_value);
899 }
900 return;
901 }
902
903 if u64::from(src_align) >= access_size && u64::from(dest_align) >= access_size {
904 flags.set_aligned();
905 }
906
907 let registers: smallvec::SmallVec<[_; THRESHOLD as usize]> = (0..load_and_store_amount)
910 .map(|i| {
911 let offset = (access_size * i) as i32;
912 (self.ins().load(int_type, flags, src, offset), offset)
913 })
914 .collect();
915
916 for (value, offset) in registers {
917 self.ins().store(flags, value, dest, offset);
918 }
919 }
920
921 pub fn call_memset(
925 &mut self,
926 config: TargetFrontendConfig,
927 buffer: Value,
928 ch: Value,
929 size: Value,
930 ) {
931 let pointer_type = config.pointer_type();
932 let signature = {
933 let mut s = Signature::new(config.default_call_conv);
934 s.params.push(AbiParam::new(pointer_type));
935 s.params.push(AbiParam::new(types::I32));
936 s.params.push(AbiParam::new(pointer_type));
937 s.returns.push(AbiParam::new(pointer_type));
938 self.import_signature(s)
939 };
940
941 let libc_memset = self.import_function(ExtFuncData {
942 name: ExternalName::LibCall(LibCall::Memset),
943 signature,
944 colocated: false,
945 });
946
947 let ch = self.ins().uextend(types::I32, ch);
948 self.ins().call(libc_memset, &[buffer, ch, size]);
949 }
950
951 pub fn emit_small_memset(
955 &mut self,
956 config: TargetFrontendConfig,
957 buffer: Value,
958 ch: u8,
959 size: u64,
960 buffer_align: u8,
961 mut flags: MemFlags,
962 ) {
963 const THRESHOLD: u64 = 4;
965
966 if size == 0 {
967 return;
968 }
969
970 let access_size = greatest_divisible_power_of_two(size);
971 assert!(
972 access_size.is_power_of_two(),
973 "`size` is not a power of two"
974 );
975 assert!(
976 access_size >= u64::from(buffer_align),
977 "`size` is smaller than `dest` and `src`'s alignment value."
978 );
979
980 let (access_size, int_type) = if access_size <= 8 {
981 (access_size, Type::int((access_size * 8) as u16).unwrap())
982 } else {
983 (8, types::I64)
984 };
985
986 let load_and_store_amount = size / access_size;
987
988 if load_and_store_amount > THRESHOLD {
989 let ch = self.ins().iconst(types::I8, i64::from(ch));
990 let size = self.ins().iconst(config.pointer_type(), size as i64);
991 self.call_memset(config, buffer, ch, size);
992 } else {
993 if u64::from(buffer_align) >= access_size {
994 flags.set_aligned();
995 }
996
997 let ch = u64::from(ch);
998 let raw_value = if int_type == types::I64 {
999 ch * 0x0101010101010101_u64
1000 } else if int_type == types::I32 {
1001 ch * 0x01010101_u64
1002 } else if int_type == types::I16 {
1003 (ch << 8) | ch
1004 } else {
1005 assert_eq!(int_type, types::I8);
1006 ch
1007 };
1008
1009 let value = self.ins().iconst(int_type, raw_value as i64);
1010 for i in 0..load_and_store_amount {
1011 let offset = (access_size * i) as i32;
1012 self.ins().store(flags, value, buffer, offset);
1013 }
1014 }
1015 }
1016
1017 pub fn call_memmove(
1022 &mut self,
1023 config: TargetFrontendConfig,
1024 dest: Value,
1025 source: Value,
1026 size: Value,
1027 ) {
1028 let pointer_type = config.pointer_type();
1029 let signature = {
1030 let mut s = Signature::new(config.default_call_conv);
1031 s.params.push(AbiParam::new(pointer_type));
1032 s.params.push(AbiParam::new(pointer_type));
1033 s.params.push(AbiParam::new(pointer_type));
1034 s.returns.push(AbiParam::new(pointer_type));
1035 self.import_signature(s)
1036 };
1037
1038 let libc_memmove = self.import_function(ExtFuncData {
1039 name: ExternalName::LibCall(LibCall::Memmove),
1040 signature,
1041 colocated: false,
1042 });
1043
1044 self.ins().call(libc_memmove, &[dest, source, size]);
1045 }
1046
1047 pub fn call_memcmp(
1056 &mut self,
1057 config: TargetFrontendConfig,
1058 left: Value,
1059 right: Value,
1060 size: Value,
1061 ) -> Value {
1062 let pointer_type = config.pointer_type();
1063 let signature = {
1064 let mut s = Signature::new(config.default_call_conv);
1065 s.params.reserve(3);
1066 s.params.push(AbiParam::new(pointer_type));
1067 s.params.push(AbiParam::new(pointer_type));
1068 s.params.push(AbiParam::new(pointer_type));
1069 s.returns.push(AbiParam::new(types::I32));
1070 self.import_signature(s)
1071 };
1072
1073 let libc_memcmp = self.import_function(ExtFuncData {
1074 name: ExternalName::LibCall(LibCall::Memcmp),
1075 signature,
1076 colocated: false,
1077 });
1078
1079 let call = self.ins().call(libc_memcmp, &[left, right, size]);
1080 self.func.dfg.first_result(call)
1081 }
1082
1083 pub fn emit_small_memory_compare(
1096 &mut self,
1097 config: TargetFrontendConfig,
1098 int_cc: IntCC,
1099 left: Value,
1100 right: Value,
1101 size: u64,
1102 left_align: std::num::NonZeroU8,
1103 right_align: std::num::NonZeroU8,
1104 flags: MemFlags,
1105 ) -> Value {
1106 use IntCC::*;
1107 let (zero_cc, empty_imm) = match int_cc {
1108 Equal => (Equal, 1),
1110 NotEqual => (NotEqual, 0),
1111
1112 UnsignedLessThan => (SignedLessThan, 0),
1113 UnsignedGreaterThanOrEqual => (SignedGreaterThanOrEqual, 1),
1114 UnsignedGreaterThan => (SignedGreaterThan, 0),
1115 UnsignedLessThanOrEqual => (SignedLessThanOrEqual, 1),
1116
1117 SignedLessThan
1118 | SignedGreaterThanOrEqual
1119 | SignedGreaterThan
1120 | SignedLessThanOrEqual => {
1121 panic!("Signed comparison {int_cc} not supported by memcmp")
1122 }
1123 };
1124
1125 if size == 0 {
1126 return self.ins().iconst(types::I8, empty_imm);
1127 }
1128
1129 if let Some(small_type) = size.try_into().ok().and_then(Type::int_with_byte_size) {
1131 if let Equal | NotEqual = zero_cc {
1132 let mut left_flags = flags;
1133 if size == left_align.get() as u64 {
1134 left_flags.set_aligned();
1135 }
1136 let mut right_flags = flags;
1137 if size == right_align.get() as u64 {
1138 right_flags.set_aligned();
1139 }
1140 let left_val = self.ins().load(small_type, left_flags, left, 0);
1141 let right_val = self.ins().load(small_type, right_flags, right, 0);
1142 return self.ins().icmp(int_cc, left_val, right_val);
1143 } else if small_type == types::I8 {
1144 let mut aligned_flags = flags;
1149 aligned_flags.set_aligned();
1150 let left_val = self.ins().load(small_type, aligned_flags, left, 0);
1151 let right_val = self.ins().load(small_type, aligned_flags, right, 0);
1152 return self.ins().icmp(int_cc, left_val, right_val);
1153 }
1154 }
1155
1156 let pointer_type = config.pointer_type();
1157 let size = self.ins().iconst(pointer_type, size as i64);
1158 let cmp = self.call_memcmp(config, left, right, size);
1159 self.ins().icmp_imm(zero_cc, cmp, 0)
1160 }
1161}
1162
1163fn greatest_divisible_power_of_two(size: u64) -> u64 {
1164 (size as i64 & -(size as i64)) as u64
1165}
1166
1167impl<'a> FunctionBuilder<'a> {
1169 fn fill_current_block(&mut self) {
1171 self.func_ctx.status[self.position.unwrap()] = BlockStatus::Filled;
1172 }
1173
1174 fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) {
1175 self.func_ctx
1176 .ssa
1177 .declare_block_predecessor(dest_block, jump_inst);
1178 }
1179
1180 fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
1181 for modified_block in side_effects.instructions_added_to_blocks {
1182 if self.is_pristine(modified_block) {
1183 self.func_ctx.status[modified_block] = BlockStatus::Partial;
1184 }
1185 }
1186 }
1187}
1188
1189#[cfg(test)]
1190mod tests {
1191 use super::greatest_divisible_power_of_two;
1192 use crate::frontend::{
1193 DeclareVariableError, DefVariableError, FunctionBuilder, FunctionBuilderContext,
1194 UseVariableError,
1195 };
1196 use crate::Variable;
1197 use alloc::string::ToString;
1198 use cranelift_codegen::entity::EntityRef;
1199 use cranelift_codegen::ir::condcodes::IntCC;
1200 use cranelift_codegen::ir::{types::*, UserFuncName};
1201 use cranelift_codegen::ir::{AbiParam, Function, InstBuilder, MemFlags, Signature, Value};
1202 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
1203 use cranelift_codegen::settings;
1204 use cranelift_codegen::verifier::verify_function;
1205 use target_lexicon::PointerWidth;
1206
1207 fn sample_function(lazy_seal: bool) {
1208 let mut sig = Signature::new(CallConv::SystemV);
1209 sig.returns.push(AbiParam::new(I32));
1210 sig.params.push(AbiParam::new(I32));
1211
1212 let mut fn_ctx = FunctionBuilderContext::new();
1213 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1214 {
1215 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1216
1217 let block0 = builder.create_block();
1218 let block1 = builder.create_block();
1219 let block2 = builder.create_block();
1220 let block3 = builder.create_block();
1221 let x = Variable::new(0);
1222 let y = Variable::new(1);
1223 let z = Variable::new(2);
1224 builder.declare_var(x, I32);
1225 builder.declare_var(y, I32);
1226 builder.declare_var(z, I32);
1227 builder.append_block_params_for_function_params(block0);
1228
1229 builder.switch_to_block(block0);
1230 if !lazy_seal {
1231 builder.seal_block(block0);
1232 }
1233 {
1234 let tmp = builder.block_params(block0)[0]; builder.def_var(x, tmp);
1236 }
1237 {
1238 let tmp = builder.ins().iconst(I32, 2);
1239 builder.def_var(y, tmp);
1240 }
1241 {
1242 let arg1 = builder.use_var(x);
1243 let arg2 = builder.use_var(y);
1244 let tmp = builder.ins().iadd(arg1, arg2);
1245 builder.def_var(z, tmp);
1246 }
1247 builder.ins().jump(block1, &[]);
1248
1249 builder.switch_to_block(block1);
1250 {
1251 let arg1 = builder.use_var(y);
1252 let arg2 = builder.use_var(z);
1253 let tmp = builder.ins().iadd(arg1, arg2);
1254 builder.def_var(z, tmp);
1255 }
1256 {
1257 let arg = builder.use_var(y);
1258 builder.ins().brif(arg, block3, &[], block2, &[]);
1259 }
1260
1261 builder.switch_to_block(block2);
1262 if !lazy_seal {
1263 builder.seal_block(block2);
1264 }
1265 {
1266 let arg1 = builder.use_var(z);
1267 let arg2 = builder.use_var(x);
1268 let tmp = builder.ins().isub(arg1, arg2);
1269 builder.def_var(z, tmp);
1270 }
1271 {
1272 let arg = builder.use_var(y);
1273 builder.ins().return_(&[arg]);
1274 }
1275
1276 builder.switch_to_block(block3);
1277 if !lazy_seal {
1278 builder.seal_block(block3);
1279 }
1280
1281 {
1282 let arg1 = builder.use_var(y);
1283 let arg2 = builder.use_var(x);
1284 let tmp = builder.ins().isub(arg1, arg2);
1285 builder.def_var(y, tmp);
1286 }
1287 builder.ins().jump(block1, &[]);
1288 if !lazy_seal {
1289 builder.seal_block(block1);
1290 }
1291
1292 if lazy_seal {
1293 builder.seal_all_blocks();
1294 }
1295
1296 builder.finalize();
1297 }
1298
1299 let flags = settings::Flags::new(settings::builder());
1300 if let Err(errors) = verify_function(&func, &flags) {
1302 panic!("{}\n{}", func.display(), errors)
1303 }
1304 }
1305
1306 #[test]
1307 fn sample() {
1308 sample_function(false)
1309 }
1310
1311 #[test]
1312 fn sample_with_lazy_seal() {
1313 sample_function(true)
1314 }
1315
1316 #[track_caller]
1317 fn check(func: &Function, expected_ir: &str) {
1318 let expected_ir = expected_ir.trim();
1319 let actual_ir = func.display().to_string();
1320 let actual_ir = actual_ir.trim();
1321 assert!(
1322 expected_ir == actual_ir,
1323 "Expected:\n{expected_ir}\nGot:\n{actual_ir}"
1324 );
1325 }
1326
1327 fn systemv_frontend_config() -> TargetFrontendConfig {
1329 TargetFrontendConfig {
1330 default_call_conv: CallConv::SystemV,
1331 pointer_width: PointerWidth::U64,
1332 page_size_align_log2: 12,
1333 }
1334 }
1335
1336 #[test]
1337 fn memcpy() {
1338 let frontend_config = systemv_frontend_config();
1339 let mut sig = Signature::new(frontend_config.default_call_conv);
1340 sig.returns.push(AbiParam::new(I32));
1341
1342 let mut fn_ctx = FunctionBuilderContext::new();
1343 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1344 {
1345 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1346
1347 let block0 = builder.create_block();
1348 let x = Variable::new(0);
1349 let y = Variable::new(1);
1350 let z = Variable::new(2);
1351 builder.declare_var(x, frontend_config.pointer_type());
1352 builder.declare_var(y, frontend_config.pointer_type());
1353 builder.declare_var(z, I32);
1354 builder.append_block_params_for_function_params(block0);
1355 builder.switch_to_block(block0);
1356
1357 let src = builder.use_var(x);
1358 let dest = builder.use_var(y);
1359 let size = builder.use_var(y);
1360 builder.call_memcpy(frontend_config, dest, src, size);
1361 builder.ins().return_(&[size]);
1362
1363 builder.seal_all_blocks();
1364 builder.finalize();
1365 }
1366
1367 check(
1368 &func,
1369 "function %sample() -> i32 system_v {
1370 sig0 = (i64, i64, i64) -> i64 system_v
1371 fn0 = %Memcpy sig0
1372
1373block0:
1374 v4 = iconst.i64 0
1375 v1 -> v4
1376 v3 = iconst.i64 0
1377 v0 -> v3
1378 v2 = call fn0(v1, v0, v1) ; v1 = 0, v0 = 0, v1 = 0
1379 return v1 ; v1 = 0
1380}
1381",
1382 );
1383 }
1384
1385 #[test]
1386 fn small_memcpy() {
1387 let frontend_config = systemv_frontend_config();
1388 let mut sig = Signature::new(frontend_config.default_call_conv);
1389 sig.returns.push(AbiParam::new(I32));
1390
1391 let mut fn_ctx = FunctionBuilderContext::new();
1392 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1393 {
1394 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1395
1396 let block0 = builder.create_block();
1397 let x = Variable::new(0);
1398 let y = Variable::new(16);
1399 builder.declare_var(x, frontend_config.pointer_type());
1400 builder.declare_var(y, frontend_config.pointer_type());
1401 builder.append_block_params_for_function_params(block0);
1402 builder.switch_to_block(block0);
1403
1404 let src = builder.use_var(x);
1405 let dest = builder.use_var(y);
1406 let size = 8;
1407 builder.emit_small_memory_copy(
1408 frontend_config,
1409 dest,
1410 src,
1411 size,
1412 8,
1413 8,
1414 true,
1415 MemFlags::new(),
1416 );
1417 builder.ins().return_(&[dest]);
1418
1419 builder.seal_all_blocks();
1420 builder.finalize();
1421 }
1422
1423 check(
1424 &func,
1425 "function %sample() -> i32 system_v {
1426block0:
1427 v4 = iconst.i64 0
1428 v1 -> v4
1429 v3 = iconst.i64 0
1430 v0 -> v3
1431 v2 = load.i64 aligned v0 ; v0 = 0
1432 store aligned v2, v1 ; v1 = 0
1433 return v1 ; v1 = 0
1434}
1435",
1436 );
1437 }
1438
1439 #[test]
1440 fn not_so_small_memcpy() {
1441 let frontend_config = systemv_frontend_config();
1442 let mut sig = Signature::new(frontend_config.default_call_conv);
1443 sig.returns.push(AbiParam::new(I32));
1444
1445 let mut fn_ctx = FunctionBuilderContext::new();
1446 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1447 {
1448 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1449
1450 let block0 = builder.create_block();
1451 let x = Variable::new(0);
1452 let y = Variable::new(16);
1453 builder.declare_var(x, frontend_config.pointer_type());
1454 builder.declare_var(y, frontend_config.pointer_type());
1455 builder.append_block_params_for_function_params(block0);
1456 builder.switch_to_block(block0);
1457
1458 let src = builder.use_var(x);
1459 let dest = builder.use_var(y);
1460 let size = 8192;
1461 builder.emit_small_memory_copy(
1462 frontend_config,
1463 dest,
1464 src,
1465 size,
1466 8,
1467 8,
1468 true,
1469 MemFlags::new(),
1470 );
1471 builder.ins().return_(&[dest]);
1472
1473 builder.seal_all_blocks();
1474 builder.finalize();
1475 }
1476
1477 check(
1478 &func,
1479 "function %sample() -> i32 system_v {
1480 sig0 = (i64, i64, i64) -> i64 system_v
1481 fn0 = %Memcpy sig0
1482
1483block0:
1484 v5 = iconst.i64 0
1485 v1 -> v5
1486 v4 = iconst.i64 0
1487 v0 -> v4
1488 v2 = iconst.i64 8192
1489 v3 = call fn0(v1, v0, v2) ; v1 = 0, v0 = 0, v2 = 8192
1490 return v1 ; v1 = 0
1491}
1492",
1493 );
1494 }
1495
1496 #[test]
1497 fn small_memset() {
1498 let frontend_config = systemv_frontend_config();
1499 let mut sig = Signature::new(frontend_config.default_call_conv);
1500 sig.returns.push(AbiParam::new(I32));
1501
1502 let mut fn_ctx = FunctionBuilderContext::new();
1503 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1504 {
1505 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1506
1507 let block0 = builder.create_block();
1508 let y = Variable::new(16);
1509 builder.declare_var(y, frontend_config.pointer_type());
1510 builder.append_block_params_for_function_params(block0);
1511 builder.switch_to_block(block0);
1512
1513 let dest = builder.use_var(y);
1514 let size = 8;
1515 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1516 builder.ins().return_(&[dest]);
1517
1518 builder.seal_all_blocks();
1519 builder.finalize();
1520 }
1521
1522 check(
1523 &func,
1524 "function %sample() -> i32 system_v {
1525block0:
1526 v2 = iconst.i64 0
1527 v0 -> v2
1528 v1 = iconst.i64 0x0101_0101_0101_0101
1529 store aligned v1, v0 ; v1 = 0x0101_0101_0101_0101, v0 = 0
1530 return v0 ; v0 = 0
1531}
1532",
1533 );
1534 }
1535
1536 #[test]
1537 fn not_so_small_memset() {
1538 let frontend_config = systemv_frontend_config();
1539 let mut sig = Signature::new(frontend_config.default_call_conv);
1540 sig.returns.push(AbiParam::new(I32));
1541
1542 let mut fn_ctx = FunctionBuilderContext::new();
1543 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1544 {
1545 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1546
1547 let block0 = builder.create_block();
1548 let y = Variable::new(16);
1549 builder.declare_var(y, frontend_config.pointer_type());
1550 builder.append_block_params_for_function_params(block0);
1551 builder.switch_to_block(block0);
1552
1553 let dest = builder.use_var(y);
1554 let size = 8192;
1555 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1556 builder.ins().return_(&[dest]);
1557
1558 builder.seal_all_blocks();
1559 builder.finalize();
1560 }
1561
1562 check(
1563 &func,
1564 "function %sample() -> i32 system_v {
1565 sig0 = (i64, i32, i64) -> i64 system_v
1566 fn0 = %Memset sig0
1567
1568block0:
1569 v5 = iconst.i64 0
1570 v0 -> v5
1571 v1 = iconst.i8 1
1572 v2 = iconst.i64 8192
1573 v3 = uextend.i32 v1 ; v1 = 1
1574 v4 = call fn0(v0, v3, v2) ; v0 = 0, v2 = 8192
1575 return v0 ; v0 = 0
1576}
1577",
1578 );
1579 }
1580
1581 #[test]
1582 fn memcmp() {
1583 use core::str::FromStr;
1584 use cranelift_codegen::isa;
1585
1586 let shared_builder = settings::builder();
1587 let shared_flags = settings::Flags::new(shared_builder);
1588
1589 let triple =
1590 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1591
1592 let target = isa::lookup(triple)
1593 .ok()
1594 .map(|b| b.finish(shared_flags))
1595 .expect("This test requires x86_64 support.")
1596 .expect("Should be able to create backend with default flags");
1597
1598 let mut sig = Signature::new(target.default_call_conv());
1599 sig.returns.push(AbiParam::new(I32));
1600
1601 let mut fn_ctx = FunctionBuilderContext::new();
1602 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1603 {
1604 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1605
1606 let block0 = builder.create_block();
1607 let x = Variable::new(0);
1608 let y = Variable::new(1);
1609 let z = Variable::new(2);
1610 builder.declare_var(x, target.pointer_type());
1611 builder.declare_var(y, target.pointer_type());
1612 builder.declare_var(z, 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 = Variable::new(0);
1821 let y = Variable::new(1);
1822 builder.declare_var(x, target.pointer_type());
1823 builder.declare_var(y, target.pointer_type());
1824 builder.append_block_params_for_function_params(block0);
1825 builder.switch_to_block(block0);
1826
1827 let left = builder.use_var(x);
1828 let right = builder.use_var(y);
1829 let ret = f(&mut builder, &*target, left, right);
1830 builder.ins().return_(&[ret]);
1831
1832 builder.seal_all_blocks();
1833 builder.finalize();
1834 }
1835
1836 check(
1837 &func,
1838 &format!("function %sample() -> i8 system_v {{{expected}\n}}\n"),
1839 );
1840 }
1841
1842 #[test]
1843 fn undef_vector_vars() {
1844 let mut sig = Signature::new(CallConv::SystemV);
1845 sig.returns.push(AbiParam::new(I8X16));
1846 sig.returns.push(AbiParam::new(I8X16));
1847 sig.returns.push(AbiParam::new(F32X4));
1848
1849 let mut fn_ctx = FunctionBuilderContext::new();
1850 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1851 {
1852 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1853
1854 let block0 = builder.create_block();
1855 let a = Variable::new(0);
1856 let b = Variable::new(1);
1857 let c = Variable::new(2);
1858 builder.declare_var(a, I8X16);
1859 builder.declare_var(b, I8X16);
1860 builder.declare_var(c, F32X4);
1861 builder.switch_to_block(block0);
1862
1863 let a = builder.use_var(a);
1864 let b = builder.use_var(b);
1865 let c = builder.use_var(c);
1866 builder.ins().return_(&[a, b, c]);
1867
1868 builder.seal_all_blocks();
1869 builder.finalize();
1870 }
1871
1872 check(
1873 &func,
1874 "function %sample() -> i8x16, i8x16, f32x4 system_v {
1875 const0 = 0x00000000000000000000000000000000
1876
1877block0:
1878 v5 = f32const 0.0
1879 v6 = splat.f32x4 v5 ; v5 = 0.0
1880 v2 -> v6
1881 v4 = vconst.i8x16 const0
1882 v1 -> v4
1883 v3 = vconst.i8x16 const0
1884 v0 -> v3
1885 return v0, v1, v2 ; v0 = const0, v1 = const0
1886}
1887",
1888 );
1889 }
1890
1891 #[test]
1892 fn test_greatest_divisible_power_of_two() {
1893 assert_eq!(64, greatest_divisible_power_of_two(64));
1894 assert_eq!(16, greatest_divisible_power_of_two(48));
1895 assert_eq!(8, greatest_divisible_power_of_two(24));
1896 assert_eq!(1, greatest_divisible_power_of_two(25));
1897 }
1898
1899 #[test]
1900 fn try_use_var() {
1901 let sig = Signature::new(CallConv::SystemV);
1902
1903 let mut fn_ctx = FunctionBuilderContext::new();
1904 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1905 {
1906 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1907
1908 let block0 = builder.create_block();
1909 builder.append_block_params_for_function_params(block0);
1910 builder.switch_to_block(block0);
1911
1912 assert_eq!(
1913 builder.try_use_var(Variable::from_u32(0)),
1914 Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))
1915 );
1916
1917 let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
1918
1919 assert_eq!(
1920 builder.try_def_var(Variable::from_u32(0), value),
1921 Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(
1922 0
1923 )))
1924 );
1925
1926 builder.declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32);
1927 assert_eq!(
1928 builder.try_declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32),
1929 Err(DeclareVariableError::DeclaredMultipleTimes(
1930 Variable::from_u32(0)
1931 ))
1932 );
1933 }
1934 }
1935
1936 #[test]
1937 fn test_builder_with_iconst_and_negative_constant() {
1938 let sig = Signature::new(CallConv::SystemV);
1939 let mut fn_ctx = FunctionBuilderContext::new();
1940 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1941
1942 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1943
1944 let block0 = builder.create_block();
1945 builder.switch_to_block(block0);
1946 builder.ins().iconst(I32, -1);
1947 builder.ins().return_(&[]);
1948
1949 builder.seal_all_blocks();
1950 builder.finalize();
1951
1952 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
1953 let ctx = cranelift_codegen::Context::for_function(func);
1954 ctx.verify(&flags).expect("should be valid");
1955
1956 check(
1957 &ctx.func,
1958 "function %sample() system_v {
1959block0:
1960 v0 = iconst.i32 -1
1961 return
1962}",
1963 );
1964 }
1965}