authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-06-16 12:43:18-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-06-16 14:59:56-04:00
log96501d338550f47671692c669cb76eb99513a8da
tree61385f8edbd3e94c32befb476e4340a17b205ec0
parent17f14e1d65b59ebb1a8b5b617cc31fb2614f0c6a

x86_64: get encoder tests passing again


4 files changed, 148 insertions(+), 133 deletions(-)

src/arch/x86_64/CodeGen.zig+18-7
......@@ -14529,6 +14529,7 @@ fn moveStrategy(self: *Self, ty: Type, class: Register.Class, aligned: bool) !Mo
1452914529 else => {},
1453014530 },
1453114531 },
14532 .ip => {},
1453214533 }
1453314534 return self.fail("TODO moveStrategy for {}", .{ty.fmt(mod)});
1453414535}
......@@ -14685,6 +14686,7 @@ fn genSetReg(
1468514686 else => unreachable,
1468614687 },
1468714688 .segment, .x87, .mmx, .sse => try self.genSetReg(dst_reg, ty, try self.genTypedValue(try mod.undefValue(ty)), opts),
14689 .ip => unreachable,
1468814690 },
1468914691 .eflags => |cc| try self.asmSetccRegister(cc, dst_reg.to8()),
1469014692 .immediate => |imm| {
......@@ -14722,7 +14724,7 @@ fn genSetReg(
1472214724 registerAlias(dst_reg, abi_size),
1472314725 src_reg,
1472414726 ),
14725 .x87, .mmx => unreachable,
14727 .x87, .mmx, .ip => unreachable,
1472614728 .sse => try self.asmRegisterRegister(
1472714729 switch (abi_size) {
1472814730 1...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov },
......@@ -14738,7 +14740,7 @@ fn genSetReg(
1473814740 dst_reg,
1473914741 switch (src_reg.class()) {
1474014742 .general_purpose, .segment => registerAlias(src_reg, abi_size),
14741 .x87, .mmx => unreachable,
14743 .x87, .mmx, .ip => unreachable,
1474214744 .sse => try self.copyToTmpRegister(ty, src_mcv),
1474314745 },
1474414746 ),
......@@ -14753,7 +14755,7 @@ fn genSetReg(
1475314755 },
1475414756 else => unreachable,
1475514757 },
14756 .mmx, .sse => unreachable,
14758 .mmx, .sse, .ip => unreachable,
1475714759 },
1475814760 .mmx => unreachable,
1475914761 .sse => switch (src_reg.class()) {
......@@ -14772,7 +14774,7 @@ fn genSetReg(
1477214774 .{ .register = try self.copyToTmpRegister(ty, src_mcv) },
1477314775 opts,
1477414776 ),
14775 .x87, .mmx => unreachable,
14777 .x87, .mmx, .ip => unreachable,
1477614778 .sse => try self.asmRegisterRegister(
1477714779 @as(?Mir.Inst.FixedTag, switch (ty.scalarType(mod).zigTypeTag(mod)) {
1477814780 else => switch (abi_size) {
......@@ -14799,6 +14801,7 @@ fn genSetReg(
1479914801 registerAlias(src_reg, abi_size),
1480014802 ),
1480114803 },
14804 .ip => unreachable,
1480214805 },
1480314806 .register_pair => |src_regs| try self.genSetReg(dst_reg, ty, .{ .register = src_regs[0] }, opts),
1480414807 .register_offset,
......@@ -14866,7 +14869,7 @@ fn genSetReg(
1486614869 });
1486714870 return;
1486814871 },
14869 .segment, .mmx => unreachable,
14872 .segment, .mmx, .ip => unreachable,
1487014873 .x87, .sse => {},
1487114874 },
1487214875 .load_direct => |sym_index| switch (dst_reg.class()) {
......@@ -14884,7 +14887,7 @@ fn genSetReg(
1488414887 });
1488514888 return;
1488614889 },
14887 .segment, .mmx => unreachable,
14890 .segment, .mmx, .ip => unreachable,
1488814891 .x87, .sse => {},
1488914892 },
1489014893 .load_got, .load_tlv => {},
......@@ -15047,7 +15050,7 @@ fn genSetMem(
1504715050 };
1504815051 const src_alias = registerAlias(src_reg, abi_size);
1504915052 const src_size: u32 = @intCast(switch (src_alias.class()) {
15050 .general_purpose, .segment, .x87 => @divExact(src_alias.bitSize(), 8),
15053 .general_purpose, .segment, .x87, .ip => @divExact(src_alias.bitSize(), 8),
1505115054 .mmx, .sse => abi_size,
1505215055 });
1505315056 const src_align = Alignment.fromNonzeroByteUnits(math.ceilPowerOfTwoAssert(u32, src_size));
......@@ -19077,6 +19080,14 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {
1907719080 reg.to256()
1907819081 else
1907919082 unreachable,
19083 .ip => if (size_bytes <= 2)
19084 .ip
19085 else if (size_bytes <= 4)
19086 .eip
19087 else if (size_bytes <= 8)
19088 .rip
19089 else
19090 unreachable,
1908019091 };
1908119092}
1908219093
src/arch/x86_64/Encoding.zig+1
......@@ -487,6 +487,7 @@ pub const Op = enum {
487487 256 => .ymm,
488488 else => unreachable,
489489 },
490 .ip => unreachable,
490491 },
491492
492493 .mem => |mem| switch (mem) {
src/arch/x86_64/bits.zig+5-1
......@@ -192,6 +192,7 @@ pub const Register = enum(u7) {
192192 x87,
193193 mmx,
194194 sse,
195 ip,
195196 };
196197
197198 pub fn class(reg: Register) Class {
......@@ -209,6 +210,7 @@ pub const Register = enum(u7) {
209210 @intFromEnum(Register.st0) ... @intFromEnum(Register.st7) => .x87,
210211
211212 @intFromEnum(Register.es) ... @intFromEnum(Register.gs) => .segment,
213 @intFromEnum(Register.rip) ... @intFromEnum(Register.ip) => .ip,
212214
213215 else => unreachable,
214216 // zig fmt: on
......@@ -370,13 +372,14 @@ pub const Register = enum(u7) {
370372 .x87 => 33 + @as(u6, reg.enc()),
371373 .mmx => 41 + @as(u6, reg.enc()),
372374 .segment => 50 + @as(u6, reg.enc()),
375 .ip => unreachable,
373376 };
374377 }
375378};
376379
377380test "Register id - different classes" {
378381 try expect(Register.al.id() == Register.ax.id());
379 try expect(Register.ah.id() == Register.spl.id());
382 try expect(Register.ah.id() != Register.spl.id());
380383 try expect(Register.ax.id() == Register.eax.id());
381384 try expect(Register.eax.id() == Register.rax.id());
382385
......@@ -391,6 +394,7 @@ test "Register id - different classes" {
391394
392395test "Register enc - different classes" {
393396 try expect(Register.al.enc() == Register.ax.enc());
397 try expect(Register.ah.enc() == Register.spl.enc());
394398 try expect(Register.ax.enc() == Register.eax.enc());
395399 try expect(Register.eax.enc() == Register.rax.enc());
396400 try expect(Register.ymm0.enc() == Register.rax.enc());
src/arch/x86_64/encoder.zig+124-125
......@@ -536,59 +536,62 @@ pub const Instruction = struct {
536536 }
537537 try encoder.disp32(sib.disp);
538538 },
539 .reg => |base| if (base.class() == .segment) {
540 // TODO audit this wrt SIB
541 try encoder.modRm_SIBDisp0(operand_enc);
542 if (mem.scaleIndex()) |si| {
543 const scale = math.log2_int(u4, si.scale);
544 try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
545 } else {
546 try encoder.sib_disp32();
547 }
548 try encoder.disp32(sib.disp);
549 } else {
550 assert(base.class() == .general_purpose);
551 const dst = base.lowEnc();
552 const src = operand_enc;
553 if (dst == 4 or mem.scaleIndex() != null) {
554 if (sib.disp == 0 and dst != 5) {
555 try encoder.modRm_SIBDisp0(src);
556 if (mem.scaleIndex()) |si| {
557 const scale = math.log2_int(u4, si.scale);
558 try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
559 } else {
560 try encoder.sib_base(dst);
561 }
562 } else if (math.cast(i8, sib.disp)) |_| {
563 try encoder.modRm_SIBDisp8(src);
564 if (mem.scaleIndex()) |si| {
565 const scale = math.log2_int(u4, si.scale);
566 try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
539 .reg => |base| switch (base.class()) {
540 .segment => {
541 // TODO audit this wrt SIB
542 try encoder.modRm_SIBDisp0(operand_enc);
543 if (mem.scaleIndex()) |si| {
544 const scale = math.log2_int(u4, si.scale);
545 try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
546 } else {
547 try encoder.sib_disp32();
548 }
549 try encoder.disp32(sib.disp);
550 },
551 .general_purpose => {
552 const dst = base.lowEnc();
553 const src = operand_enc;
554 if (dst == 4 or mem.scaleIndex() != null) {
555 if (sib.disp == 0 and dst != 5) {
556 try encoder.modRm_SIBDisp0(src);
557 if (mem.scaleIndex()) |si| {
558 const scale = math.log2_int(u4, si.scale);
559 try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
560 } else {
561 try encoder.sib_base(dst);
562 }
563 } else if (math.cast(i8, sib.disp)) |_| {
564 try encoder.modRm_SIBDisp8(src);
565 if (mem.scaleIndex()) |si| {
566 const scale = math.log2_int(u4, si.scale);
567 try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
568 } else {
569 try encoder.sib_baseDisp8(dst);
570 }
571 try encoder.disp8(@as(i8, @truncate(sib.disp)));
567572 } else {
568 try encoder.sib_baseDisp8(dst);
573 try encoder.modRm_SIBDisp32(src);
574 if (mem.scaleIndex()) |si| {
575 const scale = math.log2_int(u4, si.scale);
576 try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
577 } else {
578 try encoder.sib_baseDisp32(dst);
579 }
580 try encoder.disp32(sib.disp);
569581 }
570 try encoder.disp8(@as(i8, @truncate(sib.disp)));
571582 } else {
572 try encoder.modRm_SIBDisp32(src);
573 if (mem.scaleIndex()) |si| {
574 const scale = math.log2_int(u4, si.scale);
575 try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
583 if (sib.disp == 0 and dst != 5) {
584 try encoder.modRm_indirectDisp0(src, dst);
585 } else if (math.cast(i8, sib.disp)) |_| {
586 try encoder.modRm_indirectDisp8(src, dst);
587 try encoder.disp8(@as(i8, @truncate(sib.disp)));
576588 } else {
577 try encoder.sib_baseDisp32(dst);
589 try encoder.modRm_indirectDisp32(src, dst);
590 try encoder.disp32(sib.disp);
578591 }
579 try encoder.disp32(sib.disp);
580 }
581 } else {
582 if (sib.disp == 0 and dst != 5) {
583 try encoder.modRm_indirectDisp0(src, dst);
584 } else if (math.cast(i8, sib.disp)) |_| {
585 try encoder.modRm_indirectDisp8(src, dst);
586 try encoder.disp8(@as(i8, @truncate(sib.disp)));
587 } else {
588 try encoder.modRm_indirectDisp32(src, dst);
589 try encoder.disp32(sib.disp);
590592 }
591 }
593 },
594 else => unreachable,
592595 },
593596 .frame => if (@TypeOf(encoder).options.allow_frame_locs) {
594597 try encoder.modRm_indirectDisp32(operand_enc, undefined);
......@@ -1101,7 +1104,7 @@ const TestEncode = struct {
11011104 var stream = std.io.fixedBufferStream(&enc.buffer);
11021105 var count_writer = std.io.countingWriter(stream.writer());
11031106 const inst = try Instruction.new(.none, mnemonic, ops);
1104 try inst.encode(count_writer.writer());
1107 try inst.encode(count_writer.writer(), .{});
11051108 enc.index = count_writer.bytes_written;
11061109 }
11071110
......@@ -1118,7 +1121,7 @@ test "encode" {
11181121 .{ .reg = .rbx },
11191122 .{ .imm = Immediate.u(4) },
11201123 });
1121 try inst.encode(buf.writer());
1124 try inst.encode(buf.writer(), .{});
11221125 try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
11231126}
11241127
......@@ -1181,7 +1184,7 @@ test "lower MI encoding" {
11811184 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
11821185
11831186 try enc.encode(.mov, &.{
1184 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
1187 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
11851188 .{ .imm = Immediate.u(0x10) },
11861189 });
11871190 try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
......@@ -1205,7 +1208,7 @@ test "lower MI encoding" {
12051208 try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
12061209
12071210 try enc.encode(.mov, &.{
1208 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11 }) },
1211 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) },
12091212 .{ .imm = Immediate.u(0x10) },
12101213 });
12111214 try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
......@@ -1221,26 +1224,26 @@ test "lower MI encoding" {
12211224 );
12221225
12231226 try enc.encode(.mov, &.{
1224 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -8 }) },
1227 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
12251228 .{ .imm = Immediate.u(0x10) },
12261229 });
12271230 try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
12281231
12291232 try enc.encode(.mov, &.{
1230 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -2 }) },
1233 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) },
12311234 .{ .imm = Immediate.s(-16) },
12321235 });
12331236 try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
12341237
12351238 try enc.encode(.mov, &.{
1236 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .rbp, .disp = -1 }) },
1239 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) },
12371240 .{ .imm = Immediate.u(0x10) },
12381241 });
12391242 try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
12401243
12411244 try enc.encode(.mov, &.{
12421245 .{ .mem = Instruction.Memory.sib(.qword, .{
1243 .base = .ds,
1246 .base = .{ .reg = .ds },
12441247 .disp = 0x10000000,
12451248 .scale_index = .{ .scale = 2, .index = .rcx },
12461249 }) },
......@@ -1253,7 +1256,7 @@ test "lower MI encoding" {
12531256 );
12541257
12551258 try enc.encode(.adc, &.{
1256 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .rbp, .disp = -0x10 }) },
1259 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
12571260 .{ .imm = Immediate.u(0x10) },
12581261 });
12591262 try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
......@@ -1271,7 +1274,7 @@ test "lower MI encoding" {
12711274 try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
12721275
12731276 try enc.encode(.add, &.{
1274 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .rdx, .disp = -8 }) },
1277 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) },
12751278 .{ .imm = Immediate.u(0x10) },
12761279 });
12771280 try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
......@@ -1283,13 +1286,13 @@ test "lower MI encoding" {
12831286 try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
12841287
12851288 try enc.encode(.add, &.{
1286 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -0x10 }) },
1289 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
12871290 .{ .imm = Immediate.s(-0x10) },
12881291 });
12891292 try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
12901293
12911294 try enc.encode(.@"and", &.{
1292 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
1295 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
12931296 .{ .imm = Immediate.u(0x10) },
12941297 });
12951298 try expectEqualHexStrings(
......@@ -1299,7 +1302,7 @@ test "lower MI encoding" {
12991302 );
13001303
13011304 try enc.encode(.@"and", &.{
1302 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .es, .disp = 0x10000000 }) },
1305 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
13031306 .{ .imm = Immediate.u(0x10) },
13041307 });
13051308 try expectEqualHexStrings(
......@@ -1309,7 +1312,7 @@ test "lower MI encoding" {
13091312 );
13101313
13111314 try enc.encode(.@"and", &.{
1312 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r12, .disp = 0x10000000 }) },
1315 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
13131316 .{ .imm = Immediate.u(0x10) },
13141317 });
13151318 try expectEqualHexStrings(
......@@ -1319,7 +1322,7 @@ test "lower MI encoding" {
13191322 );
13201323
13211324 try enc.encode(.sub, &.{
1322 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11, .disp = 0x10000000 }) },
1325 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
13231326 .{ .imm = Immediate.u(0x10) },
13241327 });
13251328 try expectEqualHexStrings(
......@@ -1334,26 +1337,26 @@ test "lower RM encoding" {
13341337
13351338 try enc.encode(.mov, &.{
13361339 .{ .reg = .rax },
1337 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11 }) },
1340 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 } }) },
13381341 });
13391342 try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
13401343
13411344 try enc.encode(.mov, &.{
13421345 .{ .reg = .rbx },
1343 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .ds, .disp = 0x10 }) },
1346 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) },
13441347 });
13451348 try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10");
13461349
13471350 try enc.encode(.mov, &.{
13481351 .{ .reg = .rax },
1349 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
1352 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
13501353 });
13511354 try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]");
13521355
13531356 try enc.encode(.mov, &.{
13541357 .{ .reg = .rax },
13551358 .{ .mem = Instruction.Memory.sib(.qword, .{
1356 .base = .rbp,
1359 .base = .{ .reg = .rbp },
13571360 .scale_index = .{ .scale = 1, .index = .rcx },
13581361 .disp = -8,
13591362 }) },
......@@ -1363,7 +1366,7 @@ test "lower RM encoding" {
13631366 try enc.encode(.mov, &.{
13641367 .{ .reg = .eax },
13651368 .{ .mem = Instruction.Memory.sib(.dword, .{
1366 .base = .rbp,
1369 .base = .{ .reg = .rbp },
13671370 .scale_index = .{ .scale = 4, .index = .rdx },
13681371 .disp = -4,
13691372 }) },
......@@ -1373,7 +1376,7 @@ test "lower RM encoding" {
13731376 try enc.encode(.mov, &.{
13741377 .{ .reg = .rax },
13751378 .{ .mem = Instruction.Memory.sib(.qword, .{
1376 .base = .rbp,
1379 .base = .{ .reg = .rbp },
13771380 .scale_index = .{ .scale = 8, .index = .rcx },
13781381 .disp = -8,
13791382 }) },
......@@ -1383,7 +1386,7 @@ test "lower RM encoding" {
13831386 try enc.encode(.mov, &.{
13841387 .{ .reg = .r8b },
13851388 .{ .mem = Instruction.Memory.sib(.byte, .{
1386 .base = .rsi,
1389 .base = .{ .reg = .rsi },
13871390 .scale_index = .{ .scale = 1, .index = .rcx },
13881391 .disp = -24,
13891392 }) },
......@@ -1398,10 +1401,10 @@ test "lower RM encoding" {
13981401 try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
13991402
14001403 try enc.encode(.mov, &.{
1401 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) },
1404 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
14021405 .{ .reg = .fs },
14031406 });
1404 try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs");
1407 try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
14051408
14061409 try enc.encode(.mov, &.{
14071410 .{ .reg = .r12w },
......@@ -1409,12 +1412,6 @@ test "lower RM encoding" {
14091412 });
14101413 try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs");
14111414
1412 try enc.encode(.mov, &.{
1413 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
1414 .{ .reg = .fs },
1415 });
1416 try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
1417
14181415 try enc.encode(.movsx, &.{
14191416 .{ .reg = .eax },
14201417 .{ .reg = .bx },
......@@ -1435,7 +1432,7 @@ test "lower RM encoding" {
14351432
14361433 try enc.encode(.movsx, &.{
14371434 .{ .reg = .eax },
1438 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) },
1435 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) },
14391436 });
14401437 try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
14411438
......@@ -1496,7 +1493,7 @@ test "lower RM encoding" {
14961493 try enc.encode(.lea, &.{
14971494 .{ .reg = .rsi },
14981495 .{ .mem = Instruction.Memory.sib(.qword, .{
1499 .base = .rbp,
1496 .base = .{ .reg = .rbp },
15001497 .scale_index = .{ .scale = 1, .index = .rcx },
15011498 }) },
15021499 });
......@@ -1504,31 +1501,31 @@ test "lower RM encoding" {
15041501
15051502 try enc.encode(.add, &.{
15061503 .{ .reg = .r11 },
1507 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .ds, .disp = 0x10000000 }) },
1504 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
15081505 });
15091506 try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000");
15101507
15111508 try enc.encode(.add, &.{
15121509 .{ .reg = .r12b },
1513 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
1510 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
15141511 });
15151512 try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000");
15161513
15171514 try enc.encode(.add, &.{
15181515 .{ .reg = .r12b },
1519 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .fs, .disp = 0x10000000 }) },
1516 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) },
15201517 });
15211518 try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000");
15221519
15231520 try enc.encode(.sub, &.{
15241521 .{ .reg = .r11 },
1525 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r13, .disp = 0x10000000 }) },
1522 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) },
15261523 });
15271524 try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]");
15281525
15291526 try enc.encode(.sub, &.{
15301527 .{ .reg = .r11 },
1531 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12, .disp = 0x10000000 }) },
1528 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
15321529 });
15331530 try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]");
15341531
......@@ -1562,7 +1559,7 @@ test "lower RMI encoding" {
15621559
15631560 try enc.encode(.imul, &.{
15641561 .{ .reg = .bx },
1565 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
1562 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
15661563 .{ .imm = Immediate.s(-1024) },
15671564 });
15681565 try expectEqualHexStrings(
......@@ -1573,7 +1570,7 @@ test "lower RMI encoding" {
15731570
15741571 try enc.encode(.imul, &.{
15751572 .{ .reg = .bx },
1576 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
1573 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
15771574 .{ .imm = Immediate.u(1024) },
15781575 });
15791576 try expectEqualHexStrings(
......@@ -1593,7 +1590,7 @@ test "lower MR encoding" {
15931590 try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
15941591
15951592 try enc.encode(.mov, &.{
1596 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
1593 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
15971594 .{ .reg = .r11 },
15981595 });
15991596 try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11");
......@@ -1606,7 +1603,7 @@ test "lower MR encoding" {
16061603
16071604 try enc.encode(.mov, &.{
16081605 .{ .mem = Instruction.Memory.sib(.qword, .{
1609 .base = .r11,
1606 .base = .{ .reg = .r11 },
16101607 .scale_index = .{ .scale = 2, .index = .r12 },
16111608 .disp = 0x10,
16121609 }) },
......@@ -1622,7 +1619,7 @@ test "lower MR encoding" {
16221619
16231620 try enc.encode(.mov, &.{
16241621 .{ .mem = Instruction.Memory.sib(.byte, .{
1625 .base = .r11,
1622 .base = .{ .reg = .r11 },
16261623 .scale_index = .{ .scale = 2, .index = .r12 },
16271624 .disp = 0x10,
16281625 }) },
......@@ -1631,25 +1628,25 @@ test "lower MR encoding" {
16311628 try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
16321629
16331630 try enc.encode(.add, &.{
1634 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
1631 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
16351632 .{ .reg = .r12b },
16361633 });
16371634 try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b");
16381635
16391636 try enc.encode(.add, &.{
1640 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
1637 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
16411638 .{ .reg = .r12d },
16421639 });
16431640 try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d");
16441641
16451642 try enc.encode(.add, &.{
1646 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) },
1643 .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) },
16471644 .{ .reg = .r12d },
16481645 });
16491646 try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d");
16501647
16511648 try enc.encode(.sub, &.{
1652 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) },
1649 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
16531650 .{ .reg = .r12 },
16541651 });
16551652 try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12");
......@@ -1664,13 +1661,13 @@ test "lower M encoding" {
16641661 try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
16651662
16661663 try enc.encode(.call, &.{
1667 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12 }) },
1664 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 } }) },
16681665 });
16691666 try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
16701667
16711668 try enc.encode(.call, &.{
16721669 .{ .mem = Instruction.Memory.sib(.qword, .{
1673 .base = null,
1670 .base = .none,
16741671 .scale_index = .{ .index = .r11, .scale = 2 },
16751672 }) },
16761673 });
......@@ -1678,14 +1675,14 @@ test "lower M encoding" {
16781675
16791676 try enc.encode(.call, &.{
16801677 .{ .mem = Instruction.Memory.sib(.qword, .{
1681 .base = null,
1678 .base = .none,
16821679 .scale_index = .{ .index = .r12, .scale = 2 },
16831680 }) },
16841681 });
16851682 try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
16861683
16871684 try enc.encode(.call, &.{
1688 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .gs }) },
1685 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .gs } }) },
16891686 });
16901687 try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
16911688
......@@ -1695,12 +1692,12 @@ test "lower M encoding" {
16951692 try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
16961693
16971694 try enc.encode(.push, &.{
1698 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp }) },
1695 .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp } }) },
16991696 });
17001697 try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
17011698
17021699 try enc.encode(.push, &.{
1703 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) },
1700 .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) },
17041701 });
17051702 try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
17061703
......@@ -1913,7 +1910,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand
19131910
19141911test "cannot encode" {
19151912 try cannotEncode(.@"test", &.{
1916 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
1913 .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
19171914 .{ .reg = .ah },
19181915 });
19191916 try cannotEncode(.@"test", &.{
......@@ -2093,7 +2090,7 @@ const Assembler = struct {
20932090 pub fn assemble(as: *Assembler, writer: anytype) !void {
20942091 while (try as.next()) |parsed_inst| {
20952092 const inst = try Instruction.new(.none, parsed_inst.mnemonic, &parsed_inst.ops);
2096 try inst.encode(writer);
2093 try inst.encode(writer, .{});
20972094 }
20982095 }
20992096
......@@ -2262,26 +2259,26 @@ const Assembler = struct {
22622259
22632260 // Supported rules and orderings.
22642261 const rules = .{
2265 .{ .open_br, .base, .close_br }, // [ base ]
2266 .{ .open_br, .base, .plus, .disp, .close_br }, // [ base + disp ]
2267 .{ .open_br, .base, .minus, .disp, .close_br }, // [ base - disp ]
2268 .{ .open_br, .disp, .plus, .base, .close_br }, // [ disp + base ]
2269 .{ .open_br, .base, .plus, .index, .close_br }, // [ base + index ]
2270 .{ .open_br, .base, .plus, .index, .star, .scale, .close_br }, // [ base + index * scale ]
2271 .{ .open_br, .index, .star, .scale, .plus, .base, .close_br }, // [ index * scale + base ]
2272 .{ .open_br, .base, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ base + index * scale + disp ]
2273 .{ .open_br, .base, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ base + index * scale - disp ]
2274 .{ .open_br, .index, .star, .scale, .plus, .base, .plus, .disp, .close_br }, // [ index * scale + base + disp ]
2275 .{ .open_br, .index, .star, .scale, .plus, .base, .minus, .disp, .close_br }, // [ index * scale + base - disp ]
2276 .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .base, .close_br }, // [ disp + index * scale + base ]
2277 .{ .open_br, .disp, .plus, .base, .plus, .index, .star, .scale, .close_br }, // [ disp + base + index * scale ]
2278 .{ .open_br, .base, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base + disp + index * scale ]
2279 .{ .open_br, .base, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base - disp + index * scale ]
2280 .{ .open_br, .base, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base + disp + scale * index ]
2281 .{ .open_br, .base, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base - disp + scale * index ]
2262 .{ .open_br, .general_purpose, .close_br }, // [ general_purpose ]
2263 .{ .open_br, .general_purpose, .plus, .disp, .close_br }, // [ general_purpose + disp ]
2264 .{ .open_br, .general_purpose, .minus, .disp, .close_br }, // [ general_purpose - disp ]
2265 .{ .open_br, .disp, .plus, .general_purpose, .close_br }, // [ disp + general_purpose ]
2266 .{ .open_br, .general_purpose, .plus, .index, .close_br }, // [ general_purpose + index ]
2267 .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + index * scale ]
2268 .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ index * scale + general_purpose ]
2269 .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ general_purpose + index * scale + disp ]
2270 .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ general_purpose + index * scale - disp ]
2271 .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .plus, .disp, .close_br }, // [ index * scale + general_purpose + disp ]
2272 .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .minus, .disp, .close_br }, // [ index * scale + general_purpose - disp ]
2273 .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ disp + index * scale + general_purpose ]
2274 .{ .open_br, .disp, .plus, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ disp + general_purpose + index * scale ]
2275 .{ .open_br, .general_purpose, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + disp + index * scale ]
2276 .{ .open_br, .general_purpose, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose - disp + index * scale ]
2277 .{ .open_br, .general_purpose, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose + disp + scale * index ]
2278 .{ .open_br, .general_purpose, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose - disp + scale * index ]
22822279 .{ .open_br, .rip, .plus, .disp, .close_br }, // [ rip + disp ]
22832280 .{ .open_br, .rip, .minus, .disp, .close_br }, // [ rig - disp ]
2284 .{ .base, .colon, .disp }, // seg:disp
2281 .{ .segment, .colon, .disp }, // seg:disp
22852282 };
22862283
22872284 const pos = as.it.pos;
......@@ -2301,7 +2298,7 @@ const Assembler = struct {
23012298 return Instruction.Memory.moffs(base, offset);
23022299 }
23032300 return Instruction.Memory.sib(ptr_size orelse .qword, .{
2304 .base = base,
2301 .base = .{ .reg = base },
23052302 .scale_index = res.scale_index,
23062303 .disp = res.disp orelse 0,
23072304 });
......@@ -2323,7 +2320,7 @@ const Assembler = struct {
23232320 offset: ?u64 = null,
23242321 };
23252322
2326 fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!Instruction.MemoryParseResult {
2323 fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult {
23272324 var res: MemoryParseResult = .{};
23282325 inline for (rule, 0..) |cond, i| {
23292326 if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
......@@ -2333,9 +2330,11 @@ const Assembler = struct {
23332330 .open_br, .close_br, .plus, .minus, .star, .colon => {
23342331 _ = try as.expect(cond);
23352332 },
2336 .base => {
2333 .general_purpose, .segment => {
23372334 const tok = try as.expect(.string);
2338 res.base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
2335 const base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
2336 if (base.class() != cond) return error.InvalidMemoryOperand;
2337 res.base = base;
23392338 },
23402339 .rip => {
23412340 const tok = try as.expect(.string);
......@@ -2529,8 +2528,8 @@ test "assemble" {
25292528 0xF3, 0x45, 0x0F, 0x10, 0xF9,
25302529 0xF2, 0x44, 0x0F, 0x10, 0x45, 0xF0,
25312530 0xF2, 0x0F, 0x11, 0x45, 0xF8,
2532 0xF3, 0x44, 0x0F, 0x7E, 0x45, 0xF0,
2533 0x66, 0x44, 0x0F, 0xD6, 0x45, 0xF0,
2531 0x66, 0x4C, 0x0F, 0x6E, 0x45, 0xF0,
2532 0x66, 0x4C, 0x0F, 0x7E, 0x45, 0xF0,
25342533 0x66, 0x0F, 0x2E, 0x45, 0xF0,
25352534 0xDD, 0x4D, 0xF0,
25362535 0xDF, 0x0D, 0x20, 0x00, 0x00, 0x00,