pending_block.h 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  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/inst.h"
  10. namespace Carbon::Check {
  11. // A block of code that contains pending instructions that might be needed but
  12. // that haven't been inserted yet.
  13. class PendingBlock {
  14. public:
  15. explicit PendingBlock(Context& context) : context_(context) {}
  16. PendingBlock(const PendingBlock&) = delete;
  17. auto operator=(const PendingBlock&) -> PendingBlock& = delete;
  18. // A scope in which we will tentatively add instructions to a pending block.
  19. // If we leave the scope without inserting or merging the block, instructions
  20. // added after this point will be removed again.
  21. class DiscardUnusedInstsScope {
  22. public:
  23. // If `block` is not null, enters the scope. If `block` is null, this object
  24. // has no effect.
  25. explicit DiscardUnusedInstsScope(PendingBlock* block)
  26. : block_(block), size_(block ? block->insts_.size() : 0) {}
  27. ~DiscardUnusedInstsScope() {
  28. if (block_ && block_->insts_.size() > size_) {
  29. block_->insts_.truncate(size_);
  30. }
  31. }
  32. private:
  33. PendingBlock* block_;
  34. size_t size_;
  35. };
  36. template <typename InstT, typename LocT>
  37. auto AddInst(LocT loc_id, InstT inst) -> SemIR::InstId {
  38. auto inst_id = AddInstInNoBlock(context_, loc_id, inst);
  39. insts_.push_back(inst_id);
  40. return inst_id;
  41. }
  42. // Insert the pending block of code at the current position.
  43. auto InsertHere() -> void {
  44. for (auto id : insts_) {
  45. context_.inst_block_stack().AddInstId(id);
  46. }
  47. insts_.clear();
  48. }
  49. // Replace the instruction at target_id with the instructions in this block.
  50. // The new value for target_id should be value_id.
  51. auto MergeReplacing(SemIR::InstId target_id, SemIR::InstId value_id) -> void {
  52. auto value = context_.insts().GetWithLocId(value_id);
  53. // There are three cases here:
  54. if (insts_.empty()) {
  55. // 1) The block is empty. Replace `target_id` with an empty splice
  56. // pointing at `value_id`.
  57. ReplaceLocIdAndInstBeforeConstantUse(
  58. context_, target_id,
  59. SemIR::LocIdAndInst(
  60. value.loc_id,
  61. SemIR::SpliceBlock{.type_id = value.inst.type_id(),
  62. .block_id = SemIR::InstBlockId::Empty,
  63. .result_id = value_id}));
  64. } else if (insts_.size() == 1 && insts_[0] == value_id) {
  65. // 2) The block is {value_id}. Replace `target_id` with the instruction
  66. // referred to by `value_id`. This is intended to be the common case.
  67. ReplaceLocIdAndInstBeforeConstantUse(context_, target_id, value);
  68. } else {
  69. // 3) Anything else: splice it into the IR, replacing `target_id`.
  70. ReplaceLocIdAndInstBeforeConstantUse(
  71. context_, target_id,
  72. SemIR::LocIdAndInst(
  73. value.loc_id,
  74. SemIR::SpliceBlock{.type_id = value.inst.type_id(),
  75. .block_id = context_.inst_blocks().Add(insts_),
  76. .result_id = value_id}));
  77. }
  78. // Prepare to stash more pending instructions.
  79. insts_.clear();
  80. }
  81. private:
  82. Context& context_;
  83. llvm::SmallVector<SemIR::InstId> insts_;
  84. };
  85. } // namespace Carbon::Check
  86. #endif // CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_