control_flow.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_
  5. #define CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_
  6. #include "toolchain/check/context.h"
  7. #include "toolchain/check/inst.h"
  8. #include "toolchain/parse/typed_nodes.h"
  9. #include "toolchain/sem_ir/ids.h"
  10. #include "toolchain/sem_ir/inst.h"
  11. namespace Carbon::Check {
  12. // Adds a `Branch` instruction branching to a new instruction block, and
  13. // returns the ID of the new block. All paths to the branch target must go
  14. // through the current block, though not necessarily through this branch.
  15. auto AddDominatedBlockAndBranch(Context& context, Parse::NodeId node_id)
  16. -> SemIR::InstBlockId;
  17. // Adds a `Branch` instruction branching to a new instruction block with a
  18. // value, and returns the ID of the new block. All paths to the branch target
  19. // must go through the current block.
  20. auto AddDominatedBlockAndBranchWithArg(Context& context, Parse::NodeId node_id,
  21. SemIR::InstId arg_id)
  22. -> SemIR::InstBlockId;
  23. // Adds a `BranchIf` instruction branching to a new instruction block, and
  24. // returns the ID of the new block. All paths to the branch target must go
  25. // through the current block.
  26. auto AddDominatedBlockAndBranchIf(Context& context, Parse::NodeId node_id,
  27. SemIR::InstId cond_id) -> SemIR::InstBlockId;
  28. // Handles recovergence of control flow. Adds branches from the top
  29. // `num_blocks` on the instruction block stack to a new block, pops the
  30. // existing blocks, pushes the new block onto the instruction block stack,
  31. // and adds it to the most recently pushed region.
  32. auto AddConvergenceBlockAndPush(Context& context, Parse::NodeId node_id,
  33. int num_blocks) -> void;
  34. // Handles recovergence of control flow with a result value. Adds branches
  35. // from the top few blocks on the instruction block stack to a new block, pops
  36. // the existing blocks, pushes the new block onto the instruction block
  37. // stack, and adds it to the most recently pushed region. The number of blocks
  38. // popped is the size of `block_args`, and the corresponding result values are
  39. // the elements of `block_args`. Returns an instruction referring to the
  40. // result value.
  41. auto AddConvergenceBlockWithArgAndPush(
  42. Context& context, Parse::NodeId node_id,
  43. std::initializer_list<SemIR::InstId> block_args) -> SemIR::InstId;
  44. // Sets the constant value of a block argument created as the result of a
  45. // branch. `select_id` should be a `BlockArg` that selects between two
  46. // values. `cond_id` is the condition, `if_false` is the value to use if the
  47. // condition is false, and `if_true` is the value to use if the condition is
  48. // true. We don't track enough information in the `BlockArg` inst for
  49. // `TryEvalInst` to do this itself.
  50. auto SetBlockArgResultBeforeConstantUse(Context& context,
  51. SemIR::InstId select_id,
  52. SemIR::InstId cond_id,
  53. SemIR::InstId if_true,
  54. SemIR::InstId if_false) -> void;
  55. // Returns whether the current position in the current block is reachable.
  56. auto IsCurrentPositionReachable(Context& context) -> bool;
  57. // Determines whether the instruction requires cleanup and, if so, adds it for
  58. // cleanup blocks. Note for example that a class may not need destruction when
  59. // neither it nor its members have `destroy` functions.
  60. auto MaybeAddCleanupForInst(Context& context, SemIR::InstId inst_id) -> void;
  61. // Adds an instruction that has cleanup associated.
  62. template <typename InstT, typename LocT>
  63. requires(InstT::Kind.has_cleanup() && std::convertible_to<LocT, SemIR::LocId>)
  64. auto AddInstWithCleanup(Context& context, LocT loc, InstT inst)
  65. -> SemIR::InstId {
  66. auto inst_id = AddInst(context, SemIR::LocIdAndInst(loc, inst));
  67. MaybeAddCleanupForInst(context, inst_id);
  68. return inst_id;
  69. }
  70. // Adds an instruction that has cleanup associated.
  71. template <typename InstT, typename LocT>
  72. requires(InstT::Kind.has_cleanup() && std::convertible_to<LocT, SemIR::LocId>)
  73. auto AddInstWithCleanupInNoBlock(Context& context, LocT loc, InstT inst)
  74. -> SemIR::InstId {
  75. auto inst_id = AddInstInNoBlock(context, SemIR::LocIdAndInst(loc, inst));
  76. MaybeAddCleanupForInst(context, inst_id);
  77. return inst_id;
  78. }
  79. // Adds a return cleanup block, including the returning instruction.
  80. //
  81. // Cleanup blocks are an effort to share cleanup instructions across equivalent
  82. // scope-ending instructions (for example, all `return;` instructions are
  83. // equivalent). Structurally, they should first run non-shared cleanup, then
  84. // either dispatch to a cleanup block that includes shared cleanup, or invoke
  85. // the control flow instruction.
  86. //
  87. // For example:
  88. //
  89. // fn F() {
  90. // var a: C;
  91. // if (...) {
  92. // // Cleanup block 1: destroy a, return
  93. // return;
  94. // }
  95. //
  96. // var b: C;
  97. // if (...) {
  98. // // Cleanup block 2: destroy b, reuse cleanup block 1.
  99. // return;
  100. // }
  101. //
  102. // DoSomethingMore();
  103. // // Cleanup block 3: reuse cleanup block 2.
  104. // }
  105. //
  106. // TODO: Add support for `break;` and `continue;`.
  107. // TODO: Add reuse (described above but not done).
  108. auto AddReturnCleanupBlock(Context& context,
  109. SemIR::LocIdAndInst loc_id_and_inst) -> void;
  110. template <typename LocT>
  111. auto AddReturnCleanupBlock(Context& context, LocT loc) -> void {
  112. AddReturnCleanupBlock(context, SemIR::LocIdAndInst(loc, SemIR::Return{}));
  113. }
  114. template <typename LocT>
  115. auto AddReturnCleanupBlockWithExpr(Context& context, LocT loc,
  116. SemIR::ReturnExpr inst) -> void {
  117. AddReturnCleanupBlock(context, SemIR::LocIdAndInst(loc, inst));
  118. }
  119. } // namespace Carbon::Check
  120. #endif // CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_