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),
1579                        I8 => Some(ExtendOp::SXTB),
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 let Some(op) = sign_ext {
1594                    Inst::gen_extend(x27wr, x27, op, size).emit(sink, emit_info, state);
1595                }
1596
1597                match op {
1598                    AtomicRMWLoopOp::Xchg => {} // do nothing
1599                    AtomicRMWLoopOp::Nand => {
1600                        // and x28, x27, x26
1601                        // mvn x28, x28
1602
1603                        Inst::AluRRR {
1604                            alu_op: ALUOp::And,
1605                            size,
1606                            rd: x28wr,
1607                            rn: x27,
1608                            rm: x26,
1609                        }
1610                        .emit(sink, emit_info, state);
1611
1612                        Inst::AluRRR {
1613                            alu_op: ALUOp::OrrNot,
1614                            size,
1615                            rd: x28wr,
1616                            rn: xzr,
1617                            rm: x28,
1618                        }
1619                        .emit(sink, emit_info, state);
1620                    }
1621                    AtomicRMWLoopOp::Umin
1622                    | AtomicRMWLoopOp::Umax
1623                    | AtomicRMWLoopOp::Smin
1624                    | AtomicRMWLoopOp::Smax => {
1625                        // cmp x27, x26 {?sxt}
1626                        // csel.op x28, x27, x26
1627
1628                        let cond = match op {
1629                            AtomicRMWLoopOp::Umin => Cond::Lo,
1630                            AtomicRMWLoopOp::Umax => Cond::Hi,
1631                            AtomicRMWLoopOp::Smin => Cond::Lt,
1632                            AtomicRMWLoopOp::Smax => Cond::Gt,
1633                            _ => unreachable!(),
1634                        };
1635
1636                        if let Some(extendop) = sign_ext.or(zero_ext) {
1637                            Inst::AluRRRExtend {
1638                                alu_op: ALUOp::SubS,
1639                                size,
1640                                rd: writable_zero_reg(),
1641                                rn: x27,
1642                                rm: x26,
1643                                extendop,
1644                            }
1645                            .emit(sink, emit_info, state);
1646                        } else {
1647                            Inst::AluRRR {
1648                                alu_op: ALUOp::SubS,
1649                                size,
1650                                rd: writable_zero_reg(),
1651                                rn: x27,
1652                                rm: x26,
1653                            }
1654                            .emit(sink, emit_info, state);
1655                        }
1656
1657                        Inst::CSel {
1658                            cond,
1659                            rd: x28wr,
1660                            rn: x27,
1661                            rm: x26,
1662                        }
1663                        .emit(sink, emit_info, state);
1664                    }
1665                    _ => {
1666                        // add/sub/and/orr/eor x28, x27, x26
1667                        let alu_op = match op {
1668                            AtomicRMWLoopOp::Add => ALUOp::Add,
1669                            AtomicRMWLoopOp::Sub => ALUOp::Sub,
1670                            AtomicRMWLoopOp::And => ALUOp::And,
1671                            AtomicRMWLoopOp::Orr => ALUOp::Orr,
1672                            AtomicRMWLoopOp::Eor => ALUOp::Eor,
1673                            AtomicRMWLoopOp::Nand
1674                            | AtomicRMWLoopOp::Umin
1675                            | AtomicRMWLoopOp::Umax
1676                            | AtomicRMWLoopOp::Smin
1677                            | AtomicRMWLoopOp::Smax
1678                            | AtomicRMWLoopOp::Xchg => unreachable!(),
1679                        };
1680
1681                        Inst::AluRRR {
1682                            alu_op,
1683                            size,
1684                            rd: x28wr,
1685                            rn: x27,
1686                            rm: x26,
1687                        }
1688                        .emit(sink, emit_info, state);
1689                    }
1690                }
1691
1692                if let Some(trap_code) = flags.trap_code() {
1693                    sink.add_trap(trap_code);
1694                }
1695                if op == AtomicRMWLoopOp::Xchg {
1696                    sink.put4(enc_stlxr(ty, x24wr, x26, x25)); // stlxr w24, x26, [x25]
1697                } else {
1698                    sink.put4(enc_stlxr(ty, x24wr, x28, x25)); // stlxr w24, x28, [x25]
1699                }
1700
1701                // cbnz w24, again
1702                // Note, we're actually testing x24, and relying on the default zero-high-half
1703                // rule in the assignment that `stlxr` does.
1704                let br_offset = sink.cur_offset();
1705                sink.put4(enc_conditional_br(
1706                    BranchTarget::Label(again_label),
1707                    CondBrKind::NotZero(x24, OperandSize::Size64),
1708                ));
1709                sink.use_label_at_offset(br_offset, again_label, LabelUse::Branch19);
1710            }
1711            &Inst::AtomicRMW128Loop { op, flags, .. } => {
1712                /* Emit this:
1713                     again:
1714                      ldaxp       x27, x23, [x25]
1715                      op          x28, x21, x27, x23, x26, x22 // op is adds,subc,and,orr,eor
1716                      stlxp       w24, x28, [x25]
1717                      cbnz        x24, again
1718
1719                   Operand conventions:
1720                      IN:  x25 (addr), x26 (low bytes of op), x22 (high bytes of op)
1721                      OUT: x27 (low bytes of old value), x23 (high bytes of old value), x24 (trashed), x28 (trashed), x21 (trashed)
1722
1723                   It is unfortunate that, per the ARM documentation, x28 cannot be used for
1724                   both the store-data and success-flag operands of stlxr.  This causes the
1725                   instruction's behaviour to be "CONSTRAINED UNPREDICTABLE", so we use x24
1726                   instead for the success-flag.
1727                */
1728                // TODO: We should not hardcode registers here, a better idea would be to
1729                // pass some scratch registers in the AtomicRMWLoop pseudo-instruction, and use those
1730                let xzr = zero_reg();
1731                let x21 = xreg(21);
1732                let x22 = xreg(22);
1733                let x23 = xreg(23);
1734                let x24 = xreg(24);
1735                let x25 = xreg(25);
1736                let x26 = xreg(26);
1737                let x27 = xreg(27);
1738                let x28 = xreg(28);
1739                let x21wr = writable_xreg(21);
1740                let x23wr = writable_xreg(23);
1741                let x24wr = writable_xreg(24);
1742                let x27wr = writable_xreg(27);
1743                let x28wr = writable_xreg(28);
1744                let again_label = sink.get_label();
1745
1746                // again:
1747                sink.bind_label(again_label, &mut state.ctrl_plane);
1748
1749                if let Some(trap_code) = flags.trap_code() {
1750                    sink.add_trap(trap_code);
1751                }
1752
1753                sink.put4(enc_ldaxp(I64, x27wr, x23wr, x25)); // ldaxp x27, x23, [x25]
1754
1755                match op {
1756                    AtomicRMWLoopOp::Xchg => {} // do nothing
1757                    AtomicRMWLoopOp::Smax
1758                    | AtomicRMWLoopOp::Umax
1759                    | AtomicRMWLoopOp::Smin
1760                    | AtomicRMWLoopOp::Umin => {
1761                        // cmp x27, x26
1762                        // sbcs xzr, x23, x22
1763                        // csel.op x21, x22, x23
1764                        // csel.op x28, x26, x27
1765
1766                        let cond = match op {
1767                            AtomicRMWLoopOp::Smax => Cond::Lt,
1768                            AtomicRMWLoopOp::Umax => Cond::Lo,
1769                            AtomicRMWLoopOp::Smin => Cond::Ge,
1770                            AtomicRMWLoopOp::Umin => Cond::Hs,
1771                            _ => unreachable!(),
1772                        };
1773
1774                        Inst::AluRRR {
1775                            alu_op: ALUOp::SubS,
1776                            size: OperandSize::Size64,
1777                            rd: writable_zero_reg(),
1778                            rn: x27,
1779                            rm: x26,
1780                        }
1781                        .emit(sink, emit_info, state);
1782
1783                        Inst::AluRRR {
1784                            alu_op: ALUOp::SbcS,
1785                            size: OperandSize::Size64,
1786                            rd: writable_zero_reg(),
1787                            rn: x23,
1788                            rm: x22,
1789                        }
1790                        .emit(sink, emit_info, state);
1791
1792                        Inst::CSel {
1793                            cond,
1794                            rd: x21wr,
1795                            rn: x22,
1796                            rm: x23,
1797                        }
1798                        .emit(sink, emit_info, state);
1799
1800                        Inst::CSel {
1801                            cond,
1802                            rd: x28wr,
1803                            rn: x26,
1804                            rm: x27,
1805                        }
1806                        .emit(sink, emit_info, state);
1807                    }
1808                    _ => {
1809                        let (op_lo, op_hi) = match op {
1810                            // adds x28, x27, x26
1811                            // adc x21, x23, x22
1812                            AtomicRMWLoopOp::Add => (ALUOp::AddS, ALUOp::Adc),
1813                            // subs x28, x27, x26
1814                            // sbc x21, x23, x22
1815                            AtomicRMWLoopOp::Sub => (ALUOp::SubS, ALUOp::Sbc),
1816                            // and x28, x27, x26
1817                            // and x21, x23, x22
1818                            AtomicRMWLoopOp::And | AtomicRMWLoopOp::Nand => {
1819                                (ALUOp::And, ALUOp::And)
1820                            }
1821                            // orr x28, x27, x26
1822                            // orr x21, x23, x22
1823                            AtomicRMWLoopOp::Orr => (ALUOp::Orr, ALUOp::Orr),
1824                            // eor x28, x27, x26
1825                            // eor x21, x23, x22
1826                            AtomicRMWLoopOp::Eor => (ALUOp::Eor, ALUOp::Eor),
1827                            _ => unreachable!(),
1828                        };
1829
1830                        Inst::AluRRR {
1831                            alu_op: op_lo,
1832                            size: OperandSize::Size64,
1833                            rd: x28wr,
1834                            rn: x27,
1835                            rm: x26,
1836                        }
1837                        .emit(sink, emit_info, state);
1838
1839                        Inst::AluRRR {
1840                            alu_op: op_hi,
1841                            size: OperandSize::Size64,
1842                            rd: x21wr,
1843                            rn: x23,
1844                            rm: x22,
1845                        }
1846                        .emit(sink, emit_info, state);
1847
1848                        if op == AtomicRMWLoopOp::Nand {
1849                            // mvn x28, x28
1850                            // mvn x21, x21
1851
1852                            Inst::AluRRR {
1853                                alu_op: ALUOp::OrrNot,
1854                                size: OperandSize::Size64,
1855                                rd: x28wr,
1856                                rn: xzr,
1857                                rm: x28,
1858                            }
1859                            .emit(sink, emit_info, state);
1860
1861                            Inst::AluRRR {
1862                                alu_op: ALUOp::OrrNot,
1863                                size: OperandSize::Size64,
1864                                rd: x21wr,
1865                                rn: xzr,
1866                                rm: x21,
1867                            }
1868                            .emit(sink, emit_info, state);
1869                        }
1870                    }
1871                }
1872
1873                if let Some(trap_code) = flags.trap_code() {
1874                    sink.add_trap(trap_code);
1875                }
1876
1877                if op == AtomicRMWLoopOp::Xchg {
1878                    sink.put4(enc_stlxp(I64, x24wr, x26, x22, x25)); // stlxp w24, x26, x22, [x25]
1879                } else {
1880                    sink.put4(enc_stlxp(I64, x24wr, x28, x21, x25)); // stlxp w24, x28, x21, [x25]
1881                }
1882
1883                // cbnz w24, again
1884                // Note, we're actually testing x24, and relying on the default zero-high-half
1885                // rule in the assignment that `stlxp` does.
1886                let br_offset = sink.cur_offset();
1887                sink.put4(enc_conditional_br(
1888                    BranchTarget::Label(again_label),
1889                    CondBrKind::NotZero(x24, OperandSize::Size64),
1890                ));
1891                sink.use_label_at_offset(br_offset, again_label, LabelUse::Branch19);
1892            }
1893            &Inst::AtomicCAS {
1894                rd,
1895                rs,
1896                rt,
1897                rn,
1898                ty,
1899                flags,
1900            } => {
1901                debug_assert_eq!(rd.to_reg(), rs);
1902                let size = match ty {
1903                    I8 => 0b00,
1904                    I16 => 0b01,
1905                    I32 => 0b10,
1906                    I64 => 0b11,
1907                    _ => panic!("Unsupported type: {ty}"),
1908                };
1909
1910                if let Some(trap_code) = flags.trap_code() {
1911                    sink.add_trap(trap_code);
1912                }
1913
1914                sink.put4(enc_cas(size, rd, rt, rn));
1915            }
1916            Inst::AtomicCAS128 { args } => {
1917                let &AtomicCAS128Args {
1918                    rd_lo,
1919                    rd_hi,
1920                    rs_lo,
1921                    rs_hi,
1922                    rt_lo,
1923                    rt_hi,
1924                    rn,
1925                    flags,
1926                } = &**args;
1927                debug_assert_eq!(rd_lo.to_reg(), rs_lo);
1928                debug_assert_eq!(rd_hi.to_reg(), rs_hi);
1929
1930                // These should be pinned to pairs that `casp` requires.
1931                debug_assert_eq!(rs_hi, xreg(machreg_to_gpr(rs_lo) as u8 + 1));
1932                debug_assert_eq!(rt_hi, xreg(machreg_to_gpr(rt_lo) as u8 + 1));
1933
1934                if let Some(trap_code) = flags.trap_code() {
1935                    sink.add_trap(trap_code);
1936                }
1937
1938                sink.put4(enc_casp(rd_lo, rt_lo, rn));
1939            }
1940            &Inst::AtomicCASLoop { ty, flags, .. } => {
1941                /* Emit this:
1942                    again:
1943                     ldaxr{,b,h} x/w27, [x25]
1944                     cmp         x/w27, x/w26 uxt{b,h}
1945                     b.ne        out
1946                     stlxr{,b,h} w24, x/w28, [x25]
1947                     cbnz        x24, again
1948                    out:
1949
1950                  Operand conventions:
1951                     IN:  x25 (addr), x26 (expected value), x28 (replacement value)
1952                     OUT: x27 (old value), x24 (trashed)
1953                */
1954                let x24 = xreg(24);
1955                let x25 = xreg(25);
1956                let x26 = xreg(26);
1957                let x27 = xreg(27);
1958                let x28 = xreg(28);
1959                let xzrwr = writable_zero_reg();
1960                let x24wr = writable_xreg(24);
1961                let x27wr = writable_xreg(27);
1962                let again_label = sink.get_label();
1963                let out_label = sink.get_label();
1964
1965                // again:
1966                sink.bind_label(again_label, &mut state.ctrl_plane);
1967
1968                if let Some(trap_code) = flags.trap_code() {
1969                    sink.add_trap(trap_code);
1970                }
1971
1972                // ldaxr x27, [x25]
1973                sink.put4(enc_ldaxr(ty, x27wr, x25));
1974
1975                // The top 32-bits are zero-extended by the ldaxr so for I64
1976                // we don't have to use UXTW, just the x-form of the
1977                // register. I8/I16 use UXTB/UXTH to only compare their
1978                // respective widths, and I32 uses the w-form of the
1979                // register (`sf` cleared) to only compare the low 32 bits,
1980                // since the expected value may have garbage in its upper
1981                // 32 bits (e.g. left behind by an `ireduce`).
1982                let (sf, bit21, extend_op) = match ty {
1983                    I8 => (0b1, 0b1, 0b000000),
1984                    I16 => (0b1, 0b1, 0b001000),
1985                    I32 => (0b0, 0b0, 0b000000),
1986                    _ => (0b1, 0b0, 0b000000),
1987                };
1988                let bits_31_21 = (sf << 10) | 0b011_01011_000 | bit21;
1989                // cmp x27, x26 (== subs xzr, x27, x26)
1990                sink.put4(enc_arith_rrr(bits_31_21, extend_op, xzrwr, x27, x26));
1991
1992                // b.ne out
1993                let br_out_offset = sink.cur_offset();
1994                sink.put4(enc_conditional_br(
1995                    BranchTarget::Label(out_label),
1996                    CondBrKind::Cond(Cond::Ne),
1997                ));
1998                sink.use_label_at_offset(br_out_offset, out_label, LabelUse::Branch19);
1999
2000                if let Some(trap_code) = flags.trap_code() {
2001                    sink.add_trap(trap_code);
2002                }
2003
2004                sink.put4(enc_stlxr(ty, x24wr, x28, x25)); // stlxr w24, x28, [x25]
2005
2006                // cbnz w24, again.
2007                // Note, we're actually testing x24, and relying on the default zero-high-half
2008                // rule in the assignment that `stlxr` does.
2009                let br_again_offset = sink.cur_offset();
2010                sink.put4(enc_conditional_br(
2011                    BranchTarget::Label(again_label),
2012                    CondBrKind::NotZero(x24, OperandSize::Size64),
2013                ));
2014                sink.use_label_at_offset(br_again_offset, again_label, LabelUse::Branch19);
2015
2016                // out:
2017                sink.bind_label(out_label, &mut state.ctrl_plane);
2018            }
2019            &Inst::AtomicCAS128Loop { flags, .. } => {
2020                /* Emit this:
2021                    again:
2022                     ldaxp       x27, x21, [x25]
2023                     cmp         x27, x26
2024                     b.ne        keep
2025                     cmp         x21, x23
2026                     b.ne        keep
2027                     stlxp       w24, x28, x22, [x25]
2028                     cbnz        w24, again
2029                     b           out
2030                    keep:
2031                     stlxp       w24, x27, x21, [x25]
2032                     cbnz        w24, again
2033                    out:
2034
2035                  Operand conventions:
2036                     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)
2037                     OUT: x27 (low bytes of old value), x21 (high bytes of old value), x24 (trashed)
2038                */
2039                let x21 = xreg(21);
2040                let x22 = xreg(22);
2041                let x23 = xreg(23);
2042                let x24 = xreg(24);
2043                let x25 = xreg(25);
2044                let x26 = xreg(26);
2045                let x27 = xreg(27);
2046                let x28 = xreg(28);
2047                let xzrwr = writable_zero_reg();
2048                let x21wr = writable_xreg(21);
2049                let x24wr = writable_xreg(24);
2050                let x27wr = writable_xreg(27);
2051                let again_label = sink.get_label();
2052                let keep_label = sink.get_label();
2053                let out_label = sink.get_label();
2054
2055                // again:
2056                sink.bind_label(again_label, &mut state.ctrl_plane);
2057
2058                if let Some(trap_code) = flags.trap_code() {
2059                    sink.add_trap(trap_code);
2060                }
2061
2062                // ldaxp x27, x21, [x25]
2063                sink.put4(enc_ldaxp(I64, x27wr, x21wr, x25));
2064
2065                // cmp x27, x26
2066                Inst::AluRRR {
2067                    alu_op: ALUOp::SubS,
2068                    size: OperandSize::Size64,
2069                    rd: xzrwr,
2070                    rn: x27,
2071                    rm: x26,
2072                }
2073                .emit(sink, emit_info, state);
2074
2075                // b.ne keep
2076                let br_keep_offset = sink.cur_offset();
2077                sink.put4(enc_conditional_br(
2078                    BranchTarget::Label(keep_label),
2079                    CondBrKind::Cond(Cond::Ne),
2080                ));
2081                sink.use_label_at_offset(br_keep_offset, keep_label, LabelUse::Branch19);
2082
2083                // cmp x21, x23
2084                Inst::AluRRR {
2085                    alu_op: ALUOp::SubS,
2086                    size: OperandSize::Size64,
2087                    rd: xzrwr,
2088                    rn: x21,
2089                    rm: x23,
2090                }
2091                .emit(sink, emit_info, state);
2092
2093                // b.ne keep
2094                let br_keep_offset = sink.cur_offset();
2095                sink.put4(enc_conditional_br(
2096                    BranchTarget::Label(keep_label),
2097                    CondBrKind::Cond(Cond::Ne),
2098                ));
2099                sink.use_label_at_offset(br_keep_offset, keep_label, LabelUse::Branch19);
2100
2101                if let Some(trap_code) = flags.trap_code() {
2102                    sink.add_trap(trap_code);
2103                }
2104
2105                // stlxp w24, x28, x22, [x25]
2106                sink.put4(enc_stlxp(I64, x24wr, x28, x22, x25));
2107
2108                // cbnz w24, again
2109                let br_again_offset = sink.cur_offset();
2110                sink.put4(enc_conditional_br(
2111                    BranchTarget::Label(again_label),
2112                    CondBrKind::NotZero(x24, OperandSize::Size64),
2113                ));
2114                sink.use_label_at_offset(br_again_offset, again_label, LabelUse::Branch19);
2115
2116                // b out
2117                let b_out_offset = sink.cur_offset();
2118                sink.put4(enc_jump26(
2119                    0b000101,
2120                    BranchTarget::Label(out_label).as_offset26_or_zero(),
2121                ));
2122                sink.use_label_at_offset(b_out_offset, out_label, LabelUse::Branch26);
2123
2124                // keep:
2125                sink.bind_label(keep_label, &mut state.ctrl_plane);
2126
2127                if let Some(trap_code) = flags.trap_code() {
2128                    sink.add_trap(trap_code);
2129                }
2130
2131                // stlxp w24, x27, x21, [x25]
2132                sink.put4(enc_stlxp(I64, x24wr, x27, x21, x25));
2133
2134                // cbnz w24, again
2135                let br_again_offset = sink.cur_offset();
2136                sink.put4(enc_conditional_br(
2137                    BranchTarget::Label(again_label),
2138                    CondBrKind::NotZero(x24, OperandSize::Size64),
2139                ));
2140                sink.use_label_at_offset(br_again_offset, again_label, LabelUse::Branch19);
2141
2142                // out:
2143                sink.bind_label(out_label, &mut state.ctrl_plane);
2144            }
2145            &Inst::LoadAcquire {
2146                access_ty,
2147                rt,
2148                rn,
2149                flags,
2150            } => {
2151                if let Some(trap_code) = flags.trap_code() {
2152                    sink.add_trap(trap_code);
2153                }
2154
2155                sink.put4(enc_ldar(access_ty, rt, rn));
2156            }
2157            &Inst::LoadAcquire128 {
2158                rt1,
2159                rt2,
2160                rn,
2161                scratch,
2162                flags,
2163            } => {
2164                let again_label = sink.get_label();
2165
2166                // again:
2167                sink.bind_label(again_label, &mut state.ctrl_plane);
2168
2169                if let Some(trap_code) = flags.trap_code() {
2170                    sink.add_trap(trap_code);
2171                }
2172
2173                // ldaxp rt1, rt2, [rn]
2174                sink.put4(enc_ldaxp(I64, rt1, rt2, rn));
2175
2176                if let Some(trap_code) = flags.trap_code() {
2177                    sink.add_trap(trap_code);
2178                }
2179
2180                // stxp scratch, rt1, rt2, [rn]
2181                sink.put4(enc_stxp(I64, scratch, rt1.to_reg(), rt2.to_reg(), rn));
2182
2183                // cbnz scratch, again.
2184                let br_again_offset = sink.cur_offset();
2185                sink.put4(enc_conditional_br(
2186                    BranchTarget::Label(again_label),
2187                    CondBrKind::NotZero(scratch.to_reg(), OperandSize::Size64),
2188                ));
2189                sink.use_label_at_offset(br_again_offset, again_label, LabelUse::Branch19);
2190            }
2191            &Inst::StoreRelease {
2192                access_ty,
2193                rt,
2194                rn,
2195                flags,
2196            } => {
2197                if let Some(trap_code) = flags.trap_code() {
2198                    sink.add_trap(trap_code);
2199                }
2200
2201                sink.put4(enc_stlr(access_ty, rt, rn));
2202            }
2203            &Inst::StoreRelease128 {
2204                rt1,
2205                rt2,
2206                rn,
2207                scratch,
2208                flags,
2209            } => {
2210                let again_label = sink.get_label();
2211
2212                // again:
2213                sink.bind_label(again_label, &mut state.ctrl_plane);
2214
2215                if let Some(trap_code) = flags.trap_code() {
2216                    sink.add_trap(trap_code);
2217                }
2218
2219                // ldxp xzr, scratch, [rn]
2220                sink.put4(enc_ldxp(I64, writable_zero_reg(), scratch, rn));
2221
2222                if let Some(trap_code) = flags.trap_code() {
2223                    sink.add_trap(trap_code);
2224                }
2225
2226                // stlxp scratch, rt1, rt2, [rn]
2227                sink.put4(enc_stlxp(I64, scratch, rt1, rt2, rn));
2228
2229                // cbnz scratch, again.
2230                let br_again_offset = sink.cur_offset();
2231                sink.put4(enc_conditional_br(
2232                    BranchTarget::Label(again_label),
2233                    CondBrKind::NotZero(scratch.to_reg(), OperandSize::Size64),
2234                ));
2235                sink.use_label_at_offset(br_again_offset, again_label, LabelUse::Branch19);
2236            }
2237            &Inst::Fence {} => {
2238                sink.put4(enc_dmb_ish()); // dmb ish
2239            }
2240            &Inst::Csdb {} => {
2241                sink.put4(0xd503229f);
2242            }
2243            &Inst::FpuMove32 { rd, rn } => {
2244                sink.put4(enc_fpurr(0b000_11110_00_1_000000_10000, rd, rn));
2245            }
2246            &Inst::FpuMove64 { rd, rn } => {
2247                sink.put4(enc_fpurr(0b000_11110_01_1_000000_10000, rd, rn));
2248            }
2249            &Inst::FpuMove128 { rd, rn } => {
2250                sink.put4(enc_vecmov(/* 16b = */ true, rd, rn));
2251            }
2252            &Inst::FpuMoveFromVec { rd, rn, idx, size } => {
2253                let (imm5, shift, mask) = match size.lane_size() {
2254                    ScalarSize::Size32 => (0b00100, 3, 0b011),
2255                    ScalarSize::Size64 => (0b01000, 4, 0b001),
2256                    _ => unimplemented!(),
2257                };
2258                debug_assert_eq!(idx & mask, idx);
2259                let imm5 = imm5 | ((idx as u32) << shift);
2260                sink.put4(
2261                    0b010_11110000_00000_000001_00000_00000
2262                        | (imm5 << 16)
2263                        | (machreg_to_vec(rn) << 5)
2264                        | machreg_to_vec(rd.to_reg()),
2265                );
2266            }
2267            &Inst::FpuExtend { rd, rn, size } => {
2268                sink.put4(enc_fpurr(
2269                    0b000_11110_00_1_000000_10000 | (size.ftype() << 12),
2270                    rd,
2271                    rn,
2272                ));
2273            }
2274            &Inst::FpuRR {
2275                fpu_op,
2276                size,
2277                rd,
2278                rn,
2279            } => {
2280                let top22 = match fpu_op {
2281                    FPUOp1::Abs => 0b000_11110_00_1_000001_10000,
2282                    FPUOp1::Neg => 0b000_11110_00_1_000010_10000,
2283                    FPUOp1::Sqrt => 0b000_11110_00_1_000011_10000,
2284                    FPUOp1::Cvt16To32 => {
2285                        debug_assert_eq!(size, ScalarSize::Size16);
2286                        0b000_11110_11_1_000100_10000
2287                    }
2288                    FPUOp1::Cvt32To64 => {
2289                        debug_assert_eq!(size, ScalarSize::Size32);
2290                        0b000_11110_00_1_000101_10000
2291                    }
2292                    FPUOp1::Cvt64To32 => {
2293                        debug_assert_eq!(size, ScalarSize::Size64);
2294                        0b000_11110_01_1_000100_10000
2295                    }
2296                };
2297                let top22 = top22 | size.ftype() << 12;
2298                sink.put4(enc_fpurr(top22, rd, rn));
2299            }
2300            &Inst::FpuRRR {
2301                fpu_op,
2302                size,
2303                rd,
2304                rn,
2305                rm,
2306            } => {
2307                let top22 = match fpu_op {
2308                    FPUOp2::Add => 0b000_11110_00_1_00000_001010,
2309                    FPUOp2::Sub => 0b000_11110_00_1_00000_001110,
2310                    FPUOp2::Mul => 0b000_11110_00_1_00000_000010,
2311                    FPUOp2::Div => 0b000_11110_00_1_00000_000110,
2312                    FPUOp2::Max => 0b000_11110_00_1_00000_010010,
2313                    FPUOp2::Min => 0b000_11110_00_1_00000_010110,
2314                };
2315                let top22 = top22 | size.ftype() << 12;
2316                sink.put4(enc_fpurrr(top22, rd, rn, rm));
2317            }
2318            &Inst::FpuRRI { fpu_op, rd, rn } => match fpu_op {
2319                FPUOpRI::UShr32(imm) => {
2320                    debug_assert_eq!(32, imm.lane_size_in_bits);
2321                    sink.put4(
2322                        0b0_0_1_011110_0000000_00_0_0_0_1_00000_00000
2323                            | imm.enc() << 16
2324                            | machreg_to_vec(rn) << 5
2325                            | machreg_to_vec(rd.to_reg()),
2326                    )
2327                }
2328                FPUOpRI::UShr64(imm) => {
2329                    debug_assert_eq!(64, imm.lane_size_in_bits);
2330                    sink.put4(
2331                        0b01_1_111110_0000000_00_0_0_0_1_00000_00000
2332                            | imm.enc() << 16
2333                            | machreg_to_vec(rn) << 5
2334                            | machreg_to_vec(rd.to_reg()),
2335                    )
2336                }
2337            },
2338            &Inst::FpuRRIMod { fpu_op, rd, ri, rn } => {
2339                debug_assert_eq!(rd.to_reg(), ri);
2340                match fpu_op {
2341                    FPUOpRIMod::Sli64(imm) => {
2342                        debug_assert_eq!(64, imm.lane_size_in_bits);
2343                        sink.put4(
2344                            0b01_1_111110_0000000_010101_00000_00000
2345                                | imm.enc() << 16
2346                                | machreg_to_vec(rn) << 5
2347                                | machreg_to_vec(rd.to_reg()),
2348                        )
2349                    }
2350                    FPUOpRIMod::Sli32(imm) => {
2351                        debug_assert_eq!(32, imm.lane_size_in_bits);
2352                        sink.put4(
2353                            0b0_0_1_011110_0000000_010101_00000_00000
2354                                | imm.enc() << 16
2355                                | machreg_to_vec(rn) << 5
2356                                | machreg_to_vec(rd.to_reg()),
2357                        )
2358                    }
2359                }
2360            }
2361            &Inst::FpuRRRR {
2362                fpu_op,
2363                size,
2364                rd,
2365                rn,
2366                rm,
2367                ra,
2368            } => {
2369                let top17 = match fpu_op {
2370                    FPUOp3::MAdd => 0b000_11111_00_0_00000_0,
2371                    FPUOp3::MSub => 0b000_11111_00_0_00000_1,
2372                    FPUOp3::NMAdd => 0b000_11111_00_1_00000_0,
2373                    FPUOp3::NMSub => 0b000_11111_00_1_00000_1,
2374                };
2375                let top17 = top17 | size.ftype() << 7;
2376                sink.put4(enc_fpurrrr(top17, rd, rn, rm, ra));
2377            }
2378            &Inst::VecMisc { op, rd, rn, size } => {
2379                let (q, enc_size) = size.enc_size();
2380                let (u, bits_12_16, size) = match op {
2381                    VecMisc2::Not => (0b1, 0b00101, 0b00),
2382                    VecMisc2::Neg => (0b1, 0b01011, enc_size),
2383                    VecMisc2::Abs => (0b0, 0b01011, enc_size),
2384                    VecMisc2::Fabs => {
2385                        debug_assert!(
2386                            size == VectorSize::Size32x2
2387                                || size == VectorSize::Size32x4
2388                                || size == VectorSize::Size64x2
2389                        );
2390                        (0b0, 0b01111, enc_size)
2391                    }
2392                    VecMisc2::Fneg => {
2393                        debug_assert!(
2394                            size == VectorSize::Size32x2
2395                                || size == VectorSize::Size32x4
2396                                || size == VectorSize::Size64x2
2397                        );
2398                        (0b1, 0b01111, enc_size)
2399                    }
2400                    VecMisc2::Fsqrt => {
2401                        debug_assert!(
2402                            size == VectorSize::Size32x2
2403                                || size == VectorSize::Size32x4
2404                                || size == VectorSize::Size64x2
2405                        );
2406                        (0b1, 0b11111, enc_size)
2407                    }
2408                    VecMisc2::Rev16 => {
2409                        debug_assert_eq!(size, VectorSize::Size8x16);
2410                        (0b0, 0b00001, enc_size)
2411                    }
2412                    VecMisc2::Rev32 => {
2413                        debug_assert!(size == VectorSize::Size8x16 || size == VectorSize::Size16x8);
2414                        (0b1, 0b00000, enc_size)
2415                    }
2416                    VecMisc2::Rev64 => {
2417                        debug_assert!(
2418                            size == VectorSize::Size8x16
2419                                || size == VectorSize::Size16x8
2420                                || size == VectorSize::Size32x4
2421                        );
2422                        (0b0, 0b00000, enc_size)
2423                    }
2424                    VecMisc2::Fcvtzs => {
2425                        debug_assert!(
2426                            size == VectorSize::Size32x2
2427                                || size == VectorSize::Size32x4
2428                                || size == VectorSize::Size64x2
2429                        );
2430                        (0b0, 0b11011, enc_size)
2431                    }
2432                    VecMisc2::Fcvtzu => {
2433                        debug_assert!(
2434                            size == VectorSize::Size32x2
2435                                || size == VectorSize::Size32x4
2436                                || size == VectorSize::Size64x2
2437                        );
2438                        (0b1, 0b11011, enc_size)
2439                    }
2440                    VecMisc2::Scvtf => {
2441                        debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
2442                        (0b0, 0b11101, enc_size & 0b1)
2443                    }
2444                    VecMisc2::Ucvtf => {
2445                        debug_assert!(size == VectorSize::Size32x4 || size == VectorSize::Size64x2);
2446                        (0b1, 0b11101, enc_size & 0b1)
2447                    }
2448                    VecMisc2::Frintn => {
2449                        debug_assert!(
2450                            size == VectorSize::Size32x2
2451                                || size == VectorSize::Size32x4
2452                                || size == VectorSize::Size64x2
2453                        );
2454                        (0b0, 0b11000, enc_size & 0b01)
2455                    }
2456                    VecMisc2::Frintz => {
2457                        debug_assert!(
2458                            size == VectorSize::Size32x2
2459                                || size == VectorSize::Size32x4
2460                                || size == VectorSize::Size64x2
2461                        );
2462                        (0b0, 0b11001, enc_size)
2463                    }
2464                    VecMisc2::Frintm => {
2465                        debug_assert!(
2466                            size == VectorSize::Size32x2
2467                                || size == VectorSize::Size32x4
2468                                || size == VectorSize::Size64x2
2469                        );
2470                        (0b0, 0b11001, enc_size & 0b01)
2471                    }
2472                    VecMisc2::Frintp => {
2473                        debug_assert!(
2474                            size == VectorSize::Size32x2
2475                                || size == VectorSize::Size32x4
2476                                || size == VectorSize::Size64x2
2477                        );
2478                        (0b0, 0b11000, enc_size)
2479                    }
2480                    VecMisc2::Cnt => {
2481                        debug_assert!(size == VectorSize::Size8x8 || size == VectorSize::Size8x16);
2482                        (0b0, 0b00101, enc_size)
2483                    }
2484                    VecMisc2::Cmeq0 => (0b0, 0b01001, enc_size),
2485                    VecMisc2::Cmge0 => (0b1, 0b01000, enc_size),
2486                    VecMisc2::Cmgt0 => (0b0, 0b01000, enc_size),
2487                    VecMisc2::Cmle0 => (0b1, 0b01001, enc_size),
2488                    VecMisc2::Cmlt0 => (0b0, 0b01010, enc_size),
2489                    VecMisc2::Fcmeq0 => {
2490                        debug_assert!(
2491                            size == VectorSize::Size32x2
2492                                || size == VectorSize::Size32x4
2493                                || size == VectorSize::Size64x2
2494                        );
2495                        (0b0, 0b01101, enc_size)
2496                    }
2497                    VecMisc2::Fcmge0 => {
2498                        debug_assert!(
2499                            size == VectorSize::Size32x2
2500                                || size == VectorSize::Size32x4
2501                                || size == VectorSize::Size64x2
2502                        );
2503                        (0b1, 0b01100, enc_size)
2504                    }
2505                    VecMisc2::Fcmgt0 => {
2506                        debug_assert!(
2507                            size == VectorSize::Size32x2
2508                                || size == VectorSize::Size32x4
2509                                || size == VectorSize::Size64x2
2510                        );
2511                        (0b0, 0b01100, enc_size)
2512                    }
2513                    VecMisc2::Fcmle0 => {
2514                        debug_assert!(
2515                            size == VectorSize::Size32x2
2516                                || size == VectorSize::Size32x4
2517                                || size == VectorSize::Size64x2
2518                        );
2519                        (0b1, 0b01101, enc_size)
2520                    }
2521                    VecMisc2::Fcmlt0 => {
2522                        debug_assert!(
2523                            size == VectorSize::Size32x2
2524                                || size == VectorSize::Size32x4
2525                                || size == VectorSize::Size64x2
2526                        );
2527                        (0b0, 0b01110, enc_size)
2528                    }
2529                };
2530                sink.put4(enc_vec_rr_misc((q << 1) | u, size, bits_12_16, rd, rn));
2531            }
2532            &Inst::VecLanes { op, rd, rn, size } => {
2533                let (q, size) = match size {
2534                    VectorSize::Size8x8 => (0b0, 0b00),
2535                    VectorSize::Size8x16 => (0b1, 0b00),
2536                    VectorSize::Size16x4 => (0b0, 0b01),
2537                    VectorSize::Size16x8 => (0b1, 0b01),
2538                    VectorSize::Size32x4 => (0b1, 0b10),
2539                    _ => unreachable!(),
2540                };
2541                let (u, opcode) = match op {
2542                    VecLanesOp::Uminv => (0b1, 0b11010),
2543                    VecLanesOp::Addv => (0b0, 0b11011),
2544                };
2545                sink.put4(enc_vec_lanes(q, u, size, opcode, rd, rn));
2546            }
2547            &Inst::VecShiftImm {
2548                op,
2549                rd,
2550                rn,
2551                size,
2552                imm,
2553            } => {
2554                let (is_shr, mut template) = match op {
2555                    VecShiftImmOp::Ushr => (true, 0b_001_011110_0000_000_000001_00000_00000_u32),
2556                    VecShiftImmOp::Sshr => (true, 0b_000_011110_0000_000_000001_00000_00000_u32),
2557                    VecShiftImmOp::Shl => (false, 0b_000_011110_0000_000_010101_00000_00000_u32),
2558                };
2559                if size.is_128bits() {
2560                    template |= 0b1 << 30;
2561                }
2562                let imm = imm as u32;
2563                // Deal with the somewhat strange encoding scheme for, and limits on,
2564                // the shift amount.
2565                let immh_immb = match (size.lane_size(), is_shr) {
2566                    (ScalarSize::Size64, true) if imm >= 1 && imm <= 64 => {
2567                        0b_1000_000_u32 | (64 - imm)
2568                    }
2569                    (ScalarSize::Size32, true) if imm >= 1 && imm <= 32 => {
2570                        0b_0100_000_u32 | (32 - imm)
2571                    }
2572                    (ScalarSize::Size16, true) if imm >= 1 && imm <= 16 => {
2573                        0b_0010_000_u32 | (16 - imm)
2574                    }
2575                    (ScalarSize::Size8, true) if imm >= 1 && imm <= 8 => {
2576                        0b_0001_000_u32 | (8 - imm)
2577                    }
2578                    (ScalarSize::Size64, false) if imm <= 63 => 0b_1000_000_u32 | imm,
2579                    (ScalarSize::Size32, false) if imm <= 31 => 0b_0100_000_u32 | imm,
2580                    (ScalarSize::Size16, false) if imm <= 15 => 0b_0010_000_u32 | imm,
2581                    (ScalarSize::Size8, false) if imm <= 7 => 0b_0001_000_u32 | imm,
2582                    _ => panic!(
2583                        "aarch64: Inst::VecShiftImm: emit: invalid op/size/imm {op:?}, {size:?}, {imm:?}"
2584                    ),
2585                };
2586                let rn_enc = machreg_to_vec(rn);
2587                let rd_enc = machreg_to_vec(rd.to_reg());
2588                sink.put4(template | (immh_immb << 16) | (rn_enc << 5) | rd_enc);
2589            }
2590            &Inst::VecShiftImmMod {
2591                op,
2592                rd,
2593                ri,
2594                rn,
2595                size,
2596                imm,
2597            } => {
2598                debug_assert_eq!(rd.to_reg(), ri);
2599                let (is_shr, mut template) = match op {
2600                    VecShiftImmModOp::Sli => (false, 0b_001_011110_0000_000_010101_00000_00000_u32),
2601                };
2602                if size.is_128bits() {
2603                    template |= 0b1 << 30;
2604                }
2605                let imm = imm as u32;
2606                // Deal with the somewhat strange encoding scheme for, and limits on,
2607                // the shift amount.
2608                let immh_immb = match (size.lane_size(), is_shr) {
2609                    (ScalarSize::Size64, true) if imm >= 1 && imm <= 64 => {
2610                        0b_1000_000_u32 | (64 - imm)
2611                    }
2612                    (ScalarSize::Size32, true) if imm >= 1 && imm <= 32 => {
2613                        0b_0100_000_u32 | (32 - imm)
2614                    }
2615                    (ScalarSize::Size16, true) if imm >= 1 && imm <= 16 => {
2616                        0b_0010_000_u32 | (16 - imm)
2617                    }
2618                    (ScalarSize::Size8, true) if imm >= 1 && imm <= 8 => {
2619                        0b_0001_000_u32 | (8 - imm)
2620                    }
2621                    (ScalarSize::Size64, false) if imm <= 63 => 0b_1000_000_u32 | imm,
2622                    (ScalarSize::Size32, false) if imm <= 31 => 0b_0100_000_u32 | imm,
2623                    (ScalarSize::Size16, false) if imm <= 15 => 0b_0010_000_u32 | imm,
2624                    (ScalarSize::Size8, false) if imm <= 7 => 0b_0001_000_u32 | imm,
2625                    _ => panic!(
2626                        "aarch64: Inst::VecShiftImmMod: emit: invalid op/size/imm {op:?}, {size:?}, {imm:?}"
2627                    ),
2628                };
2629                let rn_enc = machreg_to_vec(rn);
2630                let rd_enc = machreg_to_vec(rd.to_reg());
2631                sink.put4(template | (immh_immb << 16) | (rn_enc << 5) | rd_enc);
2632            }
2633            &Inst::VecExtract { rd, rn, rm, imm4 } => {
2634                if imm4 < 16 {
2635                    let template = 0b_01_101110_000_00000_0_0000_0_00000_00000_u32;
2636                    let rm_enc = machreg_to_vec(rm);
2637                    let rn_enc = machreg_to_vec(rn);
2638                    let rd_enc = machreg_to_vec(rd.to_reg());
2639                    sink.put4(
2640                        template | (rm_enc << 16) | ((imm4 as u32) << 11) | (rn_enc << 5) | rd_enc,
2641                    );
2642                } else {
2643                    panic!("aarch64: Inst::VecExtract: emit: invalid extract index {imm4}");
2644                }
2645            }
2646            &Inst::VecTbl { rd, rn, rm } => {
2647                sink.put4(enc_tbl(/* is_extension = */ false, 0b00, rd, rn, rm));
2648            }
2649            &Inst::VecTblExt { rd, ri, rn, rm } => {
2650                debug_assert_eq!(rd.to_reg(), ri);
2651                sink.put4(enc_tbl(/* is_extension = */ true, 0b00, rd, rn, rm));
2652            }
2653            &Inst::VecTbl2 { rd, rn, rn2, rm } => {
2654                assert_eq!(machreg_to_vec(rn2), (machreg_to_vec(rn) + 1) % 32);
2655                sink.put4(enc_tbl(/* is_extension = */ false, 0b01, rd, rn, rm));
2656            }
2657            &Inst::VecTbl2Ext {
2658                rd,
2659                ri,
2660                rn,
2661                rn2,
2662                rm,
2663            } => {
2664                debug_assert_eq!(rd.to_reg(), ri);
2665                assert_eq!(machreg_to_vec(rn2), (machreg_to_vec(rn) + 1) % 32);
2666                sink.put4(enc_tbl(/* is_extension = */ true, 0b01, rd, rn, rm));
2667            }
2668            &Inst::FpuCmp { size, rn, rm } => {
2669                sink.put4(enc_fcmp(size, rn, rm));
2670            }
2671            &Inst::FpuToInt { op, rd, rn } => {
2672                let top16 = match op {
2673                    // FCVTZS (32/32-bit)
2674                    FpuToIntOp::F32ToI32 => 0b000_11110_00_1_11_000,
2675                    // FCVTZU (32/32-bit)
2676                    FpuToIntOp::F32ToU32 => 0b000_11110_00_1_11_001,
2677                    // FCVTZS (32/64-bit)
2678                    FpuToIntOp::F32ToI64 => 0b100_11110_00_1_11_000,
2679                    // FCVTZU (32/64-bit)
2680                    FpuToIntOp::F32ToU64 => 0b100_11110_00_1_11_001,
2681                    // FCVTZS (64/32-bit)
2682                    FpuToIntOp::F64ToI32 => 0b000_11110_01_1_11_000,
2683                    // FCVTZU (64/32-bit)
2684                    FpuToIntOp::F64ToU32 => 0b000_11110_01_1_11_001,
2685                    // FCVTZS (64/64-bit)
2686                    FpuToIntOp::F64ToI64 => 0b100_11110_01_1_11_000,
2687                    // FCVTZU (64/64-bit)
2688                    FpuToIntOp::F64ToU64 => 0b100_11110_01_1_11_001,
2689                };
2690                sink.put4(enc_fputoint(top16, rd, rn));
2691            }
2692            &Inst::IntToFpu { op, rd, rn } => {
2693                let top16 = match op {
2694                    // SCVTF (32/32-bit)
2695                    IntToFpuOp::I32ToF32 => 0b000_11110_00_1_00_010,
2696                    // UCVTF (32/32-bit)
2697                    IntToFpuOp::U32ToF32 => 0b000_11110_00_1_00_011,
2698                    // SCVTF (64/32-bit)
2699                    IntToFpuOp::I64ToF32 => 0b100_11110_00_1_00_010,
2700                    // UCVTF (64/32-bit)
2701                    IntToFpuOp::U64ToF32 => 0b100_11110_00_1_00_011,
2702                    // SCVTF (32/64-bit)
2703                    IntToFpuOp::I32ToF64 => 0b000_11110_01_1_00_010,
2704                    // UCVTF (32/64-bit)
2705                    IntToFpuOp::U32ToF64 => 0b000_11110_01_1_00_011,
2706                    // SCVTF (64/64-bit)
2707                    IntToFpuOp::I64ToF64 => 0b100_11110_01_1_00_010,
2708                    // UCVTF (64/64-bit)
2709                    IntToFpuOp::U64ToF64 => 0b100_11110_01_1_00_011,
2710                };
2711                sink.put4(enc_inttofpu(top16, rd, rn));
2712            }
2713            &Inst::FpuCSel16 { rd, rn, rm, cond } => {
2714                sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size16));
2715            }
2716            &Inst::FpuCSel32 { rd, rn, rm, cond } => {
2717                sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size32));
2718            }
2719            &Inst::FpuCSel64 { rd, rn, rm, cond } => {
2720                sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size64));
2721            }
2722            &Inst::FpuRound { op, rd, rn } => {
2723                let top22 = match op {
2724                    FpuRoundMode::Minus32 => 0b000_11110_00_1_001_010_10000,
2725                    FpuRoundMode::Minus64 => 0b000_11110_01_1_001_010_10000,
2726                    FpuRoundMode::Plus32 => 0b000_11110_00_1_001_001_10000,
2727                    FpuRoundMode::Plus64 => 0b000_11110_01_1_001_001_10000,
2728                    FpuRoundMode::Zero32 => 0b000_11110_00_1_001_011_10000,
2729                    FpuRoundMode::Zero64 => 0b000_11110_01_1_001_011_10000,
2730                    FpuRoundMode::Nearest32 => 0b000_11110_00_1_001_000_10000,
2731                    FpuRoundMode::Nearest64 => 0b000_11110_01_1_001_000_10000,
2732                };
2733                sink.put4(enc_fround(top22, rd, rn));
2734            }
2735            &Inst::MovToFpu { rd, rn, size } => {
2736                let template = match size {
2737                    ScalarSize::Size16 => 0b000_11110_11_1_00_111_000000_00000_00000,
2738                    ScalarSize::Size32 => 0b000_11110_00_1_00_111_000000_00000_00000,
2739                    ScalarSize::Size64 => 0b100_11110_01_1_00_111_000000_00000_00000,
2740                    _ => unreachable!(),
2741                };
2742                sink.put4(template | (machreg_to_gpr(rn) << 5) | machreg_to_vec(rd.to_reg()));
2743            }
2744            &Inst::FpuMoveFPImm { rd, imm, size } => {
2745                sink.put4(
2746                    0b000_11110_00_1_00_000_000100_00000_00000
2747                        | size.ftype() << 22
2748                        | ((imm.enc_bits() as u32) << 13)
2749                        | machreg_to_vec(rd.to_reg()),
2750                );
2751            }
2752            &Inst::MovToVec {
2753                rd,
2754                ri,
2755                rn,
2756                idx,
2757                size,
2758            } => {
2759                debug_assert_eq!(rd.to_reg(), ri);
2760                let (imm5, shift) = match size.lane_size() {
2761                    ScalarSize::Size8 => (0b00001, 1),
2762                    ScalarSize::Size16 => (0b00010, 2),
2763                    ScalarSize::Size32 => (0b00100, 3),
2764                    ScalarSize::Size64 => (0b01000, 4),
2765                    _ => unreachable!(),
2766                };
2767                debug_assert_eq!(idx & (0b11111 >> shift), idx);
2768                let imm5 = imm5 | ((idx as u32) << shift);
2769                sink.put4(
2770                    0b010_01110000_00000_0_0011_1_00000_00000
2771                        | (imm5 << 16)
2772                        | (machreg_to_gpr(rn) << 5)
2773                        | machreg_to_vec(rd.to_reg()),
2774                );
2775            }
2776            &Inst::MovFromVec { rd, rn, idx, size } => {
2777                let (q, imm5, shift, mask) = match size {
2778                    ScalarSize::Size8 => (0b0, 0b00001, 1, 0b1111),
2779                    ScalarSize::Size16 => (0b0, 0b00010, 2, 0b0111),
2780                    ScalarSize::Size32 => (0b0, 0b00100, 3, 0b0011),
2781                    ScalarSize::Size64 => (0b1, 0b01000, 4, 0b0001),
2782                    _ => panic!("Unexpected scalar FP operand size: {size:?}"),
2783                };
2784                debug_assert_eq!(idx & mask, idx);
2785                let imm5 = imm5 | ((idx as u32) << shift);
2786                sink.put4(
2787                    0b000_01110000_00000_0_0111_1_00000_00000
2788                        | (q << 30)
2789                        | (imm5 << 16)
2790                        | (machreg_to_vec(rn) << 5)
2791                        | machreg_to_gpr(rd.to_reg()),
2792                );
2793            }
2794            &Inst::MovFromVecSigned {
2795                rd,
2796                rn,
2797                idx,
2798                size,
2799                scalar_size,
2800            } => {
2801                let (imm5, shift, half) = match size {
2802                    VectorSize::Size8x8 => (0b00001, 1, true),
2803                    VectorSize::Size8x16 => (0b00001, 1, false),
2804                    VectorSize::Size16x4 => (0b00010, 2, true),
2805                    VectorSize::Size16x8 => (0b00010, 2, false),
2806                    VectorSize::Size32x2 => {
2807                        debug_assert_ne!(scalar_size, OperandSize::Size32);
2808                        (0b00100, 3, true)
2809                    }
2810                    VectorSize::Size32x4 => {
2811                        debug_assert_ne!(scalar_size, OperandSize::Size32);
2812                        (0b00100, 3, false)
2813                    }
2814                    _ => panic!("Unexpected vector operand size"),
2815                };
2816                debug_assert_eq!(idx & (0b11111 >> (half as u32 + shift)), idx);
2817                let imm5 = imm5 | ((idx as u32) << shift);
2818                sink.put4(
2819                    0b000_01110000_00000_0_0101_1_00000_00000
2820                        | (scalar_size.is64() as u32) << 30
2821                        | (imm5 << 16)
2822                        | (machreg_to_vec(rn) << 5)
2823                        | machreg_to_gpr(rd.to_reg()),
2824                );
2825            }
2826            &Inst::VecDup { rd, rn, size } => {
2827                let q = size.is_128bits() as u32;
2828                let imm5 = match size.lane_size() {
2829                    ScalarSize::Size8 => 0b00001,
2830                    ScalarSize::Size16 => 0b00010,
2831                    ScalarSize::Size32 => 0b00100,
2832                    ScalarSize::Size64 => 0b01000,
2833                    _ => unreachable!(),
2834                };
2835                sink.put4(
2836                    0b0_0_0_01110000_00000_000011_00000_00000
2837                        | (q << 30)
2838                        | (imm5 << 16)
2839                        | (machreg_to_gpr(rn) << 5)
2840                        | machreg_to_vec(rd.to_reg()),
2841                );
2842            }
2843            &Inst::VecDupFromFpu { rd, rn, size, lane } => {
2844                let q = size.is_128bits() as u32;
2845                let imm5 = match size.lane_size() {
2846                    ScalarSize::Size8 => {
2847                        assert!(lane < 16);
2848                        0b00001 | (u32::from(lane) << 1)
2849                    }
2850                    ScalarSize::Size16 => {
2851                        assert!(lane < 8);
2852                        0b00010 | (u32::from(lane) << 2)
2853                    }
2854                    ScalarSize::Size32 => {
2855                        assert!(lane < 4);
2856                        0b00100 | (u32::from(lane) << 3)
2857                    }
2858                    ScalarSize::Size64 => {
2859                        assert!(lane < 2);
2860                        0b01000 | (u32::from(lane) << 4)
2861                    }
2862                    _ => unimplemented!(),
2863                };
2864                sink.put4(
2865                    0b000_01110000_00000_000001_00000_00000
2866                        | (q << 30)
2867                        | (imm5 << 16)
2868                        | (machreg_to_vec(rn) << 5)
2869                        | machreg_to_vec(rd.to_reg()),
2870                );
2871            }
2872            &Inst::VecDupFPImm { rd, imm, size } => {
2873                let imm = imm.enc_bits();
2874                let op = match size.lane_size() {
2875                    ScalarSize::Size32 => 0,
2876                    ScalarSize::Size64 => 1,
2877                    _ => unimplemented!(),
2878                };
2879                let q_op = op | ((size.is_128bits() as u32) << 1);
2880
2881                sink.put4(enc_asimd_mod_imm(rd, q_op, 0b1111, imm));
2882            }
2883            &Inst::VecDupImm {
2884                rd,
2885                imm,
2886                invert,
2887                size,
2888            } => {
2889                let (imm, shift, shift_ones) = imm.value();
2890                let (op, cmode) = match size.lane_size() {
2891                    ScalarSize::Size8 => {
2892                        assert!(!invert);
2893                        assert_eq!(shift, 0);
2894
2895                        (0, 0b1110)
2896                    }
2897                    ScalarSize::Size16 => {
2898                        let s = shift & 8;
2899
2900                        assert!(!shift_ones);
2901                        assert_eq!(s, shift);
2902
2903                        (invert as u32, 0b1000 | (s >> 2))
2904                    }
2905                    ScalarSize::Size32 => {
2906                        if shift_ones {
2907                            assert!(shift == 8 || shift == 16);
2908
2909                            (invert as u32, 0b1100 | (shift >> 4))
2910                        } else {
2911                            let s = shift & 24;
2912
2913                            assert_eq!(s, shift);
2914
2915                            (invert as u32, 0b0000 | (s >> 2))
2916                        }
2917                    }
2918                    ScalarSize::Size64 => {
2919                        assert!(!invert);
2920                        assert_eq!(shift, 0);
2921
2922                        (1, 0b1110)
2923                    }
2924                    _ => unreachable!(),
2925                };
2926                let q_op = op | ((size.is_128bits() as u32) << 1);
2927
2928                sink.put4(enc_asimd_mod_imm(rd, q_op, cmode, imm));
2929            }
2930            &Inst::VecExtend {
2931                t,
2932                rd,
2933                rn,
2934                high_half,
2935                lane_size,
2936            } => {
2937                let immh = match lane_size {
2938                    ScalarSize::Size16 => 0b001,
2939                    ScalarSize::Size32 => 0b010,
2940                    ScalarSize::Size64 => 0b100,
2941                    _ => panic!("Unexpected VecExtend to lane size of {lane_size:?}"),
2942                };
2943                let u = match t {
2944                    VecExtendOp::Sxtl => 0b0,
2945                    VecExtendOp::Uxtl => 0b1,
2946                };
2947                sink.put4(
2948                    0b000_011110_0000_000_101001_00000_00000
2949                        | ((high_half as u32) << 30)
2950                        | (u << 29)
2951                        | (immh << 19)
2952                        | (machreg_to_vec(rn) << 5)
2953                        | machreg_to_vec(rd.to_reg()),
2954                );
2955            }
2956            &Inst::VecRRLong {
2957                op,
2958                rd,
2959                rn,
2960                high_half,
2961            } => {
2962                let (u, size, bits_12_16) = match op {
2963                    VecRRLongOp::Fcvtl16 => (0b0, 0b00, 0b10111),
2964                    VecRRLongOp::Fcvtl32 => (0b0, 0b01, 0b10111),
2965                    VecRRLongOp::Shll8 => (0b1, 0b00, 0b10011),
2966                    VecRRLongOp::Shll16 => (0b1, 0b01, 0b10011),
2967                    VecRRLongOp::Shll32 => (0b1, 0b10, 0b10011),
2968                };
2969
2970                sink.put4(enc_vec_rr_misc(
2971                    ((high_half as u32) << 1) | u,
2972                    size,
2973                    bits_12_16,
2974                    rd,
2975                    rn,
2976                ));
2977            }
2978            &Inst::VecRRNarrowLow {
2979                op,
2980                rd,
2981                rn,
2982                lane_size,
2983            }
2984            | &Inst::VecRRNarrowHigh {
2985                op,
2986                rd,
2987                rn,
2988                lane_size,
2989                ..
2990            } => {
2991                let high_half = match self {
2992                    &Inst::VecRRNarrowLow { .. } => false,
2993                    &Inst::VecRRNarrowHigh { .. } => true,
2994                    _ => unreachable!(),
2995                };
2996
2997                let size = match lane_size {
2998                    ScalarSize::Size8 => 0b00,
2999                    ScalarSize::Size16 => 0b01,
3000                    ScalarSize::Size32 => 0b10,
3001                    _ => panic!("unsupported size: {lane_size:?}"),
3002                };
3003
3004                // Floats use a single bit, to encode either half or single.
3005                let size = match op {
3006                    VecRRNarrowOp::Fcvtn => size >> 1,
3007                    _ => size,
3008                };
3009
3010                let (u, bits_12_16) = match op {
3011                    VecRRNarrowOp::Xtn => (0b0, 0b10010),
3012                    VecRRNarrowOp::Sqxtn => (0b0, 0b10100),
3013                    VecRRNarrowOp::Sqxtun => (0b1, 0b10010),
3014                    VecRRNarrowOp::Uqxtn => (0b1, 0b10100),
3015                    VecRRNarrowOp::Fcvtn => (0b0, 0b10110),
3016                };
3017
3018                sink.put4(enc_vec_rr_misc(
3019                    ((high_half as u32) << 1) | u,
3020                    size,
3021                    bits_12_16,
3022                    rd,
3023                    rn,
3024                ));
3025            }
3026            &Inst::VecMovElement {
3027                rd,
3028                ri,
3029                rn,
3030                dest_idx,
3031                src_idx,
3032                size,
3033            } => {
3034                debug_assert_eq!(rd.to_reg(), ri);
3035                let (imm5, shift) = match size.lane_size() {
3036                    ScalarSize::Size8 => (0b00001, 1),
3037                    ScalarSize::Size16 => (0b00010, 2),
3038                    ScalarSize::Size32 => (0b00100, 3),
3039                    ScalarSize::Size64 => (0b01000, 4),
3040                    _ => unreachable!(),
3041                };
3042                let mask = 0b11111 >> shift;
3043                debug_assert_eq!(dest_idx & mask, dest_idx);
3044                debug_assert_eq!(src_idx & mask, src_idx);
3045                let imm4 = (src_idx as u32) << (shift - 1);
3046                let imm5 = imm5 | ((dest_idx as u32) << shift);
3047                sink.put4(
3048                    0b011_01110000_00000_0_0000_1_00000_00000
3049                        | (imm5 << 16)
3050                        | (imm4 << 11)
3051                        | (machreg_to_vec(rn) << 5)
3052                        | machreg_to_vec(rd.to_reg()),
3053                );
3054            }
3055            &Inst::VecRRPair { op, rd, rn } => {
3056                let bits_12_16 = match op {
3057                    VecPairOp::Addp => 0b11011,
3058                };
3059
3060                sink.put4(enc_vec_rr_pair(bits_12_16, rd, rn));
3061            }
3062            &Inst::VecRRRLong {
3063                rd,
3064                rn,
3065                rm,
3066                alu_op,
3067                high_half,
3068            } => {
3069                let (u, size, bit14) = match alu_op {
3070                    VecRRRLongOp::Smull8 => (0b0, 0b00, 0b1),
3071                    VecRRRLongOp::Smull16 => (0b0, 0b01, 0b1),
3072                    VecRRRLongOp::Smull32 => (0b0, 0b10, 0b1),
3073                    VecRRRLongOp::Umull8 => (0b1, 0b00, 0b1),
3074                    VecRRRLongOp::Umull16 => (0b1, 0b01, 0b1),
3075                    VecRRRLongOp::Umull32 => (0b1, 0b10, 0b1),
3076                };
3077                sink.put4(enc_vec_rrr_long(
3078                    high_half as u32,
3079                    u,
3080                    size,
3081                    bit14,
3082                    rm,
3083                    rn,
3084                    rd,
3085                ));
3086            }
3087            &Inst::VecRRRLongMod {
3088                rd,
3089                ri,
3090                rn,
3091                rm,
3092                alu_op,
3093                high_half,
3094            } => {
3095                debug_assert_eq!(rd.to_reg(), ri);
3096                let (u, size, bit14) = match alu_op {
3097                    VecRRRLongModOp::Umlal8 => (0b1, 0b00, 0b0),
3098                    VecRRRLongModOp::Umlal16 => (0b1, 0b01, 0b0),
3099                    VecRRRLongModOp::Umlal32 => (0b1, 0b10, 0b0),
3100                };
3101                sink.put4(enc_vec_rrr_long(
3102                    high_half as u32,
3103                    u,
3104                    size,
3105                    bit14,
3106                    rm,
3107                    rn,
3108                    rd,
3109                ));
3110            }
3111            &Inst::VecRRPairLong { op, rd, rn } => {
3112                let (u, size) = match op {
3113                    VecRRPairLongOp::Saddlp8 => (0b0, 0b0),
3114                    VecRRPairLongOp::Uaddlp8 => (0b1, 0b0),
3115                    VecRRPairLongOp::Saddlp16 => (0b0, 0b1),
3116                    VecRRPairLongOp::Uaddlp16 => (0b1, 0b1),
3117                };
3118
3119                sink.put4(enc_vec_rr_pair_long(u, size, rd, rn));
3120            }
3121            &Inst::VecRRR {
3122                rd,
3123                rn,
3124                rm,
3125                alu_op,
3126                size,
3127            } => {
3128                let (q, enc_size) = size.enc_size();
3129                let is_float = match alu_op {
3130                    VecALUOp::Fcmeq
3131                    | VecALUOp::Fcmgt
3132                    | VecALUOp::Fcmge
3133                    | VecALUOp::Fadd
3134                    | VecALUOp::Fsub
3135                    | VecALUOp::Fdiv
3136                    | VecALUOp::Fmax
3137                    | VecALUOp::Fmin
3138                    | VecALUOp::Fmul => true,
3139                    _ => false,
3140                };
3141
3142                let (top11, bit15_10) = match alu_op {
3143                    VecALUOp::Sqadd => (0b000_01110_00_1 | enc_size << 1, 0b000011),
3144                    VecALUOp::Sqsub => (0b000_01110_00_1 | enc_size << 1, 0b001011),
3145                    VecALUOp::Uqadd => (0b001_01110_00_1 | enc_size << 1, 0b000011),
3146                    VecALUOp::Uqsub => (0b001_01110_00_1 | enc_size << 1, 0b001011),
3147                    VecALUOp::Cmeq => (0b001_01110_00_1 | enc_size << 1, 0b100011),
3148                    VecALUOp::Cmge => (0b000_01110_00_1 | enc_size << 1, 0b001111),
3149                    VecALUOp::Cmgt => (0b000_01110_00_1 | enc_size << 1, 0b001101),
3150                    VecALUOp::Cmhi => (0b001_01110_00_1 | enc_size << 1, 0b001101),
3151                    VecALUOp::Cmhs => (0b001_01110_00_1 | enc_size << 1, 0b001111),
3152                    VecALUOp::Fcmeq => (0b000_01110_00_1, 0b111001),
3153                    VecALUOp::Fcmgt => (0b001_01110_10_1, 0b111001),
3154                    VecALUOp::Fcmge => (0b001_01110_00_1, 0b111001),
3155                    // The following logical instructions operate on bytes, so are not encoded differently
3156                    // for the different vector types.
3157                    VecALUOp::And => (0b000_01110_00_1, 0b000111),
3158                    VecALUOp::Bic => (0b000_01110_01_1, 0b000111),
3159                    VecALUOp::Orr => (0b000_01110_10_1, 0b000111),
3160                    VecALUOp::Orn => (0b000_01110_11_1, 0b000111),
3161                    VecALUOp::Eor => (0b001_01110_00_1, 0b000111),
3162                    VecALUOp::Umaxp => {
3163                        debug_assert_ne!(size, VectorSize::Size64x2);
3164
3165                        (0b001_01110_00_1 | enc_size << 1, 0b101001)
3166                    }
3167                    VecALUOp::Add => (0b000_01110_00_1 | enc_size << 1, 0b100001),
3168                    VecALUOp::Sub => (0b001_01110_00_1 | enc_size << 1, 0b100001),
3169                    VecALUOp::Mul => {
3170                        debug_assert_ne!(size, VectorSize::Size64x2);
3171                        (0b000_01110_00_1 | enc_size << 1, 0b100111)
3172                    }
3173                    VecALUOp::Sshl => (0b000_01110_00_1 | enc_size << 1, 0b010001),
3174                    VecALUOp::Ushl => (0b001_01110_00_1 | enc_size << 1, 0b010001),
3175                    VecALUOp::Umin => {
3176                        debug_assert_ne!(size, VectorSize::Size64x2);
3177
3178                        (0b001_01110_00_1 | enc_size << 1, 0b011011)
3179                    }
3180                    VecALUOp::Smin => {
3181                        debug_assert_ne!(size, VectorSize::Size64x2);
3182
3183                        (0b000_01110_00_1 | enc_size << 1, 0b011011)
3184                    }
3185                    VecALUOp::Umax => {
3186                        debug_assert_ne!(size, VectorSize::Size64x2);
3187
3188                        (0b001_01110_00_1 | enc_size << 1, 0b011001)
3189                    }
3190                    VecALUOp::Smax => {
3191                        debug_assert_ne!(size, VectorSize::Size64x2);
3192
3193                        (0b000_01110_00_1 | enc_size << 1, 0b011001)
3194                    }
3195                    VecALUOp::Urhadd => {
3196                        debug_assert_ne!(size, VectorSize::Size64x2);
3197
3198                        (0b001_01110_00_1 | enc_size << 1, 0b000101)
3199                    }
3200                    VecALUOp::Fadd => (0b000_01110_00_1, 0b110101),
3201                    VecALUOp::Fsub => (0b000_01110_10_1, 0b110101),
3202                    VecALUOp::Fdiv => (0b001_01110_00_1, 0b111111),
3203                    VecALUOp::Fmax => (0b000_01110_00_1, 0b111101),
3204                    VecALUOp::Fmin => (0b000_01110_10_1, 0b111101),
3205                    VecALUOp::Fmul => (0b001_01110_00_1, 0b110111),
3206                    VecALUOp::Addp => (0b000_01110_00_1 | enc_size << 1, 0b101111),
3207                    VecALUOp::Zip1 => (0b01001110_00_0 | enc_size << 1, 0b001110),
3208                    VecALUOp::Zip2 => (0b01001110_00_0 | enc_size << 1, 0b011110),
3209                    VecALUOp::Sqrdmulh => {
3210                        debug_assert!(
3211                            size.lane_size() == ScalarSize::Size16
3212                                || size.lane_size() == ScalarSize::Size32
3213                        );
3214
3215                        (0b001_01110_00_1 | enc_size << 1, 0b101101)
3216                    }
3217                    VecALUOp::Uzp1 => (0b01001110_00_0 | enc_size << 1, 0b000110),
3218                    VecALUOp::Uzp2 => (0b01001110_00_0 | enc_size << 1, 0b010110),
3219                    VecALUOp::Trn1 => (0b01001110_00_0 | enc_size << 1, 0b001010),
3220                    VecALUOp::Trn2 => (0b01001110_00_0 | enc_size << 1, 0b011010),
3221                };
3222                let top11 = if is_float {
3223                    top11 | size.enc_float_size() << 1
3224                } else {
3225                    top11
3226                };
3227                sink.put4(enc_vec_rrr(top11 | q << 9, rm, bit15_10, rn, rd));
3228            }
3229            &Inst::VecRRRMod {
3230                rd,
3231                ri,
3232                rn,
3233                rm,
3234                alu_op,
3235                size,
3236            } => {
3237                debug_assert_eq!(rd.to_reg(), ri);
3238                let (q, _enc_size) = size.enc_size();
3239
3240                let (top11, bit15_10) = match alu_op {
3241                    VecALUModOp::Bsl => (0b001_01110_01_1, 0b000111),
3242                    VecALUModOp::Fmla => {
3243                        (0b000_01110_00_1 | (size.enc_float_size() << 1), 0b110011)
3244                    }
3245                    VecALUModOp::Fmls => {
3246                        (0b000_01110_10_1 | (size.enc_float_size() << 1), 0b110011)
3247                    }
3248                    // SDOT Vd.4S, Vn.16B, Vm.16B (FEAT_DotProd). The size/element
3249                    // field (bits 23:22 = 0b10) is part of the dot-product opcode,
3250                    // so it is baked into top11; only Q (from `size`) is variable.
3251                    // top11 (Q=0) | q<<9 with bit15_10 yields 0x4E809400 for .4S/.16B.
3252                    VecALUModOp::Sdot => (0b000_01110_10_0, 0b100101),
3253                    // USDOT Vd.4S, Vn.16B, Vm.16B (FEAT_I8MM). Same shape as
3254                    // SDOT; only the opcode field differs.
3255                    VecALUModOp::Usdot => (0b000_01110_10_0, 0b100111),
3256                };
3257                sink.put4(enc_vec_rrr(top11 | q << 9, rm, bit15_10, rn, rd));
3258            }
3259            &Inst::VecFmlaElem {
3260                rd,
3261                ri,
3262                rn,
3263                rm,
3264                alu_op,
3265                size,
3266                idx,
3267            } => {
3268                debug_assert_eq!(rd.to_reg(), ri);
3269                let idx = u32::from(idx);
3270
3271                let (q, _size) = size.enc_size();
3272                let o2 = match alu_op {
3273                    VecALUModOp::Fmla => 0b0,
3274                    VecALUModOp::Fmls => 0b1,
3275                    _ => unreachable!(),
3276                };
3277
3278                let (h, l) = match size {
3279                    VectorSize::Size32x4 => {
3280                        assert!(idx < 4);
3281                        (idx >> 1, idx & 1)
3282                    }
3283                    VectorSize::Size64x2 => {
3284                        assert!(idx < 2);
3285                        (idx, 0)
3286                    }
3287                    _ => unreachable!(),
3288                };
3289
3290                let top11 = 0b000_011111_00 | (q << 9) | (size.enc_float_size() << 1) | l;
3291                let bit15_10 = 0b000100 | (o2 << 4) | (h << 1);
3292                sink.put4(enc_vec_rrr(top11, rm, bit15_10, rn, rd));
3293            }
3294            &Inst::VecLoadReplicate {
3295                rd,
3296                rn,
3297                size,
3298                flags,
3299            } => {
3300                let (q, size) = size.enc_size();
3301
3302                if let Some(trap_code) = flags.trap_code() {
3303                    // Register the offset at which the actual load instruction starts.
3304                    sink.add_trap(trap_code);
3305                }
3306
3307                sink.put4(enc_ldst_vec(q, size, rn, rd));
3308            }
3309            &Inst::VecCSel { rd, rn, rm, cond } => {
3310                /* Emit this:
3311                      b.cond  else
3312                      mov     rd, rm
3313                      b       out
3314                     else:
3315                      mov     rd, rn
3316                     out:
3317
3318                   Note, we could do better in the cases where rd == rn or rd == rm.
3319                */
3320                let else_label = sink.get_label();
3321                let out_label = sink.get_label();
3322
3323                // b.cond else
3324                let br_else_offset = sink.cur_offset();
3325                sink.put4(enc_conditional_br(
3326                    BranchTarget::Label(else_label),
3327                    CondBrKind::Cond(cond),
3328                ));
3329                sink.use_label_at_offset(br_else_offset, else_label, LabelUse::Branch19);
3330
3331                // mov rd, rm
3332                sink.put4(enc_vecmov(/* 16b = */ true, rd, rm));
3333
3334                // b out
3335                let b_out_offset = sink.cur_offset();
3336                sink.use_label_at_offset(b_out_offset, out_label, LabelUse::Branch26);
3337                sink.add_uncond_branch(b_out_offset, b_out_offset + 4, out_label);
3338                sink.put4(enc_jump26(0b000101, 0 /* will be fixed up later */));
3339
3340                // else:
3341                sink.bind_label(else_label, &mut state.ctrl_plane);
3342
3343                // mov rd, rn
3344                sink.put4(enc_vecmov(/* 16b = */ true, rd, rn));
3345
3346                // out:
3347                sink.bind_label(out_label, &mut state.ctrl_plane);
3348            }
3349            &Inst::MovToNZCV { rn } => {
3350                sink.put4(0xd51b4200 | machreg_to_gpr(rn));
3351            }
3352            &Inst::MovFromNZCV { rd } => {
3353                sink.put4(0xd53b4200 | machreg_to_gpr(rd.to_reg()));
3354            }
3355            &Inst::BitfieldMove {
3356                size,
3357                bfm_op,
3358                rd,
3359                rn,
3360                immr,
3361                imms,
3362            } => {
3363                let opc = bfm_op.opc();
3364                sink.put4(enc_bfm(opc, size, rd, rn, immr.value(), imms.value()));
3365            }
3366            &Inst::BitfieldMoveMod {
3367                size,
3368                rd,
3369                ri,
3370                rn,
3371                immr,
3372                imms,
3373            } => {
3374                debug_assert_eq!(rd.to_reg(), ri);
3375                sink.put4(enc_bfm(0b01, size, rd, rn, immr.value(), imms.value()));
3376            }
3377            &Inst::Jump { ref dest } => {
3378                let off = sink.cur_offset();
3379                // Indicate that the jump uses a label, if so, so that a fixup can occur later.
3380                if let Some(l) = dest.as_label() {
3381                    sink.use_label_at_offset(off, l, LabelUse::Branch26);
3382                    sink.add_uncond_branch(off, off + 4, l);
3383                }
3384                // Emit the jump itself.
3385                sink.put4(enc_jump26(0b000101, dest.as_offset26_or_zero()));
3386            }
3387            &Inst::Args { .. } | &Inst::Rets { .. } => {
3388                // Nothing: this is a pseudoinstruction that serves
3389                // only to constrain registers at a certain point.
3390            }
3391            &Inst::Ret {} => {
3392                sink.put4(0xd65f03c0);
3393            }
3394            &Inst::AuthenticatedRet { key, is_hint } => {
3395                let (op2, is_hint) = match key {
3396                    APIKey::AZ => (0b100, true),
3397                    APIKey::ASP => (0b101, is_hint),
3398                    APIKey::BZ => (0b110, true),
3399                    APIKey::BSP => (0b111, is_hint),
3400                };
3401
3402                if is_hint {
3403                    sink.put4(key.enc_auti_hint());
3404                    Inst::Ret {}.emit(sink, emit_info, state);
3405                } else {
3406                    sink.put4(0xd65f0bff | (op2 << 9)); // reta{key}
3407                }
3408            }
3409            &Inst::Call { ref info } => {
3410                let start = sink.cur_offset();
3411                let user_stack_map = state.take_stack_map();
3412                sink.add_reloc(Reloc::Arm64Call, &info.dest, 0);
3413                sink.put4(enc_jump26(0b100101, 0));
3414                if let Some(s) = user_stack_map {
3415                    let offset = sink.cur_offset();
3416                    sink.push_user_stack_map(state, offset, s);
3417                }
3418
3419                if let Some(try_call) = info.try_call_info.as_ref() {
3420                    sink.add_try_call_site(
3421                        Some(state.frame_layout.sp_to_fp()),
3422                        try_call.exception_handlers(&state.frame_layout),
3423                    );
3424                } else {
3425                    sink.add_call_site();
3426                }
3427
3428                if info.callee_pop_size > 0 {
3429                    let callee_pop_size =
3430                        i32::try_from(info.callee_pop_size).expect("callee popped more than 2GB");
3431                    for inst in AArch64MachineDeps::gen_sp_reg_adjust(-callee_pop_size) {
3432                        inst.emit(sink, emit_info, state);
3433                    }
3434                }
3435
3436                if info.patchable {
3437                    sink.add_patchable_call_site(sink.cur_offset() - start);
3438                } else {
3439                    // Load any stack-carried return values.
3440                    info.emit_retval_loads::<AArch64MachineDeps, _, _>(
3441                        state.frame_layout().stackslots_size,
3442                        |inst| inst.emit(sink, emit_info, state),
3443                        |needed_space| Some(Inst::EmitIsland { needed_space }),
3444                    );
3445                }
3446
3447                // If this is a try-call, jump to the continuation
3448                // (normal-return) block.
3449                if let Some(try_call) = info.try_call_info.as_ref() {
3450                    let jmp = Inst::Jump {
3451                        dest: BranchTarget::Label(try_call.continuation),
3452                    };
3453                    jmp.emit(sink, emit_info, state);
3454                }
3455
3456                // We produce an island above if needed, so disable
3457                // the worst-case-size check in this case.
3458                start_off = sink.cur_offset();
3459            }
3460            &Inst::CallInd { ref info } => {
3461                let user_stack_map = state.take_stack_map();
3462                sink.put4(
3463                    0b1101011_0001_11111_000000_00000_00000 | (machreg_to_gpr(info.dest) << 5),
3464                );
3465                if let Some(s) = user_stack_map {
3466                    let offset = sink.cur_offset();
3467                    sink.push_user_stack_map(state, offset, s);
3468                }
3469
3470                if let Some(try_call) = info.try_call_info.as_ref() {
3471                    sink.add_try_call_site(
3472                        Some(state.frame_layout.sp_to_fp()),
3473                        try_call.exception_handlers(&state.frame_layout),
3474                    );
3475                } else {
3476                    sink.add_call_site();
3477                }
3478
3479                if info.callee_pop_size > 0 {
3480                    let callee_pop_size =
3481                        i32::try_from(info.callee_pop_size).expect("callee popped more than 2GB");
3482                    for inst in AArch64MachineDeps::gen_sp_reg_adjust(-callee_pop_size) {
3483                        inst.emit(sink, emit_info, state);
3484                    }
3485                }
3486
3487                // Load any stack-carried return values.
3488                info.emit_retval_loads::<AArch64MachineDeps, _, _>(
3489                    state.frame_layout().stackslots_size,
3490                    |inst| inst.emit(sink, emit_info, state),
3491                    |needed_space| Some(Inst::EmitIsland { needed_space }),
3492                );
3493
3494                // If this is a try-call, jump to the continuation
3495                // (normal-return) block.
3496                if let Some(try_call) = info.try_call_info.as_ref() {
3497                    let jmp = Inst::Jump {
3498                        dest: BranchTarget::Label(try_call.continuation),
3499                    };
3500                    jmp.emit(sink, emit_info, state);
3501                }
3502
3503                // We produce an island above if needed, so disable
3504                // the worst-case-size check in this case.
3505                start_off = sink.cur_offset();
3506            }
3507            &Inst::ReturnCall { ref info } => {
3508                emit_return_call_common_sequence(sink, emit_info, state, info);
3509
3510                // Note: this is not `Inst::Jump { .. }.emit(..)` because we
3511                // have different metadata in this case: we don't have a label
3512                // for the target, but rather a function relocation.
3513                sink.add_reloc(Reloc::Arm64Call, &info.dest, 0);
3514                sink.put4(enc_jump26(0b000101, 0));
3515                sink.add_call_site();
3516
3517                // `emit_return_call_common_sequence` emits an island if
3518                // necessary, so we can safely disable the worst-case-size check
3519                // in this case.
3520                start_off = sink.cur_offset();
3521            }
3522            &Inst::ReturnCallInd { ref info } => {
3523                emit_return_call_common_sequence(sink, emit_info, state, info);
3524
3525                // Indirect tail calls must use x16 or x17 when BTI is enabled
3526                // so that they are compatible with the callee's `BTI C`
3527                // landing pad. Other registers are only accepted by `BTI J`,
3528                // which is not a valid landing pad for a function entry.
3529                //
3530                // x16 is the spill-temp register, which by Cranelift
3531                // convention we are allowed to use as a scratch register
3532                // within one VCode inst, so this is safe.
3533                let dest = if emit_info.isa_flags.use_bti() {
3534                    let dest = xreg(16);
3535                    if info.dest != dest {
3536                        Inst::Mov {
3537                            size: OperandSize::Size64,
3538                            rd: writable_xreg(16),
3539                            rm: info.dest,
3540                        }
3541                        .emit(sink, emit_info, state);
3542                    }
3543                    dest
3544                } else {
3545                    info.dest
3546                };
3547
3548                Inst::IndirectBr {
3549                    rn: dest,
3550                    targets: vec![],
3551                }
3552                .emit(sink, emit_info, state);
3553                sink.add_call_site();
3554
3555                // `emit_return_call_common_sequence` emits an island if
3556                // necessary, so we can safely disable the worst-case-size check
3557                // in this case.
3558                start_off = sink.cur_offset();
3559            }
3560            &Inst::CondBr {
3561                taken,
3562                not_taken,
3563                kind,
3564            } => {
3565                // Conditional part first.
3566                let cond_off = sink.cur_offset();
3567                if let Some(l) = taken.as_label() {
3568                    sink.use_label_at_offset(cond_off, l, LabelUse::Branch19);
3569                    let inverted = enc_conditional_br(taken, kind.invert()).to_le_bytes();
3570                    sink.add_cond_branch(cond_off, cond_off + 4, l, &inverted[..]);
3571                }
3572                sink.put4(enc_conditional_br(taken, kind));
3573
3574                // Unconditional part next.
3575                let uncond_off = sink.cur_offset();
3576                if let Some(l) = not_taken.as_label() {
3577                    sink.use_label_at_offset(uncond_off, l, LabelUse::Branch26);
3578                    sink.add_uncond_branch(uncond_off, uncond_off + 4, l);
3579                }
3580                sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero()));
3581            }
3582            &Inst::TestBitAndBranch {
3583                taken,
3584                not_taken,
3585                kind,
3586                rn,
3587                bit,
3588            } => {
3589                // Emit the conditional branch first
3590                let cond_off = sink.cur_offset();
3591                if let Some(l) = taken.as_label() {
3592                    sink.use_label_at_offset(cond_off, l, LabelUse::Branch14);
3593                    let inverted =
3594                        enc_test_bit_and_branch(kind.complement(), taken, rn, bit).to_le_bytes();
3595                    sink.add_cond_branch(cond_off, cond_off + 4, l, &inverted[..]);
3596                }
3597                sink.put4(enc_test_bit_and_branch(kind, taken, rn, bit));
3598
3599                // Unconditional part next.
3600                let uncond_off = sink.cur_offset();
3601                if let Some(l) = not_taken.as_label() {
3602                    sink.use_label_at_offset(uncond_off, l, LabelUse::Branch26);
3603                    sink.add_uncond_branch(uncond_off, uncond_off + 4, l);
3604                }
3605                sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero()));
3606            }
3607            &Inst::TrapIf { kind, trap_code } => {
3608                let label = sink.defer_trap(trap_code);
3609                // condbr KIND, LABEL
3610                let off = sink.cur_offset();
3611                sink.put4(enc_conditional_br(BranchTarget::Label(label), kind));
3612                sink.use_label_at_offset(off, label, LabelUse::Branch19);
3613            }
3614            &Inst::IndirectBr { rn, .. } => {
3615                sink.put4(enc_br(rn));
3616            }
3617            &Inst::Nop0 => {}
3618            &Inst::Nop4 => {
3619                sink.put4(0xd503201f);
3620            }
3621            &Inst::Brk => {
3622                sink.put4(0xd43e0000);
3623            }
3624            &Inst::Udf { trap_code } => {
3625                sink.add_trap(trap_code);
3626                sink.put_data(Inst::TRAP_OPCODE);
3627            }
3628            &Inst::Adr { rd, off } => {
3629                assert!(off > -(1 << 20));
3630                assert!(off < (1 << 20));
3631                sink.put4(enc_adr(off, rd));
3632            }
3633            &Inst::Adrp { rd, off } => {
3634                assert!(off > -(1 << 20));
3635                assert!(off < (1 << 20));
3636                sink.put4(enc_adrp(off, rd));
3637            }
3638            &Inst::Word4 { data } => {
3639                sink.put4(data);
3640            }
3641            &Inst::Word8 { data } => {
3642                sink.put8(data);
3643            }
3644            &Inst::JTSequence {
3645                ridx,
3646                rtmp1,
3647                rtmp2,
3648                default,
3649                ref targets,
3650                ..
3651            } => {
3652                // This sequence is *one* instruction in the vcode, and is expanded only here at
3653                // emission time, because we cannot allow the regalloc to insert spills/reloads in
3654                // the middle; we depend on hardcoded PC-rel addressing below.
3655
3656                // The default target is stored as the last entry in the
3657                // jump table (at index `targets.len()`), and we clamp the
3658                // index to that entry when it is out of bounds. This avoids
3659                // any conditional branch to the default target: such a
3660                // branch would have limited range, and the inline jump
3661                // table that follows it can be arbitrarily large.
3662                //
3663                // The flags are set by a prior comparison of `ridx` against
3664                // `targets.len()`; "lo" (unsigned less-than) means in-bounds.
3665                let default_index = u64::try_from(targets.len()).unwrap();
3666                for inst in Inst::load_constant(rtmp2, default_index) {
3667                    inst.emit(sink, emit_info, state);
3668                }
3669                // Clamp the index: in-bounds indices are kept, and
3670                // out-of-bounds indices select the default entry. This is
3671                // also the Spectre mitigation, since there is no
3672                // conditional branch to mispredict.
3673                let inst = Inst::CSel {
3674                    rd: rtmp2,
3675                    cond: Cond::Lo,
3676                    rn: ridx,
3677                    rm: rtmp2.to_reg(),
3678                };
3679                inst.emit(sink, emit_info, state);
3680                // Prevent any data value speculation if spectre mitigations are
3681                // enabled.
3682                if emit_info.flags.enable_table_access_spectre_mitigation()
3683                    && emit_info.isa_flags.use_csdb()
3684                {
3685                    Inst::Csdb.emit(sink, emit_info, state);
3686                }
3687
3688                // Load address of jump table
3689                let inst = Inst::Adr { rd: rtmp1, off: 16 };
3690                inst.emit(sink, emit_info, state);
3691                // Load value out of jump table
3692                let inst = Inst::SLoad32 {
3693                    rd: rtmp2,
3694                    mem: AMode::reg_plus_reg_scaled_extended(
3695                        rtmp1.to_reg(),
3696                        rtmp2.to_reg(),
3697                        ExtendOp::UXTW,
3698                    ),
3699                    flags: MemFlagsData::trusted(),
3700                };
3701                inst.emit(sink, emit_info, state);
3702                // Add base of jump table to jump-table-sourced block offset
3703                let inst = Inst::AluRRR {
3704                    alu_op: ALUOp::Add,
3705                    size: OperandSize::Size64,
3706                    rd: rtmp1,
3707                    rn: rtmp1.to_reg(),
3708                    rm: rtmp2.to_reg(),
3709                };
3710                inst.emit(sink, emit_info, state);
3711                // Branch to computed address. (`targets` here is only used for successor queries
3712                // and is not needed for emission.)
3713                let inst = Inst::IndirectBr {
3714                    rn: rtmp1.to_reg(),
3715                    targets: vec![],
3716                };
3717                inst.emit(sink, emit_info, state);
3718                // Emit jump table (table of 32-bit offsets), with the
3719                // default target as the final entry.
3720                let jt_off = sink.cur_offset();
3721                for &target in targets.iter().chain(core::iter::once(&default)) {
3722                    let word_off = sink.cur_offset();
3723                    // off_into_table is an addend here embedded in the label to be later patched
3724                    // at the end of codegen. The offset is initially relative to this jump table
3725                    // entry; with the extra addend, it'll be relative to the jump table's start,
3726                    // after patching.
3727                    let off_into_table = word_off - jt_off;
3728                    sink.use_label_at_offset(word_off, target, LabelUse::PCRel32);
3729                    sink.put4(off_into_table);
3730                }
3731
3732                // Lowering produces an EmitIsland before using a JTSequence, so we can safely
3733                // disable the worst-case-size check in this case.
3734                start_off = sink.cur_offset();
3735            }
3736            &Inst::LoadExtNameGot { rd, ref name } => {
3737                // See this CE Example for the variations of this with and without BTI & PAUTH
3738                // https://godbolt.org/z/ncqjbbvvn
3739                //
3740                // Emit the following code:
3741                //   adrp    rd, :got:X
3742                //   ldr     rd, [rd, :got_lo12:X]
3743
3744                // adrp rd, symbol
3745                sink.add_reloc(Reloc::Aarch64AdrGotPage21, &**name, 0);
3746                let inst = Inst::Adrp { rd, off: 0 };
3747                inst.emit(sink, emit_info, state);
3748
3749                // ldr rd, [rd, :got_lo12:X]
3750                sink.add_reloc(Reloc::Aarch64Ld64GotLo12Nc, &**name, 0);
3751                let inst = Inst::ULoad64 {
3752                    rd,
3753                    mem: AMode::reg(rd.to_reg()),
3754                    flags: MemFlagsData::trusted(),
3755                };
3756                inst.emit(sink, emit_info, state);
3757            }
3758            &Inst::LoadExtNameNear {
3759                rd,
3760                ref name,
3761                offset,
3762            } => {
3763                // Emit the following code:
3764                //   adrp    rd, X
3765                //   add     rd, rd, :lo12:X
3766                //
3767                // See https://godbolt.org/z/855KEvM5r for an example.
3768
3769                // adrp rd, symbol
3770                sink.add_reloc(Reloc::Aarch64AdrPrelPgHi21, &**name, offset);
3771                let inst = Inst::Adrp { rd, off: 0 };
3772                inst.emit(sink, emit_info, state);
3773
3774                // add rd, rd, :lo12:X
3775                sink.add_reloc(Reloc::Aarch64AddAbsLo12Nc, &**name, offset);
3776                let inst = Inst::AluRRImm12 {
3777                    alu_op: ALUOp::Add,
3778                    size: OperandSize::Size64,
3779                    rd,
3780                    rn: rd.to_reg(),
3781                    imm12: Imm12::ZERO,
3782                };
3783                inst.emit(sink, emit_info, state);
3784            }
3785            &Inst::LoadExtNameFar {
3786                rd,
3787                ref name,
3788                offset,
3789            } => {
3790                // With absolute offsets we set up a load from a preallocated space, and then jump
3791                // over it.
3792                //
3793                // Emit the following code:
3794                //   ldr     rd, #8
3795                //   b       #0x10
3796                //   <8 byte space>
3797
3798                let inst = Inst::ULoad64 {
3799                    rd,
3800                    mem: AMode::Label {
3801                        label: MemLabel::PCRel(8),
3802                    },
3803                    flags: MemFlagsData::trusted(),
3804                };
3805                inst.emit(sink, emit_info, state);
3806                let inst = Inst::Jump {
3807                    dest: BranchTarget::ResolvedOffset(12),
3808                };
3809                inst.emit(sink, emit_info, state);
3810                sink.add_reloc(Reloc::Abs8, &**name, offset);
3811                sink.put8(0);
3812            }
3813            &Inst::LoadAddr { rd, ref mem } => {
3814                let mem = mem.clone();
3815                let (mem_insts, mem) = mem_finalize(Some(sink), &mem, I8, state);
3816                for inst in mem_insts.into_iter() {
3817                    inst.emit(sink, emit_info, state);
3818                }
3819
3820                let (reg, index_reg, offset) = match mem {
3821                    AMode::RegExtended { rn, rm, extendop } => {
3822                        let r = rn;
3823                        (r, Some((rm, extendop)), 0)
3824                    }
3825                    AMode::Unscaled { rn, simm9 } => {
3826                        let r = rn;
3827                        (r, None, simm9.value())
3828                    }
3829                    AMode::UnsignedOffset { rn, uimm12 } => {
3830                        let r = rn;
3831                        (r, None, uimm12.value() as i32)
3832                    }
3833                    _ => panic!("Unsupported case for LoadAddr: {mem:?}"),
3834                };
3835                let abs_offset = if offset < 0 {
3836                    -offset as u64
3837                } else {
3838                    offset as u64
3839                };
3840                let alu_op = if offset < 0 { ALUOp::Sub } else { ALUOp::Add };
3841
3842                if let Some((idx, extendop)) = index_reg {
3843                    let add = Inst::AluRRRExtend {
3844                        alu_op: ALUOp::Add,
3845                        size: OperandSize::Size64,
3846                        rd,
3847                        rn: reg,
3848                        rm: idx,
3849                        extendop,
3850                    };
3851
3852                    add.emit(sink, emit_info, state);
3853                } else if offset == 0 {
3854                    if reg != rd.to_reg() {
3855                        let mov = Inst::Mov {
3856                            size: OperandSize::Size64,
3857                            rd,
3858                            rm: reg,
3859                        };
3860
3861                        mov.emit(sink, emit_info, state);
3862                    }
3863                } else if let Some(imm12) = Imm12::maybe_from_u64(abs_offset) {
3864                    let add = Inst::AluRRImm12 {
3865                        alu_op,
3866                        size: OperandSize::Size64,
3867                        rd,
3868                        rn: reg,
3869                        imm12,
3870                    };
3871                    add.emit(sink, emit_info, state);
3872                } else {
3873                    // Use `tmp2` here: `reg` may be `spilltmp` if the `AMode` on this instruction
3874                    // was initially an `SPOffset`. Assert that `tmp2` is truly free to use. Note
3875                    // that no other instructions will be inserted here (we're emitting directly),
3876                    // and a live range of `tmp2` should not span this instruction, so this use
3877                    // should otherwise be correct.
3878                    debug_assert!(rd.to_reg() != tmp2_reg());
3879                    debug_assert!(reg != tmp2_reg());
3880                    let tmp = writable_tmp2_reg();
3881                    for insn in Inst::load_constant(tmp, abs_offset).into_iter() {
3882                        insn.emit(sink, emit_info, state);
3883                    }
3884                    let add = Inst::AluRRR {
3885                        alu_op,
3886                        size: OperandSize::Size64,
3887                        rd,
3888                        rn: reg,
3889                        rm: tmp.to_reg(),
3890                    };
3891                    add.emit(sink, emit_info, state);
3892                }
3893            }
3894            &Inst::Paci { key } => {
3895                let (crm, op2) = match key {
3896                    APIKey::AZ => (0b0011, 0b000),
3897                    APIKey::ASP => (0b0011, 0b001),
3898                    APIKey::BZ => (0b0011, 0b010),
3899                    APIKey::BSP => (0b0011, 0b011),
3900                };
3901
3902                sink.put4(0xd503211f | (crm << 8) | (op2 << 5));
3903            }
3904            &Inst::Xpaclri => sink.put4(0xd50320ff),
3905            &Inst::Bti { targets } => {
3906                let targets = match targets {
3907                    BranchTargetType::None => 0b00,
3908                    BranchTargetType::C => 0b01,
3909                    BranchTargetType::J => 0b10,
3910                    BranchTargetType::JC => 0b11,
3911                };
3912
3913                sink.put4(0xd503241f | targets << 6);
3914            }
3915            &Inst::EmitIsland { needed_space } => {
3916                if sink.island_needed(needed_space + 4) {
3917                    let jump_around_label = sink.get_label();
3918                    let jmp = Inst::Jump {
3919                        dest: BranchTarget::Label(jump_around_label),
3920                    };
3921                    jmp.emit(sink, emit_info, state);
3922                    sink.emit_island(needed_space + 4, &mut state.ctrl_plane);
3923                    sink.bind_label(jump_around_label, &mut state.ctrl_plane);
3924                }
3925            }
3926
3927            &Inst::ElfTlsGetAddr {
3928                ref symbol,
3929                rd,
3930                tmp,
3931            } => {
3932                assert_eq!(xreg(0), rd.to_reg());
3933
3934                // See the original proposal for TLSDESC.
3935                // http://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf
3936                //
3937                // Implement the TLSDESC instruction sequence:
3938                //   adrp x0, :tlsdesc:tlsvar
3939                //   ldr  tmp, [x0, :tlsdesc_lo12:tlsvar]
3940                //   add  x0, x0, :tlsdesc_lo12:tlsvar
3941                //   blr  tmp
3942                //   mrs  tmp, tpidr_el0
3943                //   add  x0, x0, tmp
3944                //
3945                // This is the instruction sequence that GCC emits for ELF GD TLS Relocations in aarch64
3946                // See: https://gcc.godbolt.org/z/e4j7MdErh
3947
3948                // adrp x0, :tlsdesc:tlsvar
3949                sink.add_reloc(Reloc::Aarch64TlsDescAdrPage21, &**symbol, 0);
3950                Inst::Adrp { rd, off: 0 }.emit(sink, emit_info, state);
3951
3952                // ldr  tmp, [x0, :tlsdesc_lo12:tlsvar]
3953                sink.add_reloc(Reloc::Aarch64TlsDescLd64Lo12, &**symbol, 0);
3954                Inst::ULoad64 {
3955                    rd: tmp,
3956                    mem: AMode::reg(rd.to_reg()),
3957                    flags: MemFlagsData::trusted(),
3958                }
3959                .emit(sink, emit_info, state);
3960
3961                // add x0, x0, :tlsdesc_lo12:tlsvar
3962                sink.add_reloc(Reloc::Aarch64TlsDescAddLo12, &**symbol, 0);
3963                Inst::AluRRImm12 {
3964                    alu_op: ALUOp::Add,
3965                    size: OperandSize::Size64,
3966                    rd,
3967                    rn: rd.to_reg(),
3968                    imm12: Imm12::maybe_from_u64(0).unwrap(),
3969                }
3970                .emit(sink, emit_info, state);
3971
3972                // blr tmp
3973                sink.add_reloc(Reloc::Aarch64TlsDescCall, &**symbol, 0);
3974                Inst::CallInd {
3975                    info: crate::isa::Box::new(CallInfo::empty(tmp.to_reg(), CallConv::SystemV)),
3976                }
3977                .emit(sink, emit_info, state);
3978
3979                // mrs tmp, tpidr_el0
3980                sink.put4(0xd53bd040 | machreg_to_gpr(tmp.to_reg()));
3981
3982                // add x0, x0, tmp
3983                Inst::AluRRR {
3984                    alu_op: ALUOp::Add,
3985                    size: OperandSize::Size64,
3986                    rd,
3987                    rn: rd.to_reg(),
3988                    rm: tmp.to_reg(),
3989                }
3990                .emit(sink, emit_info, state);
3991            }
3992
3993            &Inst::MachOTlsGetAddr { ref symbol, rd } => {
3994                // Each thread local variable gets a descriptor, where the first xword of the descriptor is a pointer
3995                // to a function that takes the descriptor address in x0, and after the function returns x0
3996                // contains the address for the thread local variable
3997                //
3998                // what we want to emit is basically:
3999                //
4000                // adrp x0, <label>@TLVPPAGE  ; Load the address of the page of the thread local variable pointer (TLVP)
4001                // ldr x0, [x0, <label>@TLVPPAGEOFF] ; Load the descriptor's address into x0
4002                // ldr x1, [x0] ; Load the function pointer (the first part of the descriptor)
4003                // blr x1 ; Call the function pointer with the descriptor address in x0
4004                // ; x0 now contains the TLV address
4005
4006                assert_eq!(xreg(0), rd.to_reg());
4007                let rtmp = writable_xreg(1);
4008
4009                // adrp x0, <label>@TLVPPAGE
4010                sink.add_reloc(Reloc::MachOAarch64TlsAdrPage21, symbol, 0);
4011                sink.put4(0x90000000);
4012
4013                // ldr x0, [x0, <label>@TLVPPAGEOFF]
4014                sink.add_reloc(Reloc::MachOAarch64TlsAdrPageOff12, symbol, 0);
4015                sink.put4(0xf9400000);
4016
4017                // load [x0] into temp register
4018                Inst::ULoad64 {
4019                    rd: rtmp,
4020                    mem: AMode::reg(rd.to_reg()),
4021                    flags: MemFlagsData::trusted(),
4022                }
4023                .emit(sink, emit_info, state);
4024
4025                // call function pointer in temp register
4026                Inst::CallInd {
4027                    info: crate::isa::Box::new(CallInfo::empty(
4028                        rtmp.to_reg(),
4029                        CallConv::AppleAarch64,
4030                    )),
4031                }
4032                .emit(sink, emit_info, state);
4033            }
4034
4035            &Inst::Unwind { ref inst } => {
4036                sink.add_unwind(inst.clone());
4037            }
4038
4039            &Inst::DummyUse { .. } => {}
4040
4041            &Inst::LabelAddress { dst, label } => {
4042                // We emit an ADR only, which is +/- 2MiB range. This
4043                // should be sufficient for the typical use-case of
4044                // this instruction, which is insmall trampolines to
4045                // get exception-handler addresses.
4046                let inst = Inst::Adr { rd: dst, off: 0 };
4047                let offset = sink.cur_offset();
4048                inst.emit(sink, emit_info, state);
4049                sink.use_label_at_offset(offset, label, LabelUse::Adr21);
4050            }
4051
4052            &Inst::SequencePoint { .. } => {
4053                // Nothing.
4054            }
4055
4056            &Inst::StackProbeLoop { start, end, step } => {
4057                assert!(emit_info.flags.enable_probestack());
4058
4059                // The loop generated here uses `start` as a counter register to
4060                // count backwards until negating it exceeds `end`. In other
4061                // words `start` is an offset from `sp` we're testing where
4062                // `end` is the max size we need to test. The loop looks like:
4063                //
4064                //      loop_start:
4065                //          sub start, start, #step
4066                //          stur xzr, [sp, start]
4067                //          cmn start, end
4068                //          br.gt loop_start
4069                //      loop_end:
4070                //
4071                // Note that this loop cannot use the spilltmp and tmp2
4072                // registers as those are currently used as the input to this
4073                // loop when generating the instruction. This means that some
4074                // more flavorful address modes and lowerings need to be
4075                // avoided.
4076                //
4077                // Perhaps someone more clever than I can figure out how to use
4078                // `subs` or the like and skip the `cmn`, but I can't figure it
4079                // out at this time.
4080
4081                let loop_start = sink.get_label();
4082                sink.bind_label(loop_start, &mut state.ctrl_plane);
4083
4084                Inst::AluRRImm12 {
4085                    alu_op: ALUOp::Sub,
4086                    size: OperandSize::Size64,
4087                    rd: start,
4088                    rn: start.to_reg(),
4089                    imm12: step,
4090                }
4091                .emit(sink, emit_info, state);
4092                Inst::Store32 {
4093                    rd: regs::zero_reg(),
4094                    mem: AMode::RegReg {
4095                        rn: regs::stack_reg(),
4096                        rm: start.to_reg(),
4097                    },
4098                    flags: MemFlagsData::trusted(),
4099                }
4100                .emit(sink, emit_info, state);
4101                Inst::AluRRR {
4102                    alu_op: ALUOp::AddS,
4103                    size: OperandSize::Size64,
4104                    rd: regs::writable_zero_reg(),
4105                    rn: start.to_reg(),
4106                    rm: end,
4107                }
4108                .emit(sink, emit_info, state);
4109
4110                let loop_end = sink.get_label();
4111                Inst::CondBr {
4112                    taken: BranchTarget::Label(loop_start),
4113                    not_taken: BranchTarget::Label(loop_end),
4114                    kind: CondBrKind::Cond(Cond::Gt),
4115                }
4116                .emit(sink, emit_info, state);
4117                sink.bind_label(loop_end, &mut state.ctrl_plane);
4118            }
4119
4120            &Inst::InterruptPoll {
4121                dst,
4122                load_ptr,
4123                trap_code,
4124                ..
4125            } => {
4126                // Record the address of the load in the trap table so a signal
4127                // handler can later distinguish whether a segfault is its
4128                // fault.
4129                sink.add_trap(trap_code);
4130
4131                // Emit `ldr dst, [load_ptr]`. Reuse the `dst` address as the
4132                // destination of the dead load, since we are clobbering it
4133                // anyway.
4134                Inst::ULoad64 {
4135                    rd: dst,
4136                    mem: AMode::UnsignedOffset {
4137                        rn: load_ptr,
4138                        uimm12: UImm12Scaled::zero(I64),
4139                    },
4140                    flags: MemFlagsData::trusted(),
4141                }
4142                .emit(sink, emit_info, state);
4143            }
4144        }
4145
4146        let end_off = sink.cur_offset();
4147        debug_assert!(
4148            (end_off - start_off) <= Inst::worst_case_size()
4149                || matches!(self, Inst::EmitIsland { .. }),
4150            "Worst case size exceed for {:?}: {}",
4151            self,
4152            end_off - start_off
4153        );
4154
4155        state.clear_post_insn();
4156    }
4157
4158    fn pretty_print_inst(&self, state: &mut Self::State) -> String {
4159        self.print_with_state(state)
4160    }
4161}
4162
4163fn emit_return_call_common_sequence<T>(
4164    sink: &mut MachBuffer<Inst>,
4165    emit_info: &EmitInfo,
4166    state: &mut EmitState,
4167    info: &ReturnCallInfo<T>,
4168) {
4169    for inst in AArch64MachineDeps::gen_clobber_restore(
4170        CallConv::Tail,
4171        &emit_info.flags,
4172        state.frame_layout(),
4173    ) {
4174        inst.emit(sink, emit_info, state);
4175    }
4176
4177    let setup_area_size = state.frame_layout().setup_area_size;
4178    if setup_area_size > 0 {
4179        // N.B.: sp is already adjusted to the appropriate place by the
4180        // clobber-restore code (which also frees the fixed frame). Hence, there
4181        // is no need for the usual `mov sp, fp` here.
4182
4183        // `ldp fp, lr, [sp], #16`
4184        Inst::LoadP64 {
4185            rt: writable_fp_reg(),
4186            rt2: writable_link_reg(),
4187            mem: PairAMode::SPPostIndexed {
4188                // TODO: we could fold the increment for incoming_args_diff here, as long as that
4189                // value is less than 502*8, by adding it to `setup_area_size`.
4190                // https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/LDP--Load-Pair-of-Registers-
4191                simm7: SImm7Scaled::maybe_from_i64(i64::from(setup_area_size), types::I64).unwrap(),
4192            },
4193            flags: MemFlagsData::trusted(),
4194        }
4195        .emit(sink, emit_info, state);
4196    }
4197
4198    // Adjust SP to account for the possible over-allocation in the prologue.
4199    let incoming_args_diff = state.frame_layout().tail_args_size - info.new_stack_arg_size;
4200    if incoming_args_diff > 0 {
4201        for inst in
4202            AArch64MachineDeps::gen_sp_reg_adjust(i32::try_from(incoming_args_diff).unwrap())
4203        {
4204            inst.emit(sink, emit_info, state);
4205        }
4206    }
4207
4208    if (setup_area_size > 0 || info.sign_return_address_all)
4209        && let Some(key) = info.key
4210    {
4211        sink.put4(key.enc_auti_hint());
4212    }
4213}