1 //! ISLE integration glue code for x64 lowering.
2 
3 // Pull in the ISLE generated code.
4 pub(crate) mod generated_code;
5 use crate::{ir::AtomicRmwOp, ir::types};
6 use generated_code::{AssemblerOutputs, Context, MInst, RegisterClass};
7 
8 // Types that the generated ISLE code uses via `use super::*`.
9 use super::external::{CraneliftRegisters, PairedGpr, PairedXmm, isle_assembler_methods};
10 use super::{MergeableLoadSize, is_int_or_ref_ty, is_mergeable_load, lower_to_amode};
11 use crate::ir::condcodes::{FloatCC, IntCC};
12 use crate::ir::immediates::*;
13 use crate::ir::types::*;
14 use crate::ir::{
15     BlockCall, Inst, InstructionData, LibCall, MemFlags, Opcode, TrapCode, Value, ValueList,
16 };
17 use crate::isa::x64::X64Backend;
18 use crate::isa::x64::inst::{ReturnCallInfo, args::*, regs};
19 use crate::isa::x64::lower::{InsnInput, emit_vm_call};
20 use crate::machinst::isle::*;
21 use crate::machinst::{
22     ArgPair, CallArgList, CallInfo, CallRetList, InstOutput, MachInst, VCodeConstant,
23     VCodeConstantData,
24 };
25 use alloc::boxed::Box;
26 use alloc::vec::Vec;
27 use cranelift_assembler_x64 as asm;
28 use regalloc2::PReg;
29 
30 /// Type representing out-of-line data for calls. This type optional because the
31 /// call instruction is also used by Winch to emit calls, but the
32 /// `Box<CallInfo>` field is not used, it's only used by Cranelift. By making it
33 /// optional, we reduce the number of heap allocations in Winch.
34 type BoxCallInfo = Box<CallInfo<ExternalName>>;
35 type BoxCallIndInfo = Box<CallInfo<RegMem>>;
36 type BoxReturnCallInfo = Box<ReturnCallInfo<ExternalName>>;
37 type BoxReturnCallIndInfo = Box<ReturnCallInfo<Reg>>;
38 type VecArgPair = Vec<ArgPair>;
39 type BoxAtomic128RmwSeqArgs = Box<Atomic128RmwSeqArgs>;
40 type BoxAtomic128XchgSeqArgs = Box<Atomic128XchgSeqArgs>;
41 
42 /// When interacting with the external assembler (see `external.rs`), we
43 /// need to fix the types we'll use.
44 type AssemblerInst = asm::Inst<CraneliftRegisters>;
45 
46 pub struct SinkableLoad {
47     inst: Inst,
48     addr_input: InsnInput,
49     offset: i32,
50 }
51 
52 /// The main entry point for lowering with ISLE.
lower( lower_ctx: &mut Lower<MInst>, backend: &X64Backend, inst: Inst, ) -> Option<InstOutput>53 pub(crate) fn lower(
54     lower_ctx: &mut Lower<MInst>,
55     backend: &X64Backend,
56     inst: Inst,
57 ) -> Option<InstOutput> {
58     // TODO: reuse the ISLE context across lowerings so we can reuse its
59     // internal heap allocations.
60     let mut isle_ctx = IsleContext { lower_ctx, backend };
61     generated_code::constructor_lower(&mut isle_ctx, inst)
62 }
63 
lower_branch( lower_ctx: &mut Lower<MInst>, backend: &X64Backend, branch: Inst, targets: &[MachLabel], ) -> Option<()>64 pub(crate) fn lower_branch(
65     lower_ctx: &mut Lower<MInst>,
66     backend: &X64Backend,
67     branch: Inst,
68     targets: &[MachLabel],
69 ) -> Option<()> {
70     // TODO: reuse the ISLE context across lowerings so we can reuse its
71     // internal heap allocations.
72     let mut isle_ctx = IsleContext { lower_ctx, backend };
73     generated_code::constructor_lower_branch(&mut isle_ctx, branch, &targets)
74 }
75 
76 impl Context for IsleContext<'_, '_, MInst, X64Backend> {
77     isle_lower_prelude_methods!();
78     isle_assembler_methods!();
79 
gen_call_info( &mut self, sig: Sig, dest: ExternalName, uses: CallArgList, defs: CallRetList, try_call_info: Option<TryCallInfo>, patchable: bool, ) -> BoxCallInfo80     fn gen_call_info(
81         &mut self,
82         sig: Sig,
83         dest: ExternalName,
84         uses: CallArgList,
85         defs: CallRetList,
86         try_call_info: Option<TryCallInfo>,
87         patchable: bool,
88     ) -> BoxCallInfo {
89         let stack_ret_space = self.lower_ctx.sigs()[sig].sized_stack_ret_space();
90         let stack_arg_space = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
91         self.lower_ctx
92             .abi_mut()
93             .accumulate_outgoing_args_size(stack_ret_space + stack_arg_space);
94 
95         Box::new(
96             self.lower_ctx
97                 .gen_call_info(sig, dest, uses, defs, try_call_info, patchable),
98         )
99     }
100 
gen_call_ind_info( &mut self, sig: Sig, dest: &RegMem, uses: CallArgList, defs: CallRetList, try_call_info: Option<TryCallInfo>, ) -> BoxCallIndInfo101     fn gen_call_ind_info(
102         &mut self,
103         sig: Sig,
104         dest: &RegMem,
105         uses: CallArgList,
106         defs: CallRetList,
107         try_call_info: Option<TryCallInfo>,
108     ) -> BoxCallIndInfo {
109         let stack_ret_space = self.lower_ctx.sigs()[sig].sized_stack_ret_space();
110         let stack_arg_space = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
111         self.lower_ctx
112             .abi_mut()
113             .accumulate_outgoing_args_size(stack_ret_space + stack_arg_space);
114 
115         Box::new(
116             self.lower_ctx
117                 .gen_call_info(sig, dest.clone(), uses, defs, try_call_info, false),
118         )
119     }
120 
gen_return_call_info( &mut self, sig: Sig, dest: ExternalName, uses: CallArgList, ) -> BoxReturnCallInfo121     fn gen_return_call_info(
122         &mut self,
123         sig: Sig,
124         dest: ExternalName,
125         uses: CallArgList,
126     ) -> BoxReturnCallInfo {
127         let new_stack_arg_size = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
128         self.lower_ctx
129             .abi_mut()
130             .accumulate_tail_args_size(new_stack_arg_size);
131 
132         Box::new(ReturnCallInfo {
133             dest,
134             uses,
135             tmp: self.lower_ctx.temp_writable_gpr(),
136             new_stack_arg_size,
137         })
138     }
139 
gen_return_call_ind_info( &mut self, sig: Sig, dest: Reg, uses: CallArgList, ) -> BoxReturnCallIndInfo140     fn gen_return_call_ind_info(
141         &mut self,
142         sig: Sig,
143         dest: Reg,
144         uses: CallArgList,
145     ) -> BoxReturnCallIndInfo {
146         let new_stack_arg_size = self.lower_ctx.sigs()[sig].sized_stack_arg_space();
147         self.lower_ctx
148             .abi_mut()
149             .accumulate_tail_args_size(new_stack_arg_size);
150 
151         Box::new(ReturnCallInfo {
152             dest,
153             uses,
154             tmp: self.lower_ctx.temp_writable_gpr(),
155             new_stack_arg_size,
156         })
157     }
158 
159     #[inline]
operand_size_of_type_32_64(&mut self, ty: Type) -> OperandSize160     fn operand_size_of_type_32_64(&mut self, ty: Type) -> OperandSize {
161         if ty.bits() == 64 {
162             OperandSize::Size64
163         } else {
164             OperandSize::Size32
165         }
166     }
167 
168     #[inline]
raw_operand_size_of_type(&mut self, ty: Type) -> OperandSize169     fn raw_operand_size_of_type(&mut self, ty: Type) -> OperandSize {
170         OperandSize::from_ty(ty)
171     }
172 
put_in_reg_mem_imm(&mut self, val: Value) -> RegMemImm173     fn put_in_reg_mem_imm(&mut self, val: Value) -> RegMemImm {
174         if let Some(imm) = self.i64_from_iconst(val) {
175             if let Ok(imm) = i32::try_from(imm) {
176                 return RegMemImm::Imm {
177                     simm32: imm.cast_unsigned(),
178                 };
179             }
180         }
181 
182         self.put_in_reg_mem(val).into()
183     }
184 
put_in_xmm_mem_imm(&mut self, val: Value) -> XmmMemImm185     fn put_in_xmm_mem_imm(&mut self, val: Value) -> XmmMemImm {
186         if let Some(imm) = self.i64_from_iconst(val) {
187             if let Ok(imm) = i32::try_from(imm) {
188                 return XmmMemImm::unwrap_new(RegMemImm::Imm {
189                     simm32: imm.cast_unsigned(),
190                 });
191             }
192         }
193 
194         let res = match self.put_in_xmm_mem(val).to_reg_mem() {
195             RegMem::Reg { reg } => RegMemImm::Reg { reg },
196             RegMem::Mem { addr } => RegMemImm::Mem { addr },
197         };
198 
199         XmmMemImm::unwrap_new(res)
200     }
201 
put_in_xmm_mem(&mut self, val: Value) -> XmmMem202     fn put_in_xmm_mem(&mut self, val: Value) -> XmmMem {
203         let inputs = self.lower_ctx.get_value_as_source_or_const(val);
204 
205         if let Some(c) = inputs.constant {
206             // A load from the constant pool is better than a rematerialization into a register,
207             // because it reduces register pressure.
208             //
209             // NOTE: this is where behavior differs from `put_in_reg_mem`, as we always force
210             // constants to be 16 bytes when a constant will be used in place of an xmm register.
211             let vcode_constant = self.emit_u128_le_const(c as u128);
212             return XmmMem::unwrap_new(RegMem::mem(SyntheticAmode::ConstantOffset(vcode_constant)));
213         }
214 
215         XmmMem::unwrap_new(self.put_in_reg_mem(val))
216     }
217 
put_in_reg_mem(&mut self, val: Value) -> RegMem218     fn put_in_reg_mem(&mut self, val: Value) -> RegMem {
219         let inputs = self.lower_ctx.get_value_as_source_or_const(val);
220 
221         if let Some(c) = inputs.constant {
222             // A load from the constant pool is better than a
223             // rematerialization into a register, because it reduces
224             // register pressure.
225             let vcode_constant = self.emit_u64_le_const(c);
226             return RegMem::mem(SyntheticAmode::ConstantOffset(vcode_constant));
227         }
228 
229         if let Some(load) = self.sinkable_load(val) {
230             return RegMem::Mem {
231                 addr: self.sink_load(&load),
232             };
233         }
234 
235         RegMem::reg(self.put_in_reg(val))
236     }
237 
238     #[inline]
encode_fcmp_imm(&mut self, imm: &FcmpImm) -> u8239     fn encode_fcmp_imm(&mut self, imm: &FcmpImm) -> u8 {
240         imm.encode()
241     }
242 
243     #[inline]
encode_round_imm(&mut self, imm: &RoundImm) -> u8244     fn encode_round_imm(&mut self, imm: &RoundImm) -> u8 {
245         imm.encode()
246     }
247 
248     #[inline]
has_avx(&mut self) -> bool249     fn has_avx(&mut self) -> bool {
250         self.backend.x64_flags.has_avx()
251     }
252 
253     #[inline]
use_avx2(&mut self) -> bool254     fn use_avx2(&mut self) -> bool {
255         self.backend.x64_flags.has_avx() && self.backend.x64_flags.has_avx2()
256     }
257 
258     #[inline]
has_avx512vl(&mut self) -> bool259     fn has_avx512vl(&mut self) -> bool {
260         self.backend.x64_flags.has_avx512vl()
261     }
262 
263     #[inline]
has_avx512dq(&mut self) -> bool264     fn has_avx512dq(&mut self) -> bool {
265         self.backend.x64_flags.has_avx512dq()
266     }
267 
268     #[inline]
has_avx512f(&mut self) -> bool269     fn has_avx512f(&mut self) -> bool {
270         self.backend.x64_flags.has_avx512f()
271     }
272 
273     #[inline]
has_avx512bitalg(&mut self) -> bool274     fn has_avx512bitalg(&mut self) -> bool {
275         self.backend.x64_flags.has_avx512bitalg()
276     }
277 
278     #[inline]
has_avx512vbmi(&mut self) -> bool279     fn has_avx512vbmi(&mut self) -> bool {
280         self.backend.x64_flags.has_avx512vbmi()
281     }
282 
283     #[inline]
has_lzcnt(&mut self) -> bool284     fn has_lzcnt(&mut self) -> bool {
285         self.backend.x64_flags.has_lzcnt()
286     }
287 
288     #[inline]
has_bmi1(&mut self) -> bool289     fn has_bmi1(&mut self) -> bool {
290         self.backend.x64_flags.has_bmi1()
291     }
292 
293     #[inline]
has_bmi2(&mut self) -> bool294     fn has_bmi2(&mut self) -> bool {
295         self.backend.x64_flags.has_bmi2()
296     }
297 
298     #[inline]
use_popcnt(&mut self) -> bool299     fn use_popcnt(&mut self) -> bool {
300         self.backend.x64_flags.has_popcnt() && self.backend.x64_flags.has_sse42()
301     }
302 
303     #[inline]
use_fma(&mut self) -> bool304     fn use_fma(&mut self) -> bool {
305         self.backend.x64_flags.has_avx() && self.backend.x64_flags.has_fma()
306     }
307 
308     #[inline]
has_sse3(&mut self) -> bool309     fn has_sse3(&mut self) -> bool {
310         self.backend.x64_flags.has_sse3()
311     }
312 
313     #[inline]
has_ssse3(&mut self) -> bool314     fn has_ssse3(&mut self) -> bool {
315         self.backend.x64_flags.has_ssse3()
316     }
317 
318     #[inline]
has_sse41(&mut self) -> bool319     fn has_sse41(&mut self) -> bool {
320         self.backend.x64_flags.has_sse41()
321     }
322 
323     #[inline]
use_sse42(&mut self) -> bool324     fn use_sse42(&mut self) -> bool {
325         self.backend.x64_flags.has_sse41() && self.backend.x64_flags.has_sse42()
326     }
327 
328     #[inline]
has_cmpxchg16b(&mut self) -> bool329     fn has_cmpxchg16b(&mut self) -> bool {
330         self.backend.x64_flags.has_cmpxchg16b()
331     }
332 
333     #[inline]
shift_mask(&mut self, ty: Type) -> u8334     fn shift_mask(&mut self, ty: Type) -> u8 {
335         debug_assert!(ty.lane_bits().is_power_of_two());
336 
337         (ty.lane_bits() - 1) as u8
338     }
339 
shift_amount_masked(&mut self, ty: Type, val: Imm64) -> u8340     fn shift_amount_masked(&mut self, ty: Type, val: Imm64) -> u8 {
341         (val.bits() as u8) & self.shift_mask(ty)
342     }
343 
344     #[inline]
simm32_from_value(&mut self, val: Value) -> Option<GprMemImm>345     fn simm32_from_value(&mut self, val: Value) -> Option<GprMemImm> {
346         let imm = self.i64_from_iconst(val)?;
347         Some(GprMemImm::unwrap_new(RegMemImm::Imm {
348             simm32: i32::try_from(imm).ok()?.cast_unsigned(),
349         }))
350     }
351 
sinkable_load(&mut self, val: Value) -> Option<SinkableLoad>352     fn sinkable_load(&mut self, val: Value) -> Option<SinkableLoad> {
353         if let Some(inst) = self.is_sinkable_inst(val) {
354             if let Some((addr_input, offset)) =
355                 is_mergeable_load(self.lower_ctx, inst, MergeableLoadSize::Min32)
356             {
357                 return Some(SinkableLoad {
358                     inst,
359                     addr_input,
360                     offset,
361                 });
362             }
363         }
364         None
365     }
366 
sinkable_load_exact(&mut self, val: Value) -> Option<SinkableLoad>367     fn sinkable_load_exact(&mut self, val: Value) -> Option<SinkableLoad> {
368         if let Some(inst) = self.is_sinkable_inst(val) {
369             if let Some((addr_input, offset)) =
370                 is_mergeable_load(self.lower_ctx, inst, MergeableLoadSize::Exact)
371             {
372                 return Some(SinkableLoad {
373                     inst,
374                     addr_input,
375                     offset,
376                 });
377             }
378         }
379         None
380     }
381 
sink_load(&mut self, load: &SinkableLoad) -> SyntheticAmode382     fn sink_load(&mut self, load: &SinkableLoad) -> SyntheticAmode {
383         self.lower_ctx.sink_inst(load.inst);
384         let addr = lower_to_amode(self.lower_ctx, load.addr_input, load.offset);
385         SyntheticAmode::Real(addr)
386     }
387 
388     #[inline]
ext_mode(&mut self, from_bits: u16, to_bits: u16) -> ExtMode389     fn ext_mode(&mut self, from_bits: u16, to_bits: u16) -> ExtMode {
390         ExtMode::new(from_bits, to_bits).unwrap()
391     }
392 
emit(&mut self, inst: &MInst) -> Unit393     fn emit(&mut self, inst: &MInst) -> Unit {
394         self.lower_ctx.emit(inst.clone());
395     }
396 
397     #[inline]
sse_insertps_lane_imm(&mut self, lane: u8) -> u8398     fn sse_insertps_lane_imm(&mut self, lane: u8) -> u8 {
399         // Insert 32-bits from replacement (at index 00, bits 7:8) to vector (lane
400         // shifted into bits 5:6).
401         0b00_00_00_00 | lane << 4
402     }
403 
404     #[inline]
synthetic_amode_to_reg_mem(&mut self, addr: &SyntheticAmode) -> RegMem405     fn synthetic_amode_to_reg_mem(&mut self, addr: &SyntheticAmode) -> RegMem {
406         RegMem::mem(addr.clone())
407     }
408 
409     #[inline]
amode_to_synthetic_amode(&mut self, amode: &Amode) -> SyntheticAmode410     fn amode_to_synthetic_amode(&mut self, amode: &Amode) -> SyntheticAmode {
411         amode.clone().into()
412     }
413 
414     #[inline]
synthetic_amode_slot(&mut self, offset: i32) -> SyntheticAmode415     fn synthetic_amode_slot(&mut self, offset: i32) -> SyntheticAmode {
416         SyntheticAmode::SlotOffset { simm32: offset }
417     }
418 
419     #[inline]
const_to_synthetic_amode(&mut self, c: VCodeConstant) -> SyntheticAmode420     fn const_to_synthetic_amode(&mut self, c: VCodeConstant) -> SyntheticAmode {
421         SyntheticAmode::ConstantOffset(c)
422     }
423 
424     #[inline]
writable_gpr_to_reg(&mut self, r: WritableGpr) -> WritableReg425     fn writable_gpr_to_reg(&mut self, r: WritableGpr) -> WritableReg {
426         r.to_writable_reg()
427     }
428 
429     #[inline]
writable_xmm_to_reg(&mut self, r: WritableXmm) -> WritableReg430     fn writable_xmm_to_reg(&mut self, r: WritableXmm) -> WritableReg {
431         r.to_writable_reg()
432     }
433 
ishl_i8x16_mask_for_const(&mut self, amt: u32) -> SyntheticAmode434     fn ishl_i8x16_mask_for_const(&mut self, amt: u32) -> SyntheticAmode {
435         // When the shift amount is known, we can statically (i.e. at compile
436         // time) determine the mask to use and only emit that.
437         debug_assert!(amt < 8);
438         let mask_offset = amt as usize * 16;
439         let mask_constant = self.lower_ctx.use_constant(VCodeConstantData::WellKnown(
440             &I8X16_ISHL_MASKS[mask_offset..mask_offset + 16],
441         ));
442         SyntheticAmode::ConstantOffset(mask_constant)
443     }
444 
ishl_i8x16_mask_table(&mut self) -> SyntheticAmode445     fn ishl_i8x16_mask_table(&mut self) -> SyntheticAmode {
446         let mask_table = self
447             .lower_ctx
448             .use_constant(VCodeConstantData::WellKnown(&I8X16_ISHL_MASKS));
449         SyntheticAmode::ConstantOffset(mask_table)
450     }
451 
ushr_i8x16_mask_for_const(&mut self, amt: u32) -> SyntheticAmode452     fn ushr_i8x16_mask_for_const(&mut self, amt: u32) -> SyntheticAmode {
453         // When the shift amount is known, we can statically (i.e. at compile
454         // time) determine the mask to use and only emit that.
455         debug_assert!(amt < 8);
456         let mask_offset = amt as usize * 16;
457         let mask_constant = self.lower_ctx.use_constant(VCodeConstantData::WellKnown(
458             &I8X16_USHR_MASKS[mask_offset..mask_offset + 16],
459         ));
460         SyntheticAmode::ConstantOffset(mask_constant)
461     }
462 
ushr_i8x16_mask_table(&mut self) -> SyntheticAmode463     fn ushr_i8x16_mask_table(&mut self) -> SyntheticAmode {
464         let mask_table = self
465             .lower_ctx
466             .use_constant(VCodeConstantData::WellKnown(&I8X16_USHR_MASKS));
467         SyntheticAmode::ConstantOffset(mask_table)
468     }
469 
470     #[inline]
writable_reg_to_xmm(&mut self, r: WritableReg) -> WritableXmm471     fn writable_reg_to_xmm(&mut self, r: WritableReg) -> WritableXmm {
472         Writable::from_reg(Xmm::unwrap_new(r.to_reg()))
473     }
474 
475     #[inline]
writable_xmm_to_xmm(&mut self, r: WritableXmm) -> Xmm476     fn writable_xmm_to_xmm(&mut self, r: WritableXmm) -> Xmm {
477         r.to_reg()
478     }
479 
480     #[inline]
writable_gpr_to_gpr(&mut self, r: WritableGpr) -> Gpr481     fn writable_gpr_to_gpr(&mut self, r: WritableGpr) -> Gpr {
482         r.to_reg()
483     }
484 
485     #[inline]
gpr_to_reg(&mut self, r: Gpr) -> Reg486     fn gpr_to_reg(&mut self, r: Gpr) -> Reg {
487         r.into()
488     }
489 
490     #[inline]
xmm_to_reg(&mut self, r: Xmm) -> Reg491     fn xmm_to_reg(&mut self, r: Xmm) -> Reg {
492         r.into()
493     }
494 
495     #[inline]
xmm_to_xmm_mem_imm(&mut self, r: Xmm) -> XmmMemImm496     fn xmm_to_xmm_mem_imm(&mut self, r: Xmm) -> XmmMemImm {
497         r.into()
498     }
499 
500     #[inline]
xmm_mem_to_xmm_mem_imm(&mut self, r: &XmmMem) -> XmmMemImm501     fn xmm_mem_to_xmm_mem_imm(&mut self, r: &XmmMem) -> XmmMemImm {
502         XmmMemImm::unwrap_new(r.clone().to_reg_mem().into())
503     }
504 
505     #[inline]
temp_writable_gpr(&mut self) -> WritableGpr506     fn temp_writable_gpr(&mut self) -> WritableGpr {
507         self.lower_ctx.temp_writable_gpr()
508     }
509 
510     #[inline]
temp_writable_xmm(&mut self) -> WritableXmm511     fn temp_writable_xmm(&mut self) -> WritableXmm {
512         self.lower_ctx.temp_writable_xmm()
513     }
514 
515     #[inline]
reg_to_reg_mem_imm(&mut self, reg: Reg) -> RegMemImm516     fn reg_to_reg_mem_imm(&mut self, reg: Reg) -> RegMemImm {
517         RegMemImm::Reg { reg }
518     }
519 
520     #[inline]
reg_mem_to_xmm_mem(&mut self, rm: &RegMem) -> XmmMem521     fn reg_mem_to_xmm_mem(&mut self, rm: &RegMem) -> XmmMem {
522         XmmMem::unwrap_new(rm.clone())
523     }
524 
525     #[inline]
gpr_mem_imm_new(&mut self, rmi: &RegMemImm) -> GprMemImm526     fn gpr_mem_imm_new(&mut self, rmi: &RegMemImm) -> GprMemImm {
527         GprMemImm::unwrap_new(rmi.clone())
528     }
529 
530     #[inline]
xmm_mem_imm_new(&mut self, rmi: &RegMemImm) -> XmmMemImm531     fn xmm_mem_imm_new(&mut self, rmi: &RegMemImm) -> XmmMemImm {
532         XmmMemImm::unwrap_new(rmi.clone())
533     }
534 
535     #[inline]
xmm_to_xmm_mem(&mut self, r: Xmm) -> XmmMem536     fn xmm_to_xmm_mem(&mut self, r: Xmm) -> XmmMem {
537         r.into()
538     }
539 
540     #[inline]
xmm_mem_to_reg_mem(&mut self, xm: &XmmMem) -> RegMem541     fn xmm_mem_to_reg_mem(&mut self, xm: &XmmMem) -> RegMem {
542         xm.clone().into()
543     }
544 
545     #[inline]
gpr_mem_to_reg_mem(&mut self, gm: &GprMem) -> RegMem546     fn gpr_mem_to_reg_mem(&mut self, gm: &GprMem) -> RegMem {
547         gm.clone().into()
548     }
549 
550     #[inline]
xmm_new(&mut self, r: Reg) -> Xmm551     fn xmm_new(&mut self, r: Reg) -> Xmm {
552         Xmm::unwrap_new(r)
553     }
554 
555     #[inline]
gpr_new(&mut self, r: Reg) -> Gpr556     fn gpr_new(&mut self, r: Reg) -> Gpr {
557         Gpr::unwrap_new(r)
558     }
559 
560     #[inline]
reg_mem_to_gpr_mem(&mut self, rm: &RegMem) -> GprMem561     fn reg_mem_to_gpr_mem(&mut self, rm: &RegMem) -> GprMem {
562         GprMem::unwrap_new(rm.clone())
563     }
564 
565     #[inline]
reg_to_gpr_mem(&mut self, r: Reg) -> GprMem566     fn reg_to_gpr_mem(&mut self, r: Reg) -> GprMem {
567         GprMem::unwrap_new(RegMem::reg(r))
568     }
569 
570     #[inline]
gpr_to_gpr_mem(&mut self, gpr: Gpr) -> GprMem571     fn gpr_to_gpr_mem(&mut self, gpr: Gpr) -> GprMem {
572         GprMem::from(gpr)
573     }
574 
575     #[inline]
gpr_to_gpr_mem_imm(&mut self, gpr: Gpr) -> GprMemImm576     fn gpr_to_gpr_mem_imm(&mut self, gpr: Gpr) -> GprMemImm {
577         GprMemImm::from(gpr)
578     }
579 
580     #[inline]
type_register_class(&mut self, ty: Type) -> Option<RegisterClass>581     fn type_register_class(&mut self, ty: Type) -> Option<RegisterClass> {
582         if is_int_or_ref_ty(ty) || ty == I128 {
583             Some(RegisterClass::Gpr {
584                 single_register: ty != I128,
585             })
586         } else if ty.is_float() || (ty.is_vector() && ty.bits() <= 128) {
587             Some(RegisterClass::Xmm)
588         } else {
589             None
590         }
591     }
592 
593     #[inline]
ty_int_bool_or_ref(&mut self, ty: Type) -> Option<()>594     fn ty_int_bool_or_ref(&mut self, ty: Type) -> Option<()> {
595         match ty {
596             types::I8 | types::I16 | types::I32 | types::I64 => Some(()),
597             _ => None,
598         }
599     }
600 
601     #[inline]
intcc_to_cc(&mut self, intcc: &IntCC) -> CC602     fn intcc_to_cc(&mut self, intcc: &IntCC) -> CC {
603         CC::from_intcc(*intcc)
604     }
605 
606     #[inline]
cc_invert(&mut self, cc: &CC) -> CC607     fn cc_invert(&mut self, cc: &CC) -> CC {
608         cc.invert()
609     }
610 
611     #[inline]
cc_nz_or_z(&mut self, cc: &CC) -> Option<CC>612     fn cc_nz_or_z(&mut self, cc: &CC) -> Option<CC> {
613         match cc {
614             CC::Z => Some(*cc),
615             CC::NZ => Some(*cc),
616             _ => None,
617         }
618     }
619 
620     #[inline]
sum_extend_fits_in_32_bits( &mut self, extend_from_ty: Type, constant_value: Imm64, offset: Offset32, ) -> Option<u32>621     fn sum_extend_fits_in_32_bits(
622         &mut self,
623         extend_from_ty: Type,
624         constant_value: Imm64,
625         offset: Offset32,
626     ) -> Option<u32> {
627         let offset: i64 = offset.into();
628         let constant_value: u64 = constant_value.bits() as u64;
629         // If necessary, zero extend `constant_value` up to 64 bits.
630         let shift = 64 - extend_from_ty.bits();
631         let zero_extended_constant_value = (constant_value << shift) >> shift;
632         // Sum up the two operands.
633         let sum = offset.wrapping_add(zero_extended_constant_value as i64);
634         // Check that the sum will fit in 32-bits.
635         if sum == ((sum << 32) >> 32) {
636             Some(sum as u32)
637         } else {
638             None
639         }
640     }
641 
642     #[inline]
amode_try_offset(&mut self, addr: &SyntheticAmode, offset: i32) -> Option<SyntheticAmode>643     fn amode_try_offset(&mut self, addr: &SyntheticAmode, offset: i32) -> Option<SyntheticAmode> {
644         addr.offset(offset)
645     }
646 
647     #[inline]
zero_offset(&mut self) -> Offset32648     fn zero_offset(&mut self) -> Offset32 {
649         Offset32::new(0)
650     }
651 
652     #[inline]
preg_rbp(&mut self) -> PReg653     fn preg_rbp(&mut self) -> PReg {
654         regs::rbp().to_real_reg().unwrap().into()
655     }
656 
657     #[inline]
preg_rsp(&mut self) -> PReg658     fn preg_rsp(&mut self) -> PReg {
659         regs::rsp().to_real_reg().unwrap().into()
660     }
661 
662     #[inline]
preg_pinned(&mut self) -> PReg663     fn preg_pinned(&mut self) -> PReg {
664         regs::pinned_reg().to_real_reg().unwrap().into()
665     }
666 
libcall_1(&mut self, libcall: &LibCall, a: Reg) -> Reg667     fn libcall_1(&mut self, libcall: &LibCall, a: Reg) -> Reg {
668         let outputs = emit_vm_call(
669             self.lower_ctx,
670             &self.backend.flags,
671             &self.backend.triple,
672             *libcall,
673             &[ValueRegs::one(a)],
674         )
675         .expect("Failed to emit LibCall");
676 
677         debug_assert_eq!(outputs.len(), 1);
678 
679         outputs[0].only_reg().unwrap()
680     }
681 
libcall_2(&mut self, libcall: &LibCall, a: Reg, b: Reg) -> Reg682     fn libcall_2(&mut self, libcall: &LibCall, a: Reg, b: Reg) -> Reg {
683         let outputs = emit_vm_call(
684             self.lower_ctx,
685             &self.backend.flags,
686             &self.backend.triple,
687             *libcall,
688             &[ValueRegs::one(a), ValueRegs::one(b)],
689         )
690         .expect("Failed to emit LibCall");
691 
692         debug_assert_eq!(outputs.len(), 1);
693 
694         outputs[0].only_reg().unwrap()
695     }
696 
libcall_3(&mut self, libcall: &LibCall, a: Reg, b: Reg, c: Reg) -> Reg697     fn libcall_3(&mut self, libcall: &LibCall, a: Reg, b: Reg, c: Reg) -> Reg {
698         let outputs = emit_vm_call(
699             self.lower_ctx,
700             &self.backend.flags,
701             &self.backend.triple,
702             *libcall,
703             &[ValueRegs::one(a), ValueRegs::one(b), ValueRegs::one(c)],
704         )
705         .expect("Failed to emit LibCall");
706 
707         debug_assert_eq!(outputs.len(), 1);
708 
709         outputs[0].only_reg().unwrap()
710     }
711 
712     #[inline]
vconst_all_ones_or_all_zeros(&mut self, constant: Constant) -> Option<()>713     fn vconst_all_ones_or_all_zeros(&mut self, constant: Constant) -> Option<()> {
714         let const_data = self.lower_ctx.get_constant_data(constant);
715         if const_data.iter().all(|&b| b == 0 || b == 0xFF) {
716             return Some(());
717         }
718         None
719     }
720 
721     #[inline]
shuffle_0_31_mask(&mut self, mask: &VecMask) -> VCodeConstant722     fn shuffle_0_31_mask(&mut self, mask: &VecMask) -> VCodeConstant {
723         let mask = mask
724             .iter()
725             .map(|&b| if b > 15 { b.wrapping_sub(16) } else { b })
726             .map(|b| if b > 15 { 0b10000000 } else { b })
727             .collect();
728         self.lower_ctx
729             .use_constant(VCodeConstantData::Generated(mask))
730     }
731 
732     #[inline]
shuffle_0_15_mask(&mut self, mask: &VecMask) -> VCodeConstant733     fn shuffle_0_15_mask(&mut self, mask: &VecMask) -> VCodeConstant {
734         let mask = mask
735             .iter()
736             .map(|&b| if b > 15 { 0b10000000 } else { b })
737             .collect();
738         self.lower_ctx
739             .use_constant(VCodeConstantData::Generated(mask))
740     }
741 
742     #[inline]
shuffle_16_31_mask(&mut self, mask: &VecMask) -> VCodeConstant743     fn shuffle_16_31_mask(&mut self, mask: &VecMask) -> VCodeConstant {
744         let mask = mask
745             .iter()
746             .map(|&b| b.wrapping_sub(16))
747             .map(|b| if b > 15 { 0b10000000 } else { b })
748             .collect();
749         self.lower_ctx
750             .use_constant(VCodeConstantData::Generated(mask))
751     }
752 
753     #[inline]
perm_from_mask_with_zeros( &mut self, mask: &VecMask, ) -> Option<(VCodeConstant, VCodeConstant)>754     fn perm_from_mask_with_zeros(
755         &mut self,
756         mask: &VecMask,
757     ) -> Option<(VCodeConstant, VCodeConstant)> {
758         if !mask.iter().any(|&b| b > 31) {
759             return None;
760         }
761 
762         let zeros = mask
763             .iter()
764             .map(|&b| if b > 31 { 0x00 } else { 0xff })
765             .collect();
766 
767         Some((
768             self.perm_from_mask(mask),
769             self.lower_ctx
770                 .use_constant(VCodeConstantData::Generated(zeros)),
771         ))
772     }
773 
774     #[inline]
perm_from_mask(&mut self, mask: &VecMask) -> VCodeConstant775     fn perm_from_mask(&mut self, mask: &VecMask) -> VCodeConstant {
776         let mask = mask.iter().cloned().collect();
777         self.lower_ctx
778             .use_constant(VCodeConstantData::Generated(mask))
779     }
780 
xmm_mem_to_xmm_mem_aligned(&mut self, arg: &XmmMem) -> XmmMemAligned781     fn xmm_mem_to_xmm_mem_aligned(&mut self, arg: &XmmMem) -> XmmMemAligned {
782         match XmmMemAligned::new(arg.clone().into()) {
783             Some(aligned) => aligned,
784             None => match arg.clone().into() {
785                 RegMem::Mem { addr } => self.load_xmm_unaligned(addr).into(),
786                 _ => unreachable!(),
787             },
788         }
789     }
790 
xmm_mem_imm_to_xmm_mem_aligned_imm(&mut self, arg: &XmmMemImm) -> XmmMemAlignedImm791     fn xmm_mem_imm_to_xmm_mem_aligned_imm(&mut self, arg: &XmmMemImm) -> XmmMemAlignedImm {
792         match XmmMemAlignedImm::new(arg.clone().into()) {
793             Some(aligned) => aligned,
794             None => match arg.clone().into() {
795                 RegMemImm::Mem { addr } => self.load_xmm_unaligned(addr).into(),
796                 _ => unreachable!(),
797             },
798         }
799     }
800 
pshufd_lhs_imm(&mut self, imm: Immediate) -> Option<u8>801     fn pshufd_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
802         let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
803         if a < 4 && b < 4 && c < 4 && d < 4 {
804             Some(a | (b << 2) | (c << 4) | (d << 6))
805         } else {
806             None
807         }
808     }
809 
pshufd_rhs_imm(&mut self, imm: Immediate) -> Option<u8>810     fn pshufd_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
811         let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
812         // When selecting from the right-hand-side, subtract these all by 4
813         // which will bail out if anything is less than 4. Afterwards the check
814         // is the same as `pshufd_lhs_imm` above.
815         let a = a.checked_sub(4)?;
816         let b = b.checked_sub(4)?;
817         let c = c.checked_sub(4)?;
818         let d = d.checked_sub(4)?;
819         if a < 4 && b < 4 && c < 4 && d < 4 {
820             Some(a | (b << 2) | (c << 4) | (d << 6))
821         } else {
822             None
823         }
824     }
825 
shufps_imm(&mut self, imm: Immediate) -> Option<u8>826     fn shufps_imm(&mut self, imm: Immediate) -> Option<u8> {
827         // The `shufps` instruction selects the first two elements from the
828         // first vector and the second two elements from the second vector, so
829         // offset the third/fourth selectors by 4 and then make sure everything
830         // fits in 32-bits.
831         let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
832         let c = c.checked_sub(4)?;
833         let d = d.checked_sub(4)?;
834         if a < 4 && b < 4 && c < 4 && d < 4 {
835             Some(a | (b << 2) | (c << 4) | (d << 6))
836         } else {
837             None
838         }
839     }
840 
shufps_rev_imm(&mut self, imm: Immediate) -> Option<u8>841     fn shufps_rev_imm(&mut self, imm: Immediate) -> Option<u8> {
842         // This is almost the same as `shufps_imm` except the elements that are
843         // subtracted are reversed. This handles the case that `shufps`
844         // instruction can be emitted if the order of the operands are swapped.
845         let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
846         let a = a.checked_sub(4)?;
847         let b = b.checked_sub(4)?;
848         if a < 4 && b < 4 && c < 4 && d < 4 {
849             Some(a | (b << 2) | (c << 4) | (d << 6))
850         } else {
851             None
852         }
853     }
854 
pshuflw_lhs_imm(&mut self, imm: Immediate) -> Option<u8>855     fn pshuflw_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
856         // Similar to `shufps` except this operates over 16-bit values so four
857         // of them must be fixed and the other four must be in-range to encode
858         // in the immediate.
859         let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
860         if a < 4 && b < 4 && c < 4 && d < 4 && [e, f, g, h] == [4, 5, 6, 7] {
861             Some(a | (b << 2) | (c << 4) | (d << 6))
862         } else {
863             None
864         }
865     }
866 
pshuflw_rhs_imm(&mut self, imm: Immediate) -> Option<u8>867     fn pshuflw_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
868         let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
869         let a = a.checked_sub(8)?;
870         let b = b.checked_sub(8)?;
871         let c = c.checked_sub(8)?;
872         let d = d.checked_sub(8)?;
873         let e = e.checked_sub(8)?;
874         let f = f.checked_sub(8)?;
875         let g = g.checked_sub(8)?;
876         let h = h.checked_sub(8)?;
877         if a < 4 && b < 4 && c < 4 && d < 4 && [e, f, g, h] == [4, 5, 6, 7] {
878             Some(a | (b << 2) | (c << 4) | (d << 6))
879         } else {
880             None
881         }
882     }
883 
pshufhw_lhs_imm(&mut self, imm: Immediate) -> Option<u8>884     fn pshufhw_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
885         // Similar to `pshuflw` except that the first four operands must be
886         // fixed and the second four are offset by an extra 4 and tested to
887         // make sure they're all in the range [4, 8).
888         let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
889         let e = e.checked_sub(4)?;
890         let f = f.checked_sub(4)?;
891         let g = g.checked_sub(4)?;
892         let h = h.checked_sub(4)?;
893         if e < 4 && f < 4 && g < 4 && h < 4 && [a, b, c, d] == [0, 1, 2, 3] {
894             Some(e | (f << 2) | (g << 4) | (h << 6))
895         } else {
896             None
897         }
898     }
899 
pshufhw_rhs_imm(&mut self, imm: Immediate) -> Option<u8>900     fn pshufhw_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
901         // Note that everything here is offset by at least 8 and the upper
902         // bits are offset by 12 to test they're in the range of [12, 16).
903         let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
904         let a = a.checked_sub(8)?;
905         let b = b.checked_sub(8)?;
906         let c = c.checked_sub(8)?;
907         let d = d.checked_sub(8)?;
908         let e = e.checked_sub(12)?;
909         let f = f.checked_sub(12)?;
910         let g = g.checked_sub(12)?;
911         let h = h.checked_sub(12)?;
912         if e < 4 && f < 4 && g < 4 && h < 4 && [a, b, c, d] == [0, 1, 2, 3] {
913             Some(e | (f << 2) | (g << 4) | (h << 6))
914         } else {
915             None
916         }
917     }
918 
palignr_imm_from_immediate(&mut self, imm: Immediate) -> Option<u8>919     fn palignr_imm_from_immediate(&mut self, imm: Immediate) -> Option<u8> {
920         let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
921 
922         if bytes.windows(2).all(|a| a[0] + 1 == a[1]) {
923             Some(bytes[0])
924         } else {
925             None
926         }
927     }
928 
pblendw_imm(&mut self, imm: Immediate) -> Option<u8>929     fn pblendw_imm(&mut self, imm: Immediate) -> Option<u8> {
930         // First make sure that the shuffle immediate is selecting 16-bit lanes.
931         let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
932 
933         // Next build up an 8-bit mask from each of the bits of the selected
934         // lanes above. This instruction can only be used when each lane
935         // selector chooses from the corresponding lane in either of the two
936         // operands, meaning the Nth lane selection must satisfy `lane % 8 ==
937         // N`.
938         //
939         // This helper closure is used to calculate the value of the
940         // corresponding bit.
941         let bit = |x: u8, c: u8| {
942             if x % 8 == c {
943                 if x < 8 { Some(0) } else { Some(1 << c) }
944             } else {
945                 None
946             }
947         };
948         Some(
949             bit(a, 0)?
950                 | bit(b, 1)?
951                 | bit(c, 2)?
952                 | bit(d, 3)?
953                 | bit(e, 4)?
954                 | bit(f, 5)?
955                 | bit(g, 6)?
956                 | bit(h, 7)?,
957         )
958     }
959 
xmi_imm(&mut self, imm: u32) -> XmmMemImm960     fn xmi_imm(&mut self, imm: u32) -> XmmMemImm {
961         XmmMemImm::unwrap_new(RegMemImm::imm(imm))
962     }
963 
insert_i8x16_lane_hole(&mut self, hole_idx: u8) -> VCodeConstant964     fn insert_i8x16_lane_hole(&mut self, hole_idx: u8) -> VCodeConstant {
965         let mask = -1i128 as u128;
966         self.emit_u128_le_const(mask ^ (0xff << (hole_idx * 8)))
967     }
968 
writable_invalid_gpr(&mut self) -> WritableGpr969     fn writable_invalid_gpr(&mut self) -> WritableGpr {
970         let reg = Gpr::new(self.invalid_reg()).unwrap();
971         WritableGpr::from_reg(reg)
972     }
973 
atomic128_rmw_seq_args( &mut self, op: &Atomic128RmwSeqOp, mem_low: &SyntheticAmode, mem_high: &SyntheticAmode, operand_low: Gpr, operand_high: Gpr, temp_low: WritableGpr, temp_high: WritableGpr, dst_old_low: WritableGpr, dst_old_high: WritableGpr, ) -> BoxAtomic128RmwSeqArgs974     fn atomic128_rmw_seq_args(
975         &mut self,
976         op: &Atomic128RmwSeqOp,
977         mem_low: &SyntheticAmode,
978         mem_high: &SyntheticAmode,
979         operand_low: Gpr,
980         operand_high: Gpr,
981         temp_low: WritableGpr,
982         temp_high: WritableGpr,
983         dst_old_low: WritableGpr,
984         dst_old_high: WritableGpr,
985     ) -> BoxAtomic128RmwSeqArgs {
986         Box::new(Atomic128RmwSeqArgs {
987             op: *op,
988             mem_low: mem_low.clone(),
989             mem_high: mem_high.clone(),
990             operand_low,
991             operand_high,
992             temp_low,
993             temp_high,
994             dst_old_low,
995             dst_old_high,
996         })
997     }
998 
atomic128_xchg_seq_args( &mut self, mem_low: &SyntheticAmode, mem_high: &SyntheticAmode, operand_low: Gpr, operand_high: Gpr, dst_old_low: WritableGpr, dst_old_high: WritableGpr, ) -> BoxAtomic128XchgSeqArgs999     fn atomic128_xchg_seq_args(
1000         &mut self,
1001         mem_low: &SyntheticAmode,
1002         mem_high: &SyntheticAmode,
1003         operand_low: Gpr,
1004         operand_high: Gpr,
1005         dst_old_low: WritableGpr,
1006         dst_old_high: WritableGpr,
1007     ) -> BoxAtomic128XchgSeqArgs {
1008         Box::new(Atomic128XchgSeqArgs {
1009             mem_low: mem_low.clone(),
1010             mem_high: mem_high.clone(),
1011             operand_low,
1012             operand_high,
1013             dst_old_low,
1014             dst_old_high,
1015         })
1016     }
1017 
1018     ////////////////////////////////////////////////////////////////////////////
1019     ///// External assembler methods.
1020     ////////////////////////////////////////////////////////////////////////////
1021 
is_imm8(&mut self, src: &GprMemImm) -> Option<u8>1022     fn is_imm8(&mut self, src: &GprMemImm) -> Option<u8> {
1023         match src.clone().to_reg_mem_imm() {
1024             RegMemImm::Imm { simm32 } => {
1025                 Some(i8::try_from(simm32.cast_signed()).ok()?.cast_unsigned())
1026             }
1027             _ => None,
1028         }
1029     }
1030 
is_imm8_xmm(&mut self, src: &XmmMemImm) -> Option<u8>1031     fn is_imm8_xmm(&mut self, src: &XmmMemImm) -> Option<u8> {
1032         match src.clone().to_reg_mem_imm() {
1033             RegMemImm::Imm { simm32 } => {
1034                 Some(i8::try_from(simm32.cast_signed()).ok()?.cast_unsigned())
1035             }
1036             _ => None,
1037         }
1038     }
1039 
is_simm8(&mut self, src: &GprMemImm) -> Option<i8>1040     fn is_simm8(&mut self, src: &GprMemImm) -> Option<i8> {
1041         match src.clone().to_reg_mem_imm() {
1042             RegMemImm::Imm { simm32 } => Some(i8::try_from(simm32.cast_signed()).ok()?),
1043             _ => None,
1044         }
1045     }
1046 
is_imm16(&mut self, src: &GprMemImm) -> Option<u16>1047     fn is_imm16(&mut self, src: &GprMemImm) -> Option<u16> {
1048         match src.clone().to_reg_mem_imm() {
1049             RegMemImm::Imm { simm32 } => {
1050                 Some(i16::try_from(simm32.cast_signed()).ok()?.cast_unsigned())
1051             }
1052             _ => None,
1053         }
1054     }
1055 
is_simm16(&mut self, src: &GprMemImm) -> Option<i16>1056     fn is_simm16(&mut self, src: &GprMemImm) -> Option<i16> {
1057         match src.clone().to_reg_mem_imm() {
1058             RegMemImm::Imm { simm32 } => Some(i16::try_from(simm32.cast_signed()).ok()?),
1059             _ => None,
1060         }
1061     }
1062 
is_imm32(&mut self, src: &GprMemImm) -> Option<u32>1063     fn is_imm32(&mut self, src: &GprMemImm) -> Option<u32> {
1064         match src.clone().to_reg_mem_imm() {
1065             RegMemImm::Imm { simm32 } => Some(simm32),
1066             _ => None,
1067         }
1068     }
1069 
is_simm32(&mut self, src: &GprMemImm) -> Option<i32>1070     fn is_simm32(&mut self, src: &GprMemImm) -> Option<i32> {
1071         match src.clone().to_reg_mem_imm() {
1072             RegMemImm::Imm { simm32 } => Some(simm32 as i32),
1073             _ => None,
1074         }
1075     }
1076 
is_gpr(&mut self, src: &GprMemImm) -> Option<Gpr>1077     fn is_gpr(&mut self, src: &GprMemImm) -> Option<Gpr> {
1078         match src.clone().to_reg_mem_imm() {
1079             RegMemImm::Reg { reg } => Gpr::new(reg),
1080             _ => None,
1081         }
1082     }
1083 
is_xmm(&mut self, src: &XmmMem) -> Option<Xmm>1084     fn is_xmm(&mut self, src: &XmmMem) -> Option<Xmm> {
1085         match src.clone().to_reg_mem() {
1086             RegMem::Reg { reg } => Xmm::new(reg),
1087             _ => None,
1088         }
1089     }
1090 
is_gpr_mem(&mut self, src: &GprMemImm) -> Option<GprMem>1091     fn is_gpr_mem(&mut self, src: &GprMemImm) -> Option<GprMem> {
1092         match src.clone().to_reg_mem_imm() {
1093             RegMemImm::Reg { reg } => GprMem::new(RegMem::Reg { reg }),
1094             RegMemImm::Mem { addr } => GprMem::new(RegMem::Mem { addr }),
1095             _ => None,
1096         }
1097     }
1098 
is_xmm_mem(&mut self, src: &XmmMemImm) -> Option<XmmMem>1099     fn is_xmm_mem(&mut self, src: &XmmMemImm) -> Option<XmmMem> {
1100         match src.clone().to_reg_mem_imm() {
1101             RegMemImm::Reg { reg } => XmmMem::new(RegMem::Reg { reg }),
1102             RegMemImm::Mem { addr } => XmmMem::new(RegMem::Mem { addr }),
1103             _ => None,
1104         }
1105     }
1106 
is_mem(&mut self, src: &XmmMem) -> Option<SyntheticAmode>1107     fn is_mem(&mut self, src: &XmmMem) -> Option<SyntheticAmode> {
1108         match src.clone().to_reg_mem() {
1109             RegMem::Reg { .. } => None,
1110             RegMem::Mem { addr } => Some(addr),
1111         }
1112     }
1113 
1114     // Custom constructors for `mulx` which only calculates the high half of the
1115     // result meaning that the same output operand is used in both destination
1116     // registers. This is in contrast to the assembler-generated version of this
1117     // instruction which generates two distinct temporary registers for output
1118     // which calculates both the high and low halves of the result.
1119 
x64_mulxl_rvm_hi(&mut self, src1: &GprMem, src2: Gpr) -> Gpr1120     fn x64_mulxl_rvm_hi(&mut self, src1: &GprMem, src2: Gpr) -> Gpr {
1121         let ret = self.temp_writable_gpr();
1122         let src1 = self.convert_gpr_mem_to_assembler_read_gpr_mem(src1);
1123         let inst = asm::inst::mulxl_rvm::new(ret, ret, src1, src2);
1124         self.emit(&MInst::External { inst: inst.into() });
1125         ret.to_reg()
1126     }
1127 
x64_mulxq_rvm_hi(&mut self, src1: &GprMem, src2: Gpr) -> Gpr1128     fn x64_mulxq_rvm_hi(&mut self, src1: &GprMem, src2: Gpr) -> Gpr {
1129         let ret = self.temp_writable_gpr();
1130         let src1 = self.convert_gpr_mem_to_assembler_read_gpr_mem(src1);
1131         let inst = asm::inst::mulxq_rvm::new(ret, ret, src1, src2);
1132         self.emit(&MInst::External { inst: inst.into() });
1133         ret.to_reg()
1134     }
1135 
bt_imm(&mut self, val: u64) -> Option<u8>1136     fn bt_imm(&mut self, val: u64) -> Option<u8> {
1137         if val.count_ones() == 1 {
1138             Some(u8::try_from(val.trailing_zeros()).unwrap())
1139         } else {
1140             None
1141         }
1142     }
1143 }
1144 
1145 impl IsleContext<'_, '_, MInst, X64Backend> {
load_xmm_unaligned(&mut self, addr: SyntheticAmode) -> Xmm1146     fn load_xmm_unaligned(&mut self, addr: SyntheticAmode) -> Xmm {
1147         let tmp = self.lower_ctx.alloc_tmp(types::F32X4).only_reg().unwrap();
1148         self.lower_ctx.emit(MInst::External {
1149             inst: asm::inst::movdqu_a::new(
1150                 Writable::from_reg(Xmm::unwrap_new(tmp.to_reg())),
1151                 asm::XmmMem::Mem(addr.into()),
1152             )
1153             .into(),
1154         });
1155         Xmm::unwrap_new(tmp.to_reg())
1156     }
1157 
1158     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_gpr_to_assembler_read_write_gpr(&mut self, read: Gpr) -> asm::Gpr<PairedGpr>1159     fn convert_gpr_to_assembler_read_write_gpr(&mut self, read: Gpr) -> asm::Gpr<PairedGpr> {
1160         let write = self.lower_ctx.alloc_tmp(types::I64).only_reg().unwrap();
1161         let write = WritableGpr::from_writable_reg(write).unwrap();
1162         asm::Gpr::new(PairedGpr { read, write })
1163     }
1164 
1165     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_gpr_to_assembler_fixed_read_write_gpr<const E: u8>( &mut self, read: Gpr, ) -> asm::Fixed<PairedGpr, E>1166     fn convert_gpr_to_assembler_fixed_read_write_gpr<const E: u8>(
1167         &mut self,
1168         read: Gpr,
1169     ) -> asm::Fixed<PairedGpr, E> {
1170         let write = self.lower_ctx.alloc_tmp(types::I64).only_reg().unwrap();
1171         let write = WritableGpr::from_writable_reg(write).unwrap();
1172         asm::Fixed(PairedGpr { read, write })
1173     }
1174 
1175     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_xmm_to_assembler_read_write_xmm(&mut self, read: Xmm) -> asm::Xmm<PairedXmm>1176     fn convert_xmm_to_assembler_read_write_xmm(&mut self, read: Xmm) -> asm::Xmm<PairedXmm> {
1177         let write = self.lower_ctx.alloc_tmp(types::F32X4).only_reg().unwrap();
1178         let write = WritableXmm::from_writable_reg(write).unwrap();
1179         asm::Xmm::new(PairedXmm { read, write })
1180     }
1181 
1182     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_gpr_mem_to_assembler_read_gpr_mem(&self, read: &GprMem) -> asm::GprMem<Gpr, Gpr>1183     fn convert_gpr_mem_to_assembler_read_gpr_mem(&self, read: &GprMem) -> asm::GprMem<Gpr, Gpr> {
1184         match read.clone().into() {
1185             RegMem::Reg { reg } => asm::GprMem::Gpr(Gpr::new(reg).unwrap()),
1186             RegMem::Mem { addr } => asm::GprMem::Mem(addr.into()),
1187         }
1188     }
1189 
1190     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_xmm_mem_to_assembler_read_xmm_mem_aligned( &self, read: &XmmMemAligned, ) -> asm::XmmMem<Xmm, Gpr>1191     fn convert_xmm_mem_to_assembler_read_xmm_mem_aligned(
1192         &self,
1193         read: &XmmMemAligned,
1194     ) -> asm::XmmMem<Xmm, Gpr> {
1195         match read.clone().into() {
1196             RegMem::Reg { reg } => asm::XmmMem::Xmm(Xmm::new(reg).unwrap()),
1197             RegMem::Mem { addr } => asm::XmmMem::Mem(addr.into()),
1198         }
1199     }
1200 
1201     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_xmm_mem_to_assembler_read_xmm_mem(&self, read: &XmmMem) -> asm::XmmMem<Xmm, Gpr>1202     fn convert_xmm_mem_to_assembler_read_xmm_mem(&self, read: &XmmMem) -> asm::XmmMem<Xmm, Gpr> {
1203         match read.clone().into() {
1204             RegMem::Reg { reg } => asm::XmmMem::Xmm(Xmm::new(reg).unwrap()),
1205             RegMem::Mem { addr } => asm::XmmMem::Mem(addr.into()),
1206         }
1207     }
1208 
1209     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_xmm_mem_to_assembler_write_xmm_mem( &self, write: &XmmMem, ) -> asm::XmmMem<Writable<Xmm>, Gpr>1210     fn convert_xmm_mem_to_assembler_write_xmm_mem(
1211         &self,
1212         write: &XmmMem,
1213     ) -> asm::XmmMem<Writable<Xmm>, Gpr> {
1214         match write.clone().into() {
1215             RegMem::Reg { reg } => asm::XmmMem::Xmm(Writable::from_reg(Xmm::new(reg).unwrap())),
1216             RegMem::Mem { addr } => asm::XmmMem::Mem(addr.into()),
1217         }
1218     }
1219 
1220     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_xmm_mem_to_assembler_write_xmm_mem_aligned( &self, write: &XmmMemAligned, ) -> asm::XmmMem<Writable<Xmm>, Gpr>1221     fn convert_xmm_mem_to_assembler_write_xmm_mem_aligned(
1222         &self,
1223         write: &XmmMemAligned,
1224     ) -> asm::XmmMem<Writable<Xmm>, Gpr> {
1225         match write.clone().into() {
1226             RegMem::Reg { reg } => asm::XmmMem::Xmm(Writable::from_reg(Xmm::new(reg).unwrap())),
1227             RegMem::Mem { addr } => asm::XmmMem::Mem(addr.into()),
1228         }
1229     }
1230 
1231     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_gpr_mem_to_assembler_read_write_gpr_mem( &mut self, read: &GprMem, ) -> asm::GprMem<PairedGpr, Gpr>1232     fn convert_gpr_mem_to_assembler_read_write_gpr_mem(
1233         &mut self,
1234         read: &GprMem,
1235     ) -> asm::GprMem<PairedGpr, Gpr> {
1236         match read.clone().into() {
1237             RegMem::Reg { reg } => asm::GprMem::Gpr(
1238                 *self
1239                     .convert_gpr_to_assembler_read_write_gpr(Gpr::new(reg).unwrap())
1240                     .as_ref(),
1241             ),
1242             RegMem::Mem { addr } => asm::GprMem::Mem(addr.into()),
1243         }
1244     }
1245 
1246     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_gpr_mem_to_assembler_write_gpr_mem( &mut self, read: &GprMem, ) -> asm::GprMem<WritableGpr, Gpr>1247     fn convert_gpr_mem_to_assembler_write_gpr_mem(
1248         &mut self,
1249         read: &GprMem,
1250     ) -> asm::GprMem<WritableGpr, Gpr> {
1251         match read.clone().into() {
1252             RegMem::Reg { reg } => asm::GprMem::Gpr(WritableGpr::from_reg(Gpr::new(reg).unwrap())),
1253             RegMem::Mem { addr } => asm::GprMem::Mem(addr.into()),
1254         }
1255     }
1256 
1257     /// Helper used by code generated by the `cranelift-assembler-x64` crate.
convert_amode_to_assembler_amode(&mut self, amode: &SyntheticAmode) -> asm::Amode<Gpr>1258     fn convert_amode_to_assembler_amode(&mut self, amode: &SyntheticAmode) -> asm::Amode<Gpr> {
1259         amode.clone().into()
1260     }
1261 }
1262 
1263 // Since x64 doesn't have 8x16 shifts and we must use a 16x8 shift instead, we
1264 // need to fix up the bits that migrate from one half of the lane to the
1265 // other. Each 16-byte mask is indexed by the shift amount: e.g. if we shift
1266 // right by 0 (no movement), we want to retain all the bits so we mask with
1267 // `0xff`; if we shift right by 1, we want to retain all bits except the MSB so
1268 // we mask with `0x7f`; etc.
1269 
1270 #[rustfmt::skip] // Preserve 16 bytes (i.e. one mask) per row.
1271 const I8X16_ISHL_MASKS: [u8; 128] = [
1272     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1273     0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
1274     0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
1275     0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
1276     0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
1277     0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
1278     0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
1279     0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
1280 ];
1281 
1282 #[rustfmt::skip] // Preserve 16 bytes (i.e. one mask) per row.
1283 const I8X16_USHR_MASKS: [u8; 128] = [
1284     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1285     0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
1286     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
1287     0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
1288     0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
1289     0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
1290     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
1291     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1292 ];
1293