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