authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-06-05 22:25:13-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-06 23:42:15-07:00
log37f763560b020ae0ef5ed076afc1872a57266b45
tree5fd862f59483a95a04f30fe137e668cf78b37764
parentf28ef7ee29ee1e8a1df092f5c4fff0497718415c

x86_64: fix switch dispatch bug

Also closes #23902

6 files changed, 33 insertions(+), 18 deletions(-)

src/Compilation.zig+1-1
...@@ -774,7 +774,7 @@ pub const Directories = struct {...@@ -774,7 +774,7 @@ pub const Directories = struct {
774/// `comp.debug_incremental`. It is inline so that comptime-known `false` propagates to the caller,774/// `comp.debug_incremental`. It is inline so that comptime-known `false` propagates to the caller,
775/// preventing debugging features from making it into release builds of the compiler.775/// preventing debugging features from making it into release builds of the compiler.
776pub inline fn debugIncremental(comp: *const Compilation) bool {776pub inline fn debugIncremental(comp: *const Compilation) bool {
777 if (!build_options.enable_debug_extensions) return false;777 if (!build_options.enable_debug_extensions or builtin.single_threaded) return false;
778 return comp.debug_incremental;778 return comp.debug_incremental;
779}779}
780780
src/IncrementalDebugServer.zig+1-1
...@@ -10,7 +10,7 @@...@@ -10,7 +10,7 @@
1010
11comptime {11comptime {
12 // This file should only be referenced when debug extensions are enabled.12 // This file should only be referenced when debug extensions are enabled.
13 std.debug.assert(@import("build_options").enable_debug_extensions);13 std.debug.assert(@import("build_options").enable_debug_extensions and !@import("builtin").single_threaded);
14}14}
1515
16zcu: *Zcu,16zcu: *Zcu,
src/arch/x86_64/CodeGen.zig+14-14
...@@ -161505,10 +161505,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -161505,10 +161505,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
161505 for (elems, 0..) |elem_ref, field_index| {161505 for (elems, 0..) |elem_ref, field_index| {
161506 const elem_dies = bt.feed();161506 const elem_dies = bt.feed();
161507 if (loaded_struct.fieldIsComptime(ip, field_index)) continue;161507 if (loaded_struct.fieldIsComptime(ip, field_index)) continue;
161508 var elem = try cg.tempFromOperand(elem_ref, elem_dies);161508 if (!hack_around_sema_opv_bugs or Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]).hasRuntimeBitsIgnoreComptime(zcu)) {
161509 try res.write(&elem, .{ .disp = @intCast(loaded_struct.offsets.get(ip)[field_index]) }, cg);161509 var elem = try cg.tempFromOperand(elem_ref, elem_dies);
161510 try elem.die(cg);161510 try res.write(&elem, .{ .disp = @intCast(loaded_struct.offsets.get(ip)[field_index]) }, cg);
161511 try cg.resetTemps(reset_index);161511 try elem.die(cg);
161512 try cg.resetTemps(reset_index);
161513 }
161512 }161514 }
161513 },161515 },
161514 .@"packed" => return cg.fail("failed to select {s} {}", .{161516 .@"packed" => return cg.fail("failed to select {s} {}", .{
...@@ -175015,8 +175017,7 @@ fn lowerSwitchBr(...@@ -175015,8 +175017,7 @@ fn lowerSwitchBr(
175015) !void {175017) !void {
175016 const zcu = cg.pt.zcu;175018 const zcu = cg.pt.zcu;
175017 const condition_ty = cg.typeOf(switch_br.operand);175019 const condition_ty = cg.typeOf(switch_br.operand);
175018 const condition_int_info = cg.intInfo(condition_ty).?;175020 const unsigned_condition_ty = try cg.pt.intType(.unsigned, cg.intInfo(condition_ty).?.bits);
175019 const condition_int_ty = try cg.pt.intType(condition_int_info.signedness, condition_int_info.bits);
175020175021
175021 const ExpectedContents = extern struct {175022 const ExpectedContents = extern struct {
175022 liveness_deaths: [1 << 8 | 1]Air.Inst.Index,175023 liveness_deaths: [1 << 8 | 1]Air.Inst.Index,
...@@ -175087,8 +175088,8 @@ fn lowerSwitchBr(...@@ -175087,8 +175088,8 @@ fn lowerSwitchBr(
175087 .{ .air_ref = Air.internedToRef(min.?.toIntern()) },175088 .{ .air_ref = Air.internedToRef(min.?.toIntern()) },
175088 );175089 );
175089 const else_reloc = if (switch_br.else_body_len > 0) else_reloc: {175090 const else_reloc = if (switch_br.else_body_len > 0) else_reloc: {
175090 var cond_temp = try cg.tempInit(condition_ty, condition_index);175091 var cond_temp = try cg.tempInit(unsigned_condition_ty, condition_index);
175091 var table_max_temp = try cg.tempFromValue(try cg.pt.intValue(condition_int_ty, table_len - 1));175092 var table_max_temp = try cg.tempFromValue(try cg.pt.intValue(unsigned_condition_ty, table_len - 1));
175092 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, cg) catch |err| switch (err) {175093 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, cg) catch |err| switch (err) {
175093 error.SelectFailed => unreachable,175094 error.SelectFailed => unreachable,
175094 else => |e| return e,175095 else => |e| return e,
...@@ -175416,8 +175417,7 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -175416,8 +175417,7 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
175416175417
175417 if (self.loop_switches.getPtr(br.block_inst)) |table| {175418 if (self.loop_switches.getPtr(br.block_inst)) |table| {
175418 const condition_ty = self.typeOf(br.operand);175419 const condition_ty = self.typeOf(br.operand);
175419 const condition_int_info = self.intInfo(condition_ty).?;175420 const unsigned_condition_ty = try self.pt.intType(.unsigned, self.intInfo(condition_ty).?.bits);
175420 const condition_int_ty = try self.pt.intType(condition_int_info.signedness, condition_int_info.bits);
175421 const condition_mcv = block_tracking.short;175421 const condition_mcv = block_tracking.short;
175422 try self.spillEflagsIfOccupied();175422 try self.spillEflagsIfOccupied();
175423 if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir(175423 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 {...@@ -175429,8 +175429,8 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
175429 switch (table.else_relocs) {175429 switch (table.else_relocs) {
175430 .@"unreachable" => {},175430 .@"unreachable" => {},
175431 .forward => |*else_relocs| {175431 .forward => |*else_relocs| {
175432 var cond_temp = try self.tempInit(condition_ty, condition_mcv);175432 var cond_temp = try self.tempInit(unsigned_condition_ty, condition_mcv);
175433 var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table.len - 1));175433 var table_max_temp = try self.tempFromValue(try self.pt.intValue(unsigned_condition_ty, table.len - 1));
175434 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) {175434 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) {
175435 error.SelectFailed => unreachable,175435 error.SelectFailed => unreachable,
175436 else => |e| return e,175436 else => |e| return e,
...@@ -175441,8 +175441,8 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -175441,8 +175441,8 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void {
175441 try cc_temp.die(self);175441 try cc_temp.die(self);
175442 },175442 },
175443 .backward => |else_reloc| {175443 .backward => |else_reloc| {
175444 var cond_temp = try self.tempInit(condition_ty, condition_mcv);175444 var cond_temp = try self.tempInit(unsigned_condition_ty, condition_mcv);
175445 var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table.len - 1));175445 var table_max_temp = try self.tempFromValue(try self.pt.intValue(unsigned_condition_ty, table.len - 1));
175446 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) {175446 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) {
175447 error.SelectFailed => unreachable,175447 error.SelectFailed => unreachable,
175448 else => |e| return e,175448 else => |e| return e,
src/main.zig+2-2
...@@ -39,7 +39,7 @@ test {...@@ -39,7 +39,7 @@ test {
39 _ = Package;39 _ = Package;
40}40}
4141
42const thread_stack_size = 50 << 20;42const thread_stack_size = 60 << 20;
4343
44pub const std_options: std.Options = .{44pub const std_options: std.Options = .{
45 .wasiCwd = wasi_cwd,45 .wasiCwd = wasi_cwd,
...@@ -4208,7 +4208,7 @@ fn serve(...@@ -4208,7 +4208,7 @@ fn serve(
4208 const main_progress_node = std.Progress.start(.{});4208 const main_progress_node = std.Progress.start(.{});
4209 const file_system_inputs = comp.file_system_inputs.?;4209 const file_system_inputs = comp.file_system_inputs.?;
42104210
4211 const IncrementalDebugServer = if (build_options.enable_debug_extensions)4211 const IncrementalDebugServer = if (build_options.enable_debug_extensions and !builtin.single_threaded)
4212 @import("IncrementalDebugServer.zig")4212 @import("IncrementalDebugServer.zig")
4213 else4213 else
4214 void;4214 void;
test/behavior/switch.zig+9
...@@ -1056,3 +1056,12 @@ test "unlabeled break ignores switch" {...@@ -1056,3 +1056,12 @@ test "unlabeled break ignores switch" {
1056 };1056 };
1057 try expect(result == 123);1057 try expect(result == 123);
1058}1058}
1059
1060test "switch on a signed value smaller than the smallest prong value" {
1061 var v: i32 = undefined;
1062 v = -1;
1063 switch (v) {
1064 inline 0...10 => return error.TestFailed,
1065 else => {},
1066 }
1067}
test/standalone/stack_iterator/build.zig+6
...@@ -52,6 +52,8 @@ pub fn build(b: *std.Build) void {...@@ -52,6 +52,8 @@ pub fn build(b: *std.Build) void {
52 .unwind_tables = .@"async",52 .unwind_tables = .@"async",
53 .omit_frame_pointer = true,53 .omit_frame_pointer = true,
54 }),54 }),
55 // self-hosted lacks omit_frame_pointer support
56 .use_llvm = true,
55 });57 });
5658
57 const run_cmd = b.addRunArtifact(exe);59 const run_cmd = b.addRunArtifact(exe);
...@@ -97,6 +99,8 @@ pub fn build(b: *std.Build) void {...@@ -97,6 +99,8 @@ pub fn build(b: *std.Build) void {
97 .unwind_tables = if (target.result.os.tag.isDarwin()) .@"async" else null,99 .unwind_tables = if (target.result.os.tag.isDarwin()) .@"async" else null,
98 .omit_frame_pointer = true,100 .omit_frame_pointer = true,
99 }),101 }),
102 // zig objcopy doesn't support incremental binaries
103 .use_llvm = true,
100 });104 });
101105
102 exe.linkLibrary(c_shared_lib);106 exe.linkLibrary(c_shared_lib);
...@@ -137,6 +141,8 @@ pub fn build(b: *std.Build) void {...@@ -137,6 +141,8 @@ pub fn build(b: *std.Build) void {
137 .unwind_tables = null,141 .unwind_tables = null,
138 .omit_frame_pointer = false,142 .omit_frame_pointer = false,
139 }),143 }),
144 // self-hosted lacks omit_frame_pointer support
145 .use_llvm = true,
140 });146 });
141147
142 // This "freestanding" binary is runnable because it invokes the148 // This "freestanding" binary is runnable because it invokes the