Commit 96501d3385

Jacob Young <jacobly0@users.noreply.github.com>
2024-06-16 18:43:18
x86_64: get encoder tests passing again
1 parent 17f14e1
Changed files (4)
src/arch/x86_64/bits.zig
@@ -192,6 +192,7 @@ pub const Register = enum(u7) {
         x87,
         mmx,
         sse,
+        ip,
     };
 
     pub fn class(reg: Register) Class {
@@ -209,6 +210,7 @@ pub const Register = enum(u7) {
             @intFromEnum(Register.st0)  ... @intFromEnum(Register.st7)   => .x87,
 
             @intFromEnum(Register.es)   ... @intFromEnum(Register.gs)    => .segment,
+            @intFromEnum(Register.rip)  ... @intFromEnum(Register.ip)    => .ip,
 
             else => unreachable,
             // zig fmt: on
@@ -370,13 +372,14 @@ pub const Register = enum(u7) {
             .x87 => 33 + @as(u6, reg.enc()),
             .mmx => 41 + @as(u6, reg.enc()),
             .segment => 50 + @as(u6, reg.enc()),
+            .ip => unreachable,
         };
     }
 };
 
 test "Register id - different classes" {
     try expect(Register.al.id() == Register.ax.id());
-    try expect(Register.ah.id() == Register.spl.id());
+    try expect(Register.ah.id() != Register.spl.id());
     try expect(Register.ax.id() == Register.eax.id());
     try expect(Register.eax.id() == Register.rax.id());
 
@@ -391,6 +394,7 @@ test "Register id - different classes" {
 
 test "Register enc - different classes" {
     try expect(Register.al.enc() == Register.ax.enc());
+    try expect(Register.ah.enc() == Register.spl.enc());
     try expect(Register.ax.enc() == Register.eax.enc());
     try expect(Register.eax.enc() == Register.rax.enc());
     try expect(Register.ymm0.enc() == Register.rax.enc());
src/arch/x86_64/CodeGen.zig
@@ -14529,6 +14529,7 @@ fn moveStrategy(self: *Self, ty: Type, class: Register.Class, aligned: bool) !Mo
                 else => {},
             },
         },
+        .ip => {},
     }
     return self.fail("TODO moveStrategy for {}", .{ty.fmt(mod)});
 }
@@ -14685,6 +14686,7 @@ fn genSetReg(
                 else => unreachable,
             },
             .segment, .x87, .mmx, .sse => try self.genSetReg(dst_reg, ty, try self.genTypedValue(try mod.undefValue(ty)), opts),
+            .ip => unreachable,
         },
         .eflags => |cc| try self.asmSetccRegister(cc, dst_reg.to8()),
         .immediate => |imm| {
@@ -14722,7 +14724,7 @@ fn genSetReg(
                     registerAlias(dst_reg, abi_size),
                     src_reg,
                 ),
-                .x87, .mmx => unreachable,
+                .x87, .mmx, .ip => unreachable,
                 .sse => try self.asmRegisterRegister(
                     switch (abi_size) {
                         1...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov },
@@ -14738,7 +14740,7 @@ fn genSetReg(
                 dst_reg,
                 switch (src_reg.class()) {
                     .general_purpose, .segment => registerAlias(src_reg, abi_size),
-                    .x87, .mmx => unreachable,
+                    .x87, .mmx, .ip => unreachable,
                     .sse => try self.copyToTmpRegister(ty, src_mcv),
                 },
             ),
@@ -14753,7 +14755,7 @@ fn genSetReg(
                     },
                     else => unreachable,
                 },
-                .mmx, .sse => unreachable,
+                .mmx, .sse, .ip => unreachable,
             },
             .mmx => unreachable,
             .sse => switch (src_reg.class()) {
@@ -14772,7 +14774,7 @@ fn genSetReg(
                     .{ .register = try self.copyToTmpRegister(ty, src_mcv) },
                     opts,
                 ),
-                .x87, .mmx => unreachable,
+                .x87, .mmx, .ip => unreachable,
                 .sse => try self.asmRegisterRegister(
                     @as(?Mir.Inst.FixedTag, switch (ty.scalarType(mod).zigTypeTag(mod)) {
                         else => switch (abi_size) {
@@ -14799,6 +14801,7 @@ fn genSetReg(
                     registerAlias(src_reg, abi_size),
                 ),
             },
+            .ip => unreachable,
         },
         .register_pair => |src_regs| try self.genSetReg(dst_reg, ty, .{ .register = src_regs[0] }, opts),
         .register_offset,
@@ -14866,7 +14869,7 @@ fn genSetReg(
                         });
                         return;
                     },
-                    .segment, .mmx => unreachable,
+                    .segment, .mmx, .ip => unreachable,
                     .x87, .sse => {},
                 },
                 .load_direct => |sym_index| switch (dst_reg.class()) {
@@ -14884,7 +14887,7 @@ fn genSetReg(
                         });
                         return;
                     },
