lib/choir/src/backends/aarch64/encoding/instruction.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const alloc_arena = @import("alloc_arena");
3 const pretty = @import("pretty");
4 const registers = @import("../registers/root.zig");
5 const disassemble = @import("../disassemble/root.zig");
6
7 const Encoded = registers.Encoded;
8 const GPR = registers.GPR;
9 const FPR = registers.FPR;
10 const GPRSize = registers.GPRSize;
11
12 pub const MoveWideOpcode = enum(u2) {
13 movn = 0b00,
14 movz = 0b10,
15 movk = 0b11,
16 };
17
18 pub const LogicalOpcode = enum(u2) {
19 @"and" = 0b00,
20 orr = 0b01,
21 eor = 0b10,
22 ands = 0b11,
23 };
24
25 pub const AddSubShift = enum(u2) {
26 lsl = 0b00,
27 lsr = 0b01,
28 asr = 0b10,
29 reserved = 0b11,
30 };
31
32 pub const AddSubExtend = enum(u3) {
33 uxtb = 0b000,
34 uxth = 0b001,
35 uxtw = 0b010,
36 uxtx = 0b011,
37 sxtb = 0b100,
38 sxth = 0b101,
39 sxtw = 0b110,
40 sxtx = 0b111,
41 };
42
43 pub const LogicalShift = enum(u2) {
44 lsl = 0b00,
45 lsr = 0b01,
46 asr = 0b10,
47 ror = 0b11,
48 };
49
50 pub const DataProcessing2SourceOpcode = enum(u6) {
51 udiv = 0b000010,
52 sdiv = 0b000011,
53 lslv = 0b001000,
54 lsrv = 0b001001,
55 asrv = 0b001010,
56 rorv = 0b001011,
57 };
58
59 pub const LoadStoreFPSize = enum(u2) {
60 single = 0b10,
61 double = 0b11,
62 };
63
64 pub const LoadStorePairMode = enum(u3) {
65 post_index = 0b001,
66 signed_offset = 0b010,
67 pre_index = 0b011,
68 };
69
70 pub const LoadStorePairFPSize = enum(u2) {
71 single = 0b00,
72 double = 0b01,
73 quad = 0b10,
74 };
75
76 pub const FP1SourceOpcode = enum(u6) {
77 fmov = 0b000000,
78 fabs = 0b000001,
79 fneg = 0b000010,
80 fsqrt = 0b000011,
81 };
82
83 pub const FP2SourceOpcode = enum(u4) {
84 fmul = 0b0000,
85 fdiv = 0b0001,
86 fadd = 0b0010,
87 fsub = 0b0011,
88 fmax = 0b0100,
89 fmin = 0b0101,
90 };
91
92 pub const FPImmediateType = enum(u2) {
93 half = 0b00,
94 single = 0b01,
95 double = 0b11,
96 };
97
98 pub const Instruction = packed union {
99 data_processing_imm: DataProcessingImmediate,
100 data_processing_reg: DataProcessingRegister,
101 load_store: LoadStore,
102 branch: Branch,
103 fp_data_processing: FPDataProcessing,
104 raw: u32,
105
106 pub const size = 4;
107
108 pub fn toBytes(self: Instruction) [4]u8 {
109 var buf: [4]u8 = undefined;
110 std.mem.writeInt(u32, buf[0..4], self.raw, .little);
111 return buf;
112 }
113
114 pub fn fromBytes(bytes: [4]u8) Instruction {
115 return .{ .raw = std.mem.readInt(u32, bytes[0..4], .little) };
116 }
117
118 pub fn write(self: Instruction, buf: []u8) void {
119 std.mem.writeInt(u32, buf[0..4], self.raw, .little);
120 }
121 };
122
123 pub const DataProcessingImmediate = packed union {
124 add_sub_imm: AddSubImmediate,
125 move_wide: MoveWide,
126 logical_imm: LogicalImmediate,
127 raw: u32,
128 };
129
130 pub const AddSubImmediate = packed struct(u32) {
131 rd: Encoded,
132 rn: Encoded,
133 imm12: u12,
134 sh: u1,
135 fixed: u6 = 0b100010,
136 s: u1,
137 op: AddSubOp,
138 sf: u1,
139
140 pub const AddSubOp = enum(u1) { add = 0, sub = 1 };
141
142 pub fn add(rd: Encoded, rn: Encoded, imm: u12, size: GPRSize) Instruction {
143 return .{
144 .data_processing_imm = .{
145 .add_sub_imm = .{
146 .rd = rd,
147 .rn = rn,
148 .imm12 = imm,
149 .sh = 0,
150 .s = 0,
151 .op = .add,
152 .sf = @backingInt(size),
153 },
154 },
155 };
156 }
157
158 pub fn adds(rd: Encoded, rn: Encoded, imm: u12, size: GPRSize) Instruction {
159 return .{
160 .data_processing_imm = .{
161 .add_sub_imm = .{
162 .rd = rd,
163 .rn = rn,
164 .imm12 = imm,
165 .sh = 0,
166 .s = 1,
167 .op = .add,
168 .sf = @backingInt(size),
169 },
170 },
171 };
172 }
173
174 pub fn sub(rd: Encoded, rn: Encoded, imm: u12, size: GPRSize) Instruction {
175 return .{
176 .data_processing_imm = .{
177 .add_sub_imm = .{
178 .rd = rd,
179 .rn = rn,
180 .imm12 = imm,
181 .sh = 0,
182 .s = 0,
183 .op = .sub,
184 .sf = @backingInt(size),
185 },
186 },
187 };
188 }
189
190 pub fn subs(rd: Encoded, rn: Encoded, imm: u12, size: GPRSize) Instruction {
191 return .{
192 .data_processing_imm = .{
193 .add_sub_imm = .{
194 .rd = rd,
195 .rn = rn,
196 .imm12 = imm,
197 .sh = 0,
198 .s = 1,
199 .op = .sub,
200 .sf = @backingInt(size),
201 },
202 },
203 };
204 }
205
206 pub fn cmpImm(rn: Encoded, imm: u12, size: GPRSize) Instruction {
207 return subs(registers.ZR.encoded, rn, imm, size);
208 }
209 };
210
211 pub const MoveWide = packed struct(u32) {
212 rd: Encoded,
213 imm16: u16,
214 hw: u2,
215 fixed: u6 = 0b100101,
216 opc: MoveWideOpcode,
217 sf: u1,
218
219 pub fn movz(rd: Encoded, imm: u16, shift: u2, size: GPRSize) Instruction {
220 return .{
221 .data_processing_imm = .{
222 .move_wide = .{
223 .rd = rd,
224 .imm16 = imm,
225 .hw = shift,
226 .opc = .movz,
227 .sf = @backingInt(size),
228 },
229 },
230 };
231 }
232
233 pub fn movk(rd: Encoded, imm: u16, shift: u2, size: GPRSize) Instruction {
234 return .{
235 .data_processing_imm = .{
236 .move_wide = .{
237 .rd = rd,
238 .imm16 = imm,
239 .hw = shift,
240 .opc = .movk,
241 .sf = @backingInt(size),
242 },
243 },
244 };
245 }
246
247 pub fn movn(rd: Encoded, imm: u16, shift: u2, size: GPRSize) Instruction {
248 return .{
249 .data_processing_imm = .{
250 .move_wide = .{
251 .rd = rd,
252 .imm16 = imm,
253 .hw = shift,
254 .opc = .movn,
255 .sf = @backingInt(size),
256 },
257 },
258 };
259 }
260 };
261
262 pub const LogicalImmediate = packed struct(u32) {
263 rd: Encoded,
264 rn: Encoded,
265 imms: u6,
266 immr: u6,
267 n: u1,
268 fixed: u6 = 0b100100,
269 opc: LogicalOpcode,
270 sf: u1,
271 };
272
273 pub const DataProcessingRegister = packed union {
274 add_sub_shifted: AddSubShiftedRegister,
275 add_sub_extended: AddSubExtendedRegister,
276 logical_shifted: LogicalShiftedRegister,
277 data_proc_2src: DataProcessing2Source,
278 data_proc_3src: DataProcessing3Source,
279 conditional_select: ConditionalSelect,
280 raw: u32,
281 };
282
283 pub const AddSubShiftedRegister = packed struct(u32) {
284 rd: Encoded,
285 rn: Encoded,
286 imm6: u6,
287 rm: Encoded,
288 fixed0: u1 = 0,
289 shift: AddSubShift,
290 fixed1: u5 = 0b01011,
291 s: u1,
292 op: u1,
293 sf: u1,
294
295 pub fn addReg(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
296 return .{
297 .data_processing_reg = .{
298 .add_sub_shifted = .{
299 .rd = rd,
300 .rn = rn,
301 .imm6 = 0,
302 .rm = rm,
303 .shift = .lsl,
304 .s = 0,
305 .op = 0,
306 .sf = @backingInt(size),
307 },
308 },
309 };
310 }
311
312 pub fn addsReg(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
313 return .{
314 .data_processing_reg = .{
315 .add_sub_shifted = .{
316 .rd = rd,
317 .rn = rn,
318 .imm6 = 0,
319 .rm = rm,
320 .shift = .lsl,
321 .s = 1,
322 .op = 0,
323 .sf = @backingInt(size),
324 },
325 },
326 };
327 }
328
329 pub fn subReg(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
330 return .{
331 .data_processing_reg = .{
332 .add_sub_shifted = .{
333 .rd = rd,
334 .rn = rn,
335 .imm6 = 0,
336 .rm = rm,
337 .shift = .lsl,
338 .s = 0,
339 .op = 1,
340 .sf = @backingInt(size),
341 },
342 },
343 };
344 }
345
346 pub fn subsReg(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
347 return .{
348 .data_processing_reg = .{
349 .add_sub_shifted = .{
350 .rd = rd,
351 .rn = rn,
352 .imm6 = 0,
353 .rm = rm,
354 .shift = .lsl,
355 .s = 1,
356 .op = 1,
357 .sf = @backingInt(size),
358 },
359 },
360 };
361 }
362
363 pub fn cmpReg(rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
364 return subsReg(registers.ZR.encoded, rn, rm, size);
365 }
366 };
367
368 pub const AddSubExtendedRegister = packed struct(u32) {
369 rd: Encoded,
370 rn: Encoded,
371 imm3: u3,
372 option: AddSubExtend,
373 rm: Encoded,
374 fixed0: u1 = 1,
375 fixed1: u2 = 0b01,
376 fixed2: u5 = 0b01011,
377 s: u1,
378 op: u1,
379 sf: u1,
380 };
381
382 pub const LogicalShiftedRegister = packed struct(u32) {
383 rd: Encoded,
384 rn: Encoded,
385 imm6: u6,
386 rm: Encoded,
387 n: u1,
388 shift: LogicalShift,
389 fixed: u5 = 0b01010,
390 opc: LogicalOpcode,
391 sf: u1,
392
393 pub fn andReg(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
394 return .{
395 .data_processing_reg = .{
396 .logical_shifted = .{
397 .rd = rd,
398 .rn = rn,
399 .imm6 = 0,
400 .rm = rm,
401 .n = 0,
402 .shift = .lsl,
403 .opc = .@"and",
404 .sf = @backingInt(size),
405 },
406 },
407 };
408 }
409
410 pub fn orrReg(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
411 return .{
412 .data_processing_reg = .{
413 .logical_shifted = .{
414 .rd = rd,
415 .rn = rn,
416 .imm6 = 0,
417 .rm = rm,
418 .n = 0,
419 .shift = .lsl,
420 .opc = .orr,
421 .sf = @backingInt(size),
422 },
423 },
424 };
425 }
426
427 pub fn eorReg(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
428 return .{
429 .data_processing_reg = .{
430 .logical_shifted = .{
431 .rd = rd,
432 .rn = rn,
433 .imm6 = 0,
434 .rm = rm,
435 .n = 0,
436 .shift = .lsl,
437 .opc = .eor,
438 .sf = @backingInt(size),
439 },
440 },
441 };
442 }
443
444 pub fn movReg(rd: Encoded, rm: Encoded, size: GPRSize) Instruction {
445 return orrReg(rd, registers.ZR.encoded, rm, size);
446 }
447 };
448
449 pub const DataProcessing2Source = packed struct(u32) {
450 rd: Encoded,
451 rn: Encoded,
452 opcode: DataProcessing2SourceOpcode,
453 rm: Encoded,
454 fixed: u8 = 0b11010110,
455 s: u1 = 0,
456 fixed2: u1 = 0,
457 sf: u1,
458
459 pub fn udiv(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
460 return .{
461 .data_processing_reg = .{
462 .data_proc_2src = .{
463 .rd = rd,
464 .rn = rn,
465 .opcode = .udiv,
466 .rm = rm,
467 .sf = @backingInt(size),
468 },
469 },
470 };
471 }
472
473 pub fn sdiv(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
474 return .{
475 .data_processing_reg = .{
476 .data_proc_2src = .{
477 .rd = rd,
478 .rn = rn,
479 .opcode = .sdiv,
480 .rm = rm,
481 .sf = @backingInt(size),
482 },
483 },
484 };
485 }
486 };
487
488 pub const DataProcessing3Source = packed struct(u32) {
489 rd: Encoded,
490 rn: Encoded,
491 ra: Encoded,
492 o0: u1,
493 rm: Encoded,
494 op31: u3,
495 fixed: u5 = 0b11011,
496 op54: u2,
497 sf: u1,
498
499 pub fn mul(rd: Encoded, rn: Encoded, rm: Encoded, size: GPRSize) Instruction {
500 return madd(rd, rn, rm, registers.ZR.encoded, size);
501 }
502
503 pub fn madd(rd: Encoded, rn: Encoded, rm: Encoded, ra: Encoded, size: GPRSize) Instruction {
504 return .{
505 .data_processing_reg = .{
506 .data_proc_3src = .{
507 .rd = rd,
508 .rn = rn,
509 .ra = ra,
510 .o0 = 0,
511 .rm = rm,
512 .op31 = 0b000,
513 .op54 = 0b00,
514 .sf = @backingInt(size),
515 },
516 },
517 };
518 }
519
520 pub fn msub(rd: Encoded, rn: Encoded, rm: Encoded, ra: Encoded, size: GPRSize) Instruction {
521 return .{
522 .data_processing_reg = .{
523 .data_proc_3src = .{
524 .rd = rd,
525 .rn = rn,
526 .ra = ra,
527 .o0 = 1,
528 .rm = rm,
529 .op31 = 0b000,
530 .op54 = 0b00,
531 .sf = @backingInt(size),
532 },
533 },
534 };
535 }
536 };
537
538 pub const ConditionalSelect = packed struct(u32) {
539 rd: Encoded,
540 rn: Encoded,
541 op2: u2,
542 cond: Condition,
543 rm: Encoded,
544 fixed0: u1 = 0,
545 fixed1: u2 = 0b10,
546 fixed2: u5 = 0b11010,
547 s: u1 = 0,
548 op: u1,
549 sf: u1,
550
551 pub fn csel(rd: Encoded, rn: Encoded, rm: Encoded, cond: Condition, size: GPRSize) Instruction {
552 return .{
553 .data_processing_reg = .{
554 .conditional_select = .{
555 .rd = rd,
556 .rn = rn,
557 .op2 = 0b00,
558 .cond = cond,
559 .rm = rm,
560 .op = 0,
561 .sf = @backingInt(size),
562 },
563 },
564 };
565 }
566
567 pub fn cset(rd: Encoded, cond: Condition, size: GPRSize) Instruction {
568 return .{
569 .data_processing_reg = .{
570 .conditional_select = .{
571 .rd = rd,
572 .rn = registers.ZR.encoded,
573 .op2 = 0b01,
574 .cond = cond.invert(),
575 .rm = registers.ZR.encoded,
576 .op = 0,
577 .sf = @backingInt(size),
578 },
579 },
580 };
581 }
582 };
583
584 pub const LoadStore = packed union {
585 load_store_reg_imm: LoadStoreRegImm,
586 load_store_reg_imm_fp: LoadStoreRegImmFP,
587 load_store_pair: LoadStorePair,
588 load_store_pair_fp: LoadStorePairFP,
589 raw: u32,
590 };
591
592 pub const LoadStoreRegImm = packed struct(u32) {
593 rt: Encoded,
594 rn: Encoded,
595 imm12: u12,
596 opc: u2,
597 fixed_01: u2 = 0b01,
598 v: u1 = 0,
599 fixed_111: u3 = 0b111,
600 size: u2,
601
602 pub fn ldr(rt: Encoded, rn: Encoded, offset: u12, gpr_size: GPRSize) Instruction {
603 return .{
604 .load_store = .{
605 .load_store_reg_imm = .{
606 .rt = rt,
607 .rn = rn,
608 .imm12 = offset,
609 .opc = 0b01,
610 .size = switch (gpr_size) {
611 .w => 0b10,
612 .x => 0b11,
613 },
614 },
615 },
616 };
617 }
618
619 pub fn str(rt: Encoded, rn: Encoded, offset: u12, gpr_size: GPRSize) Instruction {
620 return .{
621 .load_store = .{
622 .load_store_reg_imm = .{
623 .rt = rt,
624 .rn = rn,
625 .imm12 = offset,
626 .opc = 0b00,
627 .size = switch (gpr_size) {
628 .w => 0b10,
629 .x => 0b11,
630 },
631 },
632 },
633 };
634 }
635 };
636
637 pub const LoadStoreRegImmFP = packed struct(u32) {
638 rt: Encoded,
639 rn: Encoded,
640 imm12: u12,
641 opc: u2,
642 fixed_01: u2 = 0b01,
643 v: u1 = 1,
644 fixed_111: u3 = 0b111,
645 size: u2,
646
647 pub fn ldr(rt: Encoded, rn: Encoded, offset: u12, fp_size: LoadStoreFPSize) Instruction {
648 return .{
649 .load_store = .{
650 .load_store_reg_imm_fp = .{
651 .rt = rt,
652 .rn = rn,
653 .imm12 = offset,
654 .opc = 0b01,
655 .size = @backingInt(fp_size),
656 },
657 },
658 };
659 }
660
661 pub fn str(rt: Encoded, rn: Encoded, offset: u12, fp_size: LoadStoreFPSize) Instruction {
662 return .{
663 .load_store = .{
664 .load_store_reg_imm_fp = .{
665 .rt = rt,
666 .rn = rn,
667 .imm12 = offset,
668 .opc = 0b00,
669 .size = @backingInt(fp_size),
670 },
671 },
672 };
673 }
674 };
675
676 fn scalePairOffsetBytes(byte_offset: i32, scale: i32) i7 {
677 std.debug.assert(scale > 0);
678 std.debug.assert(@mod(byte_offset, scale) == 0);
679 return @intCast(@divTrunc(byte_offset, scale));
680 }
681
682 pub const LoadStorePair = packed struct(u32) {
683 rt: Encoded,
684 rn: Encoded,
685 rt2: Encoded,
686 imm7: i7,
687 l: u1,
688 op2: LoadStorePairMode,
689 v: u1 = 0,
690 fixed: u3 = 0b101,
691 opc: u2,
692
693 pub fn stp(rt: Encoded, rt2: Encoded, rn: Encoded, offset: i7, gpr_size: GPRSize) Instruction {
694 return .{
695 .load_store = .{
696 .load_store_pair = .{
697 .rt = rt,
698 .rn = rn,
699 .rt2 = rt2,
700 .imm7 = offset,
701 .l = 0,
702 .op2 = .signed_offset,
703 .opc = @as(u2, @backingInt(gpr_size)) << 1,
704 },
705 },
706 };
707 }
708
709 pub fn stpBytes(rt: Encoded, rt2: Encoded, rn: Encoded, byte_offset: i32, gpr_size: GPRSize) Instruction {
710 const scale: i32 = switch (gpr_size) {
711 .w => 4,
712 .x => 8,
713 };
714 return stp(rt, rt2, rn, scalePairOffsetBytes(byte_offset, scale), gpr_size);
715 }
716
717 pub fn stpPre(rt: Encoded, rt2: Encoded, rn: Encoded, offset: i7, gpr_size: GPRSize) Instruction {
718 return .{
719 .load_store = .{
720 .load_store_pair = .{
721 .rt = rt,
722 .rn = rn,
723 .rt2 = rt2,
724 .imm7 = offset,
725 .l = 0,
726 .op2 = .pre_index,
727 .opc = @as(u2, @backingInt(gpr_size)) << 1,
728 },
729 },
730 };
731 }
732
733 pub fn stpPreBytes(rt: Encoded, rt2: Encoded, rn: Encoded, byte_offset: i32, gpr_size: GPRSize) Instruction {
734 const scale: i32 = switch (gpr_size) {
735 .w => 4,
736 .x => 8,
737 };
738 return stpPre(rt, rt2, rn, scalePairOffsetBytes(byte_offset, scale), gpr_size);
739 }
740
741 pub fn ldp(rt: Encoded, rt2: Encoded, rn: Encoded, offset: i7, gpr_size: GPRSize) Instruction {
742 return .{
743 .load_store = .{
744 .load_store_pair = .{
745 .rt = rt,
746 .rn = rn,
747 .rt2 = rt2,
748 .imm7 = offset,
749 .l = 1,
750 .op2 = .signed_offset,
751 .opc = @as(u2, @backingInt(gpr_size)) << 1,
752 },
753 },
754 };
755 }
756
757 pub fn ldpBytes(rt: Encoded, rt2: Encoded, rn: Encoded, byte_offset: i32, gpr_size: GPRSize) Instruction {
758 const scale: i32 = switch (gpr_size) {
759 .w => 4,
760 .x => 8,
761 };
762 return ldp(rt, rt2, rn, scalePairOffsetBytes(byte_offset, scale), gpr_size);
763 }
764
765 pub fn ldpPost(rt: Encoded, rt2: Encoded, rn: Encoded, offset: i7, gpr_size: GPRSize) Instruction {
766 return .{
767 .load_store = .{
768 .load_store_pair = .{
769 .rt = rt,
770 .rn = rn,
771 .rt2 = rt2,
772 .imm7 = offset,
773 .l = 1,
774 .op2 = .post_index,
775 .opc = @as(u2, @backingInt(gpr_size)) << 1,
776 },
777 },
778 };
779 }
780
781 pub fn ldpPostBytes(rt: Encoded, rt2: Encoded, rn: Encoded, byte_offset: i32, gpr_size: GPRSize) Instruction {
782 const scale: i32 = switch (gpr_size) {
783 .w => 4,
784 .x => 8,
785 };
786 return ldpPost(rt, rt2, rn, scalePairOffsetBytes(byte_offset, scale), gpr_size);
787 }
788 };
789
790 pub const LoadStorePairFP = packed struct(u32) {
791 rt: Encoded,
792 rn: Encoded,
793 rt2: Encoded,
794 imm7: i7,
795 l: u1,
796 op2: LoadStorePairMode,
797 v: u1 = 1,
798 fixed: u3 = 0b101,
799 opc: LoadStorePairFPSize,
800
801 pub fn stp(
802 rt: Encoded,
803 rt2: Encoded,
804 rn: Encoded,
805 offset: i7,
806 size: LoadStorePairFPSize,
807 ) Instruction {
808 return .{
809 .load_store = .{
810 .load_store_pair_fp = .{
811 .rt = rt,
812 .rn = rn,
813 .rt2 = rt2,
814 .imm7 = offset,
815 .l = 0,
816 .op2 = .signed_offset,
817 .opc = size,
818 },
819 },
820 };
821 }
822
823 pub fn stpBytes(
824 rt: Encoded,
825 rt2: Encoded,
826 rn: Encoded,
827 byte_offset: i32,
828 size: LoadStorePairFPSize,
829 ) Instruction {
830 const scale: i32 = switch (size) {
831 .single => 4,
832 .double => 8,
833 .quad => 16,
834 };
835 return stp(rt, rt2, rn, scalePairOffsetBytes(byte_offset, scale), size);
836 }
837
838 pub fn stpPre(
839 rt: Encoded,
840 rt2: Encoded,
841 rn: Encoded,
842 offset: i7,
843 size: LoadStorePairFPSize,
844 ) Instruction {
845 return .{
846 .load_store = .{
847 .load_store_pair_fp = .{
848 .rt = rt,
849 .rn = rn,
850 .rt2 = rt2,
851 .imm7 = offset,
852 .l = 0,
853 .op2 = .pre_index,
854 .opc = size,
855 },
856 },
857 };
858 }
859
860 pub fn stpPreBytes(
861 rt: Encoded,
862 rt2: Encoded,
863 rn: Encoded,
864 byte_offset: i32,
865 size: LoadStorePairFPSize,
866 ) Instruction {
867 const scale: i32 = switch (size) {
868 .single => 4,
869 .double => 8,
870 .quad => 16,
871 };
872 return stpPre(rt, rt2, rn, scalePairOffsetBytes(byte_offset, scale), size);
873 }
874
875 pub fn ldp(
876 rt: Encoded,
877 rt2: Encoded,
878 rn: Encoded,
879 offset: i7,
880 size: LoadStorePairFPSize,
881 ) Instruction {
882 return .{
883 .load_store = .{
884 .load_store_pair_fp = .{
885 .rt = rt,
886 .rn = rn,
887 .rt2 = rt2,
888 .imm7 = offset,
889 .l = 1,
890 .op2 = .signed_offset,
891 .opc = size,
892 },
893 },
894 };
895 }
896
897 pub fn ldpBytes(
898 rt: Encoded,
899 rt2: Encoded,
900 rn: Encoded,
901 byte_offset: i32,
902 size: LoadStorePairFPSize,
903 ) Instruction {
904 const scale: i32 = switch (size) {
905 .single => 4,
906 .double => 8,
907 .quad => 16,
908 };
909 return ldp(rt, rt2, rn, scalePairOffsetBytes(byte_offset, scale), size);
910 }
911
912 pub fn ldpPost(
913 rt: Encoded,
914 rt2: Encoded,
915 rn: Encoded,
916 offset: i7,
917 size: LoadStorePairFPSize,
918 ) Instruction {
919 return .{
920 .load_store = .{
921 .load_store_pair_fp = .{
922 .rt = rt,
923 .rn = rn,
924 .rt2 = rt2,
925 .imm7 = offset,
926 .l = 1,
927 .op2 = .post_index,
928 .opc = size,
929 },
930 },
931 };
932 }
933
934 pub fn ldpPostBytes(
935 rt: Encoded,
936 rt2: Encoded,
937 rn: Encoded,
938 byte_offset: i32,
939 size: LoadStorePairFPSize,
940 ) Instruction {
941 const scale: i32 = switch (size) {
942 .single => 4,
943 .double => 8,
944 .quad => 16,
945 };
946 return ldpPost(rt, rt2, rn, scalePairOffsetBytes(byte_offset, scale), size);
947 }
948 };
949
950 pub const Branch = packed union {
951 unconditional_imm: UnconditionalBranchImm,
952 conditional: ConditionalBranch,
953 unconditional_reg: UnconditionalBranchReg,
954 raw: u32,
955 };
956
957 pub const UnconditionalBranchImm = packed struct(u32) {
958 imm26: i26,
959 fixed: u5 = 0b00101,
960 op: u1,
961
962 pub fn b(offset: i26) Instruction {
963 return .{
964 .branch = .{
965 .unconditional_imm = .{
966 .imm26 = offset,
967 .op = 0,
968 },
969 },
970 };
971 }
972
973 pub fn bl(offset: i26) Instruction {
974 return .{
975 .branch = .{
976 .unconditional_imm = .{
977 .imm26 = offset,
978 .op = 1,
979 },
980 },
981 };
982 }
983 };
984
985 pub const ConditionalBranch = packed struct(u32) {
986 cond: Condition,
987 fixed0: u1 = 0,
988 imm19: i19,
989 fixed1: u1 = 0,
990 fixed2: u7 = 0b0101010,
991
992 pub fn bCond(cond: Condition, offset: i19) Instruction {
993 return .{
994 .branch = .{
995 .conditional = .{
996 .cond = cond,
997 .imm19 = offset,
998 },
999 },
1000 };
1001 }
1002 };
1003
1004 pub const UnconditionalBranchReg = packed struct(u32) {
1005 fixed0: u5 = 0b00000,
1006 rn: Encoded,
1007 fixed1: u6 = 0b000000,
1008 op2: u5,
1009 opc: u4,
1010 fixed2: u7 = 0b1101011,
1011
1012 pub fn ret(rn: Encoded) Instruction {
1013 return .{
1014 .branch = .{
1015 .unconditional_reg = .{
1016 .rn = rn,
1017 .op2 = 0b11111,
1018 .opc = 0b0010,
1019 },
1020 },
1021 };
1022 }
1023
1024 pub fn retLr() Instruction {
1025 return ret(GPR.lr.encode());
1026 }
1027
1028 pub fn br(rn: Encoded) Instruction {
1029 return .{
1030 .branch = .{
1031 .unconditional_reg = .{
1032 .rn = rn,
1033 .op2 = 0b11111,
1034 .opc = 0b0000,
1035 },
1036 },
1037 };
1038 }
1039
1040 pub fn blr(rn: Encoded) Instruction {
1041 return .{
1042 .branch = .{
1043 .unconditional_reg = .{
1044 .rn = rn,
1045 .op2 = 0b11111,
1046 .opc = 0b0001,
1047 },
1048 },
1049 };
1050 }
1051 };
1052
1053 pub const FPDataProcessing = packed union {
1054 fp_1src: FP1Source,
1055 fp_2src: FP2Source,
1056 fp_compare: FPCompare,
1057 fp_conversion: FPConversion,
1058 fp_imm: FPImmediate,
1059 fmov_general: FMovGeneral,
1060 raw: u32,
1061 };
1062
1063 pub const FP1Source = packed struct(u32) {
1064 rd: Encoded,
1065 rn: Encoded,
1066 fixed0: u5 = 0b10000,
1067 opcode: FP1SourceOpcode,
1068 fixed1: u1 = 1,
1069 ptype: FP2Source.FPType,
1070 fixed2: u5 = 0b11110,
1071 s: u1 = 0,
1072 fixed3: u1 = 0,
1073 m: u1 = 0,
1074
1075 pub fn fabs(rd: Encoded, rn: Encoded, ftype: FP2Source.FPType) Instruction {
1076 return .{
1077 .fp_data_processing = .{
1078 .fp_1src = .{
1079 .rd = rd,
1080 .rn = rn,
1081 .opcode = .fabs,
1082 .ptype = ftype,
1083 },
1084 },
1085 };
1086 }
1087
1088 pub fn fneg(rd: Encoded, rn: Encoded, ftype: FP2Source.FPType) Instruction {
1089 return .{
1090 .fp_data_processing = .{
1091 .fp_1src = .{
1092 .rd = rd,
1093 .rn = rn,
1094 .opcode = .fneg,
1095 .ptype = ftype,
1096 },
1097 },
1098 };
1099 }
1100
1101 pub fn fsqrt(rd: Encoded, rn: Encoded, ftype: FP2Source.FPType) Instruction {
1102 return .{
1103 .fp_data_processing = .{
1104 .fp_1src = .{
1105 .rd = rd,
1106 .rn = rn,
1107 .opcode = .fsqrt,
1108 .ptype = ftype,
1109 },
1110 },
1111 };
1112 }
1113
1114 pub fn fmov(rd: Encoded, rn: Encoded, ftype: FP2Source.FPType) Instruction {
1115 return .{
1116 .fp_data_processing = .{
1117 .fp_1src = .{
1118 .rd = rd,
1119 .rn = rn,
1120 .opcode = .fmov,
1121 .ptype = ftype,
1122 },
1123 },
1124 };
1125 }
1126 };
1127
1128 pub const FP2Source = packed struct(u32) {
1129 rd: Encoded,
1130 rn: Encoded,
1131 fixed0: u2 = 0b10,
1132 opcode: FP2SourceOpcode,
1133 rm: Encoded,
1134 fixed1: u1 = 1,
1135 ptype: FPType,
1136 fixed2: u5 = 0b11110,
1137 m: u1 = 0,
1138 fixed3: u1 = 0,
1139 s: u1 = 0,
1140
1141 pub const FPType = enum(u2) {
1142 single = 0b00,
1143 double = 0b01,
1144 half = 0b11,
1145 };
1146
1147 pub fn fadd(rd: Encoded, rn: Encoded, rm: Encoded, ftype: FPType) Instruction {
1148 return .{
1149 .fp_data_processing = .{
1150 .fp_2src = .{
1151 .rd = rd,
1152 .rn = rn,
1153 .opcode = .fadd,
1154 .rm = rm,
1155 .ptype = ftype,
1156 },
1157 },
1158 };
1159 }
1160
1161 pub fn fsub(rd: Encoded, rn: Encoded, rm: Encoded, ftype: FPType) Instruction {
1162 return .{
1163 .fp_data_processing = .{
1164 .fp_2src = .{
1165 .rd = rd,
1166 .rn = rn,
1167 .opcode = .fsub,
1168 .rm = rm,
1169 .ptype = ftype,
1170 },
1171 },
1172 };
1173 }
1174
1175 pub fn fmul(rd: Encoded, rn: Encoded, rm: Encoded, ftype: FPType) Instruction {
1176 return .{
1177 .fp_data_processing = .{
1178 .fp_2src = .{
1179 .rd = rd,
1180 .rn = rn,
1181 .opcode = .fmul,
1182 .rm = rm,
1183 .ptype = ftype,
1184 },
1185 },
1186 };
1187 }
1188
1189 pub fn fdiv(rd: Encoded, rn: Encoded, rm: Encoded, ftype: FPType) Instruction {
1190 return .{
1191 .fp_data_processing = .{
1192 .fp_2src = .{
1193 .rd = rd,
1194 .rn = rn,
1195 .opcode = .fdiv,
1196 .rm = rm,
1197 .ptype = ftype,
1198 },
1199 },
1200 };
1201 }
1202 };
1203
1204 pub const FPCompare = packed struct(u32) {
1205 opcode2: u5,
1206 rn: Encoded,
1207 fixed0: u4 = 0b1000,
1208 op: u2,
1209 rm: Encoded,
1210 fixed1: u1 = 1,
1211 ptype: FP2Source.FPType,
1212 fixed2: u5 = 0b11110,
1213 m: u1 = 0,
1214 fixed3: u1 = 0,
1215 s: u1 = 0,
1216
1217 pub fn fcmp(rn: Encoded, rm: Encoded, ftype: FP2Source.FPType) Instruction {
1218 return .{
1219 .fp_data_processing = .{
1220 .fp_compare = .{
1221 .opcode2 = 0b00000,
1222 .rn = rn,
1223 .op = 0b00,
1224 .rm = rm,
1225 .ptype = ftype,
1226 },
1227 },
1228 };
1229 }
1230
1231 pub fn fcmpZero(rn: Encoded, ftype: FP2Source.FPType) Instruction {
1232 return .{
1233 .fp_data_processing = .{
1234 .fp_compare = .{
1235 .opcode2 = 0b01000,
1236 .rn = rn,
1237 .op = 0b00,
1238 .rm = @fromBackingInt(@intCast(0)),
1239 .ptype = ftype,
1240 },
1241 },
1242 };
1243 }
1244 };
1245
1246 pub const FPConversion = packed struct(u32) {
1247 rd: Encoded,
1248 rn: Encoded,
1249 fixed0: u6 = 0b000000,
1250 opcode: u3,
1251 rmode: u2,
1252 fixed1: u1 = 0,
1253 ptype: u2,
1254 fixed2: u5 = 0b11110,
1255 s: u1 = 0,
1256 fixed3: u1 = 0,
1257 sf: u1,
1258 };
1259
1260 pub const FPImmediate = packed struct(u32) {
1261 rd: Encoded,
1262 fixed0: u5 = 0b00000,
1263 fixed1: u3 = 0b100,
1264 imm8: u8,
1265 ptype: FPImmediateType,
1266 fixed2: u5 = 0b11100,
1267 fixed3: u4 = 0b0001,
1268
1269 fn immTypeFromFP(ftype: FP2Source.FPType) FPImmediateType {
1270 return switch (ftype) {
1271 .half => .half,
1272 .single => .single,
1273 .double => .double,
1274 };
1275 }
1276
1277 pub fn fmovImm(rd: Encoded, imm8: u8, ftype: FP2Source.FPType) Instruction {
1278 return .{
1279 .fp_data_processing = .{
1280 .fp_imm = .{
1281 .rd = rd,
1282 .imm8 = imm8,
1283 .ptype = immTypeFromFP(ftype),
1284 },
1285 },
1286 };
1287 }
1288
1289 pub fn encodeF32(value: f32) ?u8 {
1290 const bits: u32 = @bitCast(value);
1291 const sign: u8 = @intCast(bits >> 31);
1292 const exp: u8 = @intCast((bits >> 23) & 0xFF);
1293 const frac: u32 = bits & 0x7FFFFF;
1294
1295 const exp_high: u8 = exp >> 3;
1296 if (exp_high != 0x0F and exp_high != 0x10) return null;
1297 if ((frac & 0x7FFFF) != 0) return null;
1298
1299 const exp_low: u8 = exp & 0x7;
1300 const frac_top: u8 = @intCast(frac >> 19);
1301 return (sign << 7) | (exp_low << 4) | frac_top;
1302 }
1303
1304 pub fn encodeF64(value: f64) ?u8 {
1305 const bits: u64 = @bitCast(value);
1306 const sign: u8 = @intCast(bits >> 63);
1307 const exp: u16 = @intCast((bits >> 52) & 0x7FF);
1308 const frac: u64 = bits & 0xFFFFFFFFFFFFF;
1309
1310 const exp_high: u16 = exp >> 3;
1311 if (exp_high != 0x7F and exp_high != 0x80) return null;
1312 if ((frac & 0xFFFFFFFFFFFF) != 0) return null;
1313
1314 const exp_low: u8 = @intCast(exp & 0x7);
1315 const frac_top: u8 = @intCast(frac >> 48);
1316 return (sign << 7) | (exp_low << 4) | frac_top;
1317 }
1318 };
1319
1320 pub const FMovGeneral = packed struct(u32) {
1321 rd: Encoded,
1322 rn: Encoded,
1323 fixed0: u6 = 0b000000,
1324 opcode: u3,
1325 rmode: u2,
1326 fixed1: u1 = 1,
1327 ptype: u2,
1328 fixed2: u5 = 0b11110,
1329 s: u1 = 0,
1330 fixed3: u1 = 0,
1331 sf: u1,
1332
1333 pub fn fmovToFprSingle(rd: Encoded, rn: Encoded) Instruction {
1334 return .{
1335 .fp_data_processing = .{
1336 .fmov_general = .{
1337 .rd = rd,
1338 .rn = rn,
1339 .opcode = 0b111,
1340 .rmode = 0b00,
1341 .ptype = 0b00,
1342 .sf = 0,
1343 },
1344 },
1345 };
1346 }
1347
1348 pub fn fmovToFprDouble(rd: Encoded, rn: Encoded) Instruction {
1349 return .{
1350 .fp_data_processing = .{
1351 .fmov_general = .{
1352 .rd = rd,
1353 .rn = rn,
1354 .opcode = 0b111,
1355 .rmode = 0b00,
1356 .ptype = 0b01,
1357 .sf = 1,
1358 },
1359 },
1360 };
1361 }
1362
1363 pub fn fmovFromFprSingle(rd: Encoded, rn: Encoded) Instruction {
1364 return .{
1365 .fp_data_processing = .{
1366 .fmov_general = .{
1367 .rd = rd,
1368 .rn = rn,
1369 .opcode = 0b110,
1370 .rmode = 0b00,
1371 .ptype = 0b00,
1372 .sf = 0,
1373 },
1374 },
1375 };
1376 }
1377
1378 pub fn fmovFromFprDouble(rd: Encoded, rn: Encoded) Instruction {
1379 return .{
1380 .fp_data_processing = .{
1381 .fmov_general = .{
1382 .rd = rd,
1383 .rn = rn,
1384 .opcode = 0b110,
1385 .rmode = 0b00,
1386 .ptype = 0b01,
1387 .sf = 1,
1388 },
1389 },
1390 };
1391 }
1392 };
1393
1394 pub const Condition = enum(u4) {
1395 eq = 0b0000,
1396 ne = 0b0001,
1397 cs = 0b0010,
1398 cc = 0b0011,
1399 mi = 0b0100,
1400 pl = 0b0101,
1401 vs = 0b0110,
1402 vc = 0b0111,
1403 hi = 0b1000,
1404 ls = 0b1001,
1405 ge = 0b1010,
1406 lt = 0b1011,
1407 gt = 0b1100,
1408 le = 0b1101,
1409 al = 0b1110,
1410 nv = 0b1111,
1411
1412 pub const hs = Condition.cs;
1413 pub const lo = Condition.cc;
1414
1415 pub fn invert(self: Condition) Condition {
1416 return @fromBackingInt(@intCast(@backingInt(self) ^ 1));
1417 }
1418 };
1419
1420 fn expectEncoded(inst: Instruction, expected_hex: u32) !void {
1421 if (inst.raw == expected_hex) return;
1422
1423 const testing = std.testing;
1424 const diff = inst.raw ^ expected_hex;
1425 var expected_asm_buf: [160]u8 = undefined;
1426 var actual_asm_buf: [160]u8 = undefined;
1427 const expected_asm = disassemble.formatInstructionRawWithDetail(expected_hex, &expected_asm_buf, true);
1428 const actual_asm = disassemble.formatInstructionRawWithDetail(inst.raw, &actual_asm_buf, true);
1429
1430 writeEncodingMismatch(expected_hex, expected_asm, inst.raw, actual_asm, diff) catch {};
1431
1432 return testing.expectEqual(expected_hex, inst.raw);
1433 }
1434
1435 fn writeEncodingMismatch(
1436 expected_hex: u32,
1437 expected_asm: []const u8,
1438 actual_hex: u32,
1439 actual_asm: []const u8,
1440 diff: u32,
1441 ) !void {
1442 var arena = alloc_arena.Arena.init(std.testing.allocator);
1443 defer arena.deinit();
1444 var report = try pretty.diagnostic.Report.init(
1445 arena.allocator(),
1446 "AArch64 encoding mismatch",
1447 );
1448 defer report.deinit();
1449 try report.field("Expected", "0x{x:0>8} → {s}", .{ expected_hex, expected_asm });
1450 try report.field("Actual", "0x{x:0>8} → {s}", .{ actual_hex, actual_asm });
1451 try report.field("XOR diff", "0x{x:0>8}", .{diff});
1452 try report.section("Bit positions that differ");
1453 var bit_buffer: [128]u8 = undefined;
1454 var bits = std.Io.Writer.fixed(&bit_buffer);
1455 var count: u32 = 0;
1456 for (0..32) |i| {
1457 const bit: u5 = @intCast(31 - i);
1458 if ((diff >> bit) & 1 != 0) {
1459 if (count != 0) try bits.writeByte(' ');
1460 try bits.print("{d}", .{bit});
1461 count += 1;
1462 }
1463 }
1464 if (count == 0) try bits.writeAll("none");
1465 try report.line("{s}", .{bits.buffered()});
1466 pretty.diagnostic.writeStderr(&report, .{ .width = 100 });
1467 }
1468
1469 test "ADD immediate encoding" {
1470 const testing = std.testing;
1471
1472 const inst = AddSubImmediate.add(GPR.x0.encode(), GPR.x1.encode(), 42, .x);
1473
1474 try testing.expectEqual(@as(u5, 0), inst.data_processing_imm.add_sub_imm.rd.toInt());
1475 try testing.expectEqual(@as(u5, 1), inst.data_processing_imm.add_sub_imm.rn.toInt());
1476 try testing.expectEqual(@as(u12, 42), inst.data_processing_imm.add_sub_imm.imm12);
1477 try testing.expectEqual(@as(u1, 1), inst.data_processing_imm.add_sub_imm.sf);
1478 }
1479
1480 test "ADD register encoding" {
1481 const testing = std.testing;
1482
1483 const inst = AddSubShiftedRegister.addReg(GPR.x0.encode(), GPR.x1.encode(), GPR.x2.encode(), .x);
1484
1485 try testing.expectEqual(@as(u5, 0), inst.data_processing_reg.add_sub_shifted.rd.toInt());
1486 try testing.expectEqual(@as(u5, 1), inst.data_processing_reg.add_sub_shifted.rn.toInt());
1487 try testing.expectEqual(@as(u5, 2), inst.data_processing_reg.add_sub_shifted.rm.toInt());
1488 }
1489
1490 test "MUL encoding" {
1491 const testing = std.testing;
1492
1493 const inst = DataProcessing3Source.mul(GPR.x0.encode(), GPR.x1.encode(), GPR.x2.encode(), .x);
1494
1495 try testing.expectEqual(@as(u5, 0), inst.data_processing_reg.data_proc_3src.rd.toInt());
1496 try testing.expectEqual(@as(u5, 1), inst.data_processing_reg.data_proc_3src.rn.toInt());
1497 try testing.expectEqual(@as(u5, 2), inst.data_processing_reg.data_proc_3src.rm.toInt());
1498 try testing.expectEqual(@as(u5, 31), inst.data_processing_reg.data_proc_3src.ra.toInt());
1499 }
1500
1501 test "B.cond encoding" {
1502 const testing = std.testing;
1503
1504 const inst = ConditionalBranch.bCond(.eq, 1);
1505
1506 try testing.expectEqual(Condition.eq, inst.branch.conditional.cond);
1507 try testing.expectEqual(@as(i19, 1), inst.branch.conditional.imm19);
1508 }
1509
1510 test "RET encoding" {
1511 const testing = std.testing;
1512
1513 const inst = UnconditionalBranchReg.retLr();
1514 try testing.expectEqual(@as(u5, 30), inst.branch.unconditional_reg.rn.toInt());
1515 }
1516
1517 test "FADD encoding" {
1518 const testing = std.testing;
1519
1520 const inst = FP2Source.fadd(FPR.v0.encode(), FPR.v1.encode(), FPR.v2.encode(), .double);
1521
1522 try testing.expectEqual(@as(u5, 0), inst.fp_data_processing.fp_2src.rd.toInt());
1523 try testing.expectEqual(@as(u5, 1), inst.fp_data_processing.fp_2src.rn.toInt());
1524 try testing.expectEqual(@as(u5, 2), inst.fp_data_processing.fp_2src.rm.toInt());
1525 try testing.expectEqual(FP2Source.FPType.double, inst.fp_data_processing.fp_2src.ptype);
1526 }
1527
1528 test "FSQRT encoding" {
1529 const testing = std.testing;
1530
1531 const inst = FP1Source.fsqrt(FPR.v0.encode(), FPR.v1.encode(), .double);
1532
1533 try testing.expectEqual(@as(u5, 0), inst.fp_data_processing.fp_1src.rd.toInt());
1534 try testing.expectEqual(@as(u5, 1), inst.fp_data_processing.fp_1src.rn.toInt());
1535 try testing.expectEqual(FP1SourceOpcode.fsqrt, inst.fp_data_processing.fp_1src.opcode);
1536 try testing.expectEqual(FP2Source.FPType.double, inst.fp_data_processing.fp_1src.ptype);
1537 }
1538
1539 test "FNEG encoding" {
1540 const testing = std.testing;
1541
1542 const inst = FP1Source.fneg(FPR.v2.encode(), FPR.v3.encode(), .single);
1543
1544 try testing.expectEqual(@as(u5, 2), inst.fp_data_processing.fp_1src.rd.toInt());
1545 try testing.expectEqual(@as(u5, 3), inst.fp_data_processing.fp_1src.rn.toInt());
1546 try testing.expectEqual(FP1SourceOpcode.fneg, inst.fp_data_processing.fp_1src.opcode);
1547 try testing.expectEqual(FP2Source.FPType.single, inst.fp_data_processing.fp_1src.ptype);
1548 }
1549
1550 test "FABS encoding" {
1551 const testing = std.testing;
1552
1553 const inst = FP1Source.fabs(FPR.v4.encode(), FPR.v5.encode(), .double);
1554
1555 try testing.expectEqual(@as(u5, 4), inst.fp_data_processing.fp_1src.rd.toInt());
1556 try testing.expectEqual(@as(u5, 5), inst.fp_data_processing.fp_1src.rn.toInt());
1557 try testing.expectEqual(FP1SourceOpcode.fabs, inst.fp_data_processing.fp_1src.opcode);
1558 try testing.expectEqual(FP2Source.FPType.double, inst.fp_data_processing.fp_1src.ptype);
1559 }
1560
1561 test "FMOV register encoding" {
1562 const testing = std.testing;
1563
1564 const inst = FP1Source.fmov(FPR.v0.encode(), FPR.v1.encode(), .single);
1565
1566 try testing.expectEqual(@as(u5, 0), inst.fp_data_processing.fp_1src.rd.toInt());
1567 try testing.expectEqual(@as(u5, 1), inst.fp_data_processing.fp_1src.rn.toInt());
1568 try testing.expectEqual(FP1SourceOpcode.fmov, inst.fp_data_processing.fp_1src.opcode);
1569 try testing.expectEqual(FP2Source.FPType.single, inst.fp_data_processing.fp_1src.ptype);
1570
1571 try testing.expectEqual(@as(u32, 0x1E204020), inst.raw);
1572 }
1573
1574 test "FMOV immediate encoding" {
1575 const testing = std.testing;
1576
1577 const inst_s = FPImmediate.fmovImm(FPR.v0.encode(), 0x70, .single);
1578 try testing.expectEqual(@as(u32, 0x1E2E1000), inst_s.raw);
1579
1580 const inst_d = FPImmediate.fmovImm(FPR.v0.encode(), 0x70, .double);
1581 try testing.expectEqual(@as(u32, 0x1E6E1000), inst_d.raw);
1582 }
1583
1584 test "FP immediate imm8 encoding (f32/f64)" {
1585 const testing = std.testing;
1586
1587 try testing.expectEqual(@as(?u8, 0x70), FPImmediate.encodeF32(1.0));
1588 try testing.expectEqual(@as(?u8, 0x60), FPImmediate.encodeF32(0.5));
1589 try testing.expectEqual(@as(?u8, 0x00), FPImmediate.encodeF32(2.0));
1590 try testing.expectEqual(@as(?u8, 0x78), FPImmediate.encodeF32(1.5));
1591 try testing.expectEqual(@as(?u8, 0x74), FPImmediate.encodeF32(1.25));
1592 try testing.expectEqual(@as(?u8, 0xF0), FPImmediate.encodeF32(-1.0));
1593 try testing.expectEqual(@as(?u8, null), FPImmediate.encodeF32(0.0));
1594 try testing.expectEqual(@as(?u8, null), FPImmediate.encodeF32(3.14159));
1595
1596 try testing.expectEqual(@as(?u8, 0x70), FPImmediate.encodeF64(1.0));
1597 try testing.expectEqual(@as(?u8, 0x60), FPImmediate.encodeF64(0.5));
1598 try testing.expectEqual(@as(?u8, 0x00), FPImmediate.encodeF64(2.0));
1599 try testing.expectEqual(@as(?u8, 0x78), FPImmediate.encodeF64(1.5));
1600 try testing.expectEqual(@as(?u8, 0x74), FPImmediate.encodeF64(1.25));
1601 try testing.expectEqual(@as(?u8, 0xF0), FPImmediate.encodeF64(-1.0));
1602 try testing.expectEqual(@as(?u8, null), FPImmediate.encodeF64(0.0));
1603 try testing.expectEqual(@as(?u8, null), FPImmediate.encodeF64(3.14159));
1604 }
1605
1606 test "Condition invert" {
1607 const testing = std.testing;
1608
1609 try testing.expectEqual(Condition.ne, Condition.eq.invert());
1610 try testing.expectEqual(Condition.eq, Condition.ne.invert());
1611 try testing.expectEqual(Condition.ge, Condition.lt.invert());
1612 try testing.expectEqual(Condition.lt, Condition.ge.invert());
1613 }
1614
1615 test "Instruction toBytes/fromBytes uses little-endian" {
1616 const testing = std.testing;
1617
1618 const raw: u32 = 0x6d0127a8;
1619 const inst: Instruction = .{ .raw = raw };
1620 const bytes = inst.toBytes();
1621
1622 try testing.expectEqualSlices(u8, &[_]u8{ 0xa8, 0x27, 0x01, 0x6d }, &bytes);
1623
1624 const roundtrip = Instruction.fromBytes(bytes);
1625 try testing.expectEqual(raw, roundtrip.raw);
1626 }
1627
1628 test "disassemble: core instruction subset" {
1629 const testing = std.testing;
1630 var buf: [160]u8 = undefined;
1631
1632 try testing.expectEqualStrings(
1633 "add x0, x1, #0x2a",
1634 disassemble.formatInstructionRawWithDetail(AddSubImmediate.add(GPR.x0.encode(), GPR.x1.encode(), 42, .x).raw, &buf, true),
1635 );
1636
1637 try testing.expectEqualStrings(
1638 "fmov s0, #0x70",
1639 disassemble.formatInstructionRawWithDetail(FPImmediate.fmovImm(FPR.v0.encode(), 0x70, .single).raw, &buf, true),
1640 );
1641
1642 try testing.expectEqualStrings(
1643 "cmp x9, #0x32",
1644 disassemble.formatInstructionRawWithDetail(AddSubImmediate.cmpImm(GPR.x9.encode(), 50, .x).raw, &buf, true),
1645 );
1646
1647 try testing.expectEqualStrings(
1648 "movz x10, #0x1234, lsl #16",
1649 disassemble.formatInstructionRawWithDetail(MoveWide.movz(GPR.x10.encode(), 0x1234, 1, .x).raw, &buf, true),
1650 );
1651
1652 try testing.expectEqualStrings(
1653 "mov x14, x15",
1654 disassemble.formatInstructionRawWithDetail(LogicalShiftedRegister.movReg(GPR.x14.encode(), GPR.x15.encode(), .x).raw, &buf, true),
1655 );
1656
1657 try testing.expectEqualStrings(
1658 "ldr x0, [x1, #0x8]",
1659 disassemble.formatInstructionRawWithDetail(LoadStoreRegImm.ldr(GPR.x0.encode(), GPR.x1.encode(), 1, .x).raw, &buf, true),
1660 );
1661
1662 try testing.expectEqualStrings(
1663 "stp x0, x1, [sp, #-0x10]",
1664 disassemble.formatInstructionRawWithDetail(LoadStorePair.stpBytes(GPR.x0.encode(), GPR.x1.encode(), registers.SP.encoded, -16, .x).raw, &buf, true),
1665 );
1666
1667 try testing.expectEqualStrings(
1668 "b #0x4",
1669 disassemble.formatInstructionRawWithDetail(UnconditionalBranchImm.b(1).raw, &buf, true),
1670 );
1671
1672 try testing.expectEqualStrings(
1673 "b.eq #0xc",
1674 disassemble.formatInstructionRawWithDetail(ConditionalBranch.bCond(.eq, 3).raw, &buf, true),
1675 );
1676
1677 try testing.expectEqualStrings(
1678 "ret",
1679 disassemble.formatInstructionRawWithDetail(UnconditionalBranchReg.retLr().raw, &buf, true),
1680 );
1681
1682 try testing.expectEqualStrings(
1683 "fmov s0, w1",
1684 disassemble.formatInstructionRawWithDetail(FMovGeneral.fmovToFprSingle(FPR.v0.encode(), GPR.x1.encode()).raw, &buf, true),
1685 );
1686 }
1687
1688 test "golden: ADD/SUB immediate" {
1689 try expectEncoded(AddSubImmediate.add(GPR.x0.encode(), GPR.x1.encode(), 42, .x), 0x9100a820);
1690
1691 try expectEncoded(AddSubImmediate.adds(GPR.x3.encode(), GPR.x4.encode(), 100, .x), 0xb1019083);
1692
1693 try expectEncoded(AddSubImmediate.sub(GPR.x5.encode(), GPR.x6.encode(), 200, .x), 0xd10320c5);
1694
1695 try expectEncoded(AddSubImmediate.subs(GPR.x7.encode(), GPR.x8.encode(), 10, .x), 0xf1002907);
1696
1697 try expectEncoded(AddSubImmediate.add(GPR.x0.encode(), GPR.x1.encode(), 42, .w), 0x1100a820);
1698
1699 try expectEncoded(AddSubImmediate.cmpImm(GPR.x9.encode(), 50, .x), 0xf100c93f);
1700 }
1701
1702 test "golden: Move wide" {
1703 try expectEncoded(MoveWide.movz(GPR.x10.encode(), 0x1234, 0, .x), 0xd282468a);
1704
1705 try expectEncoded(MoveWide.movz(GPR.x10.encode(), 0x1234, 1, .x), 0xd2a2468a);
1706
1707 try expectEncoded(MoveWide.movk(GPR.x10.encode(), 0xABCD, 1, .x), 0xf2b579aa);
1708
1709 try expectEncoded(MoveWide.movn(GPR.x11.encode(), 0xFFFF, 0, .x), 0x929fffeb);
1710
1711 try expectEncoded(MoveWide.movz(GPR.x12.encode(), 0x5678, 0, .w), 0x528acf0c);
1712 }
1713
1714 test "golden: ADD/SUB shifted register" {
1715 try expectEncoded(AddSubShiftedRegister.addReg(GPR.x0.encode(), GPR.x1.encode(), GPR.x2.encode(), .x), 0x8b020020);
1716
1717 try expectEncoded(AddSubShiftedRegister.addsReg(GPR.x3.encode(), GPR.x4.encode(), GPR.x5.encode(), .x), 0xab050083);
1718
1719 try expectEncoded(AddSubShiftedRegister.subReg(GPR.x6.encode(), GPR.x7.encode(), GPR.x8.encode(), .x), 0xcb0800e6);
1720
1721 try expectEncoded(AddSubShiftedRegister.subsReg(GPR.x9.encode(), GPR.x10.encode(), GPR.x11.encode(), .x), 0xeb0b0149);
1722
1723 try expectEncoded(AddSubShiftedRegister.cmpReg(GPR.x12.encode(), GPR.x13.encode(), .x), 0xeb0d019f);
1724 }
1725
1726 test "golden: Logical shifted register" {
1727 try expectEncoded(LogicalShiftedRegister.andReg(GPR.x0.encode(), GPR.x1.encode(), GPR.x2.encode(), .x), 0x8a020020);
1728
1729 try expectEncoded(LogicalShiftedRegister.orrReg(GPR.x3.encode(), GPR.x4.encode(), GPR.x5.encode(), .x), 0xaa050083);
1730
1731 try expectEncoded(LogicalShiftedRegister.eorReg(GPR.x6.encode(), GPR.x7.encode(), GPR.x8.encode(), .x), 0xca0800e6);
1732
1733 try expectEncoded(LogicalShiftedRegister.movReg(GPR.x14.encode(), GPR.x15.encode(), .x), 0xaa0f03ee);
1734 }
1735
1736 test "golden: Data processing 2-source" {
1737 try expectEncoded(DataProcessing2Source.udiv(GPR.x0.encode(), GPR.x1.encode(), GPR.x2.encode(), .x), 0x9ac20820);
1738
1739 try expectEncoded(DataProcessing2Source.sdiv(GPR.x3.encode(), GPR.x4.encode(), GPR.x5.encode(), .x), 0x9ac50c83);
1740 }
1741
1742 test "golden: Data processing 3-source" {
1743 try expectEncoded(DataProcessing3Source.mul(GPR.x0.encode(), GPR.x1.encode(), GPR.x2.encode(), .x), 0x9b027c20);
1744
1745 try expectEncoded(DataProcessing3Source.madd(GPR.x3.encode(), GPR.x4.encode(), GPR.x5.encode(), GPR.x6.encode(), .x), 0x9b051883);
1746
1747 try expectEncoded(DataProcessing3Source.msub(GPR.x7.encode(), GPR.x8.encode(), GPR.x9.encode(), GPR.x10.encode(), .x), 0x9b09a907);
1748 }
1749
1750 test "golden: Conditional select" {
1751 try expectEncoded(ConditionalSelect.csel(GPR.x0.encode(), GPR.x1.encode(), GPR.x2.encode(), .eq, .x), 0x9a820020);
1752
1753 try expectEncoded(ConditionalSelect.csel(GPR.x3.encode(), GPR.x4.encode(), GPR.x5.encode(), .ne, .x), 0x9a851083);
1754
1755 try expectEncoded(ConditionalSelect.cset(GPR.x6.encode(), .eq, .x), 0x9a9f17e6);
1756 }
1757
1758 test "golden: Load/Store register immediate" {
1759 try expectEncoded(LoadStoreRegImm.ldr(GPR.x0.encode(), GPR.x1.encode(), 0, .x), 0xf9400020);
1760
1761 try expectEncoded(LoadStoreRegImm.ldr(GPR.x0.encode(), GPR.x1.encode(), 1, .x), 0xf9400420);
1762
1763 try expectEncoded(LoadStoreRegImm.str(GPR.x2.encode(), GPR.x3.encode(), 0, .x), 0xf9000062);
1764
1765 try expectEncoded(LoadStoreRegImm.str(GPR.x2.encode(), GPR.x3.encode(), 2, .x), 0xf9000862);
1766
1767 try expectEncoded(LoadStoreRegImm.ldr(GPR.x4.encode(), GPR.x5.encode(), 0, .w), 0xb94000a4);
1768
1769 try expectEncoded(LoadStoreRegImm.str(GPR.x6.encode(), GPR.x7.encode(), 1, .w), 0xb90004e6);
1770 }
1771
1772 test "golden: Load/Store pair" {
1773 try expectEncoded(LoadStorePair.stpBytes(GPR.x0.encode(), GPR.x1.encode(), registers.SP.encoded, -16, .x), 0xa93f07e0);
1774
1775 try expectEncoded(LoadStorePair.ldpBytes(GPR.x2.encode(), GPR.x3.encode(), registers.SP.encoded, 16, .x), 0xa9410fe2);
1776 }
1777
1778 test "golden: Unconditional branch immediate" {
1779 try expectEncoded(UnconditionalBranchImm.b(1), 0x14000001);
1780
1781 try expectEncoded(UnconditionalBranchImm.bl(2), 0x94000002);
1782 }
1783
1784 test "golden: Conditional branch" {
1785 try expectEncoded(ConditionalBranch.bCond(.eq, 3), 0x54000060);
1786 }
1787
1788 test "golden: Unconditional branch register" {
1789 try expectEncoded(UnconditionalBranchReg.retLr(), 0xd65f03c0);
1790
1791 try expectEncoded(UnconditionalBranchReg.br(GPR.x10.encode()), 0xd61f0140);
1792
1793 try expectEncoded(UnconditionalBranchReg.blr(GPR.x11.encode()), 0xd63f0160);
1794 }
1795
1796 test "golden: FP 1-source" {
1797 try expectEncoded(FP1Source.fmov(FPR.v0.encode(), FPR.v1.encode(), .single), 0x1e204020);
1798
1799 try expectEncoded(FP1Source.fmov(FPR.v2.encode(), FPR.v3.encode(), .double), 0x1e604062);
1800
1801 try expectEncoded(FP1Source.fabs(FPR.v4.encode(), FPR.v5.encode(), .single), 0x1e20c0a4);
1802
1803 try expectEncoded(FP1Source.fabs(FPR.v6.encode(), FPR.v7.encode(), .double), 0x1e60c0e6);
1804
1805 try expectEncoded(FP1Source.fneg(FPR.v8.encode(), FPR.v9.encode(), .single), 0x1e214128);
1806
1807 try expectEncoded(FP1Source.fneg(FPR.v10.encode(), FPR.v11.encode(), .double), 0x1e61416a);
1808
1809 try expectEncoded(FP1Source.fsqrt(FPR.v12.encode(), FPR.v13.encode(), .single), 0x1e21c1ac);
1810
1811 try expectEncoded(FP1Source.fsqrt(FPR.v14.encode(), FPR.v15.encode(), .double), 0x1e61c1ee);
1812 }
1813
1814 test "golden: FP 2-source" {
1815 try expectEncoded(FP2Source.fadd(FPR.v0.encode(), FPR.v1.encode(), FPR.v2.encode(), .single), 0x1e222820);
1816
1817 try expectEncoded(FP2Source.fadd(FPR.v3.encode(), FPR.v4.encode(), FPR.v5.encode(), .double), 0x1e652883);
1818
1819 try expectEncoded(FP2Source.fsub(FPR.v6.encode(), FPR.v7.encode(), FPR.v8.encode(), .single), 0x1e2838e6);
1820
1821 try expectEncoded(FP2Source.fsub(FPR.v9.encode(), FPR.v10.encode(), FPR.v11.encode(), .double), 0x1e6b3949);
1822
1823 try expectEncoded(FP2Source.fmul(FPR.v12.encode(), FPR.v13.encode(), FPR.v14.encode(), .single), 0x1e2e09ac);
1824
1825 try expectEncoded(FP2Source.fmul(FPR.v15.encode(), FPR.v16.encode(), FPR.v17.encode(), .double), 0x1e710a0f);
1826
1827 try expectEncoded(FP2Source.fdiv(FPR.v18.encode(), FPR.v19.encode(), FPR.v20.encode(), .single), 0x1e341a72);
1828
1829 try expectEncoded(FP2Source.fdiv(FPR.v21.encode(), FPR.v22.encode(), FPR.v23.encode(), .double), 0x1e771ad5);
1830 }
1831
1832 test "golden: FP compare" {
1833 try expectEncoded(FPCompare.fcmp(FPR.v0.encode(), FPR.v1.encode(), .single), 0x1e212000);
1834
1835 try expectEncoded(FPCompare.fcmp(FPR.v2.encode(), FPR.v3.encode(), .double), 0x1e632040);
1836
1837 try expectEncoded(FPCompare.fcmpZero(FPR.v4.encode(), .single), 0x1e202088);
1838
1839 try expectEncoded(FPCompare.fcmpZero(FPR.v5.encode(), .double), 0x1e6020a8);
1840 }
1841
1842 test "golden: FMOV general (GPR <-> FPR)" {
1843 try expectEncoded(FMovGeneral.fmovToFprSingle(FPR.v0.encode(), GPR.x1.encode()), 0x1e270020);
1844
1845 try expectEncoded(FMovGeneral.fmovToFprDouble(FPR.v2.encode(), GPR.x3.encode()), 0x9e670062);
1846
1847 try expectEncoded(FMovGeneral.fmovFromFprSingle(GPR.x4.encode(), FPR.v5.encode()), 0x1e2600a4);
1848
1849 try expectEncoded(FMovGeneral.fmovFromFprDouble(GPR.x6.encode(), FPR.v7.encode()), 0x9e6600e6);
1850 }
1851
1852 test "golden: FP Load/Store pair" {
1853 try expectEncoded(LoadStorePairFP.stpBytes(FPR.v8.encode(), FPR.v9.encode(), GPR.x29.encode(), 16, .double), 0x6d0127a8);
1854
1855 try expectEncoded(LoadStorePairFP.stpBytes(FPR.v10.encode(), FPR.v11.encode(), GPR.x29.encode(), 32, .double), 0x6d022faa);
1856
1857 try expectEncoded(LoadStorePairFP.ldpBytes(FPR.v8.encode(), FPR.v9.encode(), GPR.x29.encode(), 16, .double), 0x6d4127a8);
1858
1859 try expectEncoded(LoadStorePairFP.ldpBytes(FPR.v10.encode(), FPR.v11.encode(), GPR.x29.encode(), 32, .double), 0x6d422faa);
1860
1861 try expectEncoded(LoadStorePairFP.stpBytes(FPR.v0.encode(), FPR.v1.encode(), registers.SP.encoded, 8, .single), 0x2d0107e0);
1862
1863 try expectEncoded(LoadStorePairFP.ldpBytes(FPR.v2.encode(), FPR.v3.encode(), registers.SP.encoded, 8, .single), 0x2d410fe2);
1864 }