Commit 94d319a10f

Jacob Young <jacobly0@users.noreply.github.com>
2025-09-24 19:09:33
x86_64: support more in/out forms
Closes #25303
1 parent 561e556
Changed files (1)
src
arch
src/arch/x86_64/CodeGen.zig
@@ -180114,50 +180114,65 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
         }
 
         var mnem_size: struct {
+            op_has_size: std.StaticBitSet(4),
+            size: Memory.Size,
             used: bool,
-            size: ?Memory.Size,
-            fn use(size: *@This()) ?Memory.Size {
+            fn init(size: ?Memory.Size) @This() {
+                return .{
+                    .op_has_size = if (size) |_| .initFull() else .initEmpty(),
+                    .size = size orelse .none,
+                    .used = false,
+                };
+            }
+            fn use(size: *@This(), op_index: usize) ?Memory.Size {
+                if (!size.op_has_size.isSet(op_index)) return null;
                 size.used = true;
                 return size.size;
             }
-        } = .{
-            .used = false,
-            .size = if (prefix == .directive)
-                null
-            else if (std.mem.endsWith(u8, mnem_str, "b"))
-                .byte
-            else if (std.mem.endsWith(u8, mnem_str, "w"))
-                .word
-            else if (std.mem.endsWith(u8, mnem_str, "l"))
-                .dword
-            else if (std.mem.endsWith(u8, mnem_str, "q") and
-                (std.mem.indexOfScalar(u8, "vp", mnem_str[0]) == null or !std.mem.endsWith(u8, mnem_str, "dq")))
-                .qword
-            else if (std.mem.endsWith(u8, mnem_str, "t"))
-                .tbyte
-            else
-                null,
-        };
+        } = .init(if (prefix == .directive)
+            null
+        else if (std.mem.endsWith(u8, mnem_str, "b"))
+            .byte
+        else if (std.mem.endsWith(u8, mnem_str, "w"))
+            .word
+        else if (std.mem.endsWith(u8, mnem_str, "l"))
+            .dword
+        else if (std.mem.endsWith(u8, mnem_str, "q") and
+            (std.mem.indexOfScalar(u8, "vp", mnem_str[0]) == null or !std.mem.endsWith(u8, mnem_str, "dq")))
+            .qword
+        else if (std.mem.endsWith(u8, mnem_str, "t"))
+            .tbyte
+        else
+            null);
         var mnem_tag = while (true) break std.meta.stringToEnum(
             encoder.Instruction.Mnemonic,
-            mnem_str[0 .. mnem_str.len - @intFromBool(mnem_size.size != null)],
-        ) orelse if (mnem_size.size) |_| {
-            mnem_size.size = null;
+            mnem_str[0 .. mnem_str.len - @intFromBool(mnem_size.size != .none)],
+        ) orelse if (mnem_size.size != .none) {
+            mnem_size = .init(null);
             continue;
         } else return self.fail("invalid mnemonic: '{s}'", .{mnem_str});
