diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index 0d6e78f1ee7e..e57ff41f0a9c 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/toolchain/sem_ir/inst_fingerprinter.cpp @@ -4,6 +4,8 @@ #include "toolchain/sem_ir/inst_fingerprinter.h" +#include + #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> todo; + llvm::SmallVector>> + 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, 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(&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(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