-                    .segment, .mmx => unreachable,
+                    .segment, .mmx, .ip => unreachable,
                     .x87, .sse => {},
                 },
                 .load_got, .load_tlv => {},
@@ -15047,7 +15050,7 @@ fn genSetMem(
             };
             const src_alias = registerAlias(src_reg, abi_size);
             const src_size: u32 = @intCast(switch (src_alias.class()) {
-                .general_purpose, .segment, .x87 => @divExact(src_alias.bitSize(), 8),
+                .general_purpose, .segment, .x87, .ip => @divExact(src_alias.bitSize(), 8),
                 .mmx, .sse => abi_size,
             });
             const src_align = Alignment.fromNonzeroByteUnits(math.ceilPowerOfTwoAssert(u32, src_size));
@@ -19077,6 +19080,14 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {
             reg.to256()
         else
             unreachable,
+        .ip => if (size_bytes <= 2)
+            .ip
+        else if (size_bytes <= 4)
+            .eip
+        else if (size_bytes <= 8)
+            .rip
+        else
+            unreachable,
     };
 }
 
src/arch/x86_64/encoder.zig
@@ -536,59 +536,62 @@ pub const Instruction = struct {
                     }
                     try encoder.disp32(sib.disp);
                 },
-                .reg => |base| if (base.class() == .segment) {
-                    // TODO audit this wrt SIB
-                    try encoder.modRm_SIBDisp0(operand_enc);
-                    if (mem.scaleIndex()) |si| {
-                        const scale = math.log2_int(u4, si.scale);
-                        try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
-                    } else {
-                        try encoder.sib_disp32();
-                    }
-                    try encoder.disp32(sib.disp);
-                } else {
-                    assert(base.class() == .general_purpose);
-                    const dst = base.lowEnc();
-                    const src = operand_enc;
-                    if (dst == 4 or mem.scaleIndex() != null) {
-                        if (sib.disp == 0 and dst != 5) {
-                            try encoder.modRm_SIBDisp0(src);
-                            if (mem.scaleIndex()) |si| {
-                                const scale = math.log2_int(u4, si.scale);
-                                try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
-                            } else {
-                                try encoder.sib_base(dst);
-                            }
-                        } else if (math.cast(i8, sib.disp)) |_| {
-                            try encoder.modRm_SIBDisp8(src);
-                            if (mem.scaleIndex()) |si| {
-                                const scale = math.log2_int(u4, si.scale);
-                                try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
+                .reg => |base| switch (base.class()) {
+                    .segment => {
+                        // TODO audit this wrt SIB
+                        try encoder.modRm_SIBDisp0(operand_enc);
+                        if (mem.scaleIndex()) |si| {
+                            const scale = math.log2_int(u4, si.scale);
+                            try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
+                        } else {
+                            try encoder.sib_disp32();
+                        }
+                        try encoder.disp32(sib.disp);
+                    },
+                    .general_purpose => {
+                        const dst = base.lowEnc();
+                        const src = operand_enc;
+                        if (dst == 4 or mem.scaleIndex() != null) {
+                            if (sib.disp == 0 and dst != 5) {
+                                try encoder.modRm_SIBDisp0(src);
+                                if (mem.scaleIndex()) |si| {
+                                    const scale = math.log2_int(u4, si.scale);
+                                    try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
+                                } else {
+                                    try encoder.sib_base(dst);
+                                }
+                            } else if (math.cast(i8, sib.disp)) |_| {
+                                try encoder.modRm_SIBDisp8(src);
+                                if (mem.scaleIndex()) |si| {
+                                    const scale = math.log2_int(u4, si.scale);
+                                    try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
+                                } else {
+                                    try encoder.sib_baseDisp8(dst);
+                                }
+                                try encoder.disp8(@as(i8, @truncate(sib.disp)));
                             } else {
-                                try encoder.sib_baseDisp8(dst);
+                                try encoder.modRm_SIBDisp32(src);
+                                if (mem.scaleIndex()) |si| {
+                                    const scale = math.log2_int(u4, si.scale);
+                                    try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
+                                } else {
+                                    try encoder.sib_baseDisp32(dst);
+                                }
+                                try encoder.disp32(sib.disp);
                             }
-                            try encoder.disp8(@as(i8, @truncate(sib.disp)));
                         } else {
-                            try encoder.modRm_SIBDisp32(src);
-                            if (mem.scaleIndex()) |si| {
-                                const scale = math.log2_int(u4, si.scale);
-                                try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
+                            if (sib.disp == 0 and dst != 5) {
+                                try encoder.modRm_indirectDisp0(src, dst);
+                            } else if (math.cast(i8, sib.disp)) |_| {
+                                try encoder.modRm_indirectDisp8(src, dst);
+                                try encoder.disp8(@as(i8, @truncate(sib.disp)));
                             } else {
-                                try encoder.sib_baseDisp32(dst);
+                                try encoder.modRm_indirectDisp32(src, dst);
+                                try encoder.disp32(sib.disp);
                             }
-                            try encoder.disp32(sib.disp);
-                        }
-                    } else {
-                        if (sib.disp == 0 and dst != 5) {
-                            try encoder.modRm_indirectDisp0(src, dst);
-                        } else if (math.cast(i8, sib.disp)) |_| {
-                            try encoder.modRm_indirectDisp8(src, dst);
-                            try encoder.disp8(@as(i8, @truncate(sib.disp)));
-                        } else {
-                            try encoder.modRm_indirectDisp32(src, dst);
-                            try encoder.disp32(sib.disp);
                         }
-                    }
+                    },
+                    else => unreachable,
                 },
                 .frame => if (@TypeOf(encoder).options.allow_frame_locs) {
                     try encoder.modRm_indirectDisp32(operand_enc, undefined);
@@ -1101,7 +1104,7 @@ const TestEncode = struct {
         var stream = std.io.fixedBufferStream(&enc.buffer);
         var count_writer = std.io.countingWriter(stream.writer());
         const inst = try Instruction.new(.none, mnemonic, ops);
-        try inst.encode(count_writer.writer());
+        try inst.encode(count_writer.writer(), .{});
         enc.index = count_writer.bytes_written;
     }
 
@@ -1118,7 +1121,7 @@ test "encode" {
         .{ .reg = .rbx },
         .{ .imm = Immediate.u(4) },
     });
-    try inst.encode(buf.writer());
+    try inst.encode(buf.writer(), .{});
     try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
 }
 
@@ -1181,7 +1184,7 @@ test "lower MI encoding" {
     try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
 
     try enc.encode(.mov, &.{
-        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
+        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
@@ -1205,7 +1208,7 @@ test "lower MI encoding" {
     try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
 
     try enc.encode(.mov, &.{
-        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11 }) },
+        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
@@ -1221,26 +1224,26 @@ test "lower MI encoding" {
     );
 
     try enc.encode(.mov, &.{
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -8 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
 
     try enc.encode(.mov, &.{
-        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -2 }) },
+        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) },
         .{ .imm = Immediate.s(-16) },
     });
     try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
 
     try enc.encode(.mov, &.{
-        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .rbp, .disp = -1 }) },
+        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.sib(.qword, .{
-            .base = .ds,
+            .base = .{ .reg = .ds },
             .disp = 0x10000000,
             .scale_index = .{ .scale = 2, .index = .rcx },
         }) },
@@ -1253,7 +1256,7 @@ test "lower MI encoding" {
     );
 
     try enc.encode(.adc, &.{
-        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .rbp, .disp = -0x10 }) },
+        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
@@ -1271,7 +1274,7 @@ test "lower MI encoding" {
     try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
 
     try enc.encode(.add, &.{
-        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .rdx, .disp = -8 }) },
+        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
@@ -1283,13 +1286,13 @@ test "lower MI encoding" {
     try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
 
     try enc.encode(.add, &.{
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -0x10 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
         .{ .imm = Immediate.s(-0x10) },
     });
     try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
 
     try enc.encode(.@"and", &.{
-        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
@@ -1299,7 +1302,7 @@ test "lower MI encoding" {
     );
 
     try enc.encode(.@"and", &.{
-        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .es, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
@@ -1309,7 +1312,7 @@ test "lower MI encoding" {
     );
 
     try enc.encode(.@"and", &.{
-        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r12, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
@@ -1319,7 +1322,7 @@ test "lower MI encoding" {
     );
 
     try enc.encode(.sub, &.{
-        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
         .{ .imm = Immediate.u(0x10) },
     });
     try expectEqualHexStrings(
@@ -1334,26 +1337,26 @@ test "lower RM encoding" {
 
     try enc.encode(.mov, &.{
         .{ .reg = .rax },
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 } }) },
     });
     try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
 
     try enc.encode(.mov, &.{
         .{ .reg = .rbx },
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .ds, .disp = 0x10 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) },
     });
     try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10");
 
     try enc.encode(.mov, &.{
         .{ .reg = .rax },
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
     });
     try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]");
 
     try enc.encode(.mov, &.{
         .{ .reg = .rax },
         .{ .mem = Instruction.Memory.sib(.qword, .{
-            .base = .rbp,
+            .base = .{ .reg = .rbp },
             .scale_index = .{ .scale = 1, .index = .rcx },
             .disp = -8,
         }) },
@@ -1363,7 +1366,7 @@ test "lower RM encoding" {
     try enc.encode(.mov, &.{
         .{ .reg = .eax },
         .{ .mem = Instruction.Memory.sib(.dword, .{
-            .base = .rbp,
+            .base = .{ .reg = .rbp },
             .scale_index = .{ .scale = 4, .index = .rdx },
             .disp = -4,
         }) },
@@ -1373,7 +1376,7 @@ test "lower RM encoding" {
     try enc.encode(.mov, &.{
         .{ .reg = .rax },
         .{ .mem = Instruction.Memory.sib(.qword, .{
-            .base = .rbp,
+            .base = .{ .reg = .rbp },
             .scale_index = .{ .scale = 8, .index = .rcx },
             .disp = -8,
         }) },
@@ -1383,7 +1386,7 @@ test "lower RM encoding" {
     try enc.encode(.mov, &.{
         .{ .reg = .r8b },
         .{ .mem = Instruction.Memory.sib(.byte, .{
-            .base = .rsi,
+            .base = .{ .reg = .rsi },
             .scale_index = .{ .scale = 1, .index = .rcx },
             .disp = -24,
         }) },
@@ -1398,10 +1401,10 @@ test "lower RM encoding" {
     try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
 
     try enc.encode(.mov, &.{
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) },
+        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
         .{ .reg = .fs },
     });
-    try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs");
+    try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
 
     try enc.encode(.mov, &.{
         .{ .reg = .r12w },
@@ -1409,12 +1412,6 @@ test "lower RM encoding" {
     });
     try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs");
 
-    try enc.encode(.mov, &.{
-        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
-        .{ .reg = .fs },
-    });
-    try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
-
     try enc.encode(.movsx, &.{
         .{ .reg = .eax },
         .{ .reg = .bx },
@@ -1435,7 +1432,7 @@ test "lower RM encoding" {
 
     try enc.encode(.movsx, &.{
         .{ .reg = .eax },
-        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) },
+        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) },
     });
     try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
 
@@ -1496,7 +1493,7 @@ test "lower RM encoding" {
     try enc.encode(.lea, &.{
         .{ .reg = .rsi },
         .{ .mem = Instruction.Memory.sib(.qword, .{
-            .base = .rbp,
+            .base = .{ .reg = .rbp },
             .scale_index = .{ .scale = 1, .index = .rcx },
         }) },
     });
@@ -1504,31 +1501,31 @@ test "lower RM encoding" {
 
     try enc.encode(.add, &.{
         .{ .reg = .r11 },
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .ds, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
     });
     try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000");
 
     try enc.encode(.add, &.{
         .{ .reg = .r12b },
-        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
     });
     try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000");
 
     try enc.encode(.add, &.{
         .{ .reg = .r12b },
-        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .fs, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) },
     });
     try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000");
 
     try enc.encode(.sub, &.{
         .{ .reg = .r11 },
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r13, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) },
     });
     try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]");
 
     try enc.encode(.sub, &.{
         .{ .reg = .r11 },
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
     });
     try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]");
 
@@ -1562,7 +1559,7 @@ test "lower RMI encoding" {
 
     try enc.encode(.imul, &.{
         .{ .reg = .bx },
-        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
+        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
         .{ .imm = Immediate.s(-1024) },
     });
     try expectEqualHexStrings(
@@ -1573,7 +1570,7 @@ test "lower RMI encoding" {
 
     try enc.encode(.imul, &.{
         .{ .reg = .bx },
-        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
+        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
         .{ .imm = Immediate.u(1024) },
     });
     try expectEqualHexStrings(
@@ -1593,7 +1590,7 @@ test "lower MR encoding" {
     try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
 
     try enc.encode(.mov, &.{
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
         .{ .reg = .r11 },
     });
     try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11");
@@ -1606,7 +1603,7 @@ test "lower MR encoding" {
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.sib(.qword, .{
-            .base = .r11,
+            .base = .{ .reg = .r11 },
             .scale_index = .{ .scale = 2, .index = .r12 },
             .disp = 0x10,
         }) },
@@ -1622,7 +1619,7 @@ test "lower MR encoding" {
 
     try enc.encode(.mov, &.{
         .{ .mem = Instruction.Memory.sib(.byte, .{
-            .base = .r11,
+            .base = .{ .reg = .r11 },
             .scale_index = .{ .scale = 2, .index = .r12 },
             .disp = 0x10,
         }) },
@@ -1631,25 +1628,25 @@ test "lower MR encoding" {
     try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
 
     try enc.encode(.add, &.{
-        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
         .{ .reg = .r12b },
     });
     try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b");
 
     try enc.encode(.add, &.{
-        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
         .{ .reg = .r12d },
     });
     try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d");
 
     try enc.encode(.add, &.{
-        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) },
         .{ .reg = .r12d },
     });
     try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d");
 
     try enc.encode(.sub, &.{
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
         .{ .reg = .r12 },
     });
     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" {
     try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
 
     try enc.encode(.call, &.{
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12 }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 } }) },
     });
     try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
 
     try enc.encode(.call, &.{
         .{ .mem = Instruction.Memory.sib(.qword, .{
-            .base = null,
+            .base = .none,
             .scale_index = .{ .index = .r11, .scale = 2 },
         }) },
     });
@@ -1678,14 +1675,14 @@ test "lower M encoding" {
 
     try enc.encode(.call, &.{
         .{ .mem = Instruction.Memory.sib(.qword, .{
-            .base = null,
+            .base = .none,
             .scale_index = .{ .index = .r12, .scale = 2 },
         }) },
     });
     try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
 
     try enc.encode(.call, &.{
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .gs }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .gs } }) },
     });
     try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
 
@@ -1695,12 +1692,12 @@ test "lower M encoding" {
     try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
 
     try enc.encode(.push, &.{
-        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp }) },
+        .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp } }) },
     });
     try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
 
     try enc.encode(.push, &.{
-        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) },
+        .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) },
     });
     try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
 
