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 }