generic_region_stack.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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_GENERIC_REGION_STACK_H_
  5. #define CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_
  6. #include "common/array_stack.h"
  7. #include "common/map.h"
  8. #include "toolchain/sem_ir/ids.h"
  9. namespace Carbon::Check {
  10. // A map from an instruction ID representing a canonical symbolic constant to an
  11. // instruction within an eval block of the generic that computes the specific
  12. // value for that constant.
  13. //
  14. // We arbitrarily use a small size of 256 bytes for the map.
  15. // TODO: Determine a better number based on measurements.
  16. using ConstantsInGenericMap = Map<SemIR::InstId, SemIR::InstId, 256>;
  17. // A stack of enclosing regions that might be declaring or defining a generic
  18. // entity. In such a region, we track the generic constructs that are used, such
  19. // as symbolic constants and types, and instructions that depend on a template
  20. // parameter.
  21. //
  22. // We split a generic into two regions -- declaration and definition -- because
  23. // these are in general introduced separately, and substituted into separately.
  24. // For example, for `class C(T:! type, N:! T) { var x: T; }`, a use such as
  25. // `C(i32, 0)*` substitutes into just the declaration, whereas a use such as
  26. // `var x: C(i32, 0) = {.x = 0};` also substitutes into the definition.
  27. class GenericRegionStack {
  28. public:
  29. GenericRegionStack() {
  30. // Reserve a large enough stack that we typically won't need to reallocate.
  31. constants_in_generic_stack_.reserve(4);
  32. }
  33. struct PendingGeneric {
  34. // The generic ID. May not have a value if no ID has been assigned yet.
  35. SemIR::GenericId generic_id;
  36. // The region of the generic that is being processed.
  37. SemIR::GenericInstIndex::Region region;
  38. };
  39. // Pushes a region that might be declaring or defining a generic.
  40. auto Push(PendingGeneric generic) -> void;
  41. // Pops a generic region.
  42. auto Pop() -> void;
  43. // Returns whether the stack is empty.
  44. auto Empty() const -> bool { return pending_generic_ids_.empty(); }
  45. // Sets the GenericId for the currently pending generic, once one has been
  46. // allocated.
  47. auto SetPendingGenericId(SemIR::GenericId generic_id) -> void {
  48. CARBON_CHECK(!pending_generic_ids_.back().generic_id.has_value(),
  49. "Already have a GenericId for the pending generic");
  50. pending_generic_ids_.back().generic_id = generic_id;
  51. }
  52. // Adds an instruction to the list of instructions whose type or value depends
  53. // on something in the current pending generic.
  54. auto AddDependentInst(SemIR::InstId inst_id) -> void {
  55. CARBON_CHECK(!Empty());
  56. dependent_inst_stack_.AppendToTop(inst_id);
  57. }
  58. // Adds an instruction to the eval block for the current pending generic.
  59. auto AddInstToEvalBlock(SemIR::InstId inst_id) -> void {
  60. CARBON_CHECK(!Empty());
  61. pending_eval_block_stack_.AppendToTop(inst_id);
  62. }
  63. // Returns the current pending generic.
  64. auto PeekPendingGeneric() const -> PendingGeneric {
  65. CARBON_CHECK(!Empty());
  66. return pending_generic_ids_.back();
  67. }
  68. // Returns the list of dependent instructions in the current generic region.
  69. auto PeekDependentInsts() -> llvm::ArrayRef<SemIR::InstId> {
  70. CARBON_CHECK(!Empty());
  71. return dependent_inst_stack_.PeekArray();
  72. }
  73. // Returns the contents of the eval block for the current generic region.
  74. auto PeekEvalBlock() -> llvm::ArrayRef<SemIR::InstId> {
  75. CARBON_CHECK(!Empty());
  76. return pending_eval_block_stack_.PeekArray();
  77. }
  78. // Returns the mapping from abstract constant instructions to eval block
  79. // instructions for the current generic.
  80. auto PeekConstantsInGenericMap() -> ConstantsInGenericMap& {
  81. CARBON_CHECK(!Empty());
  82. return constants_in_generic_stack_.back();
  83. }
  84. // Runs verification that the processing cleanly finished.
  85. auto VerifyOnFinish() const -> void {
  86. CARBON_CHECK(pending_generic_ids_.empty(),
  87. "pending_generic_ids_ still has {0} entries",
  88. pending_generic_ids_.size());
  89. }
  90. private:
  91. // The IDs of pending generics.
  92. llvm::SmallVector<PendingGeneric> pending_generic_ids_;
  93. // Contents of eval blocks for pending generics.
  94. ArrayStack<SemIR::InstId> pending_eval_block_stack_;
  95. // Instructions that depend on the current generic.
  96. ArrayStack<SemIR::InstId> dependent_inst_stack_;
  97. // Mapping from constant InstIds to the corresponding InstIds in the eval
  98. // blocks for each enclosing generic. We reserve this to a suitable size in
  99. // the constructor.
  100. llvm::SmallVector<ConstantsInGenericMap, 0> constants_in_generic_stack_;
  101. };
  102. } // namespace Carbon::Check
  103. #endif // CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_