1pub mod generated_code;
5use generated_code::{Context, ImmExtend};
6
7use super::{
9 ASIMDFPModImm, ASIMDMovModImm, BranchTarget, CallInfo, Cond, CondBrKind, ExtendOp, FPUOpRI,
10 FPUOpRIMod, FloatCC, Imm12, ImmLogic, ImmShift, Inst as MInst, IntCC, MachLabel, MemLabel,
11 MoveWideConst, MoveWideOp, NZCV, Opcode, OperandSize, Reg, SImm9, ScalarSize, ShiftOpAndAmt,
12 UImm5, UImm6, UImm12Scaled, VecMisc2, VectorSize, fp_reg, lower_condcode, stack_reg,
13 writable_link_reg, writable_zero_reg, zero_reg,
14};
15use crate::ir::{ArgumentExtension, condcodes};
16use crate::isa;
17use crate::isa::aarch64::AArch64Backend;
18use crate::isa::aarch64::inst::{FPULeftShiftImm, FPURightShiftImm, ReturnCallInfo};
19use crate::machinst::isle::*;
20use crate::{
21 binemit::CodeOffset,
22 ir::{
23 AtomicRmwOp, BlockCall, ExternalName, Inst, InstructionData, MemFlagsData, TrapCode, Value,
24 ValueList, immediates::*, types::*,
25 },
26 isa::aarch64::abi::AArch64MachineDeps,
27 isa::aarch64::inst::SImm7Scaled,
28 isa::aarch64::inst::args::{AtomicCAS128Args, ShiftOp, ShiftOpShiftImm},
29 machinst::{
30 CallArgList, CallRetList, InstOutput, MachInst, VCodeConstant, VCodeConstantData,
31 abi::ArgPair, ty_bits,
32 },
33};
34use alloc::boxed::Box;
35use alloc::vec::Vec;
36use regalloc2::PReg;
37
38type BoxCallInfo = Box<CallInfo<ExternalName>>;
39type BoxCallIndInfo = Box<CallInfo<Reg>>;
40type BoxReturnCallInfo = Box<ReturnCallInfo<ExternalName>>;
41type BoxReturnCallIndInfo = Box<ReturnCallInfo<Reg>>;
42type VecMachLabel = Vec<MachLabel>;
43type BoxExternalName = Box<ExternalName>;
44type VecArgPair = Vec<ArgPair>;
45type BoxAtomicCAS128Args = Box<AtomicCAS128Args>;
46
47pub(crate) fn lower(
49 lower_ctx: &mut Lower<MInst>,
50 backend: &AArch64Backend,
51 inst: Inst,
52) -> Option<InstOutput> {
53 let mut isle_ctx = IsleContext { lower_ctx, backend };
56 generated_code::constructor_lower(&mut isle_ctx, inst)
57}
58
59pub(crate) fn lower_branch(
60 lower_ctx: &mut Lower<MInst>,
61 backend: &AArch64Backend,
62 branch: Inst,
63 targets: &[MachLabel],
64) -> Option<()> {
65 let mut isle_ctx = IsleContext { lower_ctx, backend };
68 generated_code::constructor_lower_branch(&mut isle_ctx, branch, targets)
69}
70
71pub struct ExtendedValue {
72 val: Value,
73 extend: ExtendOp,
74}
75
76impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
77 isle_lower_prelude_methods!();
78
79 fn gen_call_info(
80 &mut self,
81 sig: Sig,
82 dest: ExternalName,
83 uses: CallArgList,
84 defs: CallRetList,
85 try_call_info: Option<TryCallInfo>,
86 patchable: bool,
87 ) -> BoxCallInfo {
88 let stack_ret_space = self.lower_ctx.sigs()[sig].sized_stack_ret_space();
89 let stack_arg_space = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
90 self.lower_ctx
91 .abi_mut()
92 .accumulate_outgoing_args_size(stack_ret_space + stack_arg_space);
93
94 Box::new(
95 self.lower_ctx
96 .gen_call_info(sig, dest, uses, defs, try_call_info, patchable),
97 )
98 }
99
100 fn gen_call_ind_info(
101 &mut self,
102 sig: Sig,
103 dest: Reg,
104 uses: CallArgList,
105 defs: CallRetList,
106 try_call_info: Option<TryCallInfo>,
107 ) -> BoxCallIndInfo {
108 let stack_ret_space = self.lower_ctx.sigs()[sig].sized_stack_ret_space();
109 let stack_arg_space = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
110 self.lower_ctx
111 .abi_mut()
112 .accumulate_outgoing_args_size(stack_ret_space + stack_arg_space);
113
114 Box::new(
115 self.lower_ctx
116 .gen_call_info(sig, dest, uses, defs, try_call_info, false),
117 )
118 }
119
120 fn gen_return_call_info(
121 &mut self,
122 sig: Sig,
123 dest: ExternalName,
124 uses: CallArgList,
125 ) -> BoxReturnCallInfo {
126 let new_stack_arg_size = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
127 self.lower_ctx
128 .abi_mut()
129 .accumulate_tail_args_size(new_stack_arg_size);
130
131 let key =
132 AArch64MachineDeps::select_api_key(&self.backend.isa_flags, isa::CallConv::Tail, true);
133
134 Box::new(ReturnCallInfo {
135 dest,
136 uses,
137 key,
138 sign_return_address_all: self.backend.isa_flags.sign_return_address_all(),
139 new_stack_arg_size,
140 })
141 }
142
143 fn gen_return_call_ind_info(
144 &mut self,
145 sig: Sig,
146 dest: Reg,
147 uses: CallArgList,
148 ) -> BoxReturnCallIndInfo {
149 let new_stack_arg_size = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
150 self.lower_ctx
151 .abi_mut()
152 .accumulate_tail_args_size(new_stack_arg_size);
153
154 let key =
155 AArch64MachineDeps::select_api_key(&self.backend.isa_flags, isa::CallConv::Tail, true);
156
157 Box::new(ReturnCallInfo {
158 dest,
159 uses,
160 key,
161 sign_return_address_all: self.backend.isa_flags.sign_return_address_all(),
162 new_stack_arg_size,
163 })
164 }
165
166 fn sign_return_address_disabled(&mut self) -> Option<()> {
167 if self.backend.isa_flags.sign_return_address() {
168 None
169 } else {
170 Some(())
171 }
172 }
173
174 fn use_lse(&mut self, _: Inst) -> Option<()> {
175 if self.backend.isa_flags.has_lse() {
176 Some(())
177 } else {
178 None
179 }
180 }
181
182 fn atomic_cas_128_args(
183 &mut self,
184 rd_lo: WritableReg,
185 rd_hi: WritableReg,
186 rs_lo: Reg,
187 rs_hi: Reg,
188 rt_lo: Reg,
189 rt_hi: Reg,
190 rn: Reg,
191 flags: MemFlagsData,
192 ) -> BoxAtomicCAS128Args {
193 Box::new(AtomicCAS128Args {
194 rd_lo,
195 rd_hi,
196 rs_lo,
197 rs_hi,
198 rt_lo,
199 rt_hi,
200 rn,
201 flags,
202 })
203 }
204
205 fn use_dotprod(&mut self, _: Inst) -> Option<()> {
206 if self.backend.isa_flags.has_dotprod() {
207 Some(())
208 } else {
209 None
210 }
211 }
212
213 fn use_i8mm(&mut self, _: Inst) -> Option<()> {
214 if self.backend.isa_flags.has_i8mm() {
215 Some(())
216 } else {
217 None
218 }
219 }
220
221 fn use_fp16(&mut self) -> bool {
222 self.backend.isa_flags.has_fp16()
223 }
224
225 fn use_csdb(&mut self) -> bool {
226 self.backend.isa_flags.use_csdb()
227 }
228
229 fn move_wide_const_from_u64(&mut self, ty: Type, n: u64) -> Option<MoveWideConst> {
230 let bits = ty.bits();
231 let n = if bits < 64 {
232 n & !(u64::MAX << bits)
233 } else {
234 n
235 };
236 MoveWideConst::maybe_from_u64(n)
237 }
238
239 fn move_wide_const_from_inverted_u64(&mut self, ty: Type, n: u64) -> Option<MoveWideConst> {
240 self.move_wide_const_from_u64(ty, !n)
241 }
242
243 fn imm_logic_from_u64(&mut self, ty: Type, n: u64) -> Option<ImmLogic> {
244 ImmLogic::maybe_from_u64(n, ty)
245 }
246
247 fn imm_size_from_type(&mut self, ty: Type) -> Option<u16> {
248 match ty {
249 I32 => Some(32),
250 I64 => Some(64),
251 _ => None,
252 }
253 }
254
255 fn imm_logic_from_imm64(&mut self, ty: Type, n: Imm64) -> Option<ImmLogic> {
256 let ty = if ty.bits() < 32 { I32 } else { ty };
257 self.imm_logic_from_u64(ty, n.bits() as u64)
258 }
259
260 fn imm12_from_u64(&mut self, n: u64) -> Option<Imm12> {
261 Imm12::maybe_from_u64(n)
262 }
263
264 fn imm_shift_from_u8(&mut self, n: u8) -> ImmShift {
265 ImmShift::maybe_from_u64(n.into()).unwrap()
266 }
267
268 fn bfm_immr(&mut self, ty: Type, a: u64, b: u64) -> UImm6 {
271 let w = ty.lane_bits() as u8;
272 debug_assert!(w <= 64);
273
274 let a = (a as u8) & (w - 1);
275 let b = (b as u8) & (w - 1);
276 let result = if a <= b { b - a } else { w - (a - b) };
277 UImm6::maybe_from_u8(result).expect("result is always less than 64")
278 }
279
280 fn bfm_imms(&mut self, ty: Type, a: u64, _b: u64) -> UImm6 {
283 let w = ty.lane_bits() as u8;
284 debug_assert!(w <= 64);
285
286 let a = (a as u8) & (w - 1);
287 let result = w - 1 - (a & (w - 1));
288 UImm6::maybe_from_u8(result).expect("result is always less than 64")
289 }
290
291 fn lshr_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt> {
292 let shiftimm = ShiftOpShiftImm::maybe_from_shift(n)?;
293 if let Ok(bits) = u8::try_from(ty_bits(ty)) {
294 let shiftimm = shiftimm.mask(bits);
295 Some(ShiftOpAndAmt::new(ShiftOp::LSR, shiftimm))
296 } else {
297 None
298 }
299 }
300
301 fn lshl_from_imm64(&mut self, ty: Type, n: Imm64) -> Option<ShiftOpAndAmt> {
302 self.lshl_from_u64(ty, n.bits() as u64)
303 }
304
305 fn lshl_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt> {
306 let shiftimm = ShiftOpShiftImm::maybe_from_shift(n)?;
307 let shiftee_bits = ty_bits(ty);
308 if shiftee_bits <= u8::MAX as usize {
309 let shiftimm = shiftimm.mask(shiftee_bits as u8);
310 Some(ShiftOpAndAmt::new(ShiftOp::LSL, shiftimm))
311 } else {
312 None
313 }
314 }
315
316 fn ashr_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt> {
317 let shiftimm = ShiftOpShiftImm::maybe_from_shift(n)?;
318 let shiftee_bits = ty_bits(ty);
319 if shiftee_bits <= u8::MAX as usize {
320 let shiftimm = shiftimm.mask(shiftee_bits as u8);
321 Some(ShiftOpAndAmt::new(ShiftOp::ASR, shiftimm))
322 } else {
323 None
324 }
325 }
326
327 fn integral_ty(&mut self, ty: Type) -> Option<Type> {
328 match ty {
329 I8 | I16 | I32 | I64 => Some(ty),
330 _ => None,
331 }
332 }
333
334 fn is_zero_simm9(&mut self, imm: &SImm9) -> Option<()> {
335 if imm.value() == 0 { Some(()) } else { None }
336 }
337
338 fn is_zero_uimm12(&mut self, imm: &UImm12Scaled) -> Option<()> {
339 if imm.value() == 0 { Some(()) } else { None }
340 }
341
342 fn valid_atomic_transaction(&mut self, ty: Type) -> Option<Type> {
344 match ty {
345 I8 | I16 | I32 | I64 => Some(ty),
346 _ => None,
347 }
348 }
349
350 fn load_constant_full(
356 &mut self,
357 ty: Type,
358 extend: &ImmExtend,
359 extend_to: &OperandSize,
360 value: u64,
361 ) -> Reg {
362 let bits = ty.bits();
363
364 let value = match (extend_to, *extend) {
365 (OperandSize::Size32, ImmExtend::Sign) if bits < 32 => {
366 let shift = 32 - bits;
367 let value = value as i32;
368
369 ((value << shift) >> shift) as u32 as u64
373 }
374 (OperandSize::Size32, ImmExtend::Zero) if bits < 32 => {
375 value & !((u32::MAX as u64) << bits)
376 }
377 (OperandSize::Size64, ImmExtend::Sign) if bits < 64 => {
378 let shift = 64 - bits;
379 let value = value as i64;
380
381 ((value << shift) >> shift) as u64
382 }
383 (OperandSize::Size64, ImmExtend::Zero) if bits < 64 => value & !(u64::MAX << bits),
384 _ => value,
385 };
386
387 fn get(value: u64, shift: u8) -> u16 {
390 (value >> (shift * 16)) as u16
391 }
392 fn replace(mut old: u64, new: u16, shift: u8) -> u64 {
393 let offset = shift * 16;
394 old &= !(0xffff << offset);
395 old |= u64::from(new) << offset;
396 old
397 }
398
399 let size = if value >> 32 == 0 {
404 OperandSize::Size32
405 } else {
406 OperandSize::Size64
407 };
408
409 let (mut running_value, op, first) =
417 [(MoveWideOp::MovZ, 0), (MoveWideOp::MovN, size.max_value())]
418 .into_iter()
419 .map(|(op, base)| {
420 let first = (0..(size.bits() / 16))
424 .find(|&i| get(base ^ value, i) != 0)
426 .unwrap_or(0);
428 (replace(base, get(value, first), first), op, first)
430 })
431 .min_by_key(|(base, ..)| (0..4).filter(|&i| get(base ^ value, i) != 0).count())
433 .unwrap();
435
436 let mut rd = self.temp_writable_reg(I64);
440 self.lower_ctx.emit(MInst::MovWide {
441 op,
442 rd,
443 imm: MoveWideConst {
444 bits: match op {
445 MoveWideOp::MovZ => get(value, first),
446 MoveWideOp::MovN => !get(value, first),
447 },
448 shift: first,
449 },
450 size,
451 });
452
453 for shift in (first + 1)..(size.bits() / 16) {
456 let bits = get(value, shift);
457 if bits != get(running_value, shift) {
458 let rn = rd.to_reg();
459 rd = self.temp_writable_reg(I64);
460 self.lower_ctx.emit(MInst::MovK {
461 rd,
462 rn,
463 imm: MoveWideConst { bits, shift },
464 size,
465 });
466 running_value = replace(running_value, bits, shift);
467 }
468 }
469
470 debug_assert_eq!(value, running_value);
471 return rd.to_reg();
472 }
473
474 fn zero_reg(&mut self) -> Reg {
475 zero_reg()
476 }
477
478 fn stack_reg(&mut self) -> Reg {
479 stack_reg()
480 }
481
482 fn fp_reg(&mut self) -> Reg {
483 fp_reg()
484 }
485
486 fn writable_link_reg(&mut self) -> WritableReg {
487 writable_link_reg()
488 }
489
490 fn extended_value_from_value(&mut self, val: Value) -> Option<ExtendedValue> {
491 let (val, extend) = super::get_as_extended_value(self.lower_ctx, val)?;
492 Some(ExtendedValue { val, extend })
493 }
494
495 fn put_extended_in_reg(&mut self, reg: &ExtendedValue) -> Reg {
496 self.put_in_reg(reg.val)
497 }
498
499 fn get_extended_op(&mut self, reg: &ExtendedValue) -> ExtendOp {
500 reg.extend
501 }
502
503 fn emit(&mut self, inst: &MInst) -> Unit {
504 self.lower_ctx.emit(inst.clone());
505 }
506
507 fn cond_br_zero(&mut self, reg: Reg, size: &OperandSize) -> CondBrKind {
508 CondBrKind::Zero(reg, *size)
509 }
510
511 fn cond_br_not_zero(&mut self, reg: Reg, size: &OperandSize) -> CondBrKind {
512 CondBrKind::NotZero(reg, *size)
513 }
514
515 fn cond_br_cond(&mut self, cond: &Cond) -> CondBrKind {
516 CondBrKind::Cond(*cond)
517 }
518
519 fn nzcv(&mut self, n: bool, z: bool, c: bool, v: bool) -> NZCV {
520 NZCV::new(n, z, c, v)
521 }
522
523 fn u8_into_uimm5(&mut self, x: u8) -> UImm5 {
524 UImm5::maybe_from_u8(x).unwrap()
525 }
526
527 fn u8_into_imm12(&mut self, x: u8) -> Imm12 {
528 Imm12::maybe_from_u64(x.into()).unwrap()
529 }
530
531 fn writable_zero_reg(&mut self) -> WritableReg {
532 writable_zero_reg()
533 }
534
535 fn shift_mask(&mut self, ty: Type) -> ImmLogic {
536 debug_assert!(ty.lane_bits().is_power_of_two());
537
538 let mask = (ty.lane_bits() - 1) as u64;
539 ImmLogic::maybe_from_u64(mask, I32).unwrap()
540 }
541
542 fn imm_shift_from_imm64(&mut self, ty: Type, val: Imm64) -> Option<ImmShift> {
543 let imm_value = (val.bits() as u64) & ((ty.bits() - 1) as u64);
544 ImmShift::maybe_from_u64(imm_value)
545 }
546
547 fn u64_into_imm_logic(&mut self, ty: Type, val: u64) -> ImmLogic {
548 ImmLogic::maybe_from_u64(val, ty).unwrap()
549 }
550
551 fn negate_imm_shift(&mut self, ty: Type, mut imm: ImmShift) -> ImmShift {
552 let size = u8::try_from(ty.bits()).unwrap();
553 imm.imm = size.wrapping_sub(imm.value());
554 imm.imm &= size - 1;
555 imm
556 }
557
558 fn rotr_mask(&mut self, ty: Type) -> ImmLogic {
559 ImmLogic::maybe_from_u64((ty.bits() - 1) as u64, I32).unwrap()
560 }
561
562 fn rotr_opposite_amount(&mut self, ty: Type, val: ImmShift) -> ImmShift {
563 let amount = val.value() & u8::try_from(ty.bits() - 1).unwrap();
564 ImmShift::maybe_from_u64(u64::from(ty.bits()) - u64::from(amount)).unwrap()
565 }
566
567 fn icmp_zero_cond(&mut self, cond: &IntCC) -> Option<IntCC> {
568 match cond {
569 &IntCC::Equal
570 | &IntCC::SignedGreaterThanOrEqual
571 | &IntCC::SignedGreaterThan
572 | &IntCC::SignedLessThanOrEqual
573 | &IntCC::SignedLessThan => Some(*cond),
574 _ => None,
575 }
576 }
577
578 fn fcmp_zero_cond(&mut self, cond: &FloatCC) -> Option<FloatCC> {
579 match cond {
580 &FloatCC::Equal
581 | &FloatCC::GreaterThanOrEqual
582 | &FloatCC::GreaterThan
583 | &FloatCC::LessThanOrEqual
584 | &FloatCC::LessThan => Some(*cond),
585 _ => None,
586 }
587 }
588
589 fn fcmp_zero_cond_not_eq(&mut self, cond: &FloatCC) -> Option<FloatCC> {
590 match cond {
591 &FloatCC::NotEqual => Some(FloatCC::NotEqual),
592 _ => None,
593 }
594 }
595
596 fn icmp_zero_cond_not_eq(&mut self, cond: &IntCC) -> Option<IntCC> {
597 match cond {
598 &IntCC::NotEqual => Some(IntCC::NotEqual),
599 _ => None,
600 }
601 }
602
603 fn float_cc_cmp_zero_to_vec_misc_op(&mut self, cond: &FloatCC) -> VecMisc2 {
604 match cond {
605 &FloatCC::Equal => VecMisc2::Fcmeq0,
606 &FloatCC::GreaterThanOrEqual => VecMisc2::Fcmge0,
607 &FloatCC::LessThanOrEqual => VecMisc2::Fcmle0,
608 &FloatCC::GreaterThan => VecMisc2::Fcmgt0,
609 &FloatCC::LessThan => VecMisc2::Fcmlt0,
610 _ => panic!(),
611 }
612 }
613
614 fn int_cc_cmp_zero_to_vec_misc_op(&mut self, cond: &IntCC) -> VecMisc2 {
615 match cond {
616 &IntCC::Equal => VecMisc2::Cmeq0,
617 &IntCC::SignedGreaterThanOrEqual => VecMisc2::Cmge0,
618 &IntCC::SignedLessThanOrEqual => VecMisc2::Cmle0,
619 &IntCC::SignedGreaterThan => VecMisc2::Cmgt0,
620 &IntCC::SignedLessThan => VecMisc2::Cmlt0,
621 _ => panic!(),
622 }
623 }
624
625 fn float_cc_cmp_zero_to_vec_misc_op_swap(&mut self, cond: &FloatCC) -> VecMisc2 {
626 match cond {
627 &FloatCC::Equal => VecMisc2::Fcmeq0,
628 &FloatCC::GreaterThanOrEqual => VecMisc2::Fcmle0,
629 &FloatCC::LessThanOrEqual => VecMisc2::Fcmge0,
630 &FloatCC::GreaterThan => VecMisc2::Fcmlt0,
631 &FloatCC::LessThan => VecMisc2::Fcmgt0,
632 _ => panic!(),
633 }
634 }
635
636 fn int_cc_cmp_zero_to_vec_misc_op_swap(&mut self, cond: &IntCC) -> VecMisc2 {
637 match cond {
638 &IntCC::Equal => VecMisc2::Cmeq0,
639 &IntCC::SignedGreaterThanOrEqual => VecMisc2::Cmle0,
640 &IntCC::SignedLessThanOrEqual => VecMisc2::Cmge0,
641 &IntCC::SignedGreaterThan => VecMisc2::Cmlt0,
642 &IntCC::SignedLessThan => VecMisc2::Cmgt0,
643 _ => panic!(),
644 }
645 }
646
647 fn cond_code(&mut self, cc: &condcodes::IntCC) -> Cond {
648 lower_condcode(*cc)
649 }
650
651 fn invert_cond(&mut self, cond: &Cond) -> Cond {
652 (*cond).invert()
653 }
654 fn preg_sp(&mut self) -> PReg {
655 super::regs::stack_reg().to_real_reg().unwrap().into()
656 }
657
658 fn preg_fp(&mut self) -> PReg {
659 super::regs::fp_reg().to_real_reg().unwrap().into()
660 }
661
662 fn preg_link(&mut self) -> PReg {
663 super::regs::link_reg().to_real_reg().unwrap().into()
664 }
665
666 fn preg_pinned(&mut self) -> PReg {
667 super::regs::pinned_reg().to_real_reg().unwrap().into()
668 }
669
670 fn branch_target(&mut self, label: MachLabel) -> BranchTarget {
671 BranchTarget::Label(label)
672 }
673
674 fn targets_jt_space(&mut self, elements: &BoxVecMachLabel) -> CodeOffset {
675 (4 * (8 + elements.len())).try_into().unwrap()
679 }
680
681 fn min_fp_value(&mut self, signed: bool, in_bits: u8, out_bits: u8) -> Reg {
682 if in_bits == 32 {
683 let min = match (signed, out_bits) {
685 (true, 8) => i8::MIN as f32 - 1.,
686 (true, 16) => i16::MIN as f32 - 1.,
687 (true, 32) => i32::MIN as f32, (true, 64) => i64::MIN as f32, (false, _) => -1.,
691 _ => unimplemented!(
692 "unexpected {} output size of {} bits for 32-bit input",
693 if signed { "signed" } else { "unsigned" },
694 out_bits
695 ),
696 };
697
698 generated_code::constructor_constant_f32(self, min.to_bits())
699 } else if in_bits == 64 {
700 let min = match (signed, out_bits) {
702 (true, 8) => i8::MIN as f64 - 1.,
703 (true, 16) => i16::MIN as f64 - 1.,
704 (true, 32) => i32::MIN as f64 - 1.,
705 (true, 64) => i64::MIN as f64,
706
707 (false, _) => -1.,
708 _ => unimplemented!(
709 "unexpected {} output size of {} bits for 64-bit input",
710 if signed { "signed" } else { "unsigned" },
711 out_bits
712 ),
713 };
714
715 generated_code::constructor_constant_f64(self, min.to_bits())
716 } else {
717 unimplemented!(
718 "unexpected input size for min_fp_value: {} (signed: {}, output size: {})",
719 in_bits,
720 signed,
721 out_bits
722 );
723 }
724 }
725
726 fn max_fp_value(&mut self, signed: bool, in_bits: u8, out_bits: u8) -> Reg {
727 if in_bits == 32 {
728 let max = match (signed, out_bits) {
730 (true, 8) => i8::MAX as f32 + 1.,
731 (true, 16) => i16::MAX as f32 + 1.,
732 (true, 32) => (i32::MAX as u64 + 1) as f32,
733 (true, 64) => (i64::MAX as u64 + 1) as f32,
734
735 (false, 8) => u8::MAX as f32 + 1.,
736 (false, 16) => u16::MAX as f32 + 1.,
737 (false, 32) => (u32::MAX as u64 + 1) as f32,
738 (false, 64) => (u64::MAX as u128 + 1) as f32,
739 _ => unimplemented!(
740 "unexpected {} output size of {} bits for 32-bit input",
741 if signed { "signed" } else { "unsigned" },
742 out_bits
743 ),
744 };
745
746 generated_code::constructor_constant_f32(self, max.to_bits())
747 } else if in_bits == 64 {
748 let max = match (signed, out_bits) {
750 (true, 8) => i8::MAX as f64 + 1.,
751 (true, 16) => i16::MAX as f64 + 1.,
752 (true, 32) => i32::MAX as f64 + 1.,
753 (true, 64) => (i64::MAX as u64 + 1) as f64,
754
755 (false, 8) => u8::MAX as f64 + 1.,
756 (false, 16) => u16::MAX as f64 + 1.,
757 (false, 32) => u32::MAX as f64 + 1.,
758 (false, 64) => (u64::MAX as u128 + 1) as f64,
759 _ => unimplemented!(
760 "unexpected {} output size of {} bits for 64-bit input",
761 if signed { "signed" } else { "unsigned" },
762 out_bits
763 ),
764 };
765
766 generated_code::constructor_constant_f64(self, max.to_bits())
767 } else {
768 unimplemented!(
769 "unexpected input size for max_fp_value: {} (signed: {}, output size: {})",
770 in_bits,
771 signed,
772 out_bits
773 );
774 }
775 }
776
777 fn fpu_op_ri_ushr(&mut self, ty_bits: u8, shift: u8) -> FPUOpRI {
778 if ty_bits == 32 {
779 FPUOpRI::UShr32(FPURightShiftImm::maybe_from_u8(shift, ty_bits).unwrap())
780 } else if ty_bits == 64 {
781 FPUOpRI::UShr64(FPURightShiftImm::maybe_from_u8(shift, ty_bits).unwrap())
782 } else {
783 unimplemented!(
784 "unexpected input size for fpu_op_ri_ushr: {} (shift: {})",
785 ty_bits,
786 shift
787 );
788 }
789 }
790
791 fn fpu_op_ri_sli(&mut self, ty_bits: u8, shift: u8) -> FPUOpRIMod {
792 if ty_bits == 32 {
793 FPUOpRIMod::Sli32(FPULeftShiftImm::maybe_from_u8(shift, ty_bits).unwrap())
794 } else if ty_bits == 64 {
795 FPUOpRIMod::Sli64(FPULeftShiftImm::maybe_from_u8(shift, ty_bits).unwrap())
796 } else {
797 unimplemented!(
798 "unexpected input size for fpu_op_ri_sli: {} (shift: {})",
799 ty_bits,
800 shift
801 );
802 }
803 }
804
805 fn vec_extract_imm4_from_immediate(&mut self, imm: Immediate) -> Option<u8> {
806 let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
807
808 if bytes.array_windows().all(|[a, b]| *a + 1 == *b) && bytes[0] < 16 {
809 Some(bytes[0])
810 } else {
811 None
812 }
813 }
814
815 fn shuffle_dup8_from_imm(&mut self, imm: Immediate) -> Option<u8> {
816 let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
817 if bytes.iter().all(|b| *b == bytes[0]) && bytes[0] < 16 {
818 Some(bytes[0])
819 } else {
820 None
821 }
822 }
823 fn shuffle_dup16_from_imm(&mut self, imm: Immediate) -> Option<u8> {
824 let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
825 if a == b && b == c && c == d && d == e && e == f && f == g && g == h && a < 8 {
826 Some(a)
827 } else {
828 None
829 }
830 }
831 fn shuffle_dup32_from_imm(&mut self, imm: Immediate) -> Option<u8> {
832 let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
833 if a == b && b == c && c == d && a < 4 {
834 Some(a)
835 } else {
836 None
837 }
838 }
839 fn shuffle_dup64_from_imm(&mut self, imm: Immediate) -> Option<u8> {
840 let (a, b) = self.shuffle64_from_imm(imm)?;
841 if a == b && a < 2 { Some(a) } else { None }
842 }
843
844 fn asimd_mov_mod_imm_zero(&mut self, size: &ScalarSize) -> ASIMDMovModImm {
845 ASIMDMovModImm::zero(*size)
846 }
847
848 fn asimd_mov_mod_imm_from_u64(
849 &mut self,
850 val: u64,
851 size: &ScalarSize,
852 ) -> Option<ASIMDMovModImm> {
853 ASIMDMovModImm::maybe_from_u64(val, *size)
854 }
855
856 fn asimd_fp_mod_imm_from_u64(&mut self, val: u64, size: &ScalarSize) -> Option<ASIMDFPModImm> {
857 ASIMDFPModImm::maybe_from_u64(val, *size)
858 }
859
860 fn u64_low32_bits_unset(&mut self, val: u64) -> Option<u64> {
861 if val & 0xffffffff == 0 {
862 Some(val)
863 } else {
864 None
865 }
866 }
867
868 fn shift_masked_imm(&mut self, ty: Type, imm: u64) -> u8 {
869 (imm as u8) & ((ty.lane_bits() - 1) as u8)
870 }
871
872 fn simm7_scaled_from_i64(&mut self, val: i64, ty: Type) -> Option<SImm7Scaled> {
873 SImm7Scaled::maybe_from_i64(val, ty)
874 }
875
876 fn simm9_from_i64(&mut self, val: i64) -> Option<SImm9> {
877 SImm9::maybe_from_i64(val)
878 }
879
880 fn uimm12_scaled_from_i64(&mut self, val: i64, ty: Type) -> Option<UImm12Scaled> {
881 UImm12Scaled::maybe_from_i64(val, ty)
882 }
883
884 fn uimm12_scaled_nonzero_from_i64(&mut self, val: i64, ty: Type) -> Option<UImm12Scaled> {
887 if val == 0 {
888 return None;
889 }
890 UImm12Scaled::maybe_from_i64(val, ty)
891 }
892
893 fn test_and_compare_bit_const(&mut self, ty: Type, n: u64) -> Option<u8> {
894 if n.count_ones() != 1 {
895 return None;
896 }
897 let bit = n.trailing_zeros();
898 if bit >= ty.bits() {
899 return None;
900 }
901 Some(bit as u8)
902 }
903
904 fn a64_extr_imm(&mut self, ty: Type, shift: ImmShift) -> ShiftOpAndAmt {
906 let (op, expected) = match ty {
911 types::I32 => (ShiftOp::LSL, 0b00),
912 types::I64 => (ShiftOp::LSR, 0b01),
913 _ => unreachable!(),
914 };
915 assert_eq!(op.bits(), expected);
916 ShiftOpAndAmt::new(
917 op,
918 ShiftOpShiftImm::maybe_from_shift(shift.value().into()).unwrap(),
919 )
920 }
921
922 fn is_pic(&mut self) -> bool {
923 self.backend.flags.is_pic()
924 }
925}