[llvm] [CodeGen] Source-qualify hashes of module-local globals for GFM and o… (PR #219938)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 31 03:40:04 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-ir

Author: Zhaoxuan Jiang (nocchijiang)

<details>
<summary>Changes</summary>

…utliner

Global function merging and outliner compare hashes across modules. Some globals are hashed by name, so two modules that each define a same-named global with local linkage produce identical operand hashes although they denote different storage. The hash collision caused both GFM and GFO to make quite a few non-beneficial optimization decisions.

Add a `SourceQualifyLocalGlobals` option to IR `StructuralHash` and MIR `stableHashValue`. When set, the name-based hash of a global with local linkage is combined with the source file name of its module. The module identifier itself is not suitable: the linker may name the module after an unstable temporary LTO object file, which would make the supposedly stable hash vary from run to run and between the generate and use rounds. Content-based hashes are unchanged, so equivalent literals still merge across modules.

The option defaults to off, so other users of `stableHashValue` and `StructuralHash` keep their hashes.

---

Patch is 25.09 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/219938.diff


10 Files Affected:

- (modified) llvm/include/llvm/CodeGen/MachineStableHash.h (+4-2) 
- (modified) llvm/include/llvm/IR/StructuralHash.h (+12-2) 
- (modified) llvm/lib/CodeGen/GlobalMergeFunctions.cpp (+10-2) 
- (modified) llvm/lib/CodeGen/MachineOutliner.cpp (+12-2) 
- (modified) llvm/lib/CodeGen/MachineStableHash.cpp (+6-4) 
- (modified) llvm/lib/IR/StructuralHash.cpp (+38-10) 
- (added) llvm/test/CodeGen/AArch64/cgdata-merge-source-qualified-globals.ll (+60) 
- (added) llvm/test/CodeGen/AArch64/cgdata-outline-source-qualified-globals.ll (+79) 
- (modified) llvm/unittests/IR/StructuralHashTest.cpp (+50) 
- (modified) llvm/unittests/MIR/MachineStableHashTest.cpp (+94) 


``````````diff
diff --git a/llvm/include/llvm/CodeGen/MachineStableHash.h b/llvm/include/llvm/CodeGen/MachineStableHash.h
index d6b16c101c550..c1d2f094f9995 100644
--- a/llvm/include/llvm/CodeGen/MachineStableHash.h
+++ b/llvm/include/llvm/CodeGen/MachineStableHash.h
@@ -23,11 +23,13 @@ class MachineFunction;
 class MachineInstr;
 class MachineOperand;
 
-LLVM_ABI stable_hash stableHashValue(const MachineOperand &MO);
+LLVM_ABI stable_hash stableHashValue(const MachineOperand &MO,
+                                     bool SourceQualifyLocalGlobals = false);
 LLVM_ABI stable_hash stableHashValue(const MachineInstr &MI,
                                      bool HashVRegs = false,
                                      bool HashConstantPoolIndices = false,
-                                     bool HashMemOperands = false);
+                                     bool HashMemOperands = false,
+                                     bool SourceQualifyLocalGlobals = false);
 LLVM_ABI stable_hash stableHashValue(const MachineBasicBlock &MBB);
 LLVM_ABI stable_hash stableHashValue(const MachineFunction &MF);
 
diff --git a/llvm/include/llvm/IR/StructuralHash.h b/llvm/include/llvm/IR/StructuralHash.h
index fc4b97ee2d41e..f1c697a959848 100644
--- a/llvm/include/llvm/IR/StructuralHash.h
+++ b/llvm/include/llvm/IR/StructuralHash.h
@@ -34,7 +34,14 @@ LLVM_ABI stable_hash StructuralHash(const Function &F,
                                     bool DetailedHash = false);
 
 /// Returns a hash of the global variable \p G.
-LLVM_ABI stable_hash StructuralHash(const GlobalVariable &G);
+/// \param SourceQualifyLocalGlobals Whether to combine the hash of a global
+/// with local linkage with the source file name of its module. Such globals
+/// are hashed by name, and the same name in different modules denotes
+/// different storage; qualifying with the source lets clients that compare
+/// hashes across modules tell them apart. Globals hashed by content are not
+/// affected.
+LLVM_ABI stable_hash StructuralHash(const GlobalVariable &G,
+                                    bool SourceQualifyLocalGlobals = false);
 
 /// Returns a hash of the module \p M by hashing all functions and global
 /// variables contained within. \param M The module to hash. \param DetailedHash
