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