Skip to main content

cranelift_codegen/isa/aarch64/inst/
emit.rs

1//! AArch64 ISA: binary code emission.
2
3use cranelift_control::ControlPlane;
4
5use crate::ir::{self, types::*};
6use crate::isa::aarch64;
7use crate::isa::aarch64::inst::*;
8use crate::trace;
9
10/// Memory addressing mode finalization: convert "special" modes (e.g.,
11/// generic arbitrary stack offset) into real addressing modes, possibly by
12/// emitting some helper instructions that come immediately before the use
13/// of this amode.
14pub fn mem_finalize(
15    sink: Option<&mut MachBuffer<Inst>>,
16    mem: &AMode,
17    access_ty: Type,
18    state: &EmitState,
19) -> (SmallVec<[Inst; 4]>, AMode) {
20    match mem {
21        &AMode::RegOffset { off, .. }
22        | &AMode::SPOffset { off }
23        | &AMode::FPOffset { off }
24        | &AMode::IncomingArg { off }
25        | &AMode::SlotOffset { off } => {
26            let basereg = match mem {
27                &AMode::RegOffset { rn, .. } => rn,
28                &AMode::SPOffset { .. }
29                | &AMode::SlotOffset { .. }
30                | &AMode::IncomingArg { .. } => stack_reg(),
31                &AMode::FPOffset { .. } => fp_reg(),
32                _ => unreachable!(),
33            };
34            let off = match mem {
35                &AMode::IncomingArg { .. } => {
36                    let frame_layout = state.frame_layout();
37                    i64::from(
38                        frame_layout.setup_area_size
39                            + frame_layout.tail_args_size
40                            + frame_layout.clobber_size
41                            + frame_layout.fixed_frame_storage_size
42                            + frame_layout.outgoing_args_size,
43                    ) - off
44                }
45                &AMode::SlotOffset { .. } => {
46                    let adj = i64::from(state.frame_layout().outgoing_args_size);
47                    trace!(
48                        "mem_finalize: slot offset {} + adj {} -> {}",
49                        off,
50                        adj,
51                        off + adj
52                    );
53                    off + adj
54                }
55                _ => off,
56            };
57
58            if let Some(simm9) = SImm9::maybe_from_i64(off) {
59                let mem = AMode::Unscaled { rn: basereg, simm9 };
60                (smallvec![], mem)
61            } else if let Some(uimm12) = UImm12Scaled::maybe_from_i64(off, access_ty) {
62                let mem = AMode::UnsignedOffset {
63                    rn: basereg,
64                    uimm12,
65                };
66                (smallvec![], mem)
67            } else {
68                let tmp = writable_spilltmp_reg();
69                (
70                    Inst::load_constant(tmp, off as u64),
71                    AMode::RegExtended {
72                        rn: basereg,
73                        rm: tmp.to_reg(),
74                        extendop: ExtendOp::SXTX,
75                    },
76                )
77            }
78        }
79
80        AMode::Const { addr } => {
81            let sink = match sink {
82                Some(sink) => sink,
83                None => return (smallvec![], mem.clone()),
84            };
85            let label = sink.get_label_for_constant(*addr);
86            let label = MemLabel::Mach(label);
87            (smallvec![], AMode::Label { label })
88        }
89
90        _ => (smallvec![], mem.clone()),
91    }
92}
93
94//=============================================================================
95// Instructions and subcomponents: emission
96
97pub(crate) fn machreg_to_gpr(m: Reg) -> u32 {
98    assert_eq!(m.class(), RegClass::Int);
99    u32::from(m.to_real_reg().unwrap().hw_enc() & 31)
100}
101
102pub(crate) fn machreg_to_vec(m: Reg) -> u32 {
103    assert_eq!(m.class(), RegClass::Float);
104    u32::from(m.to_real_reg().unwrap().hw_enc())
105}
106
107fn machreg_to_gpr_or_vec(m: Reg) -> u32 {
108    u32::from(m.to_real_reg().unwrap().hw_enc() & 31)
109}
110
111/// Encode a 3-register aeithmeric instruction.
112pub fn enc_arith_rrr(bits_31_21: u32, bits_15_10: u32, rd: Writable<Reg>, rn: Reg, rm: Reg) -> u32 {
113    (bits_31_21 << 21)
114        | (bits_15_10 << 10)
115        | machreg_to_gpr(rd.to_reg())
116        | (machreg_to_gpr(rn) << 5)
117        | (machreg_to_gpr(rm) << 16)
118}
119
120fn enc_arith_rr_imm12(
121    bits_31_24: u32,
122    immshift: u32,
123    imm12: u32,
124    rn: Reg,
125    rd: Writable<Reg>,
126) -> u32 {
127    (bits_31_24 << 24)
128        | (immshift << 22)
129        | (imm12 << 10)
130        | (machreg_to_gpr(rn) << 5)
131        | machreg_to_gpr(rd.to_reg())
132}
133
134fn enc_arith_rr_imml(bits_31_23: u32, imm_bits: u32, rn: Reg, rd: Writable<Reg>) -> u32 {
135    (bits_31_23 << 23) | (imm_bits << 10) | (machreg_to_gpr(rn) << 5) | machreg_to_gpr(rd.to_reg())
136}
137
138fn enc_arith_rrrr(top11: u32, rm: Reg, bit15: u32, ra: Reg, rn: Reg, rd: Writable<Reg>) -> u32 {
139    (top11 << 21)
140        | (machreg_to_gpr(rm) << 16)
141        | (bit15 << 15)
142        | (machreg_to_gpr(ra) << 10)
143        | (machreg_to_gpr(rn) << 5)
144        | machreg_to_gpr(rd.to_reg())
145}
146
147fn enc_jump26(op_31_26: u32, off_26_0: u32) -> u32 {
148    assert!(off_26_0 < (1 << 26));
149    (op_31_26 << 26) | off_26_0
150}
151
152fn enc_cmpbr(op_31_24: u32, off_18_0: u32, reg: Reg) -> u32 {
153    assert!(off_18_0 < (1 << 19));
154    (op_31_24 << 24) | (off_18_0 << 5) | machreg_to_gpr(reg)
155}
156
157fn enc_cbr(op_31_24: u32, off_18_0: u32, op_4: u32, cond: u32) -> u32 {
158    assert!(off_18_0 < (1 << 19));
159    assert!(cond < (1 << 4));
160    (op_31_24 << 24) | (off_18_0 << 5) | (op_4 << 4) | cond
161}
162
163/// Set the size bit of an instruction.
164fn enc_op_size(op: u32, size: OperandSize) -> u32 {
165    (op & !(1 << 31)) | (size.sf_bit() << 31)
166}
167
168fn enc_conditional_br(taken: BranchTarget, kind: CondBrKind) -> u32 {
169    match kind {
170        CondBrKind::Zero(reg, size) => enc_op_size(
171            enc_cmpbr(0b0_011010_0, taken.as_offset19_or_zero(), reg),
172            size,
173        ),
174        CondBrKind::NotZero(reg, size) => enc_op_size(
175            enc_cmpbr(0b0_011010_1, taken.as_offset19_or_zero(), reg),
176            size,
177        ),
178        CondBrKind::Cond(c) => enc_cbr(0b01010100, taken.as_offset19_or_zero(), 0b0, c.bits()),
179    }
180}
181
182fn enc_test_bit_and_branch(
183    kind: TestBitAndBranchKind,
184    taken: BranchTarget,
185    reg: Reg,
186    bit: u8,
187) -> u32 {
188    assert!(bit < 64);
189    let op_31 = u32::from(bit >> 5);
190    let op_23_19 = u32::from(bit & 0b11111);
191    let op_30_24 = 0b0110110
192        | match kind {
193            TestBitAndBranchKind::Z => 0,
194            TestBitAndBranchKind::NZ => 1,
195        };
196    (op_31 << 31)
197        | (op_30_24 << 24)
198        | (op_23_19 << 19)
199        | (taken.as_offset14_or_zero() << 5)
200        | machreg_to_gpr(reg)
201}
202
203/// Encode a move-wide instruction.
204pub fn enc_move_wide(
205    op: MoveWideOp,
206    rd: Writable<Reg>,
207    imm: MoveWideConst,
208    size: OperandSize,
209) -> u32 {
210    assert!(imm.shift <= 0b11);
211    let op = match op {
212        MoveWideOp::MovN => 0b00,
213        MoveWideOp::MovZ => 0b10,
214    };
215    0x12800000
216        | size.sf_bit() << 31
217        | op << 29
218        | u32::from(imm.shift) << 21
219        | u32::from(imm.bits) << 5
220        | machreg_to_gpr(rd.to_reg())
221}
222
223/// Encode a move-keep immediate instruction.
224pub fn enc_movk(rd: Writable<Reg>, imm: MoveWideConst, size: OperandSize) -> u32 {
225    assert!(imm.shift <= 0b11);
226    0x72800000
227        | size.sf_bit() << 31
228        | u32::from(imm.shift) << 21
229        | u32::from(imm.bits) << 5
230        | machreg_to_gpr(rd.to_reg())
231}
232
233fn enc_ldst_pair(op_31_22: u32, simm7: SImm7Scaled, rn: Reg, rt: Reg, rt2: Reg) -> u32 {
234    (op_31_22 << 22)
235        | (simm7.bits() << 15)
236        | (machreg_to_gpr(rt2) << 10)
237        | (machreg_to_gpr(rn) << 5)
238        | machreg_to_gpr(rt)
239}
240
241fn enc_ldst_simm9(op_31_22: u32, simm9: SImm9, op_11_10: u32, rn: Reg, rd: Reg) -> u32 {
242    (op_31_22 << 22)
243        | (simm9.bits() << 12)
244        | (op_11_10 << 10)
245        | (machreg_to_gpr(rn) << 5)
246        | machreg_to_gpr_or_vec(rd)
247}
248
249fn enc_ldst_uimm12(op_31_22: u32, uimm12: UImm12Scaled, rn: Reg, rd: Reg) -> u32 {
250    (op_31_22 << 22)
251        | (0b1 << 24)
252        | (uimm12.bits() << 10)
253        | (machreg_to_gpr(rn) << 5)
254        | machreg_to_gpr_or_vec(rd)
255}
256
257fn enc_ldst_reg(
258    op_31_22: u32,
259    rn: Reg,
260    rm: Reg,
261    s_bit: bool,
262    extendop: Option<ExtendOp>,
263    rd: Reg,
264) -> u32 {
265    let s_bit = if s_bit { 1 } else { 0 };
266    let extend_bits = match extendop {
267        Some(ExtendOp::UXTW) => 0b010,
268        Some(ExtendOp::SXTW) => 0b110,
269        Some(ExtendOp::SXTX) => 0b111,
270        None => 0b011, // LSL
271        _ => panic!("bad extend mode for ld/st AMode"),
272    };
273    (op_31_22 << 22)
274        | (1 << 21)
275        | (machreg_to_gpr(rm) << 16)
276        | (extend_bits << 13)
277        | (s_bit << 12)
278        | (0b10 << 10)
279        | (machreg_to_gpr(rn) << 5)
280        | machreg_to_gpr_or_vec(rd)
281}
282
283pub(crate) fn enc_ldst_imm19(op_31_24: u32, imm19: u32, rd: Reg) -> u32 {
284    (op_31_24 << 24) | (imm19 << 5) | machreg_to_gpr_or_vec(rd)
285}
286
287fn enc_ldst_vec(q: u32, size: u32, rn: Reg, rt: Writable<Reg>) -> u32 {
288    debug_assert_eq!(q & 0b1, q);
289    debug_assert_eq!(size & 0b11, size);
290    0b0_0_0011010_10_00000_110_0_00_00000_00000
291        | q << 30
292        | size << 10
293        | machreg_to_gpr(rn) << 5
294        | machreg_to_vec(rt.to_reg())
295}
296
297fn enc_ldst_vec_pair(
298    opc: u32,
299    amode: u32,
300    is_load: bool,
301    simm7: SImm7Scaled,
302    rn: Reg,
303    rt: Reg,
304    rt2: Reg,
305) -> u32 {
306    debug_assert_eq!(opc & 0b11, opc);
307    debug_assert_eq!(amode & 0b11, amode);
308
309    0b00_10110_00_0_0000000_00000_00000_00000
310        | opc << 30
311        | amode << 23
312        | (is_load as u32) << 22
313        | simm7.bits() << 15
314        | machreg_to_vec(rt2) << 10
315        | machreg_to_gpr(rn) << 5
316        | machreg_to_vec(rt)
317}
318
319fn enc_vec_rrr(top11: u32, rm: Reg, bit15_10: u32, rn: Reg, rd: Writable<Reg>) -> u32 {
320    (top11 << 21)
321        | (machreg_to_vec(rm) << 16)
322        | (bit15_10 << 10)
323        | (machreg_to_vec(rn) << 5)
324        | machreg_to_vec(rd.to_reg())
325}
326
327fn enc_vec_rrr_long(
328    q: u32,
329    u: u32,
330    size: u32,
331    bit14: u32,
332    rm: Reg,
333    rn: Reg,
334    rd: Writable<Reg>,
335) -> u32 {
336    debug_assert_eq!(q & 0b1, q);
337    debug_assert_eq!(u & 0b1, u);
338    debug_assert_eq!(size & 0b11, size);
339    debug_assert_eq!(bit14 & 0b1, bit14);
340
341    0b0_0_0_01110_00_1_00000_100000_00000_00000
342        | q << 30
343        | u << 29
344        | size << 22
345        | bit14 << 14
346        | (machreg_to_vec(rm) << 16)
347        | (machreg_to_vec(rn) << 5)
348        | machreg_to_vec(rd.to_reg())
349}
350
351fn enc_bit_rr(size: u32, opcode2: u32, opcode1: u32, rn: Reg, rd: Writable<Reg>) -> u32 {
352    (0b01011010110 << 21)
353        | size << 31
354        | opcode2 << 16
355        | opcode1 << 10
356        | machreg_to_gpr(rn) << 5
357        | machreg_to_gpr(rd.to_reg())
358}
359
360pub(crate) fn enc_br(rn: Reg) -> u32 {
361    0b1101011_0000_11111_000000_00000_00000 | (machreg_to_gpr(rn) << 5)
362}
363
364pub(crate) fn enc_adr_inst(opcode: u32, off: i32, rd: Writable<Reg>) -> u32 {
365    let off = u32::try_from(off).unwrap();
366    let immlo = off & 3;
367    let immhi = (off >> 2) & ((1 << 19) - 1);
368    opcode | (immlo << 29) | (immhi << 5) | machreg_to_gpr(rd.to_reg())
369}
370
371pub(crate) fn enc_adr(off: i32, rd: Writable<Reg>) -> u32 {
372    let opcode = 0b00010000 << 24;
373    enc_adr_inst(opcode, off, rd)
374}
375
376pub(crate) fn enc_adrp(off: i32, rd: Writable<Reg>) -> u32 {
377    let opcode = 0b10010000 << 24;
378    enc_adr_inst(opcode, off, rd)
379}
380
381fn enc_csel(rd: Writable<Reg>, rn: Reg, rm: Reg, cond: Cond, op: u32, o2: u32) -> u32 {
382    debug_assert_eq!(op & 0b1, op);
383    debug_assert_eq!(o2 & 0b1, o2);
384    0b100_11010100_00000_0000_00_00000_00000
385        | (op << 30)
386        | (machreg_to_gpr(rm) << 16)
387        | (cond.bits() << 12)
388        | (o2 << 10)
389        | (machreg_to_gpr(rn) << 5)
390        | machreg_to_gpr(rd.to_reg())
391}
392
393fn enc_fcsel(rd: Writable<Reg>, rn: Reg, rm: Reg, cond: Cond, size: ScalarSize) -> u32 {
394    0b000_11110_00_1_00000_0000_11_00000_00000
395        | (size.ftype() << 22)
396        | (machreg_to_vec(rm) << 16)
397        | (machreg_to_vec(rn) << 5)
398        | machreg_to_vec(rd.to_reg())
399        | (cond.bits() << 12)
400}
401
402fn enc_ccmp(size: OperandSize, rn: Reg, rm: Reg, nzcv: NZCV, cond: Cond) -> u32 {
403    0b0_1_1_11010010_00000_0000_00_00000_0_0000
404        | size.sf_bit() << 31
405        | machreg_to_gpr(rm) << 16
406        | cond.bits() << 12
407        | machreg_to_gpr(rn) << 5
408        | nzcv.bits()
409}
410
411fn enc_ccmp_imm(size: OperandSize, rn: Reg, imm: UImm5, nzcv: NZCV, cond: Cond) -> u32 {
412    0b0_1_1_11010010_00000_0000_10_00000_0_0000
413        | size.sf_bit() << 31
414        | imm.bits() << 16
415        | cond.bits() << 12
416        | machreg_to_gpr(rn) << 5
417        | nzcv.bits()
418}
419
420fn enc_bfm(opc: u8, size: OperandSize, rd: Writable<Reg>, rn: Reg, immr: u8, imms: u8) -> u32 {
421    match size {
422        OperandSize::Size64 => {
423            debug_assert!(immr <= 63);
424            debug_assert!(imms <= 63);
425        }
426        OperandSize::Size32 => {
427            debug_assert!(immr <= 31);
428            debug_assert!(imms <= 31);
429        }
430    }
431    debug_assert_eq!(opc & 0b11, opc);
432    let n_bit = size.sf_bit();
433    0b0_00_100110_0_000000_000000_00000_00000
434        | size.sf_bit() << 31
435        | u32::from(opc) << 29
436        | n_bit << 22
437        | u32::from(immr) << 16
438        | u32::from(imms) << 10
439        | machreg_to_gpr(rn) << 5
440        | machreg_to_gpr(rd.to_reg())
441}
442
443fn enc_vecmov(is_16b: bool, rd: Writable<Reg>, rn: Reg) -> u32 {
444    0b00001110_101_00000_00011_1_00000_00000
445        | ((is_16b as u32) << 30)
446        | machreg_to_vec(rd.to_reg())
447        | (machreg_to_vec(rn) << 16)
448        | (machreg_to_vec(rn) << 5)
449}
450
451fn enc_fpurr(top22: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
452    (top22 << 10) | (machreg_to_vec(rn) << 5) | machreg_to_vec(rd.to_reg())
453}
454
455fn enc_fpurrr(top22: u32, rd: Writable<Reg>, rn: Reg, rm: Reg) -> u32 {
456    (top22 << 10)
457        | (machreg_to_vec(rm) << 16)
458        | (machreg_to_vec(rn) << 5)
459        | machreg_to_vec(rd.to_reg())
460}
461
462fn enc_fpurrrr(top17: u32, rd: Writable<Reg>, rn: Reg, rm: Reg, ra: Reg) -> u32 {
463    (top17 << 15)
464        | (machreg_to_vec(rm) << 16)
465        | (machreg_to_vec(ra) << 10)
466        | (machreg_to_vec(rn) << 5)
467        | machreg_to_vec(rd.to_reg())
468}
469
470fn enc_fcmp(size: ScalarSize, rn: Reg, rm: Reg) -> u32 {
471    0b000_11110_00_1_00000_00_1000_00000_00000
472        | (size.ftype() << 22)
473        | (machreg_to_vec(rm) << 16)
474        | (machreg_to_vec(rn) << 5)
475}
476
477fn enc_fputoint(top16: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
478    (top16 << 16) | (machreg_to_vec(rn) << 5) | machreg_to_gpr(rd.to_reg())
479}
480
481fn enc_inttofpu(top16: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
482    (top16 << 16) | (machreg_to_gpr(rn) << 5) | machreg_to_vec(rd.to_reg())
483}
484
485fn enc_fround(top22: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
486    (top22 << 10) | (machreg_to_vec(rn) << 5) | machreg_to_vec(rd.to_reg())
487}
488
489fn enc_vec_rr_misc(qu: u32, size: u32, bits_12_16: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
490    debug_assert_eq!(qu & 0b11, qu);
491    debug_assert_eq!(size & 0b11, size);
492    debug_assert_eq!(bits_12_16 & 0b11111, bits_12_16);
493    let bits = 0b0_00_01110_00_10000_00000_10_00000_00000;
494    bits | qu << 29
495        | size << 22
496        | bits_12_16 << 12
497        | machreg_to_vec(rn) << 5
498        | machreg_to_vec(rd.to_reg())
499}
500
501fn enc_vec_rr_pair(bits_12_16: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
502    debug_assert_eq!(bits_12_16 & 0b11111, bits_12_16);
503
504    0b010_11110_11_11000_11011_10_00000_00000
505        | bits_12_16 << 12
506        | machreg_to_vec(rn) << 5
507        | machreg_to_vec(rd.to_reg())
508}
509
510fn enc_vec_rr_pair_long(u: u32, enc_size: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
511    debug_assert_eq!(u & 0b1, u);
512    debug_assert_eq!(enc_size & 0b1, enc_size);
513
514    0b0_1_0_01110_00_10000_00_0_10_10_00000_00000
515        | u << 29
516        | enc_size << 22
517        | machreg_to_vec(rn) << 5
518        | machreg_to_vec(rd.to_reg())
519}
520
521fn enc_vec_lanes(q: u32, u: u32, size: u32, opcode: u32, rd: Writable<Reg>, rn: Reg) -> u32 {
522    debug_assert_eq!(q & 0b1, q);
523    debug_assert_eq!(u & 0b1, u);
524    debug_assert_eq!(size & 0b11, size);
525    debug_assert_eq!(opcode & 0b11111, opcode);
526    0b0_0_0_01110_00_11000_0_0000_10_00000_00000
527        | q << 30
528        | u << 29
529        | size << 22
530        | opcode << 12
531        | machreg_to_vec(rn) << 5
532        | machreg_to_vec(rd.to_reg())
533}
534
535fn enc_tbl(is_extension: bool, len: u32, rd: Writable<Reg>, rn: Reg, rm: Reg) -> u32 {
536    debug_assert_eq!(len & 0b11, len);
537    0b0_1_001110_000_00000_0_00_0_00_00000_00000
538        | (machreg_to_vec(rm) << 16)
539        | len << 13
540        | (is_extension as u32) << 12
541        | (machreg_to_vec(rn) << 5)
542        | machreg_to_vec(rd.to_reg())
543}
544
545fn enc_dmb_ish() -> u32 {
546    0xD5033BBF
547}
548
549fn enc_acq_rel(ty: Type, op: AtomicRMWOp, rs: Reg, rt: Writable<Reg>, rn: Reg) -> u32 {
550    assert!(machreg_to_gpr(rt.to_reg()) != 31);
551    let sz = match ty {
552        I64 => 0b11,
553        I32 => 0b10,
554        I16 => 0b01,
555        I8 => 0b00,
556        _ => unreachable!(),
557    };
558    let bit15 = match op {
559        AtomicRMWOp::Swp => 0b1,
560        _ => 0b0,
561    };
562    let op = match op {
563        AtomicRMWOp::Add => 0b000,
564        AtomicRMWOp::Clr => 0b001,
565        AtomicRMWOp::Eor => 0b010,
566        AtomicRMWOp::Set => 0b011,
567        AtomicRMWOp::Smax => 0b100,
568        AtomicRMWOp::Smin => 0b101,
569        AtomicRMWOp::Umax => 0b110,
570        AtomicRMWOp::Umin => 0b111,
571        AtomicRMWOp::Swp => 0b000,
572    };
573    0b00_111_000_111_00000_0_000_00_00000_00000
574        | (sz << 30)
575        | (machreg_to_gpr(rs) << 16)
576        | bit15 << 15
577        | (op << 12)
578        | (machreg_to_gpr(rn) << 5)
579        | machreg_to_gpr(rt.to_reg())
580}
581
582fn enc_ldar(ty: Type, rt: Writable<Reg>, rn: Reg) -> u32 {
583    let sz = match ty {
584        I64 => 0b11,
585        I32 => 0b10,
586        I16 => 0b01,
587        I8 => 0b00,
588        _ => unreachable!(),
589    };
590    0b00_001000_1_1_0_11111_1_11111_00000_00000
591        | (sz << 30)
592        | (machreg_to_gpr(rn) << 5)
593        | machreg_to_gpr(rt.to_reg())
594}
595
596fn enc_stlr(ty: Type, rt: Reg, rn: Reg) -> u32 {
597    let sz = match ty {
598        I64 => 0b11,
599        I32 => 0b10,
600        I16 => 0b01,
601        I8 => 0b00,
602        _ => unreachable!(),
603    };
604    0b00_001000_100_11111_1_11111_00000_00000
605        | (sz << 30)
606        | (machreg_to_gpr(rn) << 5)
607        | machreg_to_gpr(rt)
608}
609
610fn enc_ldaxr(ty: Type, rt: Writable<Reg>, rn: Reg) -> u32 {
611    let sz = match ty {
612        I64 => 0b11,
613        I32 => 0b10,
614        I16 => 0b01,
615        I8 => 0b00,
616        _ => unreachable!(),
617    };
618    0b00_001000_0_1_0_11111_1_11111_00000_00000
619        | (sz << 30)
620        | (machreg_to_gpr(rn) << 5)
621        | machreg_to_gpr(rt.to_reg())
622}
623
624fn enc_stlxr(ty: Type, rs: Writable<Reg>, rt: Reg, rn: Reg) -> u32 {
625    let sz = match ty {
626        I64 => 0b11,
627        I32 => 0b10,
628        I16 => 0b01,
629        I8 => 0b00,
630        _ => unreachable!(),
631    };
632    0b00_001000_000_00000_1_11111_00000_00000
633        | (sz << 30)
634        | (machreg_to_gpr(rs.to_reg()) << 16)
635        | (machreg_to_gpr(rn) << 5)
636        | machreg_to_gpr(rt)
637}
638
639fn enc_cas(size: u32, rs: Writable<Reg>, rt: Reg, rn: Reg) -> u32 {
640    debug_assert_eq!(size & 0b11, size);
641
642    0b00_0010001_1_1_00000_1_11111_00000_00000
643        | size << 30
644        | machreg_to_gpr(rs.to_reg()) << 16
645        | machreg_to_gpr(rn) << 5
646        | machreg_to_gpr(rt)
647}
648
649fn enc_casp(rs: Writable<Reg>, rt: Reg, rn: Reg) -> u32 {
650    debug_assert_eq!(machreg_to_gpr(rs.to_reg()) & 1, 0);
651    debug_assert_eq!(machreg_to_gpr(rt) & 1, 0);
652
653    0b0_1_0010000_1_1_00000_1_11111_00000_00000
654        | machreg_to_gpr(rs.to_reg()) << 16
655        | machreg_to_gpr(rn) << 5
656        | machreg_to_gpr(rt)
657}
658
659fn enc_asimd_mod_imm(rd: Writable<Reg>, q_op: u32, cmode: u32, imm: u8) -> u32 {
660    let abc = (imm >> 5) as u32;
661    let defgh = (imm & 0b11111) as u32;
662
663    debug_assert_eq!(cmode & 0b1111, cmode);
664    debug_assert_eq!(q_op & 0b11, q_op);
665
666    0b0_0_0_0111100000_000_0000_01_00000_00000
667        | (q_op << 29)
668        | (abc << 16)
669        | (cmode << 12)
670        | (defgh << 5)
671        | machreg_to_vec(rd.to_reg())
672}
673
674/// State carried between emissions of a sequence of instructions.
675#[derive(Default, Clone, Debug)]
676pub struct EmitState {
677    /// The user stack map for the upcoming instruction, as provided to
678    /// `pre_safepoint()`.
679    user_stack_map: Option<ir::UserStackMap>,
680
681    /// Only used during fuzz-testing. Otherwise, it is a zero-sized struct and
682    /// optimized away at compiletime. See [cranelift_control].
683    ctrl_plane: ControlPlane,
684
685    frame_layout: FrameLayout,
686}
687
688impl MachInstEmitState<Inst> for EmitState {
689    fn new(abi: &Callee<AArch64MachineDeps>, ctrl_plane: ControlPlane) -> Self {
690        EmitState {
691            user_stack_map: None,
692            ctrl_plane,
693            frame_layout: abi.frame_layout().clone(),
694        }
695    }
696
697    fn pre_safepoint(&mut self, user_stack_map: Option<ir::UserStackMap>) {
698        self.user_stack_map = user_stack_map;
699    }
700
701    fn ctrl_plane_mut(&mut self) -> &mut ControlPlane {
702        &mut self.ctrl_plane
703    }
704
705    fn take_ctrl_plane(self) -> ControlPlane {
706        self.ctrl_plane
707    }
708
709    fn frame_layout(&self) -> &FrameLayout {
710        &self.frame_layout
711    }
712}
713
714impl EmitState {
715    fn take_stack_map(&mut self) -> Option<ir::UserStackMap> {
716        self.user_stack_map.take()
717    }
718
719    fn clear_post_insn(&mut self) {
720        self.user_stack_map = None;
721    }
722}
723
724/// Constant state used during function compilation.
725pub struct EmitInfo {
726    flags: settings::Flags,
727    isa_flags: aarch64::settings::Flags,
728}
729
730impl EmitInfo {
731    /// Create a constant state for emission of instructions.
732    pub fn new(flags: settings::Flags, isa_flags: aarch64::settings::Flags) -> Self {
733        Self { flags, isa_flags }
734    }
735}
736
737impl MachInstEmit for Inst {
738    type State = EmitState;
739    type Info = EmitInfo;
740
741    fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
742        // N.B.: we *must* not exceed the "worst-case size" used to compute
743        // where to insert islands, except when islands are explicitly triggered
744        // (with an `EmitIsland`). We check this in debug builds. This is `mut`
745        // to allow disabling the check for `JTSequence`, which is always
746        // emitted following an `EmitIsland`.
747        let mut start_off = sink.cur_offset();
748
749        match self {
750            &Inst::AluRRR {
751                alu_op,
752                size,
753                rd,
754                rn,
755                rm,
756            } => {
757                debug_assert!(match alu_op {
758                    ALUOp::SMulH | ALUOp::UMulH => size == OperandSize::Size64,
759                    _ => true,
760                });
761                let top11 = match alu_op {
762                    ALUOp::Add => 0b00001011_000,
763                    ALUOp::Adc => 0b00011010_000,
764                    ALUOp::AdcS => 0b00111010_000,
765                    ALUOp::Sub => 0b01001011_000,
766                    ALUOp::Sbc => 0b01011010_000,
767                    ALUOp::SbcS => 0b01111010_000,
768                    ALUOp::Orr => 0b00101010_000,
769                    ALUOp::And => 0b00001010_000,
770                    ALUOp::AndS => 0b01101010_000,
771                    ALUOp::Eor => 0b01001010_000,
772                    ALUOp::OrrNot => 0b00101010_001,
773                    ALUOp::AndNot => 0b00001010_001,
774                    ALUOp::EorNot => 0b01001010_001,
775                    ALUOp::AddS => 0b00101011_000,
776                    ALUOp::SubS => 0b01101011_000,
777                    ALUOp::SDiv | ALUOp::UDiv => 0b00011010_110,
778                    ALUOp::Extr | ALUOp::Lsr | ALUOp::Asr | ALUOp::Lsl => 0b00011010_110,
779                    ALUOp::SMulH => 0b10011011_010,
780                    ALUOp::UMulH => 0b10011011_110,
781                };
782
783                let top11 = top11 | size.sf_bit() << 10;
784                let bit15_10 = match alu_op {
785                    ALUOp::SDiv => 0b000011,
786                    ALUOp::UDiv => 0b000010,
787                    ALUOp::Extr => 0b001011,
788                    ALUOp::Lsr => 0b001001,
789                    ALUOp::Asr => 0b001010,
790                    ALUOp::Lsl => 0b001000,
791                    ALUOp::SMulH | ALUOp::UMulH => 0b011111,
792                    _ => 0b000000,
793                };
794                debug_assert_ne!(writable_stack_reg(), rd);
795                // The stack pointer is the zero register in this context, so this might be an
796                // indication that something is wrong.
797                debug_assert_ne!(stack_reg(), rn);
798                debug_assert_ne!(stack_reg(), rm);
799                sink.put4(enc_arith_rrr(top11, bit15_10, rd, rn, rm));
800            }
801            &Inst::AluRRRR {
802                alu_op,
803                size,
804                rd,
805                rm,
806                rn,
807                ra,
808            } => {
809                let (top11, bit15) = match alu_op {
810                    ALUOp3::MAdd => (0b0_00_11011_000, 0),
811                    ALUOp3::MSub => (0b0_00_11011_000, 1),
812                    ALUOp3::UMAddL => {
813                        debug_assert!(size == OperandSize::Size32);
814                        (0b1_00_11011_1_01, 0)
815                    }
816                    ALUOp3::SMAddL => {
817                        debug_assert!(size == OperandSize::Size32);
818                        (0b1_00_11011_0_01, 0)
819                    }
820                };
821                let top11 = top11 | size.sf_bit() << 10;
822                sink.put4(enc_arith_rrrr(top11, rm, bit15, ra, rn, rd));
823            }
824            &Inst::AluRRImm12 {
825                alu_op,
826                size,
827                rd,
828                rn,
829                ref imm12,
830            } => {
831                let top8 = match alu_op {
832                    ALUOp::Add => 0b000_10001,
833                    ALUOp::Sub => 0b010_10001,
834                    ALUOp::AddS => 0b001_10001,
835                    ALUOp::SubS => 0b011_10001,
836                    _ => unimplemented!("{:?}", alu_op),
837                };
838                let top8 = top8 | size.sf_bit() << 7;
839                sink.put4(enc_arith_rr_imm12(
840                    top8,
841                    imm12.shift_bits(),
842                    imm12.imm_bits(),
843                    rn,
844                    rd,
845                ));
846            }
847            &Inst::AluRRImmLogic {
848                alu_op,
849                size,
850                rd,
851                rn,
852                ref imml,
853            } => {
854                let (top9, inv) = match alu_op {
855                    ALUOp::Orr => (0b001_100100, false),
856                    ALUOp::And => (0b000_100100, false),
857                    ALUOp::AndS => (0b011_100100, false),
858                    ALUOp::Eor => (0b010_100100, false),
859                    ALUOp::OrrNot => (0b001_100100, true),
860                    ALUOp::AndNot => (0b000_100100, true),
861                    ALUOp::EorNot => (0b010_100100, true),
862                    _ => unimplemented!("{:?}", alu_op),
863                };
864                let top9 = top9 | size.sf_bit() << 8;
865                let imml = if inv { imml.invert() } else { *imml };
866                sink.put4(enc_arith_rr_imml(top9, imml.enc_bits(), rn, rd));
867            }
868
869            &Inst::AluRRImmShift {
870                alu_op,
871                size,
872                rd,
873                rn,
874                ref immshift,
875            } => {
876                let amt = immshift.value();
877                let (top10, immr, imms) = match alu_op {
878                    ALUOp::Extr => (0b0001001110, machreg_to_gpr(rn), u32::from(amt)),
879                    ALUOp::Lsr => (0b0101001100, u32::from(amt), 0b011111),
880                    ALUOp::Asr => (0b0001001100, u32::from(amt), 0b011111),
881                    ALUOp::Lsl => {
882                        let bits = if size.is64() { 64 } else { 32 };
883                        (
884                            0b0101001100,
885                            u32::from((bits - amt) % bits),
886                            u32::from(bits - 1 - amt),
887                        )
888                    }
889                    _ => unimplemented!("{:?}", alu_op),
890                };
891                let top10 = top10 | size.sf_bit() << 9 | size.sf_bit();
892                let imms = match alu_op {
893                    ALUOp::Lsr | ALUOp::Asr => imms | size.sf_bit() << 5,
894                    _ => imms,
895                };
896                sink.put4(
897                    (top10 << 22)
898                        | (immr << 16)
899                        | (imms << 10)
900                        | (machreg_to_gpr(rn) << 5)
901                        | machreg_to_gpr(rd.to_reg()),
902                );
903            }
904
905            &Inst::AluRRRShift {
906                alu_op,
907                size,
908                rd,
909                rn,
910                rm,
911                ref shiftop,
912            } => {
913                let top11: u32 = match alu_op {
914                    ALUOp::Add => 0b000_01011000,
915                    ALUOp::AddS => 0b001_01011000,
916                    ALUOp::Sub => 0b010_01011000,
917                    ALUOp::SubS => 0b011_01011000,
918                    ALUOp::Orr => 0b001_01010000,
919                    ALUOp::And => 0b000_01010000,
920                    ALUOp::AndS => 0b011_01010000,
921                    ALUOp::Eor => 0b010_01010000,
922                    ALUOp::OrrNot => 0b001_01010001,
923                    ALUOp::EorNot => 0b010_01010001,
924                    ALUOp::AndNot => 0b000_01010001,
925                    ALUOp::Extr => 0b000_10011100,
926                    _ => unimplemented!("{:?}", alu_op),
927                };
928                let top11 = top11 | size.sf_bit() << 10;
929                let top11 = top11 | (u32::from(shiftop.op().bits()) << 1);
930                let bits_15_10 = u32::from(shiftop.amt().value());
931                sink.put4(enc_arith_rrr(top11, bits_15_10, rd, rn, rm));
932            }
933
934            &Inst::AluRRRExtend {
935                alu_op,
936                size,
937                rd,
938                rn,
939                rm,
940                extendop,
941            } => {
942                let top11: u32 = match alu_op {
943                    ALUOp::Add => 0b00001011001,
944                    ALUOp::Sub => 0b01001011001,
945                    ALUOp::AddS => 0b00101011001,
946                    ALUOp::SubS => 0b01101011001,
947                    _ => unimplemented!("{:?}", alu_op),
948                };
949                let top11 = top11 | size.sf_bit() << 10;
950                let bits_15_10 = u32::from(extendop.bits()) << 3;
951                sink.put4(enc_arith_rrr(top11, bits_15_10, rd, rn, rm));
952            }
953
954            &Inst::BitRR {
955                op, size, rd, rn, ..
956            } => {
957                let (op1, op2) = match op {
958                    BitOp::RBit => (0b00000, 0b000000),
959                    BitOp::Clz => (0b00000, 0b000100),
960                    BitOp::Cls => (0b00000, 0b000101),
961                    BitOp::Rev16 => (0b00000, 0b000001),
962                    BitOp::Rev32 => (0b00000, 0b000010),
963                    BitOp::Rev64 => (0b00000, 0b000011),
964                };
965                sink.put4(enc_bit_rr(size.sf_bit(), op1, op2, rn, rd))
966            }
967
968            &Inst::ULoad8 { rd, ref mem, flags }
969            | &Inst::SLoad8 { rd, ref mem, flags }
970            | &Inst::ULoad16 { rd, ref mem, flags }
971            | &Inst::SLoad16 { rd, ref mem, flags }
972            | &Inst::ULoad32 { rd, ref mem, flags }
973            | &Inst::SLoad32 { rd, ref mem, flags }
974            | &Inst::ULoad64 {
975                rd, ref mem, flags, ..
976            }
977            | &Inst::FpuLoad16 { rd, ref mem, flags }
978            | &Inst::FpuLoad32 { rd, ref mem, flags }
979            | &Inst::FpuLoad64 { rd, ref mem, flags }
980            | &Inst::FpuLoad128 { rd, ref mem, flags } => {
981                let mem = mem.clone();
982                let access_ty = self.mem_type().unwrap();
983                let (mem_insts, mem) = mem_finalize(Some(sink), &mem, access_ty, state);
984
985                for inst in mem_insts.into_iter() {
986                    inst.emit(sink, emit_info, state);
987                }
988
989                // ldst encoding helpers take Reg, not Writable<Reg>.
990                let rd = rd.to_reg();
991
992                // This is the base opcode (top 10 bits) for the "unscaled
993                // immediate" form (Unscaled). Other addressing modes will OR in
994                // other values for bits 24/25 (bits 1/2 of this constant).
995                let op = match self {
996                    Inst::ULoad8 { .. } => 0b0011100001,
997                    Inst::SLoad8 { .. } => 0b0011100010,
998                    Inst::ULoad16 { .. } => 0b0111100001,
999                    Inst::SLoad16 { .. } => 0b0111100010,
1000                    Inst::ULoad32 { .. } => 0b1011100001,
1001                    Inst::SLoad32 { .. } => 0b1011100010,
1002                    Inst::ULoad64 { .. } => 0b1111100001,
1003                    Inst::FpuLoad16 { .. } => 0b0111110001,
1004                    Inst::FpuLoad32 { .. } => 0b1011110001,
1005                    Inst::FpuLoad64 { .. } => 0b1111110001,
1006                    Inst::FpuLoad128 { .. } => 0b0011110011,
1007                    _ => unreachable!(),
1008                };
1009
1010                if let Some(trap_code) = flags.trap_code() {
1011                    // Register the offset at which the actual load instruction starts.
1012                    sink.add_trap(trap_code);
1013                }
1014
1015                match &mem {
1016                    &AMode::Unscaled { rn, simm9 } => {
1017                        let reg = rn;
1018                        sink.put4(enc_ldst_simm9(op, simm9, 0b00, reg, rd));
1019                    }
1020                    &AMode::UnsignedOffset { rn, uimm12 } => {
1021                        let reg = rn;
1022                        sink.put4(enc_ldst_uimm12(op, uimm12, reg, rd));
1023                    }
1024                    &AMode::RegReg { rn, rm } => {
1025                        let r1 = rn;
1026                        let r2 = rm;
1027                        sink.put4(enc_ldst_reg(
1028                            op, r1, r2, /* scaled = */ false, /* extendop = */ None, rd,
1029                        ));
1030                    }
1031                    &AMode::RegScaled { rn, rm } | &AMode::RegScaledExtended { rn, rm, .. } => {
1032                        let r1 = rn;
1033                        let r2 = rm;
1034                        let extendop = match &mem {
1035                            &AMode::RegScaled { .. } => None,
1036                            &AMode::RegScaledExtended { extendop, .. } => Some(extendop),
1037                            _ => unreachable!(),
1038                        };
1039                        sink.put4(enc_ldst_reg(
1040                            op, r1, r2, /* scaled = */ true, extendop, rd,
1041                        ));
1042                    }
1043                    &AMode::RegExtended { rn, rm, extendop } => {
1044                        let r1 = rn;
1045                        let r2 = rm;
1046                        sink.put4(enc_ldst_reg(
1047                            op,
1048                            r1,
1049                            r2,
1050                            /* scaled = */ false,
1051                            Some(extendop),
1052                            rd,
1053                        ));
1054                    }
1055                    &AMode::Label { ref label } => {
1056                        let offset = match label {
1057                            // cast i32 to u32 (two's-complement)
1058                            MemLabel::PCRel(off) => *off as u32,
1059                            // Emit a relocation into the `MachBuffer`
1060                            // for the label that's being loaded from and
1061                            // encode an address of 0 in its place which will
1062                            // get filled in by relocation resolution later on.
1063                            MemLabel::Mach(label) => {
1064                                sink.use_label_at_offset(
1065                                    sink.cur_offset(),
1066                                    *label,
1067                                    LabelUse::Ldr19,
1068                                );
1069                                0
1070                            }
1071                        } / 4;
1072                        assert!(offset < (1 << 19));
1073                        match self {
1074                            &Inst::ULoad32 { .. } => {
1075                                sink.put4(enc_ldst_imm19(0b00011000, offset, rd));
1076                            }
1077                            &Inst::SLoad32 { .. } => {
1078                                sink.put4(enc_ldst_imm19(0b10011000, offset, rd));
1079                            }
1080                            &Inst::FpuLoad32 { .. } => {
1081                                sink.put4(enc_ldst_imm19(0b00011100, offset, rd));
1082                            }
1083                            &Inst::ULoad64 { .. } => {
1084                                sink.put4(enc_ldst_imm19(0b01011000, offset, rd));
1085                            }
1086                            &Inst::FpuLoad64 { .. } => {
1087                                sink.put4(enc_ldst_imm19(0b01011100, offset, rd));
1088                            }
1089                            &Inst::FpuLoad128 { .. } => {
1090                                sink.put4(enc_ldst_imm19(0b10011100, offset, rd));
1091                            }
1092                            _ => panic!("Unsupported size for LDR from constant pool!"),
1093                        }
1094                    }
1095                    &AMode::SPPreIndexed { simm9 } => {
1096                        let reg = stack_reg();
1097                        sink.put4(enc_ldst_simm9(op, simm9, 0b11, reg, rd));
1098                    }
1099                    &AMode::SPPostIndexed { simm9 } => {
1100                        let reg = stack_reg();
1101                        sink.put4(enc_ldst_simm9(op, simm9, 0b01, reg, rd));
1102                    }
1103                    // Eliminated by `mem_finalize()` above.
1104                    &AMode::SPOffset { .. }
1105                    | &AMode::FPOffset { .. }
1106                    | &AMode::IncomingArg { .. }
1107                    | &AMode::SlotOffset { .. }
1108                    | &AMode::Const { .. }
1109                    | &AMode::RegOffset { .. } => {
1110                        panic!("Should not see {mem:?} here!")
1111                    }
1112                }
1113            }
1114
1115            &Inst::Store8 { rd, ref mem, flags }
1116            | &Inst::Store16 { rd, ref mem, flags }
1117            | &Inst::Store32 { rd, ref mem, flags }
1118            | &Inst::Store64 { rd, ref mem, flags }
1119            | &Inst::FpuStore16 { rd, ref mem, flags }
1120            | &Inst::FpuStore32 { rd, ref mem, flags }
1121            | &Inst::FpuStore64 { rd, ref mem, flags }
1122            | &Inst::FpuStore128 { rd, ref mem, flags } => {
1123                let mem = mem.clone();
1124                let access_ty = self.mem_type().unwrap();
1125                let (mem_insts, mem) = mem_finalize(Some(sink), &mem, access_ty, state);
1126
1127                for inst in mem_insts.into_iter() {
1128                    inst.emit(sink, emit_info, state);
1129                }
1130
1131                let op = match self {
1132                    Inst::Store8 { .. } => 0b0011100000,
1133                    Inst::Store16 { .. } => 0b0111100000,
1134                    Inst::Store32 { .. } => 0b1011100000,
1135                    Inst::Store64 { .. } => 0b1111100000,
1136                    Inst::FpuStore16 { .. } => 0b0111110000,
1137                    Inst::FpuStore32 { .. } => 0b1011110000,
1138                    Inst::FpuStore64 { .. } => 0b1111110000,
1139                    Inst::FpuStore128 { .. } => 0b0011110010,
1140                    _ => unreachable!(),
1141                };
1142
1143                if let Some(trap_code) = flags.trap_code() {
1144                    // Register the offset at which the actual store instruction starts.
1145                    sink.add_trap(trap_code);
1146                }
1147
1148                match &mem {
1149                    &AMode::Unscaled { rn, simm9 } => {
1150                        let reg = rn;
1151                        sink.put4(enc_ldst_simm9(op, simm9, 0b00, reg, rd));
1152                    }
1153                    &AMode::UnsignedOffset { rn, uimm12 } => {
1154                        let reg = rn;
1155                        sink.put4(enc_ldst_uimm12(op, uimm12, reg, rd));
1156                    }
1157                    &AMode::RegReg { rn, rm } => {
1158                        let r1 = rn;
1159                        let r2 = rm;
1160                        sink.put4(enc_ldst_reg(
1161                            op, r1, r2, /* scaled = */ false, /* extendop = */ None, rd,
1162                        ));
1163                    }
1164                    &AMode::RegScaled { rn, rm } | &AMode::RegScaledExtended { rn, rm, .. } => {
1165                        let r1 = rn;
1166                        let r2 = rm;
1167                        let extendop = match &mem {
1168                            &AMode::RegScaled { .. } => None,
1169                            &AMode::RegScaledExtended { extendop, .. } => Some(extendop),
1170                            _ => unreachable!(),
1171                        };
1172                        sink.put4(enc_ldst_reg(
1173                            op, r1, r2, /* scaled = */ true, extendop, rd,
1174                        ));
1175                    }
1176                    &AMode::RegExtended { rn, rm, extendop } => {
1177                        let r1 = rn;
1178                        let r2 = rm;
1179                        sink.put4(enc_ldst_reg(
1180                            op,
1181                            r1,
1182                            r2,
1183                            /* scaled = */ false,
1184                            Some(extendop),
1185                            rd,
1186                        ));
1187                    }
1188                    &AMode::Label { .. } => {
1189                        panic!("Store to a MemLabel not implemented!");
1190                    }
1191                    &AMode::SPPreIndexed { simm9 } => {
1192                        let reg = stack_reg();
1193                        sink.put4(enc_ldst_simm9(op, simm9, 0b11, reg, rd));
1194                    }
1195                    &AMode::SPPostIndexed { simm9 } => {
1196                        let reg = stack_reg();
1197                        sink.put4(enc_ldst_simm9(op, simm9, 0b01, reg, rd));
1198                    }
1199                    // Eliminated by `mem_finalize()` above.
1200                    &AMode::SPOffset { .. }
1201                    | &AMode::FPOffset { .. }
1202                    | &AMode::IncomingArg { .. }
1203                    | &AMode::SlotOffset { .. }
1204                    | &AMode::Const { .. }
1205                    | &AMode::RegOffset { .. } => {
1206                        panic!("Should not see {mem:?} here!")
1207                    }
1208                }
1209            }
1210
1211            &Inst::StoreP64 {
1212                rt,
1213                rt2,
1214                ref mem,
1215                flags,
1216            } => {
1217                let mem = mem.clone();
1218                if let Some(trap_code) = flags.trap_code() {
1219                    // Register the offset at which the actual store instruction starts.
1220                    sink.add_trap(trap_code);
1221                }
1222                match &mem {
1223                    &PairAMode::SignedOffset { reg, simm7 } => {
1224                        assert_eq!(simm7.scale_ty, I64);
1225                        sink.put4(enc_ldst_pair(0b1010100100, simm7, reg, rt, rt2));
1226                    }
1227                    &PairAMode::SPPreIndexed { simm7 } => {
1228                        assert_eq!(simm7.scale_ty, I64);
1229                        let reg = stack_reg();
1230                        sink.put4(enc_ldst_pair(0b1010100110, simm7, reg, rt, rt2));
1231                    }
1232                    &PairAMode::SPPostIndexed { simm7 } => {
1233                        assert_eq!(simm7.scale_ty, I64);
1234                        let reg = stack_reg();
1235                        sink.put4(enc_ldst_pair(0b1010100010, simm7, reg, rt, rt2));
1236                    }
1237                }
1238            }
1239            &Inst::LoadP64 {
1240                rt,
1241                rt2,
1242                ref mem,
1243                flags,
1244            } => {
1245                let rt = rt.to_reg();
1246                let rt2 = rt2.to_reg();
1247                let mem = mem.clone();
1248                if let Some(trap_code) = flags.trap_code() {
1249                    // Register the offset at which the actual load instruction starts.
1250                    sink.add_trap(trap_code);
1251                }
1252
1253                match &mem {
1254                    &PairAMode::SignedOffset { reg, simm7 } => {
1255                        assert_eq!(simm7.scale_ty, I64);
1256                        sink.put4(enc_ldst_pair(0b1010100101, simm7, reg, rt, rt2));
1257                    }
1258                    &PairAMode::SPPreIndexed { simm7 } => {
1259                        assert_eq!(simm7.scale_ty, I64);
1260                        let reg = stack_reg();
1261                        sink.put4(enc_ldst_pair(0b1010100111, simm7, reg, rt, rt2));
1262                    }
1263                    &PairAMode::SPPostIndexed { simm7 } => {
1264                        assert_eq!(simm7.scale_ty, I64);
1265                        let reg = stack_reg();
1266                        sink.put4(enc_ldst_pair(0b1010100011, simm7, reg, rt, rt2));
1267                    }
1268                }
1269            }
1270            &Inst::FpuLoadP64 {
1271                rt,
1272                rt2,
1273                ref mem,
1274                flags,
1275            }
1276            | &Inst::FpuLoadP128 {
1277                rt,
1278                rt2,
1279                ref mem,
1280                flags,
1281            } => {
1282                let rt = rt.to_reg();
1283                let rt2 = rt2.to_reg();
1284                let mem = mem.clone();
1285
1286                if let Some(trap_code) = flags.trap_code() {
1287                    // Register the offset at which the actual load instruction starts.
1288                    sink.add_trap(trap_code);
1289                }
1290
1291                let opc = match self {
1292                    &Inst::FpuLoadP64 { .. } => 0b01,
1293                    &Inst::FpuLoadP128 { .. } => 0b10,
1294                    _ => unreachable!(),
1295                };
1296
1297                match &mem {
1298                    &PairAMode::SignedOffset { reg, simm7 } => {
1299                        assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16);
1300                        sink.put4(enc_ldst_vec_pair(opc, 0b10, true, simm7, reg, rt, rt2));
1301                    }
1302                    &PairAMode::SPPreIndexed { simm7 } => {
1303                        assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16);
1304                        let reg = stack_reg();
1305                        sink.put4(enc_ldst_vec_pair(opc, 0b11, true, simm7, reg, rt, rt2));
1306                    }
1307                    &PairAMode::SPPostIndexed { simm7 } => {
1308                        assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16);
1309                        let reg = stack_reg();
1310                        sink.put4(enc_ldst_vec_pair(opc, 0b01, true, simm7, reg, rt, rt2));
1311                    }
1312                }
1313            }
1314            &Inst::FpuStoreP64 {
1315                rt,
1316                rt2,
1317                ref mem,
1318                flags,
1319            }
1320            | &Inst::FpuStoreP128 {
1321                rt,
1322                rt2,
1323                ref mem,
1324                flags,
1325            } => {
1326                let mem = mem.clone();
1327
1328                if let Some(trap_code) = flags.trap_code() {
1329                    // Register the offset at which the actual store instruction starts.
1330                    sink.add_trap(trap_code);
1331                }
1332
1333                let opc = match self {
1334                    &Inst::FpuStoreP64 { .. } => 0b01,
1335                    &Inst::FpuStoreP128 { .. } => 0b10,
1336                    _ => unreachable!(),
1337                };
1338
1339                match &mem {
1340                    &PairAMode::SignedOffset { reg, simm7 } => {
1341                        assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16);
1342                        sink.put4(enc_ldst_vec_pair(opc, 0b10, false, simm7, reg, rt, rt2));
1343                    }
1344                    &PairAMode::SPPreIndexed { simm7 } => {
1345                        assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16);
1346                        let reg = stack_reg();
1347                        sink.put4(enc_ldst_vec_pair(opc, 0b11, false, simm7, reg, rt, rt2));
1348                    }
1349                    &PairAMode::SPPostIndexed { simm7 } => {
1350                        assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16);
1351                        let reg = stack_reg();
1352                        sink.put4(enc_ldst_vec_pair(opc, 0b01, false, simm7, reg, rt, rt2));
1353                    }
1354                }
1355            }
1356            &Inst::Mov { size, rd, rm } => {
1357                assert!(rd.to_reg().class() == rm.class());
1358                assert!(rm.class() == RegClass::Int);
1359
1360                match size {
1361                    OperandSize::Size64 => {
1362                        // MOV to SP is interpreted as MOV to XZR instead. And our codegen
1363                        // should never MOV to XZR.
1364                        assert!(rd.to_reg() != stack_reg());
1365
1366                        if rm == stack_reg() {
1367                            // We can't use ORR here, so use an `add rd, sp, #0` instead.
1368                            let imm12 = Imm12::maybe_from_u64(0).unwrap();
1369                            sink.put4(enc_arith_rr_imm12(
1370                                0b100_10001,
1371                                imm12.shift_bits(),
1372                                imm12.imm_bits(),
1373                                rm,
1374                                rd,
1375                            ));
1376                        } else {
1377                            // Encoded as ORR rd, rm, zero.
1378                            sink.put4(enc_arith_rrr(0b10101010_000, 0b000_000, rd, zero_reg(), rm));
1379                        }
1380                    }
1381                    OperandSize::Size32 => {
1382                        // MOV to SP is interpreted as MOV to XZR instead. And our codegen
1383                        // should never MOV to XZR.
1384                        assert!(machreg_to_gpr(rd.to_reg()) != 31);
1385                        // Encoded as ORR rd, rm, zero.
1386                        sink.put4(enc_arith_rrr(0b00101010_000, 0b000_000, rd, zero_reg(), rm));
1387                    }
1388                }
1389            }
1390            &Inst::MovFromPReg { rd, rm } => {
1391                let rm: Reg = rm.into();
1392                debug_assert!(
1393                    [
1394                        regs::fp_reg(),
1395                        regs::stack_reg(),
1396                        regs::link_reg(),
1397                        regs::pinned_reg()
1398                    ]
1399                    .contains(&rm)
1400                );
1401                assert!(rm.class() == RegClass::Int);
1402                assert!(rd.to_reg().class() == rm.class());
1403                let size = OperandSize::Size64;
1404                Inst::Mov { size, rd, rm }.emit(sink, emit_info, state);
1405            }
1406            &Inst::MovToPReg { rd, rm } => {
1407                let rd: Writable<Reg> = Writable::from_reg(rd.into());
1408                debug_assert!(
1409                    [
1410                        regs::fp_reg(),
1411                        regs::stack_reg(),
1412                        regs::link_reg(),
1413                        regs::pinned_reg()
1414                    ]
1415                    .contains(&rd.to_reg())
1416                );
1417                assert!(rd.to_reg().class() == RegClass::Int);
1418                assert!(rm.class() == rd.to_reg().class());
1419                let size = OperandSize::Size64;
1420                Inst::Mov { size, rd, rm }.emit(sink, emit_info, state);
1421            }
1422            &Inst::MovWide { op, rd, imm, size } => {
1423                sink.put4(enc_move_wide(op, rd, imm, size));
1424            }
1425            &Inst::MovK { rd, rn, imm, size } => {
1426                debug_assert_eq!(rn, rd.to_reg());
1427                sink.put4(enc_movk(rd, imm, size));
1428            }
1429            &Inst::CSel { rd, rn, rm, cond } => {
1430                sink.put4(enc_csel(rd, rn, rm, cond, 0, 0));
1431            }
1432            &Inst::CSNeg { rd, rn, rm, cond } => {
1433                sink.put4(enc_csel(rd, rn, rm, cond, 1, 1));
1434            }
1435            &Inst::CSet { rd, cond } => {
1436                sink.put4(enc_csel(rd, zero_reg(), zero_reg(), cond.invert(), 0, 1));
1437            }
1438            &Inst::CSetm { rd, cond } => {
1439                sink.put4(enc_csel(rd, zero_reg(), zero_reg(), cond.invert(), 1, 0));
1440            }
1441            &Inst::CCmp {
1442                size,
1443                rn,
1444                rm,
1445                nzcv,
1446                cond,
1447            } => {
1448                sink.put4(enc_ccmp(size, rn, rm, nzcv, cond));
1449            }
1450            &Inst::CCmpImm {
1451                size,
1452                rn,
1453                imm,
1454                nzcv,
1455                cond,
1456            } => {
1457                sink.put4(enc_ccmp_imm(size, rn, imm, nzcv, cond));
1458            }
1459            &Inst::AtomicRMW {
1460                ty,
1461                op,
1462                rs,
1463                rt,
1464                rn,
1465                flags,
1466            } => {
1467                if let Some(trap_code) = flags.trap_code() {
1468                    sink.add_trap(trap_code);
1469                }
1470
1471                sink.put4(enc_acq_rel(ty, op, rs, rt, rn));
1472            }
1473            &Inst::AtomicRMWLoop { ty, op, flags, .. } => {
1474                /* Emit this:
1475                     again:
1476                      ldaxr{,b,h}  x/w27, [x25]
1477                      // maybe sign extend
1478                      op          x28, x27, x26 // op is add,sub,and,orr,eor
1479                      stlxr{,b,h}  w24, x/w28, [x25]
1480                      cbnz        x24, again
1481
1482                   Operand conventions:
1483                      IN:  x25 (addr), x26 (2nd arg for op)
1484                      OUT: x27 (old value), x24 (trashed), x28 (trashed)
1485
1486                   It is unfortunate that, per the ARM documentation, x28 cannot be used for
1487                   both the store-data and success-flag operands of stlxr.  This causes the
1488                   instruction's behaviour to be "CONSTRAINED UNPREDICTABLE", so we use x24
1489                   instead for the success-flag.
1490                */
1491                // TODO: We should not hardcode registers here, a better idea would be to
1492                // pass some scratch registers in the AtomicRMWLoop pseudo-instruction, and use those
1493                let xzr = zero_reg();
1494                let x24 = xreg(24);
1495                let x25 = xreg(25);
1496                let x26 = xreg(26);
1497                let x27 = xreg(27);
1498                let x28 = xreg(28);
1499                let x24wr = writable_xreg(24);
1500                let x27wr = writable_xreg(27);
1501                let x28wr = writable_xreg(28);
1502                let again_label = sink.get_label();
1503
1504                // again:
1505                sink.bind_label(again_label, &mut state.ctrl_plane);
1506
1507                if let Some(trap_code) = flags.trap_code() {
1508                    sink.add_trap(trap_code);
1509                }
1510
1511                sink.put4(enc_ldaxr(ty, x27wr, x25)); // ldaxr x27, [x25]
1512                let size = OperandSize::from_ty(ty);
1513                let sign_ext = match op {
1514                    AtomicRMWLoopOp::Smin | AtomicRMWLoopOp::Smax => match ty {
1515                        I16 => Some((ExtendOp::SXTH, 16)),
1516                        I8 => Some((ExtendOp::SXTB, 8)),
1517                        _ => None,
1518                    },
1519                    _ => None,
1520                };
1521                let zero_ext = match op {
1522                    AtomicRMWLoopOp::Umin | AtomicRMWLoopOp::Umax => match ty {
1523                        I16 => Some(ExtendOp::UXTH),
1524                        I8 => Some(ExtendOp::UXTB),
1525                        _ => None,
1526                    },
1527                    _ => None,
1528                };
1529                // sxt{b|h} the loaded result if necessary.
1530                if sign_ext.is_some() {
1531                    let (_, from_bits) = sign_ext.unwrap();
1532                    Inst::Extend {
1533                        rd: x27wr,
1534                        rn: x27,
1535                        signed: true,
1536                        from_bits,
1537                        to_bits: size.bits(),
1538                    }
1539                    .emit(sink, emit_info, state);
1540                }
1541
1542                match op {
1543                    AtomicRMWLoopOp::Xchg => {} // do nothing
1544                    AtomicRMWLoopOp::Nand => {
1545                        // and x28, x27, x26
1546                        // mvn x28, x28
1547
1548                        Inst::AluRRR {
1549                            alu_op: ALUOp::And,
1550                            size,
1551                            rd: x28wr,
1552                            rn: x27,
1553                            rm: x26,
1554                        }
1555                        .emit(sink, emit_info, state);
1556
1557                        Inst::AluRRR {
1558                            alu_op: ALUOp::OrrNot,
1559                            size,
1560                            rd: x28wr,
1561                            rn: xzr,
1562                            rm: x28,
1563                        }
1564                        .emit(sink, emit_info, state);
1565                    }
1566                    AtomicRMWLoopOp::Umin
1567                    | AtomicRMWLoopOp::Umax
1568                    | AtomicRMWLoopOp::Smin
1569                    | AtomicRMWLoopOp::Smax => {
1570                        // cmp x27, x26 {?sxt}
1571                        // csel.op x28, x27, x26
1572
1573                        let cond = match op {
1574                            AtomicRMWLoopOp::Umin => Cond::Lo,
1575                            AtomicRMWLoopOp::Umax => Cond::Hi,
1576                            AtomicRMWLoopOp::Smin => Cond::Lt,
1577                            AtomicRMWLoopOp::Smax => Cond::Gt,
1578                            _ => unreachable!(),
1579                        };
1580
1581                        if let Some(extendop) = sign_ext.map(|(op, _)| op).or(zero_ext) {
1582                            Inst::AluRRRExtend {
1583                                alu_op: ALUOp::SubS,
1584                                size,
1585                                rd: writable_zero_reg(),
1586                                rn: x27,
1587                                rm: x26,
1588                                extendop,
1589                            }
1590                            .emit(sink, emit_info, state);
1591                        } else {
1592                            Inst::AluRRR {
1593                                alu_op: ALUOp::SubS,
1594                                size,
1595                                rd: writable_zero_reg(),
1596                                rn: x27,
1597                                rm: x26,
1598                            }
1599                            .emit(sink, emit_info, state);
1600                        }
1601
1602                        Inst::CSel {
1603                            cond,
1604                            rd: x28wr,
1605                            rn: x27,
1606                            rm: x26,
1607                        }
1608                        .emit(sink, emit_info, state);
1609                    }
1610                    _ => {
1611                        // add/sub/and/orr/eor x28, x27, x26
1612                        let alu_op = match op {
1613                            AtomicRMWLoopOp::Add => ALUOp::Add,
1614                            AtomicRMWLoopOp::Sub => ALUOp::Sub,
1615                            AtomicRMWLoopOp::And => ALUOp::And,
1616                            AtomicRMWLoopOp::Orr => ALUOp::Orr,
1617                            AtomicRMWLoopOp::Eor => ALUOp::Eor,
1618                            AtomicRMWLoopOp::Nand
1619                            | AtomicRMWLoopOp::Umin
1620                            | AtomicRMWLoopOp::Umax
1621                            | AtomicRMWLoopOp::Smin
1622                            | AtomicRMWLoopOp::Smax
1623                            | AtomicRMWLoopOp::Xchg => unreachable!(),
1624                        };
1625
1626                        Inst::AluRRR {
1627                            alu_op,
1628                            size,
1629                            rd: x28wr,
1630                            rn: x27,
1631                            rm: x26,
1632                        }
1633                        .emit(sink, emit_info, state);
1634                    }
1635                }
1636
1637                if let Some(trap_code) = flags.trap_code() {
1638                    sink.add_trap(trap_code);
1639                }
1640                if op == AtomicRMWLoopOp::Xchg {
1641                    sink.put4(enc_stlxr(ty, x24wr, x26, x25)); // stlxr w24, x26, [x25]
1642                } else {
1643                    sink.put4(enc_stlxr(ty, x24wr, x28, x25)); // stlxr w24, x28, [x25]
1644                }
1645
1646                // cbnz w24, again
1647                // Note, we're actually testing x24, and relying on the default zero-high-half
1648                // rule in the assignment that `stlxr` does.
1649                let br_offset = sink.cur_offset();
1650                sink.put4(enc_conditional_br(
1651                    BranchTarget::Label(again_label),
1652                    CondBrKind::NotZero(x24, OperandSize::Size64),
1653                ));
1654                sink.use_label_at_offset(br_offset, again_label, LabelUse::Branch19);
1655            }
1656            &Inst::AtomicCAS {
1657                rd,
1658                rs,
1659                rt,
1660                rn,
1661                ty,
1662                flags,
1663            } => {
1664                debug_assert_eq!(rd.to_reg(), rs);
1665                let size = match ty {
1666                    I8 => 0b00,
1667                    I16 => 0b01,
1668                    I32 => 0b10,
1669                    I64 => 0b11,
1670                    _ => panic!("Unsupported type: {ty}"),
1671                };
1672
1673                if let Some(trap_code) = flags.trap_code() {
1674                    sink.add_trap(trap_code);
1675                }
1676
1677                sink.put4(enc_cas(size, rd, rt, rn));
1678            }
1679            Inst::AtomicCAS128 { args } => {
1680                let &AtomicCAS128Args {
1681                    rd_lo,
1682                    rd_hi,
1683                    rs_lo,
1684                    rs_hi,
1685                    rt_lo,
1686                    rt_hi,
1687                    rn,
1688                    flags,
1689                } = &**args;
1690                debug_assert_eq!(rd_lo.to_reg(), rs_lo);
1691                debug_assert_eq!(rd_hi.to_reg(), rs_hi);
1692
1693                // These should be pinned to pairs that `casp` requires.
1694                debug_assert_eq!(rs_hi, xreg(machreg_to_gpr(rs_lo) as u8 + 1));
1695                debug_assert_eq!(rt_hi, xreg(machreg_to_gpr(rt_lo) as u8 + 1));
1696
1697                if let Some(trap_code) = flags.trap_code() {
1698                    sink.add_trap(trap_code);
1699                }
1700
1701                sink.put4(enc_casp(rd_lo, rt_lo, rn));
1702            }
1703            &Inst::AtomicCASLoop { ty, flags, .. } => {
1704                /* Emit this:
1705                    again:
1706                     ldaxr{,b,h} x/w27, [x25]
1707                     cmp         x27, x/w26 uxt{b,h}
1708                     b.ne        out
1709                     stlxr{,b,h} w24, x/w28, [x25]
1710                     cbnz        x24, again
1711                    out:
1712
1713                  Operand conventions:
1714                     IN:  x25 (addr), x26 (expected value), x28 (replacement value)
1715                     OUT: x27 (old value), x24 (trashed)
1716                */
1717                let x24 = xreg(24);
1718                let x25 = xreg(25);
1719                let x26 = xreg(26);
1720                let x27 = xreg(27);
1721                let x28 = xreg(28);
1722                let xzrwr = writable_zero_reg();
1723                let x24wr = writable_xreg(24);
1724                let x27wr = writable_xreg(27);
1725                let again_label = sink.get_label();
1726                let out_label = sink.get_label();
1727
1728                // again:
1729                sink.bind_label(again_label, &mut state.ctrl_plane);
1730
1731                if let Some(trap_code) = flags.trap_code() {
1732                    sink.add_trap(trap_code);
1733                }
1734
1735                // ldaxr x27, [x25]
1736                sink.put4(enc_ldaxr(ty, x27wr, x25));
1737
1738                // The top 32-bits are zero-extended by the ldaxr so we don't
1739                // have to use UXTW, just the x-form of the register.
1740                let (bit21, extend_op) = match ty {
1741                    I8 => (0b1, 0b000000),
1742                    I16 => (0b1, 0b001000),
1743                    _ => (0b0, 0b000000),
1744                };
1745                let bits_31_21 = 0b111_01011_000 | bit21;
1746                // cmp x27, x26 (== subs xzr, x27, x26)
1747                sink.put4(enc_arith_rrr(bits_31_21, extend_op, xzrwr, x27, x26));
1748
1749                // b.ne out
1750                let br_out_offset = sink.cur_offset();
1751                sink.put4(enc_conditional_br(
1752                    BranchTarget::Label(out_label),
1753                    CondBrKind::Cond(Cond::Ne),
1754                ));
1755                sink.use_label_at_offset(br_out_offset, out_label, LabelUse::Branch19);
1756
1757                if let Some(trap_code) = flags.trap_code() {
1758                    sink.add_trap(trap_code);
1759                }
1760
1761                sink.put4(enc_stlxr(ty, x24wr, x28, x25)); // stlxr w24, x28, [x25]
1762
1763                // cbnz w24, again.
1764                // Note, we're actually testing x24, and relying on the default zero-high-half
1765                // rule in the assignment that `stlxr` does.
1766                let br_again_offset = sink.cur_offset();
1767                sink.put4(enc_conditional_br(
1768                    BranchTarget::Label(again_label),
1769                    CondBrKind::NotZero(x24, OperandSize::Size64),
1770                ));
1771                sink.use_label_at_offset(br_again_offset, again_label, LabelUse::Branch19);
1772
1773                // out:
1774                sink.bind_label(out_label, &mut state.ctrl_plane);
1775            }
1776            &Inst::LoadAcquire {
1777                access_ty,
1778                rt,
1779                rn,
1780                flags,
1781            } => {
1782                if let Some(trap_code) = flags.trap_code() {
1783                    sink.add_trap(trap_code);
1784                }
1785
1786                sink.put4(enc_ldar(access_ty, rt, rn));
1787            }
1788            &Inst::StoreRelease {
1789                access_ty,
1790                rt,
1791                rn,
1792                flags,
1793            } => {
1794                if let Some(trap_code) = flags.trap_code() {
1795                    sink.add_trap(trap_code);
1796                }
1797
1798                sink.put4(enc_stlr(access_ty, rt, rn));
1799            }
1800            &Inst::Fence {} => {
1801                sink.put4(enc_dmb_ish()); // dmb ish
1802            }
1803            &Inst::Csdb {} => {
1804                sink.put4(0xd503229f);
1805            }
1806            &Inst::FpuMove32 { rd, rn } => {
1807                sink.put4(enc_fpurr(0b000_11110_00_1_000000_10000, rd, rn));
1808            }
1809            &Inst::FpuMove64 { rd, rn } => {
1810                sink.put4(enc_fpurr(0b000_11110_01_1_000000_10000, rd, rn));
1811            }
1812            &Inst::FpuMove128 { rd, rn } => {
1813                sink.put4(enc_vecmov(/* 16b = */ true, rd, rn));
1814            }
1815            &Inst::FpuMoveFromVec { rd, rn, idx, size } => {
1816                let (imm5, shift, mask) = match size.lane_size() {
1817                    ScalarSize::Size32 => (0b00100, 3, 0b011),
1818                    ScalarSize::Size64 => (0b01000, 4, 0b001),
1819                    _ => unimplemented!(),
1820                };
1821                debug_assert_eq!(idx & mask, idx);
1822                let imm5 = imm5 | ((idx as u32) << shift);
1823                sink.put4(
1824                    0b010_11110000_00000_000001_00000_00000
1825                        | (imm5 << 16)
1826                        | (machreg_to_vec(rn) << 5)
1827                        | machreg_to_vec(rd.to_reg()),
1828                );
1829            }
1830            &Inst::FpuExtend { rd, rn, size } => {
1831                sink.put4(enc_fpurr(
1832                    0b000_11110_00_1_000000_10000 | (size.ftype() << 12),
1833                    rd,
1834                    rn,
1835                ));
1836            }
1837            &Inst::FpuRR {
1838                fpu_op,
1839                size,
1840                rd,
1841                rn,
1842            } => {
1843                let top22 = match fpu_op {
1844                    FPUOp1::Abs => 0b000_11110_00_1_000001_10000,
1845                    FPUOp1::Neg => 0b000_11110_00_1_000010_10000,
1846                    FPUOp1::Sqrt => 0b000_11110_00_1_000011_10000,
1847                    FPUOp1::Cvt32To64 => {
1848                        debug_assert_eq!(size, ScalarSize::Size32);
1849                        0b000_11110_00_1_000101_10000
1850                    }
1851                    FPUOp1::Cvt64To32 => {
1852                        debug_assert_eq!(size, ScalarSize::Size64);
1853                        0b000_11110_01_1_000100_10000
1854                    }
1855                };
1856                let top22 = top22 | size.ftype() << 12;
1857                sink.put4(enc_fpurr(top22, rd, rn));
1858            }
1859            &Inst::FpuRRR {
1860                fpu_op,
1861                size,
1862                rd,
1863                rn,
1864                rm,
1865            } => {
1866                let top22 = match fpu_op {
1867                    FPUOp2::Add => 0b000_11110_00_1_00000_001010,
1868                    FPUOp2::Sub => 0b000_11110_00_1_00000_001110,
1869                    FPUOp2::Mul => 0b000_11110_00_1_00000_000010,
1870                    FPUOp2::Div => 0b000_11110_00_1_00000_000110,
1871                    FPUOp2::Max => 0b000_11110_00_1_00000_010010,
1872                    FPUOp2::Min => 0b000_11110_00_1_00000_010110,
1873                };
1874                let top22 = top22 | size.ftype() << 12;
1875                sink.put4(enc_fpurrr(top22, rd, rn, rm));
1876            }
1877            &Inst::FpuRRI { fpu_op, rd, rn } => match fpu_op {
1878                FPUOpRI::UShr32(imm) => {
1879                    debug_assert_eq!(32, imm.lane_size_in_bits);
1880                    sink.put4(
1881                        0b0_0_1_011110_0000000_00_0_0_0_1_00000_00000
1882                            | imm.enc() << 16
1883                            | machreg_to_vec(rn) << 5
1884                            | machreg_to_vec(rd.to_reg()),
1885                    )
1886                }
1887                FPUOpRI::UShr64(imm) => {
1888                    debug_assert_eq!(64, imm.lane_size_in_bits);
1889                    sink.put4(
1890                        0b01_1_111110_0000000_00_0_0_0_1_00000_00000
1891                            | imm.enc() << 16
1892                            | machreg_to_vec(rn) << 5
1893                            | machreg_to_vec(rd.to_reg()),
1894                    )
1895                }
1896            },
1897            &Inst::FpuRRIMod { fpu_op, rd, ri, rn } => {
1898                debug_assert_eq!(rd.to_reg(), ri);
1899                match fpu_op {
1900                    FPUOpRIMod::Sli64(imm) => {
1901                        debug_assert_eq!(64, imm.lane_size_in_bits);
1902                        sink.put4(
1903                            0b01_1_111110_0000000_010101_00000_00000
1904                                | imm.enc() << 16
1905                                | machreg_to_vec(rn) << 5
1906                                | machreg_to_vec(rd.to_reg()),
1907                        )
1908                    }
1909                    FPUOpRIMod::Sli32(imm) => {
1910                        debug_assert_eq!(32, imm.lane_size_in_bits);
1911                        sink.put4(
1912                            0b0_0_1_011110_0000000_010101_00000_00000
1913                                | imm.enc() << 16
1914                                | machreg_to_vec(rn) << 5
1915                                | machreg_to_vec(rd.to_reg()),
1916                        )
1917                    }
1918                }
1919            }
1920            &Inst::FpuRRRR {
1921                fpu_op,
1922                size,
1923                rd,
1924                rn,
1925                rm,
1926                ra,
1927            } => {
1928                let top17 = match fpu_op {
1929                    FPUOp3::MAdd => 0b000_11111_00_0_00000_0,
1930                    FPUOp3::MSub => 0b000_11111_00_0_00000_1,
1931                    FPUOp3::NMAdd => 0b000_11111_00_1_00000_0,
1932                    FPUOp3::NMSub => 0b000_11111_00_1_00000_1,
1933                };
1934                let top17 = top17 | size.ftype() << 7;
1935                sink.put4(enc_fpurrrr(top17, rd, rn, rm, ra));
1936            }
1937            &Inst::VecMisc { op, rd, rn, size } => {
1938                let (q, enc_size) = size.enc_size();
1939                let (u, bits_12_16, size) = match op {
1940                    VecMisc2::Not => (0b1, 0b00101, 0b00),
1941                    VecMisc2::Neg => (0b1, 0b01011, enc_size),
1942                    VecMisc2::Abs => (0b0, 0b01011, enc_size),
1943                    VecMisc2::Fabs => {
1944                        debug_assert!(
1945                            size == VectorSize::Size32x2
1946                                || size == VectorSize::Size32x4
1947                                || size == VectorSize::Size64x2
1948                        );
1949                        (0b0, 0b01111, enc_size)
1950                    }
1951                    VecMisc2::Fneg => {
1952                        debug_assert!(
1953                            size == VectorSize::Size32x2
1954                                || size == VectorSize::Size32x4
1955                                || size == VectorSize::Size64x2
1956                        );
1957                        (0b1, 0b01111, enc_size)
1958                    }
1959                    VecMisc2::Fsqrt => {
1960                        debug_assert!(
1961                            size == VectorSize::Size32x2
1962                                || size == VectorSize::Size32x4
1963                                || size == VectorSize::Size64x2
1964                        );
1965                        (0b1, 0b11111, enc_size)
1966                    }
1967                    VecMisc2::Rev16 => {
1968                        debug_assert_eq!(size, VectorSize::Size8x16);
1969                        (0b0, 0b00001, enc_size)
1970                    }
1971                    VecMisc2::Rev32 => {
1972                        debug_assert!(size == VectorSize::Size8x16 || size == VectorSize::Size16x8);
1973                        (0b1, 0b00000, enc_size)
1974                    }
1975                    VecMisc2::Rev64 => {
1976                        debug_assert!(
1977                            size == VectorSize::Size8x16
1978                                || size == VectorSize::Size16x8
1979                                || size == VectorSize::Size32x4
1980                        );
1981                        (0b0, 0b00000, enc_size)
1982                    }
1983                    VecMisc2::Fcvtzs => {
1984                        debug_assert!(
1985                            size == VectorSize::Size32x2
1986                                || size == VectorSize::Size32x4
1987                                || size == VectorSize::Size64x2
1988                        );
1989                        (0b0, 0b11011, enc_size)
1990                    }
1991                    VecMisc2::Fcvtzu => {
1992                        debug_assert!(
1993                            size == VectorSize::Size32x2
1994                                || size == VectorSize::Size32x4
1995                                || size == VectorSize::Size64x2
1996                        );
1997                        (0b1, 0b11011, enc_size)
1998                    }
1999                    VecMisc2::Scvtf => {
2000                        debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
2001                        (0b0, 0b11101, enc_size & 0b1)
2002                    }
2003                    VecMisc2::Ucvtf => {
2004                        debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
2005                        (0b1, 0b11101, enc_size & 0b1)
2006                    }
2007                    VecMisc2::Frintn => {
2008                        debug_assert!(
2009                            size == VectorSize::Size32x2
2010                                || size == VectorSize::Size32x4
2011                                || size == VectorSize::Size64x2
2012                        );
2013                        (0b0, 0b11000, enc_size & 0b01)
2014                    }
2015                    VecMisc2::Frintz => {
2016                        debug_assert!(
2017                            size == VectorSize::Size32x2
2018                                || size == VectorSize::Size32x4
2019                                || size == VectorSize::Size64x2
2020                        );
2021                        (0b0, 0b11001, enc_size)
2022                    }
2023                    VecMisc2::Frintm => {
2024                        debug_assert!(
2025                            size == VectorSize::Size32x2
2026                                || size == VectorSize::Size32x4
2027                                || size == VectorSize::Size64x2
2028                        );
2029                        (0b0, 0b11001, enc_size & 0b01)
2030                    }
2031                    VecMisc2::Frintp => {
2032                        debug_assert!(
2033                            size == VectorSize::Size32x2
2034                                || size == VectorSize::Size32x4
2035                                || size == VectorSize::Size64x2
2036                        );
2037                        (0b0, 0b11000, enc_size)
2038                    }
2039                    VecMisc2::Cnt => {
2040                        debug_assert!(size == VectorSize::Size8x8 || size == VectorSize::Size8x16);
2041                        (0b0, 0b00101, enc_size)
2042                    }
2043                    VecMisc2::Cmeq0 => (0b0, 0b01001, enc_size),
2044                    VecMisc2::Cmge0 => (0b1, 0b01000, enc_size),
2045                    VecMisc2::Cmgt0 => (0b0, 0b01000, enc_size),
2046                    VecMisc2::Cmle0 => (0b1, 0b01001, enc_size),
2047                    VecMisc2::Cmlt0 => (0b0, 0b01010, enc_size),
2048                    VecMisc2::Fcmeq0 => {
2049                        debug_assert!(
2050                            size == VectorSize::Size32x2
2051                                || size == VectorSize::Size32x4
2052                                || size == VectorSize::Size64x2
2053                        );
2054                        (0b0, 0b01101, enc_size)
2055                    }
2056                    VecMisc2::Fcmge0 => {
2057                        debug_assert!(
2058                            size == VectorSize::Size32x2
2059                                || size == VectorSize::Size32x4
2060                                || size == VectorSize::Size64x2
2061                        );
2062                        (0b1, 0b01100, enc_size)
2063                    }
2064                    VecMisc2::Fcmgt0 => {
2065                        debug_assert!(
2066                            size == VectorSize::Size32x2
2067                                || size == VectorSize::Size32x4
2068                                || size == VectorSize::Size64x2
2069                        );
2070                        (0b0, 0b01100, enc_size)
2071                    }
2072                    VecMisc2::Fcmle0 => {
2073                        debug_assert!(
2074                            size == VectorSize::Size32x2
2075                                || size == VectorSize::Size32x4
2076                                || size == VectorSize::Size64x2
2077                        );
2078                        (0b1, 0b01101, enc_size)
2079                    }
2080                    VecMisc2::Fcmlt0 => {
2081                        debug_assert!(
2082                            size == VectorSize::Size32x2
2083                                || size == VectorSize::Size32x4
2084                                || size == VectorSize::Size64x2
2085                        );
2086                        (0b0, 0b01110, enc_size)
2087                    }
2088                };
2089                sink.put4(enc_vec_rr_misc((q << 1) | u, size, bits_12_16, rd, rn));
2090            }
2091            &Inst::VecLanes { op, rd, rn, size } => {
2092                let (q, size) = match size {
2093                    VectorSize::Size8x8 => (0b0, 0b00),
2094                    VectorSize::Size8x16 => (0b1, 0b00),
2095                    VectorSize::Size16x4 => (0b0, 0b01),
2096                    VectorSize::Size16x8 => (0b1, 0b01),
2097                    VectorSize::Size32x4 => (0b1, 0b10),
2098                    _ => unreachable!(),
2099                };
2100                let (u, opcode) = match op {
2101                    VecLanesOp::Uminv => (0b1, 0b11010),
2102                    VecLanesOp::Addv => (0b0, 0b11011),
2103                };
2104                sink.put4(enc_vec_lanes(q, u, size, opcode, rd, rn));
2105            }
2106            &Inst::VecShiftImm {
2107                op,
2108                rd,
2109                rn,
2110                size,
2111                imm,
2112            } => {
2113                let (is_shr, mut template) = match op {
2114                    VecShiftImmOp::Ushr => (true, 0b_001_011110_0000_000_000001_00000_00000_u32),
2115                    VecShiftImmOp::Sshr => (true, 0b_000_011110_0000_000_000001_00000_00000_u32),
2116                    VecShiftImmOp::Shl => (false, 0b_000_011110_0000_000_010101_00000_00000_u32),
2117                };
2118                if size.is_128bits() {
2119                    template |= 0b1 << 30;
2120                }
2121                let imm = imm as u32;
2122                // Deal with the somewhat strange encoding scheme for, and limits on,
2123                // the shift amount.
2124                let immh_immb = match (size.lane_size(), is_shr) {
2125                    (ScalarSize::Size64, true) if imm >= 1 && imm <= 64 => {
2126                        0b_1000_000_u32 | (64 - imm)
2127                    }
2128                    (ScalarSize::Size32, true) if imm >= 1 && imm <= 32 => {
2129                        0b_0100_000_u32 | (32 - imm)
2130                    }
2131                    (ScalarSize::Size16, true) if imm >= 1 && imm <= 16 => {
2132                        0b_0010_000_u32 | (16 - imm)
2133                    }
2134                    (ScalarSize::Size8, true) if imm >= 1 && imm <= 8 => {
2135                        0b_0001_000_u32 | (8 - imm)
2136                    }
2137                    (ScalarSize::Size64, false) if imm <= 63 => 0b_1000_000_u32 | imm,
2138                    (ScalarSize::Size32, false) if imm <= 31 => 0b_0100_000_u32 | imm,
2139                    (ScalarSize::Size16, false) if imm <= 15 => 0b_0010_000_u32 | imm,
2140                    (ScalarSize::Size8, false) if imm <= 7 => 0b_0001_000_u32 | imm,
2141                    _ => panic!(
2142                        "aarch64: Inst::VecShiftImm: emit: invalid op/size/imm {op:?}, {size:?}, {imm:?}"
2143                    ),
2144                };
2145                let rn_enc = machreg_to_vec(rn);
2146                let rd_enc = machreg_to_vec(rd.to_reg());
2147                sink.put4(template | (immh_immb << 16) | (rn_enc << 5) | rd_enc);
2148            }
2149            &Inst::VecShiftImmMod {
2150                op,
2151                rd,
2152                ri,
2153                rn,
2154                size,
2155                imm,
2156            } => {
2157                debug_assert_eq!(rd.to_reg(), ri);
2158                let (is_shr, mut template) = match op {
2159                    VecShiftImmModOp::Sli => (false, 0b_001_011110_0000_000_010101_00000_00000_u32),
2160                };
2161                if size.is_128bits() {
2162                    template |= 0b1 << 30;
2163                }
2164                let imm = imm as u32;
2165                // Deal with the somewhat strange encoding scheme for, and limits on,
2166                // the shift amount.
2167                let immh_immb = match (size.lane_size(), is_shr) {
2168                    (ScalarSize::Size64, true) if imm >= 1 && imm <= 64 => {
2169                        0b_1000_000_u32 | (64 - imm)
2170                    }
2171                    (ScalarSize::Size32, true) if imm >= 1 && imm <= 32 => {
2172                        0b_0100_000_u32 | (32 - imm)
2173                    }
2174                    (ScalarSize::Size16, true) if imm >= 1 && imm <= 16 => {
2175                        0b_0010_000_u32 | (16 - imm)
2176                    }
2177                    (ScalarSize::Size8, true) if imm >= 1 && imm <= 8 => {
2178                        0b_0001_000_u32 | (8 - imm)
2179                    }
2180                    (ScalarSize::Size64, false) if imm <= 63 => 0b_1000_000_u32 | imm,
2181                    (ScalarSize::Size32, false) if imm <= 31 => 0b_0100_000_u32 | imm,
2182                    (ScalarSize::Size16, false) if imm <= 15 => 0b_0010_000_u32 | imm,
2183                    (ScalarSize::Size8, false) if imm <= 7 => 0b_0001_000_u32 | imm,
2184                    _ => panic!(
2185                        "aarch64: Inst::VecShiftImmMod: emit: invalid op/size/imm {op:?}, {size:?}, {imm:?}"
2186                    ),
2187                };
2188                let rn_enc = machreg_to_vec(rn);
2189                let rd_enc = machreg_to_vec(rd.to_reg());
2190                sink.put4(template | (immh_immb << 16) | (rn_enc << 5) | rd_enc);
2191            }
2192            &Inst::VecExtract { rd, rn, rm, imm4 } => {
2193                if imm4 < 16 {
2194                    let template = 0b_01_101110_000_00000_0_0000_0_00000_00000_u32;
2195                    let rm_enc = machreg_to_vec(rm);
2196                    let rn_enc = machreg_to_vec(rn);
2197                    let rd_enc = machreg_to_vec(rd.to_reg());
2198                    sink.put4(
2199                        template | (rm_enc << 16) | ((imm4 as u32) << 11) | (rn_enc << 5) | rd_enc,
2200                    );
2201                } else {
2202                    panic!("aarch64: Inst::VecExtract: emit: invalid extract index {imm4}");
2203                }
2204            }
2205            &Inst::VecTbl { rd, rn, rm } => {
2206                sink.put4(enc_tbl(/* is_extension = */ false, 0b00, rd, rn, rm));
2207            }
2208            &Inst::VecTblExt { rd, ri, rn, rm } => {
2209                debug_assert_eq!(rd.to_reg(), ri);
2210                sink.put4(enc_tbl(/* is_extension = */ true, 0b00, rd, rn, rm));
2211            }
2212            &Inst::VecTbl2 { rd, rn, rn2, rm } => {
2213                assert_eq!(machreg_to_vec(rn2), (machreg_to_vec(rn) + 1) % 32);
2214                sink.put4(enc_tbl(/* is_extension = */ false, 0b01, rd, rn, rm));
2215            }
2216            &Inst::VecTbl2Ext {
2217                rd,
2218                ri,
2219                rn,
2220                rn2,
2221                rm,
2222            } => {
2223                debug_assert_eq!(rd.to_reg(), ri);
2224                assert_eq!(machreg_to_vec(rn2), (machreg_to_vec(rn) + 1) % 32);
2225                sink.put4(enc_tbl(/* is_extension = */ true, 0b01, rd, rn, rm));
2226            }
2227            &Inst::FpuCmp { size, rn, rm } => {
2228                sink.put4(enc_fcmp(size, rn, rm));
2229            }
2230            &Inst::FpuToInt { op, rd, rn } => {
2231                let top16 = match op {
2232                    // FCVTZS (32/32-bit)
2233                    FpuToIntOp::F32ToI32 => 0b000_11110_00_1_11_000,
2234                    // FCVTZU (32/32-bit)
2235                    FpuToIntOp::F32ToU32 => 0b000_11110_00_1_11_001,
2236                    // FCVTZS (32/64-bit)
2237                    FpuToIntOp::F32ToI64 => 0b100_11110_00_1_11_000,
2238                    // FCVTZU (32/64-bit)
2239                    FpuToIntOp::F32ToU64 => 0b100_11110_00_1_11_001,
2240                    // FCVTZS (64/32-bit)
2241                    FpuToIntOp::F64ToI32 => 0b000_11110_01_1_11_000,
2242                    // FCVTZU (64/32-bit)
2243                    FpuToIntOp::F64ToU32 => 0b000_11110_01_1_11_001,
2244                    // FCVTZS (64/64-bit)
2245                    FpuToIntOp::F64ToI64 => 0b100_11110_01_1_11_000,
2246                    // FCVTZU (64/64-bit)
2247                    FpuToIntOp::F64ToU64 => 0b100_11110_01_1_11_001,
2248                };
2249                sink.put4(enc_fputoint(top16, rd, rn));
2250            }
2251            &Inst::IntToFpu { op, rd, rn } => {
2252                let top16 = match op {
2253                    // SCVTF (32/32-bit)
2254                    IntToFpuOp::I32ToF32 => 0b000_11110_00_1_00_010,
2255                    // UCVTF (32/32-bit)
2256                    IntToFpuOp::U32ToF32 => 0b000_11110_00_1_00_011,
2257                    // SCVTF (64/32-bit)
2258                    IntToFpuOp::I64ToF32 => 0b100_11110_00_1_00_010,
2259                    // UCVTF (64/32-bit)
2260                    IntToFpuOp::U64ToF32 => 0b100_11110_00_1_00_011,
2261                    // SCVTF (32/64-bit)
2262                    IntToFpuOp::I32ToF64 => 0b000_11110_01_1_00_010,
2263                    // UCVTF (32/64-bit)
2264                    IntToFpuOp::U32ToF64 => 0b000_11110_01_1_00_011,
2265                    // SCVTF (64/64-bit)
2266                    IntToFpuOp::I64ToF64 => 0b100_11110_01_1_00_010,
2267                    // UCVTF (64/64-bit)
2268                    IntToFpuOp::U64ToF64 => 0b100_11110_01_1_00_011,
2269                };
2270                sink.put4(enc_inttofpu(top16, rd, rn));
2271            }
2272            &Inst::FpuCSel16 { rd, rn, rm, cond } => {
2273                sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size16));
2274            }
2275            &Inst::FpuCSel32 { rd, rn, rm, cond } => {
2276                sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size32));
2277            }
2278            &Inst::FpuCSel64 { rd, rn, rm, cond } => {
2279                sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size64));
2280            }
2281            &Inst::FpuRound { op, rd, rn } => {
2282                let top22 = match op {
2283                    FpuRoundMode::Minus32 => 0b000_11110_00_1_001_010_10000,
2284                    FpuRoundMode::Minus64 => 0b000_11110_01_1_001_010_10000,
2285                    FpuRoundMode::Plus32 => 0b000_11110_00_1_001_001_10000,
2286                    FpuRoundMode::Plus64 => 0b000_11110_01_1_001_001_10000,
2287                    FpuRoundMode::Zero32 => 0b000_11110_00_1_001_011_10000,
2288                    FpuRoundMode::Zero64 => 0b000_11110_01_1_001_011_10000,
2289                    FpuRoundMode::Nearest32 => 0b000_11110_00_1_001_000_10000,
2290                    FpuRoundMode::Nearest64 => 0b000_11110_01_1_001_000_10000,
2291                };
2292                sink.put4(enc_fround(top22, rd, rn));
2293            }
2294            &Inst::MovToFpu { rd, rn, size } => {
2295                let template = match size {
2296                    ScalarSize::Size16 => 0b000_11110_11_1_00_111_000000_00000_00000,
2297                    ScalarSize::Size32 => 0b000_11110_00_1_00_111_000000_00000_00000,
2298                    ScalarSize::Size64 => 0b100_11110_01_1_00_111_000000_00000_00000,
2299                    _ => unreachable!(),
2300                };
2301                sink.put4(template | (machreg_to_gpr(rn) << 5) | machreg_to_vec(rd.to_reg()));
2302            }
2303            &Inst::FpuMoveFPImm { rd, imm, size } => {
2304                sink.put4(
2305                    0b000_11110_00_1_00_000_000100_00000_00000
2306                        | size.ftype() << 22
2307                        | ((imm.enc_bits() as u32) << 13)
2308                        | machreg_to_vec(rd.to_reg()),
2309                );
2310            }
2311            &Inst::MovToVec {
2312                rd,
2313                ri,
2314                rn,
2315                idx,
2316                size,
2317            } => {
2318                debug_assert_eq!(rd.to_reg(), ri);
2319                let (imm5, shift) = match size.lane_size() {
2320                    ScalarSize::Size8 => (0b00001, 1),
2321                    ScalarSize::Size16 => (0b00010, 2),
2322                    ScalarSize::Size32 => (0b00100, 3),
2323                    ScalarSize::Size64 => (0b01000, 4),
2324                    _ => unreachable!(),
2325                };
2326                debug_assert_eq!(idx & (0b11111 >> shift), idx);
2327                let imm5 = imm5 | ((idx as u32) << shift);
2328                sink.put4(
2329                    0b010_01110000_00000_0_0011_1_00000_00000
2330                        | (imm5 << 16)
2331                        | (machreg_to_gpr(rn) << 5)
2332                        | machreg_to_vec(rd.to_reg()),
2333                );
2334            }
2335            &Inst::MovFromVec { rd, rn, idx, size } => {
2336                let (q, imm5, shift, mask) = match size {
2337                    ScalarSize::Size8 => (0b0, 0b00001, 1, 0b1111),
2338                    ScalarSize::Size16 => (0b0, 0b00010, 2, 0b0111),
2339                    ScalarSize::Size32 => (0b0, 0b00100, 3, 0b0011),
2340                    ScalarSize::Size64 => (0b1, 0b01000, 4, 0b0001),
2341                    _ => panic!("Unexpected scalar FP operand size: {size:?}"),
2342                };
2343                debug_assert_eq!(idx & mask, idx);
2344                let imm5 = imm5 | ((idx as u32) << shift);
2345                sink.put4(
2346                    0b000_01110000_00000_0_0111_1_00000_00000
2347                        | (q << 30)
2348                        | (imm5 << 16)
2349                        | (machreg_to_vec(rn) << 5)
2350                        | machreg_to_gpr(rd.to_reg()),
2351                );
2352            }
2353            &Inst::MovFromVecSigned {
2354                rd,
2355                rn,
2356                idx,
2357                size,
2358                scalar_size,
2359            } => {
2360                let (imm5, shift, half) = match size {
2361                    VectorSize::Size8x8 => (0b00001, 1, true),
2362                    VectorSize::Size8x16 => (0b00001, 1, false),
2363                    VectorSize::Size16x4 => (0b00010, 2, true),
2364                    VectorSize::Size16x8 => (0b00010, 2, false),
2365                    VectorSize::Size32x2 => {
2366                        debug_assert_ne!(scalar_size, OperandSize::Size32);
2367                        (0b00100, 3, true)
2368                    }
2369                    VectorSize::Size32x4 => {
2370                        debug_assert_ne!(scalar_size, OperandSize::Size32);
2371                        (0b00100, 3, false)
2372                    }
2373                    _ => panic!("Unexpected vector operand size"),
2374                };
2375                debug_assert_eq!(idx & (0b11111 >> (half as u32 + shift)), idx);
2376                let imm5 = imm5 | ((idx as u32) << shift);
2377                sink.put4(
2378                    0b000_01110000_00000_0_0101_1_00000_00000
2379                        | (scalar_size.is64() as u32) << 30
2380                        | (imm5 << 16)
2381                        | (machreg_to_vec(rn) << 5)
2382                        | machreg_to_gpr(rd.to_reg()),
2383                );
2384            }
2385            &Inst::VecDup { rd, rn, size } => {
2386                let q = size.is_128bits() as u32;
2387                let imm5 = match size.lane_size() {
2388                    ScalarSize::Size8 => 0b00001,
2389                    ScalarSize::Size16 => 0b00010,
2390                    ScalarSize::Size32 => 0b00100,
2391                    ScalarSize::Size64 => 0b01000,
2392                    _ => unreachable!(),
2393                };
2394                sink.put4(
2395                    0b0_0_0_01110000_00000_000011_00000_00000
2396                        | (q << 30)
2397                        | (imm5 << 16)
2398                        | (machreg_to_gpr(rn) << 5)
2399                        | machreg_to_vec(rd.to_reg()),
2400                );
2401            }
2402            &Inst::VecDupFromFpu { rd, rn, size, lane } => {
2403                let q = size.is_128bits() as u32;
2404                let imm5 = match size.lane_size() {
2405                    ScalarSize::Size8 => {
2406                        assert!(lane < 16);
2407                        0b00001 | (u32::from(lane) << 1)
2408                    }
2409                    ScalarSize::Size16 => {
2410                        assert!(lane < 8);
2411                        0b00010 | (u32::from(lane) << 2)
2412                    }
2413                    ScalarSize::Size32 => {
2414                        assert!(lane < 4);
2415                        0b00100 | (u32::from(lane) << 3)
2416                    }
2417                    ScalarSize::Size64 => {
2418                        assert!(lane < 2);
2419                        0b01000 | (u32::from(lane) << 4)
2420                    }
2421                    _ => unimplemented!(),
2422                };
2423                sink.put4(
2424                    0b000_01110000_00000_000001_00000_00000
2425                        | (q << 30)
2426                        | (imm5 << 16)
2427                        | (machreg_to_vec(rn) << 5)
2428                        | machreg_to_vec(rd.to_reg()),
2429                );
2430            }
2431            &Inst::VecDupFPImm { rd, imm, size } => {
2432                let imm = imm.enc_bits();
2433                let op = match size.lane_size() {
2434                    ScalarSize::Size32 => 0,
2435                    ScalarSize::Size64 => 1,
2436                    _ => unimplemented!(),
2437                };
2438                let q_op = op | ((size.is_128bits() as u32) << 1);
2439
2440                sink.put4(enc_asimd_mod_imm(rd, q_op, 0b1111, imm));
2441            }
2442            &Inst::VecDupImm {
2443                rd,
2444                imm,
2445                invert,
2446                size,
2447            } => {
2448                let (imm, shift, shift_ones) = imm.value();
2449                let (op, cmode) = match size.lane_size() {
2450                    ScalarSize::Size8 => {
2451                        assert!(!invert);
2452                        assert_eq!(shift, 0);
2453
2454                        (0, 0b1110)
2455                    }
2456                    ScalarSize::Size16 => {
2457                        let s = shift & 8;
2458
2459                        assert!(!shift_ones);
2460                        assert_eq!(s, shift);
2461
2462                        (invert as u32, 0b1000 | (s >> 2))
2463                    }
2464                    ScalarSize::Size32 => {
2465                        if shift_ones {
2466                            assert!(shift == 8 || shift == 16);
2467
2468                            (invert as u32, 0b1100 | (shift >> 4))
2469                        } else {
2470                            let s = shift & 24;
2471
2472                            assert_eq!(s, shift);
2473
2474                            (invert as u32, 0b0000 | (s >> 2))
2475                        }
2476                    }
2477                    ScalarSize::Size64 => {
2478                        assert!(!invert);
2479                        assert_eq!(shift, 0);
2480
2481                        (1, 0b1110)
2482                    }
2483                    _ => unreachable!(),
2484                };
2485                let q_op = op | ((size.is_128bits() as u32) << 1);
2486
2487                sink.put4(enc_asimd_mod_imm(rd, q_op, cmode, imm));
2488            }
2489            &Inst::VecExtend {
2490                t,
2491                rd,
2492                rn,
2493                high_half,
2494                lane_size,
2495            } => {
2496                let immh = match lane_size {
2497                    ScalarSize::Size16 => 0b001,
2498                    ScalarSize::Size32 => 0b010,
2499                    ScalarSize::Size64 => 0b100,
2500                    _ => panic!("Unexpected VecExtend to lane size of {lane_size:?}"),
2501                };
2502                let u = match t {
2503                    VecExtendOp::Sxtl => 0b0,
2504                    VecExtendOp::Uxtl => 0b1,
2505                };
2506                sink.put4(
2507                    0b000_011110_0000_000_101001_00000_00000
2508                        | ((high_half as u32) << 30)
2509                        | (u << 29)
2510                        | (immh << 19)
2511                        | (machreg_to_vec(rn) << 5)
2512                        | machreg_to_vec(rd.to_reg()),
2513                );
2514            }
2515            &Inst::VecRRLong {
2516                op,
2517                rd,
2518                rn,
2519                high_half,
2520            } => {
2521                let (u, size, bits_12_16) = match op {
2522                    VecRRLongOp::Fcvtl16 => (0b0, 0b00, 0b10111),
2523                    VecRRLongOp::Fcvtl32 => (0b0, 0b01, 0b10111),
2524                    VecRRLongOp::Shll8 => (0b1, 0b00, 0b10011),
2525                    VecRRLongOp::Shll16 => (0b1, 0b01, 0b10011),
2526                    VecRRLongOp::Shll32 => (0b1, 0b10, 0b10011),
2527                };
2528
2529                sink.put4(enc_vec_rr_misc(
2530                    ((high_half as u32) << 1) | u,
2531                    size,
2532                    bits_12_16,
2533                    rd,
2534                    rn,
2535                ));
2536            }
2537            &Inst::VecRRNarrowLow {
2538                op,
2539                rd,
2540                rn,
2541                lane_size,
2542            }
2543            | &Inst::VecRRNarrowHigh {
2544                op,
2545                rd,
2546                rn,
2547                lane_size,
2548                ..
2549            } => {
2550                let high_half = match self {
2551                    &Inst::VecRRNarrowLow { .. } => false,
2552                    &Inst::VecRRNarrowHigh { .. } => true,
2553                    _ => unreachable!(),
2554                };
2555
2556                let size = match lane_size {
2557                    ScalarSize::Size8 => 0b00,
2558                    ScalarSize::Size16 => 0b01,
2559                    ScalarSize::Size32 => 0b10,
2560                    _ => panic!("unsupported size: {lane_size:?}"),
2561                };
2562
2563                // Floats use a single bit, to encode either half or single.
2564                let size = match op {
2565                    VecRRNarrowOp::Fcvtn => size >> 1,
2566                    _ => size,
2567                };
2568
2569                let (u, bits_12_16) = match op {
2570                    VecRRNarrowOp::Xtn => (0b0, 0b10010),
2571                    VecRRNarrowOp::Sqxtn => (0b0, 0b10100),
2572                    VecRRNarrowOp::Sqxtun => (0b1, 0b10010),
2573                    VecRRNarrowOp::Uqxtn => (0b1, 0b10100),
2574                    VecRRNarrowOp::Fcvtn => (0b0, 0b10110),
2575                };
2576
2577                sink.put4(enc_vec_rr_misc(
2578                    ((high_half as u32) << 1) | u,
2579                    size,
2580                    bits_12_16,
2581                    rd,
2582                    rn,
2583                ));
2584            }
2585            &Inst::VecMovElement {
2586                rd,
2587                ri,
2588                rn,
2589                dest_idx,
2590                src_idx,
2591                size,
2592            } => {
2593                debug_assert_eq!(rd.to_reg(), ri);
2594                let (imm5, shift) = match size.lane_size() {
2595                    ScalarSize::Size8 => (0b00001, 1),
2596                    ScalarSize::Size16 => (0b00010, 2),
2597                    ScalarSize::Size32 => (0b00100, 3),
2598                    ScalarSize::Size64 => (0b01000, 4),
2599                    _ => unreachable!(),
2600                };
2601                let mask = 0b11111 >> shift;
2602                debug_assert_eq!(dest_idx & mask, dest_idx);
2603                debug_assert_eq!(src_idx & mask, src_idx);
2604                let imm4 = (src_idx as u32) << (shift - 1);
2605                let imm5 = imm5 | ((dest_idx as u32) << shift);
2606                sink.put4(
2607                    0b011_01110000_00000_0_0000_1_00000_00000
2608                        | (imm5 << 16)
2609                        | (imm4 << 11)
2610                        | (machreg_to_vec(rn) << 5)
2611                        | machreg_to_vec(rd.to_reg()),
2612                );
2613            }
2614            &Inst::VecRRPair { op, rd, rn } => {
2615                let bits_12_16 = match op {
2616                    VecPairOp::Addp => 0b11011,
2617                };
2618
2619                sink.put4(enc_vec_rr_pair(bits_12_16, rd, rn));
2620            }
2621            &Inst::VecRRRLong {
2622                rd,
2623                rn,
2624                rm,
2625                alu_op,
2626                high_half,
2627            } => {
2628                let (u, size, bit14) = match alu_op {
2629                    VecRRRLongOp::Smull8 => (0b0, 0b00, 0b1),
2630                    VecRRRLongOp::Smull16 => (0b0, 0b01, 0b1),
2631                    VecRRRLongOp::Smull32 => (0b0, 0b10, 0b1),
2632                    VecRRRLongOp::Umull8 => (0b1, 0b00, 0b1),
2633                    VecRRRLongOp::Umull16 => (0b1, 0b01, 0b1),
2634                    VecRRRLongOp::Umull32 => (0b1, 0b10, 0b1),
2635                };
2636                sink.put4(enc_vec_rrr_long(
2637                    high_half as u32,
2638                    u,
2639                    size,
2640                    bit14,
2641                    rm,
2642                    rn,
2643                    rd,
2644                ));
2645            }
2646            &Inst::VecRRRLongMod {
2647                rd,
2648                ri,
2649                rn,
2650                rm,
2651                alu_op,
2652                high_half,
2653            } => {
2654                debug_assert_eq!(rd.to_reg(), ri);
2655                let (u, size, bit14) = match alu_op {
2656                    VecRRRLongModOp::Umlal8 => (0b1, 0b00, 0b0),
2657                    VecRRRLongModOp::Umlal16 => (0b1, 0b01, 0b0),
2658                    VecRRRLongModOp::Umlal32 => (0b1, 0b10, 0b0),
2659                };
2660                sink.put4(enc_vec_rrr_long(
2661                    high_half as u32,
2662                    u,
2663                    size,
2664                    bit14,
2665                    rm,
2666                    rn,
2667                    rd,
2668                ));
2669            }
2670            &Inst::VecRRPairLong { op, rd, rn } => {
2671                let (u, size) = match op {
2672                    VecRRPairLongOp::Saddlp8 => (0b0, 0b0),
2673                    VecRRPairLongOp::Uaddlp8 => (0b1, 0b0),
2674                    VecRRPairLongOp::Saddlp16 => (0b0, 0b1),
2675                    VecRRPairLongOp::Uaddlp16 => (0b1, 0b1),
2676                };
2677
2678                sink.put4(enc_vec_rr_pair_long(u, size, rd, rn));
2679            }
2680            &Inst::VecRRR {
2681                rd,
2682                rn,
2683                rm,
2684                alu_op,
2685                size,
2686            } => {
2687                let (q, enc_size) = size.enc_size();
2688                let is_float = match alu_op {
2689                    VecALUOp::Fcmeq
2690                    | VecALUOp::Fcmgt
2691                    | VecALUOp::Fcmge
2692                    | VecALUOp::Fadd
2693                    | VecALUOp::Fsub
2694                    | VecALUOp::Fdiv
2695                    | VecALUOp::Fmax
2696                    | VecALUOp::Fmin
2697                    | VecALUOp::Fmul => true,
2698                    _ => false,
2699                };
2700
2701                let (top11, bit15_10) = match alu_op {
2702                    VecALUOp::Sqadd => (0b000_01110_00_1 | enc_size << 1, 0b000011),
2703                    VecALUOp::Sqsub => (0b000_01110_00_1 | enc_size << 1, 0b001011),
2704                    VecALUOp::Uqadd => (0b001_01110_00_1 | enc_size << 1, 0b000011),
2705                    VecALUOp::Uqsub => (0b001_01110_00_1 | enc_size << 1, 0b001011),
2706                    VecALUOp::Cmeq => (0b001_01110_00_1 | enc_size << 1, 0b100011),
2707                    VecALUOp::Cmge => (0b000_01110_00_1 | enc_size << 1, 0b001111),
2708                    VecALUOp::Cmgt => (0b000_01110_00_1 | enc_size << 1, 0b001101),
2709                    VecALUOp::Cmhi => (0b001_01110_00_1 | enc_size << 1, 0b001101),
2710                    VecALUOp::Cmhs => (0b001_01110_00_1 | enc_size << 1, 0b001111),
2711                    VecALUOp::Fcmeq => (0b000_01110_00_1, 0b111001),
2712                    VecALUOp::Fcmgt => (0b001_01110_10_1, 0b111001),
2713                    VecALUOp::Fcmge => (0b001_01110_00_1, 0b111001),
2714                    // The following logical instructions operate on bytes, so are not encoded differently
2715                    // for the different vector types.
2716                    VecALUOp::And => (0b000_01110_00_1, 0b000111),
2717                    VecALUOp::Bic => (0b000_01110_01_1, 0b000111),
2718                    VecALUOp::Orr => (0b000_01110_10_1, 0b000111),
2719                    VecALUOp::Orn => (0b000_01110_11_1, 0b000111),
2720                    VecALUOp::Eor => (0b001_01110_00_1, 0b000111),
2721                    VecALUOp::Umaxp => {
2722                        debug_assert_ne!(size, VectorSize::Size64x2);
2723
2724                        (0b001_01110_00_1 | enc_size << 1, 0b101001)
2725                    }
2726                    VecALUOp::Add => (0b000_01110_00_1 | enc_size << 1, 0b100001),
2727                    VecALUOp::Sub => (0b001_01110_00_1 | enc_size << 1, 0b100001),
2728                    VecALUOp::Mul => {
2729                        debug_assert_ne!(size, VectorSize::Size64x2);
2730                        (0b000_01110_00_1 | enc_size << 1, 0b100111)
2731                    }
2732                    VecALUOp::Sshl => (0b000_01110_00_1 | enc_size << 1, 0b010001),
2733                    VecALUOp::Ushl => (0b001_01110_00_1 | enc_size << 1, 0b010001),
2734                    VecALUOp::Umin => {
2735                        debug_assert_ne!(size, VectorSize::Size64x2);
2736
2737                        (0b001_01110_00_1 | enc_size << 1, 0b011011)
2738                    }
2739                    VecALUOp::Smin => {
2740                        debug_assert_ne!(size, VectorSize::Size64x2);
2741
2742                        (0b000_01110_00_1 | enc_size << 1, 0b011011)
2743                    }
2744                    VecALUOp::Umax => {
2745                        debug_assert_ne!(size, VectorSize::Size64x2);
2746
2747                        (0b001_01110_00_1 | enc_size << 1, 0b011001)
2748                    }
2749                    VecALUOp::Smax => {
2750                        debug_assert_ne!(size, VectorSize::Size64x2);
2751
2752                        (0b000_01110_00_1 | enc_size << 1, 0b011001)
2753                    }
2754                    VecALUOp::Urhadd => {
2755                        debug_assert_ne!(size, VectorSize::Size64x2);
2756
2757                        (0b001_01110_00_1 | enc_size << 1, 0b000101)
2758                    }
2759                    VecALUOp::Fadd => (0b000_01110_00_1, 0b110101),
2760                    VecALUOp::Fsub => (0b000_01110_10_1, 0b110101),
2761                    VecALUOp::Fdiv => (0b001_01110_00_1, 0b111111),
2762                    VecALUOp::Fmax => (0b000_01110_00_1, 0b111101),
2763                    VecALUOp::Fmin => (0b000_01110_10_1, 0b111101),
2764                    VecALUOp::Fmul => (0b001_01110_00_1, 0b110111),
2765                    VecALUOp::Addp => (0b000_01110_00_1 | enc_size << 1, 0b101111),
2766                    VecALUOp::Zip1 => (0b01001110_00_0 | enc_size << 1, 0b001110),
2767                    VecALUOp::Zip2 => (0b01001110_00_0 | enc_size << 1, 0b011110),
2768                    VecALUOp::Sqrdmulh => {
2769                        debug_assert!(
2770                            size.lane_size() == ScalarSize::Size16
2771                                || size.lane_size() == ScalarSize::Size32
2772                        );
2773
2774                        (0b001_01110_00_1 | enc_size << 1, 0b101101)
2775                    }
2776                    VecALUOp::Uzp1 => (0b01001110_00_0 | enc_size << 1, 0b000110),
2777                    VecALUOp::Uzp2 => (0b01001110_00_0 | enc_size << 1, 0b010110),
2778                    VecALUOp::Trn1 => (0b01001110_00_0 | enc_size << 1, 0b001010),
2779                    VecALUOp::Trn2 => (0b01001110_00_0 | enc_size << 1, 0b011010),
2780                };
2781                let top11 = if is_float {
2782                    top11 | size.enc_float_size() << 1
2783                } else {
2784                    top11
2785                };
2786                sink.put4(enc_vec_rrr(top11 | q << 9, rm, bit15_10, rn, rd));
2787            }
2788            &Inst::VecRRRMod {
2789                rd,
2790                ri,
2791                rn,
2792                rm,
2793                alu_op,
2794                size,
2795            } => {
2796                debug_assert_eq!(rd.to_reg(), ri);
2797                let (q, _enc_size) = size.enc_size();
2798
2799                let (top11, bit15_10) = match alu_op {
2800                    VecALUModOp::Bsl => (0b001_01110_01_1, 0b000111),
2801                    VecALUModOp::Fmla => {
2802                        (0b000_01110_00_1 | (size.enc_float_size() << 1), 0b110011)
2803                    }
2804                    VecALUModOp::Fmls => {
2805                        (0b000_01110_10_1 | (size.enc_float_size() << 1), 0b110011)
2806                    }
2807                    // SDOT Vd.4S, Vn.16B, Vm.16B (FEAT_DotProd). The size/element
2808                    // field (bits 23:22 = 0b10) is part of the dot-product opcode,
2809                    // so it is baked into top11; only Q (from `size`) is variable.
2810                    // top11 (Q=0) | q<<9 with bit15_10 yields 0x4E809400 for .4S/.16B.
2811                    VecALUModOp::Sdot => (0b000_01110_10_0, 0b100101),
2812                    // USDOT Vd.4S, Vn.16B, Vm.16B (FEAT_I8MM). Same shape as
2813                    // SDOT; only the opcode field differs.
2814                    VecALUModOp::Usdot => (0b000_01110_10_0, 0b100111),
2815                };
2816                sink.put4(enc_vec_rrr(top11 | q << 9, rm, bit15_10, rn, rd));
2817            }
2818            &Inst::VecFmlaElem {
2819                rd,
2820                ri,
2821                rn,
2822                rm,
2823                alu_op,
2824                size,
2825                idx,
2826            } => {
2827                debug_assert_eq!(rd.to_reg(), ri);
2828                let idx = u32::from(idx);
2829
2830                let (q, _size) = size.enc_size();
2831                let o2 = match alu_op {
2832                    VecALUModOp::Fmla => 0b0,
2833                    VecALUModOp::Fmls => 0b1,
2834                    _ => unreachable!(),
2835                };
2836
2837                let (h, l) = match size {
2838                    VectorSize::Size32x4 => {
2839                        assert!(idx < 4);
2840                        (idx >> 1, idx & 1)
2841                    }
2842                    VectorSize::Size64x2 => {
2843                        assert!(idx < 2);
2844                        (idx, 0)
2845                    }
2846                    _ => unreachable!(),
2847                };
2848
2849                let top11 = 0b000_011111_00 | (q << 9) | (size.enc_float_size() << 1) | l;
2850                let bit15_10 = 0b000100 | (o2 << 4) | (h << 1);
2851                sink.put4(enc_vec_rrr(top11, rm, bit15_10, rn, rd));
2852            }
2853            &Inst::VecLoadReplicate {
2854                rd,
2855                rn,
2856                size,
2857                flags,
2858            } => {
2859                let (q, size) = size.enc_size();
2860
2861                if let Some(trap_code) = flags.trap_code() {
2862                    // Register the offset at which the actual load instruction starts.
2863                    sink.add_trap(trap_code);
2864                }
2865
2866                sink.put4(enc_ldst_vec(q, size, rn, rd));
2867            }
2868            &Inst::VecCSel { rd, rn, rm, cond } => {
2869                /* Emit this:
2870                      b.cond  else
2871                      mov     rd, rm
2872                      b       out
2873                     else:
2874                      mov     rd, rn
2875                     out:
2876
2877                   Note, we could do better in the cases where rd == rn or rd == rm.
2878                */
2879                let else_label = sink.get_label();
2880                let out_label = sink.get_label();
2881
2882                // b.cond else
2883                let br_else_offset = sink.cur_offset();
2884                sink.put4(enc_conditional_br(
2885                    BranchTarget::Label(else_label),
2886                    CondBrKind::Cond(cond),
2887                ));
2888                sink.use_label_at_offset(br_else_offset, else_label, LabelUse::Branch19);
2889
2890                // mov rd, rm
2891                sink.put4(enc_vecmov(/* 16b = */ true, rd, rm));
2892
2893                // b out
2894                let b_out_offset = sink.cur_offset();
2895                sink.use_label_at_offset(b_out_offset, out_label, LabelUse::Branch26);
2896                sink.add_uncond_branch(b_out_offset, b_out_offset + 4, out_label);
2897                sink.put4(enc_jump26(0b000101, 0 /* will be fixed up later */));
2898
2899                // else:
2900                sink.bind_label(else_label, &mut state.ctrl_plane);
2901
2902                // mov rd, rn
2903                sink.put4(enc_vecmov(/* 16b = */ true, rd, rn));
2904
2905                // out:
2906                sink.bind_label(out_label, &mut state.ctrl_plane);
2907            }
2908            &Inst::MovToNZCV { rn } => {
2909                sink.put4(0xd51b4200 | machreg_to_gpr(rn));
2910            }
2911            &Inst::MovFromNZCV { rd } => {
2912                sink.put4(0xd53b4200 | machreg_to_gpr(rd.to_reg()));
2913            }
2914            &Inst::Extend {
2915                rd,
2916                rn,
2917                signed: false,
2918                from_bits: 1,
2919                to_bits,
2920            } => {
2921                assert!(to_bits <= 64);
2922                // Reduce zero-extend-from-1-bit to:
2923                // - and rd, rn, #1
2924                // Note: This is special cased as UBFX may take more cycles
2925                // than AND on smaller cores.
2926                let imml = ImmLogic::maybe_from_u64(1, I32).unwrap();
2927                Inst::AluRRImmLogic {
2928                    alu_op: ALUOp::And,
2929                    size: OperandSize::Size32,
2930                    rd,
2931                    rn,
2932                    imml,
2933                }
2934                .emit(sink, emit_info, state);
2935            }
2936            &Inst::Extend {
2937                rd,
2938                rn,
2939                signed: false,
2940                from_bits: 32,
2941                to_bits: 64,
2942            } => {
2943                let mov = Inst::Mov {
2944                    size: OperandSize::Size32,
2945                    rd,
2946                    rm: rn,
2947                };
2948                mov.emit(sink, emit_info, state);
2949            }
2950            &Inst::Extend {
2951                rd,
2952                rn,
2953                signed,
2954                from_bits,
2955                to_bits,
2956            } => {
2957                let (opc, size) = if signed {
2958                    (0b00, OperandSize::from_bits(to_bits))
2959                } else {
2960                    (0b10, OperandSize::Size32)
2961                };
2962                sink.put4(enc_bfm(opc, size, rd, rn, 0, from_bits - 1));
2963            }
2964            &Inst::Jump { ref dest } => {
2965                let off = sink.cur_offset();
2966                // Indicate that the jump uses a label, if so, so that a fixup can occur later.
2967                if let Some(l) = dest.as_label() {
2968                    sink.use_label_at_offset(off, l, LabelUse::Branch26);
2969                    sink.add_uncond_branch(off, off + 4, l);
2970                }
2971                // Emit the jump itself.
2972                sink.put4(enc_jump26(0b000101, dest.as_offset26_or_zero()));
2973            }
2974            &Inst::Args { .. } | &Inst::Rets { .. } => {
2975                // Nothing: this is a pseudoinstruction that serves
2976                // only to constrain registers at a certain point.
2977            }
2978            &Inst::Ret {} => {
2979                sink.put4(0xd65f03c0);
2980            }
2981            &Inst::AuthenticatedRet { key, is_hint } => {
2982                let (op2, is_hint) = match key {
2983                    APIKey::AZ => (0b100, true),
2984                    APIKey::ASP => (0b101, is_hint),
2985                    APIKey::BZ => (0b110, true),
2986                    APIKey::BSP => (0b111, is_hint),
2987                };
2988
2989                if is_hint {
2990                    sink.put4(key.enc_auti_hint());
2991                    Inst::Ret {}.emit(sink, emit_info, state);
2992                } else {
2993                    sink.put4(0xd65f0bff | (op2 << 9)); // reta{key}
2994                }
2995            }
2996            &Inst::Call { ref info } => {
2997                let start = sink.cur_offset();
2998                let user_stack_map = state.take_stack_map();
2999                sink.add_reloc(Reloc::Arm64Call, &info.dest, 0);
3000                sink.put4(enc_jump26(0b100101, 0));
3001                if let Some(s) = user_stack_map {
3002                    let offset = sink.cur_offset();
3003                    sink.push_user_stack_map(state, offset, s);
3004                }
3005
3006                if let Some(try_call) = info.try_call_info.as_ref() {
3007                    sink.add_try_call_site(
3008                        Some(state.frame_layout.sp_to_fp()),
3009                        try_call.exception_handlers(&state.frame_layout),
3010                    );
3011                } else {
3012                    sink.add_call_site();
3013                }
3014
3015                if info.callee_pop_size > 0 {
3016                    let callee_pop_size =
3017                        i32::try_from(info.callee_pop_size).expect("callee popped more than 2GB");
3018                    for inst in AArch64MachineDeps::gen_sp_reg_adjust(-callee_pop_size) {
3019                        inst.emit(sink, emit_info, state);
3020                    }
3021                }
3022
3023                if info.patchable {
3024                    sink.add_patchable_call_site(sink.cur_offset() - start);
3025                } else {
3026                    // Load any stack-carried return values.
3027                    info.emit_retval_loads::<AArch64MachineDeps, _, _>(
3028                        state.frame_layout().stackslots_size,
3029                        |inst| inst.emit(sink, emit_info, state),
3030                        |needed_space| Some(Inst::EmitIsland { needed_space }),
3031                    );
3032                }
3033
3034                // If this is a try-call, jump to the continuation
3035                // (normal-return) block.
3036                if let Some(try_call) = info.try_call_info.as_ref() {
3037                    let jmp = Inst::Jump {
3038                        dest: BranchTarget::Label(try_call.continuation),
3039                    };
3040                    jmp.emit(sink, emit_info, state);
3041                }
3042
3043                // We produce an island above if needed, so disable
3044                // the worst-case-size check in this case.
3045                start_off = sink.cur_offset();
3046            }
3047            &Inst::CallInd { ref info } => {
3048                let user_stack_map = state.take_stack_map();
3049                sink.put4(
3050                    0b1101011_0001_11111_000000_00000_00000 | (machreg_to_gpr(info.dest) << 5),
3051                );
3052                if let Some(s) = user_stack_map {
3053                    let offset = sink.cur_offset();
3054                    sink.push_user_stack_map(state, offset, s);
3055                }
3056
3057                if let Some(try_call) = info.try_call_info.as_ref() {
3058                    sink.add_try_call_site(
3059                        Some(state.frame_layout.sp_to_fp()),
3060                        try_call.exception_handlers(&state.frame_layout),
3061                    );
3062                } else {
3063                    sink.add_call_site();
3064                }
3065
3066                if info.callee_pop_size > 0 {
3067                    let callee_pop_size =
3068                        i32::try_from(info.callee_pop_size).expect("callee popped more than 2GB");
3069                    for inst in AArch64MachineDeps::gen_sp_reg_adjust(-callee_pop_size) {
3070                        inst.emit(sink, emit_info, state);
3071                    }
3072                }
3073
3074                // Load any stack-carried return values.
3075                info.emit_retval_loads::<AArch64MachineDeps, _, _>(
3076                    state.frame_layout().stackslots_size,
3077                    |inst| inst.emit(sink, emit_info, state),
3078                    |needed_space| Some(Inst::EmitIsland { needed_space }),
3079                );
3080
3081                // If this is a try-call, jump to the continuation
3082                // (normal-return) block.
3083                if let Some(try_call) = info.try_call_info.as_ref() {
3084                    let jmp = Inst::Jump {
3085                        dest: BranchTarget::Label(try_call.continuation),
3086                    };
3087                    jmp.emit(sink, emit_info, state);
3088                }
3089
3090                // We produce an island above if needed, so disable
3091                // the worst-case-size check in this case.
3092                start_off = sink.cur_offset();
3093            }
3094            &Inst::ReturnCall { ref info } => {
3095                emit_return_call_common_sequence(sink, emit_info, state, info);
3096
3097                // Note: this is not `Inst::Jump { .. }.emit(..)` because we
3098                // have different metadata in this case: we don't have a label
3099                // for the target, but rather a function relocation.
3100                sink.add_reloc(Reloc::Arm64Call, &info.dest, 0);
3101                sink.put4(enc_jump26(0b000101, 0));
3102                sink.add_call_site();
3103
3104                // `emit_return_call_common_sequence` emits an island if
3105                // necessary, so we can safely disable the worst-case-size check
3106                // in this case.
3107                start_off = sink.cur_offset();
3108            }
3109            &Inst::ReturnCallInd { ref info } => {
3110                emit_return_call_common_sequence(sink, emit_info, state, info);
3111
3112                Inst::IndirectBr {
3113                    rn: info.dest,
3114                    targets: vec![],
3115                }
3116                .emit(sink, emit_info, state);
3117                sink.add_call_site();
3118
3119                // `emit_return_call_common_sequence` emits an island if
3120                // necessary, so we can safely disable the worst-case-size check
3121                // in this case.
3122                start_off = sink.cur_offset();
3123            }
3124            &Inst::CondBr {
3125                taken,
3126                not_taken,
3127                kind,
3128            } => {
3129                // Conditional part first.
3130                let cond_off = sink.cur_offset();
3131                if let Some(l) = taken.as_label() {
3132                    sink.use_label_at_offset(cond_off, l, LabelUse::Branch19);
3133                    let inverted = enc_conditional_br(taken, kind.invert()).to_le_bytes();
3134                    sink.add_cond_branch(cond_off, cond_off + 4, l, &inverted[..]);
3135                }
3136                sink.put4(enc_conditional_br(taken, kind));
3137
3138                // Unconditional part next.
3139                let uncond_off = sink.cur_offset();
3140                if let Some(l) = not_taken.as_label() {
3141                    sink.use_label_at_offset(uncond_off, l, LabelUse::Branch26);
3142                    sink.add_uncond_branch(uncond_off, uncond_off + 4, l);
3143                }
3144                sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero()));
3145            }
3146            &Inst::TestBitAndBranch {
3147                taken,
3148                not_taken,
3149                kind,
3150                rn,
3151                bit,
3152            } => {
3153                // Emit the conditional branch first
3154                let cond_off = sink.cur_offset();
3155                if let Some(l) = taken.as_label() {
3156                    sink.use_label_at_offset(cond_off, l, LabelUse::Branch14);
3157                    let inverted =
3158                        enc_test_bit_and_branch(kind.complement(), taken, rn, bit).to_le_bytes();
3159                    sink.add_cond_branch(cond_off, cond_off + 4, l, &inverted[..]);
3160                }
3161                sink.put4(enc_test_bit_and_branch(kind, taken, rn, bit));
3162
3163                // Unconditional part next.
3164                let uncond_off = sink.cur_offset();
3165                if let Some(l) = not_taken.as_label() {
3166                    sink.use_label_at_offset(uncond_off, l, LabelUse::Branch26);
3167                    sink.add_uncond_branch(uncond_off, uncond_off + 4, l);
3168                }
3169                sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero()));
3170            }
3171            &Inst::TrapIf { kind, trap_code } => {
3172                let label = sink.defer_trap(trap_code);
3173                // condbr KIND, LABEL
3174                let off = sink.cur_offset();
3175                sink.put4(enc_conditional_br(BranchTarget::Label(label), kind));
3176                sink.use_label_at_offset(off, label, LabelUse::Branch19);
3177            }
3178            &Inst::IndirectBr { rn, .. } => {
3179                sink.put4(enc_br(rn));
3180            }
3181            &Inst::Nop0 => {}
3182            &Inst::Nop4 => {
3183                sink.put4(0xd503201f);
3184            }
3185            &Inst::Brk => {
3186                sink.put4(0xd43e0000);
3187            }
3188            &Inst::Udf { trap_code } => {
3189                sink.add_trap(trap_code);
3190                sink.put_data(Inst::TRAP_OPCODE);
3191            }
3192            &Inst::Adr { rd, off } => {
3193                assert!(off > -(1 << 20));
3194                assert!(off < (1 << 20));
3195                sink.put4(enc_adr(off, rd));
3196            }
3197            &Inst::Adrp { rd, off } => {
3198                assert!(off > -(1 << 20));
3199                assert!(off < (1 << 20));
3200                sink.put4(enc_adrp(off, rd));
3201            }
3202            &Inst::Word4 { data } => {
3203                sink.put4(data);
3204            }
3205            &Inst::Word8 { data } => {
3206                sink.put8(data);
3207            }
3208            &Inst::JTSequence {
3209                ridx,
3210                rtmp1,
3211                rtmp2,
3212                default,
3213                ref targets,
3214                ..
3215            } => {
3216                // This sequence is *one* instruction in the vcode, and is expanded only here at
3217                // emission time, because we cannot allow the regalloc to insert spills/reloads in
3218                // the middle; we depend on hardcoded PC-rel addressing below.
3219
3220                // Branch to default when condition code from prior comparison indicates.
3221                let br =
3222                    enc_conditional_br(BranchTarget::Label(default), CondBrKind::Cond(Cond::Hs));
3223
3224                // No need to inform the sink's branch folding logic about this branch, because it
3225                // will not be merged with any other branch, flipped, or elided (it is not preceded
3226                // or succeeded by any other branch). Just emit it with the label use.
3227                let default_br_offset = sink.cur_offset();
3228                sink.use_label_at_offset(default_br_offset, default, LabelUse::Branch19);
3229                sink.put4(br);
3230
3231                // Overwrite the index with a zero when the above
3232                // branch misspeculates (Spectre mitigation). Save the
3233                // resulting index in rtmp2.
3234                let inst = Inst::CSel {
3235                    rd: rtmp2,
3236                    cond: Cond::Hs,
3237                    rn: zero_reg(),
3238                    rm: ridx,
3239                };
3240                inst.emit(sink, emit_info, state);
3241                // Prevent any data value speculation if spectre mitigations are
3242                // enabled.
3243                if emit_info.flags.enable_table_access_spectre_mitigation()
3244                    && emit_info.isa_flags.use_csdb()
3245                {
3246                    Inst::Csdb.emit(sink, emit_info, state);
3247                }
3248
3249                // Load address of jump table
3250                let inst = Inst::Adr { rd: rtmp1, off: 16 };
3251                inst.emit(sink, emit_info, state);
3252                // Load value out of jump table
3253                let inst = Inst::SLoad32 {
3254                    rd: rtmp2,
3255                    mem: AMode::reg_plus_reg_scaled_extended(
3256                        rtmp1.to_reg(),
3257                        rtmp2.to_reg(),
3258                        ExtendOp::UXTW,
3259                    ),
3260                    flags: MemFlagsData::trusted(),
3261                };
3262                inst.emit(sink, emit_info, state);
3263                // Add base of jump table to jump-table-sourced block offset
3264                let inst = Inst::AluRRR {
3265                    alu_op: ALUOp::Add,
3266                    size: OperandSize::Size64,
3267                    rd: rtmp1,
3268                    rn: rtmp1.to_reg(),
3269                    rm: rtmp2.to_reg(),
3270                };
3271                inst.emit(sink, emit_info, state);
3272                // Branch to computed address. (`targets` here is only used for successor queries
3273                // and is not needed for emission.)
3274                let inst = Inst::IndirectBr {
3275                    rn: rtmp1.to_reg(),
3276                    targets: vec![],
3277                };
3278                inst.emit(sink, emit_info, state);
3279                // Emit jump table (table of 32-bit offsets).
3280                let jt_off = sink.cur_offset();
3281                for &target in targets.iter() {
3282                    let word_off = sink.cur_offset();
3283                    // off_into_table is an addend here embedded in the label to be later patched
3284                    // at the end of codegen. The offset is initially relative to this jump table
3285                    // entry; with the extra addend, it'll be relative to the jump table's start,
3286                    // after patching.
3287                    let off_into_table = word_off - jt_off;
3288                    sink.use_label_at_offset(word_off, target, LabelUse::PCRel32);
3289                    sink.put4(off_into_table);
3290                }
3291
3292                // Lowering produces an EmitIsland before using a JTSequence, so we can safely
3293                // disable the worst-case-size check in this case.
3294                start_off = sink.cur_offset();
3295            }
3296            &Inst::LoadExtNameGot { rd, ref name } => {
3297                // See this CE Example for the variations of this with and without BTI & PAUTH
3298                // https://godbolt.org/z/ncqjbbvvn
3299                //
3300                // Emit the following code:
3301                //   adrp    rd, :got:X
3302                //   ldr     rd, [rd, :got_lo12:X]
3303
3304                // adrp rd, symbol
3305                sink.add_reloc(Reloc::Aarch64AdrGotPage21, &**name, 0);
3306                let inst = Inst::Adrp { rd, off: 0 };
3307                inst.emit(sink, emit_info, state);
3308
3309                // ldr rd, [rd, :got_lo12:X]
3310                sink.add_reloc(Reloc::Aarch64Ld64GotLo12Nc, &**name, 0);
3311                let inst = Inst::ULoad64 {
3312                    rd,
3313                    mem: AMode::reg(rd.to_reg()),
3314                    flags: MemFlagsData::trusted(),
3315                };
3316                inst.emit(sink, emit_info, state);
3317            }
3318            &Inst::LoadExtNameNear {
3319                rd,
3320                ref name,
3321                offset,
3322            } => {
3323                // Emit the following code:
3324                //   adrp    rd, X
3325                //   add     rd, rd, :lo12:X
3326                //
3327                // See https://godbolt.org/z/855KEvM5r for an example.
3328
3329                // adrp rd, symbol
3330                sink.add_reloc(Reloc::Aarch64AdrPrelPgHi21, &**name, offset);
3331                let inst = Inst::Adrp { rd, off: 0 };
3332                inst.emit(sink, emit_info, state);
3333
3334                // add rd, rd, :lo12:X
3335                sink.add_reloc(Reloc::Aarch64AddAbsLo12Nc, &**name, offset);
3336                let inst = Inst::AluRRImm12 {
3337                    alu_op: ALUOp::Add,
3338                    size: OperandSize::Size64,
3339                    rd,
3340                    rn: rd.to_reg(),
3341                    imm12: Imm12::ZERO,
3342                };
3343                inst.emit(sink, emit_info, state);
3344            }
3345            &Inst::LoadExtNameFar {
3346                rd,
3347                ref name,
3348                offset,
3349            } => {
3350                // With absolute offsets we set up a load from a preallocated space, and then jump
3351                // over it.
3352                //
3353                // Emit the following code:
3354                //   ldr     rd, #8
3355                //   b       #0x10
3356                //   <8 byte space>
3357
3358                let inst = Inst::ULoad64 {
3359                    rd,
3360                    mem: AMode::Label {
3361                        label: MemLabel::PCRel(8),
3362                    },
3363                    flags: MemFlagsData::trusted(),
3364                };
3365                inst.emit(sink, emit_info, state);
3366                let inst = Inst::Jump {
3367                    dest: BranchTarget::ResolvedOffset(12),
3368                };
3369                inst.emit(sink, emit_info, state);
3370                sink.add_reloc(Reloc::Abs8, &**name, offset);
3371                sink.put8(0);
3372            }
3373            &Inst::LoadAddr { rd, ref mem } => {
3374                let mem = mem.clone();
3375                let (mem_insts, mem) = mem_finalize(Some(sink), &mem, I8, state);
3376                for inst in mem_insts.into_iter() {
3377                    inst.emit(sink, emit_info, state);
3378                }
3379
3380                let (reg, index_reg, offset) = match mem {
3381                    AMode::RegExtended { rn, rm, extendop } => {
3382                        let r = rn;
3383                        (r, Some((rm, extendop)), 0)
3384                    }
3385                    AMode::Unscaled { rn, simm9 } => {
3386                        let r = rn;
3387                        (r, None, simm9.value())
3388                    }
3389                    AMode::UnsignedOffset { rn, uimm12 } => {
3390                        let r = rn;
3391                        (r, None, uimm12.value() as i32)
3392                    }
3393                    _ => panic!("Unsupported case for LoadAddr: {mem:?}"),
3394                };
3395                let abs_offset = if offset < 0 {
3396                    -offset as u64
3397                } else {
3398                    offset as u64
3399                };
3400                let alu_op = if offset < 0 { ALUOp::Sub } else { ALUOp::Add };
3401
3402                if let Some((idx, extendop)) = index_reg {
3403                    let add = Inst::AluRRRExtend {
3404                        alu_op: ALUOp::Add,
3405                        size: OperandSize::Size64,
3406                        rd,
3407                        rn: reg,
3408                        rm: idx,
3409                        extendop,
3410                    };
3411
3412                    add.emit(sink, emit_info, state);
3413                } else if offset == 0 {
3414                    if reg != rd.to_reg() {
3415                        let mov = Inst::Mov {
3416                            size: OperandSize::Size64,
3417                            rd,
3418                            rm: reg,
3419                        };
3420
3421                        mov.emit(sink, emit_info, state);
3422                    }
3423                } else if let Some(imm12) = Imm12::maybe_from_u64(abs_offset) {
3424                    let add = Inst::AluRRImm12 {
3425                        alu_op,
3426                        size: OperandSize::Size64,
3427                        rd,
3428                        rn: reg,
3429                        imm12,
3430                    };
3431                    add.emit(sink, emit_info, state);
3432                } else {
3433                    // Use `tmp2` here: `reg` may be `spilltmp` if the `AMode` on this instruction
3434                    // was initially an `SPOffset`. Assert that `tmp2` is truly free to use. Note
3435                    // that no other instructions will be inserted here (we're emitting directly),
3436                    // and a live range of `tmp2` should not span this instruction, so this use
3437                    // should otherwise be correct.
3438                    debug_assert!(rd.to_reg() != tmp2_reg());
3439                    debug_assert!(reg != tmp2_reg());
3440                    let tmp = writable_tmp2_reg();
3441                    for insn in Inst::load_constant(tmp, abs_offset).into_iter() {
3442                        insn.emit(sink, emit_info, state);
3443                    }
3444                    let add = Inst::AluRRR {
3445                        alu_op,
3446                        size: OperandSize::Size64,
3447                        rd,
3448                        rn: reg,
3449                        rm: tmp.to_reg(),
3450                    };
3451                    add.emit(sink, emit_info, state);
3452                }
3453            }
3454            &Inst::Paci { key } => {
3455                let (crm, op2) = match key {
3456                    APIKey::AZ => (0b0011, 0b000),
3457                    APIKey::ASP => (0b0011, 0b001),
3458                    APIKey::BZ => (0b0011, 0b010),
3459                    APIKey::BSP => (0b0011, 0b011),
3460                };
3461
3462                sink.put4(0xd503211f | (crm << 8) | (op2 << 5));
3463            }
3464            &Inst::Xpaclri => sink.put4(0xd50320ff),
3465            &Inst::Bti { targets } => {
3466                let targets = match targets {
3467                    BranchTargetType::None => 0b00,
3468                    BranchTargetType::C => 0b01,
3469                    BranchTargetType::J => 0b10,
3470                    BranchTargetType::JC => 0b11,
3471                };
3472
3473                sink.put4(0xd503241f | targets << 6);
3474            }
3475            &Inst::EmitIsland { needed_space } => {
3476                if sink.island_needed(needed_space + 4) {
3477                    let jump_around_label = sink.get_label();
3478                    let jmp = Inst::Jump {
3479                        dest: BranchTarget::Label(jump_around_label),
3480                    };
3481                    jmp.emit(sink, emit_info, state);
3482                    sink.emit_island(needed_space + 4, &mut state.ctrl_plane);
3483                    sink.bind_label(jump_around_label, &mut state.ctrl_plane);
3484                }
3485            }
3486
3487            &Inst::ElfTlsGetAddr {
3488                ref symbol,
3489                rd,
3490                tmp,
3491            } => {
3492                assert_eq!(xreg(0), rd.to_reg());
3493
3494                // See the original proposal for TLSDESC.
3495                // http://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf
3496                //
3497                // Implement the TLSDESC instruction sequence:
3498                //   adrp x0, :tlsdesc:tlsvar
3499                //   ldr  tmp, [x0, :tlsdesc_lo12:tlsvar]
3500                //   add  x0, x0, :tlsdesc_lo12:tlsvar
3501                //   blr  tmp
3502                //   mrs  tmp, tpidr_el0
3503                //   add  x0, x0, tmp
3504                //
3505                // This is the instruction sequence that GCC emits for ELF GD TLS Relocations in aarch64
3506                // See: https://gcc.godbolt.org/z/e4j7MdErh
3507
3508                // adrp x0, :tlsdesc:tlsvar
3509                sink.add_reloc(Reloc::Aarch64TlsDescAdrPage21, &**symbol, 0);
3510                Inst::Adrp { rd, off: 0 }.emit(sink, emit_info, state);
3511
3512                // ldr  tmp, [x0, :tlsdesc_lo12:tlsvar]
3513                sink.add_reloc(Reloc::Aarch64TlsDescLd64Lo12, &**symbol, 0);
3514                Inst::ULoad64 {
3515                    rd: tmp,
3516                    mem: AMode::reg(rd.to_reg()),
3517                    flags: MemFlagsData::trusted(),
3518                }
3519                .emit(sink, emit_info, state);
3520
3521                // add x0, x0, :tlsdesc_lo12:tlsvar
3522                sink.add_reloc(Reloc::Aarch64TlsDescAddLo12, &**symbol, 0);
3523                Inst::AluRRImm12 {
3524                    alu_op: ALUOp::Add,
3525                    size: OperandSize::Size64,
3526                    rd,
3527                    rn: rd.to_reg(),
3528                    imm12: Imm12::maybe_from_u64(0).unwrap(),
3529                }
3530                .emit(sink, emit_info, state);
3531
3532                // blr tmp
3533                sink.add_reloc(Reloc::Aarch64TlsDescCall, &**symbol, 0);
3534                Inst::CallInd {
3535                    info: crate::isa::Box::new(CallInfo::empty(tmp.to_reg(), CallConv::SystemV)),
3536                }
3537                .emit(sink, emit_info, state);
3538
3539                // mrs tmp, tpidr_el0
3540                sink.put4(0xd53bd040 | machreg_to_gpr(tmp.to_reg()));
3541
3542                // add x0, x0, tmp
3543                Inst::AluRRR {
3544                    alu_op: ALUOp::Add,
3545                    size: OperandSize::Size64,
3546                    rd,
3547                    rn: rd.to_reg(),
3548                    rm: tmp.to_reg(),
3549                }
3550                .emit(sink, emit_info, state);
3551            }
3552
3553            &Inst::MachOTlsGetAddr { ref symbol, rd } => {
3554                // Each thread local variable gets a descriptor, where the first xword of the descriptor is a pointer
3555                // to a function that takes the descriptor address in x0, and after the function returns x0
3556                // contains the address for the thread local variable
3557                //
3558                // what we want to emit is basically:
3559                //
3560                // adrp x0, <label>@TLVPPAGE  ; Load the address of the page of the thread local variable pointer (TLVP)
3561                // ldr x0, [x0, <label>@TLVPPAGEOFF] ; Load the descriptor's address into x0
3562                // ldr x1, [x0] ; Load the function pointer (the first part of the descriptor)
3563                // blr x1 ; Call the function pointer with the descriptor address in x0
3564                // ; x0 now contains the TLV address
3565
3566                assert_eq!(xreg(0), rd.to_reg());
3567                let rtmp = writable_xreg(1);
3568
3569                // adrp x0, <label>@TLVPPAGE
3570                sink.add_reloc(Reloc::MachOAarch64TlsAdrPage21, symbol, 0);
3571                sink.put4(0x90000000);
3572
3573                // ldr x0, [x0, <label>@TLVPPAGEOFF]
3574                sink.add_reloc(Reloc::MachOAarch64TlsAdrPageOff12, symbol, 0);
3575                sink.put4(0xf9400000);
3576
3577                // load [x0] into temp register
3578                Inst::ULoad64 {
3579                    rd: rtmp,
3580                    mem: AMode::reg(rd.to_reg()),
3581                    flags: MemFlagsData::trusted(),
3582                }
3583                .emit(sink, emit_info, state);
3584
3585                // call function pointer in temp register
3586                Inst::CallInd {
3587                    info: crate::isa::Box::new(CallInfo::empty(
3588                        rtmp.to_reg(),
3589                        CallConv::AppleAarch64,
3590                    )),
3591                }
3592                .emit(sink, emit_info, state);
3593            }
3594
3595            &Inst::Unwind { ref inst } => {
3596                sink.add_unwind(inst.clone());
3597            }
3598
3599            &Inst::DummyUse { .. } => {}
3600
3601            &Inst::LabelAddress { dst, label } => {
3602                // We emit an ADR only, which is +/- 2MiB range. This
3603                // should be sufficient for the typical use-case of
3604                // this instruction, which is insmall trampolines to
3605                // get exception-handler addresses.
3606                let inst = Inst::Adr { rd: dst, off: 0 };
3607                let offset = sink.cur_offset();
3608                inst.emit(sink, emit_info, state);
3609                sink.use_label_at_offset(offset, label, LabelUse::Adr21);
3610            }
3611
3612            &Inst::SequencePoint { .. } => {
3613                // Nothing.
3614            }
3615
3616            &Inst::StackProbeLoop { start, end, step } => {
3617                assert!(emit_info.flags.enable_probestack());
3618
3619                // The loop generated here uses `start` as a counter register to
3620                // count backwards until negating it exceeds `end`. In other
3621                // words `start` is an offset from `sp` we're testing where
3622                // `end` is the max size we need to test. The loop looks like:
3623                //
3624                //      loop_start:
3625                //          sub start, start, #step
3626                //          stur xzr, [sp, start]
3627                //          cmn start, end
3628                //          br.gt loop_start
3629                //      loop_end:
3630                //
3631                // Note that this loop cannot use the spilltmp and tmp2
3632                // registers as those are currently used as the input to this
3633                // loop when generating the instruction. This means that some
3634                // more flavorful address modes and lowerings need to be
3635                // avoided.
3636                //
3637                // Perhaps someone more clever than I can figure out how to use
3638                // `subs` or the like and skip the `cmn`, but I can't figure it
3639                // out at this time.
3640
3641                let loop_start = sink.get_label();
3642                sink.bind_label(loop_start, &mut state.ctrl_plane);
3643
3644                Inst::AluRRImm12 {
3645                    alu_op: ALUOp::Sub,
3646                    size: OperandSize::Size64,
3647                    rd: start,
3648                    rn: start.to_reg(),
3649                    imm12: step,
3650                }
3651                .emit(sink, emit_info, state);
3652                Inst::Store32 {
3653                    rd: regs::zero_reg(),
3654                    mem: AMode::RegReg {
3655                        rn: regs::stack_reg(),
3656                        rm: start.to_reg(),
3657                    },
3658                    flags: MemFlagsData::trusted(),
3659                }
3660                .emit(sink, emit_info, state);
3661                Inst::AluRRR {
3662                    alu_op: ALUOp::AddS,
3663                    size: OperandSize::Size64,
3664                    rd: regs::writable_zero_reg(),
3665                    rn: start.to_reg(),
3666                    rm: end,
3667                }
3668                .emit(sink, emit_info, state);
3669
3670                let loop_end = sink.get_label();
3671                Inst::CondBr {
3672                    taken: BranchTarget::Label(loop_start),
3673                    not_taken: BranchTarget::Label(loop_end),
3674                    kind: CondBrKind::Cond(Cond::Gt),
3675                }
3676                .emit(sink, emit_info, state);
3677                sink.bind_label(loop_end, &mut state.ctrl_plane);
3678            }
3679        }
3680
3681        let end_off = sink.cur_offset();
3682        debug_assert!(
3683            (end_off - start_off) <= Inst::worst_case_size()
3684                || matches!(self, Inst::EmitIsland { .. }),
3685            "Worst case size exceed for {:?}: {}",
3686            self,
3687            end_off - start_off
3688        );
3689
3690        state.clear_post_insn();
3691    }
3692
3693    fn pretty_print_inst(&self, state: &mut Self::State) -> String {
3694        self.print_with_state(state)
3695    }
3696}
3697
3698fn emit_return_call_common_sequence<T>(
3699    sink: &mut MachBuffer<Inst>,
3700    emit_info: &EmitInfo,
3701    state: &mut EmitState,
3702    info: &ReturnCallInfo<T>,
3703) {
3704    for inst in AArch64MachineDeps::gen_clobber_restore(
3705        CallConv::Tail,
3706        &emit_info.flags,
3707        state.frame_layout(),
3708    ) {
3709        inst.emit(sink, emit_info, state);
3710    }
3711
3712    let setup_area_size = state.frame_layout().setup_area_size;
3713    if setup_area_size > 0 {
3714        // N.B.: sp is already adjusted to the appropriate place by the
3715        // clobber-restore code (which also frees the fixed frame). Hence, there
3716        // is no need for the usual `mov sp, fp` here.
3717
3718        // `ldp fp, lr, [sp], #16`
3719        Inst::LoadP64 {
3720            rt: writable_fp_reg(),
3721            rt2: writable_link_reg(),
3722            mem: PairAMode::SPPostIndexed {
3723                // TODO: we could fold the increment for incoming_args_diff here, as long as that
3724                // value is less than 502*8, by adding it to `setup_area_size`.
3725                // https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/LDP--Load-Pair-of-Registers-
3726                simm7: SImm7Scaled::maybe_from_i64(i64::from(setup_area_size), types::I64).unwrap(),
3727            },
3728            flags: MemFlagsData::trusted(),
3729        }
3730        .emit(sink, emit_info, state);
3731    }
3732
3733    // Adjust SP to account for the possible over-allocation in the prologue.
3734    let incoming_args_diff = state.frame_layout().tail_args_size - info.new_stack_arg_size;
3735    if incoming_args_diff > 0 {
3736        for inst in
3737            AArch64MachineDeps::gen_sp_reg_adjust(i32::try_from(incoming_args_diff).unwrap())
3738        {
3739            inst.emit(sink, emit_info, state);
3740        }
3741    }
3742
3743    if (setup_area_size > 0 || info.sign_return_address_all)
3744        && let Some(key) = info.key
3745    {
3746        sink.put4(key.enc_auti_hint());
3747    }
3748}