authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-30 20:29:27+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-01 18:30:31+01:00
log1b000b90c9a7abde3aeacf29cef73a877da237e1
tree837c7481d97c1bbf8d165f2b74b3ae77fbcd2f09
parent49ad51b2feacad394e05d7b5c87c5020c3bc0f5e
signaturelock-open Commit is signed but in an unrecognized format.

Air: direct representation of ranges in switch cases

This commit modifies the representation of the AIR `switch_br` instruction to represent ranges in cases. Previously, Sema emitted different AIR in the case of a range, where the `else` branch of the `switch_br` contained a simple `cond_br` for each such case which did a simple range check (`x > a and x < b`). Not only does this add complexity to Sema, which we would like to minimize, but it also gets in the way of the implementation of #8220. That proposal turns certain `switch` statements into a looping construct, and for optimization purposes, we want to lower this to AIR fairly directly (i.e. without involving a `loop` instruction). That means we would ideally like a single instruction to represent the entire `switch` statement, so that we can dispatch back to it with a different operand as in #8220. This is not really possible to do correctly under the status quo system. This commit implements lowering of this new `switch_br` usage in the LLVM and C backends. The C backend just turns any case containing ranges entirely into conditionals, as before. The LLVM backend is a little smarter, and puts scalar items into the `switch` instruction, only using conditionals for the range cases (which direct to the same bb). All remaining self-hosted backends are temporarily regressed in the presence of switch range cases. This functionality will be restored for at least the x86_64 backend before merge.

12 files changed, 268 insertions(+), 248 deletions(-)

