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