فهرست منبع

Move control flow block functions to their own file. (#4921)

context.cpp is getting large, so I'm looking at a few ways to cut out
clusters of functions. This felt like a logical cluster of functions to
move to their own file.

Note this doesn't touch the implementation at all, beyond what's needed
to change from `Context` members to context args.
Jon Ross-Perkins 1 سال پیش
والد
کامیت
b0d49ba957

+ 2 - 0
toolchain/check/BUILD

@@ -17,6 +17,7 @@ cc_library(
     srcs = [
         "call.cpp",
         "context.cpp",
+        "control_flow.cpp",
         "convert.cpp",
         "decl_name_stack.cpp",
         "deduce.cpp",
@@ -44,6 +45,7 @@ cc_library(
     hdrs = [
         "call.h",
         "context.h",
+        "control_flow.h",
         "convert.h",
         "decl_introducer_state.h",
         "decl_name_stack.h",

+ 0 - 120
toolchain/check/context.cpp

@@ -745,110 +745,6 @@ auto Context::LookupNameInCore(SemIR::LocId loc_id, llvm::StringRef name)
   return constant_values().GetConstantInstId(scope_result.target_inst_id());
 }
 
-template <typename BranchNode, typename... Args>
-static auto AddDominatedBlockAndBranchImpl(Context& context,
-                                           Parse::NodeId node_id, Args... args)
-    -> SemIR::InstBlockId {
-  if (!context.inst_block_stack().is_current_block_reachable()) {
-    return SemIR::InstBlockId::Unreachable;
-  }
-  auto block_id = context.inst_blocks().AddDefaultValue();
-  context.AddInst<BranchNode>(node_id, {block_id, args...});
-  return block_id;
-}
-
-auto Context::AddDominatedBlockAndBranch(Parse::NodeId node_id)
-    -> SemIR::InstBlockId {
-  return AddDominatedBlockAndBranchImpl<SemIR::Branch>(*this, node_id);
-}
-
-auto Context::AddDominatedBlockAndBranchWithArg(Parse::NodeId node_id,
-                                                SemIR::InstId arg_id)
-    -> SemIR::InstBlockId {
-  return AddDominatedBlockAndBranchImpl<SemIR::BranchWithArg>(*this, node_id,
-                                                              arg_id);
-}
-
-auto Context::AddDominatedBlockAndBranchIf(Parse::NodeId node_id,
-                                           SemIR::InstId cond_id)
-    -> SemIR::InstBlockId {
-  return AddDominatedBlockAndBranchImpl<SemIR::BranchIf>(*this, node_id,
-                                                         cond_id);
-}
-
-auto Context::AddConvergenceBlockAndPush(Parse::NodeId node_id, int num_blocks)
-    -> void {
-  CARBON_CHECK(num_blocks >= 2, "no convergence");
-
-  SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable;
-  for ([[maybe_unused]] auto _ : llvm::seq(num_blocks)) {
-    if (inst_block_stack().is_current_block_reachable()) {
-      if (new_block_id == SemIR::InstBlockId::Unreachable) {
-        new_block_id = inst_blocks().AddDefaultValue();
-      }
-      CARBON_CHECK(node_id.has_value());
-      AddInst<SemIR::Branch>(node_id, {.target_id = new_block_id});
-    }
-    inst_block_stack().Pop();
-  }
-  inst_block_stack().Push(new_block_id);
-  AddToRegion(new_block_id, node_id);
-}
-
-auto Context::AddConvergenceBlockWithArgAndPush(
-    Parse::NodeId node_id, std::initializer_list<SemIR::InstId> block_args)
-    -> SemIR::InstId {
-  CARBON_CHECK(block_args.size() >= 2, "no convergence");
-
-  SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable;
-  for (auto arg_id : block_args) {
-    if (inst_block_stack().is_current_block_reachable()) {
-      if (new_block_id == SemIR::InstBlockId::Unreachable) {
-        new_block_id = inst_blocks().AddDefaultValue();
-      }
-      AddInst<SemIR::BranchWithArg>(
-          node_id, {.target_id = new_block_id, .arg_id = arg_id});
-    }
-    inst_block_stack().Pop();
-  }
-  inst_block_stack().Push(new_block_id);
-  AddToRegion(new_block_id, node_id);
-
-  // Acquire the result value.
-  SemIR::TypeId result_type_id = insts().Get(*block_args.begin()).type_id();
-  return AddInst<SemIR::BlockArg>(
-      node_id, {.type_id = result_type_id, .block_id = new_block_id});
-}
-
-auto Context::SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id,
-                                                 SemIR::InstId cond_id,
-                                                 SemIR::InstId if_true,
-                                                 SemIR::InstId if_false)
-    -> void {
-  CARBON_CHECK(insts().Is<SemIR::BlockArg>(select_id));
-
-  // Determine the constant result based on the condition value.
-  SemIR::ConstantId const_id = SemIR::ConstantId::NotConstant;
-  auto cond_const_id = constant_values().Get(cond_id);
-  if (!cond_const_id.is_template()) {
-    // Symbolic or non-constant condition means a non-constant result.
-  } else if (auto literal = insts().TryGetAs<SemIR::BoolLiteral>(
-                 constant_values().GetInstId(cond_const_id))) {
-    const_id = constant_values().Get(literal.value().value.ToBool() ? if_true
-                                                                    : if_false);
-  } else {
-    CARBON_CHECK(cond_const_id == SemIR::ErrorInst::SingletonConstantId,
-                 "Unexpected constant branch condition.");
-    const_id = SemIR::ErrorInst::SingletonConstantId;
-  }
-
-  if (const_id.is_constant()) {
-    CARBON_VLOG("Constant: {0} -> {1}\n", insts().Get(select_id),
-                constant_values().GetInstId(const_id));
-    constant_values().Set(select_id, const_id);
-  }
-}
-
 auto Context::AddToRegion(SemIR::InstBlockId block_id, SemIR::LocId loc_id)
     -> void {
   if (region_stack_.empty()) {
@@ -936,22 +832,6 @@ auto Context::InsertHere(SemIR::ExprRegionId region_id) -> SemIR::InstId {
   return region.result_id;
 }
 
-auto Context::is_current_position_reachable() -> bool {
-  if (!inst_block_stack().is_current_block_reachable()) {
-    return false;
-  }
-
-  // Our current position is at the end of a reachable block. That position is
-  // reachable unless the previous instruction is a terminator instruction.
-  auto block_contents = inst_block_stack().PeekCurrentBlockContents();
-  if (block_contents.empty()) {
-    return true;
-  }
-  const auto& last_inst = insts().Get(block_contents.back());
-  return last_inst.kind().terminator_kind() !=
-         SemIR::TerminatorKind::Terminator;
-}
-
 auto Context::Finalize() -> void {
   // Pop information for the file-level scope.
   sem_ir().set_top_inst_block_id(inst_block_stack().Pop());

+ 2 - 51
toolchain/check/context.h

@@ -314,57 +314,6 @@ class Context {
     return result;
   }
 
-  // Adds a `Branch` instruction branching to a new instruction block, and
-  // returns the ID of the new block. All paths to the branch target must go
-  // through the current block, though not necessarily through this branch.
-  auto AddDominatedBlockAndBranch(Parse::NodeId node_id) -> SemIR::InstBlockId;
-
-  // Adds a `Branch` instruction branching to a new instruction block with a
-  // value, and returns the ID of the new block. All paths to the branch target
-  // must go through the current block.
-  auto AddDominatedBlockAndBranchWithArg(Parse::NodeId node_id,
-                                         SemIR::InstId arg_id)
-      -> SemIR::InstBlockId;
-
-  // Adds a `BranchIf` instruction branching to a new instruction block, and
-  // returns the ID of the new block. All paths to the branch target must go
-  // through the current block.
-  auto AddDominatedBlockAndBranchIf(Parse::NodeId node_id,
-                                    SemIR::InstId cond_id)
-      -> SemIR::InstBlockId;
-
-  // Handles recovergence of control flow. Adds branches from the top
-  // `num_blocks` on the instruction block stack to a new block, pops the
-  // existing blocks, pushes the new block onto the instruction block stack,
-  // and adds it to the most recently pushed region.
-  auto AddConvergenceBlockAndPush(Parse::NodeId node_id, int num_blocks)
-      -> void;
-
-  // Handles recovergence of control flow with a result value. Adds branches
-  // from the top few blocks on the instruction block stack to a new block, pops
-  // the existing blocks,  pushes the new block onto the instruction block
-  // stack, and adds it to the most recently pushed region. The number of blocks
-  // popped is the size of `block_args`, and the corresponding result values are
-  // the elements of `block_args`. Returns an instruction referring to the
-  // result value.
-  auto AddConvergenceBlockWithArgAndPush(
-      Parse::NodeId node_id, std::initializer_list<SemIR::InstId> block_args)
-      -> SemIR::InstId;
-
-  // Sets the constant value of a block argument created as the result of a
-  // branch.  `select_id` should be a `BlockArg` that selects between two
-  // values. `cond_id` is the condition, `if_false` is the value to use if the
-  // condition is false, and `if_true` is the value to use if the condition is
-  // true.  We don't track enough information in the `BlockArg` inst for
-  // `TryEvalInst` to do this itself.
-  auto SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id,
-                                          SemIR::InstId cond_id,
-                                          SemIR::InstId if_true,
-                                          SemIR::InstId if_false) -> void;
-
-  // Returns whether the current position in the current block is reachable.
-  auto is_current_position_reachable() -> bool;
-
   // Returns the type ID for a constant of type `type`.
   auto GetTypeIdForTypeConstant(SemIR::ConstantId constant_id) -> SemIR::TypeId;
 
@@ -489,6 +438,8 @@ class Context {
     return parse_tree().tokens();
   }
 
+  auto vlog_stream() -> llvm::raw_ostream* { return vlog_stream_; }
+
   auto node_stack() -> NodeStack& { return node_stack_; }
 
   auto inst_block_stack() -> InstBlockStack& { return inst_block_stack_; }

+ 132 - 0
toolchain/check/control_flow.cpp

@@ -0,0 +1,132 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/check/control_flow.h"
+
+#include "toolchain/sem_ir/typed_insts.h"
+
+namespace Carbon::Check {
+
+template <typename BranchNode, typename... Args>
+static auto AddDominatedBlockAndBranchImpl(Context& context,
+                                           Parse::NodeId node_id, Args... args)
+    -> SemIR::InstBlockId {
+  if (!context.inst_block_stack().is_current_block_reachable()) {
+    return SemIR::InstBlockId::Unreachable;
+  }
+  auto block_id = context.inst_blocks().AddDefaultValue();
+  context.AddInst<BranchNode>(node_id, {block_id, args...});
+  return block_id;
+}
+
+auto AddDominatedBlockAndBranch(Context& context, Parse::NodeId node_id)
+    -> SemIR::InstBlockId {
+  return AddDominatedBlockAndBranchImpl<SemIR::Branch>(context, node_id);
+}
+
+auto AddDominatedBlockAndBranchWithArg(Context& context, Parse::NodeId node_id,
+                                       SemIR::InstId arg_id)
+    -> SemIR::InstBlockId {
+  return AddDominatedBlockAndBranchImpl<SemIR::BranchWithArg>(context, node_id,
+                                                              arg_id);
+}
+
+auto AddDominatedBlockAndBranchIf(Context& context, Parse::NodeId node_id,
+                                  SemIR::InstId cond_id) -> SemIR::InstBlockId {
+  return AddDominatedBlockAndBranchImpl<SemIR::BranchIf>(context, node_id,
+                                                         cond_id);
+}
+
+auto AddConvergenceBlockAndPush(Context& context, Parse::NodeId node_id,
+                                int num_blocks) -> void {
+  CARBON_CHECK(num_blocks >= 2, "no convergence");
+
+  SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable;
+  for ([[maybe_unused]] auto _ : llvm::seq(num_blocks)) {
+    if (context.inst_block_stack().is_current_block_reachable()) {
+      if (new_block_id == SemIR::InstBlockId::Unreachable) {
+        new_block_id = context.inst_blocks().AddDefaultValue();
+      }
+      CARBON_CHECK(node_id.has_value());
+      context.AddInst<SemIR::Branch>(node_id, {.target_id = new_block_id});
+    }
+    context.inst_block_stack().Pop();
+  }
+  context.inst_block_stack().Push(new_block_id);
+  context.AddToRegion(new_block_id, node_id);
+}
+
+auto AddConvergenceBlockWithArgAndPush(
+    Context& context, Parse::NodeId node_id,
+    std::initializer_list<SemIR::InstId> block_args) -> SemIR::InstId {
+  CARBON_CHECK(block_args.size() >= 2, "no convergence");
+
+  SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable;
+  for (auto arg_id : block_args) {
+    if (context.inst_block_stack().is_current_block_reachable()) {
+      if (new_block_id == SemIR::InstBlockId::Unreachable) {
+        new_block_id = context.inst_blocks().AddDefaultValue();
+      }
+      context.AddInst<SemIR::BranchWithArg>(
+          node_id, {.target_id = new_block_id, .arg_id = arg_id});
+    }
+    context.inst_block_stack().Pop();
+  }
+  context.inst_block_stack().Push(new_block_id);
+  context.AddToRegion(new_block_id, node_id);
+
+  // Acquire the result value.
+  SemIR::TypeId result_type_id =
+      context.insts().Get(*block_args.begin()).type_id();
+  return context.AddInst<SemIR::BlockArg>(
+      node_id, {.type_id = result_type_id, .block_id = new_block_id});
+}
+
+auto SetBlockArgResultBeforeConstantUse(Context& context,
+                                        SemIR::InstId select_id,
+                                        SemIR::InstId cond_id,
+                                        SemIR::InstId if_true,
+                                        SemIR::InstId if_false) -> void {
+  CARBON_CHECK(context.insts().Is<SemIR::BlockArg>(select_id));
+
+  // Determine the constant result based on the condition value.
+  SemIR::ConstantId const_id = SemIR::ConstantId::NotConstant;
+  auto cond_const_id = context.constant_values().Get(cond_id);
+  if (!cond_const_id.is_template()) {
+    // Symbolic or non-constant condition means a non-constant result.
+  } else if (auto literal = context.insts().TryGetAs<SemIR::BoolLiteral>(
+                 context.constant_values().GetInstId(cond_const_id))) {
+    const_id = context.constant_values().Get(
+        literal.value().value.ToBool() ? if_true : if_false);
+  } else {
+    CARBON_CHECK(cond_const_id == SemIR::ErrorInst::SingletonConstantId,
+                 "Unexpected constant branch condition.");
+    const_id = SemIR::ErrorInst::SingletonConstantId;
+  }
+
+  if (const_id.is_constant()) {
+    CARBON_VLOG_TO(context.vlog_stream(), "Constant: {0} -> {1}\n",
+                   context.insts().Get(select_id),
+                   context.constant_values().GetInstId(const_id));
+    context.constant_values().Set(select_id, const_id);
+  }
+}
+
+auto IsCurrentPositionReachable(Context& context) -> bool {
+  if (!context.inst_block_stack().is_current_block_reachable()) {
+    return false;
+  }
+
+  // Our current position is at the end of a reachable block. That position is
+  // reachable unless the previous instruction is a terminator instruction.
+  auto block_contents = context.inst_block_stack().PeekCurrentBlockContents();
+  if (block_contents.empty()) {
+    return true;
+  }
+  const auto& last_inst = context.insts().Get(block_contents.back());
+  return last_inst.kind().terminator_kind() !=
+         SemIR::TerminatorKind::Terminator;
+}
+
+}  // namespace Carbon::Check

+ 67 - 0
toolchain/check/control_flow.h

@@ -0,0 +1,67 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_
+#define CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_
+
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+// Adds a `Branch` instruction branching to a new instruction block, and
+// returns the ID of the new block. All paths to the branch target must go
+// through the current block, though not necessarily through this branch.
+auto AddDominatedBlockAndBranch(Context& context, Parse::NodeId node_id)
+    -> SemIR::InstBlockId;
+
+// Adds a `Branch` instruction branching to a new instruction block with a
+// value, and returns the ID of the new block. All paths to the branch target
+// must go through the current block.
+auto AddDominatedBlockAndBranchWithArg(Context& context, Parse::NodeId node_id,
+                                       SemIR::InstId arg_id)
+    -> SemIR::InstBlockId;
+
+// Adds a `BranchIf` instruction branching to a new instruction block, and
+// returns the ID of the new block. All paths to the branch target must go
+// through the current block.
+auto AddDominatedBlockAndBranchIf(Context& context, Parse::NodeId node_id,
+                                  SemIR::InstId cond_id) -> SemIR::InstBlockId;
+
+// Handles recovergence of control flow. Adds branches from the top
+// `num_blocks` on the instruction block stack to a new block, pops the
+// existing blocks, pushes the new block onto the instruction block stack,
+// and adds it to the most recently pushed region.
+auto AddConvergenceBlockAndPush(Context& context, Parse::NodeId node_id,
+                                int num_blocks) -> void;
+
+// Handles recovergence of control flow with a result value. Adds branches
+// from the top few blocks on the instruction block stack to a new block, pops
+// the existing blocks,  pushes the new block onto the instruction block
+// stack, and adds it to the most recently pushed region. The number of blocks
+// popped is the size of `block_args`, and the corresponding result values are
+// the elements of `block_args`. Returns an instruction referring to the
+// result value.
+auto AddConvergenceBlockWithArgAndPush(
+    Context& context, Parse::NodeId node_id,
+    std::initializer_list<SemIR::InstId> block_args) -> SemIR::InstId;
+
+// Sets the constant value of a block argument created as the result of a
+// branch.  `select_id` should be a `BlockArg` that selects between two
+// values. `cond_id` is the condition, `if_false` is the value to use if the
+// condition is false, and `if_true` is the value to use if the condition is
+// true.  We don't track enough information in the `BlockArg` inst for
+// `TryEvalInst` to do this itself.
+auto SetBlockArgResultBeforeConstantUse(Context& context,
+                                        SemIR::InstId select_id,
+                                        SemIR::InstId cond_id,
+                                        SemIR::InstId if_true,
+                                        SemIR::InstId if_false) -> void;
+
+// Returns whether the current position in the current block is reachable.
+auto IsCurrentPositionReachable(Context& context) -> bool;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_

+ 2 - 1
toolchain/check/handle_function.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/check/context.h"
+#include "toolchain/check/control_flow.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/decl_introducer_state.h"
 #include "toolchain/check/decl_name_stack.h"
@@ -445,7 +446,7 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id)
 
   // If the `}` of the function is reachable, reject if we need a return value
   // and otherwise add an implicit `return;`.
-  if (context.is_current_position_reachable()) {
+  if (IsCurrentPositionReachable(context)) {
     if (context.functions()
             .Get(function_id)
             .return_slot_pattern_id.has_value()) {

+ 7 - 6
toolchain/check/handle_if_expr.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "toolchain/check/context.h"
+#include "toolchain/check/control_flow.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/literal.h"
@@ -19,8 +20,8 @@ auto HandleParseNode(Context& context, Parse::IfExprIfId node_id) -> bool {
   cond_value_id = ConvertToBoolValue(context, if_node, cond_value_id);
   context.node_stack().Push(cond_node, cond_value_id);
   auto then_block_id =
-      context.AddDominatedBlockAndBranchIf(if_node, cond_value_id);
-  auto else_block_id = context.AddDominatedBlockAndBranch(if_node);
+      AddDominatedBlockAndBranchIf(context, if_node, cond_value_id);
+  auto else_block_id = AddDominatedBlockAndBranch(context, if_node);
 
   // Start emitting the `then` block.
   context.inst_block_stack().Pop();
@@ -85,10 +86,10 @@ auto HandleParseNode(Context& context, Parse::IfExprElseId node_id) -> bool {
       ConvertToValueOfType(context, else_node, else_value_id, result_type_id);
 
   // Create a resumption block and branches to it.
-  auto chosen_value_id = context.AddConvergenceBlockWithArgAndPush(
-      if_node, {else_value_id, then_value_id});
-  context.SetBlockArgResultBeforeConstantUse(chosen_value_id, cond_value_id,
-                                             then_value_id, else_value_id);
+  auto chosen_value_id = AddConvergenceBlockWithArgAndPush(
+      context, if_node, {else_value_id, then_value_id});
+  SetBlockArgResultBeforeConstantUse(context, chosen_value_id, cond_value_id,
+                                     then_value_id, else_value_id);
 
   // Push the result value.
   context.node_stack().Push(else_node, chosen_value_id);

+ 4 - 3
toolchain/check/handle_if_statement.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "toolchain/check/context.h"
+#include "toolchain/check/control_flow.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/handle.h"
 
@@ -22,8 +23,8 @@ auto HandleParseNode(Context& context, Parse::IfConditionId node_id) -> bool {
   // there is no `else`, the then block will terminate with a branch to the
   // else block, which will be reused as the resumption block.
   auto then_block_id =
-      context.AddDominatedBlockAndBranchIf(node_id, cond_value_id);
-  auto else_block_id = context.AddDominatedBlockAndBranch(node_id);
+      AddDominatedBlockAndBranchIf(context, node_id, cond_value_id);
+  auto else_block_id = AddDominatedBlockAndBranch(context, node_id);
 
   // Start emitting the `then` block.
   context.inst_block_stack().Pop();
@@ -64,7 +65,7 @@ auto HandleParseNode(Context& context, Parse::IfStatementId node_id) -> bool {
       // Branch from the then and else blocks to a new resumption block.
       context.node_stack()
           .PopAndDiscardSoloNodeId<Parse::NodeKind::IfStatementElse>();
-      context.AddConvergenceBlockAndPush(node_id, /*num_blocks=*/2);
+      AddConvergenceBlockAndPush(context, node_id, /*num_blocks=*/2);
       break;
     }
 

+ 4 - 3
toolchain/check/handle_loop_statement.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "toolchain/check/context.h"
+#include "toolchain/check/control_flow.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/handle.h"
 
@@ -16,7 +17,7 @@ auto HandleParseNode(Context& context, Parse::WhileConditionStartId node_id)
   // Branch to the loop header block. Note that we create a new block here even
   // if the current block is empty; this ensures that the loop always has a
   // preheader block.
-  auto loop_header_id = context.AddDominatedBlockAndBranch(node_id);
+  auto loop_header_id = AddDominatedBlockAndBranch(context, node_id);
   context.inst_block_stack().Pop();
 
   // Start emitting the loop header block.
@@ -36,8 +37,8 @@ auto HandleParseNode(Context& context, Parse::WhileConditionId node_id)
 
   // Branch to either the loop body or the loop exit block.
   auto loop_body_id =
-      context.AddDominatedBlockAndBranchIf(node_id, cond_value_id);
-  auto loop_exit_id = context.AddDominatedBlockAndBranch(node_id);
+      AddDominatedBlockAndBranchIf(context, node_id, cond_value_id);
+  auto loop_exit_id = AddDominatedBlockAndBranch(context, node_id);
   context.inst_block_stack().Pop();
 
   // Start emitting the loop body.

+ 6 - 5
toolchain/check/handle_operator.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "toolchain/check/context.h"
+#include "toolchain/check/control_flow.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/operator.h"
@@ -349,9 +350,9 @@ static auto HandleShortCircuitOperand(Context& context, Parse::NodeId node_id,
 
   // Create a block for the right-hand side and for the continuation.
   auto rhs_block_id =
-      context.AddDominatedBlockAndBranchIf(node_id, branch_value_id);
-  auto end_block_id = context.AddDominatedBlockAndBranchWithArg(
-      node_id, short_circuit_result_id);
+      AddDominatedBlockAndBranchIf(context, node_id, branch_value_id);
+  auto end_block_id = AddDominatedBlockAndBranchWithArg(
+      context, node_id, short_circuit_result_id);
 
   // Push the branch condition and result for use when handling the complete
   // expression.
@@ -410,8 +411,8 @@ static auto HandleShortCircuitOperator(Context& context, Parse::NodeId node_id)
   auto result_id = context.AddInst<SemIR::BlockArg>(
       node_id, {.type_id = context.insts().Get(rhs_id).type_id(),
                 .block_id = resume_block_id});
-  context.SetBlockArgResultBeforeConstantUse(result_id, branch_value_id, rhs_id,
-                                             short_circuit_result_id);
+  SetBlockArgResultBeforeConstantUse(context, result_id, branch_value_id,
+                                     rhs_id, short_circuit_result_id);
   context.node_stack().Push(node_id, result_id);
   return true;
 }