Commit 37f763560b

Jacob Young <jacobly0@users.noreply.github.com>
2025-06-06 04:25:13
x86_64: fix switch dispatch bug
Also closes #23902
1 parent f28ef7e
Changed files (6)
src
test
behavior
standalone
stack_iterator
src/arch/x86_64/CodeGen.zig
@@ -161505,10 +161505,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
                                 for (elems, 0..) |elem_ref, field_index| {
                                     const elem_dies = bt.feed();
                                     if (loaded_struct.fieldIsComptime(ip, field_index)) continue;
-                                    var elem = try cg.tempFromOperand(elem_ref, elem_dies);
-                                    try res.write(&elem, .{ .disp = @intCast(loaded_struct.offsets.get(ip)[field_index]) }, cg);
-                                    try elem.die(cg);
-                                    try cg.resetTemps(reset_index);
+                                    if (!hack_around_sema_opv_bugs or Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]).hasRuntimeBitsIgnoreComptime(zcu)) {
+                                        var elem = try cg.tempFromOperand(elem_ref, elem_dies);
+                                        try res.write(&elem, .{ .disp = @intCast(loaded_struct.offsets.get(ip)[field_index]) }, cg);
+                                        try elem.die(cg);
+                                        try cg.resetTemps(reset_index);
+                                    }
                                 }
                             },
                             .@"packed" => return cg.fail("failed to select {s} {}", .{
@@ -175015,8 +175017,7 @@ fn lowerSwitchBr(
 ) !void {
     const zcu = cg.pt.zcu;
     const condition_ty = cg.typeOf(switch_br.operand);
-    const condition_int_info = cg.intInfo(condition_ty).?;
-    const condition_int_ty = try cg.pt.intType(condition_int_info.signedness, condition_int_info.bits);
+    const unsigned_condition_ty = try cg.pt.intType(.unsigned, cg.intInfo(condition_ty).?.bits);
 
     const ExpectedContents = extern struct {
         liveness_deaths: [1 << 8 | 1]Air.Inst.Index,
@@ -175087,8 +175088,8 @@ fn lowerSwitchBr(
             .{ .air_ref = Air.internedToRef(min.?.toIntern()) },
         );
         const else_reloc = if (switch_br.else_body_len > 0) else_reloc: {
-            var cond_temp = try cg.tempInit(condition_ty, condition_index);
-            var table_max_temp = try cg.tempFromValue(try cg.pt.intValue(condition_int_ty, table_len - 1));
+            var cond_temp = try cg.tempInit(unsigned_condition_ty, condition_index);
+            var table_max_temp = try cg.tempFromValue(try cg.pt.intValue(unsigned_condition_ty, table_len - 1));
             const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, cg) catch |err| switch (err) {
                 error.SelectFailed => unreachable,
                 else => |e| return e,
@@ -175416,8 +175417,7 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
 
     if (self.loop_switches.getPtr(br.block_inst)) |table| {
         const condition_ty = self.typeOf(br.operand);
-        const condition_int_info = self.intInfo(condition_ty).?;
-        const condition_int_ty = try self.pt.intType(condition_int_info.signedness, condition_int_info.bits);
+        const unsigned_condition_ty = try self.pt.intType(.unsigned, self.intInfo(condition_ty).?.bits);
         const condition_mcv = block_tracking.short;
         try self.spillEflagsIfOccupied();
         if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir(
@@ -175429,8 +175429,8 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
         switch (table.else_relocs) {
             .@"unreachable" => {},
             .forward => |*else_relocs| {
-                var cond_temp = try self.tempInit(condition_ty, condition_mcv);
-                var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table.len - 1));
+                var cond_temp = try self.tempInit(unsigned_condition_ty, condition_mcv);
+                var table_max_temp = try self.tempFromValue(try self.pt.intValue(unsigned_condition_ty, table.len - 1));
                 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) {
                     error.SelectFailed => unreachable,
                     else => |e| return e,
@@ -175441,8 +175441,8 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
                 try cc_temp.die(self);
             },
             .backward => |else_reloc| {
-                var cond_temp = try self.tempInit(condition_ty, condition_mcv);
-                var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table.len - 1));
+                var cond_temp = try self.tempInit(unsigned_condition_ty, condition_mcv);
+                var table_max_temp = try self.tempFromValue(try self.pt.intValue(unsigned_condition_ty, table.len - 1));
                 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) {
                     error.SelectFailed => unreachable,
                     else => |e| return e,
src/Compilation.zig
@@ -774,7 +774,7 @@ pub const Directories = struct {
 /// `comp.debug_incremental`. It is inline so that comptime-known `false` propagates to the caller,
 /// preventing debugging features from making it into release builds of the compiler.
 pub inline fn debugIncremental(comp: *const Compilation) bool {
-    if (!build_options.enable_debug_extensions) return false;
+    if (!build_options.enable_debug_extensions or builtin.single_threaded) return false;
     return comp.debug_incremental;
 }
 
src/IncrementalDebugServer.zig
@@ -10,7 +10,7 @@
 
 comptime {
     // This file should only be referenced when debug extensions are enabled.
-    std.debug.assert(@import("build_options").enable_debug_extensions);
+    std.debug.assert(@import("build_options").enable_debug_extensions and !@import("builtin").single_threaded);
 }
 
 zcu: *Zcu,
src/main.zig
@@ -39,7 +39,7 @@ test {
     _ = Package;
 }
 
-const thread_stack_size = 50 << 20;
+const thread_stack_size = 60 << 20;
 
 pub const std_options: std.Options = .{
     .wasiCwd = wasi_cwd,
@@ -4208,7 +4208,7 @@ fn serve(
     const main_progress_node = std.Progress.start(.{});
     const file_system_inputs = comp.file_system_inputs.?;
 
-    const IncrementalDebugServer = if (build_options.enable_debug_extensions)
+    const IncrementalDebugServer = if (build_options.enable_debug_extensions and !builtin.single_threaded)
         @import("IncrementalDebugServer.zig")
     else
         void;
test/behavior/switch.zig
@@ -1056,3 +1056,12 @@ test "unlabeled break ignores switch" {
     };
     try expect(result == 123);
 }
+
+test "switch on a signed value smaller than the smallest prong value" {
+    var v: i32 = undefined;
+    v = -1;
+    switch (v) {
+        inline 0...10 => return error.TestFailed,
+        else => {},
+    }
+}
test/standalone/stack_iterator/build.zig
@@ -52,6 +52,8 @@ pub fn build(b: *std.Build) void {
                 .unwind_tables = .@"async",
                 .omit_frame_pointer = true,
             }),
+            // self-hosted lacks omit_frame_pointer support
+            .use_llvm = true,
         });
 
         const run_cmd = b.addRunArtifact(exe);
@@ -97,6 +99,8 @@ pub fn build(b: *std.Build) void {
                 .unwind_tables = if (target.result.os.tag.isDarwin()) .@"async" else null,
                 .omit_frame_pointer = true,
             }),
+            // zig objcopy doesn't support incremental binaries
+            .use_llvm = true,
         });
 
         exe.linkLibrary(c_shared_lib);
@@ -137,6 +141,8 @@ pub fn build(b: *std.Build) void {
                 .unwind_tables = null,
                 .omit_frame_pointer = false,
             }),
+            // self-hosted lacks omit_frame_pointer support
+            .use_llvm = true,
         });
 
         // This "freestanding" binary is runnable because it invokes the