src/Air.zig+10-2
...@@ -1143,10 +1143,12 @@ pub const SwitchBr = struct {...@@ -1143,10 +1143,12 @@ pub const SwitchBr = struct {
1143 else_body_len: u32,1143 else_body_len: u32,
11441144
1145 /// Trailing:1145 /// Trailing:
1146 /// * item: Inst.Ref // for each `items_len`.1146 /// * item: Inst.Ref // for each `items_len`
1147 /// * instruction index for each `body_len`.1147 /// * { range_start: Inst.Ref, range_end: Inst.Ref } // for each `ranges_len`
1148 /// * body_inst: Inst.Index // for each `body_len`
1148 pub const Case = struct {1149 pub const Case = struct {
1149 items_len: u32,1150 items_len: u32,
1151 ranges_len: u32,
1150 body_len: u32,1152 body_len: u32,
1151 };1153 };
1152};1154};
...@@ -1862,6 +1864,10 @@ pub const UnwrappedSwitch = struct {...@@ -1862,6 +1864,10 @@ pub const UnwrappedSwitch = struct {
1862 var extra_index = extra.end;1864 var extra_index = extra.end;
1863 const items: []const Inst.Ref = @ptrCast(it.air.extra[extra_index..][0..extra.data.items_len]);1865 const items: []const Inst.Ref = @ptrCast(it.air.extra[extra_index..][0..extra.data.items_len]);
1864 extra_index += items.len;1866 extra_index += items.len;
1867 // TODO: ptrcast from []const Inst.Ref to []const [2]Inst.Ref when supported
1868 const ranges_ptr: [*]const [2]Inst.Ref = @ptrCast(it.air.extra[extra_index..]);
1869 const ranges: []const [2]Inst.Ref = ranges_ptr[0..extra.data.ranges_len];
1870 extra_index += ranges.len * 2;
1865 const body: []const Inst.Index = @ptrCast(it.air.extra[extra_index..][0..extra.data.body_len]);1871 const body: []const Inst.Index = @ptrCast(it.air.extra[extra_index..][0..extra.data.body_len]);
1866 extra_index += body.len;1872 extra_index += body.len;
1867 it.extra_index = @intCast(extra_index);1873 it.extra_index = @intCast(extra_index);
...@@ -1869,6 +1875,7 @@ pub const UnwrappedSwitch = struct {...@@ -1869,6 +1875,7 @@ pub const UnwrappedSwitch = struct {
1869 return .{1875 return .{
1870 .idx = idx,1876 .idx = idx,
1871 .items = items,1877 .items = items,
1878 .ranges = ranges,
1872 .body = body,1879 .body = body,
1873 };1880 };
1874 }1881 }
...@@ -1881,6 +1888,7 @@ pub const UnwrappedSwitch = struct {...@@ -1881,6 +1888,7 @@ pub const UnwrappedSwitch = struct {
1881 pub const Case = struct {1888 pub const Case = struct {
1882 idx: u32,1889 idx: u32,
1883 items: []const Inst.Ref,1890 items: []const Inst.Ref,
1891 ranges: []const [2]Inst.Ref,
1884 body: []const Inst.Index,1892 body: []const Inst.Index,
1885 };1893 };
1886 };1894 };
src/Air/types_resolved.zig+4
...@@ -386,6 +386,10 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {...@@ -386,6 +386,10 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
386 var it = switch_br.iterateCases();386 var it = switch_br.iterateCases();
387 while (it.next()) |case| {387 while (it.next()) |case| {
388 for (case.items) |item| if (!checkRef(item, zcu)) return false;388 for (case.items) |item| if (!checkRef(item, zcu)) return false;
389 for (case.ranges) |range| {
390 if (!checkRef(range[0], zcu)) return false;
391 if (!checkRef(range[1], zcu)) return false;
392 }
389 if (!checkBody(air, case.body, zcu)) return false;393 if (!checkBody(air, case.body, zcu)) return false;
390 }394 }
391 if (!checkBody(air, it.elseBody(), zcu)) return false;395 if (!checkBody(air, it.elseBody(), zcu)) return false;
src/Sema.zig+143-227
...@@ -11353,9 +11353,14 @@ const SwitchProngAnalysis = struct {...@@ -11353,9 +11353,14 @@ const SwitchProngAnalysis = struct {
11353 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);11353 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
11354 _ = try coerce_block.addBr(capture_block_inst, coerced);11354 _ = try coerce_block.addBr(capture_block_inst, coerced);
1135511355
11356 try cases_extra.ensureUnusedCapacity(3 + coerce_block.instructions.items.len);11356 try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11357 cases_extra.appendAssumeCapacity(1); // items_len11357 1 + // `item`, no ranges
11358 cases_extra.appendAssumeCapacity(@intCast(coerce_block.instructions.items.len)); // body_len11358 coerce_block.instructions.items.len);
11359 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11360 .items_len = 1,
11361 .ranges_len = 0,
11362 .body_len = @intCast(coerce_block.instructions.items.len),
11363 }));
11359 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item11364 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
11360 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body11365 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
11361 }11366 }
...@@ -12578,21 +12583,18 @@ fn analyzeSwitchRuntimeBlock(...@@ -12578,21 +12583,18 @@ fn analyzeSwitchRuntimeBlock(
12578 };12583 };
1257912584
12580 try branch_hints.append(gpa, prong_hint);12585 try branch_hints.append(gpa, prong_hint);
12581 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);12586 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12582 cases_extra.appendAssumeCapacity(1); // items_len12587 1 + // `item`, no ranges
12583 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));12588 case_block.instructions.items.len);
12589 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12590 .items_len = 1,
12591 .ranges_len = 0,
12592 .body_len = @intCast(case_block.instructions.items.len),
12593 }));
12584 cases_extra.appendAssumeCapacity(@intFromEnum(item));12594 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12585 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12595 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12586 }12596 }
1258712597
12588 var is_first = true;
12589 var prev_cond_br: Air.Inst.Index = undefined;
12590 var prev_hint: std.builtin.BranchHint = undefined;
12591 var first_else_body: []const Air.Inst.Index = &.{};
12592 defer gpa.free(first_else_body);
12593 var prev_then_body: []const Air.Inst.Index = &.{};
12594 defer gpa.free(prev_then_body);
12595
12596 var cases_len = scalar_cases_len;12598 var cases_len = scalar_cases_len;
12597 var case_val_idx: usize = scalar_cases_len;12599 var case_val_idx: usize = scalar_cases_len;
12598 var multi_i: u32 = 0;12600 var multi_i: u32 = 0;
...@@ -12602,31 +12604,27 @@ fn analyzeSwitchRuntimeBlock(...@@ -12602,31 +12604,27 @@ fn analyzeSwitchRuntimeBlock(
12602 const ranges_len = sema.code.extra[extra_index];12604 const ranges_len = sema.code.extra[extra_index];
12603 extra_index += 1;12605 extra_index += 1;
12604 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);12606 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12605 extra_index += 1 + items_len;12607 extra_index += 1 + items_len + 2 * ranges_len;
1260612608
12607 const items = case_vals.items[case_val_idx..][0..items_len];12609 const items = case_vals.items[case_val_idx..][0..items_len];
12608 case_val_idx += items_len;12610 case_val_idx += items_len;
12611 // TODO: @ptrCast slice once Sema supports it
12612 const ranges: []const [2]Air.Inst.Ref = @as([*]const [2]Air.Inst.Ref, @ptrCast(case_vals.items[case_val_idx..]))[0..ranges_len];
12613 case_val_idx += ranges_len * 2;
12614
12615 const body = sema.code.bodySlice(extra_index, info.body_len);
12616 extra_index += info.body_len;
1260912617
12610 case_block.instructions.shrinkRetainingCapacity(0);12618 case_block.instructions.shrinkRetainingCapacity(0);
12611 case_block.error_return_trace_index = child_block.error_return_trace_index;12619 case_block.error_return_trace_index = child_block.error_return_trace_index;
1261212620
12613 // Generate all possible cases as scalar prongs.12621 // Generate all possible cases as scalar prongs.
12614 if (info.is_inline) {12622 if (info.is_inline) {
12615 const body_start = extra_index + 2 * ranges_len;
12616 const body = sema.code.bodySlice(body_start, info.body_len);
12617 var emit_bb = false;12623 var emit_bb = false;
1261812624
12619 var range_i: u32 = 0;12625 for (ranges, 0..) |range_items, range_i| {
12620 while (range_i < ranges_len) : (range_i += 1) {12626 var item = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;
12621 const range_items = case_vals.items[case_val_idx..][0..2];12627 const item_last = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;
12622 extra_index += 2;
12623 case_val_idx += 2;
12624
12625 const item_first_ref = range_items[0];
12626 const item_last_ref = range_items[1];
12627
12628 var item = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item_first_ref, undefined) catch unreachable;
12629 const item_last = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item_last_ref, undefined) catch unreachable;
1263012628
12631 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({12629 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
12632 // Previous validation has resolved any possible lazy values.12630 // Previous validation has resolved any possible lazy values.
...@@ -12664,9 +12662,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12664,9 +12662,14 @@ fn analyzeSwitchRuntimeBlock(
12664 );12662 );
12665 try branch_hints.append(gpa, prong_hint);12663 try branch_hints.append(gpa, prong_hint);
1266612664
12667 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);12665 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12668 cases_extra.appendAssumeCapacity(1); // items_len12666 1 + // `item`, no ranges
12669 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));12667 case_block.instructions.items.len);
12668 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12669 .items_len = 1,
12670 .ranges_len = 0,
12671 .body_len = @intCast(case_block.instructions.items.len),
12672 }));
12670 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));12673 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12671 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12674 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1267212675
...@@ -12713,134 +12716,39 @@ fn analyzeSwitchRuntimeBlock(...@@ -12713,134 +12716,39 @@ fn analyzeSwitchRuntimeBlock(
12713 };12716 };
12714 try branch_hints.append(gpa, prong_hint);12717 try branch_hints.append(gpa, prong_hint);
1271512718
12716 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);12719 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12717 cases_extra.appendAssumeCapacity(1); // items_len12720 1 + // `item`, no ranges
12718 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));12721 case_block.instructions.items.len);
12722 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12723 .items_len = 1,
12724 .ranges_len = 0,
12725 .body_len = @intCast(case_block.instructions.items.len),
12726 }));
12719 cases_extra.appendAssumeCapacity(@intFromEnum(item));12727 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12720 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12728 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12721 }12729 }
1272212730
12723 extra_index += info.body_len;
12724 continue;12731 continue;
12725 }12732 }
1272612733
12727 var any_ok: Air.Inst.Ref = .none;12734 cases_len += 1;
12728
12729 // If there are any ranges, we have to put all the items into the
12730 // else prong. Otherwise, we can take advantage of multiple items
12731 // mapping to the same body.
12732 if (ranges_len == 0) {
12733 cases_len += 1;
12734
12735 const analyze_body = if (union_originally)
12736 for (items) |item| {
12737 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
12738 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12739 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
12740 } else false
12741 else
12742 true;
1274312735
12744 const body = sema.code.bodySlice(extra_index, info.body_len);12736 const analyze_body = if (union_originally)
12745 extra_index += info.body_len;
12746 const prong_hint: std.builtin.BranchHint = if (err_set and
12747 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12748 h: {
12749 // nothing to do here. weight against error branch
12750 break :h .unlikely;
12751 } else if (analyze_body) h: {
12752 break :h try spa.analyzeProngRuntime(
12753 &case_block,
12754 .normal,
12755 body,
12756 info.capture,
12757 child_block.src(.{ .switch_capture = .{
12758 .switch_node_offset = switch_node_offset,
12759 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12760 } }),
12761 items,
12762 .none,
12763 false,
12764 );
12765 } else h: {
12766 _ = try case_block.addNoOp(.unreach);
12767 break :h .none;
12768 };
12769
12770 try branch_hints.append(gpa, prong_hint);
12771 try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len +
12772 case_block.instructions.items.len);
12773
12774 cases_extra.appendAssumeCapacity(@intCast(items.len));
12775 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
12776
12777 for (items) |item| {
12778 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12779 }
12780
12781 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12782 } else {
12783 for (items) |item| {12737 for (items) |item| {
12784 const cmp_ok = try case_block.addBinOp(if (case_block.float_mode == .optimized) .cmp_eq_optimized else .cmp_eq, operand, item);12738 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
12785 if (any_ok != .none) {12739 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12786 any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok);12740 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
12787 } else {12741 } else false
12788 any_ok = cmp_ok;12742 else
12789 }12743 true;
12790 }
12791
12792 var range_i: usize = 0;
12793 while (range_i < ranges_len) : (range_i += 1) {
12794 const range_items = case_vals.items[case_val_idx..][0..2];
12795 extra_index += 2;
12796 case_val_idx += 2;
12797
12798 const item_first = range_items[0];
12799 const item_last = range_items[1];
12800
12801 // operand >= first and operand <= last
12802 const range_first_ok = try case_block.addBinOp(
12803 if (case_block.float_mode == .optimized) .cmp_gte_optimized else .cmp_gte,
12804 operand,
12805 item_first,
12806 );
12807 const range_last_ok = try case_block.addBinOp(
12808 if (case_block.float_mode == .optimized) .cmp_lte_optimized else .cmp_lte,
12809 operand,
12810 item_last,
12811 );
12812 const range_ok = try case_block.addBinOp(
12813 .bool_and,
12814 range_first_ok,
12815 range_last_ok,
12816 );
12817 if (any_ok != .none) {
12818 any_ok = try case_block.addBinOp(.bool_or, any_ok, range_ok);
12819 } else {
12820 any_ok = range_ok;
12821 }
12822 }
12823
12824 const new_cond_br = try case_block.addInstAsIndex(.{ .tag = .cond_br, .data = .{
12825 .pl_op = .{
12826 .operand = any_ok,
12827 .payload = undefined,
12828 },
12829 } });
12830 var cond_body = try case_block.instructions.toOwnedSlice(gpa);
12831 defer gpa.free(cond_body);
12832
12833 case_block.instructions.shrinkRetainingCapacity(0);
12834 case_block.error_return_trace_index = child_block.error_return_trace_index;
1283512744
12836 const body = sema.code.bodySlice(extra_index, info.body_len);12745 const prong_hint: std.builtin.BranchHint = if (err_set and
12837 extra_index += info.body_len;12746 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12838 const prong_hint: std.builtin.BranchHint = if (err_set and12747 h: {
12839 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))12748 // nothing to do here. weight against error branch
12840 h: {12749 break :h .unlikely;
12841 // nothing to do here. weight against error branch12750 } else if (analyze_body) h: {
12842 break :h .unlikely;12751 break :h try spa.analyzeProngRuntime(
12843 } else try spa.analyzeProngRuntime(
12844 &case_block,12752 &case_block,
12845 .normal,12753 .normal,
12846 body,12754 body,
...@@ -12853,40 +12761,36 @@ fn analyzeSwitchRuntimeBlock(...@@ -12853,40 +12761,36 @@ fn analyzeSwitchRuntimeBlock(
12853 .none,12761 .none,
12854 false,12762 false,
12855 );12763 );
12764 } else h: {
12765 _ = try case_block.addNoOp(.unreach);
12766 break :h .none;
12767 };
1285612768
12857 if (is_first) {12769 try branch_hints.append(gpa, prong_hint);
12858 is_first = false;
12859 first_else_body = cond_body;
12860 cond_body = &.{};
12861 } else {
12862 try sema.air_extra.ensureUnusedCapacity(
12863 gpa,
12864 @typeInfo(Air.CondBr).@"struct".fields.len + prev_then_body.len + cond_body.len,
12865 );
1286612770
12867 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{12771 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12868 .then_body_len = @intCast(prev_then_body.len),12772 items.len + 2 * ranges_len +
12869 .else_body_len = @intCast(cond_body.len),12773 case_block.instructions.items.len);
12870 .branch_hints = .{12774 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12871 .true = prev_hint,12775 .items_len = @intCast(items.len),
12872 .false = .none,12776 .ranges_len = @intCast(ranges_len),
12873 // Code coverage is desired for error handling.12777 .body_len = @intCast(case_block.instructions.items.len),
12874 .then_cov = .poi,12778 }));
12875 .else_cov = .poi,12779
12876 },12780 for (items) |item| {
12877 });12781 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12878 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));
12879 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cond_body));
12880 }
12881 gpa.free(prev_then_body);
12882 prev_then_body = try case_block.instructions.toOwnedSlice(gpa);
12883 prev_cond_br = new_cond_br;
12884 prev_hint = prong_hint;
12885 }12782 }
12783 for (ranges) |range| {
12784 cases_extra.appendSliceAssumeCapacity(&.{
12785 @intFromEnum(range[0]),
12786 @intFromEnum(range[1]),
12787 });
12788 }
12789
12790 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12886 }12791 }
1288712792
12888 var final_else_body: []const Air.Inst.Index = &.{};12793 const else_body: []const Air.Inst.Index = if (special.body.len != 0 or case_block.wantSafety()) else_body: {
12889 if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
12890 var emit_bb = false;12794 var emit_bb = false;
12891 if (special.is_inline) switch (operand_ty.zigTypeTag(zcu)) {12795 if (special.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
12892 .@"enum" => {12796 .@"enum" => {
...@@ -12933,9 +12837,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12933,9 +12837,14 @@ fn analyzeSwitchRuntimeBlock(
12933 };12837 };
12934 try branch_hints.append(gpa, prong_hint);12838 try branch_hints.append(gpa, prong_hint);
1293512839
12936 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);12840 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12937 cases_extra.appendAssumeCapacity(1); // items_len12841 1 + // `item`, no ranges
12938 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));12842 case_block.instructions.items.len);
12843 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12844 .items_len = 1,
12845 .ranges_len = 0,
12846 .body_len = @intCast(case_block.instructions.items.len),
12847 }));
12939 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));12848 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12940 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12849 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12941 }12850 }
...@@ -12979,9 +12888,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12979,9 +12888,14 @@ fn analyzeSwitchRuntimeBlock(
12979 );12888 );
12980 try branch_hints.append(gpa, prong_hint);12889 try branch_hints.append(gpa, prong_hint);
1298112890
12982 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);12891 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12983 cases_extra.appendAssumeCapacity(1); // items_len12892 1 + // `item`, no ranges
12984 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));12893 case_block.instructions.items.len);
12894 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12895 .items_len = 1,
12896 .ranges_len = 0,
12897 .body_len = @intCast(case_block.instructions.items.len),
12898 }));
12985 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));12899 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12986 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12900 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12987 }12901 }
...@@ -13014,9 +12928,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -13014,9 +12928,14 @@ fn analyzeSwitchRuntimeBlock(
13014 );12928 );
13015 try branch_hints.append(gpa, prong_hint);12929 try branch_hints.append(gpa, prong_hint);
1301612930
13017 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);12931 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
13018 cases_extra.appendAssumeCapacity(1); // items_len12932 1 + // `item`, no ranges
13019 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));12933 case_block.instructions.items.len);
12934 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12935 .items_len = 1,
12936 .ranges_len = 0,
12937 .body_len = @intCast(case_block.instructions.items.len),
12938 }));
13020 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));12939 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
13021 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12940 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
13022 }12941 }
...@@ -13046,9 +12965,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -13046,9 +12965,14 @@ fn analyzeSwitchRuntimeBlock(
13046 );12965 );
13047 try branch_hints.append(gpa, prong_hint);12966 try branch_hints.append(gpa, prong_hint);
1304812967
13049 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);12968 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
13050 cases_extra.appendAssumeCapacity(1); // items_len12969 1 + // `item`, no ranges
13051 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));12970 case_block.instructions.items.len);
12971 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12972 .items_len = 1,
12973 .ranges_len = 0,
12974 .body_len = @intCast(case_block.instructions.items.len),
12975 }));
13052 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true));12976 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true));
13053 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12977 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
13054 }12978 }
...@@ -13076,9 +13000,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -13076,9 +13000,14 @@ fn analyzeSwitchRuntimeBlock(
13076 );13000 );
13077 try branch_hints.append(gpa, prong_hint);13001 try branch_hints.append(gpa, prong_hint);
1307813002
13079 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);13003 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
13080 cases_extra.appendAssumeCapacity(1); // items_len13004 1 + // `item`, no ranges
13081 cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));13005 case_block.instructions.items.len);
13006 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
13007 .items_len = 1,
13008 .ranges_len = 0,
13009 .body_len = @intCast(case_block.instructions.items.len),
13010 }));
13082 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false));13011 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false));
13083 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));13012 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
13084 }13013 }
...@@ -13142,41 +13071,22 @@ fn analyzeSwitchRuntimeBlock(...@@ -13142,41 +13071,22 @@ fn analyzeSwitchRuntimeBlock(
13142 break :h .cold;13071 break :h .cold;
13143 };13072 };
1314413073
13145 if (is_first) {13074 try branch_hints.append(gpa, else_hint);
13146 try branch_hints.append(gpa, else_hint);13075 break :else_body case_block.instructions.items;
13147 final_else_body = case_block.instructions.items;13076 } else else_body: {
13148 } else {
13149 try branch_hints.append(gpa, .none); // we have the range conditionals first
13150 try sema.air_extra.ensureUnusedCapacity(gpa, prev_then_body.len +
13151 @typeInfo(Air.CondBr).@"struct".fields.len + case_block.instructions.items.len);
13152
13153 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{
13154 .then_body_len = @intCast(prev_then_body.len),
13155 .else_body_len = @intCast(case_block.instructions.items.len),
13156 .branch_hints = .{
13157 .true = prev_hint,
13158 .false = else_hint,
13159 .then_cov = .poi,
13160 .else_cov = .poi,
13161 },
13162 });
13163 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));
13164 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
13165 final_else_body = first_else_body;
13166 }
13167 } else {
13168 try branch_hints.append(gpa, .none);13077 try branch_hints.append(gpa, .none);
13169 }13078 break :else_body &.{};
13079 };
1317013080
13171 assert(branch_hints.items.len == cases_len + 1);13081 assert(branch_hints.items.len == cases_len + 1);
1317213082
13173 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +13083 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
13174 cases_extra.items.len + final_else_body.len +13084 cases_extra.items.len + else_body.len +
13175 (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints13085 (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints
1317613086
13177 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{13087 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
13178 .cases_len = @intCast(cases_len),13088 .cases_len = @intCast(cases_len),
13179 .else_body_len = @intCast(final_else_body.len),13089 .else_body_len = @intCast(else_body.len),
13180 });13090 });
1318113091
13182 {13092 {
...@@ -13195,7 +13105,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -13195,7 +13105,7 @@ fn analyzeSwitchRuntimeBlock(
13195 }13105 }
13196 }13106 }
13197 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));13107 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));
13198 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body));13108 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_body));
1319913109
13200 return try child_block.addInst(.{13110 return try child_block.addInst(.{
13201 .tag = .switch_br,13111 .tag = .switch_br,
...@@ -37386,15 +37296,21 @@ pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {...@@ -37386,15 +37296,21 @@ pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
37386}37296}
3738737297
37388pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {37298pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
37389 const fields = std.meta.fields(@TypeOf(extra));
37390 const result: u32 = @intCast(sema.air_extra.items.len);37299 const result: u32 = @intCast(sema.air_extra.items.len);
37391 inline for (fields) |field| {37300 sema.air_extra.appendSliceAssumeCapacity(&payloadToExtraItems(extra));
37392 sema.air_extra.appendAssumeCapacity(switch (field.type) {37301 return result;
37393 u32 => @field(extra, field.name),37302}
37394 i32, Air.CondBr.BranchHints => @bitCast(@field(extra, field.name)),37303
37395 Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(extra, field.name)),37304fn payloadToExtraItems(data: anytype) [@typeInfo(@TypeOf(data)).@"struct".fields.len]u32 {
37305 const fields = @typeInfo(@TypeOf(data)).@"struct".fields;
37306 var result: [fields.len]u32 = undefined;
37307 inline for (&result, fields) |*val, field| {
37308 val.* = switch (field.type) {
37309 u32 => @field(data, field.name),
37310 i32, Air.CondBr.BranchHints => @bitCast(@field(data, field.name)),
37311 Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(data, field.name)),
37396 else => @compileError("bad field type: " ++ @typeName(field.type)),37312 else => @compileError("bad field type: " ++ @typeName(field.type)),
37397 });37313 };
37398 }37314 }
37399 return result;37315 return result;
37400}37316}
src/arch/aarch64/CodeGen.zig+2
...@@ -5105,6 +5105,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5105,6 +5105,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
51055105
5106 var it = switch_br.iterateCases();5106 var it = switch_br.iterateCases();
5107 while (it.next()) |case| {5107 while (it.next()) |case| {
5108 if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{});
5109
5108 // For every item, we compare it to condition and branch into5110 // For every item, we compare it to condition and branch into
5109 // the prong if they are equal. After we compared to all5111 // the prong if they are equal. After we compared to all
5110 // items, we branch into the next prong (or if no other prongs5112 // items, we branch into the next prong (or if no other prongs
src/arch/arm/CodeGen.zig+1
...@@ -5053,6 +5053,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5053,6 +5053,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
50535053
5054 var it = switch_br.iterateCases();5054 var it = switch_br.iterateCases();
5055 while (it.next()) |case| {5055 while (it.next()) |case| {
5056 if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{});
5056 // For every item, we compare it to condition and branch into5057 // For every item, we compare it to condition and branch into
5057 // the prong if they are equal. After we compared to all5058 // the prong if they are equal. After we compared to all
5058 // items, we branch into the next prong (or if no other prongs5059 // items, we branch into the next prong (or if no other prongs
src/arch/riscv64/CodeGen.zig+2
...@@ -5681,6 +5681,8 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {...@@ -5681,6 +5681,8 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
56815681
5682 var it = switch_br.iterateCases();5682 var it = switch_br.iterateCases();
5683 while (it.next()) |case| {5683 while (it.next()) |case| {
5684 if (case.ranges.len > 0) return func.fail("TODO: switch with ranges", .{});
5685
5684 var relocs = try func.gpa.alloc(Mir.Inst.Index, case.items.len);5686 var relocs = try func.gpa.alloc(Mir.Inst.Index, case.items.len);
5685 defer func.gpa.free(relocs);5687 defer func.gpa.free(relocs);
56865688
src/arch/wasm/CodeGen.zig+2
...@@ -4064,6 +4064,8 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4064,6 +4064,8 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
40644064
4065 var it = switch_br.iterateCases();4065 var it = switch_br.iterateCases();
4066 while (it.next()) |case| {4066 while (it.next()) |case| {
4067 if (case.ranges.len > 0) return func.fail("TODO: switch with ranges", .{});
4068
4067 const values = try func.gpa.alloc(CaseValue, case.items.len);4069 const values = try func.gpa.alloc(CaseValue, case.items.len);
4068 errdefer func.gpa.free(values);4070 errdefer func.gpa.free(values);
40694071
src/arch/x86_64/CodeGen.zig+2
...@@ -13695,6 +13695,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -13695,6 +13695,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
1369513695
13696 var it = switch_br.iterateCases();13696 var it = switch_br.iterateCases();
13697 while (it.next()) |case| {13697 while (it.next()) |case| {
13698 if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{});
13699
13698 var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len);13700 var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len);
13699 defer self.gpa.free(relocs);13701 defer self.gpa.free(relocs);
1370013702
src/codegen/c.zig+43-15
...@@ -5017,12 +5017,13 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5017,12 +5017,13 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
5017 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1);5017 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1);
5018 defer gpa.free(liveness.deaths);5018 defer gpa.free(liveness.deaths);
50195019
5020 // On the final iteration we do not need to fix any state. This is because, like in the `else`5020 var any_range_cases = false;
5021 // branch of a `cond_br`, our parent has to do it for this entire body anyway.
5022 const last_case_i = switch_br.cases_len - @intFromBool(switch_br.else_body_len == 0);
5023
5024 var it = switch_br.iterateCases();5021 var it = switch_br.iterateCases();
5025 while (it.next()) |case| {5022 while (it.next()) |case| {
5023 if (case.ranges.len > 0) {
5024 any_range_cases = true;
5025 continue;
5026 }
5026 for (case.items) |item| {5027 for (case.items) |item| {
5027 try f.object.indent_writer.insertNewline();5028 try f.object.indent_writer.insertNewline();
5028 try writer.writeAll("case ");5029 try writer.writeAll("case ");
...@@ -5041,29 +5042,56 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5041,29 +5042,56 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
5041 }5042 }
5042 try writer.writeByte(' ');5043 try writer.writeByte(' ');
50435044
5044 if (case.idx != last_case_i) {5045 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, false);
5045 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, false);
5046 } else {
5047 for (liveness.deaths[case.idx]) |death| {
5048 try die(f, inst, death.toRef());
5049 }
5050 try genBody(f, case.body);
5051 }
50525046
5053 // The case body must be noreturn so we don't need to insert a break.5047 // The case body must be noreturn so we don't need to insert a break.
5054 }5048 }
50555049
5056 const else_body = it.elseBody();5050 const else_body = it.elseBody();
5057 try f.object.indent_writer.insertNewline();5051 try f.object.indent_writer.insertNewline();
5052
5053 try writer.writeAll("default: ");
5054 if (any_range_cases) {
5055 // We will iterate the cases again to handle those with ranges, and generate
5056 // code using conditions rather than switch cases for such cases.
5057 it = switch_br.iterateCases();
5058 while (it.next()) |case| {
5059 if (case.ranges.len == 0) continue; // handled above
5060
5061 try writer.writeAll("if (");
5062 for (case.items, 0..) |item, item_i| {
5063 if (item_i != 0) try writer.writeAll(" || ");
5064 try f.writeCValue(writer, condition, .Other);
5065 try writer.writeAll(" == ");
5066 try f.object.dg.renderValue(writer, (try f.air.value(item, pt)).?, .Other);
5067 }
5068 for (case.ranges, 0..) |range, range_i| {
5069 if (case.items.len != 0 or range_i != 0) try writer.writeAll(" || ");
5070 // "(x >= lower && x <= upper)"
5071 try writer.writeByte('(');
5072 try f.writeCValue(writer, condition, .Other);
5073 try writer.writeAll(" >= ");
5074 try f.object.dg.renderValue(writer, (try f.air.value(range[0], pt)).?, .Other);
5075 try writer.writeAll(" && ");
5076 try f.writeCValue(writer, condition, .Other);
5077 try writer.writeAll(" <= ");
5078 try f.object.dg.renderValue(writer, (try f.air.value(range[1], pt)).?, .Other);
5079 try writer.writeByte(')');
5080 }
5081 try writer.writeAll(") ");
5082 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, false);
5083 }
5084 }
5085
5058 if (else_body.len > 0) {5086 if (else_body.len > 0) {
5059 // Note that this must be the last case (i.e. the `last_case_i` case was not hit above)5087 // Note that this must be the last case, so we do not need to use `genBodyResolveState` since
5088 // the parent block will do it (because the case body is noreturn).
5060 for (liveness.deaths[liveness.deaths.len - 1]) |death| {5089 for (liveness.deaths[liveness.deaths.len - 1]) |death| {
5061 try die(f, inst, death.toRef());5090 try die(f, inst, death.toRef());
5062 }5091 }
5063 try writer.writeAll("default: ");
5064 try genBody(f, else_body);5092 try genBody(f, else_body);
5065 } else {5093 } else {
5066 try writer.writeAll("default: zig_unreachable();");5094 try writer.writeAll("zig_unreachable();");
5067 }5095 }
5068 try f.object.indent_writer.insertNewline();5096 try f.object.indent_writer.insertNewline();
50695097
src/codegen/llvm.zig+52-4
...@@ -6230,7 +6230,15 @@ pub const FuncGen = struct {...@@ -6230,7 +6230,15 @@ pub const FuncGen = struct {
62306230
6231 const cond = try self.resolveInst(switch_br.operand);6231 const cond = try self.resolveInst(switch_br.operand);
62326232
6233 const else_block = try self.wip.block(1, "Default");6233 // This is not necessarily the actual `else` prong; it first contains conditionals
6234 // for any range cases. It's just the `else` of the LLVM switch.
6235 const llvm_else_block = try self.wip.block(1, "Default");
6236
6237 const case_blocks = try self.gpa.alloc(Builder.Function.Block.Index, switch_br.cases_len);
6238 defer self.gpa.free(case_blocks);
6239 // We set incoming as 0 for now, and increment it as we construct the switch.
6240 for (case_blocks) |*b| b.* = try self.wip.block(0, "Case");
6241
6234 const llvm_usize = try o.lowerType(Type.usize);6242 const llvm_usize = try o.lowerType(Type.usize);
6235 const cond_int = if (cond.typeOfWip(&self.wip).isPointer(&o.builder))6243 const cond_int = if (cond.typeOfWip(&self.wip).isPointer(&o.builder))
6236 try self.wip.cast(.ptrtoint, cond, llvm_usize, "")6244 try self.wip.cast(.ptrtoint, cond, llvm_usize, "")
...@@ -6294,12 +6302,17 @@ pub const FuncGen = struct {...@@ -6294,12 +6302,17 @@ pub const FuncGen = struct {
6294 break :weights @enumFromInt(@intFromEnum(tuple));6302 break :weights @enumFromInt(@intFromEnum(tuple));
6295 };6303 };
62966304
6297 var wip_switch = try self.wip.@"switch"(cond_int, else_block, llvm_cases_len, weights);6305 var wip_switch = try self.wip.@"switch"(cond_int, llvm_else_block, llvm_cases_len, weights);
6298 defer wip_switch.finish(&self.wip);6306 defer wip_switch.finish(&self.wip);
62996307
6300 var it = switch_br.iterateCases();6308 var it = switch_br.iterateCases();
6309 var any_ranges = false;
6301 while (it.next()) |case| {6310 while (it.next()) |case| {
6302 const case_block = try self.wip.block(@intCast(case.items.len), "Case");6311 if (case.ranges.len > 0) any_ranges = true;
6312 const case_block = case_blocks[case.idx];
6313 case_block.ptr(&self.wip).incoming += @intCast(case.items.len);
6314 // Handle scalar items, and generate the block.
6315 // We'll generate conditionals for the ranges later on.
6303 for (case.items) |item| {6316 for (case.items) |item| {
6304 const llvm_item = (try self.resolveInst(item)).toConst().?;6317 const llvm_item = (try self.resolveInst(item)).toConst().?;
6305 const llvm_int_item = if (llvm_item.typeOf(&o.builder).isPointer(&o.builder))6318 const llvm_int_item = if (llvm_item.typeOf(&o.builder).isPointer(&o.builder))
...@@ -6314,7 +6327,42 @@ pub const FuncGen = struct {...@@ -6314,7 +6327,42 @@ pub const FuncGen = struct {
6314 }6327 }
63156328
6316 const else_body = it.elseBody();6329 const else_body = it.elseBody();
6317 self.wip.cursor = .{ .block = else_block };6330 self.wip.cursor = .{ .block = llvm_else_block };
6331 if (any_ranges) {
6332 const cond_ty = self.typeOf(switch_br.operand);
6333 // Add conditionals for the ranges, directing to the relevant bb.
6334 // We don't need to consider `cold` branch hints since that information is stored
6335 // in the target bb body, but we do care about likely/unlikely/unpredictable.
6336 it = switch_br.iterateCases();
6337 while (it.next()) |case| {
6338 if (case.ranges.len == 0) continue;
6339 const case_block = case_blocks[case.idx];
6340 const hint = switch_br.getHint(case.idx);
6341 case_block.ptr(&self.wip).incoming += 1;
6342 const next_else_block = try self.wip.block(1, "Default");
6343 var range_cond: ?Builder.Value = null;
6344 for (case.ranges) |range| {
6345 const llvm_min = try self.resolveInst(range[0]);
6346 const llvm_max = try self.resolveInst(range[1]);
6347 const cond_part = try self.wip.bin(
6348 .@"and",
6349 try self.cmp(.normal, .gte, cond_ty, cond, llvm_min),
6350 try self.cmp(.normal, .lte, cond_ty, cond, llvm_max),
6351 "",
6352 );
6353 if (range_cond) |prev| {
6354 range_cond = try self.wip.bin(.@"or", prev, cond_part, "");
6355 } else range_cond = cond_part;
6356 }
6357 _ = try self.wip.brCond(range_cond.?, case_block, next_else_block, switch (hint) {
6358 .none, .cold => .none,
6359 .unpredictable => .unpredictable,
6360 .likely => .then_likely,
6361 .unlikely => .else_likely,
6362 });
6363 self.wip.cursor = .{ .block = next_else_block };
6364 }
6365 }
6318 if (switch_br.getElseHint() == .cold) _ = try self.wip.callIntrinsicAssumeCold();6366 if (switch_br.getElseHint() == .cold) _ = try self.wip.callIntrinsicAssumeCold();
6319 if (else_body.len != 0) {6367 if (else_body.len != 0) {
6320 try self.genBodyDebugScope(null, else_body, .poi);6368 try self.genBodyDebugScope(null, else_body, .poi);
src/codegen/spirv.zig+1
...@@ -6211,6 +6211,7 @@ const NavGen = struct {...@@ -6211,6 +6211,7 @@ const NavGen = struct {
6211 var num_conditions: u32 = 0;6211 var num_conditions: u32 = 0;
6212 var it = switch_br.iterateCases();6212 var it = switch_br.iterateCases();
6213 while (it.next()) |case| {6213 while (it.next()) |case| {
6214 if (case.ranges.len > 0) return self.todo("switch with ranges", .{});
6214 num_conditions += @intCast(case.items.len);6215 num_conditions += @intCast(case.items.len);
6215 }6216 }
6216 break :blk num_conditions;6217 break :blk num_conditions;
src/print_air.zig+6
...@@ -864,6 +864,12 @@ const Writer = struct {...@@ -864,6 +864,12 @@ const Writer = struct {
864 if (item_i != 0) try s.writeAll(", ");864 if (item_i != 0) try s.writeAll(", ");
865 try w.writeInstRef(s, item, false);865 try w.writeInstRef(s, item, false);
866 }866 }
867 for (case.ranges, 0..) |range, range_i| {
868 if (range_i != 0 or case.items.len != 0) try s.writeAll(", ");
869 try w.writeInstRef(s, range[0], false);
870 try s.writeAll("...");
871 try w.writeInstRef(s, range[1], false);
872 }
867 try s.writeAll("] ");873 try s.writeAll("] ");
868 const hint = switch_br.getHint(case.idx);874 const hint = switch_br.getHint(case.idx);
869 if (hint != .none) {875 if (hint != .none) {