Bläddra i källkod

Allow Worklist construction with an initial InstBlockId (#4776)

This makes InstFingerprinter::GetOrCompute for InstId and InstBlockId
more similar, in that they just construct a Worklist and call Run.
    
The Worklist::Run method differentiates if the next todo item is an
InstBlockId and in that case it adds everything in the block to its
todo list and continues processing.
    
We only use the fingerprint of an InstBlockId if its at the bottom of
the todo stack, which is the case when it's placed there initially by
InstFingerprinter::GetOrCompute. We could cache it but we currently
do not. If we did, we could also cache other InstBlockIds found
inside instructions, but at the moment we skip through them and
add their instructions rather than adding the InstBlockId to the
todo stack.
Dana Jansens 1 år sedan
förälder
incheckning
21998f6a65
1 ändrade filer med 40 tillägg och 23 borttagningar
  1. 40 23
      toolchain/sem_ir/inst_fingerprinter.cpp

+ 40 - 23
toolchain/sem_ir/inst_fingerprinter.cpp

@@ -4,6 +4,8 @@
 
 #include "toolchain/sem_ir/inst_fingerprinter.h"
 
+#include <variant>
+
 #include "common/ostream.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
@@ -19,7 +21,8 @@ struct Worklist {
   // The file containing the instruction we're currently processing.
   const File* sem_ir = nullptr;
   // The instructions we need to compute fingerprints for.
-  llvm::SmallVector<std::pair<const File*, InstId>> todo;
+  llvm::SmallVector<std::pair<const File*, std::variant<InstId, InstBlockId>>>
+      todo;
   // The contents of the current instruction as accumulated so far. This is used
   // to build a Merkle tree containing a fingerprint for the current
   // instruction.
@@ -28,12 +31,6 @@ struct Worklist {
   // the cache if not already present.
   Map<std::pair<const File*, InstId>, uint64_t>* fingerprints;
 
-  // Prepare to fingerprint a new instruction.
-  auto Prepare(const File* file) -> void {
-    sem_ir = file;
-    contents.clear();
-  }
-
   // Finish fingerprinting and compute the fingerprint.
   auto Finish() -> uint64_t { return llvm::stable_hash_combine(contents); }
 
@@ -301,7 +298,39 @@ struct Worklist {
   auto Run() -> uint64_t {
     CARBON_CHECK(!todo.empty());
     while (true) {
-      auto [next_sem_ir, next_inst_id] = todo.back();
+      const size_t init_size = todo.size();
+      auto [next_sem_ir, next_inst_id_or_block] = todo.back();
+
+      sem_ir = next_sem_ir;
+      contents.clear();
+
+      if (auto* inst_block_id =
+              std::get_if<InstBlockId>(&next_inst_id_or_block)) {
+        // Add all the instructions in the block so they all contribute to the
+        // `contents`.
+        Add(*inst_block_id);
+
+        // If we didn't add any more work, then we have a fingerprint for the
+        // instruction block, otherwise we wait until that work is completed. If
+        // the block is the last thing in `todo`, we return the fingerprint.
+        // Otherwise we would just discard it because we don't currently cache
+        // the fingerprint for blocks, but we really only expect `InstBlockId`
+        // to be at the bottom of the `todo` stack since they are not added to
+        // `todo` during Run().
+        if (todo.size() == init_size) {
+          auto fingerprint = Finish();
+          todo.pop_back();
+          CARBON_CHECK(todo.empty(),
+                       "An InstBlockId was inserted into `todo` during Run()");
+          return fingerprint;
+        }
+
+        // Move on to processing the instructions in the block; we will come
+        // back to this branch once they are done.
+        continue;
+      }
+
+      auto next_inst_id = std::get<InstId>(next_inst_id_or_block);
 
       // If we already have a fingerprint for this instruction, we have nothing
       // to do. Just pop it from `todo`.
@@ -317,13 +346,9 @@ struct Worklist {
       // Keep this instruction in `todo` for now. If we add more work, we'll
       // finish that work and process this instruction again, and if not, we'll
       // pop the instruction at the end of the loop.
-      size_t init_size = todo.size();
       auto inst = next_sem_ir->insts().Get(next_inst_id);
       auto [arg0_kind, arg1_kind] = inst.ArgKinds();
 
-      // Prepare to fingerprint this instruction.
-      Prepare(next_sem_ir);
-
       // Add the instruction's fields to the contents.
       Add(inst.kind());
 
@@ -362,17 +387,9 @@ auto InstFingerprinter::GetOrCompute(const File* file, InstId inst_id)
 
 auto InstFingerprinter::GetOrCompute(const File* file,
                                      InstBlockId inst_block_id) -> uint64_t {
-  Worklist worklist = {.todo = {}, .fingerprints = &fingerprints_};
-  worklist.Prepare(file);
-  worklist.Add(inst_block_id);
-  if (!worklist.todo.empty()) {
-    worklist.Run();
-    worklist.Prepare(file);
-    worklist.Add(inst_block_id);
-  }
-  CARBON_CHECK(worklist.todo.empty(),
-               "Should not require more than two passes.");
-  return worklist.Finish();
+  Worklist worklist = {.todo = {{file, inst_block_id}},
+                       .fingerprints = &fingerprints_};
+  return worklist.Run();
 }
 
 }  // namespace Carbon::SemIR