Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/aarch64/inst/imms.rs
1693 views
1
//! AArch64 ISA definitions: immediate constants.
2
3
use crate::ir::types::*;
4
use crate::isa::aarch64::inst::{OperandSize, ScalarSize};
5
use crate::machinst::PrettyPrint;
6
7
use std::string::String;
8
9
/// An immediate that represents the NZCV flags.
10
#[derive(Clone, Copy, Debug)]
11
pub struct NZCV {
12
/// The negative condition flag.
13
n: bool,
14
/// The zero condition flag.
15
z: bool,
16
/// The carry condition flag.
17
c: bool,
18
/// The overflow condition flag.
19
v: bool,
20
}
21
22
impl NZCV {
23
/// Create a new NZCV flags representation.
24
pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV {
25
NZCV { n, z, c, v }
26
}
27
28
/// Bits for encoding.
29
pub fn bits(&self) -> u32 {
30
(u32::from(self.n) << 3)
31
| (u32::from(self.z) << 2)
32
| (u32::from(self.c) << 1)
33
| u32::from(self.v)
34
}
35
}
36
37
/// An unsigned 5-bit immediate.
38
#[derive(Clone, Copy, Debug)]
39
pub struct UImm5 {
40
/// The value.
41
value: u8,
42
}
43
44
impl UImm5 {
45
/// Create an unsigned 5-bit immediate from u8.
46
pub fn maybe_from_u8(value: u8) -> Option<UImm5> {
47
if value < 32 {
48
Some(UImm5 { value })
49
} else {
50
None
51
}
52
}
53
54
/// Bits for encoding.
55
pub fn bits(&self) -> u32 {
56
u32::from(self.value)
57
}
58
}
59
60
/// A signed, scaled 7-bit offset.
61
#[derive(Clone, Copy, Debug)]
62
pub struct SImm7Scaled {
63
/// The value.
64
pub value: i16,
65
/// multiplied by the size of this type
66
pub scale_ty: Type,
67
}
68
69
impl SImm7Scaled {
70
/// Create a SImm7Scaled from a raw offset and the known scale type, if
71
/// possible.
72
pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<SImm7Scaled> {
73
assert!(scale_ty == I64 || scale_ty == I32 || scale_ty == F64 || scale_ty == I8X16);
74
let scale = scale_ty.bytes();
75
assert!(scale.is_power_of_two());
76
let scale = i64::from(scale);
77
let upper_limit = 63 * scale;
78
let lower_limit = -(64 * scale);
79
if value >= lower_limit && value <= upper_limit && (value & (scale - 1)) == 0 {
80
Some(SImm7Scaled {
81
value: i16::try_from(value).unwrap(),
82
scale_ty,
83
})
84
} else {
85
None
86
}
87
}
88
89
/// Bits for encoding.
90
pub fn bits(&self) -> u32 {
91
let ty_bytes: i16 = self.scale_ty.bytes() as i16;
92
let scaled: i16 = self.value / ty_bytes;
93
assert!(scaled <= 63 && scaled >= -64);
94
let scaled: i8 = scaled as i8;
95
let encoded: u32 = scaled as u32;
96
encoded & 0x7f
97
}
98
}
99
100
/// Floating-point unit immediate left shift.
101
#[derive(Clone, Copy, Debug)]
102
pub struct FPULeftShiftImm {
103
/// Shift amount.
104
pub amount: u8,
105
/// Lane size in bits.
106
pub lane_size_in_bits: u8,
107
}
108
109
impl FPULeftShiftImm {
110
/// Create a floating-point unit immediate left shift from u8.
111
pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
112
debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
113
if amount < lane_size_in_bits {
114
Some(Self {
115
amount,
116
lane_size_in_bits,
117
})
118
} else {
119
None
120
}
121
}
122
123
/// Returns the encoding of the immediate.
124
pub fn enc(&self) -> u32 {
125
debug_assert!(self.lane_size_in_bits.is_power_of_two());
126
debug_assert!(self.lane_size_in_bits > self.amount);
127
// The encoding of the immediate follows the table below,
128
// where xs encode the shift amount.
129
//
130
// | lane_size_in_bits | encoding |
131
// +------------------------------+
132
// | 8 | 0001xxx |
133
// | 16 | 001xxxx |
134
// | 32 | 01xxxxx |
135
// | 64 | 1xxxxxx |
136
//
137
// The highest one bit is represented by `lane_size_in_bits`. Since
138
// `lane_size_in_bits` is a power of 2 and `amount` is less
139
// than `lane_size_in_bits`, they can be ORed
140
// together to produced the encoded value.
141
u32::from(self.lane_size_in_bits | self.amount)
142
}
143
}
144
145
/// Floating-point unit immediate right shift.
146
#[derive(Clone, Copy, Debug)]
147
pub struct FPURightShiftImm {
148
/// Shift amount.
149
pub amount: u8,
150
/// Lane size in bits.
151
pub lane_size_in_bits: u8,
152
}
153
154
impl FPURightShiftImm {
155
/// Create a floating-point unit immediate right shift from u8.
156
pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
157
debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
158
if amount > 0 && amount <= lane_size_in_bits {
159
Some(Self {
160
amount,
161
lane_size_in_bits,
162
})
163
} else {
164
None
165
}
166
}
167
168
/// Returns encoding of the immediate.
169
pub fn enc(&self) -> u32 {
170
debug_assert_ne!(0, self.amount);
171
// The encoding of the immediate follows the table below,
172
// where xs encodes the negated shift amount.
173
//
174
// | lane_size_in_bits | encoding |
175
// +------------------------------+
176
// | 8 | 0001xxx |
177
// | 16 | 001xxxx |
178
// | 32 | 01xxxxx |
179
// | 64 | 1xxxxxx |
180
//
181
// The shift amount is negated such that a shift amount
182
// of 1 (in 64-bit) is encoded as 0b111111 and a shift
183
// amount of 64 is encoded as 0b000000,
184
// in the bottom 6 bits.
185
u32::from((self.lane_size_in_bits * 2) - self.amount)
186
}
187
}
188
189
/// a 9-bit signed offset.
190
#[derive(Clone, Copy, Debug)]
191
pub struct SImm9 {
192
/// The value.
193
pub value: i16,
194
}
195
196
impl SImm9 {
197
/// Create a signed 9-bit offset from a full-range value, if possible.
198
pub fn maybe_from_i64(value: i64) -> Option<SImm9> {
199
if value >= -256 && value <= 255 {
200
Some(SImm9 {
201
value: value as i16,
202
})
203
} else {
204
None
205
}
206
}
207
208
/// Bits for encoding.
209
pub fn bits(&self) -> u32 {
210
(self.value as u32) & 0x1ff
211
}
212
213
/// Signed value of immediate.
214
pub fn value(&self) -> i32 {
215
self.value as i32
216
}
217
}
218
219
/// An unsigned, scaled 12-bit offset.
220
#[derive(Clone, Copy, Debug)]
221
pub struct UImm12Scaled {
222
/// The value.
223
value: u16,
224
/// multiplied by the size of this type
225
scale_ty: Type,
226
}
227
228
impl UImm12Scaled {
229
/// Create a UImm12Scaled from a raw offset and the known scale type, if
230
/// possible.
231
pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled> {
232
let scale = scale_ty.bytes();
233
assert!(scale.is_power_of_two());
234
let scale = scale as i64;
235
let limit = 4095 * scale;
236
if value >= 0 && value <= limit && (value & (scale - 1)) == 0 {
237
Some(UImm12Scaled {
238
value: value as u16,
239
scale_ty,
240
})
241
} else {
242
None
243
}
244
}
245
246
/// Create a zero immediate of this format.
247
pub fn zero(scale_ty: Type) -> UImm12Scaled {
248
UImm12Scaled { value: 0, scale_ty }
249
}
250
251
/// Encoded bits.
252
pub fn bits(&self) -> u32 {
253
(self.value as u32 / self.scale_ty.bytes()) & 0xfff
254
}
255
256
/// Value after scaling.
257
pub fn value(&self) -> u32 {
258
self.value as u32
259
}
260
}
261
262
/// A shifted immediate value in 'imm12' format: supports 12 bits, shifted
263
/// left by 0 or 12 places.
264
#[derive(Copy, Clone, Debug)]
265
pub struct Imm12 {
266
/// The immediate bits.
267
pub bits: u16,
268
/// Whether the immediate bits are shifted left by 12 or not.
269
pub shift12: bool,
270
}
271
272
impl Imm12 {
273
/// Handy 0-value constant.
274
pub const ZERO: Imm12 = Imm12 {
275
bits: 0,
276
shift12: false,
277
};
278
279
/// Compute a Imm12 from raw bits, if possible.
280
pub fn maybe_from_u64(val: u64) -> Option<Imm12> {
281
if val & !0xfff == 0 {
282
Some(Imm12 {
283
bits: val as u16,
284
shift12: false,
285
})
286
} else if val & !(0xfff << 12) == 0 {
287
Some(Imm12 {
288
bits: (val >> 12) as u16,
289
shift12: true,
290
})
291
} else {
292
None
293
}
294
}
295
296
/// Bits for 2-bit "shift" field in e.g. AddI.
297
pub fn shift_bits(&self) -> u32 {
298
if self.shift12 { 0b01 } else { 0b00 }
299
}
300
301
/// Bits for 12-bit "imm" field in e.g. AddI.
302
pub fn imm_bits(&self) -> u32 {
303
self.bits as u32
304
}
305
306
/// Get the actual value that this immediate corresponds to.
307
pub fn value(&self) -> u32 {
308
let base = self.bits as u32;
309
if self.shift12 { base << 12 } else { base }
310
}
311
}
312
313
/// An immediate for logical instructions.
314
#[derive(Copy, Clone, Debug, PartialEq)]
315
pub struct ImmLogic {
316
/// The actual value.
317
value: u64,
318
/// `N` flag.
319
pub n: bool,
320
/// `S` field: element size and element bits.
321
pub r: u8,
322
/// `R` field: rotate amount.
323
pub s: u8,
324
/// Was this constructed for a 32-bit or 64-bit instruction?
325
pub size: OperandSize,
326
}
327
328
impl ImmLogic {
329
/// Compute an ImmLogic from raw bits, if possible.
330
pub fn maybe_from_u64(value: u64, ty: Type) -> Option<ImmLogic> {
331
// Note: This function is a port of VIXL's Assembler::IsImmLogical.
332
333
if ty != I64 && ty != I32 {
334
return None;
335
}
336
let operand_size = OperandSize::from_ty(ty);
337
338
let original_value = value;
339
340
let value = if ty == I32 {
341
// To handle 32-bit logical immediates, the very easiest thing is to repeat
342
// the input value twice to make a 64-bit word. The correct encoding of that
343
// as a logical immediate will also be the correct encoding of the 32-bit
344
// value.
345
346
// Avoid making the assumption that the most-significant 32 bits are zero by
347
// shifting the value left and duplicating it.
348
let value = value << 32;
349
value | value >> 32
350
} else {
351
value
352
};
353
354
// Logical immediates are encoded using parameters n, imm_s and imm_r using
355
// the following table:
356
//
357
// N imms immr size S R
358
// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
359
// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
360
// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
361
// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
362
// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
363
// 0 11110s xxxxxr 2 UInt(s) UInt(r)
364
// (s bits must not be all set)
365
//
366
// A pattern is constructed of size bits, where the least significant S+1 bits
367
// are set. The pattern is rotated right by R, and repeated across a 32 or
368
// 64-bit value, depending on destination register width.
369
//
370
// Put another way: the basic format of a logical immediate is a single
371
// contiguous stretch of 1 bits, repeated across the whole word at intervals
372
// given by a power of 2. To identify them quickly, we first locate the
373
// lowest stretch of 1 bits, then the next 1 bit above that; that combination
374
// is different for every logical immediate, so it gives us all the
375
// information we need to identify the only logical immediate that our input
376
// could be, and then we simply check if that's the value we actually have.
377
//
378
// (The rotation parameter does give the possibility of the stretch of 1 bits
379
// going 'round the end' of the word. To deal with that, we observe that in
380
// any situation where that happens the bitwise NOT of the value is also a
381
// valid logical immediate. So we simply invert the input whenever its low bit
382
// is set, and then we know that the rotated case can't arise.)
383
let (value, inverted) = if value & 1 == 1 {
384
(!value, true)
385
} else {
386
(value, false)
387
};
388
389
if value == 0 {
390
return None;
391
}
392
393
// The basic analysis idea: imagine our input word looks like this.
394
//
395
// 0011111000111110001111100011111000111110001111100011111000111110
396
// c b a
397
// |<--d-->|
398
//
399
// We find the lowest set bit (as an actual power-of-2 value, not its index)
400
// and call it a. Then we add a to our original number, which wipes out the
401
// bottommost stretch of set bits and replaces it with a 1 carried into the
402
// next zero bit. Then we look for the new lowest set bit, which is in
403
// position b, and subtract it, so now our number is just like the original
404
// but with the lowest stretch of set bits completely gone. Now we find the
405
// lowest set bit again, which is position c in the diagram above. Then we'll
406
// measure the distance d between bit positions a and c (using CLZ), and that
407
// tells us that the only valid logical immediate that could possibly be equal
408
// to this number is the one in which a stretch of bits running from a to just
409
// below b is replicated every d bits.
410
fn lowest_set_bit(value: u64) -> u64 {
411
let bit = value.trailing_zeros();
412
1u64.checked_shl(bit).unwrap_or(0)
413
}
414
let a = lowest_set_bit(value);
415
assert_ne!(0, a);
416
let value_plus_a = value.wrapping_add(a);
417
let b = lowest_set_bit(value_plus_a);
418
let value_plus_a_minus_b = value_plus_a - b;
419
let c = lowest_set_bit(value_plus_a_minus_b);
420
421
let (d, clz_a, out_n, mask) = if c != 0 {
422
// The general case, in which there is more than one stretch of set bits.
423
// Compute the repeat distance d, and set up a bitmask covering the basic
424
// unit of repetition (i.e. a word with the bottom d bits set). Also, in all
425
// of these cases the N bit of the output will be zero.
426
let clz_a = a.leading_zeros();
427
let clz_c = c.leading_zeros();
428
let d = clz_a - clz_c;
429
let mask = (1 << d) - 1;
430
(d, clz_a, 0, mask)
431
} else {
432
(64, a.leading_zeros(), 1, u64::max_value())
433
};
434
435
// If the repeat period d is not a power of two, it can't be encoded.
436
if !d.is_power_of_two() {
437
return None;
438
}
439
440
if ((b.wrapping_sub(a)) & !mask) != 0 {
441
// If the bit stretch (b - a) does not fit within the mask derived from the
442
// repeat period, then fail.
443
return None;
444
}
445
446
// The only possible option is b - a repeated every d bits. Now we're going to
447
// actually construct the valid logical immediate derived from that
448
// specification, and see if it equals our original input.
449
//
450
// To repeat a value every d bits, we multiply it by a number of the form
451
// (1 + 2^d + 2^(2d) + ...), i.e. 0x0001000100010001 or similar. These can
452
// be derived using a table lookup on CLZ(d).
453
const MULTIPLIERS: [u64; 6] = [
454
0x0000000000000001,
455
0x0000000100000001,
456
0x0001000100010001,
457
0x0101010101010101,
458
0x1111111111111111,
459
0x5555555555555555,
460
];
461
let multiplier = MULTIPLIERS[(u64::from(d).leading_zeros() - 57) as usize];
462
let candidate = b.wrapping_sub(a) * multiplier;
463
464
if value != candidate {
465
// The candidate pattern doesn't match our input value, so fail.
466
return None;
467
}
468
469
// We have a match! This is a valid logical immediate, so now we have to
470
// construct the bits and pieces of the instruction encoding that generates
471
// it.
472
473
// Count the set bits in our basic stretch. The special case of clz(0) == -1
474
// makes the answer come out right for stretches that reach the very top of
475
// the word (e.g. numbers like 0xffffc00000000000).
476
let clz_b = if b == 0 {
477
u32::max_value() // -1
478
} else {
479
b.leading_zeros()
480
};
481
let s = clz_a.wrapping_sub(clz_b);
482
483
// Decide how many bits to rotate right by, to put the low bit of that basic
484
// stretch in position a.
485
let (s, r) = if inverted {
486
// If we inverted the input right at the start of this function, here's
487
// where we compensate: the number of set bits becomes the number of clear
488
// bits, and the rotation count is based on position b rather than position
489
// a (since b is the location of the 'lowest' 1 bit after inversion).
490
// Need wrapping for when clz_b is max_value() (for when b == 0).
491
(d - s, clz_b.wrapping_add(1) & (d - 1))
492
} else {
493
(s, (clz_a + 1) & (d - 1))
494
};
495
496
// Now we're done, except for having to encode the S output in such a way that
497
// it gives both the number of set bits and the length of the repeated
498
// segment. The s field is encoded like this:
499
//
500
// imms size S
501
// ssssss 64 UInt(ssssss)
502
// 0sssss 32 UInt(sssss)
503
// 10ssss 16 UInt(ssss)
504
// 110sss 8 UInt(sss)
505
// 1110ss 4 UInt(ss)
506
// 11110s 2 UInt(s)
507
//
508
// So we 'or' (2 * -d) with our computed s to form imms.
509
let s = ((d * 2).wrapping_neg() | (s - 1)) & 0x3f;
510
debug_assert!(u8::try_from(r).is_ok());
511
debug_assert!(u8::try_from(s).is_ok());
512
Some(ImmLogic {
513
value: original_value,
514
n: out_n != 0,
515
r: r as u8,
516
s: s as u8,
517
size: operand_size,
518
})
519
}
520
521
/// Returns bits ready for encoding: (N:1, R:6, S:6)
522
pub fn enc_bits(&self) -> u32 {
523
((self.n as u32) << 12) | ((self.r as u32) << 6) | (self.s as u32)
524
}
525
526
/// Returns the value that this immediate represents.
527
pub fn value(&self) -> u64 {
528
self.value
529
}
530
531
/// Return an immediate for the bitwise-inverted value.
532
pub fn invert(&self) -> ImmLogic {
533
// For every ImmLogical immediate, the inverse can also be encoded.
534
Self::maybe_from_u64(!self.value, self.size.to_ty()).unwrap()
535
}
536
}
537
538
/// An immediate for shift instructions.
539
#[derive(Copy, Clone, Debug)]
540
pub struct ImmShift {
541
/// 6-bit shift amount.
542
pub imm: u8,
543
}
544
545
impl ImmShift {
546
/// Create an ImmShift from raw bits, if possible.
547
pub fn maybe_from_u64(val: u64) -> Option<ImmShift> {
548
if val < 64 {
549
Some(ImmShift { imm: val as u8 })
550
} else {
551
None
552
}
553
}
554
555
/// Get the immediate value.
556
pub fn value(&self) -> u8 {
557
self.imm
558
}
559
}
560
561
/// A 16-bit immediate for a MOVZ instruction, with a {0,16,32,48}-bit shift.
562
#[derive(Clone, Copy, Debug)]
563
pub struct MoveWideConst {
564
/// The value.
565
pub bits: u16,
566
/// Result is `bits` shifted 16*shift bits to the left.
567
pub shift: u8,
568
}
569
570
impl MoveWideConst {
571
/// Construct a MoveWideConst from an arbitrary 64-bit constant if possible.
572
pub fn maybe_from_u64(value: u64) -> Option<MoveWideConst> {
573
let mask0 = 0x0000_0000_0000_ffffu64;
574
let mask1 = 0x0000_0000_ffff_0000u64;
575
let mask2 = 0x0000_ffff_0000_0000u64;
576
let mask3 = 0xffff_0000_0000_0000u64;
577
578
if value == (value & mask0) {
579
return Some(MoveWideConst {
580
bits: (value & mask0) as u16,
581
shift: 0,
582
});
583
}
584
if value == (value & mask1) {
585
return Some(MoveWideConst {
586
bits: ((value >> 16) & mask0) as u16,
587
shift: 1,
588
});
589
}
590
if value == (value & mask2) {
591
return Some(MoveWideConst {
592
bits: ((value >> 32) & mask0) as u16,
593
shift: 2,
594
});
595
}
596
if value == (value & mask3) {
597
return Some(MoveWideConst {
598
bits: ((value >> 48) & mask0) as u16,
599
shift: 3,
600
});
601
}
602
None
603
}
604
605
/// Create a `MoveWideCosnt` from a given shift, if possible.
606
pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst> {
607
let shift_enc = shift / 16;
608
if shift_enc > 3 {
609
None
610
} else {
611
Some(MoveWideConst {
612
bits: imm,
613
shift: shift_enc,
614
})
615
}
616
}
617
618
/// Create a zero immediate of this format.
619
pub fn zero() -> MoveWideConst {
620
MoveWideConst { bits: 0, shift: 0 }
621
}
622
}
623
624
/// Advanced SIMD modified immediate as used by MOVI/MVNI.
625
#[derive(Clone, Copy, Debug, PartialEq)]
626
pub struct ASIMDMovModImm {
627
imm: u8,
628
shift: u8,
629
is_64bit: bool,
630
shift_ones: bool,
631
}
632
633
impl ASIMDMovModImm {
634
/// Construct an ASIMDMovModImm from an arbitrary 64-bit constant, if possible.
635
/// Note that the bits in `value` outside of the range specified by `size` are
636
/// ignored; for example, in the case of `ScalarSize::Size8` all bits above the
637
/// lowest 8 are ignored.
638
pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDMovModImm> {
639
match size {
640
ScalarSize::Size8 => Some(ASIMDMovModImm {
641
imm: value as u8,
642
shift: 0,
643
is_64bit: false,
644
shift_ones: false,
645
}),
646
ScalarSize::Size16 => {
647
let value = value as u16;
648
649
if value >> 8 == 0 {
650
Some(ASIMDMovModImm {
651
imm: value as u8,
652
shift: 0,
653
is_64bit: false,
654
shift_ones: false,
655
})
656
} else if value as u8 == 0 {
657
Some(ASIMDMovModImm {
658
imm: (value >> 8) as u8,
659
shift: 8,
660
is_64bit: false,
661
shift_ones: false,
662
})
663
} else {
664
None
665
}
666
}
667
ScalarSize::Size32 => {
668
let value = value as u32;
669
670
// Value is of the form 0x00MMFFFF.
671
if value & 0xFF00FFFF == 0x0000FFFF {
672
let imm = (value >> 16) as u8;
673
674
Some(ASIMDMovModImm {
675
imm,
676
shift: 16,
677
is_64bit: false,
678
shift_ones: true,
679
})
680
// Value is of the form 0x0000MMFF.
681
} else if value & 0xFFFF00FF == 0x000000FF {
682
let imm = (value >> 8) as u8;
683
684
Some(ASIMDMovModImm {
685
imm,
686
shift: 8,
687
is_64bit: false,
688
shift_ones: true,
689
})
690
} else {
691
// Of the 4 bytes, at most one is non-zero.
692
for shift in (0..32).step_by(8) {
693
if value & (0xFF << shift) == value {
694
return Some(ASIMDMovModImm {
695
imm: (value >> shift) as u8,
696
shift,
697
is_64bit: false,
698
shift_ones: false,
699
});
700
}
701
}
702
703
None
704
}
705
}
706
ScalarSize::Size64 => {
707
let mut imm = 0u8;
708
709
// Check if all bytes are either 0 or 0xFF.
710
for i in 0..8 {
711
let b = (value >> (i * 8)) as u8;
712
713
if b == 0 || b == 0xFF {
714
imm |= (b & 1) << i;
715
} else {
716
return None;
717
}
718
}
719
720
Some(ASIMDMovModImm {
721
imm,
722
shift: 0,
723
is_64bit: true,
724
shift_ones: false,
725
})
726
}
727
_ => None,
728
}
729
}
730
731
/// Create a zero immediate of this format.
732
pub fn zero(size: ScalarSize) -> Self {
733
ASIMDMovModImm {
734
imm: 0,
735
shift: 0,
736
is_64bit: size == ScalarSize::Size64,
737
shift_ones: false,
738
}
739
}
740
741
/// Returns the value that this immediate represents.
742
pub fn value(&self) -> (u8, u32, bool) {
743
(self.imm, self.shift as u32, self.shift_ones)
744
}
745
}
746
747
/// Advanced SIMD modified immediate as used by the vector variant of FMOV.
748
#[derive(Clone, Copy, Debug, PartialEq)]
749
pub struct ASIMDFPModImm {
750
imm: u8,
751
size: ScalarSize,
752
}
753
754
impl ASIMDFPModImm {
755
/// Construct an ASIMDFPModImm from an arbitrary 64-bit constant, if possible.
756
pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDFPModImm> {
757
// In all cases immediates are encoded as an 8-bit number 0b_abcdefgh;
758
// let `D` be the inverse of the digit `d`.
759
match size {
760
ScalarSize::Size16 => {
761
// In this case the representable immediates are 16-bit numbers of the form
762
// 0b_aBbb_cdef_gh00_0000.
763
let value = value as u16;
764
let b0_5 = (value >> 6) & 0b111111;
765
let b6 = (value >> 6) & (1 << 6);
766
let b7 = (value >> 8) & (1 << 7);
767
let imm = (b0_5 | b6 | b7) as u8;
768
769
if value == Self::value16(imm) {
770
Some(ASIMDFPModImm { imm, size })
771
} else {
772
None
773
}
774
}
775
ScalarSize::Size32 => {
776
// In this case the representable immediates are 32-bit numbers of the form
777
// 0b_aBbb_bbbc_defg_h000 shifted to the left by 16.
778
let value = value as u32;
779
let b0_5 = (value >> 19) & 0b111111;
780
let b6 = (value >> 19) & (1 << 6);
781
let b7 = (value >> 24) & (1 << 7);
782
let imm = (b0_5 | b6 | b7) as u8;
783
784
if value == Self::value32(imm) {
785
Some(ASIMDFPModImm { imm, size })
786
} else {
787
None
788
}
789
}
790
ScalarSize::Size64 => {
791
// In this case the representable immediates are 64-bit numbers of the form
792
// 0b_aBbb_bbbb_bbcd_efgh shifted to the left by 48.
793
let b0_5 = (value >> 48) & 0b111111;
794
let b6 = (value >> 48) & (1 << 6);
795
let b7 = (value >> 56) & (1 << 7);
796
let imm = (b0_5 | b6 | b7) as u8;
797
798
if value == Self::value64(imm) {
799
Some(ASIMDFPModImm { imm, size })
800
} else {
801
None
802
}
803
}
804
_ => None,
805
}
806
}
807
808
/// Returns bits ready for encoding.
809
pub fn enc_bits(&self) -> u8 {
810
self.imm
811
}
812
813
/// Returns the 16-bit value that corresponds to an 8-bit encoding.
814
fn value16(imm: u8) -> u16 {
815
let imm = imm as u16;
816
let b0_5 = imm & 0b111111;
817
let b6 = (imm >> 6) & 1;
818
let b6_inv = b6 ^ 1;
819
let b7 = (imm >> 7) & 1;
820
821
b0_5 << 6 | (b6 * 0b11) << 12 | b6_inv << 14 | b7 << 15
822
}
823
824
/// Returns the 32-bit value that corresponds to an 8-bit encoding.
825
fn value32(imm: u8) -> u32 {
826
let imm = imm as u32;
827
let b0_5 = imm & 0b111111;
828
let b6 = (imm >> 6) & 1;
829
let b6_inv = b6 ^ 1;
830
let b7 = (imm >> 7) & 1;
831
832
b0_5 << 19 | (b6 * 0b11111) << 25 | b6_inv << 30 | b7 << 31
833
}
834
835
/// Returns the 64-bit value that corresponds to an 8-bit encoding.
836
fn value64(imm: u8) -> u64 {
837
let imm = imm as u64;
838
let b0_5 = imm & 0b111111;
839
let b6 = (imm >> 6) & 1;
840
let b6_inv = b6 ^ 1;
841
let b7 = (imm >> 7) & 1;
842
843
b0_5 << 48 | (b6 * 0b11111111) << 54 | b6_inv << 62 | b7 << 63
844
}
845
}
846
847
impl PrettyPrint for NZCV {
848
fn pretty_print(&self, _: u8) -> String {
849
let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c };
850
format!(
851
"#{}{}{}{}",
852
fmt('n', self.n),
853
fmt('z', self.z),
854
fmt('c', self.c),
855
fmt('v', self.v)
856
)
857
}
858
}
859
860
impl PrettyPrint for UImm5 {
861
fn pretty_print(&self, _: u8) -> String {
862
format!("#{}", self.value)
863
}
864
}
865
866
impl PrettyPrint for Imm12 {
867
fn pretty_print(&self, _: u8) -> String {
868
let shift = if self.shift12 { 12 } else { 0 };
869
let value = u32::from(self.bits) << shift;
870
format!("#{value}")
871
}
872
}
873
874
impl PrettyPrint for SImm7Scaled {
875
fn pretty_print(&self, _: u8) -> String {
876
format!("#{}", self.value)
877
}
878
}
879
880
impl PrettyPrint for FPULeftShiftImm {
881
fn pretty_print(&self, _: u8) -> String {
882
format!("#{}", self.amount)
883
}
884
}
885
886
impl PrettyPrint for FPURightShiftImm {
887
fn pretty_print(&self, _: u8) -> String {
888
format!("#{}", self.amount)
889
}
890
}
891
892
impl PrettyPrint for SImm9 {
893
fn pretty_print(&self, _: u8) -> String {
894
format!("#{}", self.value)
895
}
896
}
897
898
impl PrettyPrint for UImm12Scaled {
899
fn pretty_print(&self, _: u8) -> String {
900
format!("#{}", self.value)
901
}
902
}
903
904
impl PrettyPrint for ImmLogic {
905
fn pretty_print(&self, _: u8) -> String {
906
format!("#{}", self.value())
907
}
908
}
909
910
impl PrettyPrint for ImmShift {
911
fn pretty_print(&self, _: u8) -> String {
912
format!("#{}", self.imm)
913
}
914
}
915
916
impl PrettyPrint for MoveWideConst {
917
fn pretty_print(&self, _: u8) -> String {
918
if self.shift == 0 {
919
format!("#{}", self.bits)
920
} else {
921
format!("#{}, LSL #{}", self.bits, self.shift * 16)
922
}
923
}
924
}
925
926
impl PrettyPrint for ASIMDMovModImm {
927
fn pretty_print(&self, _: u8) -> String {
928
if self.is_64bit {
929
debug_assert_eq!(self.shift, 0);
930
931
let enc_imm = self.imm as i8;
932
let mut imm = 0u64;
933
934
for i in 0..8 {
935
let b = (enc_imm >> i) & 1;
936
937
imm |= (-b as u8 as u64) << (i * 8);
938
}
939
940
format!("#{imm}")
941
} else if self.shift == 0 {
942
format!("#{}", self.imm)
943
} else {
944
let shift_type = if self.shift_ones { "MSL" } else { "LSL" };
945
format!("#{}, {} #{}", self.imm, shift_type, self.shift)
946
}
947
}
948
}
949
950
impl PrettyPrint for ASIMDFPModImm {
951
fn pretty_print(&self, _: u8) -> String {
952
match self.size {
953
ScalarSize::Size16 => {
954
// FIXME(#8312): Use `f16` once it is stable.
955
// `value` will always be a normal number. Convert it to a `f32`.
956
let value: u32 = Self::value16(self.imm).into();
957
let sign = (value & 0x8000) << 16;
958
// Adjust the exponent for the difference between the `f16` exponent bias and the
959
// `f32` exponent bias.
960
let exponent = ((value & 0x7c00) + ((127 - 15) << 10)) << 13;
961
let significand = (value & 0x3ff) << 13;
962
format!("#{}", f32::from_bits(sign | exponent | significand))
963
}
964
ScalarSize::Size32 => format!("#{}", f32::from_bits(Self::value32(self.imm))),
965
ScalarSize::Size64 => format!("#{}", f64::from_bits(Self::value64(self.imm))),
966
_ => unreachable!(),
967
}
968
}
969
}
970
971
#[cfg(test)]
972
mod test {
973
use super::*;
974
975
#[test]
976
fn imm_logical_test() {
977
assert_eq!(None, ImmLogic::maybe_from_u64(0, I64));
978
assert_eq!(None, ImmLogic::maybe_from_u64(u64::max_value(), I64));
979
980
assert_eq!(
981
Some(ImmLogic {
982
value: 1,
983
n: true,
984
r: 0,
985
s: 0,
986
size: OperandSize::Size64,
987
}),
988
ImmLogic::maybe_from_u64(1, I64)
989
);
990
991
assert_eq!(
992
Some(ImmLogic {
993
value: 2,
994
n: true,
995
r: 63,
996
s: 0,
997
size: OperandSize::Size64,
998
}),
999
ImmLogic::maybe_from_u64(2, I64)
1000
);
1001
1002
assert_eq!(None, ImmLogic::maybe_from_u64(5, I64));
1003
1004
assert_eq!(None, ImmLogic::maybe_from_u64(11, I64));
1005
1006
assert_eq!(
1007
Some(ImmLogic {
1008
value: 248,
1009
n: true,
1010
r: 61,
1011
s: 4,
1012
size: OperandSize::Size64,
1013
}),
1014
ImmLogic::maybe_from_u64(248, I64)
1015
);
1016
1017
assert_eq!(None, ImmLogic::maybe_from_u64(249, I64));
1018
1019
assert_eq!(
1020
Some(ImmLogic {
1021
value: 1920,
1022
n: true,
1023
r: 57,
1024
s: 3,
1025
size: OperandSize::Size64,
1026
}),
1027
ImmLogic::maybe_from_u64(1920, I64)
1028
);
1029
1030
assert_eq!(
1031
Some(ImmLogic {
1032
value: 0x7ffe,
1033
n: true,
1034
r: 63,
1035
s: 13,
1036
size: OperandSize::Size64,
1037
}),
1038
ImmLogic::maybe_from_u64(0x7ffe, I64)
1039
);
1040
1041
assert_eq!(
1042
Some(ImmLogic {
1043
value: 0x30000,
1044
n: true,
1045
r: 48,
1046
s: 1,
1047
size: OperandSize::Size64,
1048
}),
1049
ImmLogic::maybe_from_u64(0x30000, I64)
1050
);
1051
1052
assert_eq!(
1053
Some(ImmLogic {
1054
value: 0x100000,
1055
n: true,
1056
r: 44,
1057
s: 0,
1058
size: OperandSize::Size64,
1059
}),
1060
ImmLogic::maybe_from_u64(0x100000, I64)
1061
);
1062
1063
assert_eq!(
1064
Some(ImmLogic {
1065
value: u64::max_value() - 1,
1066
n: true,
1067
r: 63,
1068
s: 62,
1069
size: OperandSize::Size64,
1070
}),
1071
ImmLogic::maybe_from_u64(u64::max_value() - 1, I64)
1072
);
1073
1074
assert_eq!(
1075
Some(ImmLogic {
1076
value: 0xaaaaaaaaaaaaaaaa,
1077
n: false,
1078
r: 1,
1079
s: 60,
1080
size: OperandSize::Size64,
1081
}),
1082
ImmLogic::maybe_from_u64(0xaaaaaaaaaaaaaaaa, I64)
1083
);
1084
1085
assert_eq!(
1086
Some(ImmLogic {
1087
value: 0x8181818181818181,
1088
n: false,
1089
r: 1,
1090
s: 49,
1091
size: OperandSize::Size64,
1092
}),
1093
ImmLogic::maybe_from_u64(0x8181818181818181, I64)
1094
);
1095
1096
assert_eq!(
1097
Some(ImmLogic {
1098
value: 0xffc3ffc3ffc3ffc3,
1099
n: false,
1100
r: 10,
1101
s: 43,
1102
size: OperandSize::Size64,
1103
}),
1104
ImmLogic::maybe_from_u64(0xffc3ffc3ffc3ffc3, I64)
1105
);
1106
1107
assert_eq!(
1108
Some(ImmLogic {
1109
value: 0x100000001,
1110
n: false,
1111
r: 0,
1112
s: 0,
1113
size: OperandSize::Size64,
1114
}),
1115
ImmLogic::maybe_from_u64(0x100000001, I64)
1116
);
1117
1118
assert_eq!(
1119
Some(ImmLogic {
1120
value: 0x1111111111111111,
1121
n: false,
1122
r: 0,
1123
s: 56,
1124
size: OperandSize::Size64,
1125
}),
1126
ImmLogic::maybe_from_u64(0x1111111111111111, I64)
1127
);
1128
1129
for n in 0..2 {
1130
let types = if n == 0 { vec![I64, I32] } else { vec![I64] };
1131
for s in 0..64 {
1132
for r in 0..64 {
1133
let imm = get_logical_imm(n, s, r);
1134
for &ty in &types {
1135
match ImmLogic::maybe_from_u64(imm, ty) {
1136
Some(ImmLogic { value, .. }) => {
1137
assert_eq!(imm, value);
1138
ImmLogic::maybe_from_u64(!value, ty).unwrap();
1139
}
1140
None => assert_eq!(0, imm),
1141
};
1142
}
1143
}
1144
}
1145
}
1146
}
1147
1148
// Repeat a value that has `width` bits, across a 64-bit value.
1149
fn repeat(value: u64, width: u64) -> u64 {
1150
let mut result = value & ((1 << width) - 1);
1151
let mut i = width;
1152
while i < 64 {
1153
result |= result << i;
1154
i *= 2;
1155
}
1156
result
1157
}
1158
1159
// Get the logical immediate, from the encoding N/R/S bits.
1160
fn get_logical_imm(n: u32, s: u32, r: u32) -> u64 {
1161
// An integer is constructed from the n, imm_s and imm_r bits according to
1162
// the following table:
1163
//
1164
// N imms immr size S R
1165
// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
1166
// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
1167
// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
1168
// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
1169
// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
1170
// 0 11110s xxxxxr 2 UInt(s) UInt(r)
1171
// (s bits must not be all set)
1172
//
1173
// A pattern is constructed of size bits, where the least significant S+1
1174
// bits are set. The pattern is rotated right by R, and repeated across a
1175
// 64-bit value.
1176
1177
if n == 1 {
1178
if s == 0x3f {
1179
return 0;
1180
}
1181
let bits = (1u64 << (s + 1)) - 1;
1182
bits.rotate_right(r)
1183
} else {
1184
if (s >> 1) == 0x1f {
1185
return 0;
1186
}
1187
let mut width = 0x20;
1188
while width >= 0x2 {
1189
if (s & width) == 0 {
1190
let mask = width - 1;
1191
if (s & mask) == mask {
1192
return 0;
1193
}
1194
let bits = (1u64 << ((s & mask) + 1)) - 1;
1195
return repeat(bits.rotate_right(r & mask), width.into());
1196
}
1197
width >>= 1;
1198
}
1199
unreachable!();
1200
}
1201
}
1202
1203
#[test]
1204
fn asimd_fp_mod_imm_test() {
1205
assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size32));
1206
assert_eq!(
1207
None,
1208
ASIMDFPModImm::maybe_from_u64(0.013671875_f32.to_bits() as u64, ScalarSize::Size32)
1209
);
1210
assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size64));
1211
assert_eq!(
1212
None,
1213
ASIMDFPModImm::maybe_from_u64(10000_f64.to_bits(), ScalarSize::Size64)
1214
);
1215
}
1216
1217
#[test]
1218
fn asimd_mov_mod_imm_test() {
1219
assert_eq!(
1220
None,
1221
ASIMDMovModImm::maybe_from_u64(513, ScalarSize::Size16)
1222
);
1223
assert_eq!(
1224
None,
1225
ASIMDMovModImm::maybe_from_u64(4278190335, ScalarSize::Size32)
1226
);
1227
assert_eq!(
1228
None,
1229
ASIMDMovModImm::maybe_from_u64(8388608, ScalarSize::Size64)
1230
);
1231
1232
assert_eq!(
1233
Some(ASIMDMovModImm {
1234
imm: 66,
1235
shift: 16,
1236
is_64bit: false,
1237
shift_ones: true,
1238
}),
1239
ASIMDMovModImm::maybe_from_u64(4390911, ScalarSize::Size32)
1240
);
1241
}
1242
}
1243
1244