| ... | ... | @@ -19,7 +19,7 @@ pub const Register = enum(u6) { |
| 19 | 19 | w16, w17, w18, w19, w20, w21, w22, w23, |
| 20 | 20 | w24, w25, w26, w27, w28, w29, w30, wzr, |
| 21 | 21 | |
| 22 | | pub const sp = .xzr; |
| 22 | pub const sp = Register.xzr; |
| 23 | 23 | |
| 24 | 24 | pub fn id(self: Register) u5 { |
| 25 | 25 | return @truncate(u5, @enumToInt(self)); |
| ... | ... | @@ -72,6 +72,9 @@ test "Register.id" { |
| 72 | 72 | |
| 73 | 73 | testing.expectEqual(@as(u5, 31), Register.xzr.id()); |
| 74 | 74 | testing.expectEqual(@as(u5, 31), Register.wzr.id()); |
| 75 | |
| 76 | testing.expectEqual(@as(u5, 31), Register.sp.id()); |
| 77 | testing.expectEqual(@as(u5, 31), Register.sp.id()); |
| 75 | 78 | } |
| 76 | 79 | |
| 77 | 80 | test "Register.size" { |
| ... | ... | @@ -232,6 +235,16 @@ pub const Instruction = union(enum) { |
| 232 | 235 | fixed: u4 = 0b111_0, |
| 233 | 236 | size: u2, |
| 234 | 237 | }, |
| 238 | LoadStorePairOfRegisters: packed struct { |
| 239 | rt1: u5, |
| 240 | rn: u5, |
| 241 | rt2: u5, |
| 242 | imm7: u7, |
| 243 | load: u1, |
| 244 | encoding: u2, |
| 245 | fixed: u5 = 0b101_0_0, |
| 246 | opc: u2, |
| 247 | }, |
| 235 | 248 | LoadLiteral: packed struct { |
| 236 | 249 | rt: u5, |
| 237 | 250 | imm19: u19, |
| ... | ... | @@ -268,6 +281,7 @@ pub const Instruction = union(enum) { |
| 268 | 281 | .MoveWideImmediate => |v| @bitCast(u32, v), |
| 269 | 282 | .PCRelativeAddress => |v| @bitCast(u32, v), |
| 270 | 283 | .LoadStoreRegister => |v| @bitCast(u32, v), |
| 284 | .LoadStorePairOfRegisters => |v| @bitCast(u32, v), |
| 271 | 285 | .LoadLiteral => |v| @bitCast(u32, v), |
| 272 | 286 | .ExceptionGeneration => |v| @bitCast(u32, v), |
| 273 | 287 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), |
| ... | ... | @@ -276,123 +290,6 @@ pub const Instruction = union(enum) { |
| 276 | 290 | }; |
| 277 | 291 | } |
| 278 | 292 | |
| 279 | | /// Represents the offset operand of a load or store instruction. |
| 280 | | /// Data can be loaded from memory with either an immediate offset |
| 281 | | /// or an offset that is stored in some register. |
| 282 | | pub const Offset = union(enum) { |
| 283 | | Immediate: union(enum) { |
| 284 | | PostIndex: i9, |
| 285 | | PreIndex: i9, |
| 286 | | Unsigned: u12, |
| 287 | | }, |
| 288 | | Register: struct { |
| 289 | | rm: u5, |
| 290 | | shift: union(enum) { |
| 291 | | Uxtw: u2, |
| 292 | | Lsl: u2, |
| 293 | | Sxtw: u2, |
| 294 | | Sxtx: u2, |
| 295 | | }, |
| 296 | | }, |
| 297 | | |
| 298 | | pub const none = Offset{ |
| 299 | | .Immediate = .{ .Unsigned = 0 }, |
| 300 | | }; |
| 301 | | |
| 302 | | pub fn toU12(self: Offset) u12 { |
| 303 | | return switch (self) { |
| 304 | | .Immediate => |imm_type| switch (imm_type) { |
| 305 | | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, |
| 306 | | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, |
| 307 | | .Unsigned => |v| v, |
| 308 | | }, |
| 309 | | .Register => |r| switch (r.shift) { |
| 310 | | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, |
| 311 | | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, |
| 312 | | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, |
| 313 | | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, |
| 314 | | }, |
| 315 | | }; |
| 316 | | } |
| 317 | | |
| 318 | | pub fn imm(offset: u12) Offset { |
| 319 | | return Offset{ |
| 320 | | .Immediate = .{ .Unsigned = offset }, |
| 321 | | }; |
| 322 | | } |
| 323 | | |
| 324 | | pub fn imm_post_index(offset: i9) Offset { |
| 325 | | return Offset{ |
| 326 | | .Immediate = .{ .PostIndex = offset }, |
| 327 | | }; |
| 328 | | } |
| 329 | | |
| 330 | | pub fn imm_pre_index(offset: i9) Offset { |
| 331 | | return Offset{ |
| 332 | | .Immediate = .{ .PreIndex = offset }, |
| 333 | | }; |
| 334 | | } |
| 335 | | |
| 336 | | pub fn reg(rm: Register) Offset { |
| 337 | | return Offset{ |
| 338 | | .Register = .{ |
| 339 | | .rm = rm.id(), |
| 340 | | .shift = .{ |
| 341 | | .Lsl = 0, |
| 342 | | }, |
| 343 | | }, |
| 344 | | }; |
| 345 | | } |
| 346 | | |
| 347 | | pub fn reg_uxtw(rm: Register, shift: u2) Offset { |
| 348 | | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| 349 | | return Offset{ |
| 350 | | .Register = .{ |
| 351 | | .rm = rm.id(), |
| 352 | | .shift = .{ |
| 353 | | .Uxtw = shift, |
| 354 | | }, |
| 355 | | }, |
| 356 | | }; |
| 357 | | } |
| 358 | | |
| 359 | | pub fn reg_lsl(rm: Register, shift: u2) Offset { |
| 360 | | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| 361 | | return Offset{ |
| 362 | | .Register = .{ |
| 363 | | .rm = rm.id(), |
| 364 | | .shift = .{ |
| 365 | | .Lsl = shift, |
| 366 | | }, |
| 367 | | }, |
| 368 | | }; |
| 369 | | } |
| 370 | | |
| 371 | | pub fn reg_sxtw(rm: Register, shift: u2) Offset { |
| 372 | | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| 373 | | return Offset{ |
| 374 | | .Register = .{ |
| 375 | | .rm = rm.id(), |
| 376 | | .shift = .{ |
| 377 | | .Sxtw = shift, |
| 378 | | }, |
| 379 | | }, |
| 380 | | }; |
| 381 | | } |
| 382 | | |
| 383 | | pub fn reg_sxtx(rm: Register, shift: u2) Offset { |
| 384 | | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| 385 | | return Offset{ |
| 386 | | .Register = .{ |
| 387 | | .rm = rm.id(), |
| 388 | | .shift = .{ |
| 389 | | .Sxtx = shift, |
| 390 | | }, |
| 391 | | }, |
| 392 | | }; |
| 393 | | } |
| 394 | | }; |
| 395 | | |
| 396 | 293 | pub const RegisterShift = struct { |
| 397 | 294 | rn: u5, |
| 398 | 295 | imm6: u6, |
| ... | ... | @@ -500,7 +397,124 @@ pub const Instruction = union(enum) { |
| 500 | 397 | }; |
| 501 | 398 | } |
| 502 | 399 | |
| 503 | | fn loadStoreRegister(rt: Register, rn: Register, offset: Offset, load: bool) Instruction { |
| 400 | /// Represents the offset operand of a load or store instruction. |
| 401 | /// Data can be loaded from memory with either an immediate offset |
| 402 | /// or an offset that is stored in some register. |
| 403 | pub const LoadStoreOffset = union(enum) { |
| 404 | Immediate: union(enum) { |
| 405 | PostIndex: i9, |
| 406 | PreIndex: i9, |
| 407 | Unsigned: u12, |
| 408 | }, |
| 409 | Register: struct { |
| 410 | rm: u5, |
| 411 | shift: union(enum) { |
| 412 | Uxtw: u2, |
| 413 | Lsl: u2, |
| 414 | Sxtw: u2, |
| 415 | Sxtx: u2, |
| 416 | }, |
| 417 | }, |
| 418 | |
| 419 | pub const none = LoadStoreOffset{ |
| 420 | .Immediate = .{ .Unsigned = 0 }, |
| 421 | }; |
| 422 | |
| 423 | pub fn toU12(self: LoadStoreOffset) u12 { |
| 424 | return switch (self) { |
| 425 | .Immediate => |imm_type| switch (imm_type) { |
| 426 | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, |
| 427 | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, |
| 428 | .Unsigned => |v| v, |
| 429 | }, |
| 430 | .Register => |r| switch (r.shift) { |
| 431 | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, |
| 432 | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, |
| 433 | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, |
| 434 | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, |
| 435 | }, |
| 436 | }; |
| 437 | } |
| 438 | |
| 439 | pub fn imm(offset: u12) LoadStoreOffset { |
| 440 | return .{ |
| 441 | .Immediate = .{ .Unsigned = offset }, |
| 442 | }; |
| 443 | } |
| 444 | |
| 445 | pub fn imm_post_index(offset: i9) LoadStoreOffset { |
| 446 | return .{ |
| 447 | .Immediate = .{ .PostIndex = offset }, |
| 448 | }; |
| 449 | } |
| 450 | |
| 451 | pub fn imm_pre_index(offset: i9) LoadStoreOffset { |
| 452 | return .{ |
| 453 | .Immediate = .{ .PreIndex = offset }, |
| 454 | }; |
| 455 | } |
| 456 | |
| 457 | pub fn reg(rm: Register) LoadStoreOffset { |
| 458 | return .{ |
| 459 | .Register = .{ |
| 460 | .rm = rm.id(), |
| 461 | .shift = .{ |
| 462 | .Lsl = 0, |
| 463 | }, |
| 464 | }, |
| 465 | }; |
| 466 | } |
| 467 | |
| 468 | pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset { |
| 469 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| 470 | return .{ |
| 471 | .Register = .{ |
| 472 | .rm = rm.id(), |
| 473 | .shift = .{ |
| 474 | .Uxtw = shift, |
| 475 | }, |
| 476 | }, |
| 477 | }; |
| 478 | } |
| 479 | |
| 480 | pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset { |
| 481 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| 482 | return .{ |
| 483 | .Register = .{ |
| 484 | .rm = rm.id(), |
| 485 | .shift = .{ |
| 486 | .Lsl = shift, |
| 487 | }, |
| 488 | }, |
| 489 | }; |
| 490 | } |
| 491 | |
| 492 | pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset { |
| 493 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| 494 | return .{ |
| 495 | .Register = .{ |
| 496 | .rm = rm.id(), |
| 497 | .shift = .{ |
| 498 | .Sxtw = shift, |
| 499 | }, |
| 500 | }, |
| 501 | }; |
| 502 | } |
| 503 | |
| 504 | pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset { |
| 505 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| 506 | return .{ |
| 507 | .Register = .{ |
| 508 | .rm = rm.id(), |
| 509 | .shift = .{ |
| 510 | .Sxtx = shift, |
| 511 | }, |
| 512 | }, |
| 513 | }; |
| 514 | } |
| 515 | }; |
| 516 | |
| 517 | fn loadStoreRegister(rt: Register, rn: Register, offset: LoadStoreOffset, load: bool) Instruction { |
| 504 | 518 | const off = offset.toU12(); |
| 505 | 519 | const op1: u2 = blk: { |
| 506 | 520 | switch (offset) { |
| ... | ... | @@ -542,6 +556,49 @@ pub const Instruction = union(enum) { |
| 542 | 556 | } |
| 543 | 557 | } |
| 544 | 558 | |
| 559 | fn loadStorePairOfRegisters( |
| 560 | rt1: Register, |
| 561 | rt2: Register, |
| 562 | rn: Register, |
| 563 | offset: i9, |
| 564 | encoding: u2, |
| 565 | load: bool, |
| 566 | ) Instruction { |
| 567 | switch (rt1.size()) { |
| 568 | 32 => { |
| 569 | assert(-256 <= offset and offset <= 252); |
| 570 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); |
| 571 | return Instruction{ |
| 572 | .LoadStorePairOfRegisters = .{ |
| 573 | .rt1 = rt1.id(), |
| 574 | .rn = rn.id(), |
| 575 | .rt2 = rt2.id(), |
| 576 | .imm7 = imm7, |
| 577 | .load = @boolToInt(load), |
| 578 | .encoding = encoding, |
| 579 | .opc = 0b00, |
| 580 | }, |
| 581 | }; |
| 582 | }, |
| 583 | 64 => { |
| 584 | assert(-512 <= offset and offset <= 504); |
| 585 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); |
| 586 | return Instruction{ |
| 587 | .LoadStorePairOfRegisters = .{ |
| 588 | .rt1 = rt1.id(), |
| 589 | .rn = rn.id(), |
| 590 | .rt2 = rt2.id(), |
| 591 | .imm7 = imm7, |
| 592 | .load = @boolToInt(load), |
| 593 | .encoding = encoding, |
| 594 | .opc = 0b10, |
| 595 | }, |
| 596 | }; |
| 597 | }, |
| 598 | else => unreachable, // unexpected register size |
| 599 | } |
| 600 | } |
| 601 | |
| 545 | 602 | fn loadLiteral(rt: Register, imm19: u19) Instruction { |
| 546 | 603 | switch (rt.size()) { |
| 547 | 604 | 32 => { |
| ... | ... | @@ -652,9 +709,10 @@ pub const Instruction = union(enum) { |
| 652 | 709 | |
| 653 | 710 | pub const LdrArgs = struct { |
| 654 | 711 | rn: ?Register = null, |
| 655 | | offset: Offset = Offset.none, |
| 712 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 656 | 713 | literal: ?u19 = null, |
| 657 | 714 | }; |
| 715 | |
| 658 | 716 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { |
| 659 | 717 | if (args.rn) |rn| { |
| 660 | 718 | return loadStoreRegister(rt, rn, args.offset, true); |
| ... | ... | @@ -664,12 +722,56 @@ pub const Instruction = union(enum) { |
| 664 | 722 | } |
| 665 | 723 | |
| 666 | 724 | pub const StrArgs = struct { |
| 667 | | offset: Offset = Offset.none, |
| 725 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 668 | 726 | }; |
| 727 | |
| 669 | 728 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { |
| 670 | 729 | return loadStoreRegister(rt, rn, args.offset, false); |
| 671 | 730 | } |
| 672 | 731 | |
| 732 | // Load or store pair of registers |
| 733 | |
| 734 | pub const LoadStorePairOffset = struct { |
| 735 | encoding: enum(u2) { |
| 736 | PostIndex = 0b01, |
| 737 | Signed = 0b10, |
| 738 | PreIndex = 0b11, |
| 739 | }, |
| 740 | offset: i9, |
| 741 | |
| 742 | pub fn none() LoadStorePairOffset { |
| 743 | return .{ .encoding = .Signed, .offset = 0 }; |
| 744 | } |
| 745 | |
| 746 | pub fn post_index(imm: i9) LoadStorePairOffset { |
| 747 | return .{ .encoding = .PostIndex, .offset = imm }; |
| 748 | } |
| 749 | |
| 750 | pub fn pre_index(imm: i9) LoadStorePairOffset { |
| 751 | return .{ .encoding = .PreIndex, .offset = imm }; |
| 752 | } |
| 753 | |
| 754 | pub fn signed(imm: i9) LoadStorePairOffset { |
| 755 | return .{ .encoding = .Signed, .offset = imm }; |
| 756 | } |
| 757 | }; |
| 758 | |
| 759 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 760 | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); |
| 761 | } |
| 762 | |
| 763 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| 764 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true); |
| 765 | } |
| 766 | |
| 767 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 768 | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); |
| 769 | } |
| 770 | |
| 771 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| 772 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, false); |
| 773 | } |
| 774 | |
| 673 | 775 | // Exception generation |
| 674 | 776 | |
| 675 | 777 | pub fn svc(imm16: u16) Instruction { |
| ... | ... | @@ -787,15 +889,15 @@ test "serialize instructions" { |
| 787 | 889 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, |
| 788 | 890 | }, |
| 789 | 891 | .{ // ldr x2, [x1, #1]! |
| 790 | | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.imm_pre_index(1) }), |
| 892 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) }), |
| 791 | 893 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, |
| 792 | 894 | }, |
| 793 | 895 | .{ // ldr x2, [x1], #-1 |
| 794 | | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.imm_post_index(-1) }), |
| 896 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) }), |
| 795 | 897 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, |
| 796 | 898 | }, |
| 797 | 899 | .{ // ldr x2, [x1], (x3) |
| 798 | | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.reg(.x3) }), |
| 900 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) }), |
| 799 | 901 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, |
| 800 | 902 | }, |
| 801 | 903 | .{ // ldr x2, label |
| ... | ... | @@ -807,7 +909,7 @@ test "serialize instructions" { |
| 807 | 909 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, |
| 808 | 910 | }, |
| 809 | 911 | .{ // str x2, [x1], (x3) |
| 810 | | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.Offset.reg(.x3) }), |
| 912 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), |
| 811 | 913 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, |
| 812 | 914 | }, |
| 813 | 915 | .{ // adr x2, #0x8 |
| ... | ... | @@ -826,6 +928,22 @@ test "serialize instructions" { |
| 826 | 928 | .inst = Instruction.adrp(.x2, -0x8), |
| 827 | 929 | .expected = 0b1_00_10000_1111111111111111110_00010, |
| 828 | 930 | }, |
| 931 | .{ // stp x1, x2, [sp, #8] |
| 932 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), |
| 933 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, |
| 934 | }, |
| 935 | .{ // ldp x1, x2, [sp, #8] |
| 936 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), |
| 937 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, |
| 938 | }, |
| 939 | .{ // stp x1, x2, [sp, #-16]! |
| 940 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)), |
| 941 | .expected = 0b10_101_0_011_0_1111110_00010_11111_00001, |
| 942 | }, |
| 943 | .{ // ldp x1, x2, [sp], #16 |
| 944 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), |
| 945 | .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, |
| 946 | }, |
| 829 | 947 | }; |
| 830 | 948 | |
| 831 | 949 | for (testcases) |case| { |