authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 23:12:26-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 23:12:26-07:00
log623d5f442c832ec0ea2a07aba73b8e2eae57191c
treebea996aaf34c60b2be183754415905ee1c77c2f4
parent281a7baaeac6b6b3c8c78124f5e484f7ee101cf0

stage2: guidance on how to implement switch expressions

Here's what I think the ZIR should be. AstGen is not yet implemented to match this, and the main implementation of analyzeSwitch in Sema is not yet implemented to match it either. Here are some example byte size reductions from master branch, with the ZIR memory layout from this commit: ``` switch (foo) { a => 1, b => 2, c => 3, d => 4, } ``` 184 bytes (master) => 40 bytes (this branch) ``` switch (foo) { a, b => 1, c..d, e, f => 2, g => 3, else => 4, } ``` 240 bytes (master) => 80 bytes (this branch)

4 files changed, 226 insertions(+), 98 deletions(-)

src/AstGen.zig+15-4
......@@ -1257,6 +1257,18 @@ fn blockExprStmts(
12571257 .break_inline,
12581258 .condbr,
12591259 .condbr_inline,
1260 .switch_br,
1261 .switch_br_range,
1262 .switch_br_else,
1263 .switch_br_else_range,
1264 .switch_br_underscore,
1265 .switch_br_underscore_range,
1266 .switch_br_ref,
1267 .switch_br_ref_range,
1268 .switch_br_ref_else,
1269 .switch_br_ref_else_range,
1270 .switch_br_ref_underscore,
1271 .switch_br_ref_underscore_range,
12601272 .compile_error,
12611273 .ret_node,
12621274 .ret_tok,
......@@ -2536,20 +2548,19 @@ fn switchExpr(
25362548 rl: ResultLoc,
25372549 switch_node: ast.Node.Index,
25382550) InnerError!zir.Inst.Ref {
2539 if (true) @panic("TODO update for zir-memory-layout");
25402551 const tree = parent_gz.tree();
25412552 const node_datas = tree.nodes.items(.data);
25422553 const main_tokens = tree.nodes.items(.main_token);
25432554 const token_tags = tree.tokens.items(.tag);
25442555 const node_tags = tree.nodes.items(.tag);
25452556
2557 if (true) @panic("TODO rework for zir-memory-layout branch");
2558
25462559 const switch_token = main_tokens[switch_node];
25472560 const target_node = node_datas[switch_node].lhs;
25482561 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
25492562 const case_nodes = tree.extra_data[extra.start..extra.end];
25502563
2551 const switch_src = token_starts[switch_token];
2552
25532564 var block_scope: GenZir = .{
25542565 .parent = scope,
25552566 .decl = scope.ownerDecl().?,
......@@ -2627,7 +2638,7 @@ fn switchExpr(
26272638 const msg = msg: {
26282639 const msg = try mod.errMsg(
26292640 scope,
2630 switch_src,
2641 parent_gz.nodeSrcLoc(switch_node),
26312642 "else and '_' prong in switch expression",
26322643 .{},
26332644 );
src/Module.zig+9
......@@ -1532,6 +1532,7 @@ pub const SrcLoc = struct {
15321532 .node_offset_bin_op,
15331533 .node_offset_bin_lhs,
15341534 .node_offset_bin_rhs,
1535 .node_offset_switch_operand,
15351536 => src_loc.container.decl.container.file_scope,
15361537 };
15371538 }
......@@ -1663,6 +1664,7 @@ pub const SrcLoc = struct {
16631664 const token_starts = tree.tokens.items(.start);
16641665 return token_starts[tok_index];
16651666 },
1667 .node_offset_switch_operand => @panic("TODO"),
16661668 }
16671669 }
16681670};
......@@ -1795,6 +1797,11 @@ pub const LazySrcLoc = union(enum) {
17951797 /// which points to a binary expression AST node. Next, nagivate to the RHS.
17961798 /// The Decl is determined contextually.
17971799 node_offset_bin_rhs: i32,
1800 /// The source location points to the operand of a switch expression, found
1801 /// by taking this AST node index offset from the containing Decl AST node,
1802 /// which points to a switch expression AST node. Next, nagivate to the operand.
1803 /// The Decl is determined contextually.
1804 node_offset_switch_operand: i32,
17981805
17991806 /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
18001807 pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
......@@ -1828,6 +1835,7 @@ pub const LazySrcLoc = union(enum) {
18281835 .node_offset_bin_op,
18291836 .node_offset_bin_lhs,
18301837 .node_offset_bin_rhs,
1838 .node_offset_switch_operand,
18311839 => .{
18321840 .container = .{ .decl = scope.srcDecl().? },
18331841 .lazy = lazy,
......@@ -1867,6 +1875,7 @@ pub const LazySrcLoc = union(enum) {
18671875 .node_offset_bin_op,
18681876 .node_offset_bin_lhs,
18691877 .node_offset_bin_rhs,
1878 .node_offset_switch_operand,
18701879 => .{
18711880 .container = .{ .decl = decl },
18721881 .lazy = lazy,
src/Sema.zig+89-53
......@@ -229,10 +229,6 @@ pub fn analyzeBody(
229229 .typeof => try sema.zirTypeof(block, inst),
230230 .typeof_peer => try sema.zirTypeofPeer(block, inst),
231231 .xor => try sema.zirBitwise(block, inst, .xor),
232 // TODO
233 //.switchbr => try sema.zirSwitchBr(block, inst, false),
234 //.switchbr_ref => try sema.zirSwitchBr(block, inst, true),
235 //.switch_range => try sema.zirSwitchRange(block, inst),
236232
237233 // Instructions that we know to *always* be noreturn based solely on their tag.
238234 // These functions match the return type of analyzeBody so that we can
......@@ -246,6 +242,18 @@ pub fn analyzeBody(
246242 .ret_tok => return sema.zirRetTok(block, inst, false),
247243 .@"unreachable" => return sema.zirUnreachable(block, inst),
248244 .repeat => return sema.zirRepeat(block, inst),
245 .switch_br => return sema.zirSwitchBr(block, inst, false, .full),
246 .switch_br_range => return sema.zirSwitchBrRange(block, inst, false, .full),
247 .switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"),
248 .switch_br_else_range => return sema.zirSwitchBrRange(block, inst, false, .@"else"),
249 .switch_br_underscore => return sema.zirSwitchBr(block, inst, false, .under),
250 .switch_br_underscore_range => return sema.zirSwitchBrRange(block, inst, false, .under),
251 .switch_br_ref => return sema.zirSwitchBr(block, inst, true, .full),
252 .switch_br_ref_range => return sema.zirSwitchBrRange(block, inst, true, .full),
253 .switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"),
254 .switch_br_ref_else_range => return sema.zirSwitchBrRange(block, inst, true, .@"else"),
255 .switch_br_ref_underscore => return sema.zirSwitchBr(block, inst, true, .under),
256 .switch_br_ref_underscore_range => return sema.zirSwitchBrRange(block, inst, true, .under),
249257
250258 // Instructions that we know can *never* be noreturn based solely on
251259 // their tag. We avoid needlessly checking if they are noreturn and
......@@ -2197,54 +2205,82 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
21972205 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
21982206}
21992207
2200fn zirSwitchRange(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
2208const ElseProng = enum { full, @"else", under };
2209
2210fn zirSwitchBr(
2211 sema: *Sema,
2212 block: *Scope.Block,
2213 inst: zir.Inst.Index,
2214 is_ref: bool,
2215 else_prong: ElseProng,
2216) InnerError!zir.Inst.Index {
22012217 const tracy = trace(@src());
22022218 defer tracy.end();
22032219
2204 const src: LazySrcLoc = .todo;
2205 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
2206 const start = try sema.resolveInst(bin_inst.lhs);
2207 const end = try sema.resolveInst(bin_inst.rhs);
2220 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2221 const src = inst_data.src();
2222 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2223 const extra = sema.code.extraData(zir.Inst.SwitchBr, inst_data.payload_index);
22082224
2209 switch (start.ty.zigTypeTag()) {
2210 .Int, .ComptimeInt => {},
2211 else => return sema.mod.constVoid(sema.arena, .unneeded),
2212 }
2213 switch (end.ty.zigTypeTag()) {
2214 .Int, .ComptimeInt => {},
2215 else => return sema.mod.constVoid(sema.arena, .unneeded),
2216 }
2217 // .switch_range must be inside a comptime scope
2218 const start_val = start.value().?;
2219 const end_val = end.value().?;
2220 if (start_val.compare(.gte, end_val)) {
2221 return sema.mod.fail(&block.base, src, "range start value must be smaller than the end value", .{});
2222 }
2223 return sema.mod.constVoid(sema.arena, .unneeded);
2225 const operand_ptr = try sema.resolveInst(extra.data.operand);
2226 const operand = if (is_ref)
2227 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
2228 else
2229 operand_ptr;
2230
2231 return sema.analyzeSwitch(block, operand, extra.end, else_prong, extra.data.cases_len, 0, 0);
22242232}
22252233
2226fn zirSwitchBr(
2234fn zirSwitchBrRange(
22272235 sema: *Sema,
2228 parent_block: *Scope.Block,
2236 block: *Scope.Block,
22292237 inst: zir.Inst.Index,
2230 ref: bool,
2231) InnerError!zir.Inst.Ref {
2238 is_ref: bool,
2239 else_prong: ElseProng,
2240) InnerError!zir.Inst.Index {
22322241 const tracy = trace(@src());
22332242 defer tracy.end();
22342243
2235 if (true) @panic("TODO rework with zir-memory-layout in mind");
2244 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2245 const src = inst_data.src();
2246 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2247 const extra = sema.code.extraData(zir.Inst.SwitchBrRange, inst_data.payload_index);
22362248
2237 const target_ptr = try sema.resolveInst(inst.positionals.target);
2238 const target = if (ref)
2239 try sema.analyzeLoad(parent_block, inst.base.src, target_ptr, inst.positionals.target.src)
2249 const operand_ptr = try sema.resolveInst(extra.data.operand);
2250 const operand = if (is_ref)
2251 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
22402252 else
2241 target_ptr;
2242 try sema.validateSwitch(parent_block, target, inst);
2253 operand_ptr;
2254
2255 return sema.analyzeSwitch(
2256 block,
2257 operand,
2258 extra.end,
2259 else_prong,
2260 extra.data.scalar_cases_len,
2261 extra.data.multi_cases_len,
2262 extra.data.range_cases_len,
2263 );
2264}
22432265
2244 if (try sema.resolveDefinedValue(parent_block, inst.base.src, target)) |target_val| {
2266fn analyzeSwitch(
2267 sema: *Sema,
2268 parent_block: *Scope.Block,
2269 operand: *Inst,
2270 extra_end: usize,
2271 else_prong: ElseProng,
2272 scalar_cases_len: usize,
2273 multi_cases_len: usize,
2274 range_cases_len: usize,
2275) InnerError!zir.Inst.Index {
2276 if (true) @panic("TODO rework for zir-memory-layout branch");
2277
2278 try sema.validateSwitch(parent_block, operand, inst);
2279
2280 if (try sema.resolveDefinedValue(parent_block, inst.base.src, operand)) |target_val| {
22452281 for (inst.positionals.cases) |case| {
22462282 const resolved = try sema.resolveInst(case.item);
2247 const casted = try sema.coerce(block, target.ty, resolved, resolved_src);
2283 const casted = try sema.coerce(block, operand.ty, resolved, resolved_src);
22482284 const item = try sema.resolveConstValue(parent_block, case_src, casted);
22492285
22502286 if (target_val.eql(item)) {
......@@ -2280,7 +2316,7 @@ fn zirSwitchBr(
22802316 case_block.instructions.items.len = 0;
22812317
22822318 const resolved = try sema.resolveInst(case.item);
2283 const casted = try sema.coerce(block, target.ty, resolved, resolved_src);
2319 const casted = try sema.coerce(block, operand.ty, resolved, resolved_src);
22842320 const item = try sema.resolveConstValue(parent_block, case_src, casted);
22852321
22862322 _ = try sema.analyzeBody(&case_block, case.body);
......@@ -2298,29 +2334,29 @@ fn zirSwitchBr(
22982334 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
22992335 };
23002336
2301 return mod.addSwitchBr(parent_block, inst.base.src, target, cases, else_body);
2337 return mod.addSwitchBr(parent_block, inst.base.src, operand, cases, else_body);
23022338}
23032339
2304fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Inst.Index) InnerError!void {
2340fn validateSwitch(sema: *Sema, block: *Scope.Block, operand: *Inst, inst: zir.Inst.Index) InnerError!void {
23052341 // validate usage of '_' prongs
2306 if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) {
2342 if (inst.positionals.special_prong == .underscore and operand.ty.zigTypeTag() != .Enum) {
23072343 return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
23082344 // TODO notes "'_' prong here" inst.positionals.cases[last].src
23092345 }
23102346
2311 // check that target type supports ranges
2347 // check that operand type supports ranges
23122348 if (inst.positionals.range) |range_inst| {
2313 switch (target.ty.zigTypeTag()) {
2349 switch (operand.ty.zigTypeTag()) {
23142350 .Int, .ComptimeInt => {},
23152351 else => {
2316 return sema.mod.fail(&block.base, target.src, "ranges not allowed when switching on type {}", .{target.ty});
2352 return sema.mod.fail(&block.base, operand.src, "ranges not allowed when switching on type {}", .{operand.ty});
23172353 // TODO notes "range used here" range_inst.src
23182354 },
23192355 }
23202356 }
23212357
23222358 // validate for duplicate items/missing else prong
2323 switch (target.ty.zigTypeTag()) {
2359 switch (operand.ty.zigTypeTag()) {
23242360 .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}),
23252361 .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}),
23262362 .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}),
......@@ -2331,9 +2367,9 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
23312367 for (inst.positionals.items) |item| {
23322368 const maybe_src = if (item.castTag(.switch_range)) |range| blk: {
23332369 const start_resolved = try sema.resolveInst(range.positionals.lhs);
2334 const start_casted = try sema.coerce(block, target.ty, start_resolved);
2370 const start_casted = try sema.coerce(block, operand.ty, start_resolved);
23352371 const end_resolved = try sema.resolveInst(range.positionals.rhs);
2336 const end_casted = try sema.coerce(block, target.ty, end_resolved);
2372 const end_casted = try sema.coerce(block, operand.ty, end_resolved);
23372373
23382374 break :blk try range_set.add(
23392375 try sema.resolveConstValue(block, range_start_src, start_casted),
......@@ -2342,7 +2378,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
23422378 );
23432379 } else blk: {
23442380 const resolved = try sema.resolveInst(item);
2345 const casted = try sema.coerce(block, target.ty, resolved);
2381 const casted = try sema.coerce(block, operand.ty, resolved);
23462382 const value = try sema.resolveConstValue(block, item_src, casted);
23472383 break :blk try range_set.add(value, value, item.src);
23482384 };
......@@ -2353,12 +2389,12 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
23532389 }
23542390 }
23552391
2356 if (target.ty.zigTypeTag() == .Int) {
2392 if (operand.ty.zigTypeTag() == .Int) {
23572393 var arena = std.heap.ArenaAllocator.init(sema.gpa);
23582394 defer arena.deinit();
23592395
2360 const start = try target.ty.minInt(&arena, mod.getTarget());
2361 const end = try target.ty.maxInt(&arena, mod.getTarget());
2396 const start = try operand.ty.minInt(&arena, mod.getTarget());
2397 const end = try operand.ty.maxInt(&arena, mod.getTarget());
23622398 if (try range_set.spans(start, end)) {
23632399 if (inst.positionals.special_prong == .@"else") {
23642400 return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{});
......@@ -2396,7 +2432,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
23962432 },
23972433 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
23982434 if (inst.positionals.special_prong != .@"else") {
2399 return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});
2435 return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{operand.ty});
24002436 }
24012437
24022438 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(sema.gpa);
......@@ -2404,7 +2440,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
24042440
24052441 for (inst.positionals.items) |item| {
24062442 const resolved = try sema.resolveInst(item);
2407 const casted = try sema.coerce(block, target.ty, resolved);
2443 const casted = try sema.coerce(block, operand.ty, resolved);
24082444 const val = try sema.resolveConstValue(block, item_src, casted);
24092445
24102446 if (try seen_values.fetchPut(val, item.src)) |prev| {
......@@ -2429,7 +2465,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
24292465 .ComptimeFloat,
24302466 .Float,
24312467 => {
2432 return sema.mod.fail(&block.base, target.src, "invalid switch target type '{}'", .{target.ty});
2468 return sema.mod.fail(&block.base, operand.src, "invalid switch operand type '{}'", .{operand.ty});
24332469 },
24342470 }
24352471}
src/zir.zig+113-41
......@@ -585,39 +585,35 @@ pub const Inst = struct {
585585 /// An enum literal 8 or fewer bytes. No source location.
586586 /// Uses the `small_str` field.
587587 enum_literal_small,
588 // /// A switch expression.
589 // /// lhs is target, SwitchBr[rhs]
590 // /// All prongs of target handled.
591 // switch_br,
592 // /// Same as switch_br, except has a range field.
593 // switch_br_range,
594 // /// Same as switch_br, except has an else prong.
595 // switch_br_else,
596 // /// Same as switch_br_else, except has a range field.
597 // switch_br_else_range,
598 // /// Same as switch_br, except has an underscore prong.
599 // switch_br_underscore,
600 // /// Same as switch_br, except has a range field.
601 // switch_br_underscore_range,
602 // /// Same as `switch_br` but the target is a pointer to the value being switched on.
603 // switch_br_ref,
604 // /// Same as `switch_br_range` but the target is a pointer to the value being switched on.
605 // switch_br_ref_range,
606 // /// Same as `switch_br_else` but the target is a pointer to the value being switched on.
607 // switch_br_ref_else,
608 // /// Same as `switch_br_else_range` but the target is a pointer to the
609 // /// value being switched on.
610 // switch_br_ref_else_range,
611 // /// Same as `switch_br_underscore` but the target is a pointer to the value
612 // /// being switched on.
613 // switch_br_ref_underscore,
614 // /// Same as `switch_br_underscore_range` but the target is a pointer to
615 // /// the value being switched on.
616 // switch_br_ref_underscore_range,
617 // /// A range in a switch case, `lhs...rhs`.
618 // /// Only checks that `lhs >= rhs` if they are ints, everything else is
619 // /// validated by the switch_br instruction.
620 // switch_range,
588 /// A switch expression. Uses the `pl_node` union field.
589 /// AST node is the switch, payload is `SwitchBr`.
590 /// All prongs of target handled.
591 switch_br,
592 /// Same as switch_br, except has a range field.
593 switch_br_range,
594 /// Same as switch_br, except has an else prong.
595 switch_br_else,
596 /// Same as switch_br_else, except has a range field.
597 switch_br_else_range,
598 /// Same as switch_br, except has an underscore prong.
599 switch_br_underscore,
600 /// Same as switch_br, except has a range field.
601 switch_br_underscore_range,
602 /// Same as `switch_br` but the target is a pointer to the value being switched on.
603 switch_br_ref,
604 /// Same as `switch_br_range` but the target is a pointer to the value being switched on.
605 switch_br_ref_range,
606 /// Same as `switch_br_else` but the target is a pointer to the value being switched on.
607 switch_br_ref_else,
608 /// Same as `switch_br_else_range` but the target is a pointer to the
609 /// value being switched on.
610 switch_br_ref_else_range,
611 /// Same as `switch_br_underscore` but the target is a pointer to the value
612 /// being switched on.
613 switch_br_ref_underscore,
614 /// Same as `switch_br_underscore_range` but the target is a pointer to
615 /// the value being switched on.
616 switch_br_ref_underscore_range,
621617
622618 /// Returns whether the instruction is one of the control flow "noreturn" types.
623619 /// Function calls do not count.
......@@ -760,6 +756,18 @@ pub const Inst = struct {
760756 .@"unreachable",
761757 .repeat,
762758 .repeat_inline,
759 .switch_br,
760 .switch_br_range,
761 .switch_br_else,
762 .switch_br_else_range,
763 .switch_br_underscore,
764 .switch_br_underscore_range,
765 .switch_br_ref,
766 .switch_br_ref_range,
767 .switch_br_ref_else,
768 .switch_br_ref_else_range,
769 .switch_br_ref_underscore,
770 .switch_br_ref_underscore_range,
763771 => true,
764772 };
765773 }
......@@ -1322,22 +1330,53 @@ pub const Inst = struct {
13221330 rhs: Ref,
13231331 };
13241332
1325 /// Stored in extra. Depending on zir tag and len fields, extra fields trail
1333 /// This form is supported when there are no ranges, and exactly 1 item per block.
1334 /// Depending on zir tag and len fields, extra fields trail
13261335 /// this one in the extra array.
1327 /// 0. range: Ref // If the tag has "_range" in it.
1328 /// 1. else_body: Ref // If the tag has "_else" or "_underscore" in it.
1329 /// 2. items: list of all individual items and ranges.
1330 /// 3. cases: {
1336 /// 0. else_body { // If the tag has "_else" or "_underscore" in it.
1337 /// body_len: u32,
1338 /// body member Index for every body_len
1339 /// }
1340 /// 1. cases: {
13311341 /// item: Ref,
13321342 /// body_len: u32,
1333 /// body member Ref for every body_len
1343 /// body member Index for every body_len
13341344 /// } for every cases_len
13351345 pub const SwitchBr = struct {
1336 /// TODO investigate, why do we need to store this? is it redundant?
1337 items_len: u32,
1346 operand: Ref,
13381347 cases_len: u32,
13391348 };
13401349
1350 /// This form is required when there exists a block which has more than one item,
1351 /// or a range.
1352 /// Depending on zir tag and len fields, extra fields trail
1353 /// this one in the extra array.
1354 /// 0. else_body { // If the tag has "_else" or "_underscore" in it.
1355 /// body_len: u32,
1356 /// body member Index for every body_len
1357 /// }
1358 /// 1. scalar_cases: { // for every scalar_cases_len
1359 /// item: Ref,
1360 /// body_len: u32,
1361 /// body member Index for every body_len
1362 /// }
1363 /// 2. multi_cases: { // for every multi_cases_len
1364 /// items_len: u32,
1365 /// item: Ref for every items_len
1366 /// block_index: u32, // index in extra to a `Block`
1367 /// }
1368 /// 3. range_cases: { // for every range_cases_len
1369 /// item_start: Ref,
1370 /// item_end: Ref,
1371 /// block_index: u32, // index in extra to a `Block`
1372 /// }
1373 pub const SwitchBrRange = struct {
1374 operand: Ref,
1375 scalar_cases_len: u32,
1376 multi_cases_len: u32,
1377 range_cases_len: u32,
1378 };
1379
13411380 pub const Field = struct {
13421381 lhs: Ref,
13431382 /// Offset into `string_bytes`.
......@@ -1503,6 +1542,22 @@ const Writer = struct {
15031542 .condbr_inline,
15041543 => try self.writePlNodeCondBr(stream, inst),
15051544
1545 .switch_br,
1546 .switch_br_else,
1547 .switch_br_underscore,
1548 .switch_br_ref,
1549 .switch_br_ref_else,
1550 .switch_br_ref_underscore,
1551 => try self.writePlNodeSwitchBr(stream, inst),
1552
1553 .switch_br_range,
1554 .switch_br_else_range,
1555 .switch_br_underscore_range,
1556 .switch_br_ref_range,
1557 .switch_br_ref_else_range,
1558 .switch_br_ref_underscore_range,
1559 => try self.writePlNodeSwitchBrRange(stream, inst),
1560
15061561 .compile_log,
15071562 .typeof_peer,
15081563 => try self.writePlNodeMultiOp(stream, inst),
......@@ -1708,6 +1763,23 @@ const Writer = struct {
17081763 try self.writeSrc(stream, inst_data.src());
17091764 }
17101765
1766 fn writePlNodeSwitchBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1767 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1768 const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);
1769
1770 try self.writeInstRef(stream, extra.data.operand);
1771 try stream.writeAll(", TODO) ");
1772 try self.writeSrc(stream, inst_data.src());
1773 }
1774
1775 fn writePlNodeSwitchBrRange(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1776 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1777 const extra = self.code.extraData(Inst.SwitchBrRange, inst_data.payload_index);
1778 try self.writeInstRef(stream, extra.data.operand);
1779 try stream.writeAll(", TODO) ");
1780 try self.writeSrc(stream, inst_data.src());
1781 }
1782
17111783 fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void {
17121784 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
17131785 const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index);