control_flow.h 5.4 KB

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