1use crate::ssa::{SSABuilder, SideEffects};
3use crate::variable::Variable;
4use alloc::{boxed::Box, vec, vec::Vec};
5use core::fmt::{self, Debug};
6use cranelift_codegen::cursor::{Cursor, CursorPosition, FuncCursor};
7use cranelift_codegen::entity::{EntityRef, EntitySet, PrimaryMap, SecondaryMap};
8use cranelift_codegen::ir;
9use cranelift_codegen::ir::condcodes::IntCC;
10use cranelift_codegen::ir::{
11 AbiParam, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, ExtFuncData,
12 ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Inst, InstBuilder,
13 InstBuilderBase, InstructionData, JumpTable, JumpTableData, LibCall, MemFlagsData,
14 RelSourceLoc, SigRef, Signature, StackSlot, StackSlotData, Type, Value, ValueLabel,
15 ValueLabelAssignments, ValueLabelStart, types,
16};
17use cranelift_codegen::isa::TargetFrontendConfig;
18use cranelift_codegen::packed_option::PackedOption;
19use cranelift_codegen::traversals::Dfs;
20use smallvec::SmallVec;
21
22mod safepoints;
23
24#[derive(Default)]
30pub struct FunctionBuilderContext {
31 ssa: SSABuilder,
32 status: SecondaryMap<Block, BlockStatus>,
33 variables: PrimaryMap<Variable, Type>,
34 safepoints: safepoints::SafepointSpiller,
35}
36
37pub struct FunctionBuilder<'a> {
39 pub func: &'a mut Function,
42
43 srcloc: ir::SourceLoc,
45
46 func_ctx: &'a mut FunctionBuilderContext,
47 position: PackedOption<Block>,
48}
49
50#[derive(Clone, Default, Eq, PartialEq)]
51enum BlockStatus {
52 #[default]
54 Empty,
55 Partial,
57 Filled,
59}
60
61impl FunctionBuilderContext {
62 pub fn new() -> Self {
65 Self::default()
66 }
67
68 fn clear(&mut self) {
69 let FunctionBuilderContext {
70 ssa,
71 status,
72 variables,
73 safepoints,
74 } = self;
75 ssa.clear();
76 status.clear();
77 variables.clear();
78 safepoints.clear();
79 safepoints.make_alias_region = None;
80 }
81
82 fn is_empty(&self) -> bool {
83 self.ssa.is_empty() && self.status.is_empty() && self.variables.is_empty()
84 }
85}
86
87pub struct FuncInstBuilder<'short, 'long: 'short> {
90 builder: &'short mut FunctionBuilder<'long>,
91 block: Block,
92}
93
94impl<'short, 'long> FuncInstBuilder<'short, 'long> {
95 fn new(builder: &'short mut FunctionBuilder<'long>, block: Block) -> Self {
96 Self { builder, block }
97 }
98}
99
100impl<'short, 'long> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long> {
101 fn data_flow_graph(&self) -> &DataFlowGraph {
102 &self.builder.func.dfg
103 }
104
105 fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
106 &mut self.builder.func.dfg
107 }
108
109 fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'short mut DataFlowGraph) {
113 self.builder.ensure_inserted_block();
115
116 let inst = self.builder.func.dfg.make_inst(data);
117 self.builder.func.dfg.make_inst_results(inst, ctrl_typevar);
118 self.builder.func.layout.append_inst(inst, self.block);
119 if !self.builder.srcloc.is_default() {
120 self.builder.func.set_srcloc(inst, self.builder.srcloc);
121 }
122
123 match &self.builder.func.dfg.insts[inst] {
124 ir::InstructionData::Jump {
125 destination: dest, ..
126 } => {
127 let block = dest.block(&self.builder.func.dfg.value_lists);
130 self.builder.declare_successor(block, inst);
131 }
132
133 ir::InstructionData::Brif {
134 blocks: [branch_then, branch_else],
135 ..
136 } => {
137 let block_then = branch_then.block(&self.builder.func.dfg.value_lists);
138 let block_else = branch_else.block(&self.builder.func.dfg.value_lists);
139
140 self.builder.declare_successor(block_then, inst);
141 if block_then != block_else {
142 self.builder.declare_successor(block_else, inst);
143 }
144 }
145
146 ir::InstructionData::BranchTable { table, .. } => {
147 let pool = &self.builder.func.dfg.value_lists;
148
149 let mut unique = EntitySet::<Block>::new();
153 for dest_block in self
154 .builder
155 .func
156 .stencil
157 .dfg
158 .jump_tables
159 .get(*table)
160 .expect("you are referencing an undeclared jump table")
161 .all_branches()
162 {
163 let block = dest_block.block(pool);
164 if !unique.insert(block) {
165 continue;
166 }
167
168 self.builder
171 .func_ctx
172 .ssa
173 .declare_block_predecessor(block, inst);
174 }
175 }
176
177 ir::InstructionData::TryCall { exception, .. }
178 | ir::InstructionData::TryCallIndirect { exception, .. } => {
179 let pool = &self.builder.func.dfg.value_lists;
180
181 let mut unique = EntitySet::<Block>::new();
185 for dest_block in self
186 .builder
187 .func
188 .stencil
189 .dfg
190 .exception_tables
191 .get(*exception)
192 .expect("you are referencing an undeclared exception table")
193 .all_branches()
194 {
195 let block = dest_block.block(pool);
196 if !unique.insert(block) {
197 continue;
198 }
199
200 self.builder
203 .func_ctx
204 .ssa
205 .declare_block_predecessor(block, inst);
206 }
207 }
208
209 inst => assert!(!inst.opcode().is_branch()),
210 }
211
212 if data.opcode().is_terminator() {
213 self.builder.fill_current_block()
214 }
215 (inst, &mut self.builder.func.dfg)
216 }
217
218 fn build_aux_inst(&mut self, data: InstructionData, ctrl_typevar: Type) -> Inst {
219 self.builder.ins().build(data, ctrl_typevar).0
223 }
224}
225
226#[derive(Debug, Copy, Clone, PartialEq, Eq)]
227pub enum UseVariableError {
229 UsedBeforeDeclared(Variable),
230}
231
232impl fmt::Display for UseVariableError {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 match self {
235 UseVariableError::UsedBeforeDeclared(variable) => {
236 write!(
237 f,
238 "variable {} was used before it was defined",
239 variable.index()
240 )?;
241 }
242 }
243 Ok(())
244 }
245}
246
247impl core::error::Error for UseVariableError {}
248
249#[derive(Debug, Copy, Clone, Eq, PartialEq)]
250pub enum DefVariableError {
252 TypeMismatch(Variable, Value),
258 DefinedBeforeDeclared(Variable),
261}
262
263impl fmt::Display for DefVariableError {
264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265 match self {
266 DefVariableError::TypeMismatch(variable, value) => {
267 write!(
268 f,
269 "the types of variable {} and value {} are not the same.
270 The `Value` supplied to `def_var` must be of the same type as
271 the variable was declared to be of in `declare_var`.",
272 variable.index(),
273 value.as_u32()
274 )?;
275 }
276 DefVariableError::DefinedBeforeDeclared(variable) => {
277 write!(
278 f,
279 "the value of variable {} was declared before it was defined",
280 variable.index()
281 )?;
282 }
283 }
284 Ok(())
285 }
286}
287
288impl<'a> FunctionBuilder<'a> {
321 pub fn new(func: &'a mut Function, func_ctx: &'a mut FunctionBuilderContext) -> Self {
324 debug_assert!(func_ctx.is_empty());
325 Self {
326 func,
327 srcloc: Default::default(),
328 func_ctx,
329 position: Default::default(),
330 }
331 }
332
333 pub fn current_block(&self) -> Option<Block> {
335 self.position.expand()
336 }
337
338 pub fn set_srcloc(&mut self, srcloc: ir::SourceLoc) {
340 self.srcloc = srcloc;
341 }
342
343 pub fn srcloc(&self) -> ir::SourceLoc {
345 self.srcloc
346 }
347
348 pub fn create_block(&mut self) -> Block {
350 let block = self.func.dfg.make_block();
351 self.func_ctx.ssa.declare_block(block);
352 block
353 }
354
355 pub fn set_cold_block(&mut self, block: Block) {
360 self.func.layout.set_cold(block);
361 }
362
363 pub fn insert_block_after(&mut self, block: Block, after: Block) {
365 self.func.layout.insert_block_after(block, after);
366 }
367
368 pub fn switch_to_block(&mut self, block: Block) {
376 log::trace!("switch to {block:?}");
377
378 debug_assert!(
380 self.position.is_none()
381 || self.is_unreachable()
382 || self.is_pristine(self.position.unwrap())
383 || self.is_filled(self.position.unwrap()),
384 "you have to fill your block before switching"
385 );
386 debug_assert!(
388 !self.is_filled(block),
389 "you cannot switch to a block which is already filled"
390 );
391
392 self.position = PackedOption::from(block);
394 }
395
396 pub fn seal_block(&mut self, block: Block) {
402 let side_effects = self.func_ctx.ssa.seal_block(block, self.func);
403 self.handle_ssa_side_effects(side_effects);
404 }
405
406 pub fn seal_all_blocks(&mut self) {
413 let side_effects = self.func_ctx.ssa.seal_all_blocks(self.func);
414 self.handle_ssa_side_effects(side_effects);
415 }
416
417 pub fn declare_var(&mut self, ty: Type) -> Variable {
423 self.func_ctx.variables.push(ty)
424 }
425
426 pub fn declare_var_needs_stack_map(&mut self, var: Variable) {
443 log::trace!("declare_var_needs_stack_map({var:?})");
444 let ty = self.func_ctx.variables[var];
445 assert!(ty != types::INVALID);
446 assert!(ty.bytes() <= 16);
447 self.func_ctx.ssa.mark_var_needs_stack_map(var);
448 }
449
450 pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {
453 self.ensure_inserted_block();
458
459 let (val, side_effects) = {
460 let ty = *self
461 .func_ctx
462 .variables
463 .get(var)
464 .ok_or(UseVariableError::UsedBeforeDeclared(var))?;
465 debug_assert_ne!(
466 ty,
467 types::INVALID,
468 "variable {var:?} is used but its type has not been declared"
469 );
470 self.func_ctx
471 .ssa
472 .use_var(self.func, var, ty, self.position.unwrap())
473 };
474 self.handle_ssa_side_effects(side_effects);
475
476 Ok(val)
477 }
478
479 pub fn use_var(&mut self, var: Variable) -> Value {
482 self.try_use_var(var).unwrap_or_else(|_| {
483 panic!("variable {var:?} is used but its type has not been declared")
484 })
485 }
486
487 pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
491 log::trace!("try_def_var: {var:?} = {val:?}");
492
493 let var_ty = *self
494 .func_ctx
495 .variables
496 .get(var)
497 .ok_or(DefVariableError::DefinedBeforeDeclared(var))?;
498 if var_ty != self.func.dfg.value_type(val) {
499 return Err(DefVariableError::TypeMismatch(var, val));
500 }
501
502 self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
503 Ok(())
504 }
505
506 pub fn def_var(&mut self, var: Variable, val: Value) {
509 self.try_def_var(var, val)
510 .unwrap_or_else(|error| match error {
511 DefVariableError::TypeMismatch(var, val) => {
512 panic!("declared type of variable {var:?} doesn't match type of value {val}");
513 }
514 DefVariableError::DefinedBeforeDeclared(var) => {
515 panic!("variable {var:?} is used but its type has not been declared");
516 }
517 })
518 }
519
520 pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
525 if let Some(values_labels) = self.func.stencil.dfg.values_labels.as_mut() {
526 use alloc::collections::btree_map::Entry;
527
528 let start = ValueLabelStart {
529 from: RelSourceLoc::from_base_offset(self.func.params.base_srcloc(), self.srcloc),
530 label,
531 };
532
533 match values_labels.entry(val) {
534 Entry::Occupied(mut e) => match e.get_mut() {
535 ValueLabelAssignments::Starts(starts) => starts.push(start),
536 _ => panic!("Unexpected ValueLabelAssignments at this stage"),
537 },
538 Entry::Vacant(e) => {
539 e.insert(ValueLabelAssignments::Starts(vec![start]));
540 }
541 }
542 }
543 }
544
545 pub fn declare_value_needs_stack_map(&mut self, val: Value) {
558 log::trace!("declare_value_needs_stack_map({val:?})");
559
560 let size = self.func.dfg.value_type(val).bytes();
562 assert!(size <= 16);
563 assert!(size.is_power_of_two());
564
565 self.func_ctx.ssa.stack_map_values_mut().insert(val);
566 }
567
568 pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
570 self.func.create_jump_table(data)
571 }
572
573 pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
576 self.func.create_sized_stack_slot(data)
577 }
578
579 pub fn create_dynamic_stack_slot(&mut self, data: DynamicStackSlotData) -> DynamicStackSlot {
584 self.func.create_dynamic_stack_slot(data)
585 }
586
587 pub fn import_signature(&mut self, signature: Signature) -> SigRef {
589 self.func.import_signature(signature)
590 }
591
592 pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef {
594 self.func.import_function(data)
595 }
596
597 pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue {
599 self.func.create_global_value(data)
600 }
601
602 pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> {
605 let block = self
606 .position
607 .expect("Please call switch_to_block before inserting instructions");
608 FuncInstBuilder::new(self, block)
609 }
610
611 pub fn ensure_inserted_block(&mut self) {
613 let block = self.position.unwrap();
614 if self.is_pristine(block) {
615 if !self.func.layout.is_block_inserted(block) {
616 self.func.layout.append_block(block);
617 }
618 self.func_ctx.status[block] = BlockStatus::Partial;
619 } else {
620 debug_assert!(
621 !self.is_filled(block),
622 "you cannot add an instruction to a block already filled"
623 );
624 }
625 }
626
627 pub fn cursor(&mut self) -> FuncCursor<'_> {
632 self.ensure_inserted_block();
633 FuncCursor::new(self.func)
634 .with_srcloc(self.srcloc)
635 .at_bottom(self.position.unwrap())
636 }
637
638 pub fn append_block_params_for_function_params(&mut self, block: Block) {
642 debug_assert!(
643 !self.func_ctx.ssa.has_any_predecessors(block),
644 "block parameters for function parameters should only be added to the entry block"
645 );
646
647 debug_assert!(
650 self.is_pristine(block),
651 "You can't add block parameters after adding any instruction"
652 );
653
654 for argtyp in &self.func.stencil.signature.params {
655 self.func
656 .stencil
657 .dfg
658 .append_block_param(block, argtyp.value_type);
659 }
660 }
661
662 pub fn append_block_params_for_function_returns(&mut self, block: Block) {
666 debug_assert!(
669 self.is_pristine(block),
670 "You can't add block parameters after adding any instruction"
671 );
672
673 for argtyp in &self.func.stencil.signature.returns {
674 self.func
675 .stencil
676 .dfg
677 .append_block_param(block, argtyp.value_type);
678 }
679 }
680
681 pub fn make_stack_map_alias_region(
690 &mut self,
691 make_alias_region: Box<
692 dyn Fn(&mut ir::AliasRegionSet, ir::Type, ir::StackSlot, u32) -> Option<ir::AliasRegion>
693 + Send
694 + Sync,
695 >,
696 ) {
697 self.func_ctx.safepoints.make_alias_region = Some(make_alias_region);
698 }
699
700 pub fn finalize(mut self, frontend_config: TargetFrontendConfig) {
705 #[cfg(debug_assertions)]
707 {
708 for block in self.func_ctx.status.keys() {
709 if !self.is_pristine(block) {
710 assert!(
711 self.func_ctx.ssa.is_sealed(block),
712 "FunctionBuilder finalized, but block {block} is not sealed",
713 );
714 assert!(
715 self.is_filled(block),
716 "FunctionBuilder finalized, but block {block} is not filled",
717 );
718 }
719 }
720 }
721
722 #[cfg(debug_assertions)]
724 {
725 for block in self.func_ctx.status.keys() {
727 if let Err((inst, msg)) = self.func.is_block_basic(block) {
728 let inst_str = self.func.dfg.display_inst(inst);
729 panic!("{block} failed basic block invariants on {inst_str}: {msg}");
730 }
731 }
732 }
733
734 if !self.func_ctx.ssa.stack_map_values().is_empty() {
738 self.func_ctx.safepoints.run(
739 &mut self.func,
740 self.func_ctx.ssa.stack_map_values(),
741 frontend_config.pointer_type(),
742 );
743 }
744
745 self.func_ctx.clear();
748 }
749}
750
751impl<'a> FunctionBuilder<'a> {
757 pub fn block_params(&self, block: Block) -> &[Value] {
760 self.func.dfg.block_params(block)
761 }
762
763 pub fn signature(&self, sigref: SigRef) -> Option<&Signature> {
766 self.func.dfg.signatures.get(sigref)
767 }
768
769 pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
775 debug_assert!(
776 self.is_pristine(block),
777 "You can't add block parameters after adding any instruction"
778 );
779 self.func.dfg.append_block_param(block, ty)
780 }
781
782 pub fn inst_results(&self, inst: Inst) -> &[Value] {
784 self.func.dfg.inst_results(inst)
785 }
786
787 pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {
792 let dfg = &mut self.func.dfg;
793 for block in
794 dfg.insts[inst].branch_destination_mut(&mut dfg.jump_tables, &mut dfg.exception_tables)
795 {
796 if block.block(&dfg.value_lists) == old_block {
797 self.func_ctx.ssa.remove_block_predecessor(old_block, inst);
798 block.set_block(new_block, &mut dfg.value_lists);
799 self.func_ctx.ssa.declare_block_predecessor(new_block, inst);
800 }
801 }
802 }
803
804 pub fn is_unreachable(&self) -> bool {
808 let is_entry = match self.func.layout.entry_block() {
809 None => false,
810 Some(entry) => self.position.unwrap() == entry,
811 };
812 !is_entry
813 && self.func_ctx.ssa.is_sealed(self.position.unwrap())
814 && !self
815 .func_ctx
816 .ssa
817 .has_any_predecessors(self.position.unwrap())
818 }
819
820 fn is_pristine(&self, block: Block) -> bool {
823 self.func_ctx.status[block] == BlockStatus::Empty
824 }
825
826 fn is_filled(&self, block: Block) -> bool {
829 self.func_ctx.status[block] == BlockStatus::Filled
830 }
831}
832
833impl<'a> FunctionBuilder<'a> {
835 pub fn call_memcpy(
842 &mut self,
843 config: TargetFrontendConfig,
844 dest: Value,
845 src: Value,
846 size: Value,
847 ) {
848 let pointer_type = config.pointer_type();
849 let signature = {
850 let mut s = Signature::new(config.default_call_conv);
851 s.params.push(AbiParam::new(pointer_type));
852 s.params.push(AbiParam::new(pointer_type));
853 s.params.push(AbiParam::new(pointer_type));
854 s.returns.push(AbiParam::new(pointer_type));
855 self.import_signature(s)
856 };
857
858 let libc_memcpy = self.import_function(ExtFuncData {
859 name: ExternalName::LibCall(LibCall::Memcpy),
860 signature,
861 colocated: false,
862 patchable: false,
863 });
864
865 self.ins().call(libc_memcpy, &[dest, src, size]);
866 }
867
868 pub fn emit_small_memory_copy(
877 &mut self,
878 config: TargetFrontendConfig,
879 dest: Value,
880 src: Value,
881 size: u64,
882 dest_align: u8,
883 src_align: u8,
884 non_overlapping: bool,
885 mut flags: MemFlagsData,
886 ) {
887 const THRESHOLD: u64 = 4;
889
890 if size == 0 {
891 return;
892 }
893
894 let access_size = greatest_divisible_power_of_two(size);
895 assert!(
896 access_size.is_power_of_two(),
897 "`size` is not a power of two"
898 );
899 assert!(
900 access_size >= u64::from(::core::cmp::min(src_align, dest_align)),
901 "`size` is smaller than `dest` and `src`'s alignment value."
902 );
903
904 let (access_size, int_type) = if access_size <= 8 {
905 (access_size, Type::int((access_size * 8) as u16).unwrap())
906 } else {
907 (8, types::I64)
908 };
909
910 let load_and_store_amount = size / access_size;
911
912 if load_and_store_amount > THRESHOLD {
913 let size_value = self.ins().iconst(config.pointer_type(), size as i64);
914 if non_overlapping {
915 self.call_memcpy(config, dest, src, size_value);
916 } else {
917 self.call_memmove(config, dest, src, size_value);
918 }
919 return;
920 }
921
922 if u64::from(src_align) >= access_size && u64::from(dest_align) >= access_size {
923 flags.set_aligned();
924 }
925
926 let registers: smallvec::SmallVec<[_; THRESHOLD as usize]> = (0..load_and_store_amount)
929 .map(|i| {
930 let offset = (access_size * i) as i32;
931 (self.ins().load(int_type, flags, src, offset), offset)
932 })
933 .collect();
934
935 for (value, offset) in registers {
936 self.ins().store(flags, value, dest, offset);
937 }
938 }
939
940 pub fn call_memset(
944 &mut self,
945 config: TargetFrontendConfig,
946 buffer: Value,
947 ch: Value,
948 size: Value,
949 ) {
950 let pointer_type = config.pointer_type();
951 let signature = {
952 let mut s = Signature::new(config.default_call_conv);
953 s.params.push(AbiParam::new(pointer_type));
954 s.params.push(AbiParam::new(types::I32));
955 s.params.push(AbiParam::new(pointer_type));
956 s.returns.push(AbiParam::new(pointer_type));
957 self.import_signature(s)
958 };
959
960 let libc_memset = self.import_function(ExtFuncData {
961 name: ExternalName::LibCall(LibCall::Memset),
962 signature,
963 colocated: false,
964 patchable: false,
965 });
966
967 let ch = self.ins().uextend(types::I32, ch);
968 self.ins().call(libc_memset, &[buffer, ch, size]);
969 }
970
971 pub fn emit_small_memset(
975 &mut self,
976 config: TargetFrontendConfig,
977 buffer: Value,
978 ch: u8,
979 size: u64,
980 buffer_align: u8,
981 mut flags: MemFlagsData,
982 ) {
983 const THRESHOLD: u64 = 4;
985
986 if size == 0 {
987 return;
988 }
989
990 let access_size = greatest_divisible_power_of_two(size);
991 assert!(
992 access_size.is_power_of_two(),
993 "`size` is not a power of two"
994 );
995 assert!(
996 access_size >= u64::from(buffer_align),
997 "`size` is smaller than `dest` and `src`'s alignment value."
998 );
999
1000 let (access_size, int_type) = if access_size <= 8 {
1001 (access_size, Type::int((access_size * 8) as u16).unwrap())
1002 } else {
1003 (8, types::I64)
1004 };
1005
1006 let load_and_store_amount = size / access_size;
1007
1008 if load_and_store_amount > THRESHOLD {
1009 let ch = self.ins().iconst(types::I8, i64::from(ch));
1010 let size = self.ins().iconst(config.pointer_type(), size as i64);
1011 self.call_memset(config, buffer, ch, size);
1012 } else {
1013 if u64::from(buffer_align) >= access_size {
1014 flags.set_aligned();
1015 }
1016
1017 let ch = u64::from(ch);
1018 let raw_value = if int_type == types::I64 {
1019 ch * 0x0101010101010101_u64
1020 } else if int_type == types::I32 {
1021 ch * 0x01010101_u64
1022 } else if int_type == types::I16 {
1023 (ch << 8) | ch
1024 } else {
1025 assert_eq!(int_type, types::I8);
1026 ch
1027 };
1028
1029 let value = self.ins().iconst(int_type, raw_value as i64);
1030 for i in 0..load_and_store_amount {
1031 let offset = (access_size * i) as i32;
1032 self.ins().store(flags, value, buffer, offset);
1033 }
1034 }
1035 }
1036
1037 pub fn call_memmove(
1042 &mut self,
1043 config: TargetFrontendConfig,
1044 dest: Value,
1045 source: Value,
1046 size: Value,
1047 ) {
1048 let pointer_type = config.pointer_type();
1049 let signature = {
1050 let mut s = Signature::new(config.default_call_conv);
1051 s.params.push(AbiParam::new(pointer_type));
1052 s.params.push(AbiParam::new(pointer_type));
1053 s.params.push(AbiParam::new(pointer_type));
1054 s.returns.push(AbiParam::new(pointer_type));
1055 self.import_signature(s)
1056 };
1057
1058 let libc_memmove = self.import_function(ExtFuncData {
1059 name: ExternalName::LibCall(LibCall::Memmove),
1060 signature,
1061 colocated: false,
1062 patchable: false,
1063 });
1064
1065 self.ins().call(libc_memmove, &[dest, source, size]);
1066 }
1067
1068 pub fn call_memcmp(
1077 &mut self,
1078 config: TargetFrontendConfig,
1079 left: Value,
1080 right: Value,
1081 size: Value,
1082 ) -> Value {
1083 let pointer_type = config.pointer_type();
1084 let signature = {
1085 let mut s = Signature::new(config.default_call_conv);
1086 s.params.reserve(3);
1087 s.params.push(AbiParam::new(pointer_type));
1088 s.params.push(AbiParam::new(pointer_type));
1089 s.params.push(AbiParam::new(pointer_type));
1090 s.returns.push(AbiParam::new(types::I32));
1091 self.import_signature(s)
1092 };
1093
1094 let libc_memcmp = self.import_function(ExtFuncData {
1095 name: ExternalName::LibCall(LibCall::Memcmp),
1096 signature,
1097 colocated: false,
1098 patchable: false,
1099 });
1100
1101 let call = self.ins().call(libc_memcmp, &[left, right, size]);
1102 self.func.dfg.first_result(call)
1103 }
1104
1105 pub fn emit_small_memory_compare(
1118 &mut self,
1119 config: TargetFrontendConfig,
1120 int_cc: IntCC,
1121 left: Value,
1122 right: Value,
1123 size: u64,
1124 left_align: core::num::NonZeroU8,
1125 right_align: core::num::NonZeroU8,
1126 flags: MemFlagsData,
1127 ) -> Value {
1128 use IntCC::*;
1129 let (zero_cc, empty_imm) = match int_cc {
1130 Equal => (Equal, 1),
1132 NotEqual => (NotEqual, 0),
1133
1134 UnsignedLessThan => (SignedLessThan, 0),
1135 UnsignedGreaterThanOrEqual => (SignedGreaterThanOrEqual, 1),
1136 UnsignedGreaterThan => (SignedGreaterThan, 0),
1137 UnsignedLessThanOrEqual => (SignedLessThanOrEqual, 1),
1138
1139 SignedLessThan
1140 | SignedGreaterThanOrEqual
1141 | SignedGreaterThan
1142 | SignedLessThanOrEqual => {
1143 panic!("Signed comparison {int_cc} not supported by memcmp")
1144 }
1145 };
1146
1147 if size == 0 {
1148 return self.ins().iconst(types::I8, empty_imm);
1149 }
1150
1151 if let Some(small_type) = size.try_into().ok().and_then(Type::int_with_byte_size) {
1153 if let Equal | NotEqual = zero_cc {
1154 let mut left_flags = flags;
1155 if size == left_align.get() as u64 {
1156 left_flags.set_aligned();
1157 }
1158 let mut right_flags = flags;
1159 if size == right_align.get() as u64 {
1160 right_flags.set_aligned();
1161 }
1162 let left_val = self.ins().load(small_type, left_flags, left, 0);
1163 let right_val = self.ins().load(small_type, right_flags, right, 0);
1164 return self.ins().icmp(int_cc, left_val, right_val);
1165 } else if small_type == types::I8 {
1166 let mut aligned_flags = flags;
1171 aligned_flags.set_aligned();
1172 let left_val = self.ins().load(small_type, aligned_flags, left, 0);
1173 let right_val = self.ins().load(small_type, aligned_flags, right, 0);
1174 return self.ins().icmp(int_cc, left_val, right_val);
1175 }
1176 }
1177
1178 let pointer_type = config.pointer_type();
1179 let size = self.ins().iconst(pointer_type, size as i64);
1180 let cmp = self.call_memcmp(config, left, right, size);
1181 self.ins().icmp_imm_s(zero_cc, cmp, 0)
1182 }
1183}
1184
1185fn greatest_divisible_power_of_two(size: u64) -> u64 {
1186 (size as i64 & -(size as i64)) as u64
1187}
1188
1189impl<'a> FunctionBuilder<'a> {
1191 fn fill_current_block(&mut self) {
1193 self.func_ctx.status[self.position.unwrap()] = BlockStatus::Filled;
1194 }
1195
1196 fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) {
1197 self.func_ctx
1198 .ssa
1199 .declare_block_predecessor(dest_block, jump_inst);
1200 }
1201
1202 fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
1203 let SideEffects {
1204 instructions_added_to_blocks,
1205 } = side_effects;
1206
1207 for modified_block in instructions_added_to_blocks {
1208 if self.is_pristine(modified_block) {
1209 self.func_ctx.status[modified_block] = BlockStatus::Partial;
1210 }
1211 }
1212 }
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217 use super::greatest_divisible_power_of_two;
1218 use crate::Variable;
1219 use crate::frontend::{
1220 DefVariableError, FunctionBuilder, FunctionBuilderContext, UseVariableError,
1221 };
1222 use alloc::string::ToString;
1223 use cranelift_codegen::ir::condcodes::IntCC;
1224 use cranelift_codegen::ir::{
1225 AbiParam, BlockCall, ExceptionTableData, ExtFuncData, ExternalName, Function, InstBuilder,
1226 MemFlagsData, Signature, UserExternalName, UserFuncName, Value, types::*,
1227 };
1228 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
1229 use cranelift_codegen::settings;
1230 use cranelift_codegen::verifier::verify_function;
1231 use target_lexicon::PointerWidth;
1232
1233 fn sample_function(lazy_seal: bool) {
1234 let mut sig = Signature::new(CallConv::SystemV);
1235 sig.returns.push(AbiParam::new(I32));
1236 sig.params.push(AbiParam::new(I32));
1237
1238 let mut fn_ctx = FunctionBuilderContext::new();
1239 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1240 {
1241 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1242
1243 let block0 = builder.create_block();
1244 let block1 = builder.create_block();
1245 let block2 = builder.create_block();
1246 let block3 = builder.create_block();
1247 let x = builder.declare_var(I32);
1248 let y = builder.declare_var(I32);
1249 let z = builder.declare_var(I32);
1250
1251 builder.append_block_params_for_function_params(block0);
1252
1253 builder.switch_to_block(block0);
1254 if !lazy_seal {
1255 builder.seal_block(block0);
1256 }
1257 {
1258 let tmp = builder.block_params(block0)[0]; builder.def_var(x, tmp);
1260 }
1261 {
1262 let tmp = builder.ins().iconst(I32, 2);
1263 builder.def_var(y, tmp);
1264 }
1265 {
1266 let arg1 = builder.use_var(x);
1267 let arg2 = builder.use_var(y);
1268 let tmp = builder.ins().iadd(arg1, arg2);
1269 builder.def_var(z, tmp);
1270 }
1271 builder.ins().jump(block1, &[]);
1272
1273 builder.switch_to_block(block1);
1274 {
1275 let arg1 = builder.use_var(y);
1276 let arg2 = builder.use_var(z);
1277 let tmp = builder.ins().iadd(arg1, arg2);
1278 builder.def_var(z, tmp);
1279 }
1280 {
1281 let arg = builder.use_var(y);
1282 builder.ins().brif(arg, block3, &[], block2, &[]);
1283 }
1284
1285 builder.switch_to_block(block2);
1286 if !lazy_seal {
1287 builder.seal_block(block2);
1288 }
1289 {
1290 let arg1 = builder.use_var(z);
1291 let arg2 = builder.use_var(x);
1292 let tmp = builder.ins().isub(arg1, arg2);
1293 builder.def_var(z, tmp);
1294 }
1295 {
1296 let arg = builder.use_var(y);
1297 builder.ins().return_(&[arg]);
1298 }
1299
1300 builder.switch_to_block(block3);
1301 if !lazy_seal {
1302 builder.seal_block(block3);
1303 }
1304
1305 {
1306 let arg1 = builder.use_var(y);
1307 let arg2 = builder.use_var(x);
1308 let tmp = builder.ins().isub(arg1, arg2);
1309 builder.def_var(y, tmp);
1310 }
1311 builder.ins().jump(block1, &[]);
1312 if !lazy_seal {
1313 builder.seal_block(block1);
1314 }
1315
1316 if lazy_seal {
1317 builder.seal_all_blocks();
1318 }
1319
1320 builder.finalize(systemv_frontend_config());
1321 }
1322
1323 let flags = settings::Flags::new(settings::builder());
1324 if let Err(errors) = verify_function(&func, &flags) {
1326 panic!("{}\n{}", func.display(), errors)
1327 }
1328 }
1329
1330 #[test]
1331 fn sample() {
1332 sample_function(false)
1333 }
1334
1335 #[test]
1336 fn sample_with_lazy_seal() {
1337 sample_function(true)
1338 }
1339
1340 #[track_caller]
1341 fn check(func: &Function, expected_ir: &str) {
1342 let expected_ir = expected_ir.trim();
1343 let actual_ir = func.display().to_string();
1344 let actual_ir = actual_ir.trim();
1345 assert!(
1346 expected_ir == actual_ir,
1347 "Expected:\n{expected_ir}\nGot:\n{actual_ir}"
1348 );
1349 }
1350
1351 fn systemv_frontend_config() -> TargetFrontendConfig {
1353 TargetFrontendConfig {
1354 default_call_conv: CallConv::SystemV,
1355 pointer_width: PointerWidth::U64,
1356 page_size_align_log2: 12,
1357 }
1358 }
1359
1360 #[test]
1361 fn memcpy() {
1362 let frontend_config = systemv_frontend_config();
1363 let mut sig = Signature::new(frontend_config.default_call_conv);
1364 sig.returns.push(AbiParam::new(I32));
1365
1366 let mut fn_ctx = FunctionBuilderContext::new();
1367 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1368 {
1369 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1370
1371 let block0 = builder.create_block();
1372 let x = builder.declare_var(frontend_config.pointer_type());
1373 let y = builder.declare_var(frontend_config.pointer_type());
1374 let _z = builder.declare_var(I32);
1375
1376 builder.append_block_params_for_function_params(block0);
1377 builder.switch_to_block(block0);
1378
1379 let src = builder.use_var(x);
1380 let dest = builder.use_var(y);
1381 let size = builder.use_var(y);
1382 builder.call_memcpy(frontend_config, dest, src, size);
1383 builder.ins().return_(&[size]);
1384
1385 builder.seal_all_blocks();
1386 builder.finalize(systemv_frontend_config());
1387 }
1388
1389 check(
1390 &func,
1391 "function %sample() -> i32 system_v {
1392 sig0 = (i64, i64, i64) -> i64 system_v
1393 fn0 = %Memcpy sig0
1394
1395block0:
1396 v4 = iconst.i64 0
1397 v1 -> v4
1398 v3 = iconst.i64 0
1399 v0 -> v3
1400 v2 = call fn0(v1, v0, v1) ; v1 = 0, v0 = 0, v1 = 0
1401 return v1 ; v1 = 0
1402}
1403",
1404 );
1405 }
1406
1407 #[test]
1408 fn small_memcpy() {
1409 let frontend_config = systemv_frontend_config();
1410 let mut sig = Signature::new(frontend_config.default_call_conv);
1411 sig.returns.push(AbiParam::new(I32));
1412
1413 let mut fn_ctx = FunctionBuilderContext::new();
1414 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1415 {
1416 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1417
1418 let block0 = builder.create_block();
1419 let x = builder.declare_var(frontend_config.pointer_type());
1420 let y = builder.declare_var(frontend_config.pointer_type());
1421
1422 builder.append_block_params_for_function_params(block0);
1423 builder.switch_to_block(block0);
1424
1425 let src = builder.use_var(x);
1426 let dest = builder.use_var(y);
1427 let size = 8;
1428 builder.emit_small_memory_copy(
1429 frontend_config,
1430 dest,
1431 src,
1432 size,
1433 8,
1434 8,
1435 true,
1436 MemFlagsData::new(),
1437 );
1438 builder.ins().return_(&[dest]);
1439
1440 builder.seal_all_blocks();
1441 builder.finalize(systemv_frontend_config());
1442 }
1443
1444 check(
1445 &func,
1446 "function %sample() -> i32 system_v {
1447block0:
1448 v4 = iconst.i64 0
1449 v1 -> v4
1450 v3 = iconst.i64 0
1451 v0 -> v3
1452 v2 = load.i64 aligned v0 ; v0 = 0
1453 store aligned v2, v1 ; v1 = 0
1454 return v1 ; v1 = 0
1455}
1456",
1457 );
1458 }
1459
1460 #[test]
1461 fn not_so_small_memcpy() {
1462 let frontend_config = systemv_frontend_config();
1463 let mut sig = Signature::new(frontend_config.default_call_conv);
1464 sig.returns.push(AbiParam::new(I32));
1465
1466 let mut fn_ctx = FunctionBuilderContext::new();
1467 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1468 {
1469 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1470
1471 let block0 = builder.create_block();
1472 let x = builder.declare_var(frontend_config.pointer_type());
1473 let y = builder.declare_var(frontend_config.pointer_type());
1474 builder.append_block_params_for_function_params(block0);
1475 builder.switch_to_block(block0);
1476
1477 let src = builder.use_var(x);
1478 let dest = builder.use_var(y);
1479 let size = 8192;
1480 builder.emit_small_memory_copy(
1481 frontend_config,
1482 dest,
1483 src,
1484 size,
1485 8,
1486 8,
1487 true,
1488 MemFlagsData::new(),
1489 );
1490 builder.ins().return_(&[dest]);
1491
1492 builder.seal_all_blocks();
1493 builder.finalize(systemv_frontend_config());
1494 }
1495
1496 check(
1497 &func,
1498 "function %sample() -> i32 system_v {
1499 sig0 = (i64, i64, i64) -> i64 system_v
1500 fn0 = %Memcpy sig0
1501
1502block0:
1503 v5 = iconst.i64 0
1504 v1 -> v5
1505 v4 = iconst.i64 0
1506 v0 -> v4
1507 v2 = iconst.i64 8192
1508 v3 = call fn0(v1, v0, v2) ; v1 = 0, v0 = 0, v2 = 8192
1509 return v1 ; v1 = 0
1510}
1511",
1512 );
1513 }
1514
1515 #[test]
1516 fn small_memset() {
1517 let frontend_config = systemv_frontend_config();
1518 let mut sig = Signature::new(frontend_config.default_call_conv);
1519 sig.returns.push(AbiParam::new(I32));
1520
1521 let mut fn_ctx = FunctionBuilderContext::new();
1522 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1523 {
1524 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1525
1526 let block0 = builder.create_block();
1527 let y = builder.declare_var(frontend_config.pointer_type());
1528 builder.append_block_params_for_function_params(block0);
1529 builder.switch_to_block(block0);
1530
1531 let dest = builder.use_var(y);
1532 let size = 8;
1533 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlagsData::new());
1534 builder.ins().return_(&[dest]);
1535
1536 builder.seal_all_blocks();
1537 builder.finalize(systemv_frontend_config());
1538 }
1539
1540 check(
1541 &func,
1542 "function %sample() -> i32 system_v {
1543block0:
1544 v2 = iconst.i64 0
1545 v0 -> v2
1546 v1 = iconst.i64 0x0101_0101_0101_0101
1547 store aligned v1, v0 ; v1 = 0x0101_0101_0101_0101, v0 = 0
1548 return v0 ; v0 = 0
1549}
1550",
1551 );
1552 }
1553
1554 #[test]
1555 fn not_so_small_memset() {
1556 let frontend_config = systemv_frontend_config();
1557 let mut sig = Signature::new(frontend_config.default_call_conv);
1558 sig.returns.push(AbiParam::new(I32));
1559
1560 let mut fn_ctx = FunctionBuilderContext::new();
1561 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1562 {
1563 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1564
1565 let block0 = builder.create_block();
1566 let y = builder.declare_var(frontend_config.pointer_type());
1567 builder.append_block_params_for_function_params(block0);
1568 builder.switch_to_block(block0);
1569
1570 let dest = builder.use_var(y);
1571 let size = 8192;
1572 builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlagsData::new());
1573 builder.ins().return_(&[dest]);
1574
1575 builder.seal_all_blocks();
1576 builder.finalize(systemv_frontend_config());
1577 }
1578
1579 check(
1580 &func,
1581 "function %sample() -> i32 system_v {
1582 sig0 = (i64, i32, i64) -> i64 system_v
1583 fn0 = %Memset sig0
1584
1585block0:
1586 v5 = iconst.i64 0
1587 v0 -> v5
1588 v1 = iconst.i8 1
1589 v2 = iconst.i64 8192
1590 v3 = uextend.i32 v1 ; v1 = 1
1591 v4 = call fn0(v0, v3, v2) ; v0 = 0, v2 = 8192
1592 return v0 ; v0 = 0
1593}
1594",
1595 );
1596 }
1597
1598 #[test]
1599 fn memcmp() {
1600 use core::str::FromStr;
1601 use cranelift_codegen::isa;
1602
1603 let shared_builder = settings::builder();
1604 let shared_flags = settings::Flags::new(shared_builder);
1605
1606 let triple =
1607 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1608
1609 let target = isa::lookup(triple)
1610 .ok()
1611 .map(|b| b.finish(shared_flags))
1612 .expect("This test requires x86_64 support.")
1613 .expect("Should be able to create backend with default flags");
1614
1615 let mut sig = Signature::new(target.default_call_conv());
1616 sig.returns.push(AbiParam::new(I32));
1617
1618 let mut fn_ctx = FunctionBuilderContext::new();
1619 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1620 {
1621 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1622
1623 let block0 = builder.create_block();
1624 let x = builder.declare_var(target.pointer_type());
1625 let y = builder.declare_var(target.pointer_type());
1626 let z = builder.declare_var(target.pointer_type());
1627 builder.append_block_params_for_function_params(block0);
1628 builder.switch_to_block(block0);
1629
1630 let left = builder.use_var(x);
1631 let right = builder.use_var(y);
1632 let size = builder.use_var(z);
1633 let cmp = builder.call_memcmp(target.frontend_config(), left, right, size);
1634 builder.ins().return_(&[cmp]);
1635
1636 builder.seal_all_blocks();
1637 builder.finalize(systemv_frontend_config());
1638 }
1639
1640 check(
1641 &func,
1642 "function %sample() -> i32 system_v {
1643 sig0 = (i64, i64, i64) -> i32 system_v
1644 fn0 = %Memcmp sig0
1645
1646block0:
1647 v6 = iconst.i64 0
1648 v2 -> v6
1649 v5 = iconst.i64 0
1650 v1 -> v5
1651 v4 = iconst.i64 0
1652 v0 -> v4
1653 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 0
1654 return v3
1655}
1656",
1657 );
1658 }
1659
1660 #[test]
1661 fn small_memcmp_zero_size() {
1662 let align_eight = std::num::NonZeroU8::new(8).unwrap();
1663 small_memcmp_helper(
1664 "
1665block0:
1666 v4 = iconst.i64 0
1667 v1 -> v4
1668 v3 = iconst.i64 0
1669 v0 -> v3
1670 v2 = iconst.i8 1
1671 return v2 ; v2 = 1",
1672 |builder, target, x, y| {
1673 builder.emit_small_memory_compare(
1674 target.frontend_config(),
1675 IntCC::UnsignedGreaterThanOrEqual,
1676 x,
1677 y,
1678 0,
1679 align_eight,
1680 align_eight,
1681 MemFlagsData::new(),
1682 )
1683 },
1684 );
1685 }
1686
1687 #[test]
1688 fn small_memcmp_byte_ugt() {
1689 let align_one = std::num::NonZeroU8::new(1).unwrap();
1690 small_memcmp_helper(
1691 "
1692block0:
1693 v6 = iconst.i64 0
1694 v1 -> v6
1695 v5 = iconst.i64 0
1696 v0 -> v5
1697 v2 = load.i8 aligned v0 ; v0 = 0
1698 v3 = load.i8 aligned v1 ; v1 = 0
1699 v4 = icmp ugt v2, v3
1700 return v4",
1701 |builder, target, x, y| {
1702 builder.emit_small_memory_compare(
1703 target.frontend_config(),
1704 IntCC::UnsignedGreaterThan,
1705 x,
1706 y,
1707 1,
1708 align_one,
1709 align_one,
1710 MemFlagsData::new(),
1711 )
1712 },
1713 );
1714 }
1715
1716 #[test]
1717 fn small_memcmp_aligned_eq() {
1718 let align_four = std::num::NonZeroU8::new(4).unwrap();
1719 small_memcmp_helper(
1720 "
1721block0:
1722 v6 = iconst.i64 0
1723 v1 -> v6
1724 v5 = iconst.i64 0
1725 v0 -> v5
1726 v2 = load.i32 aligned v0 ; v0 = 0
1727 v3 = load.i32 aligned v1 ; v1 = 0
1728 v4 = icmp eq v2, v3
1729 return v4",
1730 |builder, target, x, y| {
1731 builder.emit_small_memory_compare(
1732 target.frontend_config(),
1733 IntCC::Equal,
1734 x,
1735 y,
1736 4,
1737 align_four,
1738 align_four,
1739 MemFlagsData::new(),
1740 )
1741 },
1742 );
1743 }
1744
1745 #[test]
1746 fn small_memcmp_ipv6_ne() {
1747 let align_two = std::num::NonZeroU8::new(2).unwrap();
1748 small_memcmp_helper(
1749 "
1750block0:
1751 v6 = iconst.i64 0
1752 v1 -> v6
1753 v5 = iconst.i64 0
1754 v0 -> v5
1755 v2 = load.i128 v0 ; v0 = 0
1756 v3 = load.i128 v1 ; v1 = 0
1757 v4 = icmp ne v2, v3
1758 return v4",
1759 |builder, target, x, y| {
1760 builder.emit_small_memory_compare(
1761 target.frontend_config(),
1762 IntCC::NotEqual,
1763 x,
1764 y,
1765 16,
1766 align_two,
1767 align_two,
1768 MemFlagsData::new(),
1769 )
1770 },
1771 );
1772 }
1773
1774 #[test]
1775 fn small_memcmp_odd_size_uge() {
1776 let one = std::num::NonZeroU8::new(1).unwrap();
1777 small_memcmp_helper(
1778 "
1779 sig0 = (i64, i64, i64) -> i32 system_v
1780 fn0 = %Memcmp sig0
1781
1782block0:
1783 v7 = iconst.i64 0
1784 v1 -> v7
1785 v6 = iconst.i64 0
1786 v0 -> v6
1787 v2 = iconst.i64 3
1788 v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 3
1789 v4 = iconst.i32 0
1790 v5 = icmp sge v3, v4 ; v4 = 0
1791 return v5",
1792 |builder, target, x, y| {
1793 builder.emit_small_memory_compare(
1794 target.frontend_config(),
1795 IntCC::UnsignedGreaterThanOrEqual,
1796 x,
1797 y,
1798 3,
1799 one,
1800 one,
1801 MemFlagsData::new(),
1802 )
1803 },
1804 );
1805 }
1806
1807 fn small_memcmp_helper(
1808 expected: &str,
1809 f: impl FnOnce(&mut FunctionBuilder, &dyn TargetIsa, Value, Value) -> Value,
1810 ) {
1811 use core::str::FromStr;
1812 use cranelift_codegen::isa;
1813
1814 let shared_builder = settings::builder();
1815 let shared_flags = settings::Flags::new(shared_builder);
1816
1817 let triple =
1818 ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1819
1820 let target = isa::lookup(triple)
1821 .ok()
1822 .map(|b| b.finish(shared_flags))
1823 .expect("This test requires x86_64 support.")
1824 .expect("Should be able to create backend with default flags");
1825
1826 let mut sig = Signature::new(target.default_call_conv());
1827 sig.returns.push(AbiParam::new(I8));
1828
1829 let mut fn_ctx = FunctionBuilderContext::new();
1830 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1831 {
1832 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1833
1834 let block0 = builder.create_block();
1835 let x = builder.declare_var(target.pointer_type());
1836 let y = builder.declare_var(target.pointer_type());
1837 builder.append_block_params_for_function_params(block0);
1838 builder.switch_to_block(block0);
1839
1840 let left = builder.use_var(x);
1841 let right = builder.use_var(y);
1842 let ret = f(&mut builder, &*target, left, right);
1843 builder.ins().return_(&[ret]);
1844
1845 builder.seal_all_blocks();
1846 builder.finalize(systemv_frontend_config());
1847 }
1848
1849 check(
1850 &func,
1851 &format!("function %sample() -> i8 system_v {{{expected}\n}}\n"),
1852 );
1853 }
1854
1855 #[test]
1856 fn undef_vector_vars() {
1857 let mut sig = Signature::new(CallConv::SystemV);
1858 sig.returns.push(AbiParam::new(I8X16));
1859 sig.returns.push(AbiParam::new(I8X16));
1860 sig.returns.push(AbiParam::new(F32X4));
1861
1862 let mut fn_ctx = FunctionBuilderContext::new();
1863 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1864 {
1865 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1866
1867 let block0 = builder.create_block();
1868 let a = builder.declare_var(I8X16);
1869 let b = builder.declare_var(I8X16);
1870 let c = builder.declare_var(F32X4);
1871 builder.switch_to_block(block0);
1872
1873 let a = builder.use_var(a);
1874 let b = builder.use_var(b);
1875 let c = builder.use_var(c);
1876 builder.ins().return_(&[a, b, c]);
1877
1878 builder.seal_all_blocks();
1879 builder.finalize(systemv_frontend_config());
1880 }
1881
1882 check(
1883 &func,
1884 "function %sample() -> i8x16, i8x16, f32x4 system_v {
1885 const0 = 0x00000000000000000000000000000000
1886
1887block0:
1888 v5 = f32const 0.0
1889 v6 = splat.f32x4 v5 ; v5 = 0.0
1890 v2 -> v6
1891 v4 = vconst.i8x16 const0
1892 v1 -> v4
1893 v3 = vconst.i8x16 const0
1894 v0 -> v3
1895 return v0, v1, v2 ; v0 = const0, v1 = const0
1896}
1897",
1898 );
1899 }
1900
1901 #[test]
1902 fn test_greatest_divisible_power_of_two() {
1903 assert_eq!(64, greatest_divisible_power_of_two(64));
1904 assert_eq!(16, greatest_divisible_power_of_two(48));
1905 assert_eq!(8, greatest_divisible_power_of_two(24));
1906 assert_eq!(1, greatest_divisible_power_of_two(25));
1907 }
1908
1909 #[test]
1910 fn try_use_var() {
1911 let sig = Signature::new(CallConv::SystemV);
1912
1913 let mut fn_ctx = FunctionBuilderContext::new();
1914 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1915 {
1916 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1917
1918 let block0 = builder.create_block();
1919 builder.append_block_params_for_function_params(block0);
1920 builder.switch_to_block(block0);
1921
1922 assert_eq!(
1923 builder.try_use_var(Variable::from_u32(0)),
1924 Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))
1925 );
1926
1927 let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
1928
1929 assert_eq!(
1930 builder.try_def_var(Variable::from_u32(0), value),
1931 Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(
1932 0
1933 )))
1934 );
1935 }
1936 }
1937
1938 #[test]
1939 fn test_builder_with_iconst_and_negative_constant() {
1940 let sig = Signature::new(CallConv::SystemV);
1941 let mut fn_ctx = FunctionBuilderContext::new();
1942 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1943
1944 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1945
1946 let block0 = builder.create_block();
1947 builder.switch_to_block(block0);
1948 builder.ins().iconst(I32, -1);
1949 builder.ins().return_(&[]);
1950
1951 builder.seal_all_blocks();
1952 builder.finalize(systemv_frontend_config());
1953
1954 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
1955 let ctx = cranelift_codegen::Context::for_function(func);
1956 ctx.verify(&flags).expect("should be valid");
1957
1958 check(
1959 &ctx.func,
1960 "function %sample() system_v {
1961block0:
1962 v0 = iconst.i32 -1
1963 return
1964}",
1965 );
1966 }
1967
1968 #[test]
1969 fn try_call() {
1970 let mut sig = Signature::new(CallConv::SystemV);
1971 sig.params.push(AbiParam::new(I8));
1972 sig.returns.push(AbiParam::new(I32));
1973 let mut fn_ctx = FunctionBuilderContext::new();
1974 let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1975
1976 let sig0 = func.import_signature(Signature::new(CallConv::SystemV));
1977 let name = func.declare_imported_user_function(UserExternalName::new(0, 0));
1978 let fn0 = func.import_function(ExtFuncData {
1979 name: ExternalName::User(name),
1980 signature: sig0,
1981 colocated: false,
1982 patchable: false,
1983 });
1984
1985 let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1986
1987 let block0 = builder.create_block();
1988 let block1 = builder.create_block();
1989 let block2 = builder.create_block();
1990 let block3 = builder.create_block();
1991
1992 let my_var = builder.declare_var(I32);
1993
1994 builder.switch_to_block(block0);
1995 let branch_val = builder.append_block_param(block0, I8);
1996 builder.ins().brif(branch_val, block1, &[], block2, &[]);
1997
1998 builder.switch_to_block(block1);
1999 let one = builder.ins().iconst(I32, 1);
2000 builder.def_var(my_var, one);
2001
2002 let normal_return = BlockCall::new(block3, [], &mut builder.func.dfg.value_lists);
2003 let exception_table = builder
2004 .func
2005 .dfg
2006 .exception_tables
2007 .push(ExceptionTableData::new(sig0, normal_return, []));
2008 builder.ins().try_call(fn0, &[], exception_table);
2009
2010 builder.switch_to_block(block2);
2011 let two = builder.ins().iconst(I32, 2);
2012 builder.def_var(my_var, two);
2013
2014 let normal_return = BlockCall::new(block3, [], &mut builder.func.dfg.value_lists);
2015 let exception_table = builder
2016 .func
2017 .dfg
2018 .exception_tables
2019 .push(ExceptionTableData::new(sig0, normal_return, []));
2020 builder.ins().try_call(fn0, &[], exception_table);
2021
2022 builder.switch_to_block(block3);
2023 let ret_val = builder.use_var(my_var);
2024 builder.ins().return_(&[ret_val]);
2025
2026 builder.seal_all_blocks();
2027 builder.finalize(systemv_frontend_config());
2028
2029 let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
2030 let ctx = cranelift_codegen::Context::for_function(func);
2031 ctx.verify(&flags).expect("should be valid");
2032
2033 check(
2034 &ctx.func,
2035 "function %sample(i8) -> i32 system_v {
2036 sig0 = () system_v
2037 fn0 = u0:0 sig0
2038
2039block0(v0: i8):
2040 brif v0, block1, block2
2041
2042block1:
2043 v1 = iconst.i32 1
2044 try_call fn0(), sig0, block3(v1), [] ; v1 = 1
2045
2046block2:
2047 v2 = iconst.i32 2
2048 try_call fn0(), sig0, block3(v2), [] ; v2 = 2
2049
2050block3(v3: i32):
2051 return v3
2052}",
2053 );
2054 }
2055}