Commit 85e94fed1e

mlugg <mlugg@mlugg.co.uk>
2023-05-27 14:50:50
Eliminate switch_cond[_ref] ZIR tags
This finishes the process of consolidating switch expressions in ZIR into as simple and compact a representation as is possible. There are now just two ZIR tags dedicated to switch expressions: switch_block and switch_block_ref, with the latter being for an operand passed by reference.
1 parent 39510cc
src/AstGen.zig
@@ -2610,8 +2610,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
             .slice_length,
             .import,
             .switch_block,
-            .switch_cond,
-            .switch_cond_ref,
+            .switch_block_ref,
             .struct_init_empty,
             .struct_init,
             .struct_init_ref,
@@ -6835,10 +6834,6 @@ fn switchExpr(
     const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
 
     const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
-    const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
-    const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
-    // Sema expects a dbg_stmt immediately after switch_cond(_ref)
-    try emitDbgStmt(parent_gz, operand_lc);
     const item_ri: ResultInfo = .{ .rl = .none };
 
     // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
@@ -6858,8 +6853,11 @@ fn switchExpr(
     block_scope.instructions_top = GenZir.unstacked_top;
     block_scope.setBreakResultInfo(ri);
 
+    // Sema expects a dbg_stmt immediately before switch_block(_ref)
+    try emitDbgStmt(parent_gz, operand_lc);
     // This gets added to the parent block later, after the item expressions.
-    const switch_block = try parent_gz.makeBlockInst(.switch_block, switch_node);
+    const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
+    const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node);
 
     // We re-use this same scope for all cases, including the special prong, if any.
     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
@@ -7076,7 +7074,7 @@ fn switchExpr(
         payloads.items.len - case_table_end);
 
     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
-        .operand = cond,
+        .operand = raw_operand,
         .bits = Zir.Inst.SwitchBlock.Bits{
             .has_multi_cases = multi_cases_len != 0,
             .has_else = special_prong == .@"else",
src/Autodoc.zig
@@ -1993,31 +1993,6 @@ fn walkInstruction(
                 .expr = .{ .switchIndex = switch_index },
             };
         },
-        .switch_cond => {
-            const un_node = data[inst_index].un_node;
-            const operand = try self.walkRef(
-                file,
-                parent_scope,
-                parent_src,
-                un_node.operand,
-                need_type,
-            );
-            const operand_index = self.exprs.items.len;
-            try self.exprs.append(self.arena, operand.expr);
-
-            // const ast_index = self.ast_nodes.items.len;
-            // const sep = "=" ** 200;
-            // log.debug("{s}", .{sep});
-            // log.debug("SWITCH COND", .{});
-            // log.debug("ast index = {}", .{ast_index});
-            // log.debug("ast previous = {}", .{self.ast_nodes.items[ast_index - 1]});
-            // log.debug("{s}", .{sep});
-
-            return DocData.WalkResult{
-                .typeRef = operand.typeRef,
-                .expr = .{ .typeOf = operand_index },
-            };
-        },
 
         .typeof => {
             const un_node = data[inst_index].un_node;
src/print_zir.zig
@@ -222,8 +222,6 @@ const Writer = struct {
             .bit_reverse,
             .@"resume",
             .@"await",
-            .switch_cond,
-            .switch_cond_ref,
             .array_base_ptr,
             .field_base_ptr,
             .validate_struct_init_ty,
@@ -388,7 +386,9 @@ const Writer = struct {
             .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
             .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
 
-            .switch_block => try self.writeSwitchBlock(stream, inst),
+            .switch_block,
+            .switch_block_ref,
+            => try self.writeSwitchBlock(stream, inst),
 
             .field_ptr,
             .field_ptr_init,
src/Sema.zig
@@ -1007,9 +1007,8 @@ fn analyzeBodyInner(
             .slice_start                  => try sema.zirSliceStart(block, inst),
             .slice_length                 => try sema.zirSliceLength(block, inst),
             .str                          => try sema.zirStr(block, inst),
-            .switch_block                 => try sema.zirSwitchBlock(block, inst),
-            .switch_cond                  => try sema.zirSwitchCond(block, inst, false),
-            .switch_cond_ref              => try sema.zirSwitchCond(block, inst, true),
+            .switch_block                 => try sema.zirSwitchBlock(block, inst, false),
+            .switch_block_ref             => try sema.zirSwitchBlock(block, inst, true),
             .type_info                    => try sema.zirTypeInfo(block, inst),
             .size_of                      => try sema.zirSizeOf(block, inst),
             .bit_size_of                  => try sema.zirBitSizeOf(block, inst),
@@ -10411,23 +10410,14 @@ const SwitchProngAnalysis = struct {
     }
 };
 
-fn zirSwitchCond(
+fn switchCond(
     sema: *Sema,
     block: *Block,
-    inst: Zir.Inst.Index,
-    is_ref: bool,
+    src: LazySrcLoc,
+    operand: Air.Inst.Ref,
 ) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
-    const src = inst_data.src();
-    const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
-    const operand_ptr = try sema.resolveInst(inst_data.operand);
-    const operand = if (is_ref)
-        try sema.analyzeLoad(block, src, operand_ptr, operand_src)
-    else
-        operand_ptr;
     const operand_ty = sema.typeOf(operand);
-
     switch (operand_ty.zigTypeTag(mod)) {
         .Type,
         .Void,
@@ -10484,7 +10474,7 @@ fn zirSwitchCond(
 
 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc);
 
-fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -10498,10 +10488,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
     const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
 
-    const operand = try sema.resolveInst(extra.data.operand);
-    // AstGen guarantees that the instruction immediately following
-    // switch_cond(_ref) is a dbg_stmt
-    const cond_dbg_node_index = Zir.refToIndex(extra.data.operand).? + 1;
+    const raw_operand: struct { val: Air.Inst.Ref, ptr: Air.Inst.Ref } = blk: {
+        const maybe_ptr = try sema.resolveInst(extra.data.operand);
+        if (operand_is_ref) {
+            const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src);
+            break :blk .{ .val = val, .ptr = maybe_ptr };
+        } else {
+            break :blk .{ .val = maybe_ptr, .ptr = undefined };
+        }
+    };
+
+    const operand = try sema.switchCond(block, src, raw_operand.val);
+
+    // AstGen guarantees that the instruction immediately preceding
+    // switch_block(_ref) is a dbg_stmt
+    const cond_dbg_node_index = inst - 1;
 
     var header_extra_index: usize = extra.end;
 
@@ -10555,19 +10556,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         },
     };
 
-    const raw_operand: struct { val: Air.Inst.Ref, ptr: Air.Inst.Ref } = blk: {
-        const zir_tags = sema.code.instructions.items(.tag);
-        const zir_data = sema.code.instructions.items(.data);
-        const cond_index = Zir.refToIndex(extra.data.operand).?;
-        const raw = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable;
-        if (zir_tags[cond_index] == .switch_cond_ref) {
-            const val = try sema.analyzeLoad(block, src, raw, operand_src);
-            break :blk .{ .val = val, .ptr = raw };
-        } else {
-            break :blk .{ .val = raw, .ptr = undefined };
-        }
-    };
-
     const maybe_union_ty = sema.typeOf(raw_operand.val);
     const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union;
 
src/Zir.zig
@@ -667,15 +667,9 @@ pub const Inst = struct {
         /// A switch expression. Uses the `pl_node` union field.
         /// AST node is the switch, payload is `SwitchBlock`.
         switch_block,
-        /// Produces the value that will be switched on. For example, for
-        /// integers, it returns the integer with no modifications. For tagged unions, it
-        /// returns the active enum tag.
-        /// Uses the `un_node` union field.
-        switch_cond,
-        /// Same as `switch_cond`, except the input operand is a pointer to
-        /// what will be switched on.
-        /// Uses the `un_node` union field.
-        switch_cond_ref,
+        /// A switch expression. Uses the `pl_node` union field.
+        /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.
+        switch_block_ref,
         /// Given a
         ///   *A returns *A
         ///   *E!A returns *A
@@ -1122,8 +1116,7 @@ pub const Inst = struct {
                 .resolve_inferred_alloc,
                 .set_eval_branch_quota,
                 .switch_block,
-                .switch_cond,
-                .switch_cond_ref,
+                .switch_block_ref,
                 .array_base_ptr,
                 .field_base_ptr,
                 .validate_array_init_ty,
@@ -1411,8 +1404,7 @@ pub const Inst = struct {
                 .import,
                 .typeof_log2_int_type,
                 .switch_block,
-                .switch_cond,
-                .switch_cond_ref,
+                .switch_block_ref,
                 .array_base_ptr,
                 .field_base_ptr,
                 .struct_init_empty,
@@ -1663,8 +1655,7 @@ pub const Inst = struct {
                 .err_union_code_ptr = .un_node,
                 .enum_literal = .str_tok,
                 .switch_block = .pl_node,
-                .switch_cond = .un_node,
-                .switch_cond_ref = .un_node,
+                .switch_block_ref = .pl_node,
                 .array_base_ptr = .un_node,
                 .field_base_ptr = .un_node,
                 .validate_array_init_ty = .pl_node,
@@ -2665,13 +2656,10 @@ pub const Inst = struct {
     /// captured payload. Whether this is captured by reference or by value
     /// depends on whether the `byref` bit is set for the corresponding body.
     pub const SwitchBlock = struct {
-        /// This is always a `switch_cond` or `switch_cond_ref` instruction.
-        /// If it is a `switch_cond_ref` instruction, bits.is_ref is always true.
-        /// If it is a `switch_cond` instruction, bits.is_ref is always false.
-        /// Both `switch_cond` and `switch_cond_ref` return a value, not a pointer,
-        /// that is useful for the case items, but cannot be used for capture values.
-        /// For the capture values, Sema is expected to find the operand of this operand
-        /// and use that.
+        /// The operand passed to the `switch` expression. If this is a
+        /// `switch_block`, this is the operand value; if `switch_block_ref` it
+        /// is a pointer to the operand. `switch_block_ref` is always used if
+        /// any prong has a byref capture.
         operand: Ref,
         bits: Bits,