| ... | @@ -8,7 +8,7 @@ const DW = std.dwarf; | ... | @@ -8,7 +8,7 @@ const DW = std.dwarf; |
| 8 | | 8 | |
| 9 | // zig fmt: off | 9 | // zig fmt: off |
| 10 | | 10 | |
| 11 | /// Definitions of all of the x64 registers. The order is semantically meaningful. | 11 | /// Definitions of all of the general purpose x64 registers. The order is semantically meaningful. |
| 12 | /// The registers are defined such that IDs go in descending order of 64-bit, | 12 | /// The registers are defined such that IDs go in descending order of 64-bit, |
| 13 | /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen | 13 | /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen |
| 14 | /// registers. This results in some useful properties: | 14 | /// registers. This results in some useful properties: |
| ... | @@ -126,6 +126,52 @@ pub const Register = enum(u7) { | ... | @@ -126,6 +126,52 @@ pub const Register = enum(u7) { |
| 126 | } | 126 | } |
| 127 | }; | 127 | }; |
| 128 | | 128 | |
| | 129 | /// AVX registers. |
| | 130 | /// TODO missing dwarfLocOp implementation. |
| | 131 | /// TODO add support for AVX-512 |
| | 132 | pub const AvxRegister = enum(u6) { |
| | 133 | // 256-bit registers |
| | 134 | ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7, |
| | 135 | ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15, |
| | 136 | |
| | 137 | // 128-bit registers |
| | 138 | xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, |
| | 139 | xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, |
| | 140 | |
| | 141 | // Pseudo, used only for MIR to signify that the |
| | 142 | // operand is not a register but an immediate, etc. |
| | 143 | none, |
| | 144 | |
| | 145 | /// Returns the bit-width of the register. |
| | 146 | pub fn size(self: AvxRegister) u4 { |
| | 147 | return switch (@enumToInt(self)) { |
| | 148 | 0...15 => 256, |
| | 149 | 16...31 => 128, |
| | 150 | else => unreachable, |
| | 151 | }; |
| | 152 | } |
| | 153 | |
| | 154 | /// This returns the 4-bit register ID. |
| | 155 | pub fn id(self: AvxRegister) u4 { |
| | 156 | return @truncate(u4, @enumToInt(self)); |
| | 157 | } |
| | 158 | |
| | 159 | /// Like id, but only returns the lower 3 bits. |
| | 160 | pub fn lowId(self: AvxRegister) u3 { |
| | 161 | return @truncate(u3, @enumToInt(self)); |
| | 162 | } |
| | 163 | |
| | 164 | /// Convert from any register to its 256 bit alias. |
| | 165 | pub fn to256(self: AvxRegister) AvxRegister { |
| | 166 | return @intToEnum(AvxRegister, self.id()); |
| | 167 | } |
| | 168 | |
| | 169 | /// Convert from any register to its 128 bit alias. |
| | 170 | pub fn to128(self: AvxRegister) AvxRegister { |
| | 171 | return @intToEnum(AvxRegister, @as(u8, self.id()) + 16); |
| | 172 | } |
| | 173 | }; |
| | 174 | |
| 129 | // zig fmt: on | 175 | // zig fmt: on |
| 130 | | 176 | |
| 131 | /// Encoding helper functions for x86_64 instructions | 177 | /// Encoding helper functions for x86_64 instructions |
| ... | @@ -251,6 +297,98 @@ pub const Encoder = struct { | ... | @@ -251,6 +297,98 @@ pub const Encoder = struct { |
| 251 | self.code.appendAssumeCapacity(0x66); | 297 | self.code.appendAssumeCapacity(0x66); |
| 252 | } | 298 | } |
| 253 | | 299 | |
| | 300 | pub fn Vex(comptime count: comptime_int) type { |
| | 301 | if (count < 2 or count > 3) { |
| | 302 | @compileError("VEX prefix can either be 2- or 3-byte long"); |
| | 303 | } |
| | 304 | |
| | 305 | return struct { |
| | 306 | bytes: [count]u8 = switch (count) { |
| | 307 | 2 => .{ 0xc5, 0xf8 }, |
| | 308 | 3 => .{ 0xc4, 0xe1, 0xf8 }, |
| | 309 | else => unreachable, |
| | 310 | }, |
| | 311 | |
| | 312 | pub fn rex(self: *@This(), prefix: Rex) void { |
| | 313 | const byte = &self.bytes[1]; |
| | 314 | if (prefix.w) switch (count) { |
| | 315 | 3 => self.bytes[2] &= 0b0111_1111, |
| | 316 | else => unreachable, |
| | 317 | }; |
| | 318 | if (prefix.r) byte.* &= 0b0111_1111; |
| | 319 | if (prefix.x) switch (count) { |
| | 320 | 3 => byte.* &= 0b1011_1111, |
| | 321 | else => unreachable, |
| | 322 | }; |
| | 323 | if (prefix.b) switch (count) { |
| | 324 | 3 => byte.* &= 0b1101_1111, |
| | 325 | else => unreachable, |
| | 326 | }; |
| | 327 | } |
| | 328 | |
| | 329 | pub fn leading_opcode_0f(self: *@This()) void { |
| | 330 | switch (count) { |
| | 331 | 3 => self.bytes[1] |= 0b0_0001, |
| | 332 | else => {}, |
| | 333 | } |
| | 334 | } |
| | 335 | |
| | 336 | pub fn leading_opcode_0f_38(self: *@This()) void { |
| | 337 | switch (count) { |
| | 338 | 3 => self.bytes[1] |= 0b0_0010, |
| | 339 | else => unreachable, |
| | 340 | } |
| | 341 | } |
| | 342 | |
| | 343 | pub fn leading_opcode_0f_3a(self: *@This()) void { |
| | 344 | switch (count) { |
| | 345 | 3 => self.bytes[1] |= 0b0_0011, |
| | 346 | else => unreachable, |
| | 347 | } |
| | 348 | } |
| | 349 | |
| | 350 | pub fn reg(self: *@This(), register: u4) void { |
| | 351 | const byte = &self.bytes[count - 1]; |
| | 352 | const mask = 0b1_0000_111; |
| | 353 | byte.* &= mask; |
| | 354 | byte.* |= @intCast(u7, ~register) << 3; |
| | 355 | } |
| | 356 | |
| | 357 | pub fn len_128(self: *@This()) void { |
| | 358 | const byte = &self.bytes[count - 1]; |
| | 359 | byte.* &= 0b0_11; |
| | 360 | } |
| | 361 | |
| | 362 | pub fn len_256(self: *@This()) void { |
| | 363 | const byte = &self.bytes[count - 1]; |
| | 364 | byte.* |= 0b1_00; |
| | 365 | } |
| | 366 | |
| | 367 | pub fn simd_prefix_66(self: *@This()) void { |
| | 368 | const byte = &self.bytes[count - 1]; |
| | 369 | byte.* |= 0b01; |
| | 370 | } |
| | 371 | |
| | 372 | pub fn simd_prefix_f2(self: *@This()) void { |
| | 373 | const byte = &self.bytes[count - 1]; |
| | 374 | byte.* |= 0b11; |
| | 375 | } |
| | 376 | |
| | 377 | pub fn simd_prefix_f3(self: *@This()) void { |
| | 378 | const byte = &self.bytes[count - 1]; |
| | 379 | byte.* |= 0b10; |
| | 380 | } |
| | 381 | }; |
| | 382 | } |
| | 383 | |
| | 384 | pub fn vex_2byte(self: Self, prefix: Vex(2)) void { |
| | 385 | self.code.appendSliceAssumeCapacity(&prefix.bytes); |
| | 386 | } |
| | 387 | |
| | 388 | pub fn vex_3byte(self: Self, prefix: Vex(3)) void { |
| | 389 | self.code.appendSliceAssumeCapacity(&prefix.bytes); |
| | 390 | } |
| | 391 | |
| 254 | /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB | 392 | /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB |
| 255 | pub const Rex = struct { | 393 | pub const Rex = struct { |
| 256 | /// Wide, enables 64-bit operation | 394 | /// Wide, enables 64-bit operation |
| ... | @@ -543,7 +681,7 @@ pub const Encoder = struct { | ... | @@ -543,7 +681,7 @@ pub const Encoder = struct { |
| 543 | } | 681 | } |
| 544 | }; | 682 | }; |
| 545 | | 683 | |
| 546 | test "x86_64 Encoder helpers" { | 684 | test "Encoder helpers - general purpose registers" { |
| 547 | var code = ArrayList(u8).init(testing.allocator); | 685 | var code = ArrayList(u8).init(testing.allocator); |
| 548 | defer code.deinit(); | 686 | defer code.deinit(); |
| 549 | | 687 | |
| ... | @@ -615,6 +753,75 @@ test "x86_64 Encoder helpers" { | ... | @@ -615,6 +753,75 @@ test "x86_64 Encoder helpers" { |
| 615 | } | 753 | } |
| 616 | } | 754 | } |
| 617 | | 755 | |
| | 756 | test "Encoder helpers - Vex prefix" { |
| | 757 | { |
| | 758 | var vex_prefix = Encoder.Vex(2){}; |
| | 759 | vex_prefix.rex(.{ |
| | 760 | .r = true, |
| | 761 | }); |
| | 762 | try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x78 }, &vex_prefix.bytes); |
| | 763 | } |
| | 764 | |
| | 765 | { |
| | 766 | var vex_prefix = Encoder.Vex(2){}; |
| | 767 | vex_prefix.reg(AvxRegister.xmm15.id()); |
| | 768 | try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, &vex_prefix.bytes); |
| | 769 | } |
| | 770 | |
| | 771 | { |
| | 772 | var vex_prefix = Encoder.Vex(3){}; |
| | 773 | vex_prefix.rex(.{ |
| | 774 | .w = true, |
| | 775 | .x = true, |
| | 776 | }); |
| | 777 | try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b101_0_0001, 0b0_1111_0_00 }, &vex_prefix.bytes); |
| | 778 | } |
| | 779 | |
| | 780 | { |
| | 781 | var vex_prefix = Encoder.Vex(3){}; |
| | 782 | vex_prefix.rex(.{ |
| | 783 | .w = true, |
| | 784 | .r = true, |
| | 785 | }); |
| | 786 | vex_prefix.len_256(); |
| | 787 | vex_prefix.leading_opcode_0f(); |
| | 788 | vex_prefix.simd_prefix_66(); |
| | 789 | try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b011_0_0001, 0b0_1111_1_01 }, &vex_prefix.bytes); |
| | 790 | } |
| | 791 | |
| | 792 | var code = ArrayList(u8).init(testing.allocator); |
| | 793 | defer code.deinit(); |
| | 794 | |
| | 795 | { |
| | 796 | // vmovapd xmm1, xmm2 |
| | 797 | const encoder = try Encoder.init(&code, 4); |
| | 798 | var vex = Encoder.Vex(2){}; |
| | 799 | vex.simd_prefix_66(); |
| | 800 | encoder.vex_2byte(vex); // use 64 bit operation |
| | 801 | encoder.opcode_1byte(0x28); |
| | 802 | encoder.modRm_direct(0, AvxRegister.xmm1.lowId()); |
| | 803 | try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items); |
| | 804 | } |
| | 805 | |
| | 806 | { |
| | 807 | try code.resize(0); |
| | 808 | |
| | 809 | // vmovhpd xmm13, xmm1, qword ptr [rip] |
| | 810 | const encoder = try Encoder.init(&code, 9); |
| | 811 | var vex = Encoder.Vex(2){}; |
| | 812 | vex.len_128(); |
| | 813 | vex.simd_prefix_66(); |
| | 814 | vex.leading_opcode_0f(); |
| | 815 | vex.rex(.{ .r = true }); |
| | 816 | vex.reg(AvxRegister.xmm1.id()); |
| | 817 | encoder.vex_2byte(vex); |
| | 818 | encoder.opcode_1byte(0x16); |
| | 819 | encoder.modRm_RIPDisp32(AvxRegister.xmm13.lowId()); |
| | 820 | encoder.disp32(0); |
| | 821 | try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items); |
| | 822 | } |
| | 823 | } |
| | 824 | |
| 618 | // TODO add these registers to the enum and populate dwarfLocOp | 825 | // TODO add these registers to the enum and populate dwarfLocOp |
| 619 | // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. | 826 | // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. |
| 620 | // RA = (16, "RA"), | 827 | // RA = (16, "RA"), |