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 debug_assert!(
362 self.position.is_none()
363 || self.is_unreachable()
364 || self.is_pristine(self.position.unwrap())
365 || self.is_filled(self.position.unwrap()),
366 "you have to fill your block before switching"
367 );
368 debug_assert!(
370 !self.is_filled(block),
371 "you cannot switch to a block which is already filled"
372 );
373
374 self.position = PackedOption::from(block);
376 }
377
378 pub fn seal_block(&mut self, block: Block) {
384 let side_effects = self.func_ctx.ssa.seal_block(block, self.func);
385 self.handle_ssa_side_effects(side_effects);
386 }
387
388 pub fn seal_all_blocks(&mut self) {
395 let side_effects = self.func_ctx.ssa.seal_all_blocks(self.func);
396 self.handle_ssa_side_effects(side_effects);
397 }
398
399 pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> {
409 if self.func_ctx.types[var] != types::INVALID {
410 return Err(DeclareVariableError::DeclaredMultipleTimes(var));
411 }
412 self.func_ctx.types[var] = ty;
413 Ok(())
414 }
415
416 pub fn declare_var(&mut self, var: Variable, ty: Type) {
422 self.try_declare_var(var, ty)
423 .unwrap_or_else(|_| panic!("the variable {var:?} has been declared multiple times"))
424 }
425
426 pub fn declare_var_needs_stack_map(&mut self, var: Variable) {
441 log::trace!("declare_var_needs_stack_map({var:?})");
442 let ty = self.func_ctx.types[var];
443 assert!(ty != types::INVALID);
444 assert!(ty.bytes() <= 16);
445 self.func_ctx.stack_map_vars.insert(var);
446 }
447
448 pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {
451 self.ensure_inserted_block();
456
457 let (val, side_effects) = {
458 let ty = *self
459 .func_ctx
460 .types
461 .get(var)
462 .ok_or(UseVariableError::UsedBeforeDeclared(var))?;
463 debug_assert_ne!(
464 ty,
465 types::INVALID,
466 "variable {var:?} is used but its type has not been declared"
467 );
468 self.func_ctx
469 .ssa
470 .use_var(self.func, var, ty, self.position.unwrap())
471 };
472 self.handle_ssa_side_effects(side_effects);
473
474 if self.func_ctx.stack_map_vars.contains(var) {
477 self.declare_value_needs_stack_map(val);
478 }
479
480 Ok(val)
481 }
482
483 pub fn use_var(&mut self, var: Variable) -> Value {
486 self.try_use_var(var).unwrap_or_else(|_| {
487 panic!("variable {var:?} is used but its type has not been declared")
488 })
489 }
490
491 pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
495 let var_ty = *self
496 .func_ctx
497 .types
498 .get(var)
499 .ok_or(DefVariableError::DefinedBeforeDeclared(var))?;
500 if var_ty != self.func.dfg.value_type(val) {
501 return Err(DefVariableError::TypeMismatch(var, val));
502 }
503
504 if self.func_ctx.stack_map_vars.contains(var) {
506 self.declare_value_needs_stack_map(val);
507 }
508
509 self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
510 Ok(())
511 }
512
513 pub fn def_var(&mut self, var: Variable, val: Value) {
516 self.try_def_var(var, val)
517 .unwrap_or_else(|error| match error {
518 DefVariableError::TypeMismatch(var, val) => {
519 panic!("declared type of variable {var:?} doesn't match type of value {val}");
520 }
521 DefVariableError::DefinedBeforeDeclared(var) => {
522 panic!("variable {var:?} is used but its type has not been declared");
523 }
524 })
525 }
526
527 pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
532 if let Some(values_labels) = self.func.stencil.dfg.values_labels.as_mut() {
533 use alloc::collections::btree_map::Entry;
534
535 let start = ValueLabelStart {
536 from: RelSourceLoc::from_base_offset(self.func.params.base_srcloc(), self.srcloc),
537 label,
538 };
539
540 match values_labels.entry(val) {
541 Entry::Occupied(mut e) => match e.get_mut() {
542 ValueLabelAssignments::Starts(starts) => starts.push(start),
543 _ => panic!("Unexpected ValueLabelAssignments at this stage"),
544 },
545 Entry::Vacant(e) => {
546 e.insert(ValueLabelAssignments::Starts(vec![start]));
547 }
548 }
549 }
550 }
551
552 pub fn declare_value_needs_stack_map(&mut self, val: Value) {
564 log::trace!("declare_value_needs_stack_map({val:?})");
565
566 let size = self.func.dfg.value_type(val).bytes();
568 assert!(size <= 16);
569 assert!(size.is_power_of_two());
570
571 self.func_ctx.stack_map_values.insert(val);
572 }
573
574 pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
576 self.func.create_jump_table(data)
577 }
578
579 pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
582 self.func.create_sized_stack_slot(data)
583 }
584
585 pub fn create_dynamic_stack_slot(&mut self, data: DynamicStackSlotData) -> DynamicStackSlot {
590 self.func.create_dynamic_stack_slot(data)
591 }
592
593 pub fn import_signature(&mut self, signature: Signature) -> SigRef {
595 self.func.import_signature(signature)
596 }
597
598 pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef {
600 self.func.import_function(data)
601 }
602
603 pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue {
605 self.func.create_global_value(data)
606 }
607
608 pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> {
611 let block = self
612 .position
613 .expect("Please call switch_to_block before inserting instructions");
614 FuncInstBuilder::new(self, block)
615 }
616
617 pub fn ensure_inserted_block(&mut self) {
619 let block = self.position.unwrap();
620 if self.is_pristine(block) {
621 if !self.func.layout.is_block_inserted(block) {
622 self.func.layout.append_block(block);
623 }
624 self.func_ctx.status[block] = BlockStatus::Partial;
625 } else {
626 debug_assert!(
627 !self.is_filled(block),
628 "you cannot add an instruction to a block already filled"
629 );
630 }
631 }
632
633 pub fn cursor(&mut self) -> FuncCursor {
638 self.ensure_inserted_block();
639 FuncCursor::new(self.func)
640 .with_srcloc(self.srcloc)
641 .at_bottom(self.position.unwrap())
642 }
643
644 pub fn append_block_params_for_function_params(&mut self, block: Block) {
648 debug_assert!(
649 !self.func_ctx.ssa.has_any_predecessors(block),
650 "block parameters for function parameters should only be added to the entry block"
651 );
652
653 debug_assert!(
656 self.is_pristine(block),
657 "You can't add block parameters after adding any instruction"
658 );
659
660 for argtyp in &self.func.stencil.signature.params {
661 self.func
662 .stencil
663 .dfg
664 .append_block_param(block, argtyp.value_type);
665 }
666 }
667
668 pub fn append_block_params_for_function_returns(&mut self, block: Block) {
672 debug_assert!(
675 self.is_pristine(block),
676 "You can't add block parameters after adding any instruction"
677 );
678
679 for argtyp in &self.func.stencil.signature.returns {
680 self.func
681 .stencil
682 .dfg
683 .append_block_param(block, argtyp.value_type);
684 }
685 }
686
687 pub fn finalize(mut self) {
692 #[cfg(debug_assertions)]
694 {
695 for block in self.func_ctx.status.keys() {
696 if !self.is_pristine(block) {
697 assert!(
698 self.func_ctx.ssa.is_sealed(block),
699 "FunctionBuilder finalized, but block {block} is not sealed",
700 );
701 assert!(
702 self.is_filled(block),
703 "FunctionBuilder finalized, but block {block} is not filled",
704 );
705 }
706 }
707 }
708
709 #[cfg(debug_assertions)]
711 {
712 for block in self.func_ctx.status.keys() {
714 if let Err((inst, msg)) = self.func.is_block_basic(block) {
715 let inst_str = self.func.dfg.display_inst(inst);
716 panic!("{block} failed basic block invariants on {inst_str}: {msg}");
717 }
718 }
719 }
720
721 if !self.func_ctx.stack_map_values.is_empty() {
722 self.func_ctx
723 .safepoints
724 .run(&mut self.func, &self.func_ctx.stack_map_values);
725 }
726
727 self.func_ctx.clear();
730 }
731}
732
733impl<'a> FunctionBuilder<'a> {
739 pub fn block_params(&self, block: Block) -> &[Value] {
742 self.func.dfg.block_params(block)
743 }
744
745 pub fn signature(&self, sigref: SigRef) -> Option<&Signature> {
748 self.func.dfg.signatures.get(sigref)
749 }
750
751 pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
757 debug_assert!(
758 self.is_pristine(block),
759 "You can't add block parameters after adding any instruction"
760 );
761 self.func.dfg.append_block_param(block, ty)
762 }
763
764 pub fn inst_results(&self, inst: Inst) -> &[Value] {
766 self.func.dfg.inst_results(inst)
767 }
768
769 pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {
774 let dfg = &mut self.func.dfg;
775 for block in dfg.insts[inst].branch_destination_mut(&mut dfg.jump_tables) {
776 if block.block(&dfg.value_lists) == old_block {
777 self.func_ctx.ssa.remove_block_predecessor(old_block, inst);
778 block.set_block(new_block, &mut dfg.value_lists);
779 self.func_ctx.ssa.declare_block_predecessor(new_block, inst);
780 }
781 }
782 }
783
784 pub fn is_unreachable(&self) -> bool {
788 let is_entry = match self.func.layout.entry_block() {
789 None => false,
790 Some(entry) => self.position.unwrap() == entry,
791 };
792 !is_entry
793 && self.func_ctx.ssa.is_sealed(self.position.unwrap())
794 && !self
795 .func_ctx
796 .ssa
797 .has_any_predecessors(self.position.unwrap())
798 }
799
800 fn is_pristine(&self, block: Block) -> bool {
803 self.func_ctx.status[block] == BlockStatus::Empty
804 }
805
806 fn is_filled(&self, block: Block) -> bool {
809 self.func_ctx.status[block] == BlockStatus::Filled
810 }
811}
812
813impl<'a> FunctionBuilder<'a> {
815 pub fn call_memcpy(
822 &mut self,
823 config: TargetFrontendConfig,
824 dest: Value,
825 src: Value,
826 size: Value,
827 ) {
828 let pointer_type = config.pointer_type();
829 let signature = {
830 let mut s = Signature::new(config.default_call_conv);
831 s.params.push(AbiParam::new(pointer_type));
832 s.params.push(AbiParam::new(pointer_type));
833 s.params.push(AbiParam::new(pointer_type));
834 s.returns.push(AbiParam::new(pointer_type));
835 self.import_signature(s)
836 };
837
838 let libc_memcpy = self.import_function(ExtFuncData {
839 name: ExternalName::LibCall(LibCall::Memcpy),
840 signature,
841 colocated: false,
842 });
843
844 self.ins().call(libc_memcpy, &[dest, src, size]);
845 }
846
847 pub fn emit_small_memory_copy(
856 &mut self,
857 config: TargetFrontendConfig,
858 dest: Value,
859 src: Value,
860 size: u64,
861 dest_align: u8,
862 src_align: u8,
863 non_overlapping: bool,
864 mut flags: MemFlags,
865 ) {
866 const THRESHOLD: u64 = 4;
868
869 if size == 0 {
870 return;
871 }
872
873 let access_size = greatest_divisible_power_of_two(size);
874 assert!(
875 access_size.is_power_of_two(),
876 "`size` is not a power of two"
877 );
878 assert!(
879 access_size >= u64::from(::core::cmp::min(src_align, dest_align)),
880 "`size` is smaller than `dest` and `src`'s alignment value."
881 );
882
883 let (access_size, int_type) = if access_size <= 8 {
884 (access_size, Type::int((access_size * 8) as u16).unwrap())
885 } else {
886 (8, types::I64)
887 };
888
889 let load_and_store_amount = size / access_size;
890
891 if load_and_store_amount > THRESHOLD {
892 let size_value = self.ins().iconst(config.pointer_type(), size as i64);
893 if non_overlapping {
894 self.call_memcpy(config, dest, src, size_value);
895 } else {
896 self.call_memmove(config, dest, src, size_value);
897 }
898 return;
899 }
900
901 if u64::from(src_align) >= access_size && u64::from(dest_align) >= access_size {
902 flags.set_aligned();
903 }
904
905 let registers: smallvec::SmallVec<[_; THRESHOLD as usize]> = (0..load_and_store_amount)
908 .map(|i| {
909 let offset = (access_size * i) as i32;
910 (self.ins().load(int_type, flags, src, offset), offset)
911 })
912 .collect();
913
914 for (value, offset) in registers {
915 self.ins().store(flags, value, dest, offset);
916 }
917 }
918
919 pub fn call_memset(
923 &mut self,
924 config: TargetFrontendConfig,
925 buffer: Value,
926 ch: Value,
927 size: Value,
928 ) {
929 let pointer_type = config.pointer_type();
930 let signature = {
931 let mut s = Signature::new(config.default_call_conv);
932 s.params.push(AbiParam::new(pointer_type));
933 s.params.push(AbiParam::new(types::I32));
934 s.params.push(AbiParam::new(pointer_type));
935 s.returns.push(AbiParam::new(pointer_type));
936 self.import_signature(s)
937 };
938
939 let libc_memset = self.import_function(ExtFuncData {
940 name: ExternalName::LibCall(LibCall::Memset),
941 signature,
942 colocated: false,
943 });
944
945 let ch = self.ins().uextend(types::I32, ch);
946 self.ins().call(libc_memset, &[buffer, ch, size]);
947 }
948
949 pub fn emit_small_memset(
953 &mut self,
954 config: TargetFrontendConfig,
955 buffer: Value,
956 ch: u8,
957 size: u64,
958 buffer_align: u8,
959 mut flags: MemFlags,
960 ) {
961 const THRESHOLD: u64 = 4;
963
964 if size == 0 {
965 return;
966 }
967
968 let access_size = greatest_divisible_power_of_two(size);
969 assert!(
970 access_size.is_power_of_two(),
971 "`size` is not a power of two"
972 );
973 assert!(
974 access_size >= u64::from(buffer_align),
975 "`size` is smaller than `dest` and `src`'s alignment value."
976 );
977
978 let (access_size, int_type) = if access_size <= 8 {
979 (access_size, Type::int((access_size * 8) as u16).unwrap())
980 } else {
981 (8, types::I64)
982 };
983
984 let load_and_store_amount = size / access_size;
985
986 if load_and_store_amount > THRESHOLD {
987 let ch = self.ins().iconst(types::I8, i64::from(ch));
988 let size = self.ins().iconst(config.pointer_type(), size as i64);
989 self.call_memset(config, buffer, ch, size);
990 } else {
991 if u64::from(buffer_align) >= access_size {
992 flags.set_aligned();
993 }
994
995 let ch = u64::from(ch);
996 let raw_value = if int_type == types::I64 {
997 ch * 0x0101010101010101_u64
998 } else if int_type == types::I32 {
999 ch * 0x01010101_u64
1000 } else if int_type == types::I16 {
1001 (ch << 8) | ch
1002 } else {
1003 assert_eq!(int_type, types::I8);
1004 ch
1005 };
1006
1007 let value = self.ins().iconst(int_type, raw_value as i64);
1008 for i in 0..load_and_store_amount {
1009 let offset = (access_size * i) as i32;
1010 self.ins().store(flags, value, buffer, offset);
1011 }
1012 }
1013 }
1014
1015 pub fn call_memmove(
1020 &mut self,
1021 config: TargetFrontendConfig,
1022 dest: Value,
1023 source: Value,
1024 size: Value,
1025 ) {
1026 let pointer_type = config.pointer_type();
1027 let signature = {
1028 let mut s = Signature::new(config.default_call_conv);
1029 s.params.push(AbiParam::new(pointer_type));
1030 s.params.push(AbiParam::new(pointer_type));
1031 s.params.push(AbiParam::new(pointer_type));
1032 s.returns.push(AbiParam::new(pointer_type));
1033 self.import_signature(s)
1034 };
1035
1036 let libc_memmove = self.import_function(ExtFuncData {
1037 name: ExternalName::LibCall(LibCall::Memmove),
1038 signature,
1039 colocated: false,
1040 });
1041
1042 self.ins().call(libc_memmove, &[dest, source, size]);
1043 }
1044
1045 pub fn call_memcmp(
1054 &mut self,
1055 config: TargetFrontendConfig,
1056 left: Value,
1057 right: Value,
1058 size: Value,
1059 ) -> Value {
1060 let pointer_type = config.pointer_type();
1061 let signature = {
1062 let mut s = Signature::new(config.default_call_conv);
1063 s.params.reserve(3);
1064 s.params.push(AbiParam::new(pointer_type));
1065 s.params.push(AbiParam::new(pointer_type));
1066 s.params.push(AbiParam::new(pointer_type));
1067 s.returns.push(AbiParam::new(types::I32));
1068 self.import_signature(s)
1069 };
1070
1071 let libc_memcmp = self.import_function(ExtFuncData {
1072 name: ExternalName::LibCall(LibCall::Memcmp),
1073 signature,
1074 colocated: false,
1075 });
1076
1077 let call = self.ins().call(libc_memcmp, &[left, right, size]);
1078 self.func.dfg.first_result(call)
1079 }
1080
1081 pub fn emit_small_memory_compare(
1094 &mut self,
1095 config: TargetFrontendConfig,
1096 int_cc: IntCC,
1097 left: Value,
1098 right: Value,
1099 size: u64,
1100 left_align: std::num::NonZeroU8,
1101 right_align: std::num::NonZeroU8,
1102 flags: MemFlags,
1103 ) -> Value {
1104 use IntCC::*;
1105 let (zero_cc, empty_imm) = match int_cc {
1106 Equal => (Equal, 1),
1108 NotEqual => (NotEqual, 0),
1109
1110 UnsignedLessThan => (SignedLessThan, 0),
1111 UnsignedGreaterThanOrEqual => (SignedGreaterThanOrEqual, 1),
1112 UnsignedGreaterThan => (SignedGreaterThan, 0),
1113 UnsignedLessThanOrEqual => (SignedLessThanOrEqual, 1),
1114
1115 SignedLessThan
1116 | SignedGreaterThanOrEqual
1117 | SignedGreaterThan
1118 | SignedLessThanOrEqual => {
1119 panic!("Signed comparison {int_cc} not supported by memcmp")
1120 }
1121 };
1122
1123 if size == 0 {
1124 return self.ins().iconst(types::I8, empty_imm);
1125 }
1126
1127 if let Some(small_type) = size.try_into().ok().and_then(Type::int_with_byte_size) {
1129 if let Equal | NotEqual = zero_cc {
1130 let mut left_flags = flags;
1131 if size == left_align.get() as u64 {
1132 left_flags.set_aligned();
1133 }
1134 let mut right_flags = flags;
1135 if size == right_align.get() as u64 {
1136 right_flags.set_aligned();
1137 }
1138 let left_val = self.ins().load(small_type, left_flags, left, 0);
1139 let right_val = self.ins().load(small_type, right_flags, right, 0);
1140 return self.ins().icmp(int_cc, left_val, right_val);
1141 } else if small_type == types::I8 {
1142 let mut aligned_flags = flags;
1147 aligned_flags.set_aligned();
1148 let left_val = self.ins().load(small_type, aligned_flags, left, 0);
1149 let right_val = self.ins().load(small_type, aligned_flags, right, 0);
1150 return self.ins().icmp(int_cc, left_val, right_val);
1151 }
1152 }
1153
1154 let pointer_type = config.pointer_type();
1155 let size = self.ins().iconst(pointer_type, size as i64);
1156 let cmp = self.call_memcmp(config, left, right, size);
1157 self.ins().icmp_imm(zero_cc, cmp, 0)
1158 }
1159}
1160
1161fn greatest_divisible_power_of_two(size: u64) -> u64 {
1162 (size as i64 & -(size as i64)) as u64
1163}
1164
1165impl<'a> FunctionBuilder<'a> {
1167 fn fill_current_block(&mut self) {
1169 self.func_ctx.status[self.position.unwrap()] = BlockStatus::Filled;
1170 }
1171
1172 fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) {
1173 self.func_ctx
1174 .ssa
1175 .declare_block_predecessor(dest_block, jump_inst);
1176 }
1177
1178 fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
1179 for modified_block in side_effects.instructions_added_to_blocks {
1180 if self.is_pristine(modified_block) {
1181 self.func_ctx.status[modified_block] = BlockStatus::Partial;
1182 }
1183 }
1184 }
1185}
1186
1187#[cfg(test)]
1188mod tests {
1189 use super::greatest_divisible_power_of_two;
1190 use crate::frontend::{
1191 DeclareVariableError, DefVariableError, FunctionBuilder, FunctionBuilderContext,
1192 UseVariableError,
1193 };
1194 use crate::Variable;
1195 use alloc::string::ToString;
1196 use cranelift_codegen::entity::EntityRef;
1197 use cranelift_codegen::ir::condcodes::IntCC;
1198 use cranelift_codegen::ir::{types::*, UserFuncName};
1199 use cranelift_codegen::ir::{AbiParam, Function, InstBuilder, MemFlags, Signature, Value};
1200 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
1201 use cranelift_codegen::settings;
1202 use cranelift_codegen::verifier::verify_function;
1203 use target_lexicon::PointerWidth;
1204
1205 fn sample_function(lazy_seal: bool) {
1206 let mut sig = Signature::new(CallConv::SystemV);
1207 sig.returns.push(AbiParam::new(I32));
1208 sig.params.push(AbiParam::new(I32));
1209
1210 let mut fn_ctx = FunctionBuilderContext::new();
1211 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1212 {
1213 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1214
1215 let block0 = builder.create_block();
1216 let block1 = builder.create_block();
1217 let block2 = builder.create_block();
1218 let block3 = builder.create_block();
1219 let x = Variable::new(0);
1220 let y = Variable::new(1);
1221 let z = Variable::new(2);
1222 builder.declare_var(x, I32);
1223 builder.declare_var(y, I32);
1224 builder.declare_var(z, I32);
1225 builder.append_block_params_for_function_params(block0);
1226
1227 builder.switch_to_block(block0);
1228 if !lazy_seal {
1229 builder.seal_block(block0);
1230 }
1231 {
1232 let tmp = builder.block_params(block0)[0]; builder.def_var(x, tmp);
1234 }
1235 {
1236 let tmp = builder.ins().iconst(I32, 2);
1237 builder.def_var(y, tmp);
1238 }
1239 {
1240 let arg1 = builder.use_var(x);
1241 let arg2 = builder.use_var(y);
1242 let tmp = builder.ins().iadd(arg1, arg2);
1243 builder.def_var(z, tmp);
1244 }
1245 builder.ins().jump(block1, &[]);
1246
1247 builder.switch_to_block(block1);
1248 {
1249 let arg1 = builder.use_var(y);
1250 let arg2 = builder.use_var(z);
1251 let tmp = builder.ins().iadd(arg1, arg2);
1252 builder.def_var(z, tmp);
1253 }
1254 {
1255 let arg = builder.use_var(y);
1256 builder.ins().brif(arg, block3, &[], block2, &[]);
1257 }
1258
1259 builder.switch_to_block(block2);
1260 if !lazy_seal {
1261 builder.seal_block(block2);
1262 }
1263 {
1264 let arg1 = builder.use_var(z);
1265 let arg2 = builder.use_var(x);
1266 let tmp = builder.ins().isub(arg1, arg2);
1267 builder.def_var(z, tmp);
1268 }
1269 {
1270 let arg = builder.use_var(y);
1271 builder.ins().return_(&[arg]);
1272 }
1273
1274 builder.switch_to_block(block3);
1275 if !lazy_seal {
1276 builder.seal_block(block3);
1277 }
1278
1279 {
1280 let arg1 = builder.use_var(y);
1281 let arg2 = builder.use_var(x);
1282 let tmp = builder.ins().isub(arg1, arg2);
1283 builder.def_var(y, tmp);
1284 }
1285 builder.ins().jump(block1, &[]);
1286 if !lazy_seal {
1287 builder.seal_block(block1);
1288 }
1289
1290 if lazy_seal {
1291 builder.seal_all_blocks();
1292 }
1293
1294 builder.finalize();
1295 }
1296
1297 let flags = settings::Flags::new(settings::builder());
1298 if let Err(errors) = verify_function(&func, &flags) {
1300 panic!("{}\n{}", func.display(), errors)
1301 }
1302 }
1303
1304 #[test]
1305 fn sample() {
1306 sample_function(false)
1307 }
1308
1309 #[test]
1310 fn sample_with_lazy_seal() {
1311 sample_function(true)
1312 }
1313
1314 #[track_caller]
1315 fn check(func: &Function, expected_ir: &str) {
1316 let expected_ir = expected_ir.trim();
1317 let actual_ir = func.display().to_string();
1318 let actual_ir = actual_ir.trim();
1319 assert!(
1320 expected_ir == actual_ir,
1321 "Expected:\n{expected_ir}\nGot:\n{actual_ir}"
1322 );
1323 }
1324
1325 fn systemv_frontend_config() -> TargetFrontendConfig {
1327 TargetFrontendConfig {
1328 default_call_conv: CallConv::SystemV,
1329 pointer_width: PointerWidth::U64,
1330 page_size_align_log2: 12,
1331 }
1332 }
1333
1334 #[test]
1335 fn memcpy() {
1336 let frontend_config = systemv_frontend_config();
1337 let mut sig = Signature::new(frontend_config.default_call_conv);
1338 sig.returns.push(AbiParam::new(I32));
1339
1340 let mut fn_ctx = FunctionBuilderContext::new();
1341 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1342 {
1343 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1344
1345 let block0 = builder.create_block();
1346 let x = Variable::new(0);
1347 let y = Variable::new(1);
1348 let z = Variable::new(2);
1349 builder.declare_var(x, frontend_config.pointer_type());
1350 builder.declare_var(y, frontend_config.pointer_type());
1351 builder.declare_var(z, I32);
1352 builder.append_block_params_for_function_params(block0);
1353 builder.switch_to_block(block0);
1354
1355 let src = builder.use_var(x);
1356 let dest = builder.use_var(y);
1357 let size = builder.use_var(y);
1358 builder.call_memcpy(frontend_config, dest, src, size);
1359 builder.ins().return_(&[size]);
1360
1361 builder.seal_all_blocks();
1362 builder.finalize();
1363 }
1364
1365 check(
1366 &func,
1367 "function %sample() -> i32 system_v {
1368 sig0 = (i64, i64, i64) -> i64 system_v
1369 fn0 = %Memcpy sig0
1370
1371block0:
1372 v4 = iconst.i64 0
1373 v1 -> v4
1374 v3 = iconst.i64 0
1375 v0 -> v3
1376 v2 = call fn0(v1, v0, v1) ; v1 = 0, v0 = 0, v1 = 0
1377 return v1 ; v1 = 0
1378}
1379",
1380 );
1381 }
1382
1383 #[test]
1384 fn small_memcpy() {
1385 let frontend_config = systemv_frontend_config();
1386 let mut sig = Signature::new(frontend_config.default_call_conv);
1387 sig.returns.push(AbiParam::new(I32));
1388
1389 let mut fn_ctx = FunctionBuilderContext::new();
1390 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1391 {
1392 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1393
1394 let block0 = builder.create_block();
1395 let x = Variable::new(0);
1396 let y = Variable::new(16);
1397 builder.declare_var(x, frontend_config.pointer_type());
1398 builder.declare_var(y, frontend_config.pointer_type());
1399 builder.append_block_params_for_function_params(block0);
1400 builder.switch_to_block(block0);
1401
1402 let src = builder.use_var(x);
1403 let dest = builder.use_var(y);
1404 let size = 8;
1405 builder.emit_small_memory_copy(
1406 frontend_config,
1407 dest,
1408 src,
1409 size,
1410 8,
1411 8,
1412 true,
1413 MemFlags::new(),
1414 );
1415 builder.ins().return_(&[dest]);
1416
1417 builder.seal_all_blocks();
1418 builder.finalize();
1419 }
1420
1421 check(
1422 &func,
1423 "function %sample() -> i32 system_v {
1424block0:
1425 v4 = iconst.i64 0
1426 v1 -> v4
1427 v3 = iconst.i64 0
1428 v0 -> v3
1429 v2 = load.i64 aligned v0 ; v0 = 0
1430 store aligned v2, v1 ; v1 = 0
1431 return v1 ; v1 = 0
1432}
1433",
1434 );
1435 }
1436
1437 #[test]
1438 fn not_so_small_memcpy() {
1439 let frontend_config = systemv_frontend_config();
1440 let mut sig = Signature::new(frontend_config.default_call_conv);
1441 sig.returns.push(AbiParam::new(I32));
1442
1443 let mut fn_ctx = FunctionBuilderContext::new();
1444 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1445 {
1446 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1447
1448 let block0 = builder.create_block();
1449 let x = Variable::new(0);
1450 let y = Variable::new(16);
1451 builder.declare_var(x, frontend_config.pointer_type());
1452 builder.declare_var(y, frontend_config.pointer_type());
1453 builder.append_block_params_for_function_params(block0);
1454 builder.switch_to_block(block0);
1455
1456 let src = builder.use_var(x);
1457 let dest = builder.use_var(y);
1458 let size = 8192;
1459 builder.emit_small_memory_copy(
1460 frontend_config,
1461 dest,
1462 src,
1463 size,
1464 8,
1465 8,
1466 true,
1467 MemFlags::new(),
1468 );
1469 builder.ins().return_(&[dest]);
1470
1471 builder.seal_all_blocks();
1472 builder.finalize();
1473 }
1474
1475 check(
1476 &func,
1477 "function %sample() -> i32 system_v {
1478 sig0 = (i64, i64, i64) -> i64 system_v
1479 fn0 = %Memcpy sig0
1480
1481block0:
1482 v5 = iconst.i64 0
1483 v1 -> v5
1484 v4 = iconst.i64 0
1485 v0 -> v4
1486 v2 = iconst.i64 8192
1487 v3 = call fn0(v1, v0, v2) ; v1 = 0, v0 = 0, v2 = 8192
1488 return v1 ; v1 = 0
1489}
1490",
1491 );
1492 }
1493
1494 #[test]
1495 fn small_memset() {
1496 let frontend_config = systemv_frontend_config();
1497 let mut sig = Signature::new(frontend_config.default_call_conv);
1498 sig.returns.push(AbiParam::new(I32));
1499
1500 let mut fn_ctx = FunctionBuilderContext::new();
1501 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1502 {
1503 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1504
1505 let block0 = builder.create_block();
1506 let y = Variable::new(16);
1507 builder.declare_var(y, frontend_config.pointer_type());
1508 builder.append_block_params_for_function_params(block0);
1509 builder.switch_to_block(block0);
1510
1511 let dest = builder.use_var(y);
1512 let size = 8;
1513 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1514 builder.ins().return_(&[dest]);
1515
1516 builder.seal_all_blocks();
1517 builder.finalize();
1518 }
1519
1520 check(
1521 &func,
1522 "function %sample() -> i32 system_v {
1523block0:
1524 v2 = iconst.i64 0
1525 v0 -> v2
1526 v1 = iconst.i64 0x0101_0101_0101_0101
1527 store aligned v1, v0 ; v1 = 0x0101_0101_0101_0101, v0 = 0
1528 return v0 ; v0 = 0
1529}
1530",
1531 );
1532 }
1533
1534 #[test]
1535 fn not_so_small_memset() {
1536 let frontend_config = systemv_frontend_config();
1537 let mut sig = Signature::new(frontend_config.default_call_conv);
1538 sig.returns.push(AbiParam::new(I32));
1539
1540 let mut fn_ctx = FunctionBuilderContext::new();
1541 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1542 {
1543 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1544
1545 let block0 = builder.create_block();
1546 let y = Variable::new(16);
1547 builder.declare_var(y, frontend_config.pointer_type());
1548 builder.append_block_params_for_function_params(block0);
1549 builder.switch_to_block(block0);
1550
1551 let dest = builder.use_var(y);
1552 let size = 8192;
1553 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1554 builder.ins().return_(&[dest]);
1555
1556 builder.seal_all_blocks();
1557 builder.finalize();
1558 }
1559
1560 check(
1561 &func,
1562 "function %sample() -> i32 system_v {
1563 sig0 = (i64, i32, i64) -> i64 system_v
1564 fn0 = %Memset sig0
1565
1566block0:
1567 v5 = iconst.i64 0
1568 v0 -> v5
1569 v1 = iconst.i8 1
1570 v2 = iconst.i64 8192
1571 v3 = uextend.i32 v1 ; v1 = 1
1572 v4 = call fn0(v0, v3, v2) ; v0 = 0, v2 = 8192
1573 return v0 ; v0 = 0
1574}
1575",
1576 );
1577 }
1578
1579 #[test]
1580 fn memcmp() {
1581 use core::str::FromStr;
1582 use cranelift_codegen::isa;
1583
1584 let shared_builder = settings::builder();
1585 let shared_flags = settings::Flags::new(shared_builder);
1586
1587 let triple =
1588 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1589
1590 let target = isa::lookup(triple)
1591 .ok()
1592 .map(|b| b.finish(shared_flags))
1593 .expect("This test requires x86_64 support.")
1594 .expect("Should be able to create backend with default flags");
1595
1596 let mut sig = Signature::new(target.default_call_conv());
1597 sig.returns.push(AbiParam::new(I32));
1598
1599 let mut fn_ctx = FunctionBuilderContext::new();
1600 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1601 {
1602 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1603
1604 let block0 = builder.create_block();
1605 let x = Variable::new(0);
1606 let y = Variable::new(1);
1607 let z = Variable::new(2);
1608 builder.declare_var(x, target.pointer_type());
1609 builder.declare_var(y, target.pointer_type());
1610 builder.declare_var(z, target.pointer_type());
1611 builder.append_block_params_for_function_params(block0);
1612 builder.switch_to_block(block0);
1613
1614 let left = builder.use_var(x);
1615 let right = builder.use_var(y);
1616 let size = builder.use_var(z);
1617 let cmp = builder.call_memcmp(target.frontend_config(), left, right, size);
1618 builder.ins().return_(&[cmp]);
1619
1620 builder.seal_all_blocks();
1621 builder.finalize();
1622 }
1623
1624 check(
1625 &func,
1626 "function %sample() -> i32 system_v {
1627 sig0 = (i64, i64, i64) -> i32 system_v
1628 fn0 = %Memcmp sig0
1629
1630block0:
1631 v6 = iconst.i64 0
1632 v2 -> v6
1633 v5 = iconst.i64 0
1634 v1 -> v5
1635 v4 = iconst.i64 0
1636 v0 -> v4
1637 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 0
1638 return v3
1639}
1640",
1641 );
1642 }
1643
1644 #[test]
1645 fn small_memcmp_zero_size() {
1646 let align_eight = std::num::NonZeroU8::new(8).unwrap();
1647 small_memcmp_helper(
1648 "
1649block0:
1650 v4 = iconst.i64 0
1651 v1 -> v4
1652 v3 = iconst.i64 0
1653 v0 -> v3
1654 v2 = iconst.i8 1
1655 return v2 ; v2 = 1",
1656 |builder, target, x, y| {
1657 builder.emit_small_memory_compare(
1658 target.frontend_config(),
1659 IntCC::UnsignedGreaterThanOrEqual,
1660 x,
1661 y,
1662 0,
1663 align_eight,
1664 align_eight,
1665 MemFlags::new(),
1666 )
1667 },
1668 );
1669 }
1670
1671 #[test]
1672 fn small_memcmp_byte_ugt() {
1673 let align_one = std::num::NonZeroU8::new(1).unwrap();
1674 small_memcmp_helper(
1675 "
1676block0:
1677 v6 = iconst.i64 0
1678 v1 -> v6
1679 v5 = iconst.i64 0
1680 v0 -> v5
1681 v2 = load.i8 aligned v0 ; v0 = 0
1682 v3 = load.i8 aligned v1 ; v1 = 0
1683 v4 = icmp ugt v2, v3
1684 return v4",
1685 |builder, target, x, y| {
1686 builder.emit_small_memory_compare(
1687 target.frontend_config(),
1688 IntCC::UnsignedGreaterThan,
1689 x,
1690 y,
1691 1,
1692 align_one,
1693 align_one,
1694 MemFlags::new(),
1695 )
1696 },
1697 );
1698 }
1699
1700 #[test]
1701 fn small_memcmp_aligned_eq() {
1702 let align_four = std::num::NonZeroU8::new(4).unwrap();
1703 small_memcmp_helper(
1704 "
1705block0:
1706 v6 = iconst.i64 0
1707 v1 -> v6
1708 v5 = iconst.i64 0
1709 v0 -> v5
1710 v2 = load.i32 aligned v0 ; v0 = 0
1711 v3 = load.i32 aligned v1 ; v1 = 0
1712 v4 = icmp eq v2, v3
1713 return v4",
1714 |builder, target, x, y| {
1715 builder.emit_small_memory_compare(
1716 target.frontend_config(),
1717 IntCC::Equal,
1718 x,
1719 y,
1720 4,
1721 align_four,
1722 align_four,
1723 MemFlags::new(),
1724 )
1725 },
1726 );
1727 }
1728
1729 #[test]
1730 fn small_memcmp_ipv6_ne() {
1731 let align_two = std::num::NonZeroU8::new(2).unwrap();
1732 small_memcmp_helper(
1733 "
1734block0:
1735 v6 = iconst.i64 0
1736 v1 -> v6
1737 v5 = iconst.i64 0
1738 v0 -> v5
1739 v2 = load.i128 v0 ; v0 = 0
1740 v3 = load.i128 v1 ; v1 = 0
1741 v4 = icmp ne v2, v3
1742 return v4",
1743 |builder, target, x, y| {
1744 builder.emit_small_memory_compare(
1745 target.frontend_config(),
1746 IntCC::NotEqual,
1747 x,
1748 y,
1749 16,
1750 align_two,
1751 align_two,
1752 MemFlags::new(),
1753 )
1754 },
1755 );
1756 }
1757
1758 #[test]
1759 fn small_memcmp_odd_size_uge() {
1760 let one = std::num::NonZeroU8::new(1).unwrap();
1761 small_memcmp_helper(
1762 "
1763 sig0 = (i64, i64, i64) -> i32 system_v
1764 fn0 = %Memcmp sig0
1765
1766block0:
1767 v6 = iconst.i64 0
1768 v1 -> v6
1769 v5 = iconst.i64 0
1770 v0 -> v5
1771 v2 = iconst.i64 3
1772 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 3
1773 v4 = icmp_imm sge v3, 0
1774 return v4",
1775 |builder, target, x, y| {
1776 builder.emit_small_memory_compare(
1777 target.frontend_config(),
1778 IntCC::UnsignedGreaterThanOrEqual,
1779 x,
1780 y,
1781 3,
1782 one,
1783 one,
1784 MemFlags::new(),
1785 )
1786 },
1787 );
1788 }
1789
1790 fn small_memcmp_helper(
1791 expected: &str,
1792 f: impl FnOnce(&mut FunctionBuilder, &dyn TargetIsa, Value, Value) -> Value,
1793 ) {
1794 use core::str::FromStr;
1795 use cranelift_codegen::isa;
1796
1797 let shared_builder = settings::builder();
1798 let shared_flags = settings::Flags::new(shared_builder);
1799
1800 let triple =
1801 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1802
1803 let target = isa::lookup(triple)
1804 .ok()
1805 .map(|b| b.finish(shared_flags))
1806 .expect("This test requires x86_64 support.")
1807 .expect("Should be able to create backend with default flags");
1808
1809 let mut sig = Signature::new(target.default_call_conv());
1810 sig.returns.push(AbiParam::new(I8));
1811
1812 let mut fn_ctx = FunctionBuilderContext::new();
1813 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1814 {
1815 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1816
1817 let block0 = builder.create_block();
1818 let x = Variable::new(0);
1819 let y = Variable::new(1);
1820 builder.declare_var(x, target.pointer_type());
1821 builder.declare_var(y, target.pointer_type());
1822 builder.append_block_params_for_function_params(block0);
1823 builder.switch_to_block(block0);
1824
1825 let left = builder.use_var(x);
1826 let right = builder.use_var(y);
1827 let ret = f(&mut builder, &*target, left, right);
1828 builder.ins().return_(&[ret]);
1829
1830 builder.seal_all_blocks();
1831 builder.finalize();
1832 }
1833
1834 check(
1835 &func,
1836 &format!("function %sample() -> i8 system_v {{{expected}\n}}\n"),
1837 );
1838 }
1839
1840 #[test]
1841 fn undef_vector_vars() {
1842 let mut sig = Signature::new(CallConv::SystemV);
1843 sig.returns.push(AbiParam::new(I8X16));
1844 sig.returns.push(AbiParam::new(I8X16));
1845 sig.returns.push(AbiParam::new(F32X4));
1846
1847 let mut fn_ctx = FunctionBuilderContext::new();
1848 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1849 {
1850 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1851
1852 let block0 = builder.create_block();
1853 let a = Variable::new(0);
1854 let b = Variable::new(1);
1855 let c = Variable::new(2);
1856 builder.declare_var(a, I8X16);
1857 builder.declare_var(b, I8X16);
1858 builder.declare_var(c, F32X4);
1859 builder.switch_to_block(block0);
1860
1861 let a = builder.use_var(a);
1862 let b = builder.use_var(b);
1863 let c = builder.use_var(c);
1864 builder.ins().return_(&[a, b, c]);
1865
1866 builder.seal_all_blocks();
1867 builder.finalize();
1868 }
1869
1870 check(
1871 &func,
1872 "function %sample() -> i8x16, i8x16, f32x4 system_v {
1873 const0 = 0x00000000000000000000000000000000
1874
1875block0:
1876 v5 = f32const 0.0
1877 v6 = splat.f32x4 v5 ; v5 = 0.0
1878 v2 -> v6
1879 v4 = vconst.i8x16 const0
1880 v1 -> v4
1881 v3 = vconst.i8x16 const0
1882 v0 -> v3
1883 return v0, v1, v2 ; v0 = const0, v1 = const0
1884}
1885",
1886 );
1887 }
1888
1889 #[test]
1890 fn test_greatest_divisible_power_of_two() {
1891 assert_eq!(64, greatest_divisible_power_of_two(64));
1892 assert_eq!(16, greatest_divisible_power_of_two(48));
1893 assert_eq!(8, greatest_divisible_power_of_two(24));
1894 assert_eq!(1, greatest_divisible_power_of_two(25));
1895 }
1896
1897 #[test]
1898 fn try_use_var() {
1899 let sig = Signature::new(CallConv::SystemV);
1900
1901 let mut fn_ctx = FunctionBuilderContext::new();
1902 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1903 {
1904 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1905
1906 let block0 = builder.create_block();
1907 builder.append_block_params_for_function_params(block0);
1908 builder.switch_to_block(block0);
1909
1910 assert_eq!(
1911 builder.try_use_var(Variable::from_u32(0)),
1912 Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))
1913 );
1914
1915 let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
1916
1917 assert_eq!(
1918 builder.try_def_var(Variable::from_u32(0), value),
1919 Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(
1920 0
1921 )))
1922 );
1923
1924 builder.declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32);
1925 assert_eq!(
1926 builder.try_declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32),
1927 Err(DeclareVariableError::DeclaredMultipleTimes(
1928 Variable::from_u32(0)
1929 ))
1930 );
1931 }
1932 }
1933
1934 #[test]
1935 fn test_builder_with_iconst_and_negative_constant() {
1936 let sig = Signature::new(CallConv::SystemV);
1937 let mut fn_ctx = FunctionBuilderContext::new();
1938 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1939
1940 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1941
1942 let block0 = builder.create_block();
1943 builder.switch_to_block(block0);
1944 builder.ins().iconst(I32, -1);
1945 builder.ins().return_(&[]);
1946
1947 builder.seal_all_blocks();
1948 builder.finalize();
1949
1950 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
1951 let ctx = cranelift_codegen::Context::for_function(func);
1952 ctx.verify(&flags).expect("should be valid");
1953
1954 check(
1955 &ctx.func,
1956 "function %sample() system_v {
1957block0:
1958 v0 = iconst.i32 -1
1959 return
1960}",
1961 );
1962 }
1963}