-        if (@as(?Memory.Size, switch (mnem_tag) {
-            .clflush => .byte,
-            .fldcw, .fnstcw, .fstcw, .fnstsw, .fstsw => .word,
-            .fldenv, .fnstenv, .fstenv => .none,
-            .frstor, .fsave, .fnsave, .fxrstor, .fxrstor64, .fxsave, .fxsave64 => .none,
-            .invlpg => .none,
-            .invpcid => .xword,
-            .ldmxcsr, .stmxcsr, .vldmxcsr, .vstmxcsr => .dword,
-            else => null,
-        })) |fixed_mnem_size| {
-            if (mnem_size.size) |size| if (size != fixed_mnem_size)
+        fixed_mnem_size: {
+            const fixed_mnem_size: Memory.Size = switch (mnem_tag) {
+                .clflush => .byte,
+                .fldcw, .fnstcw, .fstcw, .fnstsw, .fstsw => .word,
+                .fldenv, .fnstenv, .fstenv => .none,
+                .frstor, .fsave, .fnsave, .fxrstor, .fxrstor64, .fxsave, .fxsave64 => .none,
+                .in => {
+                    mnem_size.op_has_size.unset(0);
+                    break :fixed_mnem_size;
+                },
+                .invlpg => .none,
+                .invpcid => .xword,
+                .ldmxcsr, .stmxcsr, .vldmxcsr, .vstmxcsr => .dword,
+                .out => {
+                    mnem_size.op_has_size.unset(1);
+                    break :fixed_mnem_size;
+                },
+                else => break :fixed_mnem_size,
+            };
+            if (mnem_size.size != .none and mnem_size.size != fixed_mnem_size)
                 return self.fail("invalid size: '{s}'", .{mnem_str});
-            mnem_size.size = fixed_mnem_size;
+            mnem_size = .init(fixed_mnem_size);
         }
 
         var ops: [4]Operand = @splat(.none);
@@ -180165,7 +180180,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
 
         var last_op = false;
         var op_it = std.mem.splitScalar(u8, mnem_it.rest(), ',');
-        next_op: for (&ops) |*op| {
+        next_op: for (&ops, 0..) |*op, op_index| {
             const op_str = while (!last_op) {
                 const full_str = op_it.next() orelse break :next_op;
                 const code_str = if (std.mem.indexOfScalar(u8, full_str, '#') orelse
@@ -180187,13 +180202,13 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
                     op.* = .{ .mem = .{
                         .base = .{ .reg = reg },
                         .mod = .{ .rm = .{
-                            .size = mnem_size.use() orelse
+                            .size = mnem_size.use(op_index) orelse
                                 return self.fail("unknown size: '{s}'", .{op_str}),
                             .disp = disp,
                         } },
                     } };
                 } else {
-                    if (mnem_size.use()) |size| if (reg.size().bitSize(self.target) != size.bitSize(self.target))
+                    if (mnem_size.use(op_index)) |size| if (reg.size().bitSize(self.target) != size.bitSize(self.target))
                         return self.fail("invalid register size: '{s}'", .{op_str});
                     op.* = .{ .reg = reg };
                 }
@@ -180212,14 +180227,14 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
                     else
                         return self.fail("invalid modifier: '{s}'", .{modifier}),
                     .register => |reg| if (std.mem.eql(u8, modifier, ""))
-                        .{ .reg = if (mnem_size.use()) |size| reg.toSize(size, self.target) else reg }
+                        .{ .reg = if (mnem_size.use(op_index)) |size| reg.toSize(size, self.target) else reg }
                     else
                         return self.fail("invalid modifier: '{s}'", .{modifier}),
                     .memory => |addr| if (std.mem.eql(u8, modifier, "") or std.mem.eql(u8, modifier, "P"))
                         .{ .mem = .{
                             .base = .{ .reg = .ds },
                             .mod = .{ .rm = .{
-                                .size = mnem_size.use() orelse
+                                .size = mnem_size.use(op_index) orelse
                                     return self.fail("unknown size: '{s}'", .{op_str}),
                                 .disp = @intCast(@as(i64, @bitCast(addr))),
                             } },
@@ -180230,7 +180245,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
                         .{ .mem = .{
                             .base = .{ .reg = reg_off.reg },
                             .mod = .{ .rm = .{
-                                .size = mnem_size.use() orelse
+                                .size = mnem_size.use(op_index) orelse
                                     return self.fail("unknown size: '{s}'", .{op_str}),
                                 .disp = reg_off.off,
                             } },
@@ -180241,7 +180256,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
                         .{ .mem = .{
                             .base = .{ .frame = frame_addr.index },
                             .mod = .{ .rm = .{
-                                .size = mnem_size.use() orelse
+                                .size = mnem_size.use(op_index) orelse
                                     return self.fail("unknown size: '{s}'", .{op_str}),
                                 .disp = frame_addr.off,
                             } },
@@ -180323,7 +180338,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
                     else
                         .none,
                     .mod = .{ .rm = .{
-                        .size = mnem_size.use() orelse return self.fail("unknown size: '{s}'", .{op_str}),
+                        .size = mnem_size.use(op_index) orelse return self.fail("unknown size: '{s}'", .{op_str}),
                         .index = if (index_str.len > 0)
                             parseRegName(index_str["%%".len..]) orelse
                                 return self.fail("invalid index register: '{s}'", .{op_str})
@@ -180376,14 +180391,14 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
 
         // convert from att syntax to intel syntax
         std.mem.reverse(Operand, ops[0..ops_len]);
-        if (!mnem_size.used) if (mnem_size.size) |size| {
+        if (mnem_size.size != .none and !mnem_size.used) {
             comptime var max_mnem_len: usize = 0;
             inline for (@typeInfo(encoder.Instruction.Mnemonic).@"enum".fields) |mnem|
                 max_mnem_len = @max(mnem.name.len, max_mnem_len);
             var intel_mnem_buf: [max_mnem_len + 1]u8 = undefined;
             const intel_mnem_str = std.fmt.bufPrint(&intel_mnem_buf, "{s}{c}", .{
                 @tagName(mnem_tag),
-                @as(u8, switch (size) {
+                @as(u8, switch (mnem_size.size) {
                     .byte => 'b',
                     .word => 'w',
                     .dword => 'd',
@@ -180393,7 +180408,7 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
                 }),
             }) catch unreachable;
             if (std.meta.stringToEnum(encoder.Instruction.Mnemonic, intel_mnem_str)) |intel_mnem_tag| mnem_tag = intel_mnem_tag;
-        };
+        }
         const mnem_name = @tagName(mnem_tag);
         const mnem_fixed_tag: Mir.Inst.FixedTag = if (prefix == .directive)
             .{ ._, .pseudo }