authorgravatar for 4678790+dweiller@users.noreply.github.comDominic <4678790+dweiller@users.noreply.github.com> 2023-12-03 22:07:45+11:00
committergravatar for 4678790+dweiller@users.noreply.github.comDominic <4678790+dweiller@users.noreply.github.com> 2024-01-09 14:42:12+11:00
logb7eb59fc140f3263b608a80fbe4e1ab56e04b318
treebb68023b3273d2c1343fe79fedf1d9b87a8ed943
parentadcaad6d91c309fab20ea208231c3e7d33fcc21f

fix x86_64 crashes for switch_block_err_union

This change only emits the unwrap_errunion_err instruction if the error capture is actually used in a branch.

4 files changed, 99 insertions(+), 31 deletions(-)

src/AstGen.zig+66-30
...@@ -7017,8 +7017,7 @@ fn switchExprErrUnion(...@@ -7017,8 +7017,7 @@ fn switchExprErrUnion(
7017 const case_slice = case_scope.instructionsSlice();7017 const case_slice = case_scope.instructionsSlice();
7018 // Since we use the switch_block_err_union instruction itself to refer7018 // Since we use the switch_block_err_union instruction itself to refer
7019 // to the capture, which will not be added to the child block, we need7019 // to the capture, which will not be added to the child block, we need
7020 // to handle ref_table manually, and the same for the inline tag7020 // to handle ref_table manually.
7021 // capture instruction.
7022 const refs_len = refs: {7021 const refs_len = refs: {
7023 var n: usize = 0;7022 var n: usize = 0;
7024 var check_inst = switch_block;7023 var check_inst = switch_block;
...@@ -7054,9 +7053,24 @@ fn switchExprErrUnion(...@@ -7054,9 +7053,24 @@ fn switchExprErrUnion(
7054 break :blk .{ err_name, error_payload };7053 break :blk .{ err_name, error_payload };
7055 };7054 };
70567055
7056 // allocate a shared dummy instruction for the error capture
7057 const err_inst = err_inst: {
7058 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
7059 try astgen.instructions.append(astgen.gpa, .{
7060 .tag = .extended,
7061 .data = .{ .extended = .{
7062 .opcode = .value_placeholder,
7063 .small = undefined,
7064 .operand = undefined,
7065 } },
7066 });
7067 break :err_inst inst;
7068 };
7069
7057 // In this pass we generate all the item and prong expressions for error cases.7070 // In this pass we generate all the item and prong expressions for error cases.
7058 var multi_case_index: u32 = 0;7071 var multi_case_index: u32 = 0;
7059 var scalar_case_index: u32 = 0;7072 var scalar_case_index: u32 = 0;
7073 var any_uses_err_capture = false;
7060 for (case_nodes) |case_node| {7074 for (case_nodes) |case_node| {
7061 const case = tree.fullSwitchCase(case_node).?;7075 const case = tree.fullSwitchCase(case_node).?;
70627076
...@@ -7066,31 +7080,42 @@ fn switchExprErrUnion(...@@ -7066,31 +7080,42 @@ fn switchExprErrUnion(
7066 var dbg_var_name: ?u32 = null;7080 var dbg_var_name: ?u32 = null;
7067 var dbg_var_inst: Zir.Inst.Ref = undefined;7081 var dbg_var_inst: Zir.Inst.Ref = undefined;
7068 var err_scope: Scope.LocalVal = undefined;7082 var err_scope: Scope.LocalVal = undefined;
7069 var tag_scope: Scope.LocalVal = undefined;7083 var capture_scope: Scope.LocalVal = undefined;
70707084
7071 const sub_scope = blk: {7085 const sub_scope = blk: {
7072 const tag_token = case.payload_token orelse break :blk &case_scope.base;7086 err_scope = .{
7073 assert(token_tags[tag_token] == .identifier);7087 .parent = &case_scope.base,
7088 .gen_zir = &case_scope,
7089 .name = err_name,
7090 .inst = err_inst.toRef(),
7091 .token_src = error_payload,
7092 .id_cat = .capture,
7093 };
70747094
7075 const tag_slice = tree.tokenSlice(tag_token);7095 const capture_token = case.payload_token orelse break :blk &err_scope.base;
7076 if (mem.eql(u8, tag_slice, "_")) {7096 assert(token_tags[capture_token] == .identifier);
7077 return astgen.failTok(tag_token, "discard of error capture; omit it instead", .{});7097
7098 const capture_slice = tree.tokenSlice(capture_token);
7099 if (mem.eql(u8, capture_slice, "_")) {
7100 return astgen.failTok(capture_token, "discard of error capture; omit it instead", .{});
7078 }7101 }
7079 const tag_name = try astgen.identAsString(tag_token);7102 const tag_name = try astgen.identAsString(capture_token);
7080 try astgen.detectLocalShadowing(&case_scope.base, tag_name, tag_token, tag_slice, .capture);7103 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
70817104
7082 tag_scope = .{7105 capture_scope = .{
7083 .parent = &case_scope.base,7106 .parent = &case_scope.base,
7084 .gen_zir = &case_scope,7107 .gen_zir = &case_scope,
7085 .name = tag_name,7108 .name = tag_name,
7086 .inst = switch_block.toRef(),7109 .inst = switch_block.toRef(),
7087 .token_src = tag_token,7110 .token_src = capture_token,
7088 .id_cat = .capture,7111 .id_cat = .capture,
7089 };7112 };
7090 dbg_var_name = tag_name;7113 dbg_var_name = tag_name;
7091 dbg_var_inst = switch_block.toRef();7114 dbg_var_inst = switch_block.toRef();
70927115
7093 break :blk &tag_scope.base;7116 err_scope.parent = &capture_scope.base;
7117
7118 break :blk &err_scope.base;
7094 };7119 };
70957120
7096 const header_index: u32 = @intCast(payloads.items.len);7121 const header_index: u32 = @intCast(payloads.items.len);
...@@ -7144,34 +7169,28 @@ fn switchExprErrUnion(...@@ -7144,34 +7169,28 @@ fn switchExprErrUnion(
7144 case_scope.instructions_top = parent_gz.instructions.items.len;7169 case_scope.instructions_top = parent_gz.instructions.items.len;
7145 defer case_scope.unstack();7170 defer case_scope.unstack();
71467171
7147 const err_code_inst = try case_scope.addUnNode(.err_union_code, raw_operand, operand_node);
7148 err_scope = .{
7149 .parent = sub_scope,
7150 .gen_zir = &case_scope,
7151 .name = err_name,
7152 .inst = err_code_inst,
7153 .token_src = error_payload,
7154 .id_cat = .capture,
7155 };
7156
7157 try case_scope.addDbgBlockBegin();7172 try case_scope.addDbgBlockBegin();
7158 if (dbg_var_name) |some| {7173 if (dbg_var_name) |some| {
7159 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);7174 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
7160 }7175 }
7161 const target_expr_node = case.ast.target_expr;7176 const target_expr_node = case.ast.target_expr;
7162 const case_result = try expr(&case_scope, &err_scope.base, block_scope.break_result_info, target_expr_node);7177 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
7163 // check sub_scope, not err_scope to avoid false positive unused error capture7178 // check capture_scope, not err_scope to avoid false positive unused error capture
7164 try checkUsed(parent_gz, &case_scope.base, sub_scope);7179 try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
7180 const uses_err = err_scope.used != 0 or err_scope.discarded != 0;
7181 if (uses_err) {
7182 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef());
7183 any_uses_err_capture = true;
7184 }
7165 try case_scope.addDbgBlockEnd();7185 try case_scope.addDbgBlockEnd();
7166 if (!parent_gz.refIsNoReturn(case_result)) {7186 if (!parent_gz.refIsNoReturn(case_result)) {
7167 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);7187 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
7168 }7188 }
71697189
7170 const case_slice = case_scope.instructionsSlice();7190 const case_slice = case_scope.instructionsSlice();
7171 // Since we use the switch_block instruction itself to refer to the7191 // Since we use the switch_block_err_union instruction itself to refer
7172 // capture, which will not be added to the child block, we need to7192 // to the capture, which will not be added to the child block, we need
7173 // handle ref_table manually, and the same for the inline tag7193 // to handle ref_table manually.
7174 // capture instruction.
7175 const refs_len = refs: {7194 const refs_len = refs: {
7176 var n: usize = 0;7195 var n: usize = 0;
7177 var check_inst = switch_block;7196 var check_inst = switch_block;
...@@ -7179,6 +7198,13 @@ fn switchExprErrUnion(...@@ -7179,6 +7198,13 @@ fn switchExprErrUnion(
7179 n += 1;7198 n += 1;
7180 check_inst = ref_inst;7199 check_inst = ref_inst;
7181 }7200 }
7201 if (uses_err) {
7202 check_inst = err_inst;
7203 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7204 n += 1;
7205 check_inst = ref_inst;
7206 }
7207 }
7182 break :refs n;7208 break :refs n;
7183 };7209 };
7184 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);7210 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
...@@ -7192,6 +7218,11 @@ fn switchExprErrUnion(...@@ -7192,6 +7218,11 @@ fn switchExprErrUnion(
7192 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {7218 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7193 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);7219 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7194 }7220 }
7221 if (uses_err) {
7222 if (astgen.ref_table.fetchRemove(err_inst)) |kv| {
7223 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7224 }
7225 }
7195 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);7226 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7196 }7227 }
7197 }7228 }
...@@ -7209,6 +7240,7 @@ fn switchExprErrUnion(...@@ -7209,6 +7240,7 @@ fn switchExprErrUnion(
7209 .has_multi_cases = multi_cases_len != 0,7240 .has_multi_cases = multi_cases_len != 0,
7210 .has_else = has_else,7241 .has_else = has_else,
7211 .scalar_cases_len = @intCast(scalar_cases_len),7242 .scalar_cases_len = @intCast(scalar_cases_len),
7243 .any_uses_err_capture = any_uses_err_capture,
7212 },7244 },
7213 });7245 });
72147246
...@@ -7216,6 +7248,10 @@ fn switchExprErrUnion(...@@ -7216,6 +7248,10 @@ fn switchExprErrUnion(
7216 astgen.extra.appendAssumeCapacity(multi_cases_len);7248 astgen.extra.appendAssumeCapacity(multi_cases_len);
7217 }7249 }
72187250
7251 if (any_uses_err_capture) {
7252 astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst));
7253 }
7254
7219 const zir_datas = astgen.instructions.items(.data);7255 const zir_datas = astgen.instructions.items(.data);
7220 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;7256 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
72217257
src/Sema.zig+20
...@@ -11189,6 +11189,16 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11189,6 +11189,16 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11189 break :blk multi_cases_len;11189 break :blk multi_cases_len;
11190 } else 0;11190 } else 0;
1119111191
11192 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
11193 const err_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
11194 header_extra_index += 1;
11195 // SwitchProngAnalysis wants inst_map to have space for the tag capture.
11196 // Note that the normal capture is referred to via the switch block
11197 // index, which there is already necessarily space for.
11198 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{err_capture_inst});
11199 break :blk err_capture_inst;
11200 } else undefined;
11201
11192 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);11202 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11193 defer case_vals.deinit(gpa);11203 defer case_vals.deinit(gpa);
1119411204
...@@ -11314,6 +11324,12 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11314,6 +11324,12 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11314 },11324 },
11315 }));11325 }));
11316 spa.operand = try sema.analyzeErrUnionCode(block, operand_src, raw_operand_val);11326 spa.operand = try sema.analyzeErrUnionCode(block, operand_src, raw_operand_val);
11327
11328 if (extra.data.bits.any_uses_err_capture) {
11329 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand);
11330 }
11331 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
11332
11317 return resolveSwitchComptime(11333 return resolveSwitchComptime(
11318 sema,11334 sema,
11319 spa,11335 spa,
...@@ -11364,6 +11380,10 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11364,6 +11380,10 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11364 defer gpa.free(true_instructions);11380 defer gpa.free(true_instructions);
1136511381
11366 spa.operand = try sema.analyzeErrUnionCode(&sub_block, operand_src, raw_operand_val);11382 spa.operand = try sema.analyzeErrUnionCode(&sub_block, operand_src, raw_operand_val);
11383 if (extra.data.bits.any_uses_err_capture) {
11384 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand);
11385 }
11386 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
11367 _ = try sema.analyzeSwitchRuntimeBlock(11387 _ = try sema.analyzeSwitchRuntimeBlock(
11368 spa,11388 spa,
11369 &sub_block,11389 &sub_block,
src/Zir.zig+2-1
...@@ -2792,9 +2792,10 @@ pub const Inst = struct {...@@ -2792,9 +2792,10 @@ pub const Inst = struct {
2792 has_multi_cases: bool,2792 has_multi_cases: bool,
2793 /// If true, there is an else prong. This is mutually exclusive with `has_under`.2793 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
2794 has_else: bool,2794 has_else: bool,
2795 any_uses_err_capture: bool,
2795 scalar_cases_len: ScalarCasesLen,2796 scalar_cases_len: ScalarCasesLen,
27962797
2797 pub const ScalarCasesLen = u30;2798 pub const ScalarCasesLen = u29;
2798 };2799 };
27992800
2800 pub const MultiProng = struct {2801 pub const MultiProng = struct {
src/print_zir.zig+11
...@@ -2040,8 +2040,19 @@ const Writer = struct {...@@ -2040,8 +2040,19 @@ const Writer = struct {
2040 break :blk multi_cases_len;2040 break :blk multi_cases_len;
2041 } else 0;2041 } else 0;
20422042
2043 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
2044 const tag_capture_inst = self.code.extra[extra_index];
2045 extra_index += 1;
2046 break :blk @enumFromInt(tag_capture_inst);
2047 } else undefined;
2048
2043 try self.writeInstRef(stream, extra.data.operand);2049 try self.writeInstRef(stream, extra.data.operand);
20442050
2051 if (extra.data.bits.any_uses_err_capture) {
2052 try stream.writeAll(", err_capture=");
2053 try self.writeInstIndex(stream, err_capture_inst);
2054 }
2055
2045 self.indent += 2;2056 self.indent += 2;
20462057
2047 {2058 {