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