@@ -1913,7 +1910,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand
 
 test "cannot encode" {
     try cannotEncode(.@"test", &.{
-        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
+        .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
         .{ .reg = .ah },
     });
     try cannotEncode(.@"test", &.{
@@ -2093,7 +2090,7 @@ const Assembler = struct {
     pub fn assemble(as: *Assembler, writer: anytype) !void {
         while (try as.next()) |parsed_inst| {
             const inst = try Instruction.new(.none, parsed_inst.mnemonic, &parsed_inst.ops);
-            try inst.encode(writer);
+            try inst.encode(writer, .{});
         }
     }
 
@@ -2262,26 +2259,26 @@ const Assembler = struct {
 
         // Supported rules and orderings.
         const rules = .{
-            .{ .open_br, .base, .close_br }, // [ base ]
-            .{ .open_br, .base, .plus, .disp, .close_br }, // [ base + disp ]
-            .{ .open_br, .base, .minus, .disp, .close_br }, // [ base - disp ]
-            .{ .open_br, .disp, .plus, .base, .close_br }, // [ disp + base ]
-            .{ .open_br, .base, .plus, .index, .close_br }, // [ base + index ]
-            .{ .open_br, .base, .plus, .index, .star, .scale, .close_br }, // [ base + index * scale ]
-            .{ .open_br, .index, .star, .scale, .plus, .base, .close_br }, // [ index * scale + base ]
-            .{ .open_br, .base, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ base + index * scale + disp ]
-            .{ .open_br, .base, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ base + index * scale - disp ]
-            .{ .open_br, .index, .star, .scale, .plus, .base, .plus, .disp, .close_br }, // [ index * scale + base + disp ]
-            .{ .open_br, .index, .star, .scale, .plus, .base, .minus, .disp, .close_br }, // [ index * scale + base - disp ]
-            .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .base, .close_br }, // [ disp + index * scale + base ]
-            .{ .open_br, .disp, .plus, .base, .plus, .index, .star, .scale, .close_br }, // [ disp + base + index * scale ]
-            .{ .open_br, .base, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base + disp + index * scale ]
-            .{ .open_br, .base, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ base - disp + index * scale ]
-            .{ .open_br, .base, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base + disp + scale * index ]
-            .{ .open_br, .base, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ base - disp + scale * index ]
+            .{ .open_br, .general_purpose, .close_br }, // [ general_purpose ]
+            .{ .open_br, .general_purpose, .plus, .disp, .close_br }, // [ general_purpose + disp ]
+            .{ .open_br, .general_purpose, .minus, .disp, .close_br }, // [ general_purpose - disp ]
+            .{ .open_br, .disp, .plus, .general_purpose, .close_br }, // [ disp + general_purpose ]
+            .{ .open_br, .general_purpose, .plus, .index, .close_br }, // [ general_purpose + index ]
+            .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + index * scale ]
+            .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ index * scale + general_purpose ]
+            .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ general_purpose + index * scale + disp ]
+            .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ general_purpose + index * scale - disp ]
+            .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .plus, .disp, .close_br }, // [ index * scale + general_purpose + disp ]
+            .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .minus, .disp, .close_br }, // [ index * scale + general_purpose - disp ]
+            .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ disp + index * scale + general_purpose ]
+            .{ .open_br, .disp, .plus, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ disp + general_purpose + index * scale ]
+            .{ .open_br, .general_purpose, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + disp + index * scale ]
+            .{ .open_br, .general_purpose, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose - disp + index * scale ]
+            .{ .open_br, .general_purpose, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose + disp + scale * index ]
+            .{ .open_br, .general_purpose, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose - disp + scale * index ]
             .{ .open_br, .rip, .plus, .disp, .close_br }, // [ rip + disp ]
             .{ .open_br, .rip, .minus, .disp, .close_br }, // [ rig - disp ]
