pending_block.h 3.2 KB

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