authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-22 22:27:46+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-27 00:41:49+01:00
log457c94d353b77b08786aa8794e1afc6a62d5c34a
treeef1463f066a1ed7526aadcb1b6b4e488845b34f1
parent72e00805a61719174b1668d5ce3cbef8338e4690
signaturelock-open Commit is signed but in an unrecognized format.

compiler: implement `@branchHint`, replacing `@setCold`

Implements the accepted proposal to introduce `@branchHint`. This builtin is permitted as the first statement of a block if that block is the direct body of any of the following: * a function (*not* a `test`) * either branch of an `if` * the RHS of a `catch` or `orelse` * a `switch` prong * an `or` or `and` expression It lowers to the ZIR instruction `extended(branch_hint(...))`. When Sema encounters this instruction, it sets `sema.branch_hint` appropriately, and `zirCondBr` etc are expected to reset this value as necessary. The state is on `Sema` rather than `Block` to make it automatically propagate up non-conditional blocks without special handling. If `@panic` is reached, the branch hint is set to `.cold` if none was already set; similarly, error branches get a hint of `.unlikely` if no hint is explicitly provided. If a condition is comptime-known, `cold` hints from the taken branch are allowed to propagate up, but other hints are discarded. This is because a `likely`/`unlikely` hint just indicates the direction this branch is likely to go, which is redundant information when the branch is known at comptime; but `cold` hints indicate that control flow is unlikely to ever reach this branch, meaning if the branch is always taken from its parent, then the parent is also unlikely to ever be reached. This branch information is stored in AIR `cond_br` and `switch_br`. In addition, `try` and `try_ptr` instructions have variants `try_cold` and `try_ptr_cold` which indicate that the error case is cold (rather than just unlikely); this is reachable through e.g. `errdefer unreachable` or `errdefer @panic("")`. A new API `unwrapSwitch` is introduced to `Air` to make it more convenient to access `switch_br` instructions. In time, I plan to update all AIR instructions to be accessed via an `unwrap` method which returns a convenient tagged union a la `InternPool.indexToKey`. The LLVM backend lowers branch hints for conditional branches and switches as follows: * If any branch is marked `unpredictable`, the instruction is marked `!unpredictable`. * Any branch which is marked as `cold` gets a `llvm.assume(i1 true) [ "cold"() ]` call to mark the code path cold. * If any branch is marked `likely` or `unlikely`, branch weight metadata is attached with `!prof`. Likely branches get a weight of 2000, and unlikely branches a weight of 1. In `switch` statements, un-annotated branches get a weight of 1000 as a "middle ground" hint, since there could be likely *and* unlikely *and* un-annotated branches. For functions, a `cold` hint corresponds to the `cold` function attribute, and other hints are currently ignored -- as far as I can tell LLVM doesn't really have a way to lower them. (Ideally, we would want the branch hint given in the function to propagate to call sites.) The compiler and standard library do not yet use this new builtin. Resolves: #21148

25 files changed, 1127 insertions(+), 563 deletions(-)