-            .{ .base, .colon, .disp }, // seg:disp
+            .{ .segment, .colon, .disp }, // seg:disp
         };
 
         const pos = as.it.pos;
@@ -2301,7 +2298,7 @@ const Assembler = struct {
                         return Instruction.Memory.moffs(base, offset);
                     }
                     return Instruction.Memory.sib(ptr_size orelse .qword, .{
-                        .base = base,
+                        .base = .{ .reg = base },
                         .scale_index = res.scale_index,
                         .disp = res.disp orelse 0,
                     });
@@ -2323,7 +2320,7 @@ const Assembler = struct {
         offset: ?u64 = null,
     };
 
-    fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!Instruction.MemoryParseResult {
+    fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult {
         var res: MemoryParseResult = .{};
         inline for (rule, 0..) |cond, i| {
             if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
@@ -2333,9 +2330,11 @@ const Assembler = struct {
                 .open_br, .close_br, .plus, .minus, .star, .colon => {
                     _ = try as.expect(cond);
                 },
-                .base => {
+                .general_purpose, .segment => {
                     const tok = try as.expect(.string);
-                    res.base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
+                    const base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
+                    if (base.class() != cond) return error.InvalidMemoryOperand;
+                    res.base = base;
                 },
                 .rip => {
                     const tok = try as.expect(.string);
@@ -2529,8 +2528,8 @@ test "assemble" {
         0xF3, 0x45, 0x0F, 0x10, 0xF9,
         0xF2, 0x44, 0x0F, 0x10, 0x45, 0xF0,
         0xF2, 0x0F, 0x11, 0x45, 0xF8,
-        0xF3, 0x44, 0x0F, 0x7E, 0x45, 0xF0,
-        0x66, 0x44, 0x0F, 0xD6, 0x45, 0xF0,
+        0x66, 0x4C, 0x0F, 0x6E, 0x45, 0xF0,
+        0x66, 0x4C, 0x0F, 0x7E, 0x45, 0xF0,
         0x66, 0x0F, 0x2E, 0x45, 0xF0,
         0xDD, 0x4D, 0xF0,
         0xDF, 0x0D, 0x20, 0x00, 0x00, 0x00,
src/arch/x86_64/Encoding.zig
@@ -487,6 +487,7 @@ pub const Op = enum {
                     256 => .ymm,
                     else => unreachable,
                 },
+                .ip => unreachable,
             },
 
             .mem => |mem| switch (mem) {