specific_coalescer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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. #include "toolchain/lower/specific_coalescer.h"
  5. #include "common/check.h"
  6. #include "common/vlog.h"
  7. #include "toolchain/lower/file_context.h"
  8. namespace Carbon::Lower {
  9. SpecificCoalescer::SpecificCoalescer(llvm::raw_ostream* vlog_stream,
  10. const SemIR::SpecificStore& specifics)
  11. : vlog_stream_(vlog_stream),
  12. lowered_specifics_type_fingerprint_(specifics, {}),
  13. lowered_specific_fingerprint_(specifics, {}),
  14. equivalent_specifics_(specifics, SemIR::SpecificId::None) {}
  15. auto SpecificCoalescer::CoalesceEquivalentSpecifics(
  16. LoweredSpecificsStore& lowered_specifics,
  17. LoweredLlvmFunctionStore& lowered_llvm_functions) -> void {
  18. for (auto& specifics : lowered_specifics.values()) {
  19. // Collect specifics to delete for each generic. Replace and remove each
  20. // after processing all specifics for a generic. Note, we could also
  21. // replace and remove all specifics after processing all generics.
  22. llvm::SmallVector<SemIR::SpecificId> specifics_to_delete;
  23. // i cannot be unsigned due to the comparison with a negative number when
  24. // the specifics vector is empty.
  25. for (int i = 0; i < static_cast<int>(specifics.size()) - 1; ++i) {
  26. // This specific was already replaced, skip it.
  27. if (equivalent_specifics_.Get(specifics[i]).has_value() &&
  28. equivalent_specifics_.Get(specifics[i]) != specifics[i]) {
  29. specifics_to_delete.push_back(specifics[i]);
  30. specifics[i] = specifics[specifics.size() - 1];
  31. specifics.pop_back();
  32. --i;
  33. continue;
  34. }
  35. // TODO: Improve quadratic behavior by using a single hash based on
  36. // `lowered_specifics_type_fingerprint_` and `common_fingerprint`.
  37. for (int j = i + 1; j < static_cast<int>(specifics.size()); ++j) {
  38. // When the specific was already replaced, skip it.
  39. if (equivalent_specifics_.Get(specifics[j]).has_value() &&
  40. equivalent_specifics_.Get(specifics[j]) != specifics[j]) {
  41. specifics_to_delete.push_back(specifics[j]);
  42. specifics[j] = specifics[specifics.size() - 1];
  43. specifics.pop_back();
  44. --j;
  45. continue;
  46. }
  47. // When the two specifics are not equivalent due to the function type
  48. // info stored in lowered_specifics_types, mark non-equivalance. This
  49. // can be reused to short-cut another path and continue the search for
  50. // other equivalences.
  51. if (!AreFunctionTypesEquivalent(specifics[i], specifics[j])) {
  52. InsertPair(specifics[i], specifics[j], non_equivalent_specifics_);
  53. continue;
  54. }
  55. Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>
  56. visited_equivalent_specifics;
  57. InsertPair(specifics[i], specifics[j], visited_equivalent_specifics);
  58. // Function type information matches; check usages inside the function
  59. // body that are dependent on the specific. This information has been
  60. // stored in lowered_states while lowering each function body.
  61. if (AreFunctionBodiesEquivalent(specifics[i], specifics[j],
  62. visited_equivalent_specifics)) {
  63. // When processing equivalences, we may change the canonical specific
  64. // multiple times, so we don't delete replaced specifics until the
  65. // end.
  66. visited_equivalent_specifics.ForEach(
  67. [&](std::pair<SemIR::SpecificId, SemIR::SpecificId>
  68. equivalent_entry) {
  69. CARBON_VLOG("Found equivalent specifics: {0}, {1}",
  70. equivalent_entry.first, equivalent_entry.second);
  71. ProcessSpecificEquivalence(equivalent_entry);
  72. });
  73. // Removed the replaced specific from the list of emitted specifics.
  74. // Only the top level, since the others are somewhere else in the
  75. // vector, they will be found and removed during processing.
  76. if (equivalent_specifics_.Get(specifics[j]).has_value() &&
  77. equivalent_specifics_.Get(specifics[j]) != specifics[j]) {
  78. specifics_to_delete.push_back(specifics[j]);
  79. specifics[j] = specifics[specifics.size() - 1];
  80. specifics.pop_back();
  81. --j;
  82. } else {
  83. // j was the canonical one, remove specifics[i], exit j loop.
  84. specifics_to_delete.push_back(specifics[i]);
  85. specifics[i] = specifics[specifics.size() - 1];
  86. specifics.pop_back();
  87. --i;
  88. break;
  89. }
  90. } else {
  91. // Only mark non-equivalence based on state for starting specifics.
  92. InsertPair(specifics[i], specifics[j], non_equivalent_specifics_);
  93. }
  94. }
  95. }
  96. // Once all equivalences are found for a generic, update and delete up
  97. // equivalent specifics.
  98. for (auto specific_id : specifics_to_delete) {
  99. UpdateAndDeleteLLVMFunction(lowered_llvm_functions, specific_id);
  100. }
  101. }
  102. }
  103. auto SpecificCoalescer::ProcessSpecificEquivalence(
  104. std::pair<SemIR::SpecificId, SemIR::SpecificId> pair) -> void {
  105. auto [specific_id1, specific_id2] = pair;
  106. CARBON_CHECK(specific_id1.has_value() && specific_id2.has_value(),
  107. "Expected values in equivalence check");
  108. auto get_canon = [&](SemIR::SpecificId specific_id) {
  109. auto equiv_id = equivalent_specifics_.Get(specific_id);
  110. return equiv_id.has_value() ? equiv_id : specific_id;
  111. };
  112. auto canon_id1 = get_canon(specific_id1);
  113. auto canon_id2 = get_canon(specific_id2);
  114. if (canon_id1 == canon_id2) {
  115. // Already equivalent, there was a previous replacement.
  116. return;
  117. }
  118. if (canon_id1.index >= canon_id2.index) {
  119. // Prefer the earlier index for canonical values.
  120. std::swap(canon_id1, canon_id2);
  121. }
  122. // Update equivalent_specifics_ for all. This is used as an indicator that
  123. // this specific_id may be the canonical one when reducing the equivalence
  124. // chains in `IsKnownEquivalence`.
  125. equivalent_specifics_.Set(specific_id1, canon_id1);
  126. equivalent_specifics_.Set(specific_id2, canon_id1);
  127. equivalent_specifics_.Set(canon_id2, canon_id1);
  128. // Only update the canonical for itself if it has no value, otherwise a
  129. // "better" canonical was previously added and the chain will be followed
  130. // when deleting specifics, by calling `UpdateEquivalentSpecific`.
  131. if (!equivalent_specifics_.Get(canon_id1).has_value()) {
  132. equivalent_specifics_.Set(canon_id1, canon_id1);
  133. }
  134. }
  135. auto SpecificCoalescer::UpdateEquivalentSpecific(SemIR::SpecificId specific_id)
  136. -> void {
  137. if (!equivalent_specifics_.Get(specific_id).has_value()) {
  138. return;
  139. }
  140. llvm::SmallVector<SemIR::SpecificId> stack;
  141. SemIR::SpecificId specific_to_update = specific_id;
  142. SemIR::SpecificId equivalent = equivalent_specifics_.Get(specific_to_update);
  143. SemIR::SpecificId equivalent_next = equivalent_specifics_.Get(equivalent);
  144. while (equivalent != equivalent_next) {
  145. stack.push_back(specific_to_update);
  146. specific_to_update = equivalent;
  147. equivalent = equivalent_next;
  148. equivalent_next = equivalent_specifics_.Get(equivalent_next);
  149. }
  150. for (auto specific : stack) {
  151. equivalent_specifics_.Set(specific, equivalent);
  152. }
  153. }
  154. auto SpecificCoalescer::UpdateAndDeleteLLVMFunction(
  155. LoweredLlvmFunctionStore& lowered_llvm_functions,
  156. SemIR::SpecificId specific_id) -> void {
  157. UpdateEquivalentSpecific(specific_id);
  158. auto& old_function = lowered_llvm_functions.Get(specific_id);
  159. auto& new_function =
  160. lowered_llvm_functions.Get(equivalent_specifics_.Get(specific_id));
  161. old_function->llvm_function->replaceAllUsesWith(new_function->llvm_function);
  162. old_function->llvm_function->eraseFromParent();
  163. lowered_llvm_functions.Set(specific_id, new_function);
  164. }
  165. auto SpecificCoalescer::IsKnownEquivalence(SemIR::SpecificId specific_id1,
  166. SemIR::SpecificId specific_id2)
  167. -> bool {
  168. if (!equivalent_specifics_.Get(specific_id1).has_value() ||
  169. !equivalent_specifics_.Get(specific_id2).has_value()) {
  170. return false;
  171. }
  172. UpdateEquivalentSpecific(specific_id1);
  173. UpdateEquivalentSpecific(specific_id2);
  174. return equivalent_specifics_.Get(specific_id1) ==
  175. equivalent_specifics_.Get(specific_id2);
  176. }
  177. auto SpecificCoalescer::AreFunctionTypesEquivalent(
  178. SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2) -> bool {
  179. CARBON_CHECK(specific_id1.has_value() && specific_id2.has_value());
  180. return lowered_specifics_type_fingerprint_.Get(specific_id1) ==
  181. lowered_specifics_type_fingerprint_.Get(specific_id2);
  182. }
  183. auto SpecificCoalescer::AreFunctionBodiesEquivalent(
  184. SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
  185. Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>&
  186. visited_equivalent_specifics) -> bool {
  187. llvm::SmallVector<std::pair<SemIR::SpecificId, SemIR::SpecificId>> worklist;
  188. worklist.push_back({specific_id1, specific_id2});
  189. while (!worklist.empty()) {
  190. auto outer_pair = worklist.pop_back_val();
  191. auto [specific_id1, specific_id2] = outer_pair;
  192. auto state1 = lowered_specific_fingerprint_.Get(specific_id1);
  193. auto state2 = lowered_specific_fingerprint_.Get(specific_id2);
  194. if (state1.common_fingerprint != state2.common_fingerprint) {
  195. InsertPair(specific_id1, specific_id2, non_equivalent_specifics_);
  196. return false;
  197. }
  198. if (state1.specific_fingerprint == state2.specific_fingerprint) {
  199. continue;
  200. }
  201. // A size difference should have been detected by the common fingerprint.
  202. CARBON_CHECK(state1.calls.size() == state2.calls.size(),
  203. "Number of specific calls expected to be the same.");
  204. for (auto [state1_call, state2_call] :
  205. llvm::zip_equal(state1.calls, state2.calls)) {
  206. if (state1_call != state2_call) {
  207. if (ContainsPair(state1_call, state2_call, non_equivalent_specifics_)) {
  208. return false;
  209. }
  210. if (IsKnownEquivalence(state1_call, state2_call)) {
  211. continue;
  212. }
  213. if (!InsertPair(state1_call, state2_call,
  214. visited_equivalent_specifics)) {
  215. continue;
  216. }
  217. // Leave the added equivalence pair in place and continue.
  218. worklist.push_back({state1_call, state2_call});
  219. }
  220. }
  221. }
  222. return true;
  223. }
  224. auto SpecificCoalescer::InsertPair(
  225. SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
  226. Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
  227. -> bool {
  228. if (specific_id1.index > specific_id2.index) {
  229. std::swap(specific_id1.index, specific_id2.index);
  230. }
  231. auto insert_result =
  232. set_of_pairs.Insert(std::make_pair(specific_id1, specific_id2));
  233. return insert_result.is_inserted();
  234. }
  235. auto SpecificCoalescer::ContainsPair(
  236. SemIR::SpecificId specific_id1, SemIR::SpecificId specific_id2,
  237. const Set<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
  238. -> bool {
  239. if (specific_id1.index > specific_id2.index) {
  240. std::swap(specific_id1.index, specific_id2.index);
  241. }
  242. return set_of_pairs.Contains(std::make_pair(specific_id1, specific_id2));
  243. }
  244. } // namespace Carbon::Lower