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");...@@ -26,7 +26,7 @@ const Mir = @import("Mir.zig");
26const Module = @import("../../Module.zig");26const Module = @import("../../Module.zig");
27const Instruction = bits.Instruction;27const Instruction = bits.Instruction;
28const GpRegister = bits.Register;28const GpRegister = bits.Register;
29const AvxRegister = bits.Register;29const AvxRegister = bits.AvxRegister;
30const Type = @import("../../type.zig").Type;30const Type = @import("../../type.zig").Type;
3131
32mir: Mir,32mir: Mir,
...@@ -1180,6 +1180,7 @@ const Tag = enum {...@@ -1180,6 +1180,7 @@ const Tag = enum {
1180 cmovng,1180 cmovng,
1181 cmovb,1181 cmovb,
1182 cmovnae,1182 cmovnae,
1183 vmovsd,
11831184
1184 fn isSetCC(tag: Tag) bool {1185 fn isSetCC(tag: Tag) bool {
1185 return switch (tag) {1186 return switch (tag) {
...@@ -1393,6 +1394,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1393,6 +1394,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1393 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),1394 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),
1394 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),1395 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),
1395 .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85),1396 .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85),
1397 .vmovsd => OpCode.oneByte(0x11),
1396 else => null,1398 else => null,
1397 },1399 },
1398 .rm => return switch (tag) {1400 .rm => return switch (tag) {
...@@ -1413,6 +1415,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -1413,6 +1415,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
1413 .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44),1415 .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44),
1414 .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42),1416 .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42),
1415 .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c),1417 .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c),
1418 .vmovsd => OpCode.oneByte(0x10),
1416 else => null,1419 else => null,
1417 },1420 },
1418 .oi => return switch (tag) {1421 .oi => return switch (tag) {
...@@ -1499,6 +1502,80 @@ inline fn getModRmExt(tag: Tag) ?u3 {...@@ -1499,6 +1502,80 @@ inline fn getModRmExt(tag: Tag) ?u3 {
1499 };1502 };
1500}1503}
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
1502const ScaleIndex = struct {1579const ScaleIndex = struct {
1503 scale: u2,1580 scale: u2,
1504 index: GpRegister,1581 index: GpRegister,
...@@ -1913,24 +1990,48 @@ fn lowerToRmEnc(...@@ -1913,24 +1990,48 @@ fn lowerToRmEnc(
1913 encoder.modRm_direct(reg.lowId(), src_reg.lowId());1990 encoder.modRm_direct(reg.lowId(), src_reg.lowId());
1914 },1991 },
1915 .memory => |src_mem| {1992 .memory => |src_mem| {
1916 const encoder = try Encoder.init(code, 9);1993 const encoder: Encoder = blk: {
1917 if (reg.size() == 16) {1994 switch (reg) {
1918 encoder.prefix16BitMode();1995 .register => {
1919 }1996 const encoder = try Encoder.init(code, 9);
1920 if (src_mem.base) |base| {1997 if (reg.size() == 16) {
1921 // TODO handle 32-bit base register - requires prefix 0x671998 encoder.prefix16BitMode();
1922 // Intel Manual, Vol 1, chapter 3.6 and 3.6.11999 }
1923 encoder.rex(.{2000 if (src_mem.base) |base| {
1924 .w = setRexWRegister(reg),2001 // TODO handle 32-bit base register - requires prefix 0x67
1925 .r = reg.isExtended(),2002 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
1926 .b = base.isExtended(),2003 encoder.rex(.{
1927 });2004 .w = setRexWRegister(reg),
1928 } else {2005 .r = reg.isExtended(),
1929 encoder.rex(.{2006 .b = base.isExtended(),
1930 .w = setRexWRegister(reg),2007 });
1931 .r = reg.isExtended(),2008 } else {
1932 });2009 encoder.rex(.{
1933 }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 };
1934 opc.encode(encoder);2035 opc.encode(encoder);
1935 src_mem.encode(encoder, reg.lowId());2036 src_mem.encode(encoder, reg.lowId());
1936 },2037 },
...@@ -1959,7 +2060,7 @@ fn lowerToMrEnc(...@@ -1959,7 +2060,7 @@ fn lowerToMrEnc(
1959 encoder.modRm_direct(reg.lowId(), dst_reg.lowId());2060 encoder.modRm_direct(reg.lowId(), dst_reg.lowId());
1960 },2061 },
1961 .memory => |dst_mem| {2062 .memory => |dst_mem| {
1962 const encoder = blk: {2063 const encoder: Encoder = blk: {
1963 switch (reg) {2064 switch (reg) {
1964 .register => {2065 .register => {
1965 const encoder = try Encoder.init(code, 9);2066 const encoder = try Encoder.init(code, 9);
...@@ -1981,7 +2082,23 @@ fn lowerToMrEnc(...@@ -1981,7 +2082,23 @@ fn lowerToMrEnc(
1981 break :blk encoder;2082 break :blk encoder;
1982 },2083 },
1983 .avx_register => {2084 .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;
1985 },2102 },
1986 }2103 }
1987 };2104 };
...@@ -2247,6 +2364,14 @@ test "lower RM encoding" {...@@ -2247,6 +2364,14 @@ test "lower RM encoding" {
2247 },2364 },
2248 }), emit.code());2365 }), emit.code());
2249 try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", emit.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]");2366 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 );
2250}2375}
22512376
2252test "lower MR encoding" {2377test "lower MR encoding" {
...@@ -2282,6 +2407,14 @@ test "lower MR encoding" {...@@ -2282,6 +2407,14 @@ test "lower MR encoding" {
2282 );2407 );
2283 try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.reg(.r12), emit.code());2408 try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.reg(.r12), emit.code());
2284 try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12");2409 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 );
2285}2418}
22862419
2287test "lower OI encoding" {2420test "lower OI encoding" {
src/arch/x86_64/bits.zig+11-11
...@@ -143,7 +143,7 @@ pub const AvxRegister = enum(u6) {...@@ -143,7 +143,7 @@ pub const AvxRegister = enum(u6) {
143 none,143 none,
144144
145 /// Returns the bit-width of the register.145 /// Returns the bit-width of the register.
146 pub fn size(self: AvxRegister) u4 {146 pub fn size(self: AvxRegister) u9 {
147 return switch (@enumToInt(self)) {147 return switch (@enumToInt(self)) {
148 0...15 => 256,148 0...15 => 256,
149 16...31 => 128,149 16...31 => 128,
...@@ -152,7 +152,7 @@ pub const AvxRegister = enum(u6) {...@@ -152,7 +152,7 @@ pub const AvxRegister = enum(u6) {
152 }152 }
153153
154 /// Returns whether the register is *extended*.154 /// Returns whether the register is *extended*.
155 pub fn isExtended(self: Register) bool {155 pub fn isExtended(self: AvxRegister) bool {
156 return @enumToInt(self) & 0x08 != 0;156 return @enumToInt(self) & 0x08 != 0;
157 }157 }
158158
...@@ -308,9 +308,9 @@ pub const Encoder = struct {...@@ -308,9 +308,9 @@ pub const Encoder = struct {
308 register: u4 = 0b1111,308 register: u4 = 0b1111,
309 length: u1 = 0b0,309 length: u1 = 0b0,
310 simd_prefix: u2 = 0b00,310 simd_prefix: u2 = 0b00,
311 wig: bool = false,311 wig_desc: bool = false,
312 lig: bool = false,312 lig_desc: bool = false,
313 lz: bool = false,313 lz_desc: bool = false,
314314
315 pub fn rex(self: *Vex, r: Rex) void {315 pub fn rex(self: *Vex, r: Rex) void {
316 self.rex_prefix = r;316 self.rex_prefix = r;
...@@ -337,7 +337,7 @@ pub const Encoder = struct {...@@ -337,7 +337,7 @@ pub const Encoder = struct {
337 }337 }
338338
339 pub fn len_256(self: *Vex) void {339 pub fn len_256(self: *Vex) void {
340 assert(!self.lz);340 assert(!self.lz_desc);
341 self.length = 1;341 self.length = 1;
342 }342 }
343343
...@@ -354,26 +354,26 @@ pub const Encoder = struct {...@@ -354,26 +354,26 @@ pub const Encoder = struct {
354 }354 }
355355
356 pub fn wig(self: *Vex) void {356 pub fn wig(self: *Vex) void {
357 self.wig = true;357 self.wig_desc = true;
358 }358 }
359359
360 pub fn lig(self: *Vex) void {360 pub fn lig(self: *Vex) void {
361 self.lig = true;361 self.lig_desc = true;
362 }362 }
363363
364 pub fn lz(self: *Vex) void {364 pub fn lz(self: *Vex) void {
365 self.lz = true;365 self.lz_desc = true;
366 }366 }
367367
368 pub fn write(self: Vex, writer: anytype) usize {368 pub fn write(self: Vex, writer: anytype) usize {
369 var buf: [3]u8 = .{0} ** 3;369 var buf: [3]u8 = .{0} ** 3;
370 const form_3byte: bool = blk: {370 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;
372 if (self.rex_prefix.x or self.rex_prefix.b) break :blk true;372 if (self.rex_prefix.x or self.rex_prefix.b) break :blk true;
373 break :blk self.lead_opc != 0b0_0001;373 break :blk self.lead_opc != 0b0_0001;
374 };374 };
375375
376 if (self.lz) {376 if (self.lz_desc) {
377 assert(self.length == 0);377 assert(self.length == 0);
378 }378 }
379379