| ... | ... | @@ -200,7 +200,7 @@ test "FloatingPointRegister.toX" { |
| 200 | 200 | |
| 201 | 201 | /// Represents an instruction in the AArch64 instruction set |
| 202 | 202 | pub const Instruction = union(enum) { |
| 203 | | MoveWideImmediate: packed struct { |
| 203 | move_wide_immediate: packed struct { |
| 204 | 204 | rd: u5, |
| 205 | 205 | imm16: u16, |
| 206 | 206 | hw: u2, |
| ... | ... | @@ -208,14 +208,14 @@ pub const Instruction = union(enum) { |
| 208 | 208 | opc: u2, |
| 209 | 209 | sf: u1, |
| 210 | 210 | }, |
| 211 | | PCRelativeAddress: packed struct { |
| 211 | pc_relative_address: packed struct { |
| 212 | 212 | rd: u5, |
| 213 | 213 | immhi: u19, |
| 214 | 214 | fixed: u5 = 0b10000, |
| 215 | 215 | immlo: u2, |
| 216 | 216 | op: u1, |
| 217 | 217 | }, |
| 218 | | LoadStoreRegister: packed struct { |
| 218 | load_store_register: packed struct { |
| 219 | 219 | rt: u5, |
| 220 | 220 | rn: u5, |
| 221 | 221 | offset: u12, |
| ... | ... | @@ -225,7 +225,7 @@ pub const Instruction = union(enum) { |
| 225 | 225 | fixed: u3 = 0b111, |
| 226 | 226 | size: u2, |
| 227 | 227 | }, |
| 228 | | LoadStorePairOfRegisters: packed struct { |
| 228 | load_store_register_pair: packed struct { |
| 229 | 229 | rt1: u5, |
| 230 | 230 | rn: u5, |
| 231 | 231 | rt2: u5, |
| ... | ... | @@ -235,20 +235,20 @@ pub const Instruction = union(enum) { |
| 235 | 235 | fixed: u5 = 0b101_0_0, |
| 236 | 236 | opc: u2, |
| 237 | 237 | }, |
| 238 | | LoadLiteral: packed struct { |
| 238 | load_literal: packed struct { |
| 239 | 239 | rt: u5, |
| 240 | 240 | imm19: u19, |
| 241 | 241 | fixed: u6 = 0b011_0_00, |
| 242 | 242 | opc: u2, |
| 243 | 243 | }, |
| 244 | | ExceptionGeneration: packed struct { |
| 244 | exception_generation: packed struct { |
| 245 | 245 | ll: u2, |
| 246 | 246 | op2: u3, |
| 247 | 247 | imm16: u16, |
| 248 | 248 | opc: u3, |
| 249 | 249 | fixed: u8 = 0b1101_0100, |
| 250 | 250 | }, |
| 251 | | UnconditionalBranchRegister: packed struct { |
| 251 | unconditional_branch_register: packed struct { |
| 252 | 252 | op4: u5, |
| 253 | 253 | rn: u5, |
| 254 | 254 | op3: u6, |
| ... | ... | @@ -256,15 +256,15 @@ pub const Instruction = union(enum) { |
| 256 | 256 | opc: u4, |
| 257 | 257 | fixed: u7 = 0b1101_011, |
| 258 | 258 | }, |
| 259 | | UnconditionalBranchImmediate: packed struct { |
| 259 | unconditional_branch_immediate: packed struct { |
| 260 | 260 | imm26: u26, |
| 261 | 261 | fixed: u5 = 0b00101, |
| 262 | 262 | op: u1, |
| 263 | 263 | }, |
| 264 | | NoOperation: packed struct { |
| 264 | no_operation: packed struct { |
| 265 | 265 | fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, |
| 266 | 266 | }, |
| 267 | | LogicalShiftedRegister: packed struct { |
| 267 | logical_shifted_register: packed struct { |
| 268 | 268 | rd: u5, |
| 269 | 269 | rn: u5, |
| 270 | 270 | imm6: u6, |
| ... | ... | @@ -275,7 +275,7 @@ pub const Instruction = union(enum) { |
| 275 | 275 | opc: u2, |
| 276 | 276 | sf: u1, |
| 277 | 277 | }, |
| 278 | | AddSubtractImmediate: packed struct { |
| 278 | add_subtract_immediate: packed struct { |
| 279 | 279 | rd: u5, |
| 280 | 280 | rn: u5, |
| 281 | 281 | imm12: u12, |
| ... | ... | @@ -285,6 +285,20 @@ pub const Instruction = union(enum) { |
| 285 | 285 | op: u1, |
| 286 | 286 | sf: u1, |
| 287 | 287 | }, |
| 288 | conditional_branch: struct { |
| 289 | cond: u4, |
| 290 | o0: u1, |
| 291 | imm19: u19, |
| 292 | o1: u1, |
| 293 | fixed: u7 = 0b0101010, |
| 294 | }, |
| 295 | compare_and_branch: struct { |
| 296 | rt: u5, |
| 297 | imm19: u19, |
| 298 | op: u1, |
| 299 | fixed: u6 = 0b011010, |
| 300 | sf: u1, |
| 301 | }, |
| 288 | 302 | |
| 289 | 303 | pub const Shift = struct { |
| 290 | 304 | shift: Type = .lsl, |
| ... | ... | @@ -303,19 +317,73 @@ pub const Instruction = union(enum) { |
| 303 | 317 | }; |
| 304 | 318 | }; |
| 305 | 319 | |
| 320 | pub const Condition = enum(u4) { |
| 321 | /// Integer: Equal |
| 322 | /// Floating point: Equal |
| 323 | eq, |
| 324 | /// Integer: Not equal |
| 325 | /// Floating point: Not equal or unordered |
| 326 | ne, |
| 327 | /// Integer: Carry set |
| 328 | /// Floating point: Greater than, equal, or unordered |
| 329 | cs, |
| 330 | /// Integer: Carry clear |
| 331 | /// Floating point: Less than |
| 332 | cc, |
| 333 | /// Integer: Minus, negative |
| 334 | /// Floating point: Less than |
| 335 | mi, |
| 336 | /// Integer: Plus, positive or zero |
| 337 | /// Floating point: Greater than, equal, or unordered |
| 338 | pl, |
| 339 | /// Integer: Overflow |
| 340 | /// Floating point: Unordered |
| 341 | vs, |
| 342 | /// Integer: No overflow |
| 343 | /// Floating point: Ordered |
| 344 | vc, |
| 345 | /// Integer: Unsigned higher |
| 346 | /// Floating point: Greater than, or unordered |
| 347 | hi, |
| 348 | /// Integer: Unsigned lower or same |
| 349 | /// Floating point: Less than or equal |
| 350 | ls, |
| 351 | /// Integer: Signed greater than or equal |
| 352 | /// Floating point: Greater than or equal |
| 353 | ge, |
| 354 | /// Integer: Signed less than |
| 355 | /// Floating point: Less than, or unordered |
| 356 | lt, |
| 357 | /// Integer: Signed greater than |
| 358 | /// Floating point: Greater than |
| 359 | gt, |
| 360 | /// Integer: Signed less than or equal |
| 361 | /// Floating point: Less than, equal, or unordered |
| 362 | le, |
| 363 | /// Integer: Always |
| 364 | /// Floating point: Always |
| 365 | al, |
| 366 | /// Integer: Always |
| 367 | /// Floating point: Always |
| 368 | nv, |
| 369 | }; |
| 370 | |
| 306 | 371 | pub fn toU32(self: Instruction) u32 { |
| 307 | 372 | return switch (self) { |
| 308 | | .MoveWideImmediate => |v| @bitCast(u32, v), |
| 309 | | .PCRelativeAddress => |v| @bitCast(u32, v), |
| 310 | | .LoadStoreRegister => |v| @bitCast(u32, v), |
| 311 | | .LoadStorePairOfRegisters => |v| @bitCast(u32, v), |
| 312 | | .LoadLiteral => |v| @bitCast(u32, v), |
| 313 | | .ExceptionGeneration => |v| @bitCast(u32, v), |
| 314 | | .UnconditionalBranchRegister => |v| @bitCast(u32, v), |
| 315 | | .UnconditionalBranchImmediate => |v| @bitCast(u32, v), |
| 316 | | .NoOperation => |v| @bitCast(u32, v), |
| 317 | | .LogicalShiftedRegister => |v| @bitCast(u32, v), |
| 318 | | .AddSubtractImmediate => |v| @bitCast(u32, v), |
| 373 | .move_wide_immediate => |v| @bitCast(u32, v), |
| 374 | .pc_relative_address => |v| @bitCast(u32, v), |
| 375 | .load_store_register => |v| @bitCast(u32, v), |
| 376 | .load_store_register_pair => |v| @bitCast(u32, v), |
| 377 | .load_literal => |v| @bitCast(u32, v), |
| 378 | .exception_generation => |v| @bitCast(u32, v), |
| 379 | .unconditional_branch_register => |v| @bitCast(u32, v), |
| 380 | .unconditional_branch_immediate => |v| @bitCast(u32, v), |
| 381 | .no_operation => |v| @bitCast(u32, v), |
| 382 | .logical_shifted_register => |v| @bitCast(u32, v), |
| 383 | .add_subtract_immediate => |v| @bitCast(u32, v), |
| 384 | // TODO once packed structs work, this can be refactored |
| 385 | .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), |
| 386 | .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), |
| 319 | 387 | }; |
| 320 | 388 | } |
| 321 | 389 | |
| ... | ... | @@ -329,7 +397,7 @@ pub const Instruction = union(enum) { |
| 329 | 397 | 32 => { |
| 330 | 398 | assert(shift % 16 == 0 and shift <= 16); |
| 331 | 399 | return Instruction{ |
| 332 | | .MoveWideImmediate = .{ |
| 400 | .move_wide_immediate = .{ |
| 333 | 401 | .rd = rd.id(), |
| 334 | 402 | .imm16 = imm16, |
| 335 | 403 | .hw = @intCast(u2, shift / 16), |
| ... | ... | @@ -341,7 +409,7 @@ pub const Instruction = union(enum) { |
| 341 | 409 | 64 => { |
| 342 | 410 | assert(shift % 16 == 0 and shift <= 48); |
| 343 | 411 | return Instruction{ |
| 344 | | .MoveWideImmediate = .{ |
| 412 | .move_wide_immediate = .{ |
| 345 | 413 | .rd = rd.id(), |
| 346 | 414 | .imm16 = imm16, |
| 347 | 415 | .hw = @intCast(u2, shift / 16), |
| ... | ... | @@ -358,7 +426,7 @@ pub const Instruction = union(enum) { |
| 358 | 426 | assert(rd.size() == 64); |
| 359 | 427 | const imm21_u = @bitCast(u21, imm21); |
| 360 | 428 | return Instruction{ |
| 361 | | .PCRelativeAddress = .{ |
| 429 | .pc_relative_address = .{ |
| 362 | 430 | .rd = rd.id(), |
| 363 | 431 | .immlo = @truncate(u2, imm21_u), |
| 364 | 432 | .immhi = @truncate(u19, imm21_u >> 2), |
| ... | ... | @@ -522,7 +590,7 @@ pub const Instruction = union(enum) { |
| 522 | 590 | .str, .strh, .strb => 0b00, |
| 523 | 591 | }; |
| 524 | 592 | return Instruction{ |
| 525 | | .LoadStoreRegister = .{ |
| 593 | .load_store_register = .{ |
| 526 | 594 | .rt = rt.id(), |
| 527 | 595 | .rn = rn.id(), |
| 528 | 596 | .offset = off, |
| ... | ... | @@ -544,7 +612,7 @@ pub const Instruction = union(enum) { |
| 544 | 612 | }; |
| 545 | 613 | } |
| 546 | 614 | |
| 547 | | fn loadStorePairOfRegisters( |
| 615 | fn loadStoreRegisterPair( |
| 548 | 616 | rt1: Register, |
| 549 | 617 | rt2: Register, |
| 550 | 618 | rn: Register, |
| ... | ... | @@ -557,7 +625,7 @@ pub const Instruction = union(enum) { |
| 557 | 625 | assert(-256 <= offset and offset <= 252); |
| 558 | 626 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); |
| 559 | 627 | return Instruction{ |
| 560 | | .LoadStorePairOfRegisters = .{ |
| 628 | .load_store_register_pair = .{ |
| 561 | 629 | .rt1 = rt1.id(), |
| 562 | 630 | .rn = rn.id(), |
| 563 | 631 | .rt2 = rt2.id(), |
| ... | ... | @@ -572,7 +640,7 @@ pub const Instruction = union(enum) { |
| 572 | 640 | assert(-512 <= offset and offset <= 504); |
| 573 | 641 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); |
| 574 | 642 | return Instruction{ |
| 575 | | .LoadStorePairOfRegisters = .{ |
| 643 | .load_store_register_pair = .{ |
| 576 | 644 | .rt1 = rt1.id(), |
| 577 | 645 | .rn = rn.id(), |
| 578 | 646 | .rt2 = rt2.id(), |
| ... | ... | @@ -591,7 +659,7 @@ pub const Instruction = union(enum) { |
| 591 | 659 | switch (rt.size()) { |
| 592 | 660 | 32 => { |
| 593 | 661 | return Instruction{ |
| 594 | | .LoadLiteral = .{ |
| 662 | .load_literal = .{ |
| 595 | 663 | .rt = rt.id(), |
| 596 | 664 | .imm19 = imm19, |
| 597 | 665 | .opc = 0b00, |
| ... | ... | @@ -600,7 +668,7 @@ pub const Instruction = union(enum) { |
| 600 | 668 | }, |
| 601 | 669 | 64 => { |
| 602 | 670 | return Instruction{ |
| 603 | | .LoadLiteral = .{ |
| 671 | .load_literal = .{ |
| 604 | 672 | .rt = rt.id(), |
| 605 | 673 | .imm19 = imm19, |
| 606 | 674 | .opc = 0b01, |
| ... | ... | @@ -618,7 +686,7 @@ pub const Instruction = union(enum) { |
| 618 | 686 | imm16: u16, |
| 619 | 687 | ) Instruction { |
| 620 | 688 | return Instruction{ |
| 621 | | .ExceptionGeneration = .{ |
| 689 | .exception_generation = .{ |
| 622 | 690 | .ll = ll, |
| 623 | 691 | .op2 = op2, |
| 624 | 692 | .imm16 = imm16, |
| ... | ... | @@ -637,7 +705,7 @@ pub const Instruction = union(enum) { |
| 637 | 705 | assert(rn.size() == 64); |
| 638 | 706 | |
| 639 | 707 | return Instruction{ |
| 640 | | .UnconditionalBranchRegister = .{ |
| 708 | .unconditional_branch_register = .{ |
| 641 | 709 | .op4 = op4, |
| 642 | 710 | .rn = rn.id(), |
| 643 | 711 | .op3 = op3, |
| ... | ... | @@ -652,7 +720,7 @@ pub const Instruction = union(enum) { |
| 652 | 720 | offset: i28, |
| 653 | 721 | ) Instruction { |
| 654 | 722 | return Instruction{ |
| 655 | | .UnconditionalBranchImmediate = .{ |
| 723 | .unconditional_branch_immediate = .{ |
| 656 | 724 | .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)), |
| 657 | 725 | .op = op, |
| 658 | 726 | }, |
| ... | ... | @@ -671,7 +739,7 @@ pub const Instruction = union(enum) { |
| 671 | 739 | 32 => { |
| 672 | 740 | assert(shift.amount < 32); |
| 673 | 741 | return Instruction{ |
| 674 | | .LogicalShiftedRegister = .{ |
| 742 | .logical_shifted_register = .{ |
| 675 | 743 | .rd = rd.id(), |
| 676 | 744 | .rn = rn.id(), |
| 677 | 745 | .imm6 = shift.amount, |
| ... | ... | @@ -685,7 +753,7 @@ pub const Instruction = union(enum) { |
| 685 | 753 | }, |
| 686 | 754 | 64 => { |
| 687 | 755 | return Instruction{ |
| 688 | | .LogicalShiftedRegister = .{ |
| 756 | .logical_shifted_register = .{ |
| 689 | 757 | .rd = rd.id(), |
| 690 | 758 | .rn = rn.id(), |
| 691 | 759 | .imm6 = shift.amount, |
| ... | ... | @@ -710,7 +778,7 @@ pub const Instruction = union(enum) { |
| 710 | 778 | shift: bool, |
| 711 | 779 | ) Instruction { |
| 712 | 780 | return Instruction{ |
| 713 | | .AddSubtractImmediate = .{ |
| 781 | .add_subtract_immediate = .{ |
| 714 | 782 | .rd = rd.id(), |
| 715 | 783 | .rn = rn.id(), |
| 716 | 784 | .imm12 = imm12, |
| ... | ... | @@ -726,6 +794,43 @@ pub const Instruction = union(enum) { |
| 726 | 794 | }; |
| 727 | 795 | } |
| 728 | 796 | |
| 797 | fn conditionalBranch( |
| 798 | o0: u1, |
| 799 | o1: u1, |
| 800 | cond: Condition, |
| 801 | offset: i21, |
| 802 | ) Instruction { |
| 803 | assert(offset & 0b11 == 0b00); |
| 804 | return Instruction{ |
| 805 | .conditional_branch = .{ |
| 806 | .cond = @enumToInt(cond), |
| 807 | .o0 = o0, |
| 808 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), |
| 809 | .o1 = o1, |
| 810 | }, |
| 811 | }; |
| 812 | } |
| 813 | |
| 814 | fn compareAndBranch( |
| 815 | op: u1, |
| 816 | rt: Register, |
| 817 | offset: i21, |
| 818 | ) Instruction { |
| 819 | assert(offset & 0b11 == 0b00); |
| 820 | return Instruction{ |
| 821 | .compare_and_branch = .{ |
| 822 | .rt = rt.id(), |
| 823 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), |
| 824 | .op = op, |
| 825 | .sf = switch (rt.size()) { |
| 826 | 32 => 0b0, |
| 827 | 64 => 0b1, |
| 828 | else => unreachable, // unexpected register size |
| 829 | }, |
| 830 | }, |
| 831 | }; |
| 832 | } |
| 833 | |
| 729 | 834 | // Helper functions for assembly syntax functions |
| 730 | 835 | |
| 731 | 836 | // Move wide (immediate) |
| ... | ... | @@ -821,19 +926,19 @@ pub const Instruction = union(enum) { |
| 821 | 926 | }; |
| 822 | 927 | |
| 823 | 928 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 824 | | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); |
| 929 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); |
| 825 | 930 | } |
| 826 | 931 | |
| 827 | 932 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| 828 | | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true); |
| 933 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true); |
| 829 | 934 | } |
| 830 | 935 | |
| 831 | 936 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 832 | | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); |
| 937 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); |
| 833 | 938 | } |
| 834 | 939 | |
| 835 | 940 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| 836 | | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, false); |
| 941 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false); |
| 837 | 942 | } |
| 838 | 943 | |
| 839 | 944 | // Exception generation |
| ... | ... | @@ -885,7 +990,7 @@ pub const Instruction = union(enum) { |
| 885 | 990 | // Nop |
| 886 | 991 | |
| 887 | 992 | pub fn nop() Instruction { |
| 888 | | return Instruction{ .NoOperation = .{} }; |
| 993 | return Instruction{ .no_operation = .{} }; |
| 889 | 994 | } |
| 890 | 995 | |
| 891 | 996 | // Logical (shifted register) |
| ... | ... | @@ -939,6 +1044,22 @@ pub const Instruction = union(enum) { |
| 939 | 1044 | pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { |
| 940 | 1045 | return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift); |
| 941 | 1046 | } |
| 1047 | |
| 1048 | // Conditional branch |
| 1049 | |
| 1050 | pub fn bCond(cond: Condition, offset: i21) Instruction { |
| 1051 | return conditionalBranch(0b0, 0b0, cond, offset); |
| 1052 | } |
| 1053 | |
| 1054 | // Compare and branch |
| 1055 | |
| 1056 | pub fn cbz(rt: Register, offset: i21) Instruction { |
| 1057 | return compareAndBranch(0b0, rt, offset); |
| 1058 | } |
| 1059 | |
| 1060 | pub fn cbnz(rt: Register, offset: i21) Instruction { |
| 1061 | return compareAndBranch(0b1, rt, offset); |
| 1062 | } |
| 942 | 1063 | }; |
| 943 | 1064 | |
| 944 | 1065 | test { |
| ... | ... | @@ -1092,6 +1213,14 @@ test "serialize instructions" { |
| 1092 | 1213 | .inst = Instruction.subs(.x0, .x5, 11, true), |
| 1093 | 1214 | .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000, |
| 1094 | 1215 | }, |
| 1216 | .{ // b.hi #-4 |
| 1217 | .inst = Instruction.bCond(.hi, -4), |
| 1218 | .expected = 0b0101010_0_1111111111111111111_0_1000, |
| 1219 | }, |
| 1220 | .{ // cbz x10, #40 |
| 1221 | .inst = Instruction.cbz(.x10, 40), |
| 1222 | .expected = 0b1_011010_0_0000000000000001010_01010, |
| 1223 | }, |
| 1095 | 1224 | }; |
| 1096 | 1225 | |
| 1097 | 1226 | for (testcases) |case| { |