authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-11 23:18:01+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-19 19:36:35+02:00
log70d809e0bbc51efa78b54838cc64158b4d4a2dd2
treec82a66fcb3f530b0054d023efb36a5f6c2269c8a
parent50a5ddecc54b88cf295fb97b47fecd2a29a1bf8e

x64: add AVX registers and Vex prefix sub-encoder


1 files changed, 209 insertions(+), 2 deletions(-)

src/arch/x86_64/bits.zig+209-2
......@@ -8,7 +8,7 @@ const DW = std.dwarf;
88
99// zig fmt: off
1010
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.
1212/// The registers are defined such that IDs go in descending order of 64-bit,
1313/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
1414/// registers. This results in some useful properties:
......@@ -126,6 +126,52 @@ pub const Register = enum(u7) {
126126 }
127127};
128128
129/// AVX registers.
130/// TODO missing dwarfLocOp implementation.
131/// TODO add support for AVX-512
132pub 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
129175// zig fmt: on
130176
131177/// Encoding helper functions for x86_64 instructions
......@@ -251,6 +297,98 @@ pub const Encoder = struct {
251297 self.code.appendAssumeCapacity(0x66);
252298 }
253299
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
254392 /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB
255393 pub const Rex = struct {
256394 /// Wide, enables 64-bit operation
......@@ -543,7 +681,7 @@ pub const Encoder = struct {
543681 }
544682};
545683
546test "x86_64 Encoder helpers" {
684test "Encoder helpers - general purpose registers" {
547685 var code = ArrayList(u8).init(testing.allocator);
548686 defer code.deinit();
549687
......@@ -615,6 +753,75 @@ test "x86_64 Encoder helpers" {
615753 }
616754}
617755
756test "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
618825// TODO add these registers to the enum and populate dwarfLocOp
619826// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
620827// RA = (16, "RA"),