[llvm] [IR][NFC] Expose structural hash details (PR #200480)
Yaxun Liu via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 29 09:15:47 PDT 2026
https://github.com/yxsamliu updated https://github.com/llvm/llvm-project/pull/200480
>From c6d8a425ce46ea3fb73136df87118b740b298a38 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Fri, 29 May 2026 14:39:07 -0400
Subject: [PATCH] [IR][NFC] Expose structural hash details
StructuralHash already computes block and function hashes while walking a
function. Expose those pieces so other users can reuse the same
change-detection logic instead of rebuilding a parallel hash walk.
This is NFC for existing users. The existing StructuralHash entry points
keep returning the same hashes.
This is intended for tools such as `-print-changed`, which need the
function hash for a quick skip check and block hashes to decide which
blocks changed.
---
llvm/include/llvm/IR/StructuralHash.h | 17 +++++++++++
llvm/lib/IR/StructuralHash.cpp | 39 +++++++++++++++++++++---
llvm/unittests/IR/StructuralHashTest.cpp | 27 ++++++++++++++++
3 files changed, 78 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/IR/StructuralHash.h b/llvm/include/llvm/IR/StructuralHash.h
index fc4b97ee2d41e..41ab733c596cc 100644
--- a/llvm/include/llvm/IR/StructuralHash.h
+++ b/llvm/include/llvm/IR/StructuralHash.h
@@ -15,6 +15,7 @@
#define LLVM_IR_STRUCTURALHASH_H
#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StableHashing.h"
#include "llvm/IR/Instruction.h"
#include "llvm/Support/Compiler.h"
@@ -22,9 +23,20 @@
namespace llvm {
+class BasicBlock;
class Function;
class Module;
+struct BasicBlockStructuralHashInfo {
+ const BasicBlock *BB = nullptr;
+ stable_hash BlockHash = 0;
+};
+
+struct FunctionStructuralHashInfo {
+ stable_hash FunctionHash = 0;
+ SmallVector<BasicBlockStructuralHashInfo, 0> Blocks;
+};
+
/// Returns a hash of the function \p F.
/// \param F The function to hash.
/// \param DetailedHash Whether or not to encode additional information in the
@@ -33,6 +45,11 @@ class Module;
LLVM_ABI stable_hash StructuralHash(const Function &F,
bool DetailedHash = false);
+/// Returns structural hash details for \p F, including the function hash and
+/// block hashes computed while building it.
+LLVM_ABI FunctionStructuralHashInfo
+StructuralHashWithDetails(const Function &F, bool DetailedHash = false);
+
/// Returns a hash of the global variable \p G.
LLVM_ABI stable_hash StructuralHash(const GlobalVariable &G);
diff --git a/llvm/lib/IR/StructuralHash.cpp b/llvm/lib/IR/StructuralHash.cpp
index 1c617c100c7dc..0ae2543f2b065 100644
--- a/llvm/lib/IR/StructuralHash.cpp
+++ b/llvm/lib/IR/StructuralHash.cpp
@@ -26,6 +26,7 @@ class StructuralHashImpl {
stable_hash Hash = 4;
bool DetailedHash;
+ bool CollectDetails;
// This random value acts as a block header, as otherwise the partition of
// opcodes into BBs wouldn't affect the hash, only the order of the opcodes.
@@ -42,6 +43,7 @@ class StructuralHashImpl {
/// A mapping from pairs of instruction indices and operand indices
/// to the hashes of the operands.
std::unique_ptr<IndexOperandHashMapType> IndexOperandHashMap = nullptr;
+ FunctionStructuralHashInfo Details;
/// Assign a unique ID to each Value in the order they are first seen.
DenseMap<const Value *, int> ValueToId;
@@ -57,8 +59,10 @@ class StructuralHashImpl {
public:
StructuralHashImpl() = delete;
explicit StructuralHashImpl(bool DetailedHash,
- IgnoreOperandFunc IgnoreOp = nullptr)
- : DetailedHash(DetailedHash), IgnoreOp(IgnoreOp) {
+ IgnoreOperandFunc IgnoreOp = nullptr,
+ bool CollectDetails = false)
+ : DetailedHash(DetailedHash), CollectDetails(CollectDetails),
+ IgnoreOp(IgnoreOp) {
if (IgnoreOp) {
IndexInstruction = std::make_unique<IndexInstrMap>();
IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>();
@@ -257,8 +261,11 @@ class StructuralHashImpl {
// selectively.
void update(const Function &F) {
// Declarations don't affect analyses.
- if (F.isDeclaration())
+ if (F.isDeclaration()) {
+ if (CollectDetails)
+ Details.FunctionHash = Hash;
return;
+ }
SmallVector<stable_hash> Hashes;
Hashes.emplace_back(Hash);
@@ -279,8 +286,18 @@ class StructuralHashImpl {
const BasicBlock *BB = BBs.pop_back_val();
Hashes.emplace_back(BlockHeaderHash);
- for (auto &Inst : *BB)
- Hashes.emplace_back(hashInstruction(Inst));
+ SmallVector<stable_hash> BlockHashes;
+ if (CollectDetails)
+ BlockHashes.emplace_back(BlockHeaderHash);
+
+ for (auto &Inst : *BB) {
+ stable_hash InstHash = hashInstruction(Inst);
+ Hashes.emplace_back(InstHash);
+ if (CollectDetails)
+ BlockHashes.emplace_back(InstHash);
+ }
+ if (CollectDetails)
+ Details.Blocks.push_back({BB, stable_hash_combine(BlockHashes)});
for (const BasicBlock *Succ : successors(BB))
if (VisitedBBs.insert(Succ).second)
@@ -289,6 +306,8 @@ class StructuralHashImpl {
// Update the combined hash in place.
Hash = stable_hash_combine(Hashes);
+ if (CollectDetails)
+ Details.FunctionHash = Hash;
}
void update(const GlobalVariable &GV) {
@@ -315,6 +334,8 @@ class StructuralHashImpl {
uint64_t getHash() const { return Hash; }
+ FunctionStructuralHashInfo getDetails() { return std::move(Details); }
+
std::unique_ptr<IndexInstrMap> getIndexInstrMap() {
return std::move(IndexInstruction);
}
@@ -332,6 +353,14 @@ stable_hash llvm::StructuralHash(const Function &F, bool DetailedHash) {
return H.getHash();
}
+FunctionStructuralHashInfo llvm::StructuralHashWithDetails(const Function &F,
+ bool DetailedHash) {
+ StructuralHashImpl H(DetailedHash, /*IgnoreOp=*/nullptr,
+ /*CollectDetails=*/true);
+ H.update(F);
+ return H.getDetails();
+}
+
stable_hash llvm::StructuralHash(const GlobalVariable &GVar) {
return StructuralHashImpl::hashGlobalVariable(GVar);
}
diff --git a/llvm/unittests/IR/StructuralHashTest.cpp b/llvm/unittests/IR/StructuralHashTest.cpp
index 81c17120a1f6f..2f8988697e7c5 100644
--- a/llvm/unittests/IR/StructuralHashTest.cpp
+++ b/llvm/unittests/IR/StructuralHashTest.cpp
@@ -70,6 +70,33 @@ TEST(StructuralHashTest, BasicFunction) {
StructuralHash(*M->getFunction("h")));
}
+TEST(StructuralHashTest, FunctionHashDetails) {
+ LLVMContext Ctx;
+ std::unique_ptr<Module> M = parseIR(Ctx, "define i32 @f(i32 %x) {\n"
+ "entry:\n"
+ " %a = add i32 %x, 1\n"
+ " ret i32 %a\n"
+ "}\n");
+ Function &F = *M->getFunction("f");
+
+ FunctionStructuralHashInfo Info =
+ StructuralHashWithDetails(F, /*DetailedHash=*/true);
+ EXPECT_EQ(StructuralHash(F, /*DetailedHash=*/true), Info.FunctionHash);
+ ASSERT_THAT(Info.Blocks, SizeIs(1));
+ EXPECT_EQ(&F.getEntryBlock(), Info.Blocks[0].BB);
+ EXPECT_NE(0u, Info.Blocks[0].BlockHash);
+}
+
+TEST(StructuralHashTest, FunctionHashDetailsForDeclaration) {
+ LLVMContext Ctx;
+ std::unique_ptr<Module> M = parseIR(Ctx, "declare void @f()\n");
+ Function &F = *M->getFunction("f");
+
+ FunctionStructuralHashInfo Info = StructuralHashWithDetails(F);
+ EXPECT_EQ(StructuralHash(F), Info.FunctionHash);
+ EXPECT_THAT(Info.Blocks, SizeIs(0));
+}
+
TEST(StructuralHashTest, Declaration) {
LLVMContext Ctx;
std::unique_ptr<Module> M0 = parseIR(Ctx, "");
More information about the llvm-commits
mailing list