1 //! ISLE integration glue code for aarch64 lowering.
2 
3 // Pull in the ISLE generated code.
4 pub mod generated_code;
5 use generated_code::{Context, ImmExtend};
6 
7 // Types that the generated ISLE code uses via `use super::*`.
8 use 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, UImm12Scaled, VecMisc2, VectorSize, fp_reg, lower_condcode, lower_fp_condcode,
13     stack_reg, writable_link_reg, writable_zero_reg, zero_reg,
14 };
15 use crate::ir::{ArgumentExtension, condcodes};
16 use crate::isa;
17 use crate::isa::aarch64::AArch64Backend;
18 use crate::isa::aarch64::inst::{FPULeftShiftImm, FPURightShiftImm, ReturnCallInfo};
19 use crate::machinst::isle::*;
20 use crate::{
21     binemit::CodeOffset,
22     ir::{
23         AtomicRmwOp, BlockCall, ExternalName, Inst, InstructionData, MemFlags, TrapCode, Value,
24         ValueList, immediates::*, types::*,
25     },
26     isa::aarch64::abi::AArch64MachineDeps,
27     isa::aarch64::inst::SImm7Scaled,
28     isa::aarch64::inst::args::{ShiftOp, ShiftOpShiftImm},
29     machinst::{
30         CallArgList, CallRetList, InstOutput, MachInst, VCodeConstant, VCodeConstantData,
31         abi::ArgPair, ty_bits,
32     },
33 };
34 use alloc::boxed::Box;
35 use alloc::vec::Vec;
36 use core::u32;
37 use regalloc2::PReg;
38 
39 type BoxCallInfo = Box<CallInfo<ExternalName>>;
40 type BoxCallIndInfo = Box<CallInfo<Reg>>;
41 type BoxReturnCallInfo = Box<ReturnCallInfo<ExternalName>>;
42 type BoxReturnCallIndInfo = Box<ReturnCallInfo<Reg>>;
43 type VecMachLabel = Vec<MachLabel>;
44 type BoxExternalName = Box<ExternalName>;
45 type VecArgPair = Vec<ArgPair>;
46 
47 /// The main entry point for lowering with ISLE.
lower( lower_ctx: &mut Lower<MInst>, backend: &AArch64Backend, inst: Inst, ) -> Option<InstOutput>48 pub(crate) fn lower(
49     lower_ctx: &mut Lower<MInst>,
50     backend: &AArch64Backend,
51     inst: Inst,
52 ) -> Option<InstOutput> {
53     // TODO: reuse the ISLE context across lowerings so we can reuse its
54     // internal heap allocations.
55     let mut isle_ctx = IsleContext { lower_ctx, backend };
56     generated_code::constructor_lower(&mut isle_ctx, inst)
57 }
58 
lower_branch( lower_ctx: &mut Lower<MInst>, backend: &AArch64Backend, branch: Inst, targets: &[MachLabel], ) -> Option<()>59 pub(crate) fn lower_branch(
60     lower_ctx: &mut Lower<MInst>,
61     backend: &AArch64Backend,
62     branch: Inst,
63     targets: &[MachLabel],
64 ) -> Option<()> {
65     // TODO: reuse the ISLE context across lowerings so we can reuse its
66     // internal heap allocations.
67     let mut isle_ctx = IsleContext { lower_ctx, backend };
68     generated_code::constructor_lower_branch(&mut isle_ctx, branch, targets)
69 }
70 
71 pub struct ExtendedValue {
72     val: Value,
73     extend: ExtendOp,
74 }
75 
76 impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
77     isle_lower_prelude_methods!();
78 
gen_call_info( &mut self, sig: Sig, dest: ExternalName, uses: CallArgList, defs: CallRetList, try_call_info: Option<TryCallInfo>, patchable: bool, ) -> BoxCallInfo79     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 
gen_call_ind_info( &mut self, sig: Sig, dest: Reg, uses: CallArgList, defs: CallRetList, try_call_info: Option<TryCallInfo>, ) -> BoxCallIndInfo100     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 
gen_return_call_info( &mut self, sig: Sig, dest: ExternalName, uses: CallArgList, ) -> BoxReturnCallInfo120     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             new_stack_arg_size,
139         })
140     }
141 
gen_return_call_ind_info( &mut self, sig: Sig, dest: Reg, uses: CallArgList, ) -> BoxReturnCallIndInfo142     fn gen_return_call_ind_info(
143         &mut self,
144         sig: Sig,
145         dest: Reg,
146         uses: CallArgList,
147     ) -> BoxReturnCallIndInfo {
148         let new_stack_arg_size = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
149         self.lower_ctx
150             .abi_mut()
151             .accumulate_tail_args_size(new_stack_arg_size);
152 
153         let key =
154             AArch64MachineDeps::select_api_key(&self.backend.isa_flags, isa::CallConv::Tail, true);
155 
156         Box::new(ReturnCallInfo {
157             dest,
158             uses,
159             key,
160             new_stack_arg_size,
161         })
162     }
163 
sign_return_address_disabled(&mut self) -> Option<()>164     fn sign_return_address_disabled(&mut self) -> Option<()> {
165         if self.backend.isa_flags.sign_return_address() {
166             None
167         } else {
168             Some(())
169         }
170     }
171 
use_lse(&mut self, _: Inst) -> Option<()>172     fn use_lse(&mut self, _: Inst) -> Option<()> {
173         if self.backend.isa_flags.has_lse() {
174             Some(())
175         } else {
176             None
177         }
178     }
179 
use_fp16(&mut self) -> bool180     fn use_fp16(&mut self) -> bool {
181         self.backend.isa_flags.has_fp16()
182     }
183 
use_csdb(&mut self) -> bool184     fn use_csdb(&mut self) -> bool {
185         self.backend.isa_flags.use_csdb()
186     }
187 
move_wide_const_from_u64(&mut self, ty: Type, n: u64) -> Option<MoveWideConst>188     fn move_wide_const_from_u64(&mut self, ty: Type, n: u64) -> Option<MoveWideConst> {
189         let bits = ty.bits();
190         let n = if bits < 64 {
191             n & !(u64::MAX << bits)
192         } else {
193             n
194         };
195         MoveWideConst::maybe_from_u64(n)
196     }
197 
move_wide_const_from_inverted_u64(&mut self, ty: Type, n: u64) -> Option<MoveWideConst>198     fn move_wide_const_from_inverted_u64(&mut self, ty: Type, n: u64) -> Option<MoveWideConst> {
199         self.move_wide_const_from_u64(ty, !n)
200     }
201 
imm_logic_from_u64(&mut self, ty: Type, n: u64) -> Option<ImmLogic>202     fn imm_logic_from_u64(&mut self, ty: Type, n: u64) -> Option<ImmLogic> {
203         ImmLogic::maybe_from_u64(n, ty)
204     }
205 
imm_size_from_type(&mut self, ty: Type) -> Option<u16>206     fn imm_size_from_type(&mut self, ty: Type) -> Option<u16> {
207         match ty {
208             I32 => Some(32),
209             I64 => Some(64),
210             _ => None,
211         }
212     }
213 
imm_logic_from_imm64(&mut self, ty: Type, n: Imm64) -> Option<ImmLogic>214     fn imm_logic_from_imm64(&mut self, ty: Type, n: Imm64) -> Option<ImmLogic> {
215         let ty = if ty.bits() < 32 { I32 } else { ty };
216         self.imm_logic_from_u64(ty, n.bits() as u64)
217     }
218 
imm12_from_u64(&mut self, n: u64) -> Option<Imm12>219     fn imm12_from_u64(&mut self, n: u64) -> Option<Imm12> {
220         Imm12::maybe_from_u64(n)
221     }
222 
imm_shift_from_u8(&mut self, n: u8) -> ImmShift223     fn imm_shift_from_u8(&mut self, n: u8) -> ImmShift {
224         ImmShift::maybe_from_u64(n.into()).unwrap()
225     }
226 
lshr_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt>227     fn lshr_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt> {
228         let shiftimm = ShiftOpShiftImm::maybe_from_shift(n)?;
229         if let Ok(bits) = u8::try_from(ty_bits(ty)) {
230             let shiftimm = shiftimm.mask(bits);
231             Some(ShiftOpAndAmt::new(ShiftOp::LSR, shiftimm))
232         } else {
233             None
234         }
235     }
236 
lshl_from_imm64(&mut self, ty: Type, n: Imm64) -> Option<ShiftOpAndAmt>237     fn lshl_from_imm64(&mut self, ty: Type, n: Imm64) -> Option<ShiftOpAndAmt> {
238         self.lshl_from_u64(ty, n.bits() as u64)
239     }
240 
lshl_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt>241     fn lshl_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt> {
242         let shiftimm = ShiftOpShiftImm::maybe_from_shift(n)?;
243         let shiftee_bits = ty_bits(ty);
244         if shiftee_bits <= core::u8::MAX as usize {
245             let shiftimm = shiftimm.mask(shiftee_bits as u8);
246             Some(ShiftOpAndAmt::new(ShiftOp::LSL, shiftimm))
247         } else {
248             None
249         }
250     }
251 
ashr_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt>252     fn ashr_from_u64(&mut self, ty: Type, n: u64) -> Option<ShiftOpAndAmt> {
253         let shiftimm = ShiftOpShiftImm::maybe_from_shift(n)?;
254         let shiftee_bits = ty_bits(ty);
255         if shiftee_bits <= core::u8::MAX as usize {
256             let shiftimm = shiftimm.mask(shiftee_bits as u8);
257             Some(ShiftOpAndAmt::new(ShiftOp::ASR, shiftimm))
258         } else {
259             None
260         }
261     }
262 
integral_ty(&mut self, ty: Type) -> Option<Type>263     fn integral_ty(&mut self, ty: Type) -> Option<Type> {
264         match ty {
265             I8 | I16 | I32 | I64 => Some(ty),
266             _ => None,
267         }
268     }
269 
is_zero_simm9(&mut self, imm: &SImm9) -> Option<()>270     fn is_zero_simm9(&mut self, imm: &SImm9) -> Option<()> {
271         if imm.value() == 0 { Some(()) } else { None }
272     }
273 
is_zero_uimm12(&mut self, imm: &UImm12Scaled) -> Option<()>274     fn is_zero_uimm12(&mut self, imm: &UImm12Scaled) -> Option<()> {
275         if imm.value() == 0 { Some(()) } else { None }
276     }
277 
278     /// This is target-word-size dependent.  And it excludes booleans and reftypes.
valid_atomic_transaction(&mut self, ty: Type) -> Option<Type>279     fn valid_atomic_transaction(&mut self, ty: Type) -> Option<Type> {
280         match ty {
281             I8 | I16 | I32 | I64 => Some(ty),
282             _ => None,
283         }
284     }
285 
286     /// This is the fallback case for loading a 64-bit integral constant into a
287     /// register.
288     ///
289     /// The logic here is nontrivial enough that it's not really worth porting
290     /// this over to ISLE.
load_constant_full( &mut self, ty: Type, extend: &ImmExtend, extend_to: &OperandSize, value: u64, ) -> Reg291     fn load_constant_full(
292         &mut self,
293         ty: Type,
294         extend: &ImmExtend,
295         extend_to: &OperandSize,
296         value: u64,
297     ) -> Reg {
298         let bits = ty.bits();
299 
300         let value = match (extend_to, *extend) {
301             (OperandSize::Size32, ImmExtend::Sign) if bits < 32 => {
302                 let shift = 32 - bits;
303                 let value = value as i32;
304 
305                 // we cast first to a u32 and then to a u64, to ensure that we are representing a
306                 // i32 in a u64, and not a i64. This is important, otherwise value will not fit in
307                 // 32 bits
308                 ((value << shift) >> shift) as u32 as u64
309             }
310             (OperandSize::Size32, ImmExtend::Zero) if bits < 32 => {
311                 value & !((u32::MAX as u64) << bits)
312             }
313             (OperandSize::Size64, ImmExtend::Sign) if bits < 64 => {
314                 let shift = 64 - bits;
315                 let value = value as i64;
316 
317                 ((value << shift) >> shift) as u64
318             }
319             (OperandSize::Size64, ImmExtend::Zero) if bits < 64 => value & !(u64::MAX << bits),
320             _ => value,
321         };
322 
323         // Divide the value into 16-bit slices that we can manipulate using
324         // `movz`, `movn`, and `movk`.
325         fn get(value: u64, shift: u8) -> u16 {
326             (value >> (shift * 16)) as u16
327         }
328         fn replace(mut old: u64, new: u16, shift: u8) -> u64 {
329             let offset = shift * 16;
330             old &= !(0xffff << offset);
331             old |= u64::from(new) << offset;
332             old
333         }
334 
335         // The 32-bit versions of `movz`/`movn`/`movk` will clear the upper 32
336         // bits, so if that's the outcome we want we might as well use them. For
337         // simplicity and ease of reading the disassembly, we use the same size
338         // for all instructions in the sequence.
339         let size = if value >> 32 == 0 {
340             OperandSize::Size32
341         } else {
342             OperandSize::Size64
343         };
344 
345         // The `movz` instruction initially sets all bits to zero, while `movn`
346         // initially sets all bits to one. A good choice of initial value can
347         // reduce the number of `movk` instructions we need afterward, so we
348         // check both variants to determine which is closest to the constant
349         // we actually wanted. In case they're equally good, we prefer `movz`
350         // because the assembly listings are generally harder to read when the
351         // operands are negated.
352         let (mut running_value, op, first) =
353             [(MoveWideOp::MovZ, 0), (MoveWideOp::MovN, size.max_value())]
354                 .into_iter()
355                 .map(|(op, base)| {
356                     // Both `movz` and `movn` can overwrite one slice after setting
357                     // the initial value; we get to pick which one. 32-bit variants
358                     // can only modify the lower two slices.
359                     let first = (0..(size.bits() / 16))
360                         // Pick one slice that's different from the initial value
361                         .find(|&i| get(base ^ value, i) != 0)
362                         // If none are different, we still have to pick one
363                         .unwrap_or(0);
364                     // Compute the value we'll get from this `movz`/`movn`
365                     (replace(base, get(value, first), first), op, first)
366                 })
367                 // Count how many `movk` instructions we'll need.
368                 .min_by_key(|(base, ..)| (0..4).filter(|&i| get(base ^ value, i) != 0).count())
369                 // `variants` isn't empty so `min_by_key` always returns something.
370                 .unwrap();
371 
372         // Build the initial instruction we chose above, putting the result
373         // into a new temporary virtual register. Note that the encoding for the
374         // immediate operand is bitwise-inverted for `movn`.
375         let mut rd = self.temp_writable_reg(I64);
376         self.lower_ctx.emit(MInst::MovWide {
377             op,
378             rd,
379             imm: MoveWideConst {
380                 bits: match op {
381                     MoveWideOp::MovZ => get(value, first),
382                     MoveWideOp::MovN => !get(value, first),
383                 },
384                 shift: first,
385             },
386             size,
387         });
388 
389         // Emit a `movk` instruction for each remaining slice of the desired
390         // constant that does not match the initial value constructed above.
391         for shift in (first + 1)..(size.bits() / 16) {
392             let bits = get(value, shift);
393             if bits != get(running_value, shift) {
394                 let rn = rd.to_reg();
395                 rd = self.temp_writable_reg(I64);
396                 self.lower_ctx.emit(MInst::MovK {
397                     rd,
398                     rn,
399                     imm: MoveWideConst { bits, shift },
400                     size,
401                 });
402                 running_value = replace(running_value, bits, shift);
403             }
404         }
405 
406         debug_assert_eq!(value, running_value);
407         return rd.to_reg();
408     }
409 
zero_reg(&mut self) -> Reg410     fn zero_reg(&mut self) -> Reg {
411         zero_reg()
412     }
413 
stack_reg(&mut self) -> Reg414     fn stack_reg(&mut self) -> Reg {
415         stack_reg()
416     }
417 
fp_reg(&mut self) -> Reg418     fn fp_reg(&mut self) -> Reg {
419         fp_reg()
420     }
421 
writable_link_reg(&mut self) -> WritableReg422     fn writable_link_reg(&mut self) -> WritableReg {
423         writable_link_reg()
424     }
425 
extended_value_from_value(&mut self, val: Value) -> Option<ExtendedValue>426     fn extended_value_from_value(&mut self, val: Value) -> Option<ExtendedValue> {
427         let (val, extend) = super::get_as_extended_value(self.lower_ctx, val)?;
428         Some(ExtendedValue { val, extend })
429     }
430 
put_extended_in_reg(&mut self, reg: &ExtendedValue) -> Reg431     fn put_extended_in_reg(&mut self, reg: &ExtendedValue) -> Reg {
432         self.put_in_reg(reg.val)
433     }
434 
get_extended_op(&mut self, reg: &ExtendedValue) -> ExtendOp435     fn get_extended_op(&mut self, reg: &ExtendedValue) -> ExtendOp {
436         reg.extend
437     }
438 
emit(&mut self, inst: &MInst) -> Unit439     fn emit(&mut self, inst: &MInst) -> Unit {
440         self.lower_ctx.emit(inst.clone());
441     }
442 
cond_br_zero(&mut self, reg: Reg, size: &OperandSize) -> CondBrKind443     fn cond_br_zero(&mut self, reg: Reg, size: &OperandSize) -> CondBrKind {
444         CondBrKind::Zero(reg, *size)
445     }
446 
cond_br_not_zero(&mut self, reg: Reg, size: &OperandSize) -> CondBrKind447     fn cond_br_not_zero(&mut self, reg: Reg, size: &OperandSize) -> CondBrKind {
448         CondBrKind::NotZero(reg, *size)
449     }
450 
cond_br_cond(&mut self, cond: &Cond) -> CondBrKind451     fn cond_br_cond(&mut self, cond: &Cond) -> CondBrKind {
452         CondBrKind::Cond(*cond)
453     }
454 
nzcv(&mut self, n: bool, z: bool, c: bool, v: bool) -> NZCV455     fn nzcv(&mut self, n: bool, z: bool, c: bool, v: bool) -> NZCV {
456         NZCV::new(n, z, c, v)
457     }
458 
u8_into_uimm5(&mut self, x: u8) -> UImm5459     fn u8_into_uimm5(&mut self, x: u8) -> UImm5 {
460         UImm5::maybe_from_u8(x).unwrap()
461     }
462 
u8_into_imm12(&mut self, x: u8) -> Imm12463     fn u8_into_imm12(&mut self, x: u8) -> Imm12 {
464         Imm12::maybe_from_u64(x.into()).unwrap()
465     }
466 
writable_zero_reg(&mut self) -> WritableReg467     fn writable_zero_reg(&mut self) -> WritableReg {
468         writable_zero_reg()
469     }
470 
shift_mask(&mut self, ty: Type) -> ImmLogic471     fn shift_mask(&mut self, ty: Type) -> ImmLogic {
472         debug_assert!(ty.lane_bits().is_power_of_two());
473 
474         let mask = (ty.lane_bits() - 1) as u64;
475         ImmLogic::maybe_from_u64(mask, I32).unwrap()
476     }
477 
imm_shift_from_imm64(&mut self, ty: Type, val: Imm64) -> Option<ImmShift>478     fn imm_shift_from_imm64(&mut self, ty: Type, val: Imm64) -> Option<ImmShift> {
479         let imm_value = (val.bits() as u64) & ((ty.bits() - 1) as u64);
480         ImmShift::maybe_from_u64(imm_value)
481     }
482 
u64_into_imm_logic(&mut self, ty: Type, val: u64) -> ImmLogic483     fn u64_into_imm_logic(&mut self, ty: Type, val: u64) -> ImmLogic {
484         ImmLogic::maybe_from_u64(val, ty).unwrap()
485     }
486 
negate_imm_shift(&mut self, ty: Type, mut imm: ImmShift) -> ImmShift487     fn negate_imm_shift(&mut self, ty: Type, mut imm: ImmShift) -> ImmShift {
488         let size = u8::try_from(ty.bits()).unwrap();
489         imm.imm = size.wrapping_sub(imm.value());
490         imm.imm &= size - 1;
491         imm
492     }
493 
rotr_mask(&mut self, ty: Type) -> ImmLogic494     fn rotr_mask(&mut self, ty: Type) -> ImmLogic {
495         ImmLogic::maybe_from_u64((ty.bits() - 1) as u64, I32).unwrap()
496     }
497 
rotr_opposite_amount(&mut self, ty: Type, val: ImmShift) -> ImmShift498     fn rotr_opposite_amount(&mut self, ty: Type, val: ImmShift) -> ImmShift {
499         let amount = val.value() & u8::try_from(ty.bits() - 1).unwrap();
500         ImmShift::maybe_from_u64(u64::from(ty.bits()) - u64::from(amount)).unwrap()
501     }
502 
icmp_zero_cond(&mut self, cond: &IntCC) -> Option<IntCC>503     fn icmp_zero_cond(&mut self, cond: &IntCC) -> Option<IntCC> {
504         match cond {
505             &IntCC::Equal
506             | &IntCC::SignedGreaterThanOrEqual
507             | &IntCC::SignedGreaterThan
508             | &IntCC::SignedLessThanOrEqual
509             | &IntCC::SignedLessThan => Some(*cond),
510             _ => None,
511         }
512     }
513 
fcmp_zero_cond(&mut self, cond: &FloatCC) -> Option<FloatCC>514     fn fcmp_zero_cond(&mut self, cond: &FloatCC) -> Option<FloatCC> {
515         match cond {
516             &FloatCC::Equal
517             | &FloatCC::GreaterThanOrEqual
518             | &FloatCC::GreaterThan
519             | &FloatCC::LessThanOrEqual
520             | &FloatCC::LessThan => Some(*cond),
521             _ => None,
522         }
523     }
524 
fcmp_zero_cond_not_eq(&mut self, cond: &FloatCC) -> Option<FloatCC>525     fn fcmp_zero_cond_not_eq(&mut self, cond: &FloatCC) -> Option<FloatCC> {
526         match cond {
527             &FloatCC::NotEqual => Some(FloatCC::NotEqual),
528             _ => None,
529         }
530     }
531 
icmp_zero_cond_not_eq(&mut self, cond: &IntCC) -> Option<IntCC>532     fn icmp_zero_cond_not_eq(&mut self, cond: &IntCC) -> Option<IntCC> {
533         match cond {
534             &IntCC::NotEqual => Some(IntCC::NotEqual),
535             _ => None,
536         }
537     }
538 
float_cc_cmp_zero_to_vec_misc_op(&mut self, cond: &FloatCC) -> VecMisc2539     fn float_cc_cmp_zero_to_vec_misc_op(&mut self, cond: &FloatCC) -> VecMisc2 {
540         match cond {
541             &FloatCC::Equal => VecMisc2::Fcmeq0,
542             &FloatCC::GreaterThanOrEqual => VecMisc2::Fcmge0,
543             &FloatCC::LessThanOrEqual => VecMisc2::Fcmle0,
544             &FloatCC::GreaterThan => VecMisc2::Fcmgt0,
545             &FloatCC::LessThan => VecMisc2::Fcmlt0,
546             _ => panic!(),
547         }
548     }
549 
int_cc_cmp_zero_to_vec_misc_op(&mut self, cond: &IntCC) -> VecMisc2550     fn int_cc_cmp_zero_to_vec_misc_op(&mut self, cond: &IntCC) -> VecMisc2 {
551         match cond {
552             &IntCC::Equal => VecMisc2::Cmeq0,
553             &IntCC::SignedGreaterThanOrEqual => VecMisc2::Cmge0,
554             &IntCC::SignedLessThanOrEqual => VecMisc2::Cmle0,
555             &IntCC::SignedGreaterThan => VecMisc2::Cmgt0,
556             &IntCC::SignedLessThan => VecMisc2::Cmlt0,
557             _ => panic!(),
558         }
559     }
560 
float_cc_cmp_zero_to_vec_misc_op_swap(&mut self, cond: &FloatCC) -> VecMisc2561     fn float_cc_cmp_zero_to_vec_misc_op_swap(&mut self, cond: &FloatCC) -> VecMisc2 {
562         match cond {
563             &FloatCC::Equal => VecMisc2::Fcmeq0,
564             &FloatCC::GreaterThanOrEqual => VecMisc2::Fcmle0,
565             &FloatCC::LessThanOrEqual => VecMisc2::Fcmge0,
566             &FloatCC::GreaterThan => VecMisc2::Fcmlt0,
567             &FloatCC::LessThan => VecMisc2::Fcmgt0,
568             _ => panic!(),
569         }
570     }
571 
int_cc_cmp_zero_to_vec_misc_op_swap(&mut self, cond: &IntCC) -> VecMisc2572     fn int_cc_cmp_zero_to_vec_misc_op_swap(&mut self, cond: &IntCC) -> VecMisc2 {
573         match cond {
574             &IntCC::Equal => VecMisc2::Cmeq0,
575             &IntCC::SignedGreaterThanOrEqual => VecMisc2::Cmle0,
576             &IntCC::SignedLessThanOrEqual => VecMisc2::Cmge0,
577             &IntCC::SignedGreaterThan => VecMisc2::Cmlt0,
578             &IntCC::SignedLessThan => VecMisc2::Cmgt0,
579             _ => panic!(),
580         }
581     }
582 
fp_cond_code(&mut self, cc: &condcodes::FloatCC) -> Cond583     fn fp_cond_code(&mut self, cc: &condcodes::FloatCC) -> Cond {
584         lower_fp_condcode(*cc)
585     }
586 
cond_code(&mut self, cc: &condcodes::IntCC) -> Cond587     fn cond_code(&mut self, cc: &condcodes::IntCC) -> Cond {
588         lower_condcode(*cc)
589     }
590 
invert_cond(&mut self, cond: &Cond) -> Cond591     fn invert_cond(&mut self, cond: &Cond) -> Cond {
592         (*cond).invert()
593     }
preg_sp(&mut self) -> PReg594     fn preg_sp(&mut self) -> PReg {
595         super::regs::stack_reg().to_real_reg().unwrap().into()
596     }
597 
preg_fp(&mut self) -> PReg598     fn preg_fp(&mut self) -> PReg {
599         super::regs::fp_reg().to_real_reg().unwrap().into()
600     }
601 
preg_link(&mut self) -> PReg602     fn preg_link(&mut self) -> PReg {
603         super::regs::link_reg().to_real_reg().unwrap().into()
604     }
605 
preg_pinned(&mut self) -> PReg606     fn preg_pinned(&mut self) -> PReg {
607         super::regs::pinned_reg().to_real_reg().unwrap().into()
608     }
609 
branch_target(&mut self, label: MachLabel) -> BranchTarget610     fn branch_target(&mut self, label: MachLabel) -> BranchTarget {
611         BranchTarget::Label(label)
612     }
613 
targets_jt_space(&mut self, elements: &BoxVecMachLabel) -> CodeOffset614     fn targets_jt_space(&mut self, elements: &BoxVecMachLabel) -> CodeOffset {
615         // calculate the number of bytes needed for the jumptable sequence:
616         // 4 bytes per instruction, with 8 instructions base + the size of
617         // the jumptable more.
618         (4 * (8 + elements.len())).try_into().unwrap()
619     }
620 
min_fp_value(&mut self, signed: bool, in_bits: u8, out_bits: u8) -> Reg621     fn min_fp_value(&mut self, signed: bool, in_bits: u8, out_bits: u8) -> Reg {
622         if in_bits == 32 {
623             // From float32.
624             let min = match (signed, out_bits) {
625                 (true, 8) => i8::MIN as f32 - 1.,
626                 (true, 16) => i16::MIN as f32 - 1.,
627                 (true, 32) => i32::MIN as f32, // I32_MIN - 1 isn't precisely representable as a f32.
628                 (true, 64) => i64::MIN as f32, // I64_MIN - 1 isn't precisely representable as a f32.
629 
630                 (false, _) => -1.,
631                 _ => unimplemented!(
632                     "unexpected {} output size of {} bits for 32-bit input",
633                     if signed { "signed" } else { "unsigned" },
634                     out_bits
635                 ),
636             };
637 
638             generated_code::constructor_constant_f32(self, min.to_bits())
639         } else if in_bits == 64 {
640             // From float64.
641             let min = match (signed, out_bits) {
642                 (true, 8) => i8::MIN as f64 - 1.,
643                 (true, 16) => i16::MIN as f64 - 1.,
644                 (true, 32) => i32::MIN as f64 - 1.,
645                 (true, 64) => i64::MIN as f64,
646 
647                 (false, _) => -1.,
648                 _ => unimplemented!(
649                     "unexpected {} output size of {} bits for 64-bit input",
650                     if signed { "signed" } else { "unsigned" },
651                     out_bits
652                 ),
653             };
654 
655             generated_code::constructor_constant_f64(self, min.to_bits())
656         } else {
657             unimplemented!(
658                 "unexpected input size for min_fp_value: {} (signed: {}, output size: {})",
659                 in_bits,
660                 signed,
661                 out_bits
662             );
663         }
664     }
665 
max_fp_value(&mut self, signed: bool, in_bits: u8, out_bits: u8) -> Reg666     fn max_fp_value(&mut self, signed: bool, in_bits: u8, out_bits: u8) -> Reg {
667         if in_bits == 32 {
668             // From float32.
669             let max = match (signed, out_bits) {
670                 (true, 8) => i8::MAX as f32 + 1.,
671                 (true, 16) => i16::MAX as f32 + 1.,
672                 (true, 32) => (i32::MAX as u64 + 1) as f32,
673                 (true, 64) => (i64::MAX as u64 + 1) as f32,
674 
675                 (false, 8) => u8::MAX as f32 + 1.,
676                 (false, 16) => u16::MAX as f32 + 1.,
677                 (false, 32) => (u32::MAX as u64 + 1) as f32,
678                 (false, 64) => (u64::MAX as u128 + 1) as f32,
679                 _ => unimplemented!(
680                     "unexpected {} output size of {} bits for 32-bit input",
681                     if signed { "signed" } else { "unsigned" },
682                     out_bits
683                 ),
684             };
685 
686             generated_code::constructor_constant_f32(self, max.to_bits())
687         } else if in_bits == 64 {
688             // From float64.
689             let max = match (signed, out_bits) {
690                 (true, 8) => i8::MAX as f64 + 1.,
691                 (true, 16) => i16::MAX as f64 + 1.,
692                 (true, 32) => i32::MAX as f64 + 1.,
693                 (true, 64) => (i64::MAX as u64 + 1) as f64,
694 
695                 (false, 8) => u8::MAX as f64 + 1.,
696                 (false, 16) => u16::MAX as f64 + 1.,
697                 (false, 32) => u32::MAX as f64 + 1.,
698                 (false, 64) => (u64::MAX as u128 + 1) as f64,
699                 _ => unimplemented!(
700                     "unexpected {} output size of {} bits for 64-bit input",
701                     if signed { "signed" } else { "unsigned" },
702                     out_bits
703                 ),
704             };
705 
706             generated_code::constructor_constant_f64(self, max.to_bits())
707         } else {
708             unimplemented!(
709                 "unexpected input size for max_fp_value: {} (signed: {}, output size: {})",
710                 in_bits,
711                 signed,
712                 out_bits
713             );
714         }
715     }
716 
fpu_op_ri_ushr(&mut self, ty_bits: u8, shift: u8) -> FPUOpRI717     fn fpu_op_ri_ushr(&mut self, ty_bits: u8, shift: u8) -> FPUOpRI {
718         if ty_bits == 32 {
719             FPUOpRI::UShr32(FPURightShiftImm::maybe_from_u8(shift, ty_bits).unwrap())
720         } else if ty_bits == 64 {
721             FPUOpRI::UShr64(FPURightShiftImm::maybe_from_u8(shift, ty_bits).unwrap())
722         } else {
723             unimplemented!(
724                 "unexpected input size for fpu_op_ri_ushr: {} (shift: {})",
725                 ty_bits,
726                 shift
727             );
728         }
729     }
730 
fpu_op_ri_sli(&mut self, ty_bits: u8, shift: u8) -> FPUOpRIMod731     fn fpu_op_ri_sli(&mut self, ty_bits: u8, shift: u8) -> FPUOpRIMod {
732         if ty_bits == 32 {
733             FPUOpRIMod::Sli32(FPULeftShiftImm::maybe_from_u8(shift, ty_bits).unwrap())
734         } else if ty_bits == 64 {
735             FPUOpRIMod::Sli64(FPULeftShiftImm::maybe_from_u8(shift, ty_bits).unwrap())
736         } else {
737             unimplemented!(
738                 "unexpected input size for fpu_op_ri_sli: {} (shift: {})",
739                 ty_bits,
740                 shift
741             );
742         }
743     }
744 
vec_extract_imm4_from_immediate(&mut self, imm: Immediate) -> Option<u8>745     fn vec_extract_imm4_from_immediate(&mut self, imm: Immediate) -> Option<u8> {
746         let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
747 
748         if bytes.windows(2).all(|a| a[0] + 1 == a[1]) && bytes[0] < 16 {
749             Some(bytes[0])
750         } else {
751             None
752         }
753     }
754 
shuffle_dup8_from_imm(&mut self, imm: Immediate) -> Option<u8>755     fn shuffle_dup8_from_imm(&mut self, imm: Immediate) -> Option<u8> {
756         let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
757         if bytes.iter().all(|b| *b == bytes[0]) && bytes[0] < 16 {
758             Some(bytes[0])
759         } else {
760             None
761         }
762     }
shuffle_dup16_from_imm(&mut self, imm: Immediate) -> Option<u8>763     fn shuffle_dup16_from_imm(&mut self, imm: Immediate) -> Option<u8> {
764         let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
765         if a == b && b == c && c == d && d == e && e == f && f == g && g == h && a < 8 {
766             Some(a)
767         } else {
768             None
769         }
770     }
shuffle_dup32_from_imm(&mut self, imm: Immediate) -> Option<u8>771     fn shuffle_dup32_from_imm(&mut self, imm: Immediate) -> Option<u8> {
772         let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
773         if a == b && b == c && c == d && a < 4 {
774             Some(a)
775         } else {
776             None
777         }
778     }
shuffle_dup64_from_imm(&mut self, imm: Immediate) -> Option<u8>779     fn shuffle_dup64_from_imm(&mut self, imm: Immediate) -> Option<u8> {
780         let (a, b) = self.shuffle64_from_imm(imm)?;
781         if a == b && a < 2 { Some(a) } else { None }
782     }
783 
asimd_mov_mod_imm_zero(&mut self, size: &ScalarSize) -> ASIMDMovModImm784     fn asimd_mov_mod_imm_zero(&mut self, size: &ScalarSize) -> ASIMDMovModImm {
785         ASIMDMovModImm::zero(*size)
786     }
787 
asimd_mov_mod_imm_from_u64( &mut self, val: u64, size: &ScalarSize, ) -> Option<ASIMDMovModImm>788     fn asimd_mov_mod_imm_from_u64(
789         &mut self,
790         val: u64,
791         size: &ScalarSize,
792     ) -> Option<ASIMDMovModImm> {
793         ASIMDMovModImm::maybe_from_u64(val, *size)
794     }
795 
asimd_fp_mod_imm_from_u64(&mut self, val: u64, size: &ScalarSize) -> Option<ASIMDFPModImm>796     fn asimd_fp_mod_imm_from_u64(&mut self, val: u64, size: &ScalarSize) -> Option<ASIMDFPModImm> {
797         ASIMDFPModImm::maybe_from_u64(val, *size)
798     }
799 
u64_low32_bits_unset(&mut self, val: u64) -> Option<u64>800     fn u64_low32_bits_unset(&mut self, val: u64) -> Option<u64> {
801         if val & 0xffffffff == 0 {
802             Some(val)
803         } else {
804             None
805         }
806     }
807 
shift_masked_imm(&mut self, ty: Type, imm: u64) -> u8808     fn shift_masked_imm(&mut self, ty: Type, imm: u64) -> u8 {
809         (imm as u8) & ((ty.lane_bits() - 1) as u8)
810     }
811 
simm7_scaled_from_i64(&mut self, val: i64, ty: Type) -> Option<SImm7Scaled>812     fn simm7_scaled_from_i64(&mut self, val: i64, ty: Type) -> Option<SImm7Scaled> {
813         SImm7Scaled::maybe_from_i64(val, ty)
814     }
815 
simm9_from_i64(&mut self, val: i64) -> Option<SImm9>816     fn simm9_from_i64(&mut self, val: i64) -> Option<SImm9> {
817         SImm9::maybe_from_i64(val)
818     }
819 
uimm12_scaled_from_i64(&mut self, val: i64, ty: Type) -> Option<UImm12Scaled>820     fn uimm12_scaled_from_i64(&mut self, val: i64, ty: Type) -> Option<UImm12Scaled> {
821         UImm12Scaled::maybe_from_i64(val, ty)
822     }
823 
test_and_compare_bit_const(&mut self, ty: Type, n: u64) -> Option<u8>824     fn test_and_compare_bit_const(&mut self, ty: Type, n: u64) -> Option<u8> {
825         if n.count_ones() != 1 {
826             return None;
827         }
828         let bit = n.trailing_zeros();
829         if bit >= ty.bits() {
830             return None;
831         }
832         Some(bit as u8)
833     }
834 
835     /// Use as a helper when generating `AluRRRShift` for `extr` instructions.
a64_extr_imm(&mut self, ty: Type, shift: ImmShift) -> ShiftOpAndAmt836     fn a64_extr_imm(&mut self, ty: Type, shift: ImmShift) -> ShiftOpAndAmt {
837         // The `ShiftOpAndAmt` immediate is used with `AluRRRShift` shape which
838         // requires `ShiftOpAndAmt` so the shift of `ty` and `shift` are
839         // translated into `ShiftOpAndAmt` here. The `ShiftOp` value here is
840         // only used for its encoding, not its logical meaning.
841         let (op, expected) = match ty {
842             types::I32 => (ShiftOp::LSL, 0b00),
843             types::I64 => (ShiftOp::LSR, 0b01),
844             _ => unreachable!(),
845         };
846         assert_eq!(op.bits(), expected);
847         ShiftOpAndAmt::new(
848             op,
849             ShiftOpShiftImm::maybe_from_shift(shift.value().into()).unwrap(),
850         )
851     }
852 
is_pic(&mut self) -> bool853     fn is_pic(&mut self) -> bool {
854         self.backend.flags.is_pic()
855     }
856 }
857