@@ -81,9 +88,12 @@ struct FunctionHashInfo {
 /// \param F The function to hash.
 /// \param IgnoreOp A callable that takes an instruction and an operand index,
 /// and returns true if the operand should be ignored in the hash computation.
+/// \param SourceQualifyLocalGlobals Whether to qualify the hashes of globals
+/// with local linkage with their module's source file name.
 /// \return A FunctionHashInfo structure
 LLVM_ABI FunctionHashInfo
-StructuralHashWithDifferences(const Function &F, IgnoreOperandFunc IgnoreOp);
+StructuralHashWithDifferences(const Function &F, IgnoreOperandFunc IgnoreOp,
+                              bool SourceQualifyLocalGlobals = false);
 
 } // end namespace llvm
 
diff --git a/llvm/lib/CodeGen/GlobalMergeFunctions.cpp b/llvm/lib/CodeGen/GlobalMergeFunctions.cpp
index dee20d601359f..356a93036adf1 100644
--- a/llvm/lib/CodeGen/GlobalMergeFunctions.cpp
+++ b/llvm/lib/CodeGen/GlobalMergeFunctions.cpp
@@ -146,6 +146,14 @@ static bool ignoreOp(const Instruction *I, unsigned OpIdx) {
   return true;
 }
 
+static FunctionHashInfo hashFunction(const Function &F) {
+  // Globals with local linkage are qualified with their module's source file
+  // name so that same-named locals in different modules are not mistaken for
+  // identical operands when the merged map is finalized across modules.
+  return StructuralHashWithDifferences(F, ignoreOp,
+                                       /*SourceQualifyLocalGlobals=*/true);
+}
+
 void GlobalMergeFunc::analyze(Module &M) {
   ++NumAnalyzedModues;
   for (Function &Func : M) {
@@ -153,7 +161,7 @@ void GlobalMergeFunc::analyze(Module &M) {
     if (isEligibleFunction(&Func)) {
       ++NumEligibleFunctions;
 
-      auto FI = llvm::StructuralHashWithDifferences(Func, ignoreOp);
+      auto FI = hashFunction(Func);
 
       // Convert the operand map to a vector for a serialization-friendly
       // format.
@@ -410,7 +418,7 @@ bool GlobalMergeFunc::merge(Module &M, const StableFunctionMap *FunctionMap) {
   for (auto &F : M) {
     if (!isEligibleFunction(&F))
       continue;
-    auto FI = llvm::StructuralHashWithDifferences(F, ignoreOp);
+    auto FI = hashFunction(F);
     if (FunctionMap->contains(FI.FunctionHash))
       HashToFuncs[FI.FunctionHash].emplace_back(&F, std::move(FI));
   }
diff --git a/llvm/lib/CodeGen/MachineOutliner.cpp b/llvm/lib/CodeGen/MachineOutliner.cpp
index 9c92c7bf2369b..7a6d5415a76d5 100644
--- a/llvm/lib/CodeGen/MachineOutliner.cpp
+++ b/llvm/lib/CodeGen/MachineOutliner.cpp
@@ -642,6 +642,16 @@ void MachineOutliner::emitOutlinedFunctionRemark(OutlinedFunction &OF) {
   MORE.emit(R);
 }
 
+static stable_hash hashInstruction(const MachineInstr &MI) {
+  // Globals with local linkage are qualified with their module's source file
+  // name so that same-named locals in different modules do not match when hash
+  // sequences are compared across modules.
+  return stableHashValue(MI, /*HashVRegs=*/false,
+                         /*HashConstantPoolIndices=*/false,
+                         /*HashMemOperands=*/false,
+                         /*SourceQualifyLocalGlobals=*/true);
+}
+
 struct MatchedEntry {
   unsigned StartIdx;
   unsigned EndIdx;
@@ -673,7 +683,7 @@ static SmallVector<MatchedEntry> getMatchedEntries(InstructionMapper &Mapper) {
 
   auto getStableHashAndFollow =
       [](const MachineInstr &MI, const HashNode *CurrNode) -> const HashNode * {
-    stable_hash StableHash = stableHashValue(MI);
+    stable_hash StableHash = hashInstruction(MI);
     if (!StableHash)
       return nullptr;
     auto It = CurrNode->Successors.find(StableHash);
@@ -856,7 +866,7 @@ void MachineOutliner::computeAndPublishHashSequence(MachineFunction &MF,
   SmallVector<stable_hash> OutlinedHashSequence;
   for (auto &MBB : MF) {
     for (auto &NewMI : MBB) {
-      stable_hash Hash = stableHashValue(NewMI);
+      stable_hash Hash = hashInstruction(NewMI);
       if (!Hash) {
         OutlinedHashSequence.clear();
         break;
diff --git a/llvm/lib/CodeGen/MachineStableHash.cpp b/llvm/lib/CodeGen/MachineStableHash.cpp
index 2f5f5aeccb2e4..c30f23f350c63 100644
--- a/llvm/lib/CodeGen/MachineStableHash.cpp
+++ b/llvm/lib/CodeGen/MachineStableHash.cpp
@@ -56,7 +56,8 @@ STATISTIC(StableHashBailingMetadataUnsupported,
           "Number of encountered unsupported MachineOperands that were "
           "Metadata of an unsupported kind while computing stable hashes");
 
-stable_hash llvm::stableHashValue(const MachineOperand &MO) {
+stable_hash llvm::stableHashValue(const MachineOperand &MO,
+                                  bool SourceQualifyLocalGlobals) {
   switch (MO.getType()) {
   case MachineOperand::MO_Register:
     if (MO.getReg().isVirtual()) {
@@ -97,7 +98,7 @@ stable_hash llvm::stableHashValue(const MachineOperand &MO) {
     const GlobalValue *GV = MO.getGlobal();
     stable_hash GVHash = 0;
     if (auto *GVar = dyn_cast<GlobalVariable>(GV))
-      GVHash = StructuralHash(*GVar);
+      GVHash = StructuralHash(*GVar, SourceQualifyLocalGlobals);
     if (!GVHash) {
       if (!GV->hasName()) {
         ++StableHashBailingGlobalAddress;
@@ -191,7 +192,8 @@ stable_hash llvm::stableHashValue(const MachineOperand &MO) {
 /// useful for CSE, etc.
 stable_hash llvm::stableHashValue(const MachineInstr &MI, bool HashVRegs,
                                   bool HashConstantPoolIndices,
-                                  bool HashMemOperands) {
+                                  bool HashMemOperands,
+                                  bool SourceQualifyLocalGlobals) {
   // Build up a buffer of hash code components.
   SmallVector<stable_hash, 16> HashComponents;
   HashComponents.reserve(MI.getNumOperands() + MI.getNumMemOperands() + 2);
@@ -207,7 +209,7 @@ stable_hash llvm::stableHashValue(const MachineInstr &MI, bool HashVRegs,
       continue;
     }
 
-    stable_hash StableHash = stableHashValue(MO);
+    stable_hash StableHash = stableHashValue(MO, SourceQualifyLocalGlobals);
     if (!StableHash)
       return 0;
     HashComponents.push_back(StableHash);
diff --git a/llvm/lib/IR/StructuralHash.cpp b/llvm/lib/IR/StructuralHash.cpp
index 1c617c100c7dc..d1919fcc9f42b 100644
--- a/llvm/lib/IR/StructuralHash.cpp
+++ b/llvm/lib/IR/StructuralHash.cpp
@@ -35,6 +35,13 @@ class StructuralHashImpl {
 
   /// IgnoreOp is a function that returns true if the operand should be ignored.
   IgnoreOperandFunc IgnoreOp = nullptr;
+  /// Whether name-based hashes of globals with local linkage are qualified
+  /// with the source file name of their module.
+  bool SourceQualifyLocalGlobals;
+  /// Lazily computed hash of the source file name. An instance only hashes
+  /// values from a single module, so one cached value suffices; 0 means the
+  /// source file name is empty and hashes are left unqualified.
+  std::optional<stable_hash> SourceNameHash;
   /// A mapping from instruction indices to instruction pointers.
   /// The index represents the position of an instruction based on the order in
   /// which it is first encountered.
@@ -57,8 +64,10 @@ class StructuralHashImpl {
 public:
   StructuralHashImpl() = delete;
   explicit StructuralHashImpl(bool DetailedHash,
-                              IgnoreOperandFunc IgnoreOp = nullptr)
-      : DetailedHash(DetailedHash), IgnoreOp(IgnoreOp) {
+                              IgnoreOperandFunc IgnoreOp = nullptr,
+                              bool SourceQualifyLocalGlobals = false)
+      : DetailedHash(DetailedHash), IgnoreOp(IgnoreOp),
+        SourceQualifyLocalGlobals(SourceQualifyLocalGlobals) {
     if (IgnoreOp) {
       IndexInstruction = std::make_unique<IndexInstrMap>();
       IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>();
@@ -77,9 +86,23 @@ class StructuralHashImpl {
     return hashAPInt(F.bitcastToAPInt());
   }
 
-  static stable_hash hashGlobalVariable(const GlobalVariable &GVar) {
+  stable_hash hashGlobalName(const GlobalVariable &GVar) {
+    stable_hash GlobalHash = hashGlobalValue(&GVar);
+    if (!GlobalHash || !SourceQualifyLocalGlobals || !GVar.hasLocalLinkage())
+      return GlobalHash;
+    if (!SourceNameHash) {
+      StringRef SourceName = GVar.getParent()->getSourceFileName();
+      SourceNameHash =
+          SourceName.empty() ? stable_hash(0) : stable_hash_name(SourceName);
+    }
+    if (!*SourceNameHash)
+      return GlobalHash;
+    return stable_hash_combine(*SourceNameHash, GlobalHash);
+  }
+
+  stable_hash hashGlobalVariable(const GlobalVariable &GVar) {
     if (!GVar.hasInitializer())
-      return hashGlobalValue(&GVar);
+      return hashGlobalName(GVar);
 
     // Hash the contents of a string.
     if (GVar.getName().starts_with(".str")) {
@@ -102,7 +125,7 @@ class StructuralHashImpl {
           return hashConstant(GVar.getInitializer());
     }
 
-    return hashGlobalValue(&GVar);
+    return hashGlobalName(GVar);
   }
 
   static stable_hash hashGlobalValue(const GlobalValue *GV) {
@@ -115,7 +138,7 @@ class StructuralHashImpl {
   // FunctionComparator::cmpConstants() in FunctionComparator.cpp, but here
   // we're interested in computing a hash rather than comparing two Constants.
   // Some of the logic is simplified, e.g, we don't expand GEPOperator.
-  static stable_hash hashConstant(const Constant *C) {
+  stable_hash hashConstant(const Constant *C) {
     SmallVector<stable_hash> Hashes;
 
     Type *Ty = C->getType();
@@ -332,8 +355,11 @@ stable_hash llvm::StructuralHash(const Function &F, bool DetailedHash) {
   return H.getHash();
 }
 
-stable_hash llvm::StructuralHash(const GlobalVariable &GVar) {
-  return StructuralHashImpl::hashGlobalVariable(GVar);
+stable_hash llvm::StructuralHash(const GlobalVariable &GVar,
+                                 bool SourceQualifyLocalGlobals) {
+  StructuralHashImpl H(/*DetailedHash=*/false, /*IgnoreOp=*/nullptr,
+                       SourceQualifyLocalGlobals);
+  return H.hashGlobalVariable(GVar);
 }
 
 stable_hash llvm::StructuralHash(const Module &M, bool DetailedHash) {
@@ -344,8 +370,10 @@ stable_hash llvm::StructuralHash(const Module &M, bool DetailedHash) {
 
 FunctionHashInfo
 llvm::StructuralHashWithDifferences(const Function &F,
-                                    IgnoreOperandFunc IgnoreOp) {
-  StructuralHashImpl H(/*DetailedHash=*/true, IgnoreOp);
+                                    IgnoreOperandFunc IgnoreOp,
+                                    bool SourceQualifyLocalGlobals) {
+  StructuralHashImpl H(/*DetailedHash=*/true, IgnoreOp,
+                       SourceQualifyLocalGlobals);
   H.update(F);
   return FunctionHashInfo(H.getHash(), H.getIndexInstrMap(),
                           H.getIndexPairOpndHashMap());
diff --git a/llvm/test/CodeGen/AArch64/cgdata-merge-source-qualified-globals.ll b/llvm/test/CodeGen/AArch64/cgdata-merge-source-qualified-globals.ll
new file mode 100644
index 0000000000000..1f6562a1398bd
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/cgdata-merge-source-qualified-globals.ll
@@ -0,0 +1,60 @@
+; Verify that GFM source-qualifies name-based hashes of module-local globals
+; while leaving content-based hashes alone.
+
+; RUN: rm -rf %t && split-file %s %t
+; RUN: llc -mtriple=arm64-apple-darwin -enable-global-merge-func=true \
+; RUN:   -codegen-data-generate=true -filetype=obj %t/a.ll -o %t/a.o
+; RUN: llc -mtriple=arm64-apple-darwin -enable-global-merge-func=true \
+; RUN:   -codegen-data-generate=true -filetype=obj %t/b.ll -o %t/b.o
+; RUN: llvm-cgdata --merge %t/a.o %t/b.o -o %t/merged.cgdata
+; RUN: llc -mtriple=arm64-apple-darwin -enable-global-merge-func=true \
+; RUN:   -codegen-data-use-path=%t/merged.cgdata %t/a.ll -o - | FileCheck %s
+
+; The string stays a shared constant in the merging instance, while the local
+; global becomes a parameter materialized by the thunk.
+; CHECK-LABEL: _fa.Tgm:
+; CHECK: adrp x0, l_.str at PAGE
+; CHECK: bl _consume
+; CHECK-LABEL: _fa:
+; CHECK: adrp x0, _cacheLock at PAGE+4
+; CHECK: b _fa.Tgm
+
+;--- a.ll
+source_filename = "a.c"
+target triple = "arm64-apple-darwin"
+
+ at cacheLock = internal global [2 x i32] [i32 1, i32 2], align 4
+ at .str = private unnamed_addr constant [6 x i8] c"hello\00", align 1
+
+declare i32 @consume(ptr, i32)
+
+define i32 @fa() {
+entry:
+  %value = load volatile i32, ptr getelementptr inbounds ([2 x i32], ptr @cacheLock, i64 0, i64 1), align 4
+  %call = call i32 @consume(ptr @.str, i32 %value)
+  %a = mul i32 %call, 3
+  %b = add i32 %a, 5
+  %c = xor i32 %b, 7
+  %d = mul i32 %c, 11
+  ret i32 %d
+}
+
+;--- b.ll
+source_filename = "b.c"
+target triple = "arm64-apple-darwin"
+
+ at cacheLock = internal global [2 x i32] [i32 1, i32 2], align 4
+ at .str.1 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
+
+declare i32 @consume(ptr, i32)
+
+define i32 @fb() {
+entry:
+  %value = load volatile i32, ptr getelementptr inbounds ([2 x i32], ptr @cacheLock, i64 0, i64 1), align 4
+  %call = call i32 @consume(ptr @.str.1, i32 %value)
+  %a = mul i32 %call, 3
+  %b = add i32 %a, 5
+  %c = xor i32 %b, 7
+  %d = mul i32 %c, 11
+  ret i32 %d
+}
diff --git a/llvm/test/CodeGen/AArch64/cgdata-outline-source-qualified-globals.ll b/llvm/test/CodeGen/AArch64/cgdata-outline-source-qualified-globals.ll
new file mode 100644
index 0000000000000..f9c2c6c0a4d71
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/cgdata-outline-source-qualified-globals.ll
@@ -0,0 +1,79 @@
+; Verify that GFO source-qualifies name-based hashes of module-local globals
+; while leaving content-based hashes alone.
+
+; RUN: split-file %s %t
+; RUN: llc -mtriple=arm64-apple-darwin -enable-machine-outliner -codegen-data-generate \
+; RUN:   -filetype=obj %t/a.ll -o %t/a.o
+; RUN: llvm-cgdata --merge %t/a.o -o %t/merged.cgdata
+; RUN: llc -mtriple=arm64-apple-darwin -enable-machine-outliner -codegen-data-use-path=%t/merged.cgdata \
+; RUN:   %t/b.ll -o - | FileCheck %s
+
+; a.ll records two outlined sequences from source a.c: one referencing the
+; internal global @cache (hashed by name) and one referencing the private
+; string @.str.4 (hashed by content). In source b.c, the singleton f5
+; referencing a same-named but distinct @cache must not match the hash tree,
+; while the otherwise identically shaped singleton f6 referencing a
+; same-content string with a different symbol name is outlined.
+; CHECK-LABEL: _f5:
+; CHECK-NOT:  _OUTLINED_FUNCTION
+; CHECK:      adrp x1, _cache at PAGE
+; CHECK:      b _goo
+; CHECK-LABEL: _f6:
+; CHECK:      b _OUTLINED_FUNCTION
+; CHECK-LABEL: _OUTLINED_FUNCTION_{{.*}}:
+; CHECK:      adrp x1, l_.str.9 at PAGE
+; CHECK-NEXT: add x1, x1, l_.str.9 at PAGEOFF
+; CHECK:      b _hoo
+
+;--- a.ll
+source_filename = "a.c"
+ at .str.1 = private unnamed_addr constant [3 x i8] c"f1\00", align 1
+ at .str.2 = private unnamed_addr constant [3 x i8] c"f2\00", align 1
+ at .str.3 = private unnamed_addr constant [3 x i8] c"f3\00", align 1
+ at .str.4 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
+ at cache = internal global [4 x i32] zeroinitializer, align 4
+
+declare noundef i32 @goo(ptr noundef, ptr noundef, i32, i32, i32)
+declare noundef i32 @hoo(ptr noundef, ptr noundef, i32, i32, i32)
+
+define i32 @f1() minsize {
+entry:
+  %call = tail call noundef i32 @goo(ptr noundef nonnull @.str.1, ptr noundef nonnull @cache, i32 1, i32 2, i32 3)
+  ret i32 %call
+}
+define i32 @f2() minsize {
+entry:
+  %call = tail call noundef i32 @goo(ptr noundef nonnull @.str.2, ptr noundef nonnull @cache, i32 1, i32 2, i32 3)
+  ret i32 %call
+}
+define i32 @f3() minsize {
+entry:
+  %call = tail call noundef i32 @hoo(ptr noundef nonnull @.str.1, ptr noundef nonnull @.str.4, i32 1, i32 2, i32 3)
+  ret i32 %call
+}
+define i32 @f4() minsize {
+entry:
+  %call = tail call noundef i32 @hoo(ptr noundef nonnull @.str.3, ptr noundef nonnull @.str.4, i32 1, i32 2, i32 3)
+  ret i32 %call
+}
+
+;--- b.ll
+source_filename = "b.c"
+ at .str.5 = private unnamed_addr constant [3 x i8] c"f5\00", align 1
+ at .str.6 = private unnamed_addr constant [3 x i8] c"f6\00", align 1
+ at .str.9 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
+ at cache = internal global [4 x i32] zeroinitializer, align 4
+
+declare noundef i32 @goo(ptr noundef, ptr noundef, i32, i32, i32)
+declare noundef i32 @hoo(ptr noundef, ptr noundef, i32, i32, i32)
+
+define i32 @f5() minsize {
+entry:
+  %call = tail call noundef i32 @goo(ptr noundef nonnull @.str.5, ptr noundef nonnull @cache, i32 1, i32 2, i32 3)
+  ret i32 %call
+}
+define i32 @f6() minsize {
+entry:
+  %call = tail call noundef i32 @hoo(ptr noundef nonnull @.str.6, ptr noundef nonnull @.str.9, i32 1, i32 2, i32 3)
+  ret i32 %call
+}
diff --git a/llvm/unittests/IR/StructuralHashTest.cpp b/llvm/unittests/IR/StructuralHashTest.cpp
index 81c17120a1f6f..a8e9c40b44697 100644
--- a/llvm/unittests/IR/StructuralHashTest.cpp
+++ b/llvm/unittests/IR/StructuralHashTest.cpp
@@ -86,6 +86,56 @@ TEST(StructuralHashTest, GlobalType) {
   EXPECT_NE(StructuralHash(*M1), StructuralHash(*M2));
 }
 
+TEST(StructuralHashTest, SourceQualifiedLocalGlobals) {
+  LLVMContext Ctx;
+  // clang-format off
+  const char *IRA = "@internal = internal global i32 0\n"
+                    "@external = global i32 0\n"
+                    "@.str = private constant [5 x i8] c\"same\\00\"\n"
+                    "@\"OBJC_CLASSLIST_REFERENCES_$_\" = internal global ptr null, section \"__DATA,__objc_classrefs,regular,no_dead_strip\"\n";
+  // clang-format on
+  // The same module compiled from a different source: @internal and @external
+  // keep their names, while the content-hashed globals have different symbol
+  // identities but identical contents.
+  // clang-format off
+  const char *IRB = "@internal = internal global i32 0\n"
+                    "@external = global i32 0\n"
+                    "@.str.1 = private constant [5 x i8] c\"same\\00\"\n"
+                    "@\"OBJC_CLASSLIST_REFERENCES_$_.1\" = internal global ptr null, section \"__DATA,__objc_classrefs,regular,no_dead_strip\"\n";
+  // clang-format on
+  std::unique_ptr<Module> M1 = parseIR(Ctx, IRA);
+  std::unique_ptr<Module> M2 = parseIR(Ctx, IRB);
+  ASSERT_TRUE(M1);
+  ASSERT_TRUE(M2);
+  M1->setSourceFileName("a.c");
+  M2->setSourceFileName("b.c");
+
+  const GlobalVariable *...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/219938


More information about the llvm-commits mailing list