From a430be097be2d6ec9237d2e81d6e576c5926dc1a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 24 Sep 2025 13:09:33 -0400 Subject: [PATCH] x86_64: support more in/out forms Closes #25303 --- src/arch/x86_64/CodeGen.zig | 107 ++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 46 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index ad27db719f..c33300a006 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -180113,50 +180113,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); @@ -180164,7 +180179,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 @@ -180186,13 +180201,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 }; } @@ -180211,14 +180226,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))), } }, @@ -180229,7 +180244,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, } }, @@ -180240,7 +180255,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, } }, @@ -180322,7 +180337,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}) @@ -180375,14 +180390,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', @@ -180392,7 +180407,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 }