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