| ... | @@ -26,7 +26,7 @@ const Mir = @import("Mir.zig"); | ... | @@ -26,7 +26,7 @@ const Mir = @import("Mir.zig"); |
| 26 | const Module = @import("../../Module.zig"); | 26 | const Module = @import("../../Module.zig"); |
| 27 | const Instruction = bits.Instruction; | 27 | const Instruction = bits.Instruction; |
| 28 | const GpRegister = bits.Register; | 28 | const GpRegister = bits.Register; |
| 29 | const AvxRegister = bits.Register; | 29 | const AvxRegister = bits.AvxRegister; |
| 30 | const Type = @import("../../type.zig").Type; | 30 | const Type = @import("../../type.zig").Type; |
| 31 | | 31 | |
| 32 | mir: Mir, | 32 | mir: 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, |
| 1183 | | 1184 | |
| 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 | } |
| 1501 | | 1504 | |
| | 1505 | const VexPrefix = struct { |
| | 1506 | prefix: Encoder.Vex, |
| | 1507 | reg: ?enum { |
| | 1508 | ndd, |
| | 1509 | nds, |
| | 1510 | dds, |
| | 1511 | }, |
| | 1512 | }; |
| | 1513 | |
| | 1514 | inline 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 | |
| 1502 | const ScaleIndex = struct { | 1579 | const 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 0x67 | 1998 | encoder.prefix16BitMode(); |
| 1922 | // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 | 1999 | } |
| 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 | } |
| 2251 | | 2376 | |
| 2252 | test "lower MR encoding" { | 2377 | test "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 | } |
| 2286 | | 2419 | |
| 2287 | test "lower OI encoding" { | 2420 | test "lower OI encoding" { |