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