pending_block.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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_PENDING_BLOCK_H_
  5. #define CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_
  6. #include "llvm/ADT/SmallVector.h"
  7. #include "toolchain/check/context.h"
  8. #include "toolchain/check/inst.h"
  9. #include "toolchain/sem_ir/ids.h"
  10. #include "toolchain/sem_ir/inst.h"
  11. namespace Carbon::Check {
  12. // A block of code that contains pending instructions that might be needed but
  13. // that haven't been inserted yet.
  14. class PendingBlock {
  15. public:
  16. // `context` must not be null.
  17. explicit PendingBlock(Context* context) : context_(context) {}
  18. PendingBlock(const PendingBlock&) = delete;
  19. auto operator=(const PendingBlock&) -> PendingBlock& = delete;
  20. // A scope in which we will tentatively add instructions to a pending block.
  21. // If we leave the scope without inserting or merging the block, instructions
  22. // added after this point will be removed again.
  23. class DiscardUnusedInstsScope {
  24. public:
  25. // If `block` is not null, enters the scope. If `block` is null, this object
  26. // has no effect.
  27. explicit DiscardUnusedInstsScope(PendingBlock* block)
  28. : block_(block), size_(block ? block->insts_.size() : 0) {}
  29. ~DiscardUnusedInstsScope() {
  30. if (block_ && block_->insts_.size() > size_) {
  31. block_->insts_.truncate(size_);
  32. }
  33. }
  34. private:
  35. PendingBlock* block_;
  36. size_t size_;
  37. };
  38. template <typename InstT, typename LocT>
  39. requires(std::convertible_to<LocT, SemIR::LocId>)
  40. auto AddInst(LocT loc_id, InstT inst) -> SemIR::InstId {
  41. auto inst_id = AddInstInNoBlock(*context_, loc_id, inst);
  42. insts_.push_back(inst_id);
  43. return inst_id;
  44. }
  45. template <typename InstT, typename LocT>
  46. requires(std::convertible_to<LocT, SemIR::LocId>)
  47. auto AddInstWithCleanup(LocT loc_id, InstT inst) -> SemIR::InstId {
  48. auto inst_id = AddInstWithCleanupInNoBlock(*context_, loc_id, inst);
  49. insts_.push_back(inst_id);
  50. return inst_id;
  51. }
  52. // Insert the pending block of code at the current position.
  53. auto InsertHere() -> void {
  54. for (auto id : insts_) {
  55. context_->inst_block_stack().AddInstId(id);
  56. }
  57. insts_.clear();
  58. }
  59. // Replace the instruction at target_id with the instructions in this block.
  60. // The new value for target_id should be value_id. Returns the InstId that
  61. // should be used to refer to the result from now on. value_id must dominate
  62. // target_id (but see below), or refer to an instruction within this block, in
  63. // order to preserve the property that SemIR is topologically sorted.
  64. //
  65. // TODO: We don't have an implementation of a proper dominance check, so we
  66. // fake one up by comparing the order in which the insts were created.
  67. // Add a general end-of-phase dominance check and remove the one here and in
  68. // `InitializeExisting`.
  69. auto MergeReplacing(SemIR::InstId target_id, SemIR::InstId value_id)
  70. -> SemIR::InstId {
  71. CARBON_CHECK(target_id != value_id);
  72. CARBON_CHECK(context_->insts().GetRawIndex(value_id) <=
  73. context_->insts().GetRawIndex(target_id) ||
  74. llvm::is_contained(insts_, value_id),
  75. "Splice might break dominance condition");
  76. SemIR::LocIdAndInst value = context_->insts().GetWithLocId(value_id);
  77. auto result_id = value_id;
  78. if (insts_.size() == 1 && insts_[0] == value_id) {
  79. // The block is {value_id}. Replace `target_id` with the instruction
  80. // referred to by `value_id`. This is intended to be the common case.
  81. result_id = target_id;
  82. } else {
  83. // Anything else: splice it into the IR, replacing `target_id`. This
  84. // includes empty blocks, which `Add` handles.
  85. value.inst =
  86. SemIR::SpliceBlock{.type_id = value.inst.type_id(),
  87. .block_id = context_->inst_blocks().Add(insts_),
  88. .result_id = value_id};
  89. }
  90. ReplaceLocIdAndInstBeforeConstantUse(*context_, target_id, value);
  91. // Prepare to stash more pending instructions.
  92. insts_.clear();
  93. return result_id;
  94. }
  95. private:
  96. Context* context_;
  97. llvm::SmallVector<SemIR::InstId> insts_;
  98. };
  99. } // namespace Carbon::Check
  100. #endif // CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_