| ... | ... | @@ -8,7 +8,7 @@ const DW = std.dwarf; |
| 8 | 8 | |
| 9 | 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 | 12 | /// The registers are defined such that IDs go in descending order of 64-bit, |
| 13 | 13 | /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen |
| 14 | 14 | /// registers. This results in some useful properties: |
| ... | ... | @@ -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 | 175 | // zig fmt: on |
| 130 | 176 | |
| 131 | 177 | /// Encoding helper functions for x86_64 instructions |
| ... | ... | @@ -251,6 +297,98 @@ pub const Encoder = struct { |
| 251 | 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 | 392 | /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB |
| 255 | 393 | pub const Rex = struct { |
| 256 | 394 | /// Wide, enables 64-bit operation |
| ... | ... | @@ -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 | 685 | var code = ArrayList(u8).init(testing.allocator); |
| 548 | 686 | defer code.deinit(); |
| 549 | 687 | |
| ... | ... | @@ -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 | 825 | // TODO add these registers to the enum and populate dwarfLocOp |
| 619 | 826 | // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. |
| 620 | 827 | // RA = (16, "RA"), |