From 37f763560b020ae0ef5ed076afc1872a57266b45 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 5 Jun 2025 22:25:13 -0400 Subject: [PATCH] x86_64: fix switch dispatch bug Also closes #23902 --- src/Compilation.zig | 2 +- src/IncrementalDebugServer.zig | 2 +- src/arch/x86_64/CodeGen.zig | 28 ++++++++++++------------ src/main.zig | 4 ++-- test/behavior/switch.zig | 9 ++++++++ test/standalone/stack_iterator/build.zig | 6 +++++ 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 13c1e50d9e..81d150b03f 100644 --- a/src/Compilation.zig +++ b/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; } diff --git a/src/IncrementalDebugServer.zig b/src/IncrementalDebugServer.zig index 69ebc3752e..531b71b4e8 100644 --- a/src/IncrementalDebugServer.zig +++ b/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, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 396c0f7318..b38492d500 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/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, diff --git a/src/main.zig b/src/main.zig index ade23312b7..20ccf4b7ec 100644 --- a/src/main.zig +++ b/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; diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index b518766d97..4ab6362a49 100644 --- a/test/behavior/switch.zig +++ b/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 => {}, + } +} diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index b76cb6cecd..486eb96439 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/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