specific_coalescer.h 6.9 KB

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