specific_coalescer.h 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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_LOWER_SPECIFIC_COALESCER_H_
  5. #define CARBON_TOOLCHAIN_LOWER_SPECIFIC_COALESCER_H_
  6. #include "llvm/Support/BLAKE3.h"
  7. #include "toolchain/lower/context.h"
  8. #include "toolchain/sem_ir/ids.h"
  9. namespace Carbon::Lower {
  10. struct FunctionInfo;
  11. // Coalescing functionality for lowering fewer specifics of the same generic.
  12. class SpecificCoalescer {
  13. public:
  14. using LoweredSpecificsStore =
  15. FixedSizeValueStore<SemIR::GenericId,
  16. llvm::SmallVector<SemIR::SpecificId>,
  17. Tag<SemIR::CheckIRId>>;
  18. using LoweredLlvmFunctionStore =
  19. FixedSizeValueStore<SemIR::SpecificId, std::optional<FunctionInfo>,
  20. Tag<SemIR::CheckIRId>>;
  21. // Describes a specific function's body fingerprint.
  22. struct SpecificFunctionFingerprint {
  23. // Fingerprint with all specific-dependent instructions, except specific
  24. // calls. This is built by the `FunctionContext` while lowering each
  25. // instruction in the definition of a specific function.
  26. // TODO: This can be merged with the function type fingerprint, for a
  27. // single upfront non-equivalence check, and hash bucketing for deeper
  28. // equivalence evaluation.
  29. llvm::BLAKE3Result<32> common_fingerprint;
  30. // Fingerprint for all calls to specific functions (hashes all calls to
  31. // other specifics). This is built by the `FunctionContext` while lowering.
  32. llvm::BLAKE3Result<32> specific_fingerprint;
  33. // All non-hashed specific_ids of functions called.
  34. llvm::SmallVector<SemIR::SpecificId> calls;
  35. };
  36. // Takes a `SpecificStore` to help initialize related `FixedSizeValueStore`s.
  37. explicit SpecificCoalescer(llvm::raw_ostream* vlog_stream,
  38. const SemIR::SpecificStore& specifics);
  39. // Entry point for coalescing equivalent specifics. Two function definitions,
  40. // from the same generic, with different specific_ids are considered
  41. // equivalent if, at the LLVM level, one can be replaced with the other, with
  42. // no change in behavior. All LLVM types and instructions must be equivalent.
  43. auto CoalesceEquivalentSpecifics(
  44. LoweredSpecificsStore& lowered_specifics,
  45. LoweredLlvmFunctionStore& lowered_llvm_functions) -> void;
  46. // Initializes and returns a SpecificFunctionFingerprint* instance for a
  47. // specific. The internal of the fingerprint are populated during and after
  48. // lowering the function body of that specific.
  49. auto InitializeFingerprintForSpecific(SemIR::SpecificId specific_id)
  50. -> SpecificFunctionFingerprint* {
  51. if (!specific_id.has_value()) {
  52. return nullptr;
  53. }
  54. return &lowered_specific_fingerprint_.Get(specific_id);
  55. }
  56. auto CreateTypeFingerprint(SemIR::SpecificId specific_id,
  57. llvm::Type* llvm_type) -> void {
  58. llvm::BLAKE3 function_type_fingerprint;
  59. RawStringOstream os;
  60. llvm_type->print(os);
  61. function_type_fingerprint.update(os.TakeStr());
  62. function_type_fingerprint.final(
  63. lowered_specifics_type_fingerprint_.Get(specific_id));
  64. }
  65. private:
  66. // While coalescing specifics, returns whether the function types for two
  67. // specifics are equivalent. This uses a fingerprint generated for each
  68. // function type.
  69. auto AreFunctionTypesEquivalent(SemIR::SpecificId specific_id1,
  70. SemIR::SpecificId specific_id2) -> bool;
  71. // While coalescing specifics, compare the function bodies for two specifics.
  72. // This uses fingerprints generated during lowering of the function body.
  73. // The `visited_equivalent_specifics` parameter is used to track cycles in
  74. // the function callgraph, and will also return equivalent pairs of specifics
  75. // found, if the two specifics given as arguments are found to be equivalent.
  76. auto AreFunctionBodiesEquivalent(
  77. SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
  78. Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>&
  79. visited_equivalent_specifics) -> bool;
  80. // Given an equivalent pair of specifics, updates the canonical specific to
  81. // use for each of the two Specifics found to be equivalent.
  82. auto ProcessSpecificEquivalence(
  83. std::pair<SemIR::SpecificId, SemIR::SpecificId> pair) -> void;
  84. // Checks if two specific_ids are equivalent and also reduces the equivalence
  85. // chains/paths. This update ensures the canonical specific is always "one
  86. // hop away".
  87. auto IsKnownEquivalence(SemIR::SpecificId specific_id1,
  88. SemIR::SpecificId specific_id2) -> bool;
  89. // Update the tracked equivalent specific for the `SpecificId`. This may
  90. // occur a replacement was performed and a chain of such replacements needs
  91. // to be followed to discover the canonical specific for the given argument.
  92. auto UpdateEquivalentSpecific(SemIR::SpecificId specific_id) -> void;
  93. // Update the LLVM function to use for a `SpecificId` that has been found to
  94. // have another equivalent LLVM function. Replace all uses of the original
  95. // LLVM function with the equivalent one found, and delete the previous LLVM
  96. // function body.
  97. auto UpdateAndDeleteLLVMFunction(
  98. LoweredLlvmFunctionStore& lowered_llvm_functions,
  99. SemIR::SpecificId specific_id) -> void;
  100. // Inserts a pair into a set of pairs in canonical form. Also implicitly
  101. // checks entry already existed if it cannot be inserted.
  102. auto InsertPair(
  103. SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
  104. Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
  105. -> bool;
  106. // Checks if a pair is contained into a set of pairs, in canonical form.
  107. auto ContainsPair(
  108. SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
  109. const Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
  110. -> bool;
  111. // The optional vlog stream.
  112. llvm::raw_ostream* vlog_stream_;
  113. // For specifics that exist in lowered_specifics, a hash of their function
  114. // type information.
  115. FixedSizeValueStore<SemIR::SpecificId, llvm::BLAKE3Result<32>,
  116. Tag<SemIR::CheckIRId>>
  117. lowered_specifics_type_fingerprint_;
  118. // This is initialized and populated while lowering a specific.
  119. FixedSizeValueStore<SemIR::SpecificId, SpecificFunctionFingerprint,
  120. Tag<SemIR::CheckIRId>>
  121. lowered_specific_fingerprint_;
  122. // Equivalent specifics that have been found. For each specific, this points
  123. // to the canonical equivalent specific, which may also be self. We currently
  124. // define the canonical specific as the one with the lowest
  125. // `SpecificId.index`.
  126. //
  127. // Entries are initialized to `SpecificId::None`, which defines that there is
  128. // no other equivalent specific to this `SpecificId`.
  129. FixedSizeValueStore<SemIR::SpecificId, SemIR::SpecificId,
  130. Tag<SemIR::CheckIRId>>
  131. equivalent_specifics_;
  132. // Non-equivalent specifics found.
  133. // TODO: Revisit this due to its quadratic space growth.
  134. Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>
  135. non_equivalent_specifics_;
  136. };
  137. } // namespace Carbon::Lower
  138. #endif // CARBON_TOOLCHAIN_LOWER_SPECIFIC_COALESCER_H_