authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-12 21:43:44+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-19 19:36:35+02:00
log0835486249f5f3d187db61c57d4d075d65e10177
tree4f7edbb28030bcc7f135d6ea41bfa17f36e9bf25
parent019cc94ec7291c652b3e18980194e028f8df9ec1

x64: add vmovsd RM and MR lowerings (first draft)


2 files changed, 165 insertions(+), 32 deletions(-)

src/arch/x86_64/Emit.zig+154-21
......@@ -26,7 +26,7 @@ const Mir = @import("Mir.zig");
2626const Module = @import("../../Module.zig");
2727const Instruction = bits.Instruction;
2828const GpRegister = bits.Register;
29const AvxRegister = bits.Register;
29const AvxRegister = bits.AvxRegister;
3030const Type = @import("../../type.zig").Type;
3131
3232mir: Mir,
......@@ -1180,6 +1180,7 @@ const Tag = enum {
11801180 cmovng,
11811181 cmovb,
11821182 cmovnae,
1183 vmovsd,
11831184
11841185 fn isSetCC(tag: Tag) bool {
11851186 return switch (tag) {
......@@ -1393,6 +1394,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
13931394 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),
13941395 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),
13951396 .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85),
1397 .vmovsd => OpCode.oneByte(0x11),
13961398 else => null,
13971399 },
13981400 .rm => return switch (tag) {
......@@ -1413,6 +1415,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
14131415 .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44),
14141416 .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42),
14151417 .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c),
1418 .vmovsd => OpCode.oneByte(0x10),
14161419 else => null,
14171420 },
14181421 .oi => return switch (tag) {
......@@ -1499,6 +1502,80 @@ inline fn getModRmExt(tag: Tag) ?u3 {
14991502 };
15001503}
15011504
1505const VexPrefix = struct {
1506 prefix: Encoder.Vex,
1507 reg: ?enum {
1508 ndd,
1509 nds,
1510 dds,
1511 },
1512};
1513
1514inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix {
1515 const desc: struct {
1516 reg: enum {
1517 none,
1518 ndd,
1519 nds,
1520 dds,
1521 } = .none,
1522 len_256: bool = false,
1523 wig: bool = false,
1524 lig: bool = false,
1525 lz: bool = false,
1526 lead_opc: enum {
1527 l_0f,
1528 l_0f_3a,
1529 l_0f_38,
1530 } = .l_0f,
1531 simd_prefix: enum {
1532 none,
1533 p_66,
1534 p_f2,
1535 p_f3,
1536 } = .none,
1537 } = blk: {
1538 switch (enc) {
1539 .mr => switch (tag) {
1540 .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true },
1541 else => return null,
1542 },
1543 .rm => switch (tag) {
1544 .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true },
1545 else => return null,
1546 },
1547 else => unreachable,
1548 }
1549 };
1550
1551 var vex: Encoder.Vex = .{};
1552
1553 if (desc.len_256) vex.len_256();
1554 if (desc.wig) vex.wig();
1555 if (desc.lig) vex.lig();
1556 if (desc.lz) vex.lz();
1557
1558 switch (desc.lead_opc) {
1559 .l_0f => {},
1560 .l_0f_3a => vex.lead_opc_0f_3a(),
1561 .l_0f_38 => vex.lead_opc_0f_38(),
1562 }
1563
1564 switch (desc.simd_prefix) {
1565 .none => {},
1566 .p_66 => vex.simd_prefix_66(),
1567 .p_f2 => vex.simd_prefix_f2(),
1568 .p_f3 => vex.simd_prefix_f3(),
1569 }
1570
1571 return VexPrefix{ .prefix = vex, .reg = switch (desc.reg) {
1572 .none => null,
1573 .nds => .nds,
1574 .dds => .dds,
1575 .ndd => .ndd,
1576 } };
1577}
1578
15021579const ScaleIndex = struct {
15031580 scale: u2,
15041581 index: GpRegister,
......@@ -1913,24 +1990,48 @@ fn lowerToRmEnc(
19131990 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
19141991 },
19151992 .memory => |src_mem| {
1916 const encoder = try Encoder.init(code, 9);
1917 if (reg.size() == 16) {
1918 encoder.prefix16BitMode();
1919 }
1920 if (src_mem.base) |base| {
1921 // TODO handle 32-bit base register - requires prefix 0x67
1922 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
1923 encoder.rex(.{
1924 .w = setRexWRegister(reg),
1925 .r = reg.isExtended(),
1926 .b = base.isExtended(),
1927 });
1928 } else {
1929 encoder.rex(.{
1930 .w = setRexWRegister(reg),
1931 .r = reg.isExtended(),
1932 });
1933 }
1993 const encoder: Encoder = blk: {
1994 switch (reg) {
1995 .register => {
1996 const encoder = try Encoder.init(code, 9);
1997 if (reg.size() == 16) {
1998 encoder.prefix16BitMode();
1999 }
2000 if (src_mem.base) |base| {
2001 // TODO handle 32-bit base register - requires prefix 0x67
2002 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
2003 encoder.rex(.{
2004 .w = setRexWRegister(reg),
2005 .r = reg.isExtended(),
2006 .b = base.isExtended(),
2007 });
2008 } else {
2009 encoder.rex(.{
2010 .w = setRexWRegister(reg),
2011 .r = reg.isExtended(),
2012 });
2013 }
2014 break :blk encoder;
2015 },
2016 .avx_register => {
2017 const encoder = try Encoder.init(code, 10);
2018 var vex_prefix = getVexPrefix(tag, .rm).?;
2019 const vex = &vex_prefix.prefix;
2020 if (src_mem.base) |base| {
2021 vex.rex(.{
2022 .r = reg.isExtended(),
2023 .b = base.isExtended(),
2024 });
2025 } else {
2026 vex.rex(.{
2027 .r = reg.isExtended(),
2028 });
2029 }
2030 encoder.vex(vex_prefix.prefix);
2031 break :blk encoder;
2032 },
2033 }
2034 };
19342035 opc.encode(encoder);
19352036 src_mem.encode(encoder, reg.lowId());
19362037 },
......@@ -1959,7 +2060,7 @@ fn lowerToMrEnc(
19592060 encoder.modRm_direct(reg.lowId(), dst_reg.lowId());
19602061 },
19612062 .memory => |dst_mem| {
1962 const encoder = blk: {
2063 const encoder: Encoder = blk: {
19632064 switch (reg) {
19642065 .register => {
19652066 const encoder = try Encoder.init(code, 9);
......@@ -1981,7 +2082,23 @@ fn lowerToMrEnc(
19812082 break :blk encoder;
19822083 },
19832084 .avx_register => {
1984 unreachable;
2085 const encoder = try Encoder.init(code, 10);
2086 var vex_prefix = getVexPrefix(tag, .mr).?;
2087 const vex = &vex_prefix.prefix;
2088 if (dst_mem.base) |base| {
2089 vex.rex(.{
2090 .w = dst_mem.ptr_size == .qword_ptr,
2091 .r = reg.isExtended(),
2092 .b = base.isExtended(),
2093 });
2094 } else {
2095 vex.rex(.{
2096 .w = dst_mem.ptr_size == .qword_ptr,
2097 .r = reg.isExtended(),
2098 });
2099 }
2100 encoder.vex(vex_prefix.prefix);
2101 break :blk encoder;
19852102 },
19862103 }
19872104 };
......@@ -2247,6 +2364,14 @@ test "lower RM encoding" {
22472364 },
22482365 }), emit.code());
22492366 try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", emit.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]");
2367
2368 // AVX extension tests
2369 try lowerToRmEnc(.vmovsd, Register.avxReg(.xmm1), RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code());
2370 try expectEqualHexStrings(
2371 "\xC5\xFB\x10\x0D\x10\x00\x00\x00",
2372 emit.lowered(),
2373 "vmovsd xmm1, qword ptr [rip + 0x10]",
2374 );
22502375}
22512376
22522377test "lower MR encoding" {
......@@ -2282,6 +2407,14 @@ test "lower MR encoding" {
22822407 );
22832408 try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.reg(.r12), emit.code());
22842409 try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12");
2410
2411 // AVX extension tests
2412 try lowerToMrEnc(.vmovsd, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.avxReg(.xmm1), emit.code());
2413 try expectEqualHexStrings(
2414 "\xC5\xFB\x11\x0D\x10\x00\x00\x00",
2415 emit.lowered(),
2416 "vmovsd qword ptr [rip + 0x10], xmm1",
2417 );
22852418}
22862419
22872420test "lower OI encoding" {
src/arch/x86_64/bits.zig+11-11
......@@ -143,7 +143,7 @@ pub const AvxRegister = enum(u6) {
143143 none,
144144
145145 /// Returns the bit-width of the register.
146 pub fn size(self: AvxRegister) u4 {
146 pub fn size(self: AvxRegister) u9 {
147147 return switch (@enumToInt(self)) {
148148 0...15 => 256,
149149 16...31 => 128,
......@@ -152,7 +152,7 @@ pub const AvxRegister = enum(u6) {
152152 }
153153
154154 /// Returns whether the register is *extended*.
155 pub fn isExtended(self: Register) bool {
155 pub fn isExtended(self: AvxRegister) bool {
156156 return @enumToInt(self) & 0x08 != 0;
157157 }
158158
......@@ -308,9 +308,9 @@ pub const Encoder = struct {
308308 register: u4 = 0b1111,
309309 length: u1 = 0b0,
310310 simd_prefix: u2 = 0b00,
311 wig: bool = false,
312 lig: bool = false,
313 lz: bool = false,
311 wig_desc: bool = false,
312 lig_desc: bool = false,
313 lz_desc: bool = false,
314314
315315 pub fn rex(self: *Vex, r: Rex) void {
316316 self.rex_prefix = r;
......@@ -337,7 +337,7 @@ pub const Encoder = struct {
337337 }
338338
339339 pub fn len_256(self: *Vex) void {
340 assert(!self.lz);
340 assert(!self.lz_desc);
341341 self.length = 1;
342342 }
343343
......@@ -354,26 +354,26 @@ pub const Encoder = struct {
354354 }
355355
356356 pub fn wig(self: *Vex) void {
357 self.wig = true;
357 self.wig_desc = true;
358358 }
359359
360360 pub fn lig(self: *Vex) void {
361 self.lig = true;
361 self.lig_desc = true;
362362 }
363363
364364 pub fn lz(self: *Vex) void {
365 self.lz = true;
365 self.lz_desc = true;
366366 }
367367
368368 pub fn write(self: Vex, writer: anytype) usize {
369369 var buf: [3]u8 = .{0} ** 3;
370370 const form_3byte: bool = blk: {
371 if (self.rex_prefix.w and !self.wig) break :blk true;
371 if (self.rex_prefix.w and !self.wig_desc) break :blk true;
372372 if (self.rex_prefix.x or self.rex_prefix.b) break :blk true;
373373 break :blk self.lead_opc != 0b0_0001;
374374 };
375375
376 if (self.lz) {
376 if (self.lz_desc) {
377377 assert(self.length == 0);
378378 }
379379