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