lib/std/builtin.zig+19
......@@ -675,6 +675,25 @@ pub const ExternOptions = struct {
675675 is_thread_local: bool = false,
676676};
677677
678/// This data structure is used by the Zig language code generation and
679/// therefore must be kept in sync with the compiler implementation.
680pub const BranchHint = enum(u3) {
681 /// Equivalent to no hint given.
682 none,
683 /// This branch of control flow is more likely to be reached than its peers.
684 /// The optimizer should optimize for reaching it.
685 likely,
686 /// This branch of control flow is less likely to be reached than its peers.
687 /// The optimizer should optimize for not reaching it.
688 unlikely,
689 /// This branch of control flow is unlikely to *ever* be reached.
690 /// The optimizer may place it in a different page of memory to optimize other branches.
691 cold,
692 /// It is difficult to predict whether this branch of control flow will be reached.
693 /// The optimizer should avoid branching behavior with expensive mispredictions.
694 unpredictable,
695};
696
678697/// This enum is set by the compiler and communicates which compiler backend is
679698/// used to produce machine code.
680699/// Think carefully before deciding to observe this value. Nearly all code should
lib/std/zig/AstGen.zig+91-53
......@@ -811,18 +811,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
811811 .builtin_call_two, .builtin_call_two_comma => {
812812 if (node_datas[node].lhs == 0) {
813813 const params = [_]Ast.Node.Index{};
814 return builtinCall(gz, scope, ri, node, &params);
814 return builtinCall(gz, scope, ri, node, &params, false);
815815 } else if (node_datas[node].rhs == 0) {
816816 const params = [_]Ast.Node.Index{node_datas[node].lhs};
817 return builtinCall(gz, scope, ri, node, &params);
817 return builtinCall(gz, scope, ri, node, &params, false);
818818 } else {
819819 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
820 return builtinCall(gz, scope, ri, node, &params);
820 return builtinCall(gz, scope, ri, node, &params, false);
821821 }
822822 },
823823 .builtin_call, .builtin_call_comma => {
824824 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
825 return builtinCall(gz, scope, ri, node, params);
825 return builtinCall(gz, scope, ri, node, params, false);
826826 },
827827
828828 .call_one,
......@@ -1017,16 +1017,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
10171017 .block_two, .block_two_semicolon => {
10181018 const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
10191019 if (node_datas[node].lhs == 0) {
1020 return blockExpr(gz, scope, ri, node, statements[0..0]);
1020 return blockExpr(gz, scope, ri, node, statements[0..0], .normal);
10211021 } else if (node_datas[node].rhs == 0) {
1022 return blockExpr(gz, scope, ri, node, statements[0..1]);
1022 return blockExpr(gz, scope, ri, node, statements[0..1], .normal);
10231023 } else {
1024 return blockExpr(gz, scope, ri, node, statements[0..2]);
1024 return blockExpr(gz, scope, ri, node, statements[0..2], .normal);
10251025 }
10261026 },
10271027 .block, .block_semicolon => {
10281028 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
1029 return blockExpr(gz, scope, ri, node, statements);
1029 return blockExpr(gz, scope, ri, node, statements, .normal);
10301030 },
10311031 .enum_literal => return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
10321032 .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
......@@ -1241,7 +1241,7 @@ fn suspendExpr(
12411241 suspend_scope.suspend_node = node;
12421242 defer suspend_scope.unstack();
12431243
1244 const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node);
1244 const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node, .normal);
12451245 if (!gz.refIsNoReturn(body_result)) {
12461246 _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
12471247 }
......@@ -1362,7 +1362,7 @@ fn fnProtoExpr(
13621362 assert(param_type_node != 0);
13631363 var param_gz = block_scope.makeSubBlock(scope);
13641364 defer param_gz.unstack();
1365 const param_type = try fullBodyExpr(&param_gz, scope, coerced_type_ri, param_type_node);
1365 const param_type = try fullBodyExpr(&param_gz, scope, coerced_type_ri, param_type_node, .normal);
13661366 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
13671367 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
13681368 const main_tokens = tree.nodes.items(.main_token);
......@@ -2040,13 +2040,13 @@ fn comptimeExpr(
20402040 else
20412041 stmts[0..2];
20422042
2043 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true);
2043 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true, .normal);
20442044 return rvalue(gz, ri, block_ref, node);
20452045 },
20462046 .block, .block_semicolon => {
20472047 const stmts = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
20482048 // Replace result location and copy back later - see above.
2049 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true);
2049 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal);
20502050 return rvalue(gz, ri, block_ref, node);
20512051 },
20522052 else => unreachable,
......@@ -2071,7 +2071,7 @@ fn comptimeExpr(
20712071 else
20722072 .none,
20732073 };
2074 const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node);
2074 const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node, .normal);
20752075 if (!gz.refIsNoReturn(block_result)) {
20762076 _ = try block_scope.addBreak(.@"break", block_inst, block_result);
20772077 }
......@@ -2311,6 +2311,7 @@ fn fullBodyExpr(
23112311 scope: *Scope,
23122312 ri: ResultInfo,
23132313 node: Ast.Node.Index,
2314 block_kind: BlockKind,
23142315) InnerError!Zir.Inst.Ref {
23152316 const tree = gz.astgen.tree;
23162317 const node_tags = tree.nodes.items(.tag);
......@@ -2340,21 +2341,24 @@ fn fullBodyExpr(
23402341 // Labeled blocks are tricky - forwarding result location information properly is non-trivial,
23412342 // plus if this block is exited with a `break_inline` we aren't allowed multiple breaks. This
23422343 // case is rare, so just treat it as a normal expression and create a nested block.
2343 return expr(gz, scope, ri, node);
2344 return blockExpr(gz, scope, ri, node, statements, block_kind);
23442345 }
23452346
23462347 var sub_gz = gz.makeSubBlock(scope);
2347 try blockExprStmts(&sub_gz, &sub_gz.base, statements);
2348 try blockExprStmts(&sub_gz, &sub_gz.base, statements, block_kind);
23482349
23492350 return rvalue(gz, ri, .void_value, node);
23502351}
23512352
2353const BlockKind = enum { normal, allow_branch_hint };
2354
23522355fn blockExpr(
23532356 gz: *GenZir,
23542357 scope: *Scope,
23552358 ri: ResultInfo,
23562359 block_node: Ast.Node.Index,
23572360 statements: []const Ast.Node.Index,
2361 kind: BlockKind,
23582362) InnerError!Zir.Inst.Ref {
23592363 const astgen = gz.astgen;
23602364 const tree = astgen.tree;
......@@ -2365,7 +2369,7 @@ fn blockExpr(
23652369 if (token_tags[lbrace - 1] == .colon and
23662370 token_tags[lbrace - 2] == .identifier)
23672371 {
2368 return labeledBlockExpr(gz, scope, ri, block_node, statements, false);
2372 return labeledBlockExpr(gz, scope, ri, block_node, statements, false, kind);
23692373 }
23702374
23712375 if (!gz.is_comptime) {
......@@ -2380,7 +2384,7 @@ fn blockExpr(
23802384 var block_scope = gz.makeSubBlock(scope);
23812385 defer block_scope.unstack();
23822386
2383 try blockExprStmts(&block_scope, &block_scope.base, statements);
2387 try blockExprStmts(&block_scope, &block_scope.base, statements, kind);
23842388
23852389 if (!block_scope.endsWithNoReturn()) {
23862390 // As our last action before the break, "pop" the error trace if needed
......@@ -2391,7 +2395,7 @@ fn blockExpr(
23912395 try block_scope.setBlockBody(block_inst);
23922396 } else {
23932397 var sub_gz = gz.makeSubBlock(scope);
2394 try blockExprStmts(&sub_gz, &sub_gz.base, statements);
2398 try blockExprStmts(&sub_gz, &sub_gz.base, statements, kind);
23952399 }
23962400
23972401 return rvalue(gz, ri, .void_value, block_node);
......@@ -2436,6 +2440,7 @@ fn labeledBlockExpr(
24362440 block_node: Ast.Node.Index,
24372441 statements: []const Ast.Node.Index,
24382442 force_comptime: bool,
2443 block_kind: BlockKind,
24392444) InnerError!Zir.Inst.Ref {
24402445 const astgen = gz.astgen;
24412446 const tree = astgen.tree;
......@@ -2476,7 +2481,7 @@ fn labeledBlockExpr(
24762481 if (force_comptime) block_scope.is_comptime = true;
24772482 defer block_scope.unstack();
24782483
2479 try blockExprStmts(&block_scope, &block_scope.base, statements);
2484 try blockExprStmts(&block_scope, &block_scope.base, statements, block_kind);
24802485 if (!block_scope.endsWithNoReturn()) {
24812486 // As our last action before the return, "pop" the error trace if needed
24822487 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
......@@ -2495,7 +2500,7 @@ fn labeledBlockExpr(
24952500 }
24962501}
24972502
2498fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index) !void {
2503fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index, block_kind: BlockKind) !void {
24992504 const astgen = gz.astgen;
25002505 const tree = astgen.tree;
25012506 const node_tags = tree.nodes.items(.tag);
......@@ -2509,7 +2514,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
25092514
25102515 var noreturn_src_node: Ast.Node.Index = 0;
25112516 var scope = parent_scope;
2512 for (statements) |statement| {
2517 for (statements, 0..) |statement, stmt_idx| {
25132518 if (noreturn_src_node != 0) {
25142519 try astgen.appendErrorNodeNotes(
25152520 statement,
......@@ -2524,6 +2529,10 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
25242529 },
25252530 );
25262531 }
2532 const allow_branch_hint = switch (block_kind) {
2533 .normal => false,
2534 .allow_branch_hint => stmt_idx == 0,
2535 };
25272536 var inner_node = statement;
25282537 while (true) {
25292538 switch (node_tags[inner_node]) {
......@@ -2567,6 +2576,30 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
25672576 .for_simple,
25682577 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
25692578
2579 // These cases are here to allow branch hints.
2580 .builtin_call_two, .builtin_call_two_comma => {
2581 try emitDbgNode(gz, inner_node);
2582 const ri: ResultInfo = .{ .rl = .none };
2583 const result = if (node_data[inner_node].lhs == 0) r: {
2584 break :r try builtinCall(gz, scope, ri, inner_node, &.{}, allow_branch_hint);
2585 } else if (node_data[inner_node].rhs == 0) r: {
2586 break :r try builtinCall(gz, scope, ri, inner_node, &.{node_data[inner_node].lhs}, allow_branch_hint);
2587 } else r: {
2588 break :r try builtinCall(gz, scope, ri, inner_node, &.{
2589 node_data[inner_node].lhs,
2590 node_data[inner_node].rhs,
2591 }, allow_branch_hint);
2592 };
2593 noreturn_src_node = try addEnsureResult(gz, result, inner_node);
2594 },
2595 .builtin_call, .builtin_call_comma => {
2596 try emitDbgNode(gz, inner_node);
2597 const ri: ResultInfo = .{ .rl = .none };
2598 const params = tree.extra_data[node_data[inner_node].lhs..node_data[inner_node].rhs];
2599 const result = try builtinCall(gz, scope, ri, inner_node, params, allow_branch_hint);
2600 noreturn_src_node = try addEnsureResult(gz, result, inner_node);
2601 },
2602
25702603 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
25712604 // zig fmt: on
25722605 }
......@@ -2827,7 +2860,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28272860 .fence,
28282861 .set_float_mode,
28292862 .set_align_stack,
2830 .set_cold,
2863 .branch_hint,
28312864 => break :b true,
28322865 else => break :b false,
28332866 },
......@@ -4154,7 +4187,7 @@ fn fnDecl(
41544187 assert(param_type_node != 0);
41554188 var param_gz = decl_gz.makeSubBlock(scope);
41564189 defer param_gz.unstack();
4157 const param_type = try fullBodyExpr(&param_gz, params_scope, coerced_type_ri, param_type_node);
4190 const param_type = try fullBodyExpr(&param_gz, params_scope, coerced_type_ri, param_type_node, .normal);
41584191 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
41594192 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
41604193
......@@ -4276,7 +4309,7 @@ fn fnDecl(
42764309 var ret_gz = decl_gz.makeSubBlock(params_scope);
42774310 defer ret_gz.unstack();
42784311 const ret_ref: Zir.Inst.Ref = inst: {
4279 const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type);
4312 const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type, .normal);
42804313 if (ret_gz.instructionsSlice().len == 0) {
42814314 // In this case we will send a len=0 body which can be encoded more efficiently.
42824315 break :inst inst;
......@@ -4351,7 +4384,7 @@ fn fnDecl(
43514384 const lbrace_line = astgen.source_line - decl_gz.decl_line;
43524385 const lbrace_column = astgen.source_column;
43534386
4354 _ = try fullBodyExpr(&fn_gz, params_scope, .{ .rl = .none }, body_node);
4387 _ = try fullBodyExpr(&fn_gz, params_scope, .{ .rl = .none }, body_node, .allow_branch_hint);
43554388 try checkUsed(gz, &fn_gz.base, params_scope);
43564389
43574390 if (!fn_gz.endsWithNoReturn()) {
......@@ -4552,20 +4585,20 @@ fn globalVarDecl(
45524585
45534586 var align_gz = block_scope.makeSubBlock(scope);
45544587 if (var_decl.ast.align_node != 0) {
4555 const align_inst = try fullBodyExpr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node);
4588 const align_inst = try fullBodyExpr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node, .normal);
45564589 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
45574590 }
45584591
45594592 var linksection_gz = align_gz.makeSubBlock(scope);
45604593 if (var_decl.ast.section_node != 0) {
4561 const linksection_inst = try fullBodyExpr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node);
4594 const linksection_inst = try fullBodyExpr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node, .normal);
45624595 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
45634596 }
45644597
45654598 var addrspace_gz = linksection_gz.makeSubBlock(scope);
45664599 if (var_decl.ast.addrspace_node != 0) {
45674600 const addrspace_ty = try addrspace_gz.addBuiltinValue(var_decl.ast.addrspace_node, .address_space);
4568 const addrspace_inst = try fullBodyExpr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, var_decl.ast.addrspace_node);
4601 const addrspace_inst = try fullBodyExpr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, var_decl.ast.addrspace_node, .normal);
45694602 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
45704603 }
45714604
......@@ -4622,7 +4655,7 @@ fn comptimeDecl(
46224655 };
46234656 defer decl_block.unstack();
46244657
4625 const block_result = try fullBodyExpr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node);
4658 const block_result = try fullBodyExpr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node, .normal);
46264659 if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
46274660 _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
46284661 }
......@@ -4843,7 +4876,7 @@ fn testDecl(
48434876 const lbrace_line = astgen.source_line - decl_block.decl_line;
48444877 const lbrace_column = astgen.source_column;
48454878
4846 const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node);
4879 const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node, .normal);
48474880 if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
48484881
48494882 // As our last action before the return, "pop" the error trace if needed
......@@ -6112,7 +6145,7 @@ fn orelseCatchExpr(
61126145 break :blk &err_val_scope.base;
61136146 };
61146147
6115 const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs);
6148 const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint);
61166149 if (!else_scope.endsWithNoReturn()) {
61176150 // As our last action before the break, "pop" the error trace if needed
61186151 if (do_err_trace)
......@@ -6280,7 +6313,7 @@ fn boolBinOp(
62806313
62816314 var rhs_scope = gz.makeSubBlock(scope);
62826315 defer rhs_scope.unstack();
6283 const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs);
6316 const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs, .allow_branch_hint);
62846317 if (!gz.refIsNoReturn(rhs)) {
62856318 _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, node_datas[node].rhs);
62866319 }
......@@ -6424,7 +6457,7 @@ fn ifExpr(
64246457 }
64256458 };
64266459
6427 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node);
6460 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint);
64286461 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
64296462 if (!then_scope.endsWithNoReturn()) {
64306463 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node);
......@@ -6466,7 +6499,7 @@ fn ifExpr(
64666499 break :s &else_scope.base;
64676500 }
64686501 };
6469 const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node);
6502 const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint);
64706503 if (!else_scope.endsWithNoReturn()) {
64716504 // As our last action before the break, "pop" the error trace if needed
64726505 if (do_err_trace)
......@@ -6575,7 +6608,7 @@ fn whileExpr(
65756608 } = c: {
65766609 if (while_full.error_token) |_| {
65776610 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
6578 const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
6611 const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal);
65796612 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
65806613 break :c .{
65816614 .inst = err_union,
......@@ -6583,14 +6616,14 @@ fn whileExpr(
65836616 };
65846617 } else if (while_full.payload_token) |_| {
65856618 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
6586 const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
6619 const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal);
65876620 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
65886621 break :c .{
65896622 .inst = optional,
65906623 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr),
65916624 };
65926625 } else {
6593 const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr);
6626 const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr, .normal);
65946627 break :c .{
65956628 .inst = cond,
65966629 .bool_bit = cond,
......@@ -6715,7 +6748,7 @@ fn whileExpr(
67156748 continue_scope.instructions_top = continue_scope.instructions.items.len;
67166749 {
67176750 try emitDbgNode(&continue_scope, then_node);
6718 const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node);
6751 const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node, .allow_branch_hint);
67196752 _ = try addEnsureResult(&continue_scope, unused_result, then_node);
67206753 }
67216754 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
......@@ -6761,7 +6794,7 @@ fn whileExpr(
67616794 // control flow apply to outer loops; not this one.
67626795 loop_scope.continue_block = .none;
67636796 loop_scope.break_block = .none;
6764 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
6797 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
67656798 if (is_statement) {
67666799 _ = try addEnsureResult(&else_scope, else_result, else_node);
67676800 }
......@@ -7029,7 +7062,7 @@ fn forExpr(
70297062 break :blk capture_sub_scope;
70307063 };
70317064
7032 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node);
7065 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node, .allow_branch_hint);
70337066 _ = try addEnsureResult(&then_scope, then_result, then_node);
70347067
70357068 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
......@@ -7048,7 +7081,7 @@ fn forExpr(
70487081 // control flow apply to outer loops; not this one.
70497082 loop_scope.continue_block = .none;
70507083 loop_scope.break_block = .none;
7051 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
7084 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
70527085 if (is_statement) {
70537086 _ = try addEnsureResult(&else_scope, else_result, else_node);
70547087 }
......@@ -7525,7 +7558,7 @@ fn switchExprErrUnion(
75257558 }
75267559
75277560 const target_expr_node = case.ast.target_expr;
7528 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
7561 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
75297562 // check capture_scope, not err_scope to avoid false positive unused error capture
75307563 try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
75317564 const uses_err = err_scope.used != 0 or err_scope.discarded != 0;
......@@ -7986,7 +8019,7 @@ fn switchExpr(
79868019 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
79878020 }
79888021 const target_expr_node = case.ast.target_expr;
7989 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
8022 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
79908023 try checkUsed(parent_gz, &case_scope.base, sub_scope);
79918024 if (!parent_gz.refIsNoReturn(case_result)) {
79928025 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
......@@ -9154,6 +9187,7 @@ fn builtinCall(
91549187 ri: ResultInfo,
91559188 node: Ast.Node.Index,
91569189 params: []const Ast.Node.Index,
9190 allow_branch_hint: bool,
91579191) InnerError!Zir.Inst.Ref {
91589192 const astgen = gz.astgen;
91599193 const tree = astgen.tree;
......@@ -9187,6 +9221,18 @@ fn builtinCall(
91879221 return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name});
91889222
91899223 switch (info.tag) {
9224 .branch_hint => {
9225 if (!allow_branch_hint) {
9226 return astgen.failNode(node, "'@branchHint' must appear as the first statement in a function or conditional branch", .{});
9227 }
9228 const hint_ty = try gz.addBuiltinValue(node, .branch_hint);
9229 const hint_val = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = hint_ty } }, params[0]);
9230 _ = try gz.addExtendedPayload(.branch_hint, Zir.Inst.UnNode{
9231 .node = gz.nodeIndexToRelative(node),
9232 .operand = hint_val,
9233 });
9234 return rvalue(gz, ri, .void_value, node);
9235 },
91909236 .import => {
91919237 const node_tags = tree.nodes.items(.tag);
91929238 const operand_node = params[0];
......@@ -9294,14 +9340,6 @@ fn builtinCall(
92949340 });
92959341 return rvalue(gz, ri, .void_value, node);
92969342 },
9297 .set_cold => {
9298 const order = try expr(gz, scope, ri, params[0]);
9299 _ = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{
9300 .node = gz.nodeIndexToRelative(node),
9301 .operand = order,
9302 });
9303 return rvalue(gz, ri, .void_value, node);
9304 },
93059343
93069344 .src => {
93079345 // Incorporate the source location into the source hash, so that
......@@ -9963,7 +10001,7 @@ fn cImport(
996310001 defer block_scope.unstack();
996410002
996510003 const block_inst = try gz.makeBlockInst(.c_import, node);
9966 const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node);
10004 const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node, .normal);
996710005 _ = try gz.addUnNode(.ensure_result_used, block_result, node);
996810006 if (!gz.refIsNoReturn(block_result)) {
996910007 _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
......@@ -10046,7 +10084,7 @@ fn callExpr(
1004610084 defer arg_block.unstack();
1004710085
1004810086 // `call_inst` is reused to provide the param type.
10049 const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
10087 const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node, .normal);
1005010088 _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node);
1005110089
1005210090 const body = arg_block.instructionsSlice();
lib/std/zig/AstRlAnnotate.zig+4-1
......@@ -829,6 +829,10 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
829829 }
830830 switch (info.tag) {
831831 .import => return false,
832 .branch_hint => {
833 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
834 return false;
835 },
832836 .compile_log, .TypeOf => {
833837 for (args) |arg_node| {
834838 _ = try astrl.expr(arg_node, block, ResultInfo.none);
......@@ -907,7 +911,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
907911 .fence,
908912 .set_float_mode,
909913 .set_align_stack,
910 .set_cold,
911914 .type_info,
912915 .work_item_id,
913916 .work_group_size,
lib/std/zig/BuiltinFn.zig+9-9
......@@ -14,6 +14,7 @@ pub const Tag = enum {
1414 bit_offset_of,
1515 int_from_bool,
1616 bit_size_of,
17 branch_hint,
1718 breakpoint,
1819 disable_instrumentation,
1920 mul_add,
......@@ -82,7 +83,6 @@ pub const Tag = enum {
8283 return_address,
8384 select,
8485 set_align_stack,
85 set_cold,
8686 set_eval_branch_quota,
8787 set_float_mode,
8888 set_runtime_safety,
......@@ -256,6 +256,14 @@ pub const list = list: {
256256 .param_count = 1,
257257 },
258258 },
259 .{
260 "@branchHint",
261 .{
262 .tag = .branch_hint,
263 .param_count = 1,
264 .illegal_outside_function = true,
265 },
266 },
259267 .{
260268 "@breakpoint",
261269 .{
......@@ -744,14 +752,6 @@ pub const list = list: {
744752 .illegal_outside_function = true,
745753 },
746754 },
747 .{
748 "@setCold",
749 .{
750 .tag = .set_cold,
751 .param_count = 1,
752 .illegal_outside_function = true,
753 },
754 },
755755 .{
756756 "@setEvalBranchQuota",
757757 .{
lib/std/zig/Zir.zig+7-5
......@@ -1546,7 +1546,7 @@ pub const Inst = struct {
15461546 => false,
15471547
15481548 .extended => switch (data.extended.opcode) {
1549 .fence, .set_cold, .breakpoint, .disable_instrumentation => true,
1549 .fence, .branch_hint, .breakpoint, .disable_instrumentation => true,
15501550 else => false,
15511551 },
15521552 };
......@@ -1954,9 +1954,6 @@ pub const Inst = struct {
19541954 /// Implement builtin `@setAlignStack`.
19551955 /// `operand` is payload index to `UnNode`.
19561956 set_align_stack,
1957 /// Implements `@setCold`.
1958 /// `operand` is payload index to `UnNode`.
1959 set_cold,
19601957 /// Implements the `@errorCast` builtin.
19611958 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
19621959 error_cast,
......@@ -2051,6 +2048,10 @@ pub const Inst = struct {
20512048 /// `operand` is `src_node: i32`.
20522049 /// `small` is an `Inst.BuiltinValue`.
20532050 builtin_value,
2051 /// Provide a `@branchHint` for the current block.
2052 /// `operand` is payload index to `UnNode`.
2053 /// `small` is unused.
2054 branch_hint,
20542055
20552056 pub const InstData = struct {
20562057 opcode: Extended,
......@@ -3142,6 +3143,7 @@ pub const Inst = struct {
31423143 export_options,
31433144 extern_options,
31443145 type_info,
3146 branch_hint,
31453147 // Values
31463148 calling_convention_c,
31473149 calling_convention_inline,
......@@ -3962,7 +3964,6 @@ fn findDeclsInner(
39623964 .fence,
39633965 .set_float_mode,
39643966 .set_align_stack,
3965 .set_cold,
39663967 .error_cast,
39673968 .await_nosuspend,
39683969 .breakpoint,
......@@ -3986,6 +3987,7 @@ fn findDeclsInner(
39863987 .closure_get,
39873988 .field_parent_ptr,
39883989 .builtin_value,
3990 .branch_hint,
39893991 => return,
39903992
39913993 // `@TypeOf` has a body.
src/Air.zig+109-6
......@@ -433,13 +433,18 @@ pub const Inst = struct {
433433 /// In the case of non-error, control flow proceeds to the next instruction
434434 /// after the `try`, with the result of this instruction being the unwrapped
435435 /// payload value, as if `unwrap_errunion_payload` was executed on the operand.
436 /// The error branch is considered to have a branch hint of `.unlikely`.
436437 /// Uses the `pl_op` field. Payload is `Try`.
437438 @"try",
439 /// Same as `try` except the error branch hint is `.cold`.
440 try_cold,
438441 /// Same as `try` except the operand is a pointer to an error union, and the
439442 /// result is a pointer to the payload. Result is as if `unwrap_errunion_payload_ptr`
440443 /// was executed on the operand.
441444 /// Uses the `ty_pl` field. Payload is `TryPtr`.
442445 try_ptr,
446 /// Same as `try_ptr` except the error branch hint is `.cold`.
447 try_ptr_cold,
443448 /// Notes the beginning of a source code statement and marks the line and column.
444449 /// Result type is always void.
445450 /// Uses the `dbg_stmt` field.
......@@ -1116,11 +1121,20 @@ pub const Call = struct {
11161121pub const CondBr = struct {
11171122 then_body_len: u32,
11181123 else_body_len: u32,
1124 branch_hints: BranchHints,
1125 pub const BranchHints = packed struct(u32) {
1126 true: std.builtin.BranchHint,
1127 false: std.builtin.BranchHint,
1128 _: u26 = 0,
1129 };
11191130};
11201131
11211132/// Trailing:
1122/// * 0. `Case` for each `cases_len`
1123/// * 1. the else body, according to `else_body_len`.
1133/// * 0. `BranchHint` for each `cases_len + 1`. bit-packed into `u32`
1134/// elems such that each `u32` contains up to 10x `BranchHint`.
1135/// LSBs are first case. Final hint is `else`.
1136/// * 1. `Case` for each `cases_len`
1137/// * 2. the else body, according to `else_body_len`.
11241138pub const SwitchBr = struct {
11251139 cases_len: u32,
11261140 else_body_len: u32,
......@@ -1380,6 +1394,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
13801394 .ptr_add,
13811395 .ptr_sub,
13821396 .try_ptr,
1397 .try_ptr_cold,
13831398 => return datas[@intFromEnum(inst)].ty_pl.ty.toType(),
13841399
13851400 .not,
......@@ -1500,7 +1515,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
15001515 return air.typeOf(extra.lhs, ip);
15011516 },
15021517
1503 .@"try" => {
1518 .@"try", .try_cold => {
15041519 const err_union_ty = air.typeOf(datas[@intFromEnum(inst)].pl_op.operand, ip);
15051520 return Type.fromInterned(ip.indexToKey(err_union_ty.ip_index).error_union_type.payload_type);
15061521 },
......@@ -1524,9 +1539,8 @@ pub fn extraData(air: Air, comptime T: type, index: usize) struct { data: T, end
15241539 inline for (fields) |field| {
15251540 @field(result, field.name) = switch (field.type) {
15261541 u32 => air.extra[i],
1527 Inst.Ref => @as(Inst.Ref, @enumFromInt(air.extra[i])),
1528 i32 => @as(i32, @bitCast(air.extra[i])),
1529 InternPool.Index => @as(InternPool.Index, @enumFromInt(air.extra[i])),
1542 InternPool.Index, Inst.Ref => @enumFromInt(air.extra[i]),
1543 i32, CondBr.BranchHints => @bitCast(air.extra[i]),
15301544 else => @compileError("bad field type: " ++ @typeName(field.type)),
15311545 };
15321546 i += 1;
......@@ -1593,7 +1607,9 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
15931607 .cond_br,
15941608 .switch_br,
15951609 .@"try",
1610 .try_cold,
15961611 .try_ptr,
1612 .try_ptr_cold,
15971613 .dbg_stmt,
15981614 .dbg_inline_block,
15991615 .dbg_var_ptr,
......@@ -1796,4 +1812,91 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
17961812 };
17971813}
17981814
1815pub const UnwrappedSwitch = struct {
1816 air: *const Air,
1817 operand: Inst.Ref,
1818 cases_len: u32,
1819 else_body_len: u32,
1820 branch_hints_start: u32,
1821 cases_start: u32,
1822
1823 /// Asserts that `case_idx < us.cases_len`.
1824 pub fn getHint(us: UnwrappedSwitch, case_idx: u32) std.builtin.BranchHint {
1825 assert(case_idx < us.cases_len);
1826 return us.getHintInner(case_idx);
1827 }
1828 pub fn getElseHint(us: UnwrappedSwitch) std.builtin.BranchHint {
1829 return us.getHintInner(us.cases_len);
1830 }
1831 fn getHintInner(us: UnwrappedSwitch, idx: u32) std.builtin.BranchHint {
1832 const bag = us.air.extra[us.branch_hints_start..][idx / 10];
1833 const bits: u3 = @truncate(bag >> @intCast(3 * (idx % 10)));
1834 return @enumFromInt(bits);
1835 }
1836
1837 pub fn iterateCases(us: UnwrappedSwitch) CaseIterator {
1838 return .{
1839 .air = us.air,
1840 .cases_len = us.cases_len,
1841 .else_body_len = us.else_body_len,
1842 .next_case = 0,
1843 .extra_index = us.cases_start,
1844 };
1845 }
1846 pub const CaseIterator = struct {
1847 air: *const Air,
1848 cases_len: u32,
1849 else_body_len: u32,
1850 next_case: u32,
1851 extra_index: u32,
1852
1853 pub fn next(it: *CaseIterator) ?Case {
1854 if (it.next_case == it.cases_len) return null;
1855 const idx = it.next_case;
1856 it.next_case += 1;
1857
1858 const extra = it.air.extraData(SwitchBr.Case, it.extra_index);
1859 var extra_index = extra.end;
1860 const items: []const Inst.Ref = @ptrCast(it.air.extra[extra_index..][0..extra.data.items_len]);
1861 extra_index += items.len;
1862 const body: []const Inst.Index = @ptrCast(it.air.extra[extra_index..][0..extra.data.body_len]);
1863 extra_index += body.len;
1864 it.extra_index = @intCast(extra_index);
1865
1866 return .{
1867 .idx = idx,
1868 .items = items,
1869 .body = body,
1870 };
1871 }
1872 /// Only valid to call once all cases have been iterated, i.e. `next` returns `null`.
1873 /// Returns the body of the "default" (`else`) case.
1874 pub fn elseBody(it: *CaseIterator) []const Inst.Index {
1875 assert(it.next_case == it.cases_len);
1876 return @ptrCast(it.air.extra[it.extra_index..][0..it.else_body_len]);
1877 }
1878 pub const Case = struct {
1879 idx: u32,
1880 items: []const Inst.Ref,
1881 body: []const Inst.Index,
1882 };
1883 };
1884};
1885
1886pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {
1887 const inst = air.instructions.get(@intFromEnum(switch_inst));
1888 assert(inst.tag == .switch_br);
1889 const pl_op = inst.data.pl_op;
1890 const extra = air.extraData(SwitchBr, pl_op.payload);
1891 const hint_bag_count = std.math.divCeil(usize, extra.data.cases_len + 1, 10) catch unreachable;
1892 return .{
1893 .air = air,
1894 .operand = pl_op.operand,
1895 .cases_len = extra.data.cases_len,
1896 .else_body_len = extra.data.else_body_len,
1897 .branch_hints_start = @intCast(extra.end),
1898 .cases_start = @intCast(extra.end + hint_bag_count),
1899 };
1900}
1901
17991902pub const typesFullyResolved = @import("Air/types_resolved.zig").typesFullyResolved;
src/Air/types_resolved.zig+9-22
......@@ -344,7 +344,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
344344 if (!checkRef(data.pl_op.operand, zcu)) return false;
345345 },
346346
347 .@"try" => {
347 .@"try", .try_cold => {
348348 const extra = air.extraData(Air.Try, data.pl_op.payload);
349349 if (!checkRef(data.pl_op.operand, zcu)) return false;
350350 if (!checkBody(
......@@ -354,7 +354,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
354354 )) return false;
355355 },
356356
357 .try_ptr => {
357 .try_ptr, .try_ptr_cold => {
358358 const extra = air.extraData(Air.TryPtr, data.ty_pl.payload);
359359 if (!checkType(data.ty_pl.ty.toType(), zcu)) return false;
360360 if (!checkRef(extra.data.ptr, zcu)) return false;
......@@ -381,27 +381,14 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
381381 },
382382
383383 .switch_br => {
384 const extra = air.extraData(Air.SwitchBr, data.pl_op.payload);
385 if (!checkRef(data.pl_op.operand, zcu)) return false;
386 var extra_index = extra.end;
387 for (0..extra.data.cases_len) |_| {
388 const case = air.extraData(Air.SwitchBr.Case, extra_index);
389 extra_index = case.end;
390 const items: []const Air.Inst.Ref = @ptrCast(air.extra[extra_index..][0..case.data.items_len]);
391 extra_index += case.data.items_len;
392 for (items) |item| if (!checkRef(item, zcu)) return false;
393 if (!checkBody(
394 air,
395 @ptrCast(air.extra[extra_index..][0..case.data.body_len]),
396 zcu,
397 )) return false;
398 extra_index += case.data.body_len;
384 const switch_br = air.unwrapSwitch(inst);
385 if (!checkRef(switch_br.operand, zcu)) return false;
386 var it = switch_br.iterateCases();
387 while (it.next()) |case| {
388 for (case.items) |item| if (!checkRef(item, zcu)) return false;
389 if (!checkBody(air, case.body, zcu)) return false;
399390 }
400 if (!checkBody(
401 air,
402 @ptrCast(air.extra[extra_index..][0..extra.data.else_body_len]),
403 zcu,
404 )) return false;
391 if (!checkBody(air, it.elseBody(), zcu)) return false;
405392 },
406393
407394 .assembly => {
src/InternPool.zig+17-18
......@@ -2121,6 +2121,17 @@ pub const Key = union(enum) {
21212121 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
21222122 }
21232123
2124 pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void {
2125 const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
2126 extra_mutex.lock();
2127 defer extra_mutex.unlock();
2128
2129 const analysis_ptr = func.analysisPtr(ip);
2130 var analysis = analysis_ptr.*;
2131 analysis.branch_hint = hint;
2132 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
2133 }
2134
21242135 /// Returns a pointer that becomes invalid after any additions to the `InternPool`.
21252136 fn zirBodyInstPtr(func: Func, ip: *InternPool) *TrackedInst.Index {
21262137 const extra = ip.getLocalShared(func.tid).extra.acquire();
......@@ -5575,7 +5586,7 @@ pub const Tag = enum(u8) {
55755586/// to be part of the type of the function.
55765587pub const FuncAnalysis = packed struct(u32) {
55775588 state: State,
5578 is_cold: bool,
5589 branch_hint: std.builtin.BranchHint,
55795590 is_noinline: bool,
55805591 calls_or_awaits_errorable_fn: bool,
55815592 stack_alignment: Alignment,
......@@ -5583,7 +5594,7 @@ pub const FuncAnalysis = packed struct(u32) {
55835594 inferred_error_set: bool,
55845595 disable_instrumentation: bool,
55855596
5586 _: u19 = 0,
5597 _: u17 = 0,
55875598
55885599 pub const State = enum(u2) {
55895600 /// The runtime function has never been referenced.
......@@ -8636,7 +8647,7 @@ pub fn getFuncDecl(
86368647 const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
86378648 .analysis = .{
86388649 .state = .unreferenced,
8639 .is_cold = false,
8650 .branch_hint = .none,
86408651 .is_noinline = key.is_noinline,
86418652 .calls_or_awaits_errorable_fn = false,
86428653 .stack_alignment = .none,
......@@ -8740,7 +8751,7 @@ pub fn getFuncDeclIes(
87408751 const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
87418752 .analysis = .{
87428753 .state = .unreferenced,
8743 .is_cold = false,
8754 .branch_hint = .none,
87448755 .is_noinline = key.is_noinline,
87458756 .calls_or_awaits_errorable_fn = false,
87468757 .stack_alignment = .none,
......@@ -8932,7 +8943,7 @@ pub fn getFuncInstance(
89328943 const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
89338944 .analysis = .{
89348945 .state = .unreferenced,
8935 .is_cold = false,
8946 .branch_hint = .none,
89368947 .is_noinline = arg.is_noinline,
89378948 .calls_or_awaits_errorable_fn = false,
89388949 .stack_alignment = .none,
......@@ -9032,7 +9043,7 @@ pub fn getFuncInstanceIes(
90329043 const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
90339044 .analysis = .{
90349045 .state = .unreferenced,
9035 .is_cold = false,
9046 .branch_hint = .none,
90369047 .is_noinline = arg.is_noinline,
90379048 .calls_or_awaits_errorable_fn = false,
90389049 .stack_alignment = .none,
......@@ -11853,18 +11864,6 @@ pub fn funcSetDisableInstrumentation(ip: *InternPool, func: Index) void {
1185311864 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
1185411865}
1185511866
11856pub fn funcSetCold(ip: *InternPool, func: Index, is_cold: bool) void {
11857 const unwrapped_func = func.unwrap(ip);
11858 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
11859 extra_mutex.lock();
11860 defer extra_mutex.unlock();
11861
11862 const analysis_ptr = ip.funcAnalysisPtr(func);
11863 var analysis = analysis_ptr.*;
11864 analysis.is_cold = is_cold;
11865 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
11866}
11867
1186811867pub fn funcZirBodyInst(ip: *const InternPool, func: Index) TrackedInst.Index {
1186911868 const unwrapped_func = func.unwrap(ip);
1187011869 const item = unwrapped_func.getItem(ip);
src/Liveness.zig+15-21
......@@ -658,10 +658,10 @@ pub fn categorizeOperand(
658658
659659 return .complex;
660660 },
661 .@"try" => {
661 .@"try", .try_cold => {
662662 return .complex;
663663 },
664 .try_ptr => {
664 .try_ptr, .try_ptr_cold => {
665665 return .complex;
666666 },
667667 .loop => {
......@@ -1254,8 +1254,8 @@ fn analyzeInst(
12541254 },
12551255 .loop => return analyzeInstLoop(a, pass, data, inst),
12561256
1257 .@"try" => return analyzeInstCondBr(a, pass, data, inst, .@"try"),
1258 .try_ptr => return analyzeInstCondBr(a, pass, data, inst, .try_ptr),
1257 .@"try", .try_cold => return analyzeInstCondBr(a, pass, data, inst, .@"try"),
1258 .try_ptr, .try_ptr_cold => return analyzeInstCondBr(a, pass, data, inst, .try_ptr),
12591259 .cond_br => return analyzeInstCondBr(a, pass, data, inst, .cond_br),
12601260 .switch_br => return analyzeInstSwitchBr(a, pass, data, inst),
12611261
......@@ -1674,21 +1674,18 @@ fn analyzeInstSwitchBr(
16741674 const inst_datas = a.air.instructions.items(.data);
16751675 const pl_op = inst_datas[@intFromEnum(inst)].pl_op;
16761676 const condition = pl_op.operand;
1677 const switch_br = a.air.extraData(Air.SwitchBr, pl_op.payload);
1677 const switch_br = a.air.unwrapSwitch(inst);
16781678 const gpa = a.gpa;
1679 const ncases = switch_br.data.cases_len;
1679 const ncases = switch_br.cases_len;
16801680
16811681 switch (pass) {
16821682 .loop_analysis => {
1683 var air_extra_index: usize = switch_br.end;
1684 for (0..ncases) |_| {
1685 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);
1686 const case_body: []const Air.Inst.Index = @ptrCast(a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len]);
1687 air_extra_index = case.end + case.data.items_len + case_body.len;
1688 try analyzeBody(a, pass, data, case_body);
1683 var it = switch_br.iterateCases();
1684 while (it.next()) |case| {
1685 try analyzeBody(a, pass, data, case.body);
16891686 }
16901687 { // else
1691 const else_body: []const Air.Inst.Index = @ptrCast(a.air.extra[air_extra_index..][0..switch_br.data.else_body_len]);
1688 const else_body = it.elseBody();
16921689 try analyzeBody(a, pass, data, else_body);
16931690 }
16941691 },
......@@ -1706,16 +1703,13 @@ fn analyzeInstSwitchBr(
17061703 @memset(case_live_sets, .{});
17071704 defer for (case_live_sets) |*live_set| live_set.deinit(gpa);
17081705
1709 var air_extra_index: usize = switch_br.end;
1710 for (case_live_sets[0..ncases]) |*live_set| {
1711 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);
1712 const case_body: []const Air.Inst.Index = @ptrCast(a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len]);
1713 air_extra_index = case.end + case.data.items_len + case_body.len;
1714 try analyzeBody(a, pass, data, case_body);
1715 live_set.* = data.live_set.move();
1706 var case_it = switch_br.iterateCases();
1707 while (case_it.next()) |case| {
1708 try analyzeBody(a, pass, data, case.body);
1709 case_live_sets[case.idx] = data.live_set.move();
17161710 }
17171711 { // else
1718 const else_body: []const Air.Inst.Index = @ptrCast(a.air.extra[air_extra_index..][0..switch_br.data.else_body_len]);
1712 const else_body = case_it.elseBody();
17191713 try analyzeBody(a, pass, data, else_body);
17201714 case_live_sets[ncases] = data.live_set.move();
17211715 }
src/Liveness/Verify.zig+11-22
......@@ -374,7 +374,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
374374 },
375375
376376 // control flow
377 .@"try" => {
377 .@"try", .try_cold => {
378378 const pl_op = data[@intFromEnum(inst)].pl_op;
379379 const extra = self.air.extraData(Air.Try, pl_op.payload);
380380 const try_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
......@@ -396,7 +396,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
396396
397397 try self.verifyInst(inst);
398398 },
399 .try_ptr => {
399 .try_ptr, .try_ptr_cold => {
400400 const ty_pl = data[@intFromEnum(inst)].ty_pl;
401401 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
402402 const try_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
......@@ -509,44 +509,33 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
509509 try self.verifyInst(inst);
510510 },
511511 .switch_br => {
512 const pl_op = data[@intFromEnum(inst)].pl_op;
513 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
514 var extra_index = switch_br.end;
515 var case_i: u32 = 0;
512 const switch_br = self.air.unwrapSwitch(inst);
516513 const switch_br_liveness = try self.liveness.getSwitchBr(
517514 self.gpa,
518515 inst,
519 switch_br.data.cases_len + 1,
516 switch_br.cases_len + 1,
520517 );
521518 defer self.gpa.free(switch_br_liveness.deaths);
522519
523 try self.verifyOperand(inst, pl_op.operand, self.liveness.operandDies(inst, 0));
520 try self.verifyOperand(inst, switch_br.operand, self.liveness.operandDies(inst, 0));
524521
525522 var live = self.live.move();
526523 defer live.deinit(self.gpa);
527524
528 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
529 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
530 const items = @as(
531 []const Air.Inst.Ref,
532 @ptrCast(self.air.extra[case.end..][0..case.data.items_len]),
533 );
534 const case_body: []const Air.Inst.Index = @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]);
535 extra_index = case.end + items.len + case_body.len;
536
525 var it = switch_br.iterateCases();
526 while (it.next()) |case| {
537527 self.live.deinit(self.gpa);
538528 self.live = try live.clone(self.gpa);
539529
540 for (switch_br_liveness.deaths[case_i]) |death| try self.verifyDeath(inst, death);
541 try self.verifyBody(case_body);
530 for (switch_br_liveness.deaths[case.idx]) |death| try self.verifyDeath(inst, death);
531 try self.verifyBody(case.body);
542532 }
543533
544 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);
534 const else_body = it.elseBody();
545535 if (else_body.len > 0) {
546536 self.live.deinit(self.gpa);
547537 self.live = try live.clone(self.gpa);
548
549 for (switch_br_liveness.deaths[case_i]) |death| try self.verifyDeath(inst, death);
538 for (switch_br_liveness.deaths[switch_br.cases_len]) |death| try self.verifyDeath(inst, death);
550539 try self.verifyBody(else_body);
551540 }
552541
src/Sema.zig+258-107
......@@ -118,6 +118,10 @@ dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .{},
118118/// by `analyzeCall`.
119119allow_memoize: bool = true,
120120
121/// The `BranchHint` for the current branch of runtime control flow.
122/// This state is on `Sema` so that `cold` hints can be propagated up through blocks with less special handling.
123branch_hint: ?std.builtin.BranchHint = null,
124
121125const MaybeComptimeAlloc = struct {
122126 /// The runtime index of the `alloc` instruction.
123127 runtime_index: Value.RuntimeIndex,
......@@ -892,7 +896,12 @@ pub fn deinit(sema: *Sema) void {
892896/// Performs semantic analysis of a ZIR body which is behind a runtime condition. If comptime
893897/// control flow happens here, Sema will convert it to runtime control flow by introducing post-hoc
894898/// blocks where necessary.
895fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void {
899/// Returns the branch hint for this branch.
900fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !std.builtin.BranchHint {
901 const parent_hint = sema.branch_hint;
902 defer sema.branch_hint = parent_hint;
903 sema.branch_hint = null;
904
896905 sema.analyzeBodyInner(block, body) catch |err| switch (err) {
897906 error.ComptimeBreak => {
898907 const zir_datas = sema.code.instructions.items(.data);
......@@ -902,6 +911,8 @@ fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.In
902911 },
903912 else => |e| return e,
904913 };
914
915 return sema.branch_hint orelse .none;
905916}
906917
907918/// Semantically analyze a ZIR function body. It is guranteed by AstGen that such a body cannot
......@@ -1304,11 +1315,6 @@ fn analyzeBodyInner(
13041315 i += 1;
13051316 continue;
13061317 },
1307 .set_cold => {
1308 try sema.zirSetCold(block, extended);
1309 i += 1;
1310 continue;
1311 },
13121318 .breakpoint => {
13131319 if (!block.is_comptime) {
13141320 _ = try block.addNoOp(.breakpoint);
......@@ -1326,6 +1332,11 @@ fn analyzeBodyInner(
13261332 i += 1;
13271333 continue;
13281334 },
1335 .branch_hint => {
1336 try sema.zirBranchHint(block, extended);
1337 i += 1;
1338 continue;
1339 },
13291340 .value_placeholder => unreachable, // never appears in a body
13301341 .field_parent_ptr => try sema.zirFieldParentPtr(block, extended),
13311342 .builtin_value => try sema.zirBuiltinValue(extended),
......@@ -5727,6 +5738,13 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
57275738 if (block.is_comptime) {
57285739 return sema.fail(block, src, "encountered @panic at comptime", .{});
57295740 }
5741
5742 // We only apply the first hint in a branch.
5743 // This allows user-provided hints to override implicit cold hints.
5744 if (sema.branch_hint == null) {
5745 sema.branch_hint = .cold;
5746 }
5747
57305748 try sema.panicWithMsg(block, src, coerced_msg, .@"@panic");
57315749}
57325750
......@@ -6418,25 +6436,6 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
64186436 sema.allow_memoize = false;
64196437}
64206438
6421fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
6422 const pt = sema.pt;
6423 const zcu = pt.zcu;
6424 const ip = &zcu.intern_pool;
6425 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
6426 const operand_src = block.builtinCallArgSrc(extra.node, 0);
6427 const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, .{
6428 .needed_comptime_reason = "operand to @setCold must be comptime-known",
6429 });
6430 // TODO: should `@setCold` apply to the parent in an inline call?
6431 // See also #20642 and friends.
6432 const func = switch (sema.owner.unwrap()) {
6433 .func => |func| func,
6434 .cau => return, // does nothing outside a function
6435 };
6436 ip.funcSetCold(func, is_cold);
6437 sema.allow_memoize = false;
6438}
6439
64406439fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
64416440 const pt = sema.pt;
64426441 const zcu = pt.zcu;
......@@ -6891,13 +6890,20 @@ fn popErrorReturnTrace(
68916890 @typeInfo(Air.Block).Struct.fields.len + 1); // +1 for the sole .cond_br instruction in the .block
68926891
68936892 const cond_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
6894 try sema.air_instructions.append(gpa, .{ .tag = .cond_br, .data = .{ .pl_op = .{
6895 .operand = is_non_error_inst,
6896 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
6897 .then_body_len = @intCast(then_block.instructions.items.len),
6898 .else_body_len = @intCast(else_block.instructions.items.len),
6899 }),
6900 } } });
6893 try sema.air_instructions.append(gpa, .{
6894 .tag = .cond_br,
6895 .data = .{
6896 .pl_op = .{
6897 .operand = is_non_error_inst,
6898 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
6899 .then_body_len = @intCast(then_block.instructions.items.len),
6900 .else_body_len = @intCast(else_block.instructions.items.len),
6901 // weight against error branch
6902 .branch_hints = .{ .true = .likely, .false = .unlikely },
6903 }),
6904 },
6905 },
6906 });
69016907 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
69026908 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
69036909
......@@ -10954,6 +10960,11 @@ const SwitchProngAnalysis = struct {
1095410960 sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src_node,
1095510961 );
1095610962
10963 // We can propagate `.cold` hints from this branch since it's comptime-known
10964 // to be taken from the parent branch.
10965 const parent_hint = sema.branch_hint;
10966 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
10967
1095710968 if (has_tag_capture) {
1095810969 const tag_ref = try spa.analyzeTagCapture(child_block, capture_src, inline_case_capture);
1095910970 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
......@@ -10990,6 +11001,7 @@ const SwitchProngAnalysis = struct {
1099011001
1099111002 /// Analyze a switch prong which may have peers at runtime.
1099211003 /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed.
11004 /// Returns the `BranchHint` for the prong.
1099311005 fn analyzeProngRuntime(
1099411006 spa: SwitchProngAnalysis,
1099511007 case_block: *Block,
......@@ -11007,7 +11019,7 @@ const SwitchProngAnalysis = struct {
1100711019 /// Whether this prong has an inline tag capture. If `true`, then
1100811020 /// `inline_case_capture` cannot be `.none`.
1100911021 has_tag_capture: bool,
11010 ) CompileError!void {
11022 ) CompileError!std.builtin.BranchHint {
1101111023 const sema = spa.sema;
1101211024
1101311025 if (has_tag_capture) {
......@@ -11033,7 +11045,7 @@ const SwitchProngAnalysis = struct {
1103311045
1103411046 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
1103511047 // No need to analyze any further, the prong is unreachable
11036 return;
11048 return .none;
1103711049 }
1103811050
1103911051 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
......@@ -11302,10 +11314,17 @@ const SwitchProngAnalysis = struct {
1130211314
1130311315 const prong_count = field_indices.len - in_mem_coercible.count();
1130411316
11305 const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts
11317 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints
1130611318 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra);
1130711319 defer cases_extra.deinit();
1130811320
11321 {
11322 // All branch hints are `.none`, so just add zero elems.
11323 comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0);
11324 const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable;
11325 try cases_extra.appendNTimes(0, need_elems);
11326 }
11327
1130911328 {
1131011329 // Non-bitcast cases
1131111330 var it = in_mem_coercible.iterator(.{ .kind = .unset });
......@@ -11728,7 +11747,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
1172811747 sub_block.need_debug_scope = null; // this body is emitted regardless
1172911748 defer sub_block.instructions.deinit(gpa);
1173011749
11731 try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body);
11750 const non_error_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body);
1173211751 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
1173311752 defer gpa.free(true_instructions);
1173411753
......@@ -11782,6 +11801,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
1178211801 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
1178311802 .then_body_len = @intCast(true_instructions.len),
1178411803 .else_body_len = @intCast(sub_block.instructions.items.len),
11804 .branch_hints = .{ .true = non_error_hint, .false = .none },
1178511805 }),
1178611806 } },
1178711807 });
......@@ -12486,6 +12506,9 @@ fn analyzeSwitchRuntimeBlock(
1248612506 var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra);
1248712507 defer cases_extra.deinit(gpa);
1248812508
12509 var branch_hints = try std.ArrayListUnmanaged(std.builtin.BranchHint).initCapacity(gpa, scalar_cases_len);
12510 defer branch_hints.deinit(gpa);
12511
1248912512 var case_block = child_block.makeSubBlock();
1249012513 case_block.runtime_loop = null;
1249112514 case_block.runtime_cond = operand_src;
......@@ -12516,10 +12539,13 @@ fn analyzeSwitchRuntimeBlock(
1251612539 break :blk field_ty.zigTypeTag(zcu) != .NoReturn;
1251712540 } else true;
1251812541
12519 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) {
12520 // nothing to do here
12521 } else if (analyze_body) {
12522 try spa.analyzeProngRuntime(
12542 const prong_hint: std.builtin.BranchHint = if (err_set and
12543 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12544 h: {
12545 // nothing to do here. weight against error branch
12546 break :h .unlikely;
12547 } else if (analyze_body) h: {
12548 break :h try spa.analyzeProngRuntime(
1252312549 &case_block,
1252412550 .normal,
1252512551 body,
......@@ -12532,10 +12558,12 @@ fn analyzeSwitchRuntimeBlock(
1253212558 if (info.is_inline) item else .none,
1253312559 info.has_tag_capture,
1253412560 );
12535 } else {
12561 } else h: {
1253612562 _ = try case_block.addNoOp(.unreach);
12537 }
12563 break :h .none;
12564 };
1253812565
12566 try branch_hints.append(gpa, prong_hint);
1253912567 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
1254012568 cases_extra.appendAssumeCapacity(1); // items_len
1254112569 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
......@@ -12545,6 +12573,7 @@ fn analyzeSwitchRuntimeBlock(
1254512573
1254612574 var is_first = true;
1254712575 var prev_cond_br: Air.Inst.Index = undefined;
12576 var prev_hint: std.builtin.BranchHint = undefined;
1254812577 var first_else_body: []const Air.Inst.Index = &.{};
1254912578 defer gpa.free(first_else_body);
1255012579 var prev_then_body: []const Air.Inst.Index = &.{};
......@@ -12606,7 +12635,7 @@ fn analyzeSwitchRuntimeBlock(
1260612635 } }));
1260712636 emit_bb = true;
1260812637
12609 try spa.analyzeProngRuntime(
12638 const prong_hint = try spa.analyzeProngRuntime(
1261012639 &case_block,
1261112640 .normal,
1261212641 body,
......@@ -12619,6 +12648,7 @@ fn analyzeSwitchRuntimeBlock(
1261912648 item_ref,
1262012649 info.has_tag_capture,
1262112650 );
12651 try branch_hints.append(gpa, prong_hint);
1262212652
1262312653 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
1262412654 cases_extra.appendAssumeCapacity(1); // items_len
......@@ -12649,8 +12679,8 @@ fn analyzeSwitchRuntimeBlock(
1264912679 } }));
1265012680 emit_bb = true;
1265112681
12652 if (analyze_body) {
12653 try spa.analyzeProngRuntime(
12682 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
12683 break :h try spa.analyzeProngRuntime(
1265412684 &case_block,
1265512685 .normal,
1265612686 body,
......@@ -12663,9 +12693,11 @@ fn analyzeSwitchRuntimeBlock(
1266312693 item,
1266412694 info.has_tag_capture,
1266512695 );
12666 } else {
12696 } else h: {
1266712697 _ = try case_block.addNoOp(.unreach);
12668 }
12698 break :h .none;
12699 };
12700 try branch_hints.append(gpa, prong_hint);
1266912701
1267012702 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
1267112703 cases_extra.appendAssumeCapacity(1); // items_len
......@@ -12697,10 +12729,13 @@ fn analyzeSwitchRuntimeBlock(
1269712729
1269812730 const body = sema.code.bodySlice(extra_index, info.body_len);
1269912731 extra_index += info.body_len;
12700 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) {
12701 // nothing to do here
12702 } else if (analyze_body) {
12703 try spa.analyzeProngRuntime(
12732 const prong_hint: std.builtin.BranchHint = if (err_set and
12733 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12734 h: {
12735 // nothing to do here. weight against error branch
12736 break :h .unlikely;
12737 } else if (analyze_body) h: {
12738 break :h try spa.analyzeProngRuntime(
1270412739 &case_block,
1270512740 .normal,
1270612741 body,
......@@ -12713,10 +12748,12 @@ fn analyzeSwitchRuntimeBlock(
1271312748 .none,
1271412749 false,
1271512750 );
12716 } else {
12751 } else h: {
1271712752 _ = try case_block.addNoOp(.unreach);
12718 }
12753 break :h .none;
12754 };
1271912755
12756 try branch_hints.append(gpa, prong_hint);
1272012757 try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len +
1272112758 case_block.instructions.items.len);
1272212759
......@@ -12784,23 +12821,24 @@ fn analyzeSwitchRuntimeBlock(
1278412821
1278512822 const body = sema.code.bodySlice(extra_index, info.body_len);
1278612823 extra_index += info.body_len;
12787 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) {
12788 // nothing to do here
12789 } else {
12790 try spa.analyzeProngRuntime(
12791 &case_block,
12792 .normal,
12793 body,
12794 info.capture,
12795 child_block.src(.{ .switch_capture = .{
12796 .switch_node_offset = switch_node_offset,
12797 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12798 } }),
12799 items,
12800 .none,
12801 false,
12802 );
12803 }
12824 const prong_hint: std.builtin.BranchHint = if (err_set and
12825 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12826 h: {
12827 // nothing to do here. weight against error branch
12828 break :h .unlikely;
12829 } else try spa.analyzeProngRuntime(
12830 &case_block,
12831 .normal,
12832 body,
12833 info.capture,
12834 child_block.src(.{ .switch_capture = .{
12835 .switch_node_offset = switch_node_offset,
12836 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12837 } }),
12838 items,
12839 .none,
12840 false,
12841 );
1280412842
1280512843 if (is_first) {
1280612844 is_first = false;
......@@ -12812,10 +12850,10 @@ fn analyzeSwitchRuntimeBlock(
1281212850 @typeInfo(Air.CondBr).Struct.fields.len + prev_then_body.len + cond_body.len,
1281312851 );
1281412852
12815 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload =
12816 sema.addExtraAssumeCapacity(Air.CondBr{
12853 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{
1281712854 .then_body_len = @intCast(prev_then_body.len),
1281812855 .else_body_len = @intCast(cond_body.len),
12856 .branch_hints = .{ .true = prev_hint, .false = .none },
1281912857 });
1282012858 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));
1282112859 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cond_body));
......@@ -12823,6 +12861,7 @@ fn analyzeSwitchRuntimeBlock(
1282312861 gpa.free(prev_then_body);
1282412862 prev_then_body = try case_block.instructions.toOwnedSlice(gpa);
1282512863 prev_cond_br = new_cond_br;
12864 prev_hint = prong_hint;
1282612865 }
1282712866 }
1282812867
......@@ -12854,8 +12893,8 @@ fn analyzeSwitchRuntimeBlock(
1285412893 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
1285512894 emit_bb = true;
1285612895
12857 if (analyze_body) {
12858 try spa.analyzeProngRuntime(
12896 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
12897 break :h try spa.analyzeProngRuntime(
1285912898 &case_block,
1286012899 .special,
1286112900 special.body,
......@@ -12868,9 +12907,11 @@ fn analyzeSwitchRuntimeBlock(
1286812907 item_ref,
1286912908 special.has_tag_capture,
1287012909 );
12871 } else {
12910 } else h: {
1287212911 _ = try case_block.addNoOp(.unreach);
12873 }
12912 break :h .none;
12913 };
12914 try branch_hints.append(gpa, prong_hint);
1287412915
1287512916 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
1287612917 cases_extra.appendAssumeCapacity(1); // items_len
......@@ -12903,7 +12944,7 @@ fn analyzeSwitchRuntimeBlock(
1290312944 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
1290412945 emit_bb = true;
1290512946
12906 try spa.analyzeProngRuntime(
12947 const prong_hint = try spa.analyzeProngRuntime(
1290712948 &case_block,
1290812949 .special,
1290912950 special.body,
......@@ -12916,6 +12957,7 @@ fn analyzeSwitchRuntimeBlock(
1291612957 item_ref,
1291712958 special.has_tag_capture,
1291812959 );
12960 try branch_hints.append(gpa, prong_hint);
1291912961
1292012962 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
1292112963 cases_extra.appendAssumeCapacity(1); // items_len
......@@ -12937,7 +12979,7 @@ fn analyzeSwitchRuntimeBlock(
1293712979 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
1293812980 emit_bb = true;
1293912981
12940 try spa.analyzeProngRuntime(
12982 const prong_hint = try spa.analyzeProngRuntime(
1294112983 &case_block,
1294212984 .special,
1294312985 special.body,
......@@ -12950,6 +12992,7 @@ fn analyzeSwitchRuntimeBlock(
1295012992 item_ref,
1295112993 special.has_tag_capture,
1295212994 );
12995 try branch_hints.append(gpa, prong_hint);
1295312996
1295412997 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
1295512998 cases_extra.appendAssumeCapacity(1); // items_len
......@@ -12968,7 +13011,7 @@ fn analyzeSwitchRuntimeBlock(
1296813011 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
1296913012 emit_bb = true;
1297013013
12971 try spa.analyzeProngRuntime(
13014 const prong_hint = try spa.analyzeProngRuntime(
1297213015 &case_block,
1297313016 .special,
1297413017 special.body,
......@@ -12981,6 +13024,7 @@ fn analyzeSwitchRuntimeBlock(
1298113024 .bool_true,
1298213025 special.has_tag_capture,
1298313026 );
13027 try branch_hints.append(gpa, prong_hint);
1298413028
1298513029 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
1298613030 cases_extra.appendAssumeCapacity(1); // items_len
......@@ -12997,7 +13041,7 @@ fn analyzeSwitchRuntimeBlock(
1299713041 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
1299813042 emit_bb = true;
1299913043
13000 try spa.analyzeProngRuntime(
13044 const prong_hint = try spa.analyzeProngRuntime(
1300113045 &case_block,
1300213046 .special,
1300313047 special.body,
......@@ -13010,6 +13054,7 @@ fn analyzeSwitchRuntimeBlock(
1301013054 .bool_false,
1301113055 special.has_tag_capture,
1301213056 );
13057 try branch_hints.append(gpa, prong_hint);
1301313058
1301413059 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
1301513060 cases_extra.appendAssumeCapacity(1); // items_len
......@@ -13045,12 +13090,13 @@ fn analyzeSwitchRuntimeBlock(
1304513090 } else false
1304613091 else
1304713092 true;
13048 if (special.body.len != 0 and err_set and
13093 const else_hint: std.builtin.BranchHint = if (special.body.len != 0 and err_set and
1304913094 try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src, allow_err_code_unwrap))
13050 {
13051 // nothing to do here
13052 } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
13053 try spa.analyzeProngRuntime(
13095 h: {
13096 // nothing to do here. weight against error branch
13097 break :h .unlikely;
13098 } else if (special.body.len != 0 and analyze_body and !special.is_inline) h: {
13099 break :h try spa.analyzeProngRuntime(
1305413100 &case_block,
1305513101 .special,
1305613102 special.body,
......@@ -13063,7 +13109,7 @@ fn analyzeSwitchRuntimeBlock(
1306313109 .none,
1306413110 false,
1306513111 );
13066 } else {
13112 } else h: {
1306713113 // We still need a terminator in this block, but we have proven
1306813114 // that it is unreachable.
1306913115 if (case_block.wantSafety()) {
......@@ -13072,33 +13118,57 @@ fn analyzeSwitchRuntimeBlock(
1307213118 } else {
1307313119 _ = try case_block.addNoOp(.unreach);
1307413120 }
13075 }
13121 // Safety check / unreachable branches are cold.
13122 break :h .cold;
13123 };
1307613124
1307713125 if (is_first) {
13126 try branch_hints.append(gpa, else_hint);
1307813127 final_else_body = case_block.instructions.items;
1307913128 } else {
13129 try branch_hints.append(gpa, .none); // we have the range conditionals first
1308013130 try sema.air_extra.ensureUnusedCapacity(gpa, prev_then_body.len +
1308113131 @typeInfo(Air.CondBr).Struct.fields.len + case_block.instructions.items.len);
1308213132
13083 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload =
13084 sema.addExtraAssumeCapacity(Air.CondBr{
13133 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{
1308513134 .then_body_len = @intCast(prev_then_body.len),
1308613135 .else_body_len = @intCast(case_block.instructions.items.len),
13136 .branch_hints = .{ .true = prev_hint, .false = else_hint },
1308713137 });
1308813138 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));
1308913139 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1309013140 final_else_body = first_else_body;
1309113141 }
13142 } else {
13143 try branch_hints.append(gpa, .none);
1309213144 }
1309313145
13146 assert(branch_hints.items.len == cases_len + 1);
13147
1309413148 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
13095 cases_extra.items.len + final_else_body.len);
13149 cases_extra.items.len + final_else_body.len +
13150 (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints
1309613151
1309713152 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
1309813153 .cases_len = @intCast(cases_len),
1309913154 .else_body_len = @intCast(final_else_body.len),
1310013155 });
1310113156
13157 {
13158 // Add branch hints.
13159 var cur_bag: u32 = 0;
13160 for (branch_hints.items, 0..) |hint, idx| {
13161 const idx_in_bag = idx % 10;
13162 cur_bag |= @as(u32, @intFromEnum(hint)) << @intCast(idx_in_bag * 3);
13163 if (idx_in_bag == 9) {
13164 sema.air_extra.appendAssumeCapacity(cur_bag);
13165 cur_bag = 0;
13166 }
13167 }
13168 if (branch_hints.items.len % 10 != 0) {
13169 sema.air_extra.appendAssumeCapacity(cur_bag);
13170 }
13171 }
1310213172 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));
1310313173 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body));
1310413174
......@@ -19159,6 +19229,10 @@ fn zirBoolBr(
1915919229 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
1916019230 _ = try lhs_block.addBr(block_inst, lhs_result);
1916119231
19232 const parent_hint = sema.branch_hint;
19233 defer sema.branch_hint = parent_hint;
19234 sema.branch_hint = null;
19235
1916219236 const rhs_result = try sema.resolveInlineBody(rhs_block, body, inst);
1916319237 const rhs_noret = sema.typeOf(rhs_result).isNoReturn(zcu);
1916419238 const coerced_rhs_result = if (!rhs_noret) rhs: {
......@@ -19167,7 +19241,17 @@ fn zirBoolBr(
1916719241 break :rhs coerced_result;
1916819242 } else rhs_result;
1916919243
19170 const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst);
19244 const rhs_hint = sema.branch_hint orelse .none;
19245
19246 const result = try sema.finishCondBr(
19247 parent_block,
19248 &child_block,
19249 &then_block,
19250 &else_block,
19251 lhs,
19252 block_inst,
19253 if (is_bool_or) .{ .true = .none, .false = rhs_hint } else .{ .true = rhs_hint, .false = .none },
19254 );
1917119255 if (!rhs_noret) {
1917219256 if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| {
1917319257 if (is_bool_or and rhs_val.toBool()) {
......@@ -19189,6 +19273,7 @@ fn finishCondBr(
1918919273 else_block: *Block,
1919019274 cond: Air.Inst.Ref,
1919119275 block_inst: Air.Inst.Index,
19276 branch_hints: Air.CondBr.BranchHints,
1919219277) !Air.Inst.Ref {
1919319278 const gpa = sema.gpa;
1919419279
......@@ -19199,6 +19284,7 @@ fn finishCondBr(
1919919284 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
1920019285 .then_body_len = @intCast(then_block.instructions.items.len),
1920119286 .else_body_len = @intCast(else_block.instructions.items.len),
19287 .branch_hints = branch_hints,
1920219288 });
1920319289 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
1920419290 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
......@@ -19333,6 +19419,11 @@ fn zirCondbr(
1933319419 if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
1933419420 const body = if (cond_val.toBool()) then_body else else_body;
1933519421
19422 // We can propagate `.cold` hints from this branch since it's comptime-known
19423 // to be taken from the parent branch.
19424 const parent_hint = sema.branch_hint;
19425 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
19426
1933619427 try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
1933719428 // We use `analyzeBodyInner` since we want to propagate any comptime control flow to the caller.
1933819429 return sema.analyzeBodyInner(parent_block, body);
......@@ -19349,7 +19440,7 @@ fn zirCondbr(
1934919440 sub_block.need_debug_scope = null; // this body is emitted regardless
1935019441 defer sub_block.instructions.deinit(gpa);
1935119442
19352 try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
19443 const true_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
1935319444 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
1935419445 defer gpa.free(true_instructions);
1935519446
......@@ -19365,11 +19456,13 @@ fn zirCondbr(
1936519456 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
1936619457 };
1936719458
19368 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false)) {
19369 // nothing to do
19370 } else {
19371 try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
19372 }
19459 const false_hint: std.builtin.BranchHint = if (err_cond != null and
19460 try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false))
19461 h: {
19462 // nothing to do here. weight against error branch
19463 break :h .unlikely;
19464 } else try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
19465
1937319466 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
1937419467 true_instructions.len + sub_block.instructions.items.len);
1937519468 _ = try parent_block.addInst(.{
......@@ -19379,6 +19472,7 @@ fn zirCondbr(
1937919472 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
1938019473 .then_body_len = @intCast(true_instructions.len),
1938119474 .else_body_len = @intCast(sub_block.instructions.items.len),
19475 .branch_hints = .{ .true = true_hint, .false = false_hint },
1938219476 }),
1938319477 } },
1938419478 });
......@@ -19403,6 +19497,11 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
1940319497 }
1940419498 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
1940519499 if (is_non_err != .none) {
19500 // We can propagate `.cold` hints from this branch since it's comptime-known
19501 // to be taken from the parent branch.
19502 const parent_hint = sema.branch_hint;
19503 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
19504
1940619505 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
1940719506 if (is_non_err_val.toBool()) {
1940819507 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
......@@ -19416,13 +19515,19 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
1941619515 var sub_block = parent_block.makeSubBlock();
1941719516 defer sub_block.instructions.deinit(sema.gpa);
1941819517
19518 const parent_hint = sema.branch_hint;
19519 defer sema.branch_hint = parent_hint;
19520
1941919521 // This body is guaranteed to end with noreturn and has no breaks.
1942019522 try sema.analyzeBodyInner(&sub_block, body);
1942119523
19524 // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`.
19525 const is_cold = sema.branch_hint == .cold;
19526
1942219527 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
1942319528 sub_block.instructions.items.len);
1942419529 const try_inst = try parent_block.addInst(.{
19425 .tag = .@"try",
19530 .tag = if (is_cold) .try_cold else .@"try",
1942619531 .data = .{ .pl_op = .{
1942719532 .operand = err_union,
1942819533 .payload = sema.addExtraAssumeCapacity(Air.Try{
......@@ -19452,6 +19557,11 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
1945219557 }
1945319558 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
1945419559 if (is_non_err != .none) {
19560 // We can propagate `.cold` hints from this branch since it's comptime-known
19561 // to be taken from the parent branch.
19562 const parent_hint = sema.branch_hint;
19563 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
19564
1945519565 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
1945619566 if (is_non_err_val.toBool()) {
1945719567 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
......@@ -19465,9 +19575,15 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
1946519575 var sub_block = parent_block.makeSubBlock();
1946619576 defer sub_block.instructions.deinit(sema.gpa);
1946719577
19578 const parent_hint = sema.branch_hint;
19579 defer sema.branch_hint = parent_hint;
19580
1946819581 // This body is guaranteed to end with noreturn and has no breaks.
1946919582 try sema.analyzeBodyInner(&sub_block, body);
1947019583
19584 // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`.
19585 const is_cold = sema.branch_hint == .cold;
19586
1947119587 const operand_ty = sema.typeOf(operand);
1947219588 const ptr_info = operand_ty.ptrInfo(zcu);
1947319589 const res_ty = try pt.ptrTypeSema(.{
......@@ -19483,7 +19599,7 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
1948319599 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len +
1948419600 sub_block.instructions.items.len);
1948519601 const try_inst = try parent_block.addInst(.{
19486 .tag = .try_ptr,
19602 .tag = if (is_cold) .try_ptr_cold else .try_ptr,
1948719603 .data = .{ .ty_pl = .{
1948819604 .ty = res_ty_ref,
1948919605 .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
......@@ -19735,6 +19851,8 @@ fn retWithErrTracing(
1973519851 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
1973619852 .then_body_len = @intCast(then_block.instructions.items.len),
1973719853 .else_body_len = @intCast(else_block.instructions.items.len),
19854 // weight against error branch
19855 .branch_hints = .{ .true = .likely, .false = .unlikely },
1973819856 });
1973919857 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
1974019858 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
......@@ -26747,6 +26865,7 @@ fn zirBuiltinValue(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileErr
2674726865 .export_options => "ExportOptions",
2674826866 .extern_options => "ExternOptions",
2674926867 .type_info => "Type",
26868 .branch_hint => "BranchHint",
2675026869
2675126870 // Values are handled here.
2675226871 .calling_convention_c => {
......@@ -26772,6 +26891,27 @@ fn zirBuiltinValue(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileErr
2677226891 return Air.internedToRef(ty.toIntern());
2677326892}
2677426893
26894fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
26895 const pt = sema.pt;
26896 const zcu = pt.zcu;
26897
26898 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
26899 const uncoerced_hint = try sema.resolveInst(extra.operand);
26900 const operand_src = block.builtinCallArgSrc(extra.node, 0);
26901
26902 const hint_ty = try pt.getBuiltinType("BranchHint");
26903 const coerced_hint = try sema.coerce(block, hint_ty, uncoerced_hint, operand_src);
26904 const hint_val = try sema.resolveConstDefinedValue(block, operand_src, coerced_hint, .{
26905 .needed_comptime_reason = "operand to '@branchHint' must be comptime-known",
26906 });
26907
26908 // We only apply the first hint in a branch.
26909 // This allows user-provided hints to override implicit cold hints.
26910 if (sema.branch_hint == null) {
26911 sema.branch_hint = zcu.toEnum(std.builtin.BranchHint, hint_val);
26912 }
26913}
26914
2677526915fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
2677626916 if (block.is_comptime) {
2677726917 const msg = msg: {
......@@ -27327,13 +27467,17 @@ fn addSafetyCheckExtra(
2732727467
2732827468 sema.air_instructions.appendAssumeCapacity(.{
2732927469 .tag = .cond_br,
27330 .data = .{ .pl_op = .{
27331 .operand = ok,
27332 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
27333 .then_body_len = 1,
27334 .else_body_len = @intCast(fail_block.instructions.items.len),
27335 }),
27336 } },
27470 .data = .{
27471 .pl_op = .{
27472 .operand = ok,
27473 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
27474 .then_body_len = 1,
27475 .else_body_len = @intCast(fail_block.instructions.items.len),
27476 // safety check failure branch is cold
27477 .branch_hints = .{ .true = .likely, .false = .cold },
27478 }),
27479 },
27480 },
2733727481 });
2733827482 sema.air_extra.appendAssumeCapacity(@intFromEnum(br_inst));
2733927483 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items));
......@@ -27530,6 +27674,7 @@ fn safetyCheckFormatted(
2753027674 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
2753127675}
2753227676
27677/// This does not set `sema.branch_hint`.
2753327678fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.PanicId) CompileError!void {
2753427679 const msg_nav_index = try sema.preparePanicId(block, src, panic_id);
2753527680 const msg_inst = try sema.analyzeNavVal(block, src, msg_nav_index);
......@@ -37179,7 +37324,7 @@ pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
3717937324 inline for (fields) |field| {
3718037325 sema.air_extra.appendAssumeCapacity(switch (field.type) {
3718137326 u32 => @field(extra, field.name),
37182 i32 => @bitCast(@field(extra, field.name)),
37327 i32, Air.CondBr.BranchHints => @bitCast(@field(extra, field.name)),
3718337328 Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(extra, field.name)),
3718437329 else => @compileError("bad field type: " ++ @typeName(field.type)),
3718537330 });
......@@ -38247,6 +38392,12 @@ fn maybeDerefSliceAsArray(
3824738392
3824838393fn analyzeUnreachable(sema: *Sema, block: *Block, src: LazySrcLoc, safety_check: bool) !void {
3824938394 if (safety_check and block.wantSafety()) {
38395 // We only apply the first hint in a branch.
38396 // This allows user-provided hints to override implicit cold hints.
38397 if (sema.branch_hint == null) {
38398 sema.branch_hint = .cold;
38399 }
38400
3825038401 try sema.safetyPanic(block, src, .unreach);
3825138402 } else {
3825238403 _ = try block.addNoOp(.unreach);
src/Zcu/PerThread.zig+2
......@@ -2188,6 +2188,8 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
21882188 });
21892189 }
21902190
2191 func.setBranchHint(ip, sema.branch_hint orelse .none);
2192
21912193 // If we don't get an error return trace from a caller, create our own.
21922194 if (func.analysisUnordered(ip).calls_or_awaits_errorable_fn and
21932195 zcu.comp.config.any_error_tracing and
src/arch/aarch64/CodeGen.zig+16-22
......@@ -795,7 +795,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
795795 .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}),
796796
797797 .@"try" => try self.airTry(inst),
798 .try_cold => try self.airTry(inst),
798799 .try_ptr => try self.airTryPtr(inst),
800 .try_ptr_cold => try self.airTryPtr(inst),
799801
800802 .dbg_stmt => try self.airDbgStmt(inst),
801803 .dbg_inline_block => try self.airDbgInlineBlock(inst),
......@@ -5092,25 +5094,17 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
50925094}
50935095
50945096fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5095 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5096 const condition_ty = self.typeOf(pl_op.operand);
5097 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
5097 const switch_br = self.air.unwrapSwitch(inst);
5098 const condition_ty = self.typeOf(switch_br.operand);
50985099 const liveness = try self.liveness.getSwitchBr(
50995100 self.gpa,
51005101 inst,
5101 switch_br.data.cases_len + 1,
5102 switch_br.cases_len + 1,
51025103 );
51035104 defer self.gpa.free(liveness.deaths);
51045105
5105 var extra_index: usize = switch_br.end;
5106 var case_i: u32 = 0;
5107 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
5108 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
5109 const items = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[case.end..][0..case.data.items_len]));
5110 assert(items.len > 0);
5111 const case_body: []const Air.Inst.Index = @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]);
5112 extra_index = case.end + items.len + case_body.len;
5113
5106 var it = switch_br.iterateCases();
5107 while (it.next()) |case| {
51145108 // For every item, we compare it to condition and branch into
51155109 // the prong if they are equal. After we compared to all
51165110 // items, we branch into the next prong (or if no other prongs
......@@ -5126,11 +5120,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51265120 // prong: ...
51275121 // ...
51285122 // out: ...
5129 const branch_into_prong_relocs = try self.gpa.alloc(u32, items.len);
5123 const branch_into_prong_relocs = try self.gpa.alloc(u32, case.items.len);
51305124 defer self.gpa.free(branch_into_prong_relocs);
51315125
5132 for (items, 0..) |item, idx| {
5133 const cmp_result = try self.cmp(.{ .inst = pl_op.operand }, .{ .inst = item }, condition_ty, .neq);
5126 for (case.items, 0..) |item, idx| {
5127 const cmp_result = try self.cmp(.{ .inst = switch_br.operand }, .{ .inst = item }, condition_ty, .neq);
51345128 branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
51355129 }
51365130
......@@ -5156,11 +5150,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51565150 _ = self.branch_stack.pop();
51575151 }
51585152
5159 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
5160 for (liveness.deaths[case_i]) |operand| {
5153 try self.ensureProcessDeathCapacity(liveness.deaths[case.idx].len);
5154 for (liveness.deaths[case.idx]) |operand| {
51615155 self.processDeath(operand);
51625156 }
5163 try self.genBody(case_body);
5157 try self.genBody(case.body);
51645158
51655159 // Revert to the previous register and stack allocation state.
51665160 var saved_case_branch = self.branch_stack.pop();
......@@ -5178,8 +5172,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51785172 try self.performReloc(branch_away_from_prong_reloc);
51795173 }
51805174
5181 if (switch_br.data.else_body_len > 0) {
5182 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);
5175 if (switch_br.else_body_len > 0) {
5176 const else_body = it.elseBody();
51835177
51845178 // Capture the state of register and stack allocation state so that we can revert to it.
51855179 const parent_next_stack_offset = self.next_stack_offset;
......@@ -5218,7 +5212,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
52185212 // in airCondBr.
52195213 }
52205214
5221 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
5215 return self.finishAir(inst, .unreach, .{ switch_br.operand, .none, .none });
52225216}
52235217
52245218fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
src/arch/arm/CodeGen.zig+16-22
......@@ -782,7 +782,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
782782 .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}),
783783
784784 .@"try" => try self.airTry(inst),
785 .try_cold => try self.airTry(inst),
785786 .try_ptr => try self.airTryPtr(inst),
787 .try_ptr_cold => try self.airTryPtr(inst),
786788
787789 .dbg_stmt => try self.airDbgStmt(inst),
788790 .dbg_inline_block => try self.airDbgInlineBlock(inst),
......@@ -5040,25 +5042,17 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
50405042}
50415043
50425044fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
5043 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5044 const condition_ty = self.typeOf(pl_op.operand);
5045 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
5045 const switch_br = self.air.unwrapSwitch(inst);
5046 const condition_ty = self.typeOf(switch_br.operand);
50465047 const liveness = try self.liveness.getSwitchBr(
50475048 self.gpa,
50485049 inst,
5049 switch_br.data.cases_len + 1,
5050 switch_br.cases_len + 1,
50505051 );
50515052 defer self.gpa.free(liveness.deaths);
50525053
5053 var extra_index: usize = switch_br.end;
5054 var case_i: u32 = 0;
5055 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
5056 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
5057 const items: []const Air.Inst.Ref = @ptrCast(self.air.extra[case.end..][0..case.data.items_len]);
5058 assert(items.len > 0);
5059 const case_body: []const Air.Inst.Index = @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]);
5060 extra_index = case.end + items.len + case_body.len;
5061
5054 var it = switch_br.iterateCases();
5055 while (it.next()) |case| {
50625056 // For every item, we compare it to condition and branch into
50635057 // the prong if they are equal. After we compared to all
50645058 // items, we branch into the next prong (or if no other prongs
......@@ -5074,11 +5068,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
50745068 // prong: ...
50755069 // ...
50765070 // out: ...
5077 const branch_into_prong_relocs = try self.gpa.alloc(u32, items.len);
5071 const branch_into_prong_relocs = try self.gpa.alloc(u32, case.items.len);
50785072 defer self.gpa.free(branch_into_prong_relocs);
50795073
5080 for (items, 0..) |item, idx| {
5081 const cmp_result = try self.cmp(.{ .inst = pl_op.operand }, .{ .inst = item }, condition_ty, .neq);
5074 for (case.items, 0..) |item, idx| {
5075 const cmp_result = try self.cmp(.{ .inst = switch_br.operand }, .{ .inst = item }, condition_ty, .neq);
50825076 branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
50835077 }
50845078
......@@ -5104,11 +5098,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51045098 _ = self.branch_stack.pop();
51055099 }
51065100
5107 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
5108 for (liveness.deaths[case_i]) |operand| {
5101 try self.ensureProcessDeathCapacity(liveness.deaths[case.idx].len);
5102 for (liveness.deaths[case.idx]) |operand| {
51095103 self.processDeath(operand);
51105104 }
5111 try self.genBody(case_body);
5105 try self.genBody(case.body);
51125106
51135107 // Revert to the previous register and stack allocation state.
51145108 var saved_case_branch = self.branch_stack.pop();
......@@ -5126,8 +5120,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51265120 try self.performReloc(branch_away_from_prong_reloc);
51275121 }
51285122
5129 if (switch_br.data.else_body_len > 0) {
5130 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);
5123 if (switch_br.else_body_len > 0) {
5124 const else_body = it.elseBody();
51315125
51325126 // Capture the state of register and stack allocation state so that we can revert to it.
51335127 const parent_next_stack_offset = self.next_stack_offset;
......@@ -5166,7 +5160,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51665160 // in airCondBr.
51675161 }
51685162
5169 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
5163 return self.finishAir(inst, .unreach, .{ switch_br.operand, .none, .none });
51705164}
51715165
51725166fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
src/arch/riscv64/CodeGen.zig+16-23
......@@ -1640,7 +1640,9 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
16401640 .addrspace_cast => return func.fail("TODO: addrspace_cast", .{}),
16411641
16421642 .@"try" => try func.airTry(inst),
1643 .try_cold => try func.airTry(inst),
16431644 .try_ptr => return func.fail("TODO: try_ptr", .{}),
1645 .try_ptr_cold => return func.fail("TODO: try_ptr_cold", .{}),
16441646
16451647 .dbg_var_ptr,
16461648 .dbg_var_val,
......@@ -5659,38 +5661,30 @@ fn lowerBlock(func: *Func, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
56595661}
56605662
56615663fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
5662 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5663 const condition_ty = func.typeOf(pl_op.operand);
5664 const switch_br = func.air.extraData(Air.SwitchBr, pl_op.payload);
5665 var extra_index: usize = switch_br.end;
5666 var case_i: u32 = 0;
5667 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.data.cases_len + 1);
5664 const switch_br = func.air.unwrapSwitch(inst);
5665
5666 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.cases_len + 1);
56685667 defer func.gpa.free(liveness.deaths);
56695668
5670 const condition = try func.resolveInst(pl_op.operand);
5669 const condition = try func.resolveInst(switch_br.operand);
5670 const condition_ty = func.typeOf(switch_br.operand);
56715671
56725672 // If the condition dies here in this switch instruction, process
56735673 // that death now instead of later as this has an effect on
56745674 // whether it needs to be spilled in the branches
56755675 if (func.liveness.operandDies(inst, 0)) {
5676 if (pl_op.operand.toIndex()) |op_inst| try func.processDeath(op_inst);
5676 if (switch_br.operand.toIndex()) |op_inst| try func.processDeath(op_inst);
56775677 }
56785678
56795679 func.scope_generation += 1;
56805680 const state = try func.saveState();
56815681
5682 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
5683 const case = func.air.extraData(Air.SwitchBr.Case, extra_index);
5684 const items: []const Air.Inst.Ref =
5685 @ptrCast(func.air.extra[case.end..][0..case.data.items_len]);
5686 const case_body: []const Air.Inst.Index =
5687 @ptrCast(func.air.extra[case.end + items.len ..][0..case.data.body_len]);
5688 extra_index = case.end + items.len + case_body.len;
5689
5690 var relocs = try func.gpa.alloc(Mir.Inst.Index, items.len);
5682 var it = switch_br.iterateCases();
5683 while (it.next()) |case| {
5684 var relocs = try func.gpa.alloc(Mir.Inst.Index, case.items.len);
56915685 defer func.gpa.free(relocs);
56925686
5693 for (items, relocs, 0..) |item, *reloc, i| {
5687 for (case.items, relocs, 0..) |item, *reloc, i| {
56945688 const item_mcv = try func.resolveInst(item);
56955689
56965690 const cond_lock = switch (condition) {
......@@ -5724,10 +5718,10 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
57245718 reloc.* = try func.condBr(condition_ty, .{ .register = cmp_reg });
57255719 }
57265720
5727 for (liveness.deaths[case_i]) |operand| try func.processDeath(operand);
5721 for (liveness.deaths[case.idx]) |operand| try func.processDeath(operand);
57285722
57295723 for (relocs[0 .. relocs.len - 1]) |reloc| func.performReloc(reloc);
5730 try func.genBody(case_body);
5724 try func.genBody(case.body);
57315725 try func.restoreState(state, &.{}, .{
57325726 .emit_instructions = false,
57335727 .update_tracking = true,
......@@ -5738,9 +5732,8 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
57385732 func.performReloc(relocs[relocs.len - 1]);
57395733 }
57405734
5741 if (switch_br.data.else_body_len > 0) {
5742 const else_body: []const Air.Inst.Index =
5743 @ptrCast(func.air.extra[extra_index..][0..switch_br.data.else_body_len]);
5735 if (switch_br.else_body_len > 0) {
5736 const else_body = it.elseBody();
57445737
57455738 const else_deaths = liveness.deaths.len - 1;
57465739 for (liveness.deaths[else_deaths]) |operand| try func.processDeath(operand);
src/arch/sparc64/CodeGen.zig+2
......@@ -637,7 +637,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
637637 .addrspace_cast => @panic("TODO try self.airAddrSpaceCast(int)"),
638638
639639 .@"try" => try self.airTry(inst),
640 .try_cold => try self.airTry(inst),
640641 .try_ptr => @panic("TODO try self.airTryPtr(inst)"),
642 .try_ptr_cold => @panic("TODO try self.airTryPtrCold(inst)"),
641643
642644 .dbg_stmt => try self.airDbgStmt(inst),
643645 .dbg_inline_block => try self.airDbgInlineBlock(inst),
src/arch/wasm/CodeGen.zig+16-20
......@@ -1913,7 +1913,9 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19131913 .get_union_tag => func.airGetUnionTag(inst),
19141914
19151915 .@"try" => func.airTry(inst),
1916 .try_cold => func.airTry(inst),
19161917 .try_ptr => func.airTryPtr(inst),
1918 .try_ptr_cold => func.airTryPtr(inst),
19171919
19181920 .dbg_stmt => func.airDbgStmt(inst),
19191921 .dbg_inline_block => func.airDbgInlineBlock(inst),
......@@ -4041,37 +4043,31 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40414043 const zcu = pt.zcu;
40424044 // result type is always 'noreturn'
40434045 const blocktype = wasm.block_empty;
4044 const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4045 const target = try func.resolveInst(pl_op.operand);
4046 const target_ty = func.typeOf(pl_op.operand);
4047 const switch_br = func.air.extraData(Air.SwitchBr, pl_op.payload);
4048 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.data.cases_len + 1);
4046 const switch_br = func.air.unwrapSwitch(inst);
4047 const target = try func.resolveInst(switch_br.operand);
4048 const target_ty = func.typeOf(switch_br.operand);
4049 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.cases_len + 1);
40494050 defer func.gpa.free(liveness.deaths);
40504051
4051 var extra_index: usize = switch_br.end;
4052 var case_i: u32 = 0;
4053
40544052 // a list that maps each value with its value and body based on the order inside the list.
40554053 const CaseValue = struct { integer: i32, value: Value };
40564054 var case_list = try std.ArrayList(struct {
40574055 values: []const CaseValue,
40584056 body: []const Air.Inst.Index,
4059 }).initCapacity(func.gpa, switch_br.data.cases_len);
4057 }).initCapacity(func.gpa, switch_br.cases_len);
40604058 defer for (case_list.items) |case| {
40614059 func.gpa.free(case.values);
40624060 } else case_list.deinit();
40634061
40644062 var lowest_maybe: ?i32 = null;
40654063 var highest_maybe: ?i32 = null;
4066 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
4067 const case = func.air.extraData(Air.SwitchBr.Case, extra_index);
4068 const items: []const Air.Inst.Ref = @ptrCast(func.air.extra[case.end..][0..case.data.items_len]);
4069 const case_body: []const Air.Inst.Index = @ptrCast(func.air.extra[case.end + items.len ..][0..case.data.body_len]);
4070 extra_index = case.end + items.len + case_body.len;
4071 const values = try func.gpa.alloc(CaseValue, items.len);
4064
4065 var it = switch_br.iterateCases();
4066 while (it.next()) |case| {
4067 const values = try func.gpa.alloc(CaseValue, case.items.len);
40724068 errdefer func.gpa.free(values);
40734069
4074 for (items, 0..) |ref, i| {
4070 for (case.items, 0..) |ref, i| {
40754071 const item_val = (try func.air.value(ref, pt)).?;
40764072 const int_val = func.valueAsI32(item_val);
40774073 if (lowest_maybe == null or int_val < lowest_maybe.?) {
......@@ -4083,7 +4079,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40834079 values[i] = .{ .integer = int_val, .value = item_val };
40844080 }
40854081
4086 case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });
4082 case_list.appendAssumeCapacity(.{ .values = values, .body = case.body });
40874083 try func.startBlock(.block, blocktype);
40884084 }
40894085
......@@ -4097,7 +4093,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40974093 // TODO: Benchmark this to find a proper value, LLVM seems to draw the line at '40~45'.
40984094 const is_sparse = highest - lowest > 50 or target_ty.bitSize(zcu) > 32;
40994095
4100 const else_body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra_index..][0..switch_br.data.else_body_len]);
4096 const else_body = it.elseBody();
41014097 const has_else_body = else_body.len != 0;
41024098 if (has_else_body) {
41034099 try func.startBlock(.block, blocktype);
......@@ -4140,11 +4136,11 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41404136 // for errors that are not present in any branch. This is fine as this default
41414137 // case will never be hit for those cases but we do save runtime cost and size
41424138 // by using a jump table for this instead of if-else chains.
4143 break :blk if (has_else_body or target_ty.zigTypeTag(zcu) == .ErrorSet) case_i else unreachable;
4139 break :blk if (has_else_body or target_ty.zigTypeTag(zcu) == .ErrorSet) switch_br.cases_len else unreachable;
41444140 };
41454141 func.mir_extra.appendAssumeCapacity(idx);
41464142 } else if (has_else_body) {
4147 func.mir_extra.appendAssumeCapacity(case_i); // default branch
4143 func.mir_extra.appendAssumeCapacity(switch_br.cases_len); // default branch
41484144 }
41494145 try func.endBlock();
41504146 }
src/arch/x86_64/CodeGen.zig+15-23
......@@ -2262,7 +2262,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
22622262 .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}),
22632263
22642264 .@"try" => try self.airTry(inst),
2265 .try_cold => try self.airTry(inst), // TODO
22652266 .try_ptr => try self.airTryPtr(inst),
2267 .try_ptr_cold => try self.airTryPtr(inst), // TODO
22662268
22672269 .dbg_stmt => try self.airDbgStmt(inst),
22682270 .dbg_inline_block => try self.airDbgInlineBlock(inst),
......@@ -13631,38 +13633,29 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
1363113633}
1363213634
1363313635fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
13634 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
13635 const condition = try self.resolveInst(pl_op.operand);
13636 const condition_ty = self.typeOf(pl_op.operand);
13637 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
13638 var extra_index: usize = switch_br.end;
13639 var case_i: u32 = 0;
13640 const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.data.cases_len + 1);
13636 const switch_br = self.air.unwrapSwitch(inst);
13637 const condition = try self.resolveInst(switch_br.operand);
13638 const condition_ty = self.typeOf(switch_br.operand);
13639 const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.cases_len + 1);
1364113640 defer self.gpa.free(liveness.deaths);
1364213641
1364313642 // If the condition dies here in this switch instruction, process
1364413643 // that death now instead of later as this has an effect on
1364513644 // whether it needs to be spilled in the branches
1364613645 if (self.liveness.operandDies(inst, 0)) {
13647 if (pl_op.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
13646 if (switch_br.operand.toIndex()) |op_inst| try self.processDeath(op_inst);
1364813647 }
1364913648
1365013649 self.scope_generation += 1;
1365113650 const state = try self.saveState();
1365213651
13653 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
13654 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
13655 const items: []const Air.Inst.Ref =
13656 @ptrCast(self.air.extra[case.end..][0..case.data.items_len]);
13657 const case_body: []const Air.Inst.Index =
13658 @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]);
13659 extra_index = case.end + items.len + case_body.len;
13660
13661 var relocs = try self.gpa.alloc(Mir.Inst.Index, items.len);
13652 var it = switch_br.iterateCases();
13653 while (it.next()) |case| {
13654 var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len);
1366213655 defer self.gpa.free(relocs);
1366313656
1366413657 try self.spillEflagsIfOccupied();
13665 for (items, relocs, 0..) |item, *reloc, i| {
13658 for (case.items, relocs, 0..) |item, *reloc, i| {
1366613659 const item_mcv = try self.resolveInst(item);
1366713660 const cc: Condition = switch (condition) {
1366813661 .eflags => |cc| switch (item_mcv.immediate) {
......@@ -13678,10 +13671,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
1367813671 reloc.* = try self.asmJccReloc(if (i < relocs.len - 1) cc else cc.negate(), undefined);
1367913672 }
1368013673
13681 for (liveness.deaths[case_i]) |operand| try self.processDeath(operand);
13674 for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand);
1368213675
1368313676 for (relocs[0 .. relocs.len - 1]) |reloc| self.performReloc(reloc);
13684 try self.genBody(case_body);
13677 try self.genBody(case.body);
1368513678 try self.restoreState(state, &.{}, .{
1368613679 .emit_instructions = false,
1368713680 .update_tracking = true,
......@@ -13692,9 +13685,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
1369213685 self.performReloc(relocs[relocs.len - 1]);
1369313686 }
1369413687
13695 if (switch_br.data.else_body_len > 0) {
13696 const else_body: []const Air.Inst.Index =
13697 @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);
13688 if (switch_br.else_body_len > 0) {
13689 const else_body = it.elseBody();
1369813690
1369913691 const else_deaths = liveness.deaths.len - 1;
1370013692 for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand);
src/codegen/c.zig+19-24
......@@ -1786,7 +1786,7 @@ pub const DeclGen = struct {
17861786 else => unreachable,
17871787 }
17881788 }
1789 if (fn_val.getFunction(zcu)) |func| if (func.analysisUnordered(ip).is_cold)
1789 if (fn_val.getFunction(zcu)) |func| if (func.analysisUnordered(ip).branch_hint == .cold)
17901790 try w.writeAll("zig_cold ");
17911791 if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn ");
17921792
......@@ -3290,8 +3290,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
32903290 .prefetch => try airPrefetch(f, inst),
32913291 .addrspace_cast => return f.fail("TODO: C backend: implement addrspace_cast", .{}),
32923292
3293 .@"try" => try airTry(f, inst),
3294 .try_ptr => try airTryPtr(f, inst),
3293 .@"try" => try airTry(f, inst),
3294 .try_cold => try airTry(f, inst),
3295 .try_ptr => try airTryPtr(f, inst),
3296 .try_ptr_cold => try airTryPtr(f, inst),
32953297
32963298 .dbg_stmt => try airDbgStmt(f, inst),
32973299 .dbg_inline_block => try airDbgInlineBlock(f, inst),
......@@ -4988,11 +4990,10 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
49884990fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
49894991 const pt = f.object.dg.pt;
49904992 const zcu = pt.zcu;
4991 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
4992 const condition = try f.resolveInst(pl_op.operand);
4993 try reap(f, inst, &.{pl_op.operand});
4994 const condition_ty = f.typeOf(pl_op.operand);
4995 const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload);
4993 const switch_br = f.air.unwrapSwitch(inst);
4994 const condition = try f.resolveInst(switch_br.operand);
4995 try reap(f, inst, &.{switch_br.operand});
4996 const condition_ty = f.typeOf(switch_br.operand);
49964997 const writer = f.object.writer();
49974998
49984999 try writer.writeAll("switch (");
......@@ -5013,22 +5014,16 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
50135014 f.object.indent_writer.pushIndent();
50145015
50155016 const gpa = f.object.dg.gpa;
5016 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.data.cases_len + 1);
5017 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1);
50175018 defer gpa.free(liveness.deaths);
50185019
50195020 // On the final iteration we do not need to fix any state. This is because, like in the `else`
50205021 // branch of a `cond_br`, our parent has to do it for this entire body anyway.
5021 const last_case_i = switch_br.data.cases_len - @intFromBool(switch_br.data.else_body_len == 0);
5022
5023 var extra_index: usize = switch_br.end;
5024 for (0..switch_br.data.cases_len) |case_i| {
5025 const case = f.air.extraData(Air.SwitchBr.Case, extra_index);
5026 const items = @as([]const Air.Inst.Ref, @ptrCast(f.air.extra[case.end..][0..case.data.items_len]));
5027 const case_body: []const Air.Inst.Index =
5028 @ptrCast(f.air.extra[case.end + items.len ..][0..case.data.body_len]);
5029 extra_index = case.end + case.data.items_len + case_body.len;
5022 const last_case_i = switch_br.cases_len - @intFromBool(switch_br.else_body_len == 0);
50305023
5031 for (items) |item| {
5024 var it = switch_br.iterateCases();
5025 while (it.next()) |case| {
5026 for (case.items) |item| {
50325027 try f.object.indent_writer.insertNewline();
50335028 try writer.writeAll("case ");
50345029 const item_value = try f.air.value(item, pt);
......@@ -5046,19 +5041,19 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
50465041 }
50475042 try writer.writeByte(' ');
50485043
5049 if (case_i != last_case_i) {
5050 try genBodyResolveState(f, inst, liveness.deaths[case_i], case_body, false);
5044 if (case.idx != last_case_i) {
5045 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, false);
50515046 } else {
5052 for (liveness.deaths[case_i]) |death| {
5047 for (liveness.deaths[case.idx]) |death| {
50535048 try die(f, inst, death.toRef());
50545049 }
5055 try genBody(f, case_body);
5050 try genBody(f, case.body);
50565051 }
50575052
50585053 // The case body must be noreturn so we don't need to insert a break.
50595054 }
50605055
5061 const else_body: []const Air.Inst.Index = @ptrCast(f.air.extra[extra_index..][0..switch_br.data.else_body_len]);
5056 const else_body = it.elseBody();
50625057 try f.object.indent_writer.insertNewline();
50635058 if (else_body.len > 0) {
50645059 // Note that this must be the last case (i.e. the `last_case_i` case was not hit above)
src/codegen/llvm.zig+158-73
......@@ -898,9 +898,9 @@ pub const Object = struct {
898898 const i32_2 = try builder.intConst(.i32, 2);
899899 const i32_3 = try builder.intConst(.i32, 3);
900900 const debug_info_version = try builder.debugModuleFlag(
901 try builder.debugConstant(i32_2),
901 try builder.metadataConstant(i32_2),
902902 try builder.metadataString("Debug Info Version"),
903 try builder.debugConstant(i32_3),
903 try builder.metadataConstant(i32_3),
904904 );
905905
906906 switch (comp.config.debug_format) {
......@@ -908,9 +908,9 @@ pub const Object = struct {
908908 .dwarf => |f| {
909909 const i32_4 = try builder.intConst(.i32, 4);
910910 const dwarf_version = try builder.debugModuleFlag(
911 try builder.debugConstant(i32_2),
911 try builder.metadataConstant(i32_2),
912912 try builder.metadataString("Dwarf Version"),
913 try builder.debugConstant(i32_4),
913 try builder.metadataConstant(i32_4),
914914 );
915915 switch (f) {
916916 .@"32" => {
......@@ -921,9 +921,9 @@ pub const Object = struct {
921921 },
922922 .@"64" => {
923923 const dwarf64 = try builder.debugModuleFlag(
924 try builder.debugConstant(i32_2),
924 try builder.metadataConstant(i32_2),
925925 try builder.metadataString("DWARF64"),
926 try builder.debugConstant(.@"1"),
926 try builder.metadataConstant(.@"1"),
927927 );
928928 try builder.debugNamed(try builder.metadataString("llvm.module.flags"), &.{
929929 debug_info_version,
......@@ -935,9 +935,9 @@ pub const Object = struct {
935935 },
936936 .code_view => {
937937 const code_view = try builder.debugModuleFlag(
938 try builder.debugConstant(i32_2),
938 try builder.metadataConstant(i32_2),
939939 try builder.metadataString("CodeView"),
940 try builder.debugConstant(.@"1"),
940 try builder.metadataConstant(.@"1"),
941941 );
942942 try builder.debugNamed(try builder.metadataString("llvm.module.flags"), &.{
943943 debug_info_version,
......@@ -1122,12 +1122,12 @@ pub const Object = struct {
11221122
11231123 self.builder.debugForwardReferenceSetType(
11241124 self.debug_enums_fwd_ref,
1125 try self.builder.debugTuple(self.debug_enums.items),
1125 try self.builder.metadataTuple(self.debug_enums.items),
11261126 );
11271127
11281128 self.builder.debugForwardReferenceSetType(
11291129 self.debug_globals_fwd_ref,
1130 try self.builder.debugTuple(self.debug_globals.items),
1130 try self.builder.metadataTuple(self.debug_globals.items),
11311131 );
11321132 }
11331133 }
......@@ -1369,7 +1369,7 @@ pub const Object = struct {
13691369 _ = try attributes.removeFnAttr(.alignstack);
13701370 }
13711371
1372 if (func_analysis.is_cold) {
1372 if (func_analysis.branch_hint == .cold) {
13731373 try attributes.addFnAttr(.cold, &o.builder);
13741374 } else {
13751375 _ = try attributes.removeFnAttr(.cold);
......@@ -1978,7 +1978,7 @@ pub const Object = struct {
19781978 try o.lowerDebugType(int_ty),
19791979 ty.abiSize(zcu) * 8,
19801980 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
1981 try o.builder.debugTuple(enumerators),
1981 try o.builder.metadataTuple(enumerators),
19821982 );
19831983
19841984 try o.debug_type_map.put(gpa, ty, debug_enum_type);
......@@ -2087,7 +2087,7 @@ pub const Object = struct {
20872087 .none, // Underlying type
20882088 ty.abiSize(zcu) * 8,
20892089 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2090 try o.builder.debugTuple(&.{
2090 try o.builder.metadataTuple(&.{
20912091 debug_ptr_type,
20922092 debug_len_type,
20932093 }),
......@@ -2167,10 +2167,10 @@ pub const Object = struct {
21672167 try o.lowerDebugType(ty.childType(zcu)),
21682168 ty.abiSize(zcu) * 8,
21692169 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2170 try o.builder.debugTuple(&.{
2170 try o.builder.metadataTuple(&.{
21712171 try o.builder.debugSubrange(
2172 try o.builder.debugConstant(try o.builder.intConst(.i64, 0)),
2173 try o.builder.debugConstant(try o.builder.intConst(.i64, ty.arrayLen(zcu))),
2172 try o.builder.metadataConstant(try o.builder.intConst(.i64, 0)),
2173 try o.builder.metadataConstant(try o.builder.intConst(.i64, ty.arrayLen(zcu))),
21742174 ),
21752175 }),
21762176 );
......@@ -2210,10 +2210,10 @@ pub const Object = struct {
22102210 debug_elem_type,
22112211 ty.abiSize(zcu) * 8,
22122212 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2213 try o.builder.debugTuple(&.{
2213 try o.builder.metadataTuple(&.{
22142214 try o.builder.debugSubrange(
2215 try o.builder.debugConstant(try o.builder.intConst(.i64, 0)),
2216 try o.builder.debugConstant(try o.builder.intConst(.i64, ty.vectorLen(zcu))),
2215 try o.builder.metadataConstant(try o.builder.intConst(.i64, 0)),
2216 try o.builder.metadataConstant(try o.builder.intConst(.i64, ty.vectorLen(zcu))),
22172217 ),
22182218 }),
22192219 );
......@@ -2288,7 +2288,7 @@ pub const Object = struct {
22882288 .none, // Underlying type
22892289 ty.abiSize(zcu) * 8,
22902290 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2291 try o.builder.debugTuple(&.{
2291 try o.builder.metadataTuple(&.{
22922292 debug_data_type,
22932293 debug_some_type,
22942294 }),
......@@ -2367,7 +2367,7 @@ pub const Object = struct {
23672367 .none, // Underlying type
23682368 ty.abiSize(zcu) * 8,
23692369 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2370 try o.builder.debugTuple(&fields),
2370 try o.builder.metadataTuple(&fields),
23712371 );
23722372
23732373 o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_error_union_type);
......@@ -2447,7 +2447,7 @@ pub const Object = struct {
24472447 .none, // Underlying type
24482448 ty.abiSize(zcu) * 8,
24492449 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2450 try o.builder.debugTuple(fields.items),
2450 try o.builder.metadataTuple(fields.items),
24512451 );
24522452
24532453 o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type);
......@@ -2520,7 +2520,7 @@ pub const Object = struct {
25202520 .none, // Underlying type
25212521 ty.abiSize(zcu) * 8,
25222522 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2523 try o.builder.debugTuple(fields.items),
2523 try o.builder.metadataTuple(fields.items),
25242524 );
25252525
25262526 o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type);
......@@ -2561,7 +2561,7 @@ pub const Object = struct {
25612561 .none, // Underlying type
25622562 ty.abiSize(zcu) * 8,
25632563 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2564 try o.builder.debugTuple(
2564 try o.builder.metadataTuple(
25652565 &.{try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty))},
25662566 ),
25672567 );
......@@ -2623,7 +2623,7 @@ pub const Object = struct {
26232623 .none, // Underlying type
26242624 ty.abiSize(zcu) * 8,
26252625 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2626 try o.builder.debugTuple(fields.items),
2626 try o.builder.metadataTuple(fields.items),
26272627 );
26282628
26292629 o.builder.debugForwardReferenceSetType(debug_union_fwd_ref, debug_union_type);
......@@ -2682,7 +2682,7 @@ pub const Object = struct {
26822682 .none, // Underlying type
26832683 ty.abiSize(zcu) * 8,
26842684 (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8,
2685 try o.builder.debugTuple(&full_fields),
2685 try o.builder.metadataTuple(&full_fields),
26862686 );
26872687
26882688 o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_tagged_union_type);
......@@ -2735,7 +2735,7 @@ pub const Object = struct {
27352735 }
27362736
27372737 const debug_function_type = try o.builder.debugSubroutineType(
2738 try o.builder.debugTuple(debug_param_types.items),
2738 try o.builder.metadataTuple(debug_param_types.items),
27392739 );
27402740
27412741 try o.debug_type_map.put(gpa, ty, debug_function_type);
......@@ -4571,7 +4571,7 @@ pub const Object = struct {
45714571 const bad_value_block = try wip.block(1, "BadValue");
45724572 const tag_int_value = wip.arg(0);
45734573 var wip_switch =
4574 try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len));
4574 try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len), .none);
45754575 defer wip_switch.finish(&wip);
45764576
45774577 for (0..enum_type.names.len) |field_index| {
......@@ -4958,8 +4958,10 @@ pub const FuncGen = struct {
49584958 .ret_addr => try self.airRetAddr(inst),
49594959 .frame_addr => try self.airFrameAddress(inst),
49604960 .cond_br => try self.airCondBr(inst),
4961 .@"try" => try self.airTry(body[i..]),
4962 .try_ptr => try self.airTryPtr(inst),
4961 .@"try" => try self.airTry(body[i..], false),
4962 .try_cold => try self.airTry(body[i..], true),
4963 .try_ptr => try self.airTryPtr(inst, false),
4964 .try_ptr_cold => try self.airTryPtr(inst, true),
49634965 .intcast => try self.airIntCast(inst),
49644966 .trunc => try self.airTrunc(inst),
49654967 .fptrunc => try self.airFptrunc(inst),
......@@ -5506,6 +5508,7 @@ pub const FuncGen = struct {
55065508 const panic_nav = ip.getNav(panic_func.owner_nav);
55075509 const fn_info = zcu.typeToFunc(Type.fromInterned(panic_nav.typeOf(ip))).?;
55085510 const panic_global = try o.resolveLlvmFunction(panic_func.owner_nav);
5511 _ = try fg.wip.callIntrinsicAssumeCold();
55095512 _ = try fg.wip.call(
55105513 .normal,
55115514 toLlvmCallConv(fn_info.cc, target),
......@@ -5794,7 +5797,7 @@ pub const FuncGen = struct {
57945797 const mixed_block = try self.wip.block(1, "Mixed");
57955798 const both_pl_block = try self.wip.block(1, "BothNonNull");
57965799 const end_block = try self.wip.block(3, "End");
5797 var wip_switch = try self.wip.@"switch"(lhs_rhs_ored, mixed_block, 2);
5800 var wip_switch = try self.wip.@"switch"(lhs_rhs_ored, mixed_block, 2, .none);
57985801 defer wip_switch.finish(&self.wip);
57995802 try wip_switch.addCase(
58005803 try o.builder.intConst(llvm_i2, 0b00),
......@@ -5948,21 +5951,62 @@ pub const FuncGen = struct {
59485951 const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.then_body_len]);
59495952 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]);
59505953
5954 const Hint = enum {
5955 none,
5956 unpredictable,
5957 then_likely,
5958 else_likely,
5959 then_cold,
5960 else_cold,
5961 };
5962 const hint: Hint = switch (extra.data.branch_hints.true) {
5963 .none => switch (extra.data.branch_hints.false) {
5964 .none => .none,
5965 .likely => .else_likely,
5966 .unlikely => .then_likely,
5967 .cold => .else_cold,
5968 .unpredictable => .unpredictable,
5969 },
5970 .likely => switch (extra.data.branch_hints.false) {
5971 .none => .then_likely,
5972 .likely => .unpredictable,
5973 .unlikely => .then_likely,
5974 .cold => .else_cold,
5975 .unpredictable => .unpredictable,
5976 },
5977 .unlikely => switch (extra.data.branch_hints.false) {
5978 .none => .else_likely,
5979 .likely => .else_likely,
5980 .unlikely => .unpredictable,
5981 .cold => .else_cold,
5982 .unpredictable => .unpredictable,
5983 },
5984 .cold => .then_cold,
5985 .unpredictable => .unpredictable,
5986 };
5987
59515988 const then_block = try self.wip.block(1, "Then");
59525989 const else_block = try self.wip.block(1, "Else");
5953 _ = try self.wip.brCond(cond, then_block, else_block);
5990 _ = try self.wip.brCond(cond, then_block, else_block, switch (hint) {
5991 .none, .then_cold, .else_cold => .none,
5992 .unpredictable => .unpredictable,
5993 .then_likely => .then_likely,
5994 .else_likely => .else_likely,
5995 });
59545996
59555997 self.wip.cursor = .{ .block = then_block };
5998 if (hint == .then_cold) _ = try self.wip.callIntrinsicAssumeCold();
59565999 try self.genBodyDebugScope(null, then_body);
59576000
59586001 self.wip.cursor = .{ .block = else_block };
6002 if (hint == .else_cold) _ = try self.wip.callIntrinsicAssumeCold();
59596003 try self.genBodyDebugScope(null, else_body);
59606004
59616005 // No need to reset the insert cursor since this instruction is noreturn.
59626006 return .none;
59636007 }
59646008
5965 fn airTry(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value {
6009 fn airTry(self: *FuncGen, body_tail: []const Air.Inst.Index, err_cold: bool) !Builder.Value {
59666010 const o = self.ng.object;
59676011 const pt = o.pt;
59686012 const zcu = pt.zcu;
......@@ -5975,10 +6019,10 @@ pub const FuncGen = struct {
59756019 const payload_ty = self.typeOfIndex(inst);
59766020 const can_elide_load = if (isByRef(payload_ty, zcu)) self.canElideLoad(body_tail) else false;
59776021 const is_unused = self.liveness.isUnused(inst);
5978 return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, is_unused);
6022 return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, is_unused, err_cold);
59796023 }
59806024
5981 fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
6025 fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index, err_cold: bool) !Builder.Value {
59826026 const o = self.ng.object;
59836027 const zcu = o.pt.zcu;
59846028 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
......@@ -5987,7 +6031,7 @@ pub const FuncGen = struct {
59876031 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
59886032 const err_union_ty = self.typeOf(extra.data.ptr).childType(zcu);
59896033 const is_unused = self.liveness.isUnused(inst);
5990 return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused);
6034 return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, err_cold);
59916035 }
59926036
59936037 fn lowerTry(
......@@ -5998,6 +6042,7 @@ pub const FuncGen = struct {
59986042 operand_is_ptr: bool,
59996043 can_elide_load: bool,
60006044 is_unused: bool,
6045 err_cold: bool,
60016046 ) !Builder.Value {
60026047 const o = fg.ng.object;
60036048 const pt = o.pt;
......@@ -6036,9 +6081,10 @@ pub const FuncGen = struct {
60366081
60376082 const return_block = try fg.wip.block(1, "TryRet");
60386083 const continue_block = try fg.wip.block(1, "TryCont");
6039 _ = try fg.wip.brCond(is_err, return_block, continue_block);
6084 _ = try fg.wip.brCond(is_err, return_block, continue_block, if (err_cold) .none else .else_likely);
60406085
60416086 fg.wip.cursor = .{ .block = return_block };
6087 if (err_cold) _ = try fg.wip.callIntrinsicAssumeCold();
60426088 try fg.genBodyDebugScope(null, body);
60436089
60446090 fg.wip.cursor = .{ .block = continue_block };
......@@ -6065,9 +6111,11 @@ pub const FuncGen = struct {
60656111
60666112 fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
60676113 const o = self.ng.object;
6068 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6069 const cond = try self.resolveInst(pl_op.operand);
6070 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
6114
6115 const switch_br = self.air.unwrapSwitch(inst);
6116
6117 const cond = try self.resolveInst(switch_br.operand);
6118
60716119 const else_block = try self.wip.block(1, "Default");
60726120 const llvm_usize = try o.lowerType(Type.usize);
60736121 const cond_int = if (cond.typeOfWip(&self.wip).isPointer(&o.builder))
......@@ -6075,34 +6123,70 @@ pub const FuncGen = struct {
60756123 else
60766124 cond;
60776125
6078 var extra_index: usize = switch_br.end;
6079 var case_i: u32 = 0;
6080 var llvm_cases_len: u32 = 0;
6081 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
6082 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
6083 const items: []const Air.Inst.Ref =
6084 @ptrCast(self.air.extra[case.end..][0..case.data.items_len]);
6085 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
6086 extra_index = case.end + case.data.items_len + case_body.len;
6126 const llvm_cases_len = llvm_cases_len: {
6127 var len: u32 = 0;
6128 var it = switch_br.iterateCases();
6129 while (it.next()) |case| len += @intCast(case.items.len);
6130 break :llvm_cases_len len;
6131 };
6132
6133 const weights: Builder.Function.Instruction.BrCond.Weights = weights: {
6134 // First pass. If any weights are `.unpredictable`, unpredictable.
6135 // If all are `.none` or `.cold`, none.
6136 var any_likely = false;
6137 for (0..switch_br.cases_len) |case_idx| {
6138 switch (switch_br.getHint(@intCast(case_idx))) {
6139 .none, .cold => {},
6140 .likely, .unlikely => any_likely = true,
6141 .unpredictable => break :weights .unpredictable,
6142 }
6143 }
6144 switch (switch_br.getElseHint()) {
6145 .none, .cold => {},
6146 .likely, .unlikely => any_likely = true,
6147 .unpredictable => break :weights .unpredictable,
6148 }
6149 if (!any_likely) break :weights .none;
60876150
6088 llvm_cases_len += @intCast(items.len);
6089 }
6151 var weights = try self.gpa.alloc(Builder.Metadata, llvm_cases_len + 1);
6152 defer self.gpa.free(weights);
60906153
6091 var wip_switch = try self.wip.@"switch"(cond_int, else_block, llvm_cases_len);
6092 defer wip_switch.finish(&self.wip);
6154 const else_weight: u32 = switch (switch_br.getElseHint()) {
6155 .unpredictable => unreachable,
6156 .none, .cold => 1000,
6157 .likely => 2000,
6158 .unlikely => 1,
6159 };
6160 weights[0] = try o.builder.metadataConstant(try o.builder.intConst(.i32, else_weight));
6161
6162 var weight_idx: usize = 1;
6163 var it = switch_br.iterateCases();
6164 while (it.next()) |case| {
6165 const weight_val: u32 = switch (switch_br.getHint(case.idx)) {
6166 .unpredictable => unreachable,
6167 .none, .cold => 1000,
6168 .likely => 2000,
6169 .unlikely => 1,
6170 };
6171 const weight_meta = try o.builder.metadataConstant(try o.builder.intConst(.i32, weight_val));
6172 @memset(weights[weight_idx..][0..case.items.len], weight_meta);
6173 weight_idx += case.items.len;
6174 }
6175
6176 assert(weight_idx == weights.len);
60936177
6094 extra_index = switch_br.end;
6095 case_i = 0;
6096 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
6097 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
6098 const items: []const Air.Inst.Ref =
6099 @ptrCast(self.air.extra[case.end..][0..case.data.items_len]);
6100 const case_body: []const Air.Inst.Index = @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]);
6101 extra_index = case.end + case.data.items_len + case_body.len;
6178 const branch_weights_str = try o.builder.metadataString("branch_weights");
6179 const tuple = try o.builder.strTuple(branch_weights_str, weights);
6180 break :weights @enumFromInt(@intFromEnum(tuple));
6181 };
61026182
6103 const case_block = try self.wip.block(@intCast(items.len), "Case");
6183 var wip_switch = try self.wip.@"switch"(cond_int, else_block, llvm_cases_len, weights);
6184 defer wip_switch.finish(&self.wip);
61046185
6105 for (items) |item| {
6186 var it = switch_br.iterateCases();
6187 while (it.next()) |case| {
6188 const case_block = try self.wip.block(@intCast(case.items.len), "Case");
6189 for (case.items) |item| {
61066190 const llvm_item = (try self.resolveInst(item)).toConst().?;
61076191 const llvm_int_item = if (llvm_item.typeOf(&o.builder).isPointer(&o.builder))
61086192 try o.builder.castConst(.ptrtoint, llvm_item, llvm_usize)
......@@ -6110,13 +6194,14 @@ pub const FuncGen = struct {
61106194 llvm_item;
61116195 try wip_switch.addCase(llvm_int_item, case_block, &self.wip);
61126196 }
6113
61146197 self.wip.cursor = .{ .block = case_block };
6115 try self.genBodyDebugScope(null, case_body);
6198 if (switch_br.getHint(case.idx) == .cold) _ = try self.wip.callIntrinsicAssumeCold();
6199 try self.genBodyDebugScope(null, case.body);
61166200 }
61176201
6202 const else_body = it.elseBody();
61186203 self.wip.cursor = .{ .block = else_block };
6119 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);
6204 if (switch_br.getElseHint() == .cold) _ = try self.wip.callIntrinsicAssumeCold();
61206205 if (else_body.len != 0) {
61216206 try self.genBodyDebugScope(null, else_body);
61226207 } else {
......@@ -7748,7 +7833,7 @@ pub const FuncGen = struct {
77487833
77497834 const fail_block = try fg.wip.block(1, "OverflowFail");
77507835 const ok_block = try fg.wip.block(1, "OverflowOk");
7751 _ = try fg.wip.brCond(overflow_bit, fail_block, ok_block);
7836 _ = try fg.wip.brCond(overflow_bit, fail_block, ok_block, .none);
77527837
77537838 fg.wip.cursor = .{ .block = fail_block };
77547839 try fg.buildSimplePanic(.integer_overflow);
......@@ -9389,7 +9474,7 @@ pub const FuncGen = struct {
93899474 self.wip.cursor = .{ .block = loop_block };
93909475 const it_ptr = try self.wip.phi(.ptr, "");
93919476 const end = try self.wip.icmp(.ne, it_ptr.toValue(), end_ptr, "");
9392 _ = try self.wip.brCond(end, body_block, end_block);
9477 _ = try self.wip.brCond(end, body_block, end_block, .none);
93939478
93949479 self.wip.cursor = .{ .block = body_block };
93959480 const elem_abi_align = elem_ty.abiAlignment(zcu);
......@@ -9427,7 +9512,7 @@ pub const FuncGen = struct {
94279512 const cond = try self.cmp(.normal, .neq, Type.usize, len, usize_zero);
94289513 const memset_block = try self.wip.block(1, "MemsetTrapSkip");
94299514 const end_block = try self.wip.block(2, "MemsetTrapEnd");
9430 _ = try self.wip.brCond(cond, memset_block, end_block);
9515 _ = try self.wip.brCond(cond, memset_block, end_block, .none);
94319516 self.wip.cursor = .{ .block = memset_block };
94329517 _ = try self.wip.callMemSet(dest_ptr, dest_ptr_align, fill_byte, len, access_kind);
94339518 _ = try self.wip.br(end_block);
......@@ -9462,7 +9547,7 @@ pub const FuncGen = struct {
94629547 const cond = try self.cmp(.normal, .neq, Type.usize, len, usize_zero);
94639548 const memcpy_block = try self.wip.block(1, "MemcpyTrapSkip");
94649549 const end_block = try self.wip.block(2, "MemcpyTrapEnd");
9465 _ = try self.wip.brCond(cond, memcpy_block, end_block);
9550 _ = try self.wip.brCond(cond, memcpy_block, end_block, .none);
94669551 self.wip.cursor = .{ .block = memcpy_block };
94679552 _ = try self.wip.callMemCpy(
94689553 dest_ptr,
......@@ -9632,7 +9717,7 @@ pub const FuncGen = struct {
96329717 const valid_block = try self.wip.block(@intCast(names.len), "Valid");
96339718 const invalid_block = try self.wip.block(1, "Invalid");
96349719 const end_block = try self.wip.block(2, "End");
9635 var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len));
9720 var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len), .none);
96369721 defer wip_switch.finish(&self.wip);
96379722
96389723 for (0..names.len) |name_index| {
......@@ -9708,7 +9793,7 @@ pub const FuncGen = struct {
97089793 const named_block = try wip.block(@intCast(enum_type.names.len), "Named");
97099794 const unnamed_block = try wip.block(1, "Unnamed");
97109795 const tag_int_value = wip.arg(0);
9711 var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.names.len));
9796 var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.names.len), .none);
97129797 defer wip_switch.finish(&wip);
97139798
97149799 for (0..enum_type.names.len) |field_index| {
......@@ -9858,7 +9943,7 @@ pub const FuncGen = struct {
98589943 const cond = try self.wip.icmp(.ult, i, llvm_vector_len, "");
98599944 const loop_then = try self.wip.block(1, "ReduceLoopThen");
98609945
9861 _ = try self.wip.brCond(cond, loop_then, loop_exit);
9946 _ = try self.wip.brCond(cond, loop_then, loop_exit, .none);
98629947
98639948 {
98649949 self.wip.cursor = .{ .block = loop_then };
src/codegen/llvm/Builder.zig+250-36
......@@ -4817,12 +4817,22 @@ pub const Function = struct {
48174817 cond: Value,
48184818 then: Block.Index,
48194819 @"else": Block.Index,
4820 weights: Weights,
4821 pub const Weights = enum(u32) {
4822 // We can do this as metadata indices 0 and 1 are reserved.
4823 none = 0,
4824 unpredictable = 1,
4825 /// These values should be converted to `Metadata` to be used
4826 /// in a `prof` annotation providing branch weights.
4827 _,
4828 };
48204829 };
48214830
48224831 pub const Switch = struct {
48234832 val: Value,
48244833 default: Block.Index,
48254834 cases_len: u32,
4835 weights: BrCond.Weights,
48264836 //case_vals: [cases_len]Constant,
48274837 //case_blocks: [cases_len]Block.Index,
48284838 };
......@@ -4969,7 +4979,8 @@ pub const Function = struct {
49694979 };
49704980 pub const Info = packed struct(u32) {
49714981 call_conv: CallConv,
4972 _: u22 = undefined,
4982 has_op_bundle_cold: bool,
4983 _: u21 = undefined,
49734984 };
49744985 };
49754986
......@@ -5036,6 +5047,7 @@ pub const Function = struct {
50365047 FunctionAttributes,
50375048 Type,
50385049 Value,
5050 Instruction.BrCond.Weights,
50395051 => @enumFromInt(value),
50405052 MemoryAccessInfo,
50415053 Instruction.Alloca.Info,
......@@ -5201,6 +5213,7 @@ pub const WipFunction = struct {
52015213 cond: Value,
52025214 then: Block.Index,
52035215 @"else": Block.Index,
5216 weights: enum { none, unpredictable, then_likely, else_likely },
52045217 ) Allocator.Error!Instruction.Index {
52055218 assert(cond.typeOfWip(self) == .i1);
52065219 try self.ensureUnusedExtraCapacity(1, Instruction.BrCond, 0);
......@@ -5210,6 +5223,22 @@ pub const WipFunction = struct {
52105223 .cond = cond,
52115224 .then = then,
52125225 .@"else" = @"else",
5226 .weights = switch (weights) {
5227 .none => .none,
5228 .unpredictable => .unpredictable,
5229 .then_likely, .else_likely => w: {
5230 const branch_weights_str = try self.builder.metadataString("branch_weights");
5231 const unlikely_const = try self.builder.metadataConstant(try self.builder.intConst(.i32, 1));
5232 const likely_const = try self.builder.metadataConstant(try self.builder.intConst(.i32, 2000));
5233 const weight_vals: [2]Metadata = switch (weights) {
5234 .none, .unpredictable => unreachable,
5235 .then_likely => .{ likely_const, unlikely_const },
5236 .else_likely => .{ unlikely_const, likely_const },
5237 };
5238 const tuple = try self.builder.strTuple(branch_weights_str, &weight_vals);
5239 break :w @enumFromInt(@intFromEnum(tuple));
5240 },
5241 },
52135242 }),
52145243 });
52155244 then.ptr(self).branches += 1;
......@@ -5248,6 +5277,7 @@ pub const WipFunction = struct {
52485277 val: Value,
52495278 default: Block.Index,
52505279 cases_len: u32,
5280 weights: Instruction.BrCond.Weights,
52515281 ) Allocator.Error!WipSwitch {
52525282 try self.ensureUnusedExtraCapacity(1, Instruction.Switch, cases_len * 2);
52535283 const instruction = try self.addInst(null, .{
......@@ -5256,6 +5286,7 @@ pub const WipFunction = struct {
52565286 .val = val,
52575287 .default = default,
52585288 .cases_len = cases_len,
5289 .weights = weights,
52595290 }),
52605291 });
52615292 _ = self.extra.addManyAsSliceAssumeCapacity(cases_len * 2);
......@@ -5895,6 +5926,20 @@ pub const WipFunction = struct {
58955926 callee: Value,
58965927 args: []const Value,
58975928 name: []const u8,
5929 ) Allocator.Error!Value {
5930 return self.callInner(kind, call_conv, function_attributes, ty, callee, args, name, false);
5931 }
5932
5933 fn callInner(
5934 self: *WipFunction,
5935 kind: Instruction.Call.Kind,
5936 call_conv: CallConv,
5937 function_attributes: FunctionAttributes,
5938 ty: Type,
5939 callee: Value,
5940 args: []const Value,
5941 name: []const u8,
5942 has_op_bundle_cold: bool,
58985943 ) Allocator.Error!Value {
58995944 const ret_ty = ty.functionReturn(self.builder);
59005945 assert(ty.isFunction(self.builder));
......@@ -5918,7 +5963,10 @@ pub const WipFunction = struct {
59185963 .tail_fast => .@"tail call fast",
59195964 },
59205965 .data = self.addExtraAssumeCapacity(Instruction.Call{
5921 .info = .{ .call_conv = call_conv },
5966 .info = .{
5967 .call_conv = call_conv,
5968 .has_op_bundle_cold = has_op_bundle_cold,
5969 },
59225970 .attributes = function_attributes,
59235971 .ty = ty,
59245972 .callee = callee,
......@@ -5964,6 +6012,20 @@ pub const WipFunction = struct {
59646012 );
59656013 }
59666014
6015 pub fn callIntrinsicAssumeCold(self: *WipFunction) Allocator.Error!Value {
6016 const intrinsic = try self.builder.getIntrinsic(.assume, &.{});
6017 return self.callInner(
6018 .normal,
6019 CallConv.default,
6020 .none,
6021 intrinsic.typeOf(self.builder),
6022 intrinsic.toValue(self.builder),
6023 &.{try self.builder.intValue(.i1, 1)},
6024 "",
6025 true,
6026 );
6027 }
6028
59676029 pub fn callMemCpy(
59686030 self: *WipFunction,
59696031 dst: Value,
......@@ -6040,7 +6102,7 @@ pub const WipFunction = struct {
60406102
60416103 break :blk metadata;
60426104 },
6043 .constant => |constant| try self.builder.debugConstant(constant),
6105 .constant => |constant| try self.builder.metadataConstant(constant),
60446106 .metadata => |metadata| metadata,
60456107 };
60466108 }
......@@ -6099,6 +6161,7 @@ pub const WipFunction = struct {
60996161 FunctionAttributes,
61006162 Type,
61016163 Value,
6164 Instruction.BrCond.Weights,
61026165 => @intFromEnum(value),
61036166 MemoryAccessInfo,
61046167 Instruction.Alloca.Info,
......@@ -6380,6 +6443,7 @@ pub const WipFunction = struct {
63806443 .cond = instructions.map(extra.cond),
63816444 .then = extra.then,
63826445 .@"else" = extra.@"else",
6446 .weights = extra.weights,
63836447 });
63846448 },
63856449 .call,
......@@ -6522,6 +6586,7 @@ pub const WipFunction = struct {
65226586 .val = instructions.map(extra.data.val),
65236587 .default = extra.data.default,
65246588 .cases_len = extra.data.cases_len,
6589 .weights = extra.data.weights,
65256590 });
65266591 wip_extra.appendSlice(case_vals);
65276592 wip_extra.appendSlice(case_blocks);
......@@ -6744,6 +6809,7 @@ pub const WipFunction = struct {
67446809 FunctionAttributes,
67456810 Type,
67466811 Value,
6812 Instruction.BrCond.Weights,
67476813 => @intFromEnum(value),
67486814 MemoryAccessInfo,
67496815 Instruction.Alloca.Info,
......@@ -6792,6 +6858,7 @@ pub const WipFunction = struct {
67926858 FunctionAttributes,
67936859 Type,
67946860 Value,
6861 Instruction.BrCond.Weights,
67956862 => @enumFromInt(value),
67966863 MemoryAccessInfo,
67976864 Instruction.Alloca.Info,
......@@ -7735,6 +7802,7 @@ pub const Metadata = enum(u32) {
77357802 enumerator_signed_negative,
77367803 subrange,
77377804 tuple,
7805 str_tuple,
77387806 module_flag,
77397807 expression,
77407808 local_var,
......@@ -7780,6 +7848,7 @@ pub const Metadata = enum(u32) {
77807848 .enumerator_signed_negative,
77817849 .subrange,
77827850 .tuple,
7851 .str_tuple,
77837852 .module_flag,
77847853 .local_var,
77857854 .parameter,
......@@ -8044,6 +8113,13 @@ pub const Metadata = enum(u32) {
80448113 // elements: [elements_len]Metadata
80458114 };
80468115
8116 pub const StrTuple = struct {
8117 str: MetadataString,
8118 elements_len: u32,
8119
8120 // elements: [elements_len]Metadata
8121 };
8122
80478123 pub const ModuleFlag = struct {
80488124 behavior: Metadata,
80498125 name: MetadataString,
......@@ -8455,11 +8531,12 @@ pub fn init(options: Options) Allocator.Error!Builder {
84558531 assert(try self.intConst(.i32, 0) == .@"0");
84568532 assert(try self.intConst(.i32, 1) == .@"1");
84578533 assert(try self.noneConst(.token) == .none);
8458 if (!self.strip) assert(try self.debugNone() == .none);
8534
8535 assert(try self.metadataNone() == .none);
8536 assert(try self.metadataTuple(&.{}) == .empty_tuple);
84598537
84608538 try self.metadata_string_indices.append(self.gpa, 0);
84618539 assert(try self.metadataString("") == .none);
8462 assert(try self.debugTuple(&.{}) == .empty_tuple);
84638540
84648541 return self;
84658542}
......@@ -9685,6 +9762,13 @@ pub fn printUnbuffered(
96859762 extra.then.toInst(&function).fmt(function_index, self),
96869763 extra.@"else".toInst(&function).fmt(function_index, self),
96879764 });
9765 switch (extra.weights) {
9766 .none => {},
9767 .unpredictable => try writer.writeAll(", !unpredictable !{}"),
9768 _ => try writer.print("{}", .{
9769 try metadata_formatter.fmt(", !prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights)))),
9770 }),
9771 }
96889772 },
96899773 .call,
96909774 .@"call fast",
......@@ -9729,6 +9813,9 @@ pub fn printUnbuffered(
97299813 });
97309814 }
97319815 try writer.writeByte(')');
9816 if (extra.data.info.has_op_bundle_cold) {
9817 try writer.writeAll(" [ \"cold\"() ]");
9818 }
97329819 const call_function_attributes = extra.data.attributes.func(self);
97339820 if (call_function_attributes != .none) try writer.print(" #{d}", .{
97349821 (try attribute_groups.getOrPutValue(
......@@ -9939,6 +10026,13 @@ pub fn printUnbuffered(
993910026 },
994010027 );
994110028 try writer.writeAll(" ]");
10029 switch (extra.data.weights) {
10030 .none => {},
10031 .unpredictable => try writer.writeAll(", !unpredictable !{}"),
10032 _ => try writer.print("{}", .{
10033 try metadata_formatter.fmt(", !prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights)))),
10034 }),
10035 }
994210036 },
994310037 .va_arg => |tag| {
994410038 const extra = function.extraData(Function.Instruction.VaArg, instruction.data);
......@@ -10287,6 +10381,17 @@ pub fn printUnbuffered(
1028710381 });
1028810382 try writer.writeAll("}\n");
1028910383 },
10384 .str_tuple => {
10385 var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data);
10386 const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
10387 try writer.print("!{{{[str]%}", .{
10388 .str = try metadata_formatter.fmt("", extra.data.str),
10389 });
10390 for (elements) |element| try writer.print("{[element]%}", .{
10391 .element = try metadata_formatter.fmt("", element),
10392 });
10393 try writer.writeAll("}\n");
10394 },
1029010395 .module_flag => {
1029110396 const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data);
1029210397 try writer.print("!{{{[behavior]%}{[name]%}{[constant]%}}}\n", .{
......@@ -11799,9 +11904,9 @@ pub fn debugNamed(self: *Builder, name: MetadataString, operands: []const Metada
1179911904 self.debugNamedAssumeCapacity(name, operands);
1180011905}
1180111906
11802fn debugNone(self: *Builder) Allocator.Error!Metadata {
11907fn metadataNone(self: *Builder) Allocator.Error!Metadata {
1180311908 try self.ensureUnusedMetadataCapacity(1, NoExtra, 0);
11804 return self.debugNoneAssumeCapacity();
11909 return self.metadataNoneAssumeCapacity();
1180511910}
1180611911
1180711912pub fn debugFile(
......@@ -12090,12 +12195,21 @@ pub fn debugExpression(
1209012195 return self.debugExpressionAssumeCapacity(elements);
1209112196}
1209212197
12093pub fn debugTuple(
12198pub fn metadataTuple(
1209412199 self: *Builder,
1209512200 elements: []const Metadata,
1209612201) Allocator.Error!Metadata {
1209712202 try self.ensureUnusedMetadataCapacity(1, Metadata.Tuple, elements.len);
12098 return self.debugTupleAssumeCapacity(elements);
12203 return self.metadataTupleAssumeCapacity(elements);
12204}
12205
12206pub fn strTuple(
12207 self: *Builder,
12208 str: MetadataString,
12209 elements: []const Metadata,
12210) Allocator.Error!Metadata {
12211 try self.ensureUnusedMetadataCapacity(1, Metadata.StrTuple, elements.len);
12212 return self.strTupleAssumeCapacity(str, elements);
1209912213}
1210012214
1210112215pub fn debugModuleFlag(
......@@ -12166,9 +12280,9 @@ pub fn debugGlobalVarExpression(
1216612280 return self.debugGlobalVarExpressionAssumeCapacity(variable, expression);
1216712281}
1216812282
12169pub fn debugConstant(self: *Builder, value: Constant) Allocator.Error!Metadata {
12283pub fn metadataConstant(self: *Builder, value: Constant) Allocator.Error!Metadata {
1217012284 try self.ensureUnusedMetadataCapacity(1, NoExtra, 0);
12171 return self.debugConstantAssumeCapacity(value);
12285 return self.metadataConstantAssumeCapacity(value);
1217212286}
1217312287
1217412288pub fn debugForwardReferenceSetType(self: *Builder, fwd_ref: Metadata, ty: Metadata) void {
......@@ -12263,8 +12377,7 @@ fn debugNamedAssumeCapacity(self: *Builder, name: MetadataString, operands: []co
1226312377 };
1226412378}
1226512379
12266pub fn debugNoneAssumeCapacity(self: *Builder) Metadata {
12267 assert(!self.strip);
12380pub fn metadataNoneAssumeCapacity(self: *Builder) Metadata {
1226812381 return self.metadataSimpleAssumeCapacity(.none, .{});
1226912382}
1227012383
......@@ -12740,11 +12853,10 @@ fn debugExpressionAssumeCapacity(
1274012853 return @enumFromInt(gop.index);
1274112854}
1274212855
12743fn debugTupleAssumeCapacity(
12856fn metadataTupleAssumeCapacity(
1274412857 self: *Builder,
1274512858 elements: []const Metadata,
1274612859) Metadata {
12747 assert(!self.strip);
1274812860 const Key = struct {
1274912861 elements: []const Metadata,
1275012862 };
......@@ -12787,6 +12899,55 @@ fn debugTupleAssumeCapacity(
1278712899 return @enumFromInt(gop.index);
1278812900}
1278912901
12902fn strTupleAssumeCapacity(
12903 self: *Builder,
12904 str: MetadataString,
12905 elements: []const Metadata,
12906) Metadata {
12907 const Key = struct {
12908 str: MetadataString,
12909 elements: []const Metadata,
12910 };
12911 const Adapter = struct {
12912 builder: *const Builder,
12913 pub fn hash(_: @This(), key: Key) u32 {
12914 var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.tuple)));
12915 hasher.update(std.mem.sliceAsBytes(key.elements));
12916 return @truncate(hasher.final());
12917 }
12918
12919 pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool {
12920 if (.str_tuple != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false;
12921 const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index];
12922 var rhs_extra = ctx.builder.metadataExtraDataTrail(Metadata.StrTuple, rhs_data);
12923 return rhs_extra.data.str == lhs_key.str and std.mem.eql(
12924 Metadata,
12925 lhs_key.elements,
12926 rhs_extra.trail.next(rhs_extra.data.elements_len, Metadata, ctx.builder),
12927 );
12928 }
12929 };
12930
12931 const gop = self.metadata_map.getOrPutAssumeCapacityAdapted(
12932 Key{ .str = str, .elements = elements },
12933 Adapter{ .builder = self },
12934 );
12935
12936 if (!gop.found_existing) {
12937 gop.key_ptr.* = {};
12938 gop.value_ptr.* = {};
12939 self.metadata_items.appendAssumeCapacity(.{
12940 .tag = .str_tuple,
12941 .data = self.addMetadataExtraAssumeCapacity(Metadata.StrTuple{
12942 .str = str,
12943 .elements_len = @intCast(elements.len),
12944 }),
12945 });
12946 self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements));
12947 }
12948 return @enumFromInt(gop.index);
12949}
12950
1279012951fn debugModuleFlagAssumeCapacity(
1279112952 self: *Builder,
1279212953 behavior: Metadata,
......@@ -12877,8 +13038,7 @@ fn debugGlobalVarExpressionAssumeCapacity(
1287713038 });
1287813039}
1287913040
12880fn debugConstantAssumeCapacity(self: *Builder, constant: Constant) Metadata {
12881 assert(!self.strip);
13041fn metadataConstantAssumeCapacity(self: *Builder, constant: Constant) Metadata {
1288213042 const Adapter = struct {
1288313043 builder: *const Builder,
1288413044 pub fn hash(_: @This(), key: Constant) u32 {
......@@ -13757,15 +13917,18 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1375713917 }
1375813918
1375913919 // METADATA_KIND_BLOCK
13760 if (!self.strip) {
13920 {
1376113921 const MetadataKindBlock = ir.MetadataKindBlock;
1376213922 var metadata_kind_block = try module_block.enterSubBlock(MetadataKindBlock, true);
1376313923
1376413924 inline for (@typeInfo(ir.FixedMetadataKind).Enum.fields) |field| {
13765 try metadata_kind_block.writeAbbrev(MetadataKindBlock.Kind{
13766 .id = field.value,
13767 .name = field.name,
13768 });
13925 // don't include `dbg` in stripped functions
13926 if (!(self.strip and std.mem.eql(u8, field.name, "dbg"))) {
13927 try metadata_kind_block.writeAbbrev(MetadataKindBlock.Kind{
13928 .id = field.value,
13929 .name = field.name,
13930 });
13931 }
1376913932 }
1377013933
1377113934 try metadata_kind_block.end();
......@@ -13810,14 +13973,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1381013973 const metadata_adapter = MetadataAdapter.init(self, constant_adapter);
1381113974
1381213975 // METADATA_BLOCK
13813 if (!self.strip) {
13976 {
1381413977 const MetadataBlock = ir.MetadataBlock;
1381513978 var metadata_block = try module_block.enterSubBlock(MetadataBlock, true);
1381613979
1381713980 const MetadataBlockWriter = @TypeOf(metadata_block);
1381813981
1381913982 // Emit all MetadataStrings
13820 {
13983 if (self.metadata_string_map.count() > 1) {
1382113984 const strings_offset, const strings_size = blk: {
1382213985 var strings_offset: u32 = 0;
1382313986 var strings_size: u32 = 0;
......@@ -14087,6 +14250,22 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1408714250 .elements = elements,
1408814251 }, metadata_adapter);
1408914252 },
14253 .str_tuple => {
14254 var extra = self.metadataExtraDataTrail(Metadata.StrTuple, data);
14255
14256 const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
14257
14258 const all_elems = try self.gpa.alloc(Metadata, elements.len + 1);
14259 defer self.gpa.free(all_elems);
14260 all_elems[0] = @enumFromInt(metadata_adapter.getMetadataStringIndex(extra.data.str));
14261 for (elements, all_elems[1..]) |elem, *out_elem| {
14262 out_elem.* = @enumFromInt(metadata_adapter.getMetadataIndex(elem));
14263 }
14264
14265 try metadata_block.writeAbbrev(MetadataBlock.Node{
14266 .elements = all_elems,
14267 });
14268 },
1409014269 .module_flag => {
1409114270 const extra = self.metadataExtraData(Metadata.ModuleFlag, data);
1409214271 try metadata_block.writeAbbrev(MetadataBlock.Node{
......@@ -14188,6 +14367,18 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1418814367 try metadata_block.end();
1418914368 }
1419014369
14370 // OPERAND_BUNDLE_TAGS_BLOCK
14371 {
14372 const OperandBundleTags = ir.OperandBundleTags;
14373 var operand_bundle_tags_block = try module_block.enterSubBlock(OperandBundleTags, true);
14374
14375 try operand_bundle_tags_block.writeAbbrev(OperandBundleTags.OperandBundleTag{
14376 .tag = "cold",
14377 });
14378
14379 try operand_bundle_tags_block.end();
14380 }
14381
1419114382 // Block info
1419214383 {
1419314384 const BlockInfo = ir.BlockInfo;
......@@ -14243,7 +14434,6 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1424314434 .instruction => |instruction| instruction.valueIndex(adapter.func) + adapter.firstInstr(),
1424414435 .constant => |constant| adapter.constant_adapter.getConstantIndex(constant),
1424514436 .metadata => |metadata| {
14246 assert(!adapter.func.strip);
1424714437 const real_metadata = metadata.unwrap(adapter.metadata_adapter.builder);
1424814438 if (@intFromEnum(real_metadata) < Metadata.first_local_metadata)
1424914439 return adapter.metadata_adapter.getMetadataIndex(real_metadata) - 1;
......@@ -14335,6 +14525,10 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1433514525 => |kind| {
1433614526 var extra = func.extraDataTrail(Function.Instruction.Call, data);
1433714527
14528 if (extra.data.info.has_op_bundle_cold) {
14529 try function_block.writeAbbrev(FunctionBlock.ColdOperandBundle{});
14530 }
14531
1433814532 const call_conv = extra.data.info.call_conv;
1433914533 const args = extra.trail.next(extra.data.args_len, Value, &func);
1434014534 try function_block.writeAbbrevAdapted(FunctionBlock.Call{
......@@ -14358,6 +14552,10 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1435814552 => |kind| {
1435914553 var extra = func.extraDataTrail(Function.Instruction.Call, data);
1436014554
14555 if (extra.data.info.has_op_bundle_cold) {
14556 try function_block.writeAbbrev(FunctionBlock.ColdOperandBundle{});
14557 }
14558
1436114559 const call_conv = extra.data.info.call_conv;
1436214560 const args = extra.trail.next(extra.data.args_len, Value, &func);
1436314561 try function_block.writeAbbrevAdapted(FunctionBlock.CallFast{
......@@ -14837,14 +15035,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1483715035 }
1483815036
1483915037 // METADATA_ATTACHMENT_BLOCK
14840 const any_nosanitize = true;
14841 if (!func.strip or any_nosanitize) {
15038 {
1484215039 const MetadataAttachmentBlock = ir.MetadataAttachmentBlock;
1484315040 var metadata_attach_block = try function_block.enterSubBlock(MetadataAttachmentBlock, false);
1484415041
14845 if (!func.strip) blk: {
15042 dbg: {
15043 if (func.strip) break :dbg;
1484615044 const dbg = func.global.ptrConst(self).dbg;
14847 if (dbg == .none) break :blk;
15045 if (dbg == .none) break :dbg;
1484815046 try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentGlobalSingle{
1484915047 .kind = .dbg,
1485015048 .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(dbg) - 1),
......@@ -14852,14 +15050,30 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1485215050 }
1485315051
1485415052 var instr_index: u32 = 0;
14855 for (func.instructions.items(.tag)) |instr_tag| switch (instr_tag) {
14856 .arg, .block => {},
15053 for (func.instructions.items(.tag), func.instructions.items(.data)) |instr_tag, data| switch (instr_tag) {
15054 .arg, .block => {}, // not an actual instruction
1485715055 else => {
14858 try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{
14859 .inst = instr_index,
14860 .kind = .nosanitize,
14861 .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(.empty_tuple) - 1),
14862 });
15056 instr_index += 1;
15057 },
15058 .br_cond, .@"switch" => {
15059 const weights = switch (instr_tag) {
15060 .br_cond => func.extraData(Function.Instruction.BrCond, data).weights,
15061 .@"switch" => func.extraData(Function.Instruction.Switch, data).weights,
15062 else => unreachable,
15063 };
15064 switch (weights) {
15065 .none => {},
15066 .unpredictable => try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{
15067 .inst = instr_index,
15068 .kind = .unpredictable,
15069 .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(.empty_tuple) - 1),
15070 }),
15071 _ => try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{
15072 .inst = instr_index,
15073 .kind = .prof,
15074 .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(@enumFromInt(@intFromEnum(weights))) - 1),
15075 }),
15076 }
1486315077 instr_index += 1;
1486415078 },
1486515079 };
src/codegen/llvm/ir.zig+25-3
......@@ -25,7 +25,7 @@ const BlockAbbrev = AbbrevOp{ .vbr = 6 };
2525pub const FixedMetadataKind = enum(u8) {
2626 dbg = 0,
2727 //tbaa = 1,
28 //prof = 2,
28 prof = 2,
2929 //fpmath = 3,
3030 //range = 4,
3131 //@"tbaa.struct" = 5,
......@@ -38,7 +38,7 @@ pub const FixedMetadataKind = enum(u8) {
3838 //dereferenceable = 12,
3939 //dereferenceable_or_null = 13,
4040 //@"make.implicit" = 14,
41 //unpredictable = 15,
41 unpredictable = 15,
4242 //@"invariant.group" = 16,
4343 //@"align" = 17,
4444 //@"llvm.loop" = 18,
......@@ -54,7 +54,7 @@ pub const FixedMetadataKind = enum(u8) {
5454 //vcall_visibility = 28,
5555 //noundef = 29,
5656 //annotation = 30,
57 nosanitize = 31,
57 //nosanitize = 31,
5858 //func_sanitize = 32,
5959 //exclude = 33,
6060 //memprof = 34,
......@@ -1220,6 +1220,20 @@ pub const MetadataBlock = struct {
12201220 };
12211221};
12221222
1223pub const OperandBundleTags = struct {
1224 pub const id = 21;
1225
1226 pub const abbrevs = [_]type{OperandBundleTag};
1227
1228 pub const OperandBundleTag = struct {
1229 pub const ops = [_]AbbrevOp{
1230 .{ .literal = 1 },
1231 .array_char6,
1232 };
1233 tag: []const u8,
1234 };
1235};
1236
12231237pub const FunctionMetadataBlock = struct {
12241238 pub const id = 15;
12251239
......@@ -1279,6 +1293,7 @@ pub const FunctionBlock = struct {
12791293 Fence,
12801294 DebugLoc,
12811295 DebugLocAgain,
1296 ColdOperandBundle,
12821297 };
12831298
12841299 pub const DeclareBlocks = struct {
......@@ -1791,6 +1806,13 @@ pub const FunctionBlock = struct {
17911806 .{ .literal = 33 },
17921807 };
17931808 };
1809
1810 pub const ColdOperandBundle = struct {
1811 pub const ops = [_]AbbrevOp{
1812 .{ .literal = 55 },
1813 .{ .literal = 0 },
1814 };
1815 };
17941816};
17951817
17961818pub const FunctionValueSymbolTable = struct {
src/codegen/spirv.zig+17-31
......@@ -6173,11 +6173,10 @@ const NavGen = struct {
61736173 const pt = self.pt;
61746174 const zcu = pt.zcu;
61756175 const target = self.getTarget();
6176 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6177 const cond_ty = self.typeOf(pl_op.operand);
6178 const cond = try self.resolve(pl_op.operand);
6176 const switch_br = self.air.unwrapSwitch(inst);
6177 const cond_ty = self.typeOf(switch_br.operand);
6178 const cond = try self.resolve(switch_br.operand);
61796179 var cond_indirect = try self.convertToIndirect(cond_ty, cond);
6180 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
61816180
61826181 const cond_words: u32 = switch (cond_ty.zigTypeTag(zcu)) {
61836182 .Bool, .ErrorSet => 1,
......@@ -6204,18 +6203,15 @@ const NavGen = struct {
62046203 else => return self.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag(zcu))}),
62056204 };
62066205
6207 const num_cases = switch_br.data.cases_len;
6206 const num_cases = switch_br.cases_len;
62086207
62096208 // Compute the total number of arms that we need.
62106209 // Zig switches are grouped by condition, so we need to loop through all of them
62116210 const num_conditions = blk: {
6212 var extra_index: usize = switch_br.end;
62136211 var num_conditions: u32 = 0;
6214 for (0..num_cases) |_| {
6215 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
6216 const case_body = self.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];
6217 extra_index = case.end + case.data.items_len + case_body.len;
6218 num_conditions += case.data.items_len;
6212 var it = switch_br.iterateCases();
6213 while (it.next()) |case| {
6214 num_conditions += @intCast(case.items.len);
62196215 }
62206216 break :blk num_conditions;
62216217 };
......@@ -6244,17 +6240,12 @@ const NavGen = struct {
62446240
62456241 // Emit each of the cases
62466242 {
6247 var extra_index: usize = switch_br.end;
6248 for (0..num_cases) |case_i| {
6243 var it = switch_br.iterateCases();
6244 while (it.next()) |case| {
62496245 // SPIR-V needs a literal here, which' width depends on the case condition.
6250 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
6251 const items: []const Air.Inst.Ref = @ptrCast(self.air.extra[case.end..][0..case.data.items_len]);
6252 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
6253 extra_index = case.end + case.data.items_len + case_body.len;
6254
6255 const label = case_labels.at(case_i);
6246 const label = case_labels.at(case.idx);
62566247
6257 for (items) |item| {
6248 for (case.items) |item| {
62586249 const value = (try self.air.value(item, pt)) orelse unreachable;
62596250 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {
62606251 .Bool, .Int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu),
......@@ -6285,20 +6276,15 @@ const NavGen = struct {
62856276 }
62866277
62876278 // Now, finally, we can start emitting each of the cases.
6288 var extra_index: usize = switch_br.end;
6289 for (0..num_cases) |case_i| {
6290 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
6291 const items: []const Air.Inst.Ref = @ptrCast(self.air.extra[case.end..][0..case.data.items_len]);
6292 const case_body: []const Air.Inst.Index = @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]);
6293 extra_index = case.end + case.data.items_len + case_body.len;
6294
6295 const label = case_labels.at(case_i);
6279 var it = switch_br.iterateCases();
6280 while (it.next()) |case| {
6281 const label = case_labels.at(case.idx);
62966282
62976283 try self.beginSpvBlock(label);
62986284
62996285 switch (self.control_flow) {
63006286 .structured => {
6301 const next_block = try self.genStructuredBody(.selection, case_body);
6287 const next_block = try self.genStructuredBody(.selection, case.body);
63026288 incoming_structured_blocks.appendAssumeCapacity(.{
63036289 .src_label = self.current_block_label,
63046290 .next_block = next_block,
......@@ -6306,12 +6292,12 @@ const NavGen = struct {
63066292 try self.func.body.emitBranch(self.spv.gpa, merge_label.?);
63076293 },
63086294 .unstructured => {
6309 try self.genBody(case_body);
6295 try self.genBody(case.body);
63106296 },
63116297 }
63126298 }
63136299
6314 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);
6300 const else_body = it.elseBody();
63156301 try self.beginSpvBlock(default);
63166302 if (else_body.len != 0) {
63176303 switch (self.control_flow) {
src/print_air.zig+25-21
......@@ -297,8 +297,8 @@ const Writer = struct {
297297 .union_init => try w.writeUnionInit(s, inst),
298298 .br => try w.writeBr(s, inst),
299299 .cond_br => try w.writeCondBr(s, inst),
300 .@"try" => try w.writeTry(s, inst),
301 .try_ptr => try w.writeTryPtr(s, inst),
300 .@"try", .try_cold => try w.writeTry(s, inst),
301 .try_ptr, .try_ptr_cold => try w.writeTryPtr(s, inst),
302302 .switch_br => try w.writeSwitchBr(s, inst),
303303 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
304304 .fence => try w.writeFence(s, inst),
......@@ -825,41 +825,40 @@ const Writer = struct {
825825 }
826826
827827 fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
828 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
829 const switch_br = w.air.extraData(Air.SwitchBr, pl_op.payload);
828 const switch_br = w.air.unwrapSwitch(inst);
829
830830 const liveness = if (w.liveness) |liveness|
831 liveness.getSwitchBr(w.gpa, inst, switch_br.data.cases_len + 1) catch
831 liveness.getSwitchBr(w.gpa, inst, switch_br.cases_len + 1) catch
832832 @panic("out of memory")
833833 else blk: {
834 const slice = w.gpa.alloc([]const Air.Inst.Index, switch_br.data.cases_len + 1) catch
834 const slice = w.gpa.alloc([]const Air.Inst.Index, switch_br.cases_len + 1) catch
835835 @panic("out of memory");
836836 @memset(slice, &.{});
837837 break :blk Liveness.SwitchBrTable{ .deaths = slice };
838838 };
839839 defer w.gpa.free(liveness.deaths);
840 var extra_index: usize = switch_br.end;
841 var case_i: u32 = 0;
842840
843 try w.writeOperand(s, inst, 0, pl_op.operand);
841 try w.writeOperand(s, inst, 0, switch_br.operand);
844842 if (w.skip_body) return s.writeAll(", ...");
845843 const old_indent = w.indent;
846844 w.indent += 2;
847845
848 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
849 const case = w.air.extraData(Air.SwitchBr.Case, extra_index);
850 const items = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra[case.end..][0..case.data.items_len]));
851 const case_body: []const Air.Inst.Index = @ptrCast(w.air.extra[case.end + items.len ..][0..case.data.body_len]);
852 extra_index = case.end + case.data.items_len + case_body.len;
853
846 var it = switch_br.iterateCases();
847 while (it.next()) |case| {
854848 try s.writeAll(", [");
855 for (items, 0..) |item, item_i| {
849 for (case.items, 0..) |item, item_i| {
856850 if (item_i != 0) try s.writeAll(", ");
857851 try w.writeInstRef(s, item, false);
858852 }
859 try s.writeAll("] => {\n");
853 try s.writeAll("] ");
854 const hint = switch_br.getHint(case.idx);
855 if (hint != .none) {
856 try s.print(".{s} ", .{@tagName(hint)});
857 }
858 try s.writeAll("=> {\n");
860859 w.indent += 2;
861860
862 const deaths = liveness.deaths[case_i];
861 const deaths = liveness.deaths[case.idx];
863862 if (deaths.len != 0) {
864863 try s.writeByteNTimes(' ', w.indent);
865864 for (deaths, 0..) |operand, i| {
......@@ -869,15 +868,20 @@ const Writer = struct {
869868 try s.writeAll("\n");
870869 }
871870
872 try w.writeBody(s, case_body);
871 try w.writeBody(s, case.body);
873872 w.indent -= 2;
874873 try s.writeByteNTimes(' ', w.indent);
875874 try s.writeAll("}");
876875 }
877876
878 const else_body: []const Air.Inst.Index = @ptrCast(w.air.extra[extra_index..][0..switch_br.data.else_body_len]);
877 const else_body = it.elseBody();
879878 if (else_body.len != 0) {
880 try s.writeAll(", else => {\n");
879 try s.writeAll(", else ");
880 const hint = switch_br.getElseHint();
881 if (hint != .none) {
882 try s.print(".{s} ", .{@tagName(hint)});
883 }
884 try s.writeAll("=> {\n");
881885 w.indent += 2;
882886
883887 const deaths = liveness.deaths[liveness.deaths.len - 1];
src/print_zir.zig+1-1
......@@ -564,7 +564,6 @@ const Writer = struct {
564564 .fence,
565565 .set_float_mode,
566566 .set_align_stack,
567 .set_cold,
568567 .wasm_memory_size,
569568 .int_from_error,
570569 .error_from_int,
......@@ -573,6 +572,7 @@ const Writer = struct {
573572 .work_item_id,
574573 .work_group_size,
575574 .work_group_id,
575 .branch_hint,
576576 => {
577577 const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
578578 try self.writeInstRef(stream, inst_data.operand);