Skip to main content

asmkit/aarch64/
encoder.rs

1//! Byte-writing emit handlers and opcode composition for the AArch64
2//! assembler: the helper layer of AsmJit's `a64assembler.cpp` (opcode
3//! composition, immediate encoders, operand checks) plus the `EmitOp` /
4//! `EmitOp_DispImm` / `EmitOp_Rel` handler blocks reached from the encoding
5//! arms in [`super::emit`].
6//!
7//! Derived from AsmJit (Zlib license) — this file is an altered version; see LICENSE notices.
8
9#![allow(clippy::eq_op, clippy::erasing_op, dead_code, unused)]
10use crate::AsmError;
11use crate::aarch64::emit::Handler;
12use crate::aarch64::encoder_tables::{SIZE_OP_MAP, SIZE_OP_TABLE};
13use crate::aarch64::operands::*;
14use crate::aarch64::{Assembler, instdb::*};
15use crate::core::buffer::LabelUse;
16use crate::core::operand::*;
17
18macro_rules! B {
19    ($e: expr) => {
20        1 << $e
21    };
22}
23
24macro_rules! check_signature {
25    ($op0: expr, $op1: expr) => {
26        $op0.signature() == $op1.signature()
27    };
28    ($op0: expr, $op1: expr, $op2: expr) => {
29        $op0.signature() == $op1.signature() && $op1.signature() == $op2.signature()
30    };
31
32    ($op0: expr, $op1: expr, $op2: expr, $op3: expr) => {
33        $op0.signature() == $op1.signature()
34            && $op1.signature() == $op2.signature()
35            && $op2.signature() == $op3.signature()
36    };
37}
38
39/// Per-instruction emit state shared between the encoding arms in
40/// [`super::emit`] and the byte-writing handlers here (AsmJit's `_emit`
41/// locals).
42pub(crate) struct A64EmitState {
43    /// The opcode word being composed.
44    pub opcode: Opc,
45    /// Offset/displacement format of pc-relative and load/store forms.
46    pub offset_format: OffsetFormat,
47    /// Displacement value packed by `EmitOp_DispImm`.
48    pub offset_value: i64,
49    /// Words of a multi-instruction mov sequence.
50    pub multiple_op_data: [u32; 4],
51    /// Number of valid words in `multiple_op_data`.
52    pub multiple_op_count: usize,
53    /// The operand a pc-relative form refers to (label, label-based mem, or
54    /// immediate).
55    pub rm_rel: Operand,
56}
57
58impl A64EmitState {
59    pub(crate) fn new() -> Self {
60        Self {
61            opcode: Opc(0),
62            offset_format: OffsetFormat::new(OffsetType::SignedOffset, 0, 0, 0, 0, 0, 0, 0),
63            offset_value: 0,
64            multiple_op_data: [0; 4],
65            multiple_op_count: 0,
66            rm_rel: Operand::new(),
67        }
68    }
69}
70
71impl Assembler<'_> {
72    /// Dispatches to a byte-writing emit handler (AsmJit's `EmitOp` /
73    /// `EmitOp_DispImm` / `EmitOp_Rel` blocks at the end of `_emit`).
74    ///
75    /// Returns `true` when the instruction was emitted or an error was
76    /// recorded. `Handler::OpRel` returns `false` when `rm_rel` is not a
77    /// relative operand and the encoding arm must continue.
78    pub(crate) fn emit_handler(&mut self, handler: Handler, st: &mut A64EmitState) -> bool {
79        match handler {
80            Handler::Op => {
81                self.buffer.write_u32(st.opcode.get());
82                true
83            }
84            Handler::OpDispImm => {
85                self.emit_disp_imm(st);
86                true
87            }
88            Handler::OpRel => self.emit_rel(st),
89            Handler::Multi => {
90                for i in 0..st.multiple_op_count {
91                    self.buffer.write_u32(st.multiple_op_data[i]);
92                }
93                true
94            }
95        }
96    }
97
98    /// AsmJit's `EmitOp_DispImm`: validates and packs the displacement
99    /// immediate described by `st.offset_format` into the opcode, then emits
100    /// the word.
101    fn emit_disp_imm(&mut self, st: &mut A64EmitState) {
102        if (st.offset_value & ((1 << st.offset_format.imm_discard_lsb()) - 1)) != 0 {
103            self.last_error = Some(AsmError::InvalidOperand);
104            return;
105        }
106
107        let disp_imm64 = (st.offset_value as i64) >> st.offset_format.imm_discard_lsb() as i64;
108        let disp_imm32 = (disp_imm64 & ((1 << st.offset_format.imm_bit_count()) - 1)) as u32;
109
110        match st.offset_format.typ() {
111            OffsetType::SignedOffset => {
112                st.opcode
113                    .add_imm(disp_imm32 as _, st.offset_format.imm_bit_shift() as _);
114                self.buffer.write_u32(st.opcode.get());
115            }
116
117            _ => {
118                let imm_lo = disp_imm32 & 0x3;
119                let imm_hi = disp_imm32 >> 2;
120                st.opcode.add_imm(imm_lo, 29);
121                st.opcode.add_imm(imm_hi, 5);
122                self.buffer.write_u32(st.opcode.get());
123            }
124        }
125    }
126
127    /// AsmJit's `EmitOp_Rel`: resolves a label or immediate pc-relative
128    /// operand into `st.offset_value` (recording a label use for unbound
129    /// labels) and emits. Returns `false` when `st.rm_rel` is not relative.
130    fn emit_rel(&mut self, st: &mut A64EmitState) -> bool {
131        if st.rm_rel.is_label() || (st.rm_rel.is_mem() && st.rm_rel.as_::<Mem>().has_base_label()) {
132            let label_id;
133            let mut label_offset = 0;
134
135            if st.rm_rel.is_label() {
136                label_id = st.rm_rel.as_::<Label>().id();
137            } else {
138                label_id = st.rm_rel.as_::<Mem>().base_id();
139                label_offset = st.rm_rel.as_::<Mem>().offset();
140            }
141
142            if self.buffer.is_bound(Label::from_id(label_id)) {
143                st.offset_value = self.buffer.label_offset(Label::from_id(label_id)) as i64
144                    + label_offset
145                    - self.buffer.cur_offset() as i64;
146                self.emit_disp_imm(st);
147            } else {
148                let offset = self.buffer.cur_offset();
149                self.buffer.use_label_at_offset(
150                    offset,
151                    Label::from_id(label_id),
152                    match st.offset_format.typ() {
153                        OffsetType::Adrp => LabelUse::A64Adrp21,
154                        OffsetType::Adr => LabelUse::A64Adr21,
155                        OffsetType::Ldr => LabelUse::A64Ldr19,
156                        OffsetType::SignedOffset => {
157                            if st.offset_format.imm_bit_count() == 26 {
158                                LabelUse::A64Branch26
159                            } else if st.offset_format.imm_bit_count() == 19 {
160                                LabelUse::A64Branch19
161                            } else if st.offset_format.imm_bit_count() == 14 {
162                                LabelUse::A64Branch14
163                            } else {
164                                panic!("Invalid offset format for label use")
165                            }
166                        }
167                    },
168                );
169
170                self.buffer.write_u32(st.opcode.get());
171            }
172
173            return true;
174        }
175
176        if st.rm_rel.is_imm() {
177            let target_offset = st.rm_rel.as_::<Imm>().value() as u64;
178            let mut pc = self.buffer.cur_offset() as u64 + 4;
179            if st.offset_format.typ() == OffsetType::Adrp {
180                pc &= !(4096 - 1);
181            }
182            st.offset_value = target_offset as i64 - pc as i64;
183            self.emit_disp_imm(st);
184            return true;
185        }
186
187        false
188    }
189}
190
191#[derive(Copy, Clone, PartialEq, Eq, Debug)]
192#[repr(transparent)]
193pub(crate) struct Opc(pub(crate) u32);
194
195impl Opc {
196    const N: u32 = 1 << 2;
197    const Q: u32 = 1 << 30;
198    const X: u32 = 1 << 31;
199
200    pub fn reset(&mut self, value: u32) {
201        self.0 = value;
202    }
203
204    pub fn get(&self) -> u32 {
205        self.0
206    }
207
208    pub const fn has_q(&self) -> bool {
209        (self.0 & Self::Q) != 0
210    }
211    pub const fn has_x(&self) -> bool {
212        (self.0 & Self::X) != 0
213    }
214
215    pub fn add_imm(&mut self, value: u32, bit_index: u32) -> &mut Self {
216        self.0 |= value << bit_index;
217        self
218    }
219
220    pub fn xor_imm(&mut self, value: u32, bit_index: u32) -> &mut Self {
221        self.0 ^= value << bit_index;
222        self
223    }
224
225    pub fn add_if(&mut self, condition: bool, value: u32, bit_index: u32) -> &mut Self {
226        if condition {
227            self.0 |= value << bit_index;
228        }
229        self
230    }
231
232    pub fn add_logical_imm(&mut self, logical_imm: &LogicalImm) -> &mut Self {
233        self.add_imm(logical_imm.n, 22)
234            .add_imm(logical_imm.s, 10)
235            .add_imm(logical_imm.r, 16);
236        self
237    }
238
239    pub fn add_reg(&mut self, id: u32, bit_index: u32) -> &mut Self {
240        self.0 |= (id & 31) << bit_index;
241        self
242    }
243}
244
245impl core::ops::BitOr<u32> for Opc {
246    type Output = Self;
247
248    fn bitor(self, rhs: u32) -> Self::Output {
249        Self(self.0 | rhs)
250    }
251}
252
253impl core::ops::BitOrAssign<u32> for Opc {
254    fn bitor_assign(&mut self, rhs: u32) {
255        self.0 |= rhs;
256    }
257}
258
259impl core::ops::BitAnd<u32> for Opc {
260    type Output = Self;
261
262    fn bitand(self, rhs: u32) -> Self::Output {
263        Self(self.0 & rhs)
264    }
265}
266
267impl core::ops::BitAndAssign<u32> for Opc {
268    fn bitand_assign(&mut self, rhs: u32) {
269        self.0 &= rhs;
270    }
271}
272
273impl core::ops::Not for Opc {
274    type Output = Self;
275
276    fn not(self) -> Self::Output {
277        Self(!self.0)
278    }
279}
280
281impl core::ops::BitXor<u32> for Opc {
282    type Output = Self;
283
284    fn bitxor(self, rhs: u32) -> Self::Output {
285        Self(self.0 ^ rhs)
286    }
287}
288
289impl core::ops::BitXorAssign<u32> for Opc {
290    fn bitxor_assign(&mut self, rhs: u32) {
291        self.0 ^= rhs;
292    }
293}
294
295impl core::ops::Shl<u32> for Opc {
296    type Output = Self;
297
298    fn shl(self, rhs: u32) -> Self::Output {
299        Self(self.0 << rhs)
300    }
301}
302
303impl core::ops::ShlAssign<u32> for Opc {
304    fn shl_assign(&mut self, rhs: u32) {
305        self.0 <<= rhs;
306    }
307}
308
309impl core::ops::Shr<u32> for Opc {
310    type Output = Self;
311
312    fn shr(self, rhs: u32) -> Self::Output {
313        Self(self.0 >> rhs)
314    }
315}
316
317impl core::ops::ShrAssign<u32> for Opc {
318    fn shr_assign(&mut self, rhs: u32) {
319        self.0 >>= rhs;
320    }
321}
322
323#[derive(Copy, Clone, PartialEq, Eq, Debug)]
324pub struct LogicalImm {
325    pub n: u32,
326    pub s: u32,
327    pub r: u32,
328}
329
330pub(crate) fn check_gp_type(op: &Operand, allowed: u32) -> bool {
331    let typ = op.as_::<Reg>().typ() as u32;
332    let mask = allowed << RegType::Gp32 as u32;
333    bit_test(mask, typ)
334}
335
336pub(crate) fn check_gp_typex(op: &Operand, allowed: u32, x: &mut u32) -> bool {
337    let typ = op.as_::<Reg>().typ() as u32;
338    *x = typ.wrapping_sub(RegType::Gp32 as u32) & allowed;
339    bit_test(allowed << RegType::Gp32 as u32, typ)
340}
341
342pub(crate) fn check_gp_typex2(o0: &Operand, o1: &Operand, allowed: u32, x: &mut u32) -> bool {
343    check_gp_typex(o0, allowed, x) && check_signature!(o0, o1)
344}
345
346pub(crate) fn check_gp_typex3(
347    o0: &Operand,
348    o1: &Operand,
349    o2: &Operand,
350    allowed: u32,
351    x: &mut u32,
352) -> bool {
353    check_gp_typex(o0, allowed, x) && check_signature!(o0, o1, o2)
354}
355
356pub(crate) fn check_gp_id(op: &Operand, hi_id: u32) -> bool {
357    op.id() < 31 || op.id() == hi_id
358}
359
360pub(crate) fn check_gp_id2(o0: &Operand, o1: &Operand, hi_id: u32) -> bool {
361    let id0 = o0.id();
362    let id1 = o1.id();
363    (id0 < 31 || id0 == hi_id) && (id1 < 31 || id1 == hi_id)
364}
365
366pub(crate) fn check_gp_id3(o0: &Operand, o1: &Operand, o2: &Operand, hi_id: u32) -> bool {
367    let id0 = o0.id();
368    let id1 = o1.id();
369    let id2 = o2.id();
370    (id0 < 31 || id0 == hi_id) && (id1 < 31 || id1 == hi_id) && (id2 < 31 || id2 == hi_id)
371}
372
373pub(crate) fn check_vec_id(o0: &Operand) -> bool {
374    let id = o0.id();
375    id < 31
376}
377
378pub(crate) fn check_vec_id2(o0: &Operand, o1: &Operand) -> bool {
379    let id0 = o0.id();
380    let id1 = o1.id();
381    id0 < 31 && id1 < 31
382}
383
384pub(crate) fn check_vec_id3(o0: &Operand, o1: &Operand, o2: &Operand) -> bool {
385    let id0 = o0.id();
386    let id1 = o1.id();
387    let id2 = o2.id();
388    id0 < 31 && id1 < 31 && id2 < 31
389}
390
391pub(crate) fn bit_test(value: u32, n: u32) -> bool {
392    n < 32 && value & (1 << n) != 0
393}
394
395pub(crate) fn encode_mov_sequence64(out: &mut [u32; 4], mut imm: u64, rd: u32, x: u32) -> usize {
396    const MOVZ: u32 = 0b11010010100000000000000000000000;
397    const MOVN: u32 = 0b10010010100000000000000000000000;
398    const MOVK: u32 = 0b11110010100000000000000000000000;
399
400    if imm <= 0xFFFFFFFF {
401        return encode_mov_sequence32(out, imm as u32, rd, x);
402    }
403
404    let zhw = count_zero_half_words_64(imm);
405    let ohw = count_zero_half_words_64(!imm);
406
407    if zhw >= ohw {
408        let mut op = MOVZ;
409        let mut count = 0;
410        for hw_index in 0..4 {
411            let hw_imm = (imm & 0xFFFF) as u32;
412            if hw_imm == 0 {
413                imm >>= 16;
414                continue;
415            }
416            out[count] = op | (hw_index << 21) | (hw_imm << 5) | rd;
417            op = MOVK;
418            count += 1;
419
420            imm >>= 16;
421        }
422
423        return count;
424    }
425
426    let mut op = MOVN;
427    let mut count = 0;
428    let mut neg_mask = 0xFFFF;
429
430    for hw_index in 0..4 {
431        let hw_imm = (imm & 0xFFFF) as u32;
432        if hw_imm == 0xFFFF {
433            imm >>= 16;
434            continue;
435        }
436
437        out[count] = op | (hw_index << 21) | ((hw_imm ^ neg_mask) << 5) | rd;
438        count += 1;
439        op = MOVK;
440        neg_mask = 0;
441        imm >>= 16;
442    }
443
444    count
445}
446
447pub(crate) fn encode_mov_sequence32(out: &mut [u32], imm: u32, rd: u32, x: u32) -> usize {
448    let movz = 0b11010010100000000000000000000000 | (x << 31);
449    let movn = 0b10010010100000000000000000000000;
450    let movk = 0b11110010100000000000000000000000;
451    if (imm & 0xFFFF0000) == 0 {
452        out[0] = movz | (0 << 21) | ((imm & 0xffff) << 5) | rd;
453        return 1;
454    }
455
456    if (imm & 0xFFFF0000) == 0xFFFF0000 {
457        out[0] = movn | (0 << 21) | ((!imm & 0xFFFF) << 5) | rd;
458        return 1;
459    }
460
461    if (imm & 0x0000FFFF) == 0x00000000 {
462        out[0] = movz | (1 << 21) | ((imm >> 16) << 5) | rd;
463        return 1;
464    }
465
466    if (imm & 0x0000FFFF) == 0x0000FFFF {
467        out[0] = movn | (1 << 21) | ((!imm >> 16) << 5) | rd;
468        return 1;
469    }
470
471    out[0] = movz | (0 << 21) | ((imm & 0xFFFF) << 5) | rd;
472    out[1] = movk | (1 << 21) | ((imm >> 16) << 5) | rd;
473    return 2;
474}
475
476pub const fn count_zero_half_words_64(imm: u64) -> u32 {
477    let mut count = 0;
478    if (imm & 0x000000000000FFFF) == 0 {
479        count += 1;
480    }
481    if (imm & 0x00000000FFFF0000) == 0 {
482        count += 1;
483    }
484    if (imm & 0x0000FFFF00000000) == 0 {
485        count += 1;
486    }
487    if (imm & 0xFFFF000000000000) == 0 {
488        count += 1;
489    }
490    count
491}
492
493/// Encodes the given `imm` value of the given `width` to a logical immediate value represented as N, S, and R fields
494/// and writes these fields to `out`.
495///
496/// Encoding Table:
497///
498/// ```text
499/// +---+--------+--------+------+
500/// | N |  ImmS  |  ImmR  | Size |
501/// +---+--------+--------+------+
502/// | 1 | ssssss | rrrrrr |  64  |
503/// | 0 | 0sssss | .rrrrr |  32  |
504/// | 0 | 10ssss | ..rrrr |  16  |
505/// | 0 | 110sss | ...rrr |  8   |
506/// | 0 | 1110ss | ....rr |  4   |
507/// | 0 | 11110s | .....r |  2   |
508/// +---+--------+--------+------+
509/// ```
510pub const fn encode_logical_imm(mut imm: u64, mut width: u32) -> Option<LogicalImm> {
511    loop {
512        width /= 2;
513        let mask = (1u64 << width) - 1;
514        if (imm & mask) != (imm >> width) & mask {
515            width *= 2;
516            break;
517        }
518        if width <= 2 {
519            break;
520        }
521    }
522
523    let width_mask = lsb_mask::<u64>(width);
524    imm &= width_mask;
525
526    // Patterns of all zeros and all ones are not encodable.
527    if imm == 0 || width_mask == imm {
528        return None;
529    }
530
531    // Inspect the pattern and get the most important bit indexes.
532    //
533    //         o_index <-+      +-> z_index
534    //                  |      |
535    // |..zeros..|o_count|z_count|..ones..|
536    // |000000000|111111|000000|11111111|
537    let z_index = (!imm).trailing_zeros();
538    let z_imm = imm ^ ((1u64 << z_index) - 1);
539    let z_count = (if z_imm != 0 {
540        z_imm.trailing_zeros()
541    } else {
542        width
543    })
544    .wrapping_sub(z_index);
545
546    let o_index = z_index + z_count;
547    let o_imm = !(z_imm ^ lsb_mask::<u64>(o_index));
548    let o_count = (if o_imm != 0 {
549        o_imm.trailing_zeros()
550    } else {
551        width
552    })
553    .wrapping_sub(o_index);
554
555    let must_be_zero = o_imm ^ !lsb_mask::<u64>((o_index + o_count) & 63);
556    if must_be_zero != 0 || (z_index > 0 && width.wrapping_sub(o_index + o_count) != 0) {
557        return None;
558    }
559
560    Some(LogicalImm {
561        n: if width == 64 { 1 } else { 0 },
562        s: (o_count + z_index).wrapping_sub(1) | 0u32.wrapping_sub(width * 2) & 0x3f,
563        r: width.wrapping_sub(o_index),
564    })
565}
566
567#[derive(Copy, Clone, PartialEq, Eq, Debug)]
568#[repr(u8)]
569pub(crate) enum OffsetType {
570    SignedOffset,
571    Adr,
572    Adrp,
573    Ldr,
574}
575
576impl TryFrom<u8> for OffsetType {
577    type Error = ();
578
579    fn try_from(value: u8) -> Result<Self, Self::Error> {
580        match value {
581            0 => Ok(Self::SignedOffset),
582            1 => Ok(Self::Adr),
583            2 => Ok(Self::Adrp),
584            3 => Ok(Self::Ldr),
585            _ => Err(()),
586        }
587    }
588}
589
590pub(crate) struct OffsetFormat {
591    pub(crate) typ: OffsetType,
592    pub(crate) flags: u8,
593    pub(crate) region_size: u8,
594    pub(crate) value_size: u8,
595    pub(crate) value_offset: u8,
596    pub(crate) imm_bit_count: u8,
597    pub(crate) imm_bit_shift: u8,
598    pub(crate) imm_discard_lsb: u8,
599}
600
601impl OffsetFormat {
602    pub const fn new(
603        typ: OffsetType,
604        flags: u8,
605        region_size: u8,
606        value_size: u8,
607        value_offset: u8,
608        imm_bit_count: u8,
609        imm_bit_shift: u8,
610        imm_discard_lsb: u8,
611    ) -> Self {
612        Self {
613            typ,
614            flags,
615            region_size,
616            value_size,
617            value_offset,
618            imm_bit_count,
619            imm_bit_shift,
620            imm_discard_lsb,
621        }
622    }
623
624    pub fn reset_to_imm_type(
625        &mut self,
626        typ: OffsetType,
627        value_size: usize,
628        imm_bit_shift: u32,
629        imm_bit_count: u32,
630        imm_discard_lsb: u32,
631    ) {
632        self.typ = typ;
633        self.value_size = value_size as u8;
634        self.region_size = value_size as u8;
635        self.imm_bit_shift = imm_bit_shift as u8;
636        self.imm_bit_count = imm_bit_count as u8;
637        self.imm_discard_lsb = imm_discard_lsb as u8;
638        self.flags = 0;
639        self.value_offset = 0;
640    }
641
642    fn set_region(&mut self, region_size: usize, value_offset: usize) {
643        self.region_size = region_size as u8;
644        self.value_offset = value_offset as u8;
645    }
646
647    fn set_leading_and_trailing_size(&mut self, leading_size: usize, trailing_size: usize) {
648        self.region_size = (leading_size + trailing_size + self.value_size as usize) as u8;
649        self.value_offset = leading_size as u8;
650    }
651
652    fn typ(&self) -> OffsetType {
653        self.typ
654    }
655
656    fn flags(&self) -> u8 {
657        self.flags
658    }
659
660    fn region_size(&self) -> usize {
661        self.region_size as usize
662    }
663
664    fn value_size(&self) -> usize {
665        self.value_size as usize
666    }
667
668    fn value_offset(&self) -> usize {
669        self.value_offset as usize
670    }
671
672    fn imm_bit_count(&self) -> usize {
673        self.imm_bit_count as usize
674    }
675
676    fn imm_bit_shift(&self) -> usize {
677        self.imm_bit_shift as usize
678    }
679
680    fn imm_discard_lsb(&self) -> usize {
681        self.imm_discard_lsb as usize
682    }
683}
684
685pub(crate) const fn lsb_mask<T>(n: u32) -> u64 {
686    if size_of::<T>() < size_of::<u64>() {
687        (1 << n) - 1
688    } else {
689        if n != 0 {
690            (!0u64).wrapping_shr((size_of::<T>() as u32 * 8) - n)
691        } else {
692            0
693        }
694    }
695}
696
697pub(crate) const fn cond_code_to_opcode_field(cond: u32) -> u32 {
698    (cond.wrapping_sub(2)) & 0xf
699}
700
701pub(crate) const fn is_byte_mask_imm(imm: u64) -> bool {
702    let mask = 0x0101010101010101 & u64::MAX;
703    imm == (imm & mask) * 255
704}
705
706pub(crate) const fn encode_imm64_byte_mask_to_imm8(imm: u64) -> u32 {
707    (((imm >> (7  - 0)) & 0b00000011) | // [.......G|H.......]
708     ((imm >> (23 - 2)) & 0b00001100) | // [.......E|F.......]
709     ((imm >> (39 - 4)) & 0b00110000) | // [.......C|D.......]
710     ((imm >> (55 - 6)) & 0b11000000)) as u32
711}
712
713macro_rules! is_fp_imm8_generic {
714    ($t: ty: $val: expr, $num_b_bits: expr, $num_cdefgh_bits: expr, $num_zero_bits: expr) => {{
715        let all_bs_mask = lsb_mask::<u32>($num_b_bits);
716        let b0_pattern = 1u32 << ($num_b_bits - 1);
717        let b1_pattern = all_bs_mask as u32 ^ b0_pattern;
718
719        let imm_z = $val & lsb_mask::<$t>($num_zero_bits as _) as $t;
720        let imm_b = ($val >> ($num_zero_bits + $num_cdefgh_bits)) as u32 & all_bs_mask as u32;
721        imm_z == 0 && (imm_b == b0_pattern || imm_b == b1_pattern)
722    }};
723}
724
725pub const fn is_fp16_imm8(val: u32) -> bool {
726    is_fp_imm8_generic!(u32: val, 3, 6, 6)
727}
728
729pub const fn is_fp32_imm8(val: u32) -> bool {
730    is_fp_imm8_generic!(u32: val, 6, 6, 19)
731}
732
733pub const fn is_fp64_imm8(val: u64) -> bool {
734    is_fp_imm8_generic!(u64: val, 9, 6, 48)
735}
736
737macro_rules! encode_fp_to_imm8_generic {
738    ($t: ty: $val: expr, $num_b_bits: expr, $num_cdefgh_bits: expr, $num_zero_bits: expr) => {{
739        let bits = ($val >> $num_zero_bits) as u32;
740        ((bits >> ($num_b_bits + $num_cdefgh_bits - 7)) & 0x80) | (bits & 0x7f)
741    }};
742}
743
744pub const fn encode_fp64_to_imm8(val: u64) -> u32 {
745    encode_fp_to_imm8_generic!(u64: val, 9, 6, 48)
746}
747
748pub(crate) fn pick_fp_opcode(
749    reg: Vec,
750    s_op: u32,
751    s_hf: u32,
752    v_op: u32,
753    v_hf: u32,
754    sz_out: &mut u32,
755) -> Option<Opc> {
756    const QBIT_INDEX: usize = 30;
757
758    struct EncodeFpOpcodeBits {
759        size_mask: u32,
760        mask: [u32; 3],
761    }
762
763    static SZ_BITS_TABLE: [EncodeFpOpcodeBits; 6] = [
764        EncodeFpOpcodeBits {
765            size_mask: (1 << 2) | (1 << 1),
766            mask: [0, 0, 1 << 22],
767        },
768        EncodeFpOpcodeBits {
769            size_mask: (1 << 2) | (1 << 1) | (1 << 0),
770            mask: [0, 0, 0],
771        },
772        EncodeFpOpcodeBits {
773            size_mask: (1 << 2) | (1 << 1) | (1 << 0),
774            mask: [1 << 23 | 1 << 22, 0, 1 << 22],
775        },
776        EncodeFpOpcodeBits {
777            size_mask: (1 << 2) | (1 << 1) | (1 << 0),
778            mask: [(1 << 22) | (1 << 20) | (1 << 19), 0, 0],
779        },
780        EncodeFpOpcodeBits {
781            size_mask: (1 << 2) | (1 << 1) | (1 << 0),
782            mask: [1 << 22 | (1 << 21) | (1 << 15) | (1 << 14), 0, 1 << 22],
783        },
784        EncodeFpOpcodeBits {
785            size_mask: (1 << 2) | (1 << 1) | (1 << 0),
786            mask: [1 << 23, 0, 1 << 22],
787        },
788    ];
789
790    let mut op = Opc(0);
791    if !reg.has_element_type() {
792        // Scalar operation [HSD].
793        let sz = (reg.typ() as u32).wrapping_sub(RegType::Vec16 as u32);
794        if sz > 2 || !bit_test32(SZ_BITS_TABLE[s_hf as usize].size_mask, sz) {
795            return None;
796        }
797
798        op.reset(SZ_BITS_TABLE[s_hf as usize].mask[sz as usize] ^ s_op);
799        *sz_out = sz;
800
801        return (s_op != 0).then_some(op);
802    } else {
803        // Vector operation [HSD].
804        let q = (reg.typ() as u32).wrapping_sub(RegType::Vec64 as u32);
805        let sz = (reg.element_type() as u32).wrapping_sub(VecElementType::H as u32);
806
807        if q > 1 || sz > 2 || !bit_test32(SZ_BITS_TABLE[v_hf as usize].size_mask, sz) {
808            return None;
809        }
810
811        op.reset(SZ_BITS_TABLE[v_hf as usize].mask[sz as usize] ^ (v_op | (q << QBIT_INDEX)));
812        *sz_out = sz;
813        return (v_op != 0).then_some(op);
814    }
815}
816
817pub(crate) const fn bit_test32(value: u32, n: u32) -> bool {
818    n < 32 && value & (1 << n) != 0
819}
820
821pub(crate) struct SizeOpTable {
822    pub(crate) array: [SizeOp; ((RegType::Vec128 as usize - RegType::Vec8 as usize + 1) + 1) * 40],
823}
824
825impl SizeOpTable {
826    const fn len() -> usize {
827        ((RegType::Vec128 as usize - RegType::Vec8 as usize + 1) + 1) * 40
828    }
829    pub(crate) const fn bin() -> Self {
830        let mut i = 0;
831        let mut array = [SizeOp::new(SizeOp::K_INVALID); Self::len()];
832        while i < Self::len() {
833            array[i] = Self::bin_at(i);
834            i += 1;
835        }
836        Self { array }
837    }
838
839    pub(crate) const fn any() -> Self {
840        let mut i = 0;
841        let mut array = [SizeOp::new(SizeOp::K_INVALID); Self::len()];
842        while i < Self::len() {
843            array[i] = Self::any_at(i);
844            i += 1;
845        }
846        Self { array }
847    }
848
849    const fn bin_at(x: usize) -> SizeOp {
850        if x == (((RegType::Vec64 as usize - RegType::Vec8 as usize) << 3)
851            | VecElementType::None as usize)
852        {
853            SizeOp::new(SizeOp::K00)
854        } else if x
855            == (((RegType::Vec128 as usize - RegType::Vec8 as usize) << 3)
856                | VecElementType::None as usize)
857        {
858            SizeOp::new(SizeOp::K00_Q)
859        } else if x
860            == (((RegType::Vec64 as usize - RegType::Vec8 as usize) << 3)
861                | VecElementType::B as usize)
862        {
863            SizeOp::new(SizeOp::K00)
864        } else if x
865            == (((RegType::Vec128 as usize - RegType::Vec8 as usize) << 3)
866                | VecElementType::B as usize)
867        {
868            SizeOp::new(SizeOp::K00_Q)
869        } else {
870            SizeOp::new(SizeOp::K_INVALID)
871        }
872    }
873
874    const fn any_at(x: usize) -> SizeOp {
875        if x == (((RegType::Vec8 as usize - RegType::Vec8 as usize) << 3)
876            | VecElementType::None as usize)
877        {
878            SizeOp::new(SizeOp::K00_S)
879        } else if x
880            == (((RegType::Vec16 as usize - RegType::Vec8 as usize) << 3)
881                | VecElementType::None as usize)
882        {
883            SizeOp::new(SizeOp::K01_S)
884        } else if x
885            == (((RegType::Vec32 as usize - RegType::Vec8 as usize) << 3)
886                | VecElementType::None as usize)
887        {
888            SizeOp::new(SizeOp::K10_S)
889        } else if x
890            == (((RegType::Vec64 as usize - RegType::Vec8 as usize) << 3)
891                | VecElementType::None as usize)
892        {
893            SizeOp::new(SizeOp::K11_S)
894        } else if x
895            == (((RegType::Vec64 as usize - RegType::Vec8 as usize) << 3)
896                | VecElementType::B as usize)
897        {
898            SizeOp::new(SizeOp::K00)
899        } else if x
900            == (((RegType::Vec128 as usize - RegType::Vec8 as usize) << 3)
901                | VecElementType::B as usize)
902        {
903            SizeOp::new(SizeOp::K00_Q)
904        } else if x
905            == (((RegType::Vec64 as usize - RegType::Vec8 as usize) << 3)
906                | VecElementType::H as usize)
907        {
908            SizeOp::new(SizeOp::K01)
909        } else if x
910            == (((RegType::Vec128 as usize - RegType::Vec8 as usize) << 3)
911                | VecElementType::H as usize)
912        {
913            SizeOp::new(SizeOp::K01_Q)
914        } else if x
915            == (((RegType::Vec64 as usize - RegType::Vec8 as usize) << 3)
916                | VecElementType::S as usize)
917        {
918            SizeOp::new(SizeOp::K10)
919        } else if x
920            == (((RegType::Vec128 as usize - RegType::Vec8 as usize) << 3)
921                | VecElementType::S as usize)
922        {
923            SizeOp::new(SizeOp::K10_Q)
924        } else if x
925            == (((RegType::Vec64 as usize - RegType::Vec8 as usize) << 3)
926                | VecElementType::D as usize)
927        {
928            SizeOp::new(SizeOp::K11_S)
929        } else if x
930            == (((RegType::Vec128 as usize - RegType::Vec8 as usize) << 3)
931                | VecElementType::D as usize)
932        {
933            SizeOp::new(SizeOp::K11_Q)
934        } else {
935            SizeOp::new(SizeOp::K_INVALID)
936        }
937    }
938}
939
940#[derive(Copy, Clone, PartialEq, Eq, Debug)]
941#[repr(transparent)]
942pub(crate) struct SizeOp(u8);
943
944impl SizeOp {
945    pub const fn new(val: u8) -> Self {
946        Self(val)
947    }
948
949    pub(crate) const K128_BIT_SHIFT: u8 = 0;
950    pub(crate) const K_SCALAR_SHIFT: u8 = 1;
951    pub(crate) const K_SIZE_SHIFT: u8 = 2;
952
953    pub(crate) const K_Q: u8 = 1u8 << Self::K128_BIT_SHIFT;
954    pub(crate) const K_S: u8 = 1u8 << Self::K_SCALAR_SHIFT;
955
956    pub(crate) const K00: u8 = 0 << Self::K_SIZE_SHIFT;
957    pub(crate) const K01: u8 = 1 << Self::K_SIZE_SHIFT;
958    pub(crate) const K10: u8 = 2 << Self::K_SIZE_SHIFT;
959    pub(crate) const K11: u8 = 3 << Self::K_SIZE_SHIFT;
960
961    pub(crate) const K00_Q: u8 = Self::K00 | Self::K_Q;
962    pub(crate) const K01_Q: u8 = Self::K01 | Self::K_Q;
963    pub(crate) const K10_Q: u8 = Self::K10 | Self::K_Q;
964    pub(crate) const K11_Q: u8 = Self::K11 | Self::K_Q;
965
966    pub(crate) const K00_S: u8 = Self::K00 | Self::K_S;
967    pub(crate) const K01_S: u8 = Self::K01 | Self::K_S;
968    pub(crate) const K10_S: u8 = Self::K10 | Self::K_S;
969    pub(crate) const K11_S: u8 = Self::K11 | Self::K_S;
970
971    pub(crate) const K_INVALID: u8 = 0xFF;
972
973    pub(crate) const K_SZ_Q: u8 = (0x3u8 << Self::K_SIZE_SHIFT) | Self::K_Q;
974    pub(crate) const K_SZ_S: u8 = (0x3u8 << Self::K_SIZE_SHIFT) | Self::K_S;
975    pub(crate) const K_SZ_QS: u8 = (0x3u8 << Self::K_SIZE_SHIFT) | Self::K_Q | Self::K_S;
976
977    pub(crate) const fn is_valid(self) -> bool {
978        self.0 != Self::K_INVALID
979    }
980
981    pub(crate) const fn make_invalid(&mut self) {
982        self.0 = Self::K_INVALID;
983    }
984
985    pub(crate) const fn q(&self) -> u32 {
986        (self.0 >> Self::K128_BIT_SHIFT) as u32 & 1
987    }
988
989    pub(crate) const fn qs(&self) -> u32 {
990        (((self.0 >> Self::K128_BIT_SHIFT) as u32) | ((self.0 >> Self::K_SCALAR_SHIFT) as u32)) & 1
991    }
992
993    pub(crate) const fn scalar(&self) -> u32 {
994        (self.0 >> Self::K_SCALAR_SHIFT) as u32 & 1
995    }
996
997    pub(crate) const fn size(&self) -> u32 {
998        (self.0 >> Self::K_SIZE_SHIFT) as u32 & 0x3
999    }
1000
1001    pub(crate) const fn decrement_size(&mut self) {
1002        self.0 = (self.0 as u32 - (1u32 << Self::K_SIZE_SHIFT)) as u8;
1003    }
1004}
1005
1006#[derive(Copy, Clone, Debug)]
1007pub(crate) struct SizeOpMap {
1008    pub(crate) table_id: u8,
1009    pub(crate) size_op_mask: u8,
1010    pub(crate) accept_mask: u16,
1011}
1012
1013pub(crate) const fn significant_simd_op<'a>(
1014    o0: &'a Operand,
1015    o1: &'a Operand,
1016    inst_flags: u32,
1017) -> &'a Operand {
1018    if (inst_flags & InstFlag::Long as u32) == 0 {
1019        o0
1020    } else {
1021        o1
1022    }
1023}
1024
1025/// AsmJit's `match_signature`: operands must share a signature, except the
1026/// first pair of Long/Narrow instructions (wide/narrow destination), which
1027/// AsmJit deliberately does not validate ("TODO: [ARM] Something smart to
1028/// validate this").
1029pub(crate) fn match_signature2(o0: &Operand, o1: &Operand, inst_flags: u32) -> bool {
1030    if inst_flags & (InstFlag::Long as u32 | InstFlag::Narrow as u32) == 0 {
1031        o0.signature() == o1.signature()
1032    } else {
1033        true
1034    }
1035}
1036
1037/// AsmJit's 3-operand `match_signature`.
1038pub(crate) fn match_signature3(o0: &Operand, o1: &Operand, o2: &Operand, inst_flags: u32) -> bool {
1039    match_signature2(o0, o1, inst_flags) && o1.signature() == o2.signature()
1040}
1041
1042/// AsmJit's 4-operand `match_signature`.
1043pub(crate) fn match_signature4(
1044    o0: &Operand,
1045    o1: &Operand,
1046    o2: &Operand,
1047    o3: &Operand,
1048    inst_flags: u32,
1049) -> bool {
1050    match_signature2(o0, o1, inst_flags)
1051        && o1.signature() == o2.signature()
1052        && o2.signature() == o3.signature()
1053}
1054
1055pub(crate) const fn element_type_to_size_op(
1056    vec_op_type: u32,
1057    reg_type: RegType,
1058    element_type: VecElementType,
1059) -> SizeOp {
1060    let map = &SIZE_OP_MAP[vec_op_type as usize];
1061    let table = &SIZE_OP_TABLE[map.table_id as usize];
1062
1063    // Mirrors AsmJit's `min(diff(reg_type, kVec8), diff(kVec128, kVec8) + 1)`:
1064    // out-of-range register types clamp to the invalid tail of the table.
1065    let a = (reg_type as usize).wrapping_sub(RegType::Vec8 as usize);
1066    let b = RegType::Vec128 as usize - RegType::Vec8 as usize;
1067
1068    let clamped = if a < b + 1 { a } else { b + 1 };
1069    let index = (clamped << 3) | (element_type as usize);
1070    let op = table.array[index];
1071    let mut modified_op = SizeOp::new(op.0 & map.size_op_mask);
1072
1073    if !bit_test32(map.accept_mask as u32, op.0 as u32) {
1074        modified_op.make_invalid();
1075    }
1076
1077    modified_op
1078}
1079
1080pub(crate) struct LMHImm {
1081    pub(crate) lm: u32,
1082    pub(crate) h: u32,
1083    pub(crate) max_rm_id: u32,
1084}
1085
1086pub(crate) fn encode_lmh(size_field: u32, element_index: u32, out: &mut LMHImm) -> bool {
1087    if size_field != 1 && size_field != 2 {
1088        return false;
1089    }
1090
1091    let h_shift = 3u32.saturating_sub(size_field);
1092    let lm_shift = size_field.saturating_sub(1u32);
1093    let max_element_index = 15u32 >> size_field;
1094
1095    out.h = element_index >> h_shift;
1096    out.lm = (element_index << lm_shift) & 0x3u32;
1097    out.max_rm_id = (8u32 << size_field).saturating_sub(1);
1098
1099    element_index <= max_element_index
1100}