generic_region_stack.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. explicit GenericRegionStack(llvm::raw_ostream* vlog_stream)
  30. : vlog_stream_(vlog_stream) {
  31. // Reserve a large enough stack that we typically won't need to reallocate.
  32. constants_in_generic_stack_.reserve(4);
  33. }
  34. struct PendingGeneric {
  35. // The generic ID. May not have a value if no ID has been assigned yet.
  36. SemIR::GenericId generic_id;
  37. // The region of the generic that is being processed.
  38. SemIR::GenericInstIndex::Region region;
  39. };
  40. // Pushes a region that might be declaring or defining a generic.
  41. auto Push(PendingGeneric generic) -> void;
  42. // Pops a generic region.
  43. auto Pop() -> void;
  44. // Returns whether the stack is empty.
  45. auto Empty() const -> bool { return pending_generic_ids_.empty(); }
  46. // Sets the GenericId for the currently pending generic, once one has been
  47. // allocated.
  48. auto SetPendingGenericId(SemIR::GenericId generic_id) -> void {
  49. CARBON_CHECK(!pending_generic_ids_.back().generic_id.has_value(),
  50. "Already have a GenericId for the pending generic");
  51. pending_generic_ids_.back().generic_id = generic_id;
  52. }
  53. // Adds an instruction to the list of instructions whose type or value depends
  54. // on something in the current pending generic.
  55. auto AddDependentInst(SemIR::InstId inst_id) -> void {
  56. CARBON_CHECK(!Empty());
  57. dependent_inst_stack_.AppendToTop(inst_id);
  58. }
  59. // Adds an instruction to the eval block for the current pending generic.
  60. auto AddInstToEvalBlock(SemIR::InstId inst_id) -> void {
  61. CARBON_CHECK(!Empty());
  62. pending_eval_block_stack_.AppendToTop(inst_id);
  63. }
  64. // Returns the current pending generic.
  65. auto PeekPendingGeneric() const -> PendingGeneric {
  66. CARBON_CHECK(!Empty());
  67. return pending_generic_ids_.back();
  68. }
  69. // Returns the list of dependent instructions in the current generic region.
  70. auto PeekDependentInsts() -> llvm::ArrayRef<SemIR::InstId> {
  71. CARBON_CHECK(!Empty());
  72. return dependent_inst_stack_.PeekArray();
  73. }
  74. // Returns the contents of the eval block for the current generic region.
  75. auto PeekEvalBlock() -> llvm::ArrayRef<SemIR::InstId> {
  76. CARBON_CHECK(!Empty());
  77. return pending_eval_block_stack_.PeekArray();
  78. }
  79. // Returns the mapping from abstract constant instructions to eval block
  80. // instructions for the current generic.
  81. auto PeekConstantsInGenericMap() -> ConstantsInGenericMap& {
  82. CARBON_CHECK(!Empty());
  83. return constants_in_generic_stack_.back();
  84. }
  85. // Runs verification that the processing cleanly finished.
  86. auto VerifyOnFinish() const -> void {
  87. CARBON_CHECK(pending_generic_ids_.empty(),
  88. "pending_generic_ids_ still has {0} entries",
  89. pending_generic_ids_.size());
  90. }
  91. private:
  92. // Whether to print verbose output.
  93. llvm::raw_ostream* vlog_stream_;
  94. // The IDs of pending generics.
  95. llvm::SmallVector<PendingGeneric> pending_generic_ids_;
  96. // Contents of eval blocks for pending generics.
  97. ArrayStack<SemIR::InstId> pending_eval_block_stack_;
  98. // Instructions that depend on the current generic.
  99. ArrayStack<SemIR::InstId> dependent_inst_stack_;
  100. // Mapping from constant InstIds to the corresponding InstIds in the eval
  101. // blocks for each enclosing generic. We reserve this to a suitable size in
  102. // the constructor.
  103. llvm::SmallVector<ConstantsInGenericMap, 0> constants_in_generic_stack_;
  104. };
  105. } // namespace Carbon::Check
  106. #endif // CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_