[clang] [llvm] [CIR][AMDGPU] Add support for AMDGCN trig_preop builtins (PR #197399)

Ayokunle Amodu via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 30 08:57:47 PDT 2026


https://github.com/ayokunle321 updated https://github.com/llvm/llvm-project/pull/197399

>From 773c65fb06e087abdb67a94b9ad6be96d0c594b7 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Mon, 29 Jun 2026 19:45:46 -0400
Subject: [PATCH 01/15] add detection of duplicate files

---
 clang/include/clang/Basic/SourceManager.h     |  27 +
 clang/include/clang/Serialization/ASTReader.h |   8 +
 clang/lib/Basic/SourceManager.cpp             |   5 +
 clang/lib/Serialization/ASTReader.cpp         |  58 ++
 dedup-test/a.h                                |   5 +
 dedup-test/b.h                                |   5 +
 dedup-test/module.modulemap                   |   2 +
 dedup-test/one_module_stats.txt               | 352 ++++++++++++
 dedup-test/shared.h                           | 403 +++++++++++++
 dedup-test/two_modules_stats.txt              | 535 ++++++++++++++++++
 dedup-test/use.cpp                            |   3 +
 dedup-test/use1.cpp                           |   2 +
 12 files changed, 1405 insertions(+)
 create mode 100644 dedup-test/a.h
 create mode 100644 dedup-test/b.h
 create mode 100644 dedup-test/module.modulemap
 create mode 100644 dedup-test/one_module_stats.txt
 create mode 100644 dedup-test/shared.h
 create mode 100644 dedup-test/two_modules_stats.txt
 create mode 100644 dedup-test/use.cpp
 create mode 100644 dedup-test/use1.cpp

diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h
index 1939d1aa4915e..fe9dd42989e50 100644
--- a/clang/include/clang/Basic/SourceManager.h
+++ b/clang/include/clang/Basic/SourceManager.h
@@ -755,6 +755,33 @@ class SourceManager : public RefCountedBase<SourceManager> {
   static const SourceLocation::UIntTy MaxLoadedOffset =
       1ULL << (8 * sizeof(SourceLocation::UIntTy) - 1);
 
+  /// --- Source-location de-duplication: detection (prototype, Stage 1) ---
+  /// The first global offset at which each file's SLoc entry was loaded.
+  /// Keyed by FileEntry identity (the same identity Clang uses to dedup file
+  /// content), so we recognize when a later module re-loads the *same file*
+  /// already present in the loaded address space (e.g. a shared header).
+  llvm::DenseMap<const FileEntry *, SourceLocation::UIntTy>
+      LoadedFileFirstOffset;
+  /// Number of loaded file SLoc entries that duplicated an already-loaded file.
+  unsigned NumDuplicateLoadedFiles = 0;
+  /// SLoc address-space bytes occupied by those duplicates (the reuse prize).
+  uint64_t DuplicateLoadedBytes = 0;
+
+public:
+  /// Record a loaded file SLoc entry. If the *same file* was already loaded,
+  /// count it as a duplicate and add its size to the reusable total. Detection
+  /// only -- no behavior change.
+  void noteLoadedFileSLocEntry(const FileEntry *FE,
+                               SourceLocation::UIntTy Offset, uint64_t Size) {
+    if (!FE)
+      return;
+    if (!LoadedFileFirstOffset.try_emplace(FE, Offset).second) {
+      ++NumDuplicateLoadedFiles;
+      DuplicateLoadedBytes += Size;
+    }
+  }
+
+private:
   /// A bitmap that indicates whether the entries of LoadedSLocEntryTable
   /// have already been loaded from the external source.
   ///
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index d800af83d350b..2afc4c199afda 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2399,6 +2399,14 @@ class ASTReader : public ExternalPreprocessorSource,
   llvm::Expected<SourceLocation::UIntTy> readSLocOffset(ModuleFile *F,
                                                         unsigned Index);
 
+  /// Walk module \p F's SLoc entry records (read-only). Fills \p Offsets[i]
+  /// with each entry's local offset and \p Files[i] with its FileEntry (null
+  /// for non-file entries). Returns false if the records couldn't be read.
+  /// Shared scaffolding for source-location de-duplication.
+  bool scanLoadedSLocEntries(ModuleFile &F,
+                             SmallVectorImpl<uint32_t> &Offsets,
+                             SmallVectorImpl<const FileEntry *> &Files);
+
   /// Retrieve the module import location and module name for the
   /// given source manager entry ID.
   std::pair<SourceLocation, StringRef> getModuleImportLoc(int ID) override;
diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp
index 5540aade05ef5..194426ae2f145 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -2152,6 +2152,11 @@ void SourceManager::PrintStats() const {
                << " bytes of capacity), "
                << MaxLoadedOffset - CurrentLoadedOffset
                << "B of SLoc address space used.\n";
+  if (NumDuplicateLoadedFiles)
+    llvm::errs() << NumDuplicateLoadedFiles
+                 << " duplicate loaded file SLocEntries detected ("
+                 << DuplicateLoadedBytes
+                 << "B of SLoc address space reusable).\n";
 
   unsigned NumLineNumsComputed = 0;
   unsigned NumFileBytesMapped = 0;
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 3455b729be696..2283a9ee90b65 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -1815,6 +1815,44 @@ llvm::Error ASTReader::ReadSourceManagerBlock(ModuleFile &F) {
   }
 }
 
+bool ASTReader::scanLoadedSLocEntries(
+    ModuleFile &F, SmallVectorImpl<uint32_t> &Offsets,
+    SmallVectorImpl<const FileEntry *> &Files) {
+  unsigned N = F.LocalNumSLocEntries;
+  Offsets.assign(N, 0);
+  Files.assign(N, nullptr);
+
+  BitstreamCursor &Cursor = F.SLocEntryCursor;
+  SavedStreamPosition SavedPosition(Cursor);
+  for (unsigned I = 0; I != N; ++I) {
+    if (llvm::Error Err = Cursor.JumpToBit(F.SLocEntryOffsetsBase +
+                                           F.SLocEntryOffsets[I])) {
+      consumeError(std::move(Err));
+      return false;
+    }
+    Expected<llvm::BitstreamEntry> Entry = Cursor.advance();
+    if (!Entry) {
+      consumeError(Entry.takeError());
+      return false;
+    }
+    if (Entry->Kind != llvm::BitstreamEntry::Record)
+      return false;
+
+    RecordData Record;
+    StringRef Blob;
+    Expected<unsigned> Code = Cursor.readRecord(Entry->ID, Record, &Blob);
+    if (!Code) {
+      consumeError(Code.takeError());
+      return false;
+    }
+    Offsets[I] = (uint32_t)Record[0];
+    if (Code.get() == SM_SLOC_FILE_ENTRY)
+      if (OptionalFileEntryRef File = getInputFile(F, Record[4]).getFile())
+        Files[I] = &File->getFileEntry();
+  }
+  return true;
+}
+
 llvm::Expected<SourceLocation::UIntTy>
 ASTReader::readSLocOffset(ModuleFile *F, unsigned Index) {
   BitstreamCursor &Cursor = F->SLocEntryCursor;
@@ -4259,6 +4297,26 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
                            - SLocSpaceSize,&F));
 
       TotalNumSLocEntries += F.LocalNumSLocEntries;
+
+      // De-duplication detection (prototype, Stage 1). Walk this module's SLoc
+      // file entries and recognize when the same file was already loaded by an
+      // earlier module, measuring the address space a future reuse (Stage 2)
+      // could reclaim. Detection only: no allocation/translation change here.
+      {
+        SmallVector<uint32_t, 64> Offsets;
+        SmallVector<const FileEntry *, 64> Files;
+        if (scanLoadedSLocEntries(F, Offsets, Files)) {
+          unsigned N = Offsets.size();
+          for (unsigned I = 0; I != N; ++I) {
+            if (!Files[I])
+              continue;
+            uint64_t Size =
+                (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) - Offsets[I];
+            SourceMgr.noteLoadedFileSLocEntry(
+                Files[I], F.SLocEntryBaseOffset + Offsets[I], Size);
+          }
+        }
+      }
       break;
     }
 
diff --git a/dedup-test/a.h b/dedup-test/a.h
new file mode 100644
index 0000000000000..1fe7fc27db549
--- /dev/null
+++ b/dedup-test/a.h
@@ -0,0 +1,5 @@
+#ifndef A_H
+#define A_H
+#include "shared.h"
+inline int a_entry(int x) { return shared_fn_0(x); }
+#endif
diff --git a/dedup-test/b.h b/dedup-test/b.h
new file mode 100644
index 0000000000000..85839261fc602
--- /dev/null
+++ b/dedup-test/b.h
@@ -0,0 +1,5 @@
+#ifndef B_H
+#define B_H
+#include "shared.h"
+inline int b_entry(int x) { return shared_fn_1(x); }
+#endif
diff --git a/dedup-test/module.modulemap b/dedup-test/module.modulemap
new file mode 100644
index 0000000000000..2da1c03a22211
--- /dev/null
+++ b/dedup-test/module.modulemap
@@ -0,0 +1,2 @@
+module A { header "a.h" export * }
+module B { header "b.h" export * }
diff --git a/dedup-test/one_module_stats.txt b/dedup-test/one_module_stats.txt
new file mode 100644
index 0000000000000..373fe97b5e1d8
--- /dev/null
+++ b/dedup-test/one_module_stats.txt
@@ -0,0 +1,352 @@
+
+STATISTICS:
+
+*** Semantic Analysis Stats:
+
+Number of memory regions: 0
+Bytes used: 0
+Bytes allocated: 0
+Bytes wasted: 0 (includes alignment, etc)
+
+*** Analysis Based Warnings Stats:
+401 functions analyzed (0 w/o CFGs).
+  1203 CFG blocks built.
+  3 average CFG blocks per function.
+  3 max CFG blocks per function.
+0 functions analyzed for uninitialiazed variables
+  0 variables analyzed.
+  0 average variables per function.
+  0 max variables per function.
+  0 block visits.
+  0 average block visits per function.
+  0 max block visits per function.
+
+*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
+
+
+
+*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
+
+
+Total missing origins: 0
+
+****************************************
+
+*** AST Context Stats:
+  156 types total.
+    120 Builtin types, 32 each (3840 bytes)
+    1 FunctionProto types, 48 each (48 bytes)
+    5 Pointer types, 48 each (240 bytes)
+    1 Record types, 32 each (32 bytes)
+    1 Typedef types, 32 each (32 bytes)
+    28 Vector types, 48 each (1344 bytes)
+Total bytes = 5536
+0/0 implicit default constructors created
+0/0 implicit copy constructors created
+0/0 implicit move constructors created
+0/0 implicit copy assignment operators created
+0/0 implicit move assignment operators created
+0/0 implicit destructors created
+
+*** AST File Statistics:
+
+*** PCH/ModuleFile Remappings:
+
+*** PCH/Modules Loaded:
+
+Number of memory regions: 62
+Bytes used: 248400
+Bytes allocated: 253952
+Bytes wasted: 5552 (includes alignment, etc)
+
+*** Decl Stats:
+  995 decls total.
+    1 TranslationUnit decls, 104 each (104 bytes)
+    1 ExternCContext decls, 72 each (72 bytes)
+    401 Function decls, 168 each (67368 bytes)
+    401 ParmVar decls, 104 each (41704 bytes)
+    8 Field decls, 80 each (640 bytes)
+    2 CXXRecord decls, 144 each (288 bytes)
+    180 Typedef decls, 88 each (15840 bytes)
+    1 Import decls, 56 each (56 bytes)
+Total bytes = 126072
+
+*** Stmt/Expr Stats:
+  2408 stmts/exprs total.
+    1 UnresolvedLookupExpr, 64 each (64 bytes)
+    400 IntegerLiteral, 32 each (12800 bytes)
+    402 DeclRefExpr, 32 each (12864 bytes)
+    402 ImplicitCastExpr, 24 each (9648 bytes)
+    1 CallExpr, 24 each (24 bytes)
+    400 BinaryOperator, 32 each (12800 bytes)
+    401 ReturnStmt, 16 each (6416 bytes)
+    401 CompoundStmt, 16 each (6416 bytes)
+Total bytes = 61032
+
+STATISTICS FOR './module.modulemap':
+
+*** Preprocessor Stats:
+564 directives found:
+  553 #define.
+  0 #undef.
+  #include/#include_next/#import:
+    4 source files entered.
+    1 max include stack depth
+  2 #if/#ifndef/#ifdef.
+  0 #else/#elif/#elifdef/#elifndef.
+  2 #endif.
+  2 #pragma.
+0 #if/#ifndef#ifdef regions skipped
+0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
+0 token paste (##) operations performed, 0 on the fast path.
+
+Preprocessor Memory: 110103B total
+  BumpPtr: 57344
+  Macro Expanded Tokens: 384
+  Predefines Buffer: 32767
+  Macros: 16512
+  #pragma push_macro Info: 2056
+  Poison Reasons: 1032
+  Comment Handlers: 8
+
+*** Identifier Table Stats:
+# Identifiers:   13432
+# Empty Buckets: 19336
+Hash density (#identifiers per bucket): 0.409912
+Ave identifier length: 26.735631
+Max identifier length: 49
+
+Number of memory regions: 182
+Bytes used: 909825
+Bytes allocated: 966656
+Bytes wasted: 56831 (includes alignment, etc)
+
+*** HeaderSearch Stats:
+3 files tracked.
+  0 #import/#pragma once files.
+  2 #include/#include_next/#import.
+    0 #includes skipped due to the multi-include optimization.
+0 framework lookups.
+0 subframework lookups.
+
+*** Source Manager Stats:
+3 files mapped, 3 mem buffers mapped.
+7 local SLocEntries allocated (168 bytes of capacity), 43765B of SLoc address space used.
+0 loaded SLocEntries allocated (0 bytes of capacity), 0B of SLoc address space used.
+20795 bytes of files mapped, 2 files with line #'s computed, 0 files with macro args computed.
+FileID scans: 32 linear, 0 binary.
+
+
+*** File Manager Stats:
+5 real files found, 7 real dirs found.
+0 virtual files found, 0 virtual dirs found.
+28 dir lookups, 9 dir cache misses.
+19 file lookups, 8 file cache misses.
+
+*** Virtual File System Stats:
+14 status() calls
+5 openFileForRead() calls
+0 dir_begin() calls
+1 getRealPath() calls
+0 exists() calls
+0 isLocal() calls
+
+===-------------------------------------------------------------------------===
+                          ... Statistics Collected ...
+===-------------------------------------------------------------------------===
+
+    2 file-search    - Number of attempted #includes.
+43765 source-manager - Maximum number of bytes used by source locations (both loaded and local).
+
+
+STATISTICS:
+
+*** Semantic Analysis Stats:
+
+Number of memory regions: 0
+Bytes used: 0
+Bytes allocated: 0
+Bytes wasted: 0 (includes alignment, etc)
+
+*** Analysis Based Warnings Stats:
+0 functions analyzed (0 w/o CFGs).
+  0 CFG blocks built.
+  0 average CFG blocks per function.
+  0 max CFG blocks per function.
+0 functions analyzed for uninitialiazed variables
+  0 variables analyzed.
+  0 average variables per function.
+  0 max variables per function.
+  0 block visits.
+  0 average block visits per function.
+  0 max block visits per function.
+
+*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
+
+
+
+*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
+
+
+Total missing origins: 0
+
+****************************************
+
+*** AST Context Stats:
+  157 types total.
+    120 Builtin types, 32 each (3840 bytes)
+    2 FunctionProto types, 48 each (96 bytes)
+    6 Pointer types, 48 each (288 bytes)
+    1 Record types, 32 each (32 bytes)
+    28 Vector types, 48 each (1344 bytes)
+Total bytes = 5600
+0/0 implicit default constructors created
+0/0 implicit copy constructors created
+0/0 implicit move constructors created
+0/0 implicit copy assignment operators created
+0/0 implicit move assignment operators created
+0/0 implicit destructors created
+
+*** AST File Statistics:
+  0/6 source location entries read (0.000000%)
+  2/32 types read (6.250000%)
+  516/888 declarations read (58.108109%)
+  7/496 identifiers read (1.411290%)
+  0/2 macros read (0.000000%)
+  7/2407 statements read (0.290818%)
+  0/2 macros read (0.000000%)
+  0/401 lexical declcontexts read (0.000000%)
+
+*** PCH/ModuleFile Remappings:
+Global bit offset map:
+  0 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache1/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
+Global source location entry map:
+  2 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache1/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
+Global submodule map:
+  1 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache1/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
+
+*** PCH/Modules Loaded:
+Module: /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache1/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
+  Base source location offset: 2147439884
+  Base identifier ID: 0
+  Number of identifiers: 496
+  Base macro ID: 0
+  Number of macros: 2
+  Base submodule ID: 0
+  Number of submodules: 1
+  Submodule ID local -> global map:
+    0 -> 0
+  Base selector ID: 0
+  Number of selectors: 0
+  Base preprocessed entity ID: 0
+  Number of preprocessed entities: 0
+  Base type index: 0
+  Number of types: 32
+  Base decl index: 0
+  Number of decls: 888
+
+
+Number of memory regions: 5
+Bytes used: 18452
+Bytes allocated: 20480
+Bytes wasted: 2028 (includes alignment, etc)
+
+*** Decl Stats:
+  1002 decls total.
+    1 TranslationUnit decls, 104 each (104 bytes)
+    2 ExternCContext decls, 72 each (144 bytes)
+    404 Function decls, 168 each (67872 bytes)
+    403 ParmVar decls, 104 each (41912 bytes)
+    8 Field decls, 80 each (640 bytes)
+    2 CXXRecord decls, 144 each (288 bytes)
+    180 Typedef decls, 88 each (15840 bytes)
+    2 Import decls, 56 each (112 bytes)
+Total bytes = 126912
+
+*** Stmt/Expr Stats:
+  2422 stmts/exprs total.
+    2 UnresolvedLookupExpr, 64 each (128 bytes)
+    401 IntegerLiteral, 32 each (12832 bytes)
+    405 DeclRefExpr, 32 each (12960 bytes)
+    405 ImplicitCastExpr, 24 each (9720 bytes)
+    3 CallExpr, 24 each (72 bytes)
+    400 BinaryOperator, 32 each (12800 bytes)
+    403 ReturnStmt, 16 each (6448 bytes)
+    403 CompoundStmt, 16 each (6448 bytes)
+Total bytes = 61408
+
+STATISTICS FOR 'use1.cpp':
+
+*** Preprocessor Stats:
+558 directives found:
+  552 #define.
+  0 #undef.
+  #include/#include_next/#import:
+    2 source files entered.
+    0 max include stack depth
+  0 #if/#ifndef/#ifdef.
+  0 #else/#elif/#elifdef/#elifndef.
+  0 #endif.
+  2 #pragma.
+0 #if/#ifndef#ifdef regions skipped
+0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
+0 token paste (##) operations performed, 0 on the fast path.
+
+Preprocessor Memory: 106007B total
+  BumpPtr: 53248
+  Macro Expanded Tokens: 384
+  Predefines Buffer: 32767
+  Macros: 16512
+  #pragma push_macro Info: 2056
+  Poison Reasons: 1032
+  Comment Handlers: 8
+
+*** Identifier Table Stats:
+# Identifiers:   13033
+# Empty Buckets: 19735
+Hash density (#identifiers per bucket): 0.397736
+Ave identifier length: 27.165119
+Max identifier length: 49
+
+Number of memory regions: 180
+Bytes used: 888396
+Bytes allocated: 950272
+Bytes wasted: 61876 (includes alignment, etc)
+
+*** HeaderSearch Stats:
+3 files tracked.
+  0 #import/#pragma once files.
+  2 #include/#include_next/#import.
+    0 #includes skipped due to the multi-include optimization.
+0 framework lookups.
+0 subframework lookups.
+
+*** Source Manager Stats:
+2 files mapped, 2 mem buffers mapped.
+5 local SLocEntries allocated (168 bytes of capacity), 23108B of SLoc address space used.
+6 loaded SLocEntries allocated (768 bytes of capacity), 43764B of SLoc address space used.
+119 bytes of files mapped, 0 files with line #'s computed, 0 files with macro args computed.
+FileID scans: 11 linear, 0 binary.
+
+
+*** File Manager Stats:
+6 real files found, 8 real dirs found.
+0 virtual files found, 0 virtual dirs found.
+31 dir lookups, 11 dir cache misses.
+27 file lookups, 9 file cache misses.
+
+*** Virtual File System Stats:
+17 status() calls
+6 openFileForRead() calls
+0 dir_begin() calls
+1 getRealPath() calls
+0 exists() calls
+0 isLocal() calls
+
+===-------------------------------------------------------------------------===
+                          ... Statistics Collected ...
+===-------------------------------------------------------------------------===
+
+    2 file-search    - Number of attempted #includes.
+66872 source-manager - Maximum number of bytes used by source locations (both loaded and local).
+
diff --git a/dedup-test/shared.h b/dedup-test/shared.h
new file mode 100644
index 0000000000000..3ac57a45355f9
--- /dev/null
+++ b/dedup-test/shared.h
@@ -0,0 +1,403 @@
+#ifndef SHARED_H
+#define SHARED_H
+inline int shared_fn_0(int x) { return x + 0; }
+inline int shared_fn_1(int x) { return x + 1; }
+inline int shared_fn_2(int x) { return x + 2; }
+inline int shared_fn_3(int x) { return x + 3; }
+inline int shared_fn_4(int x) { return x + 4; }
+inline int shared_fn_5(int x) { return x + 5; }
+inline int shared_fn_6(int x) { return x + 6; }
+inline int shared_fn_7(int x) { return x + 7; }
+inline int shared_fn_8(int x) { return x + 8; }
+inline int shared_fn_9(int x) { return x + 9; }
+inline int shared_fn_10(int x) { return x + 10; }
+inline int shared_fn_11(int x) { return x + 11; }
+inline int shared_fn_12(int x) { return x + 12; }
+inline int shared_fn_13(int x) { return x + 13; }
+inline int shared_fn_14(int x) { return x + 14; }
+inline int shared_fn_15(int x) { return x + 15; }
+inline int shared_fn_16(int x) { return x + 16; }
+inline int shared_fn_17(int x) { return x + 17; }
+inline int shared_fn_18(int x) { return x + 18; }
+inline int shared_fn_19(int x) { return x + 19; }
+inline int shared_fn_20(int x) { return x + 20; }
+inline int shared_fn_21(int x) { return x + 21; }
+inline int shared_fn_22(int x) { return x + 22; }
+inline int shared_fn_23(int x) { return x + 23; }
+inline int shared_fn_24(int x) { return x + 24; }
+inline int shared_fn_25(int x) { return x + 25; }
+inline int shared_fn_26(int x) { return x + 26; }
+inline int shared_fn_27(int x) { return x + 27; }
+inline int shared_fn_28(int x) { return x + 28; }
+inline int shared_fn_29(int x) { return x + 29; }
+inline int shared_fn_30(int x) { return x + 30; }
+inline int shared_fn_31(int x) { return x + 31; }
+inline int shared_fn_32(int x) { return x + 32; }
+inline int shared_fn_33(int x) { return x + 33; }
+inline int shared_fn_34(int x) { return x + 34; }
+inline int shared_fn_35(int x) { return x + 35; }
+inline int shared_fn_36(int x) { return x + 36; }
+inline int shared_fn_37(int x) { return x + 37; }
+inline int shared_fn_38(int x) { return x + 38; }
+inline int shared_fn_39(int x) { return x + 39; }
+inline int shared_fn_40(int x) { return x + 40; }
+inline int shared_fn_41(int x) { return x + 41; }
+inline int shared_fn_42(int x) { return x + 42; }
+inline int shared_fn_43(int x) { return x + 43; }
+inline int shared_fn_44(int x) { return x + 44; }
+inline int shared_fn_45(int x) { return x + 45; }
+inline int shared_fn_46(int x) { return x + 46; }
+inline int shared_fn_47(int x) { return x + 47; }
+inline int shared_fn_48(int x) { return x + 48; }
+inline int shared_fn_49(int x) { return x + 49; }
+inline int shared_fn_50(int x) { return x + 50; }
+inline int shared_fn_51(int x) { return x + 51; }
+inline int shared_fn_52(int x) { return x + 52; }
+inline int shared_fn_53(int x) { return x + 53; }
+inline int shared_fn_54(int x) { return x + 54; }
+inline int shared_fn_55(int x) { return x + 55; }
+inline int shared_fn_56(int x) { return x + 56; }
+inline int shared_fn_57(int x) { return x + 57; }
+inline int shared_fn_58(int x) { return x + 58; }
+inline int shared_fn_59(int x) { return x + 59; }
+inline int shared_fn_60(int x) { return x + 60; }
+inline int shared_fn_61(int x) { return x + 61; }
+inline int shared_fn_62(int x) { return x + 62; }
+inline int shared_fn_63(int x) { return x + 63; }
+inline int shared_fn_64(int x) { return x + 64; }
+inline int shared_fn_65(int x) { return x + 65; }
+inline int shared_fn_66(int x) { return x + 66; }
+inline int shared_fn_67(int x) { return x + 67; }
+inline int shared_fn_68(int x) { return x + 68; }
+inline int shared_fn_69(int x) { return x + 69; }
+inline int shared_fn_70(int x) { return x + 70; }
+inline int shared_fn_71(int x) { return x + 71; }
+inline int shared_fn_72(int x) { return x + 72; }
+inline int shared_fn_73(int x) { return x + 73; }
+inline int shared_fn_74(int x) { return x + 74; }
+inline int shared_fn_75(int x) { return x + 75; }
+inline int shared_fn_76(int x) { return x + 76; }
+inline int shared_fn_77(int x) { return x + 77; }
+inline int shared_fn_78(int x) { return x + 78; }
+inline int shared_fn_79(int x) { return x + 79; }
+inline int shared_fn_80(int x) { return x + 80; }
+inline int shared_fn_81(int x) { return x + 81; }
+inline int shared_fn_82(int x) { return x + 82; }
+inline int shared_fn_83(int x) { return x + 83; }
+inline int shared_fn_84(int x) { return x + 84; }
+inline int shared_fn_85(int x) { return x + 85; }
+inline int shared_fn_86(int x) { return x + 86; }
+inline int shared_fn_87(int x) { return x + 87; }
+inline int shared_fn_88(int x) { return x + 88; }
+inline int shared_fn_89(int x) { return x + 89; }
+inline int shared_fn_90(int x) { return x + 90; }
+inline int shared_fn_91(int x) { return x + 91; }
+inline int shared_fn_92(int x) { return x + 92; }
+inline int shared_fn_93(int x) { return x + 93; }
+inline int shared_fn_94(int x) { return x + 94; }
+inline int shared_fn_95(int x) { return x + 95; }
+inline int shared_fn_96(int x) { return x + 96; }
+inline int shared_fn_97(int x) { return x + 97; }
+inline int shared_fn_98(int x) { return x + 98; }
+inline int shared_fn_99(int x) { return x + 99; }
+inline int shared_fn_100(int x) { return x + 100; }
+inline int shared_fn_101(int x) { return x + 101; }
+inline int shared_fn_102(int x) { return x + 102; }
+inline int shared_fn_103(int x) { return x + 103; }
+inline int shared_fn_104(int x) { return x + 104; }
+inline int shared_fn_105(int x) { return x + 105; }
+inline int shared_fn_106(int x) { return x + 106; }
+inline int shared_fn_107(int x) { return x + 107; }
+inline int shared_fn_108(int x) { return x + 108; }
+inline int shared_fn_109(int x) { return x + 109; }
+inline int shared_fn_110(int x) { return x + 110; }
+inline int shared_fn_111(int x) { return x + 111; }
+inline int shared_fn_112(int x) { return x + 112; }
+inline int shared_fn_113(int x) { return x + 113; }
+inline int shared_fn_114(int x) { return x + 114; }
+inline int shared_fn_115(int x) { return x + 115; }
+inline int shared_fn_116(int x) { return x + 116; }
+inline int shared_fn_117(int x) { return x + 117; }
+inline int shared_fn_118(int x) { return x + 118; }
+inline int shared_fn_119(int x) { return x + 119; }
+inline int shared_fn_120(int x) { return x + 120; }
+inline int shared_fn_121(int x) { return x + 121; }
+inline int shared_fn_122(int x) { return x + 122; }
+inline int shared_fn_123(int x) { return x + 123; }
+inline int shared_fn_124(int x) { return x + 124; }
+inline int shared_fn_125(int x) { return x + 125; }
+inline int shared_fn_126(int x) { return x + 126; }
+inline int shared_fn_127(int x) { return x + 127; }
+inline int shared_fn_128(int x) { return x + 128; }
+inline int shared_fn_129(int x) { return x + 129; }
+inline int shared_fn_130(int x) { return x + 130; }
+inline int shared_fn_131(int x) { return x + 131; }
+inline int shared_fn_132(int x) { return x + 132; }
+inline int shared_fn_133(int x) { return x + 133; }
+inline int shared_fn_134(int x) { return x + 134; }
+inline int shared_fn_135(int x) { return x + 135; }
+inline int shared_fn_136(int x) { return x + 136; }
+inline int shared_fn_137(int x) { return x + 137; }
+inline int shared_fn_138(int x) { return x + 138; }
+inline int shared_fn_139(int x) { return x + 139; }
+inline int shared_fn_140(int x) { return x + 140; }
+inline int shared_fn_141(int x) { return x + 141; }
+inline int shared_fn_142(int x) { return x + 142; }
+inline int shared_fn_143(int x) { return x + 143; }
+inline int shared_fn_144(int x) { return x + 144; }
+inline int shared_fn_145(int x) { return x + 145; }
+inline int shared_fn_146(int x) { return x + 146; }
+inline int shared_fn_147(int x) { return x + 147; }
+inline int shared_fn_148(int x) { return x + 148; }
+inline int shared_fn_149(int x) { return x + 149; }
+inline int shared_fn_150(int x) { return x + 150; }
+inline int shared_fn_151(int x) { return x + 151; }
+inline int shared_fn_152(int x) { return x + 152; }
+inline int shared_fn_153(int x) { return x + 153; }
+inline int shared_fn_154(int x) { return x + 154; }
+inline int shared_fn_155(int x) { return x + 155; }
+inline int shared_fn_156(int x) { return x + 156; }
+inline int shared_fn_157(int x) { return x + 157; }
+inline int shared_fn_158(int x) { return x + 158; }
+inline int shared_fn_159(int x) { return x + 159; }
+inline int shared_fn_160(int x) { return x + 160; }
+inline int shared_fn_161(int x) { return x + 161; }
+inline int shared_fn_162(int x) { return x + 162; }
+inline int shared_fn_163(int x) { return x + 163; }
+inline int shared_fn_164(int x) { return x + 164; }
+inline int shared_fn_165(int x) { return x + 165; }
+inline int shared_fn_166(int x) { return x + 166; }
+inline int shared_fn_167(int x) { return x + 167; }
+inline int shared_fn_168(int x) { return x + 168; }
+inline int shared_fn_169(int x) { return x + 169; }
+inline int shared_fn_170(int x) { return x + 170; }
+inline int shared_fn_171(int x) { return x + 171; }
+inline int shared_fn_172(int x) { return x + 172; }
+inline int shared_fn_173(int x) { return x + 173; }
+inline int shared_fn_174(int x) { return x + 174; }
+inline int shared_fn_175(int x) { return x + 175; }
+inline int shared_fn_176(int x) { return x + 176; }
+inline int shared_fn_177(int x) { return x + 177; }
+inline int shared_fn_178(int x) { return x + 178; }
+inline int shared_fn_179(int x) { return x + 179; }
+inline int shared_fn_180(int x) { return x + 180; }
+inline int shared_fn_181(int x) { return x + 181; }
+inline int shared_fn_182(int x) { return x + 182; }
+inline int shared_fn_183(int x) { return x + 183; }
+inline int shared_fn_184(int x) { return x + 184; }
+inline int shared_fn_185(int x) { return x + 185; }
+inline int shared_fn_186(int x) { return x + 186; }
+inline int shared_fn_187(int x) { return x + 187; }
+inline int shared_fn_188(int x) { return x + 188; }
+inline int shared_fn_189(int x) { return x + 189; }
+inline int shared_fn_190(int x) { return x + 190; }
+inline int shared_fn_191(int x) { return x + 191; }
+inline int shared_fn_192(int x) { return x + 192; }
+inline int shared_fn_193(int x) { return x + 193; }
+inline int shared_fn_194(int x) { return x + 194; }
+inline int shared_fn_195(int x) { return x + 195; }
+inline int shared_fn_196(int x) { return x + 196; }
+inline int shared_fn_197(int x) { return x + 197; }
+inline int shared_fn_198(int x) { return x + 198; }
+inline int shared_fn_199(int x) { return x + 199; }
+inline int shared_fn_200(int x) { return x + 200; }
+inline int shared_fn_201(int x) { return x + 201; }
+inline int shared_fn_202(int x) { return x + 202; }
+inline int shared_fn_203(int x) { return x + 203; }
+inline int shared_fn_204(int x) { return x + 204; }
+inline int shared_fn_205(int x) { return x + 205; }
+inline int shared_fn_206(int x) { return x + 206; }
+inline int shared_fn_207(int x) { return x + 207; }
+inline int shared_fn_208(int x) { return x + 208; }
+inline int shared_fn_209(int x) { return x + 209; }
+inline int shared_fn_210(int x) { return x + 210; }
+inline int shared_fn_211(int x) { return x + 211; }
+inline int shared_fn_212(int x) { return x + 212; }
+inline int shared_fn_213(int x) { return x + 213; }
+inline int shared_fn_214(int x) { return x + 214; }
+inline int shared_fn_215(int x) { return x + 215; }
+inline int shared_fn_216(int x) { return x + 216; }
+inline int shared_fn_217(int x) { return x + 217; }
+inline int shared_fn_218(int x) { return x + 218; }
+inline int shared_fn_219(int x) { return x + 219; }
+inline int shared_fn_220(int x) { return x + 220; }
+inline int shared_fn_221(int x) { return x + 221; }
+inline int shared_fn_222(int x) { return x + 222; }
+inline int shared_fn_223(int x) { return x + 223; }
+inline int shared_fn_224(int x) { return x + 224; }
+inline int shared_fn_225(int x) { return x + 225; }
+inline int shared_fn_226(int x) { return x + 226; }
+inline int shared_fn_227(int x) { return x + 227; }
+inline int shared_fn_228(int x) { return x + 228; }
+inline int shared_fn_229(int x) { return x + 229; }
+inline int shared_fn_230(int x) { return x + 230; }
+inline int shared_fn_231(int x) { return x + 231; }
+inline int shared_fn_232(int x) { return x + 232; }
+inline int shared_fn_233(int x) { return x + 233; }
+inline int shared_fn_234(int x) { return x + 234; }
+inline int shared_fn_235(int x) { return x + 235; }
+inline int shared_fn_236(int x) { return x + 236; }
+inline int shared_fn_237(int x) { return x + 237; }
+inline int shared_fn_238(int x) { return x + 238; }
+inline int shared_fn_239(int x) { return x + 239; }
+inline int shared_fn_240(int x) { return x + 240; }
+inline int shared_fn_241(int x) { return x + 241; }
+inline int shared_fn_242(int x) { return x + 242; }
+inline int shared_fn_243(int x) { return x + 243; }
+inline int shared_fn_244(int x) { return x + 244; }
+inline int shared_fn_245(int x) { return x + 245; }
+inline int shared_fn_246(int x) { return x + 246; }
+inline int shared_fn_247(int x) { return x + 247; }
+inline int shared_fn_248(int x) { return x + 248; }
+inline int shared_fn_249(int x) { return x + 249; }
+inline int shared_fn_250(int x) { return x + 250; }
+inline int shared_fn_251(int x) { return x + 251; }
+inline int shared_fn_252(int x) { return x + 252; }
+inline int shared_fn_253(int x) { return x + 253; }
+inline int shared_fn_254(int x) { return x + 254; }
+inline int shared_fn_255(int x) { return x + 255; }
+inline int shared_fn_256(int x) { return x + 256; }
+inline int shared_fn_257(int x) { return x + 257; }
+inline int shared_fn_258(int x) { return x + 258; }
+inline int shared_fn_259(int x) { return x + 259; }
+inline int shared_fn_260(int x) { return x + 260; }
+inline int shared_fn_261(int x) { return x + 261; }
+inline int shared_fn_262(int x) { return x + 262; }
+inline int shared_fn_263(int x) { return x + 263; }
+inline int shared_fn_264(int x) { return x + 264; }
+inline int shared_fn_265(int x) { return x + 265; }
+inline int shared_fn_266(int x) { return x + 266; }
+inline int shared_fn_267(int x) { return x + 267; }
+inline int shared_fn_268(int x) { return x + 268; }
+inline int shared_fn_269(int x) { return x + 269; }
+inline int shared_fn_270(int x) { return x + 270; }
+inline int shared_fn_271(int x) { return x + 271; }
+inline int shared_fn_272(int x) { return x + 272; }
+inline int shared_fn_273(int x) { return x + 273; }
+inline int shared_fn_274(int x) { return x + 274; }
+inline int shared_fn_275(int x) { return x + 275; }
+inline int shared_fn_276(int x) { return x + 276; }
+inline int shared_fn_277(int x) { return x + 277; }
+inline int shared_fn_278(int x) { return x + 278; }
+inline int shared_fn_279(int x) { return x + 279; }
+inline int shared_fn_280(int x) { return x + 280; }
+inline int shared_fn_281(int x) { return x + 281; }
+inline int shared_fn_282(int x) { return x + 282; }
+inline int shared_fn_283(int x) { return x + 283; }
+inline int shared_fn_284(int x) { return x + 284; }
+inline int shared_fn_285(int x) { return x + 285; }
+inline int shared_fn_286(int x) { return x + 286; }
+inline int shared_fn_287(int x) { return x + 287; }
+inline int shared_fn_288(int x) { return x + 288; }
+inline int shared_fn_289(int x) { return x + 289; }
+inline int shared_fn_290(int x) { return x + 290; }
+inline int shared_fn_291(int x) { return x + 291; }
+inline int shared_fn_292(int x) { return x + 292; }
+inline int shared_fn_293(int x) { return x + 293; }
+inline int shared_fn_294(int x) { return x + 294; }
+inline int shared_fn_295(int x) { return x + 295; }
+inline int shared_fn_296(int x) { return x + 296; }
+inline int shared_fn_297(int x) { return x + 297; }
+inline int shared_fn_298(int x) { return x + 298; }
+inline int shared_fn_299(int x) { return x + 299; }
+inline int shared_fn_300(int x) { return x + 300; }
+inline int shared_fn_301(int x) { return x + 301; }
+inline int shared_fn_302(int x) { return x + 302; }
+inline int shared_fn_303(int x) { return x + 303; }
+inline int shared_fn_304(int x) { return x + 304; }
+inline int shared_fn_305(int x) { return x + 305; }
+inline int shared_fn_306(int x) { return x + 306; }
+inline int shared_fn_307(int x) { return x + 307; }
+inline int shared_fn_308(int x) { return x + 308; }
+inline int shared_fn_309(int x) { return x + 309; }
+inline int shared_fn_310(int x) { return x + 310; }
+inline int shared_fn_311(int x) { return x + 311; }
+inline int shared_fn_312(int x) { return x + 312; }
+inline int shared_fn_313(int x) { return x + 313; }
+inline int shared_fn_314(int x) { return x + 314; }
+inline int shared_fn_315(int x) { return x + 315; }
+inline int shared_fn_316(int x) { return x + 316; }
+inline int shared_fn_317(int x) { return x + 317; }
+inline int shared_fn_318(int x) { return x + 318; }
+inline int shared_fn_319(int x) { return x + 319; }
+inline int shared_fn_320(int x) { return x + 320; }
+inline int shared_fn_321(int x) { return x + 321; }
+inline int shared_fn_322(int x) { return x + 322; }
+inline int shared_fn_323(int x) { return x + 323; }
+inline int shared_fn_324(int x) { return x + 324; }
+inline int shared_fn_325(int x) { return x + 325; }
+inline int shared_fn_326(int x) { return x + 326; }
+inline int shared_fn_327(int x) { return x + 327; }
+inline int shared_fn_328(int x) { return x + 328; }
+inline int shared_fn_329(int x) { return x + 329; }
+inline int shared_fn_330(int x) { return x + 330; }
+inline int shared_fn_331(int x) { return x + 331; }
+inline int shared_fn_332(int x) { return x + 332; }
+inline int shared_fn_333(int x) { return x + 333; }
+inline int shared_fn_334(int x) { return x + 334; }
+inline int shared_fn_335(int x) { return x + 335; }
+inline int shared_fn_336(int x) { return x + 336; }
+inline int shared_fn_337(int x) { return x + 337; }
+inline int shared_fn_338(int x) { return x + 338; }
+inline int shared_fn_339(int x) { return x + 339; }
+inline int shared_fn_340(int x) { return x + 340; }
+inline int shared_fn_341(int x) { return x + 341; }
+inline int shared_fn_342(int x) { return x + 342; }
+inline int shared_fn_343(int x) { return x + 343; }
+inline int shared_fn_344(int x) { return x + 344; }
+inline int shared_fn_345(int x) { return x + 345; }
+inline int shared_fn_346(int x) { return x + 346; }
+inline int shared_fn_347(int x) { return x + 347; }
+inline int shared_fn_348(int x) { return x + 348; }
+inline int shared_fn_349(int x) { return x + 349; }
+inline int shared_fn_350(int x) { return x + 350; }
+inline int shared_fn_351(int x) { return x + 351; }
+inline int shared_fn_352(int x) { return x + 352; }
+inline int shared_fn_353(int x) { return x + 353; }
+inline int shared_fn_354(int x) { return x + 354; }
+inline int shared_fn_355(int x) { return x + 355; }
+inline int shared_fn_356(int x) { return x + 356; }
+inline int shared_fn_357(int x) { return x + 357; }
+inline int shared_fn_358(int x) { return x + 358; }
+inline int shared_fn_359(int x) { return x + 359; }
+inline int shared_fn_360(int x) { return x + 360; }
+inline int shared_fn_361(int x) { return x + 361; }
+inline int shared_fn_362(int x) { return x + 362; }
+inline int shared_fn_363(int x) { return x + 363; }
+inline int shared_fn_364(int x) { return x + 364; }
+inline int shared_fn_365(int x) { return x + 365; }
+inline int shared_fn_366(int x) { return x + 366; }
+inline int shared_fn_367(int x) { return x + 367; }
+inline int shared_fn_368(int x) { return x + 368; }
+inline int shared_fn_369(int x) { return x + 369; }
+inline int shared_fn_370(int x) { return x + 370; }
+inline int shared_fn_371(int x) { return x + 371; }
+inline int shared_fn_372(int x) { return x + 372; }
+inline int shared_fn_373(int x) { return x + 373; }
+inline int shared_fn_374(int x) { return x + 374; }
+inline int shared_fn_375(int x) { return x + 375; }
+inline int shared_fn_376(int x) { return x + 376; }
+inline int shared_fn_377(int x) { return x + 377; }
+inline int shared_fn_378(int x) { return x + 378; }
+inline int shared_fn_379(int x) { return x + 379; }
+inline int shared_fn_380(int x) { return x + 380; }
+inline int shared_fn_381(int x) { return x + 381; }
+inline int shared_fn_382(int x) { return x + 382; }
+inline int shared_fn_383(int x) { return x + 383; }
+inline int shared_fn_384(int x) { return x + 384; }
+inline int shared_fn_385(int x) { return x + 385; }
+inline int shared_fn_386(int x) { return x + 386; }
+inline int shared_fn_387(int x) { return x + 387; }
+inline int shared_fn_388(int x) { return x + 388; }
+inline int shared_fn_389(int x) { return x + 389; }
+inline int shared_fn_390(int x) { return x + 390; }
+inline int shared_fn_391(int x) { return x + 391; }
+inline int shared_fn_392(int x) { return x + 392; }
+inline int shared_fn_393(int x) { return x + 393; }
+inline int shared_fn_394(int x) { return x + 394; }
+inline int shared_fn_395(int x) { return x + 395; }
+inline int shared_fn_396(int x) { return x + 396; }
+inline int shared_fn_397(int x) { return x + 397; }
+inline int shared_fn_398(int x) { return x + 398; }
+inline int shared_fn_399(int x) { return x + 399; }
+#endif
diff --git a/dedup-test/two_modules_stats.txt b/dedup-test/two_modules_stats.txt
new file mode 100644
index 0000000000000..dd3a1f0c2d3ac
--- /dev/null
+++ b/dedup-test/two_modules_stats.txt
@@ -0,0 +1,535 @@
+
+STATISTICS:
+
+*** Semantic Analysis Stats:
+
+Number of memory regions: 0
+Bytes used: 0
+Bytes allocated: 0
+Bytes wasted: 0 (includes alignment, etc)
+
+*** Analysis Based Warnings Stats:
+401 functions analyzed (0 w/o CFGs).
+  1203 CFG blocks built.
+  3 average CFG blocks per function.
+  3 max CFG blocks per function.
+0 functions analyzed for uninitialiazed variables
+  0 variables analyzed.
+  0 average variables per function.
+  0 max variables per function.
+  0 block visits.
+  0 average block visits per function.
+  0 max block visits per function.
+
+*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
+
+
+
+*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
+
+
+Total missing origins: 0
+
+****************************************
+
+*** AST Context Stats:
+  156 types total.
+    120 Builtin types, 32 each (3840 bytes)
+    1 FunctionProto types, 48 each (48 bytes)
+    5 Pointer types, 48 each (240 bytes)
+    1 Record types, 32 each (32 bytes)
+    1 Typedef types, 32 each (32 bytes)
+    28 Vector types, 48 each (1344 bytes)
+Total bytes = 5536
+0/0 implicit default constructors created
+0/0 implicit copy constructors created
+0/0 implicit move constructors created
+0/0 implicit copy assignment operators created
+0/0 implicit move assignment operators created
+0/0 implicit destructors created
+
+*** AST File Statistics:
+
+*** PCH/ModuleFile Remappings:
+
+*** PCH/Modules Loaded:
+
+Number of memory regions: 62
+Bytes used: 248400
+Bytes allocated: 253952
+Bytes wasted: 5552 (includes alignment, etc)
+
+*** Decl Stats:
+  995 decls total.
+    1 TranslationUnit decls, 104 each (104 bytes)
+    1 ExternCContext decls, 72 each (72 bytes)
+    401 Function decls, 168 each (67368 bytes)
+    401 ParmVar decls, 104 each (41704 bytes)
+    8 Field decls, 80 each (640 bytes)
+    2 CXXRecord decls, 144 each (288 bytes)
+    180 Typedef decls, 88 each (15840 bytes)
+    1 Import decls, 56 each (56 bytes)
+Total bytes = 126072
+
+*** Stmt/Expr Stats:
+  2408 stmts/exprs total.
+    1 UnresolvedLookupExpr, 64 each (64 bytes)
+    400 IntegerLiteral, 32 each (12800 bytes)
+    402 DeclRefExpr, 32 each (12864 bytes)
+    402 ImplicitCastExpr, 24 each (9648 bytes)
+    1 CallExpr, 24 each (24 bytes)
+    400 BinaryOperator, 32 each (12800 bytes)
+    401 ReturnStmt, 16 each (6416 bytes)
+    401 CompoundStmt, 16 each (6416 bytes)
+Total bytes = 61032
+
+STATISTICS FOR './module.modulemap':
+
+*** Preprocessor Stats:
+564 directives found:
+  553 #define.
+  0 #undef.
+  #include/#include_next/#import:
+    4 source files entered.
+    1 max include stack depth
+  2 #if/#ifndef/#ifdef.
+  0 #else/#elif/#elifdef/#elifndef.
+  2 #endif.
+  2 #pragma.
+0 #if/#ifndef#ifdef regions skipped
+0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
+0 token paste (##) operations performed, 0 on the fast path.
+
+Preprocessor Memory: 110103B total
+  BumpPtr: 57344
+  Macro Expanded Tokens: 384
+  Predefines Buffer: 32767
+  Macros: 16512
+  #pragma push_macro Info: 2056
+  Poison Reasons: 1032
+  Comment Handlers: 8
+
+*** Identifier Table Stats:
+# Identifiers:   13432
+# Empty Buckets: 19336
+Hash density (#identifiers per bucket): 0.409912
+Ave identifier length: 26.735631
+Max identifier length: 49
+
+Number of memory regions: 182
+Bytes used: 909825
+Bytes allocated: 966656
+Bytes wasted: 56831 (includes alignment, etc)
+
+*** HeaderSearch Stats:
+3 files tracked.
+  0 #import/#pragma once files.
+  2 #include/#include_next/#import.
+    0 #includes skipped due to the multi-include optimization.
+0 framework lookups.
+0 subframework lookups.
+
+*** Source Manager Stats:
+3 files mapped, 3 mem buffers mapped.
+7 local SLocEntries allocated (168 bytes of capacity), 43765B of SLoc address space used.
+0 loaded SLocEntries allocated (0 bytes of capacity), 0B of SLoc address space used.
+20795 bytes of files mapped, 2 files with line #'s computed, 0 files with macro args computed.
+FileID scans: 32 linear, 0 binary.
+
+
+*** File Manager Stats:
+5 real files found, 7 real dirs found.
+0 virtual files found, 0 virtual dirs found.
+28 dir lookups, 9 dir cache misses.
+19 file lookups, 8 file cache misses.
+
+*** Virtual File System Stats:
+14 status() calls
+5 openFileForRead() calls
+0 dir_begin() calls
+1 getRealPath() calls
+0 exists() calls
+0 isLocal() calls
+
+===-------------------------------------------------------------------------===
+                          ... Statistics Collected ...
+===-------------------------------------------------------------------------===
+
+    2 file-search    - Number of attempted #includes.
+43765 source-manager - Maximum number of bytes used by source locations (both loaded and local).
+
+
+STATISTICS:
+
+*** Semantic Analysis Stats:
+
+Number of memory regions: 0
+Bytes used: 0
+Bytes allocated: 0
+Bytes wasted: 0 (includes alignment, etc)
+
+*** Analysis Based Warnings Stats:
+401 functions analyzed (0 w/o CFGs).
+  1203 CFG blocks built.
+  3 average CFG blocks per function.
+  3 max CFG blocks per function.
+0 functions analyzed for uninitialiazed variables
+  0 variables analyzed.
+  0 average variables per function.
+  0 max variables per function.
+  0 block visits.
+  0 average block visits per function.
+  0 max block visits per function.
+
+*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
+
+
+
+*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
+
+
+Total missing origins: 0
+
+****************************************
+
+*** AST Context Stats:
+  156 types total.
+    120 Builtin types, 32 each (3840 bytes)
+    1 FunctionProto types, 48 each (48 bytes)
+    5 Pointer types, 48 each (240 bytes)
+    1 Record types, 32 each (32 bytes)
+    1 Typedef types, 32 each (32 bytes)
+    28 Vector types, 48 each (1344 bytes)
+Total bytes = 5536
+0/0 implicit default constructors created
+0/0 implicit copy constructors created
+0/0 implicit move constructors created
+0/0 implicit copy assignment operators created
+0/0 implicit move assignment operators created
+0/0 implicit destructors created
+
+*** AST File Statistics:
+
+*** PCH/ModuleFile Remappings:
+
+*** PCH/Modules Loaded:
+
+Number of memory regions: 62
+Bytes used: 248400
+Bytes allocated: 253952
+Bytes wasted: 5552 (includes alignment, etc)
+
+*** Decl Stats:
+  1896 decls total.
+    2 TranslationUnit decls, 104 each (208 bytes)
+    2 ExternCContext decls, 72 each (144 bytes)
+    802 Function decls, 168 each (134736 bytes)
+    802 ParmVar decls, 104 each (83408 bytes)
+    12 Field decls, 80 each (960 bytes)
+    3 CXXRecord decls, 144 each (432 bytes)
+    270 Typedef decls, 88 each (23760 bytes)
+    3 Import decls, 56 each (168 bytes)
+Total bytes = 243816
+
+*** Stmt/Expr Stats:
+  4816 stmts/exprs total.
+    2 UnresolvedLookupExpr, 64 each (128 bytes)
+    800 IntegerLiteral, 32 each (25600 bytes)
+    804 DeclRefExpr, 32 each (25728 bytes)
+    804 ImplicitCastExpr, 24 each (19296 bytes)
+    2 CallExpr, 24 each (48 bytes)
+    800 BinaryOperator, 32 each (25600 bytes)
+    802 ReturnStmt, 16 each (12832 bytes)
+    802 CompoundStmt, 16 each (12832 bytes)
+Total bytes = 122064
+
+STATISTICS FOR './module.modulemap':
+
+*** Preprocessor Stats:
+564 directives found:
+  553 #define.
+  0 #undef.
+  #include/#include_next/#import:
+    4 source files entered.
+    1 max include stack depth
+  2 #if/#ifndef/#ifdef.
+  0 #else/#elif/#elifdef/#elifndef.
+  2 #endif.
+  2 #pragma.
+0 #if/#ifndef#ifdef regions skipped
+0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
+0 token paste (##) operations performed, 0 on the fast path.
+
+Preprocessor Memory: 110103B total
+  BumpPtr: 57344
+  Macro Expanded Tokens: 384
+  Predefines Buffer: 32767
+  Macros: 16512
+  #pragma push_macro Info: 2056
+  Poison Reasons: 1032
+  Comment Handlers: 8
+
+*** Identifier Table Stats:
+# Identifiers:   13432
+# Empty Buckets: 19336
+Hash density (#identifiers per bucket): 0.409912
+Ave identifier length: 26.735631
+Max identifier length: 49
+
+Number of memory regions: 182
+Bytes used: 909825
+Bytes allocated: 966656
+Bytes wasted: 56831 (includes alignment, etc)
+
+*** HeaderSearch Stats:
+3 files tracked.
+  0 #import/#pragma once files.
+  4 #include/#include_next/#import.
+    0 #includes skipped due to the multi-include optimization.
+0 framework lookups.
+0 subframework lookups.
+
+*** Source Manager Stats:
+3 files mapped, 3 mem buffers mapped.
+7 local SLocEntries allocated (168 bytes of capacity), 43765B of SLoc address space used.
+0 loaded SLocEntries allocated (0 bytes of capacity), 0B of SLoc address space used.
+20795 bytes of files mapped, 2 files with line #'s computed, 0 files with macro args computed.
+FileID scans: 32 linear, 0 binary.
+
+
+*** File Manager Stats:
+5 real files found, 7 real dirs found.
+0 virtual files found, 0 virtual dirs found.
+42 dir lookups, 10 dir cache misses.
+38 file lookups, 8 file cache misses.
+
+*** Virtual File System Stats:
+15 status() calls
+8 openFileForRead() calls
+0 dir_begin() calls
+1 getRealPath() calls
+0 exists() calls
+0 isLocal() calls
+
+===-------------------------------------------------------------------------===
+                          ... Statistics Collected ...
+===-------------------------------------------------------------------------===
+
+    4 file-search    - Number of attempted #includes.
+66900 source-manager - Maximum number of bytes used by source locations (both loaded and local).
+
+
+STATISTICS:
+
+*** Semantic Analysis Stats:
+
+Number of memory regions: 0
+Bytes used: 0
+Bytes allocated: 0
+Bytes wasted: 0 (includes alignment, etc)
+
+*** Analysis Based Warnings Stats:
+0 functions analyzed (0 w/o CFGs).
+  0 CFG blocks built.
+  0 average CFG blocks per function.
+  0 max CFG blocks per function.
+0 functions analyzed for uninitialiazed variables
+  0 variables analyzed.
+  0 average variables per function.
+  0 max variables per function.
+  0 block visits.
+  0 average block visits per function.
+  0 max block visits per function.
+
+*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
+
+
+
+*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
+
+
+Total missing origins: 0
+
+****************************************
+
+*** AST Context Stats:
+  157 types total.
+    120 Builtin types, 32 each (3840 bytes)
+    2 FunctionProto types, 48 each (96 bytes)
+    6 Pointer types, 48 each (288 bytes)
+    1 Record types, 32 each (32 bytes)
+    28 Vector types, 48 each (1344 bytes)
+Total bytes = 5600
+0/0 implicit default constructors created
+0/0 implicit copy constructors created
+0/0 implicit move constructors created
+0/0 implicit copy assignment operators created
+0/0 implicit move assignment operators created
+0/0 implicit destructors created
+
+*** AST File Statistics:
+  0/12 source location entries read (0.000000%)
+  4/64 types read (6.250000%)
+  1032/1776 declarations read (58.108109%)
+  14/992 identifiers read (1.411290%)
+  0/4 macros read (0.000000%)
+  14/4814 statements read (0.290818%)
+  0/4 macros read (0.000000%)
+  0/802 lexical declcontexts read (0.000000%)
+
+*** PCH/ModuleFile Remappings:
+Global bit offset map:
+  0 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
+  1144160 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/B-CAAI8SHN1D31.pcm
+Global source location entry map:
+  2 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
+  8 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/B-CAAI8SHN1D31.pcm
+Global submodule map:
+  1 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
+  2 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/B-CAAI8SHN1D31.pcm
+
+*** PCH/Modules Loaded:
+Module: /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
+  Base source location offset: 2147439884
+  Base identifier ID: 0
+  Number of identifiers: 496
+  Base macro ID: 0
+  Number of macros: 2
+  Base submodule ID: 0
+  Number of submodules: 1
+  Submodule ID local -> global map:
+    0 -> 0
+  Base selector ID: 0
+  Number of selectors: 0
+  Base preprocessed entity ID: 0
+  Number of preprocessed entities: 0
+  Base type index: 0
+  Number of types: 32
+  Base decl index: 0
+  Number of decls: 888
+
+Module: /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/B-CAAI8SHN1D31.pcm
+  Base source location offset: 2147396120
+  Base identifier ID: 496
+  Number of identifiers: 496
+  Base macro ID: 2
+  Number of macros: 2
+  Base submodule ID: 1
+  Number of submodules: 1
+  Submodule ID local -> global map:
+    0 -> 1
+  Base selector ID: 0
+  Number of selectors: 0
+  Base preprocessed entity ID: 0
+  Number of preprocessed entities: 0
+  Base type index: 32
+  Number of types: 32
+  Base decl index: 888
+  Number of decls: 888
+
+
+Number of memory regions: 5
+Bytes used: 19784
+Bytes allocated: 20480
+Bytes wasted: 696 (includes alignment, etc)
+
+*** Decl Stats:
+  1907 decls total.
+    2 TranslationUnit decls, 104 each (208 bytes)
+    3 ExternCContext decls, 72 each (216 bytes)
+    807 Function decls, 168 each (135576 bytes)
+    806 ParmVar decls, 104 each (83824 bytes)
+    12 Field decls, 80 each (960 bytes)
+    3 CXXRecord decls, 144 each (432 bytes)
+    270 Typedef decls, 88 each (23760 bytes)
+    4 Import decls, 56 each (224 bytes)
+Total bytes = 245200
+
+*** Stmt/Expr Stats:
+  4843 stmts/exprs total.
+    4 UnresolvedLookupExpr, 64 each (256 bytes)
+    802 IntegerLiteral, 32 each (25664 bytes)
+    810 DeclRefExpr, 32 each (25920 bytes)
+    810 ImplicitCastExpr, 24 each (19440 bytes)
+    6 CallExpr, 24 each (144 bytes)
+    801 BinaryOperator, 32 each (25632 bytes)
+    805 ReturnStmt, 16 each (12880 bytes)
+    805 CompoundStmt, 16 each (12880 bytes)
+Total bytes = 122816
+
+STATISTICS FOR 'use.cpp':
+
+*** Preprocessor Stats:
+559 directives found:
+  552 #define.
+  0 #undef.
+  #include/#include_next/#import:
+    2 source files entered.
+    0 max include stack depth
+  0 #if/#ifndef/#ifdef.
+  0 #else/#elif/#elifdef/#elifndef.
+  0 #endif.
+  2 #pragma.
+0 #if/#ifndef#ifdef regions skipped
+0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
+0 token paste (##) operations performed, 0 on the fast path.
+
+Preprocessor Memory: 106007B total
+  BumpPtr: 53248
+  Macro Expanded Tokens: 384
+  Predefines Buffer: 32767
+  Macros: 16512
+  #pragma push_macro Info: 2056
+  Poison Reasons: 1032
+  Comment Handlers: 8
+
+*** Identifier Table Stats:
+# Identifiers:   13037
+# Empty Buckets: 19731
+Hash density (#identifiers per bucket): 0.397858
+Ave identifier length: 27.158472
+Max identifier length: 49
+
+Number of memory regions: 180
+Bytes used: 888582
+Bytes allocated: 950272
+Bytes wasted: 61690 (includes alignment, etc)
+
+*** HeaderSearch Stats:
+3 files tracked.
+  0 #import/#pragma once files.
+  4 #include/#include_next/#import.
+    0 #includes skipped due to the multi-include optimization.
+0 framework lookups.
+0 subframework lookups.
+
+*** Source Manager Stats:
+2 files mapped, 2 mem buffers mapped.
+5 local SLocEntries allocated (168 bytes of capacity), 23136B of SLoc address space used.
+12 loaded SLocEntries allocated (768 bytes of capacity), 87528B of SLoc address space used.
+2 duplicate loaded file SLocEntries detected (20694B of SLoc address space reusable).
+147 bytes of files mapped, 0 files with line #'s computed, 0 files with macro args computed.
+FileID scans: 15 linear, 0 binary.
+
+
+*** File Manager Stats:
+7 real files found, 8 real dirs found.
+0 virtual files found, 0 virtual dirs found.
+46 dir lookups, 11 dir cache misses.
+47 file lookups, 10 file cache misses.
+
+*** Virtual File System Stats:
+18 status() calls
+10 openFileForRead() calls
+0 dir_begin() calls
+1 getRealPath() calls
+0 exists() calls
+0 isLocal() calls
+
+===-------------------------------------------------------------------------===
+                          ... Statistics Collected ...
+===-------------------------------------------------------------------------===
+
+     4 file-search    - Number of attempted #includes.
+110664 source-manager - Maximum number of bytes used by source locations (both loaded and local).
+
diff --git a/dedup-test/use.cpp b/dedup-test/use.cpp
new file mode 100644
index 0000000000000..cc00041ff4cc4
--- /dev/null
+++ b/dedup-test/use.cpp
@@ -0,0 +1,3 @@
+#include "a.h"
+#include "b.h"
+int main() { return a_entry(1) + b_entry(2); }
diff --git a/dedup-test/use1.cpp b/dedup-test/use1.cpp
new file mode 100644
index 0000000000000..1d4c4250df0c7
--- /dev/null
+++ b/dedup-test/use1.cpp
@@ -0,0 +1,2 @@
+#include "a.h"
+int main() { return a_entry(1); }

>From b2f0c7269aeb4b33a7a7e16404976275062f8e36 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Mon, 29 Jun 2026 20:13:59 -0400
Subject: [PATCH 02/15] add no-op piecewise translation

---
 clang/include/clang/Serialization/ASTReader.h  | 15 +++++++++++++++
 clang/include/clang/Serialization/ModuleFile.h | 16 ++++++++++++++++
 clang/lib/Serialization/ASTReader.cpp          |  9 +++++++++
 two_modules_stats.txt                          |  1 +
 4 files changed, 41 insertions(+)
 create mode 100644 two_modules_stats.txt

diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 2afc4c199afda..62bc2b185d9d6 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2501,6 +2501,21 @@ class ASTReader : public ExternalPreprocessorSource,
     // translated or refactor the code to make it clear that
     // TranslateSourceLocation won't be called with translated source location.
 
+    // De-duplication (Stage 2): a module's local->global offset map may be
+    // piecewise rather than a single flat shift, so look up the segment that
+    // covers this location. The seeded identity segment makes this identical to
+    // the flat shift below; redirect segments (Stage 2b) send a duplicated
+    // file's locations into the module that first loaded it.
+    if (!ModuleFile.SLocRemap.empty()) {
+      SourceLocation::UIntTy Raw = Loc.getRawEncoding();
+      for (const auto &Seg : ModuleFile.SLocRemap)
+        if (Raw >= Seg.LocalBegin && Raw < Seg.LocalEnd)
+          return SourceLocation::getFromRawEncoding(
+              static_cast<SourceLocation::UIntTy>(
+                  static_cast<int64_t>(Raw) + Seg.Delta));
+      // No segment matched (shouldn't happen): fall through to the flat shift.
+    }
+
     return Loc.getLocWithOffset(ModuleFile.SLocEntryBaseOffset - 2);
   }
 
diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h
index 6c47040fde093..6d3d46003f058 100644
--- a/clang/include/clang/Serialization/ModuleFile.h
+++ b/clang/include/clang/Serialization/ModuleFile.h
@@ -346,6 +346,22 @@ class ModuleFile {
   /// AST file.
   const uint32_t *SLocEntryOffsets = nullptr;
 
+  // === Source-location de-duplication: offset remap (prototype, Stage 2) ===
+
+  /// One offset-remap segment in this module's raw SourceLocation space:
+  /// a raw local location L in [LocalBegin, LocalEnd) maps to global L + Delta.
+  struct SLocRemapSegment {
+    SourceLocation::UIntTy LocalBegin;
+    SourceLocation::UIntTy LocalEnd;
+    int64_t Delta;
+  };
+
+  /// Piecewise local->global offset map for this module, sorted by LocalBegin.
+  /// Empty => fall back to the flat shift by (SLocEntryBaseOffset - 2).
+  /// Stage 2a seeds a single identity segment equivalent to the flat shift;
+  /// Stage 2b adds redirect/shift segments for de-duplicated files.
+  llvm::SmallVector<SLocRemapSegment, 4> SLocRemap;
+
   // === Identifiers ===
 
   /// The number of identifiers in this AST file.
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 2283a9ee90b65..d7c5468392ddf 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -4282,6 +4282,15 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
         return llvm::createStringError(std::errc::invalid_argument,
                                        "ran out of source locations");
       }
+
+      // De-duplication (Stage 2a): seed the piecewise offset map with a single
+      // identity segment spanning the whole value range, equivalent to the flat
+      // shift by (BaseOffset - 2). This routes translation through the segment
+      // list with no behavior change; Stage 2b adds redirect/shift segments for
+      // duplicated files.
+      F.SLocRemap.push_back(
+          {/*LocalBegin=*/0, /*LocalEnd=*/~SourceLocation::UIntTy(0),
+           /*Delta=*/static_cast<int64_t>(F.SLocEntryBaseOffset) - 2});
       // Make our entry in the range map. BaseID is negative and growing, so
       // we invert it. Because we invert it, though, we need the other end of
       // the range.
diff --git a/two_modules_stats.txt b/two_modules_stats.txt
new file mode 100644
index 0000000000000..d0df4d9e70b35
--- /dev/null
+++ b/two_modules_stats.txt
@@ -0,0 +1 @@
+zsh: command not found: -std=c++20

>From b2653b000796d7881768a01a96d0782c45fb4c78 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 1 Jul 2026 08:56:07 -0400
Subject: [PATCH 03/15] redirect duplicated files to previous allocation

---
 clang/include/clang/Basic/SourceManager.h     |  56 ++++--
 clang/include/clang/Serialization/ASTReader.h |  28 ++-
 .../include/clang/Serialization/ModuleFile.h  |  11 ++
 clang/lib/Basic/SourceManager.cpp             |   4 +-
 clang/lib/Serialization/ASTReader.cpp         | 181 +++++++++++++-----
 dedup-test/use_err.cpp                        |  14 ++
 dedup-test/use_err_rev.cpp                    |  10 +
 7 files changed, 235 insertions(+), 69 deletions(-)
 create mode 100644 dedup-test/use_err.cpp
 create mode 100644 dedup-test/use_err_rev.cpp

diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h
index fe9dd42989e50..75b346018bb67 100644
--- a/clang/include/clang/Basic/SourceManager.h
+++ b/clang/include/clang/Basic/SourceManager.h
@@ -755,30 +755,48 @@ class SourceManager : public RefCountedBase<SourceManager> {
   static const SourceLocation::UIntTy MaxLoadedOffset =
       1ULL << (8 * sizeof(SourceLocation::UIntTy) - 1);
 
-  /// --- Source-location de-duplication: detection (prototype, Stage 1) ---
-  /// The first global offset at which each file's SLoc entry was loaded.
-  /// Keyed by FileEntry identity (the same identity Clang uses to dedup file
-  /// content), so we recognize when a later module re-loads the *same file*
-  /// already present in the loaded address space (e.g. a shared header).
-  llvm::DenseMap<const FileEntry *, SourceLocation::UIntTy>
-      LoadedFileFirstOffset;
+  /// --- Source-location de-duplication (prototype, Stage 2) ---
+  /// The canonical loaded location of a file: where its SLoc entry first landed
+  /// in the global address space. Lets a later module reuse it instead of
+  /// re-allocating. Keyed by FileEntry identity (the same identity Clang uses
+  /// to dedup file content).
+public:
+  struct LoadedFileLoc {
+    SourceLocation::UIntTy Offset = 0; ///< global raw start offset
+    int ID = 0;                        ///< global SLoc entry ID
+  };
+
+private:
+  llvm::DenseMap<const FileEntry *, LoadedFileLoc> CanonicalLoadedFiles;
   /// Number of loaded file SLoc entries that duplicated an already-loaded file.
   unsigned NumDuplicateLoadedFiles = 0;
-  /// SLoc address-space bytes occupied by those duplicates (the reuse prize).
+  /// SLoc address-space bytes those duplicates reused instead of allocating.
   uint64_t DuplicateLoadedBytes = 0;
 
 public:
-  /// Record a loaded file SLoc entry. If the *same file* was already loaded,
-  /// count it as a duplicate and add its size to the reusable total. Detection
-  /// only -- no behavior change.
-  void noteLoadedFileSLocEntry(const FileEntry *FE,
-                               SourceLocation::UIntTy Offset, uint64_t Size) {
-    if (!FE)
-      return;
-    if (!LoadedFileFirstOffset.try_emplace(FE, Offset).second) {
-      ++NumDuplicateLoadedFiles;
-      DuplicateLoadedBytes += Size;
-    }
+  /// Whether a file with this identity was already loaded into the address
+  /// space by an earlier module (i.e. its SLoc entry would be a duplicate).
+  bool isLoadedFileDuplicate(const FileEntry *FE) const {
+    return FE && CanonicalLoadedFiles.contains(FE);
+  }
+
+  /// The canonical loaded location of a previously-loaded file, or null.
+  const LoadedFileLoc *getCanonicalLoadedFile(const FileEntry *FE) const {
+    auto It = CanonicalLoadedFiles.find(FE);
+    return It == CanonicalLoadedFiles.end() ? nullptr : &It->second;
+  }
+
+  /// Record the canonical loaded location of a file the first time it loads.
+  void registerCanonicalLoadedFile(const FileEntry *FE,
+                                   SourceLocation::UIntTy Offset, int ID) {
+    if (FE)
+      CanonicalLoadedFiles.try_emplace(FE, LoadedFileLoc{Offset, ID});
+  }
+
+  /// Account for a de-duplicated file entry (for -print-stats reporting).
+  void noteDuplicateLoadedFile(uint64_t Size) {
+    ++NumDuplicateLoadedFiles;
+    DuplicateLoadedBytes += Size;
   }
 
 private:
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 62bc2b185d9d6..a063904e4567e 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2407,6 +2407,13 @@ class ASTReader : public ExternalPreprocessorSource,
                              SmallVectorImpl<uint32_t> &Offsets,
                              SmallVectorImpl<const FileEntry *> &Files);
 
+  /// Map a module-local SLoc entry offset (the value stored in an entry record,
+  /// i.e. Record[0]) to its global raw start offset, applying the
+  /// de-duplication remap. Equals (SLocEntryBaseOffset + LocalOffset) when the
+  /// module is not de-duplicated.
+  SourceLocation::UIntTy remapSLocEntryOffset(ModuleFile &F,
+                                              uint32_t LocalOffset) const;
+
   /// Retrieve the module import location and module name for the
   /// given source manager entry ID.
   std::pair<SourceLocation, StringRef> getModuleImportLoc(int ID) override;
@@ -2506,13 +2513,20 @@ class ASTReader : public ExternalPreprocessorSource,
     // covers this location. The seeded identity segment makes this identical to
     // the flat shift below; redirect segments (Stage 2b) send a duplicated
     // file's locations into the module that first loaded it.
+    //
+    // Segments are keyed by the *file-offset* part of the location, so strip
+    // the macro bit before matching and re-apply it to the result (a macro
+    // location encodes its position in the low bits with the high bit set).
     if (!ModuleFile.SLocRemap.empty()) {
       SourceLocation::UIntTy Raw = Loc.getRawEncoding();
+      SourceLocation::UIntTy MacroBit = Raw & SourceLocation::MacroIDBit;
+      SourceLocation::UIntTy Low = Raw & ~SourceLocation::MacroIDBit;
       for (const auto &Seg : ModuleFile.SLocRemap)
-        if (Raw >= Seg.LocalBegin && Raw < Seg.LocalEnd)
+        if (Low >= Seg.LocalBegin && Low < Seg.LocalEnd)
           return SourceLocation::getFromRawEncoding(
-              static_cast<SourceLocation::UIntTy>(
-                  static_cast<int64_t>(Raw) + Seg.Delta));
+              (static_cast<SourceLocation::UIntTy>(static_cast<int64_t>(Low) +
+                                                   Seg.Delta)) |
+              MacroBit);
       // No segment matched (shouldn't happen): fall through to the flat shift.
     }
 
@@ -2537,6 +2551,14 @@ class ASTReader : public ExternalPreprocessorSource,
     assert(FID.ID >= 0 && "Reading non-local FileID.");
     if (FID.isInvalid())
       return FID;
+    // De-duplication (Stage 2b): a local FileID may map to the canonical copy
+    // in an earlier module, so consult the explicit map when present. Local
+    // FileID N corresponds to local entry index N-1.
+    if (!F.LocalToGlobalID.empty()) {
+      assert((unsigned)(FID.ID - 1) < F.LocalToGlobalID.size() &&
+             "local FileID out of range");
+      return FileID::get(F.LocalToGlobalID[FID.ID - 1]);
+    }
     return FileID::get(F.SLocEntryBaseID + FID.ID - 1);
   }
 
diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h
index 6d3d46003f058..6b65089cc7e6b 100644
--- a/clang/include/clang/Serialization/ModuleFile.h
+++ b/clang/include/clang/Serialization/ModuleFile.h
@@ -362,6 +362,17 @@ class ModuleFile {
   /// Stage 2b adds redirect/shift segments for de-duplicated files.
   llvm::SmallVector<SLocRemapSegment, 4> SLocRemap;
 
+  /// Maps a local SLoc entry index -> its resulting global SLoc entry ID.
+  /// Kept entries map to their own newly-allocated ID; de-duplicated entries
+  /// map to the ID of the canonical copy in an earlier module. Empty => no
+  /// de-duplication for this module (global ID is SLocEntryBaseID + index).
+  std::vector<int> LocalToGlobalID;
+
+  /// For each kept global slot j (0-based, relative to SLocEntryBaseID), the
+  /// original local entry index in this module (index into SLocEntryOffsets).
+  /// Empty => no de-duplication (kept slot j == local index j).
+  std::vector<unsigned> KeptSLocLocalIndex;
+
   // === Identifiers ===
 
   /// The number of identifiers in this AST file.
diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp
index 194426ae2f145..fc1d0094a84d8 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -2154,9 +2154,9 @@ void SourceManager::PrintStats() const {
                << "B of SLoc address space used.\n";
   if (NumDuplicateLoadedFiles)
     llvm::errs() << NumDuplicateLoadedFiles
-                 << " duplicate loaded file SLocEntries detected ("
+                 << " duplicate loaded file SLocEntries de-duplicated ("
                  << DuplicateLoadedBytes
-                 << "B of SLoc address space reusable).\n";
+                 << "B of SLoc address space reused).\n";
 
   unsigned NumLineNumsComputed = 0;
   unsigned NumFileBytesMapped = 0;
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index d7c5468392ddf..d8041c06a4674 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -1853,6 +1853,21 @@ bool ASTReader::scanLoadedSLocEntries(
   return true;
 }
 
+SourceLocation::UIntTy
+ASTReader::remapSLocEntryOffset(ModuleFile &F, uint32_t LocalOffset) const {
+  // The entry's local raw start location is LocalOffset + 2 (offsets 0 and 1
+  // are reserved). Find the segment covering it and apply that segment's delta.
+  if (!F.SLocRemap.empty()) {
+    SourceLocation::UIntTy Low = LocalOffset + 2;
+    for (const auto &Seg : F.SLocRemap)
+      if (Low >= Seg.LocalBegin && Low < Seg.LocalEnd)
+        return static_cast<SourceLocation::UIntTy>(static_cast<int64_t>(Low) +
+                                                   Seg.Delta);
+  }
+  // No remap (or no segment matched): original flat shift.
+  return F.SLocEntryBaseOffset + LocalOffset;
+}
+
 llvm::Expected<SourceLocation::UIntTy>
 ASTReader::readSLocOffset(ModuleFile *F, unsigned Index) {
   BitstreamCursor &Cursor = F->SLocEntryCursor;
@@ -1885,7 +1900,7 @@ ASTReader::readSLocOffset(ModuleFile *F, unsigned Index) {
   case SM_SLOC_FILE_ENTRY:
   case SM_SLOC_BUFFER_ENTRY:
   case SM_SLOC_EXPANSION_ENTRY:
-    return F->SLocEntryBaseOffset + Record[0];
+    return remapSLocEntryOffset(*F, Record[0]);
   }
 }
 
@@ -1898,13 +1913,21 @@ int ASTReader::getSLocEntryID(SourceLocation::UIntTy SLocOffset) {
 
   bool Invalid = false;
 
+  // De-duplication (Stage 2b): the global table holds only this module's kept
+  // entries, so search over kept slots and translate each slot to its on-disk
+  // local entry index when reading the offset.
+  bool Dedup = !F->KeptSLocLocalIndex.empty();
+  unsigned NumSlots =
+      Dedup ? F->KeptSLocLocalIndex.size() : F->LocalNumSLocEntries;
+
   auto It = llvm::upper_bound(
-      llvm::index_range(0, F->LocalNumSLocEntries), SLocOffset,
-      [&](SourceLocation::UIntTy Offset, std::size_t LocalIndex) {
-        int ID = F->SLocEntryBaseID + LocalIndex;
+      llvm::index_range(0, NumSlots), SLocOffset,
+      [&](SourceLocation::UIntTy Offset, std::size_t Slot) {
+        int ID = F->SLocEntryBaseID + Slot;
         std::size_t Index = -ID - 2;
         if (!SourceMgr.SLocEntryOffsetLoaded[Index]) {
           assert(!SourceMgr.SLocEntryLoaded[Index]);
+          unsigned LocalIndex = Dedup ? F->KeptSLocLocalIndex[Slot] : Slot;
           auto MaybeEntryOffset = readSLocOffset(F, LocalIndex);
           if (!MaybeEntryOffset) {
             Error(MaybeEntryOffset.takeError());
@@ -1986,15 +2009,23 @@ bool ASTReader::ReadSLocEntry(int ID) {
   };
 
   ModuleFile *F = GlobalSLocEntryMap.find(-ID)->second;
+  // De-duplication (Stage 2b): with entries skipped, the global slot
+  // (ID - SLocEntryBaseID) is no longer the on-disk local index, so translate
+  // it through the kept-index table when present.
+  unsigned LocalIndex = ID - F->SLocEntryBaseID;
+  if (!F->KeptSLocLocalIndex.empty()) {
+    assert(LocalIndex < F->KeptSLocLocalIndex.size() && "kept slot out of range");
+    LocalIndex = F->KeptSLocLocalIndex[LocalIndex];
+  }
   if (llvm::Error Err = F->SLocEntryCursor.JumpToBit(
-          F->SLocEntryOffsetsBase +
-          F->SLocEntryOffsets[ID - F->SLocEntryBaseID])) {
+          F->SLocEntryOffsetsBase + F->SLocEntryOffsets[LocalIndex])) {
     Error(std::move(Err));
     return true;
   }
 
   BitstreamCursor &SLocEntryCursor = F->SLocEntryCursor;
   SourceLocation::UIntTy BaseOffset = F->SLocEntryBaseOffset;
+  (void)BaseOffset;
 
   ++NumSLocEntriesRead;
   Expected<llvm::BitstreamEntry> MaybeEntry = SLocEntryCursor.advance();
@@ -2044,7 +2075,7 @@ bool ASTReader::ReadSLocEntry(int ID) {
     SrcMgr::CharacteristicKind
       FileCharacter = (SrcMgr::CharacteristicKind)Record[2];
     FileID FID = SourceMgr.createFileID(*File, IncludeLoc, FileCharacter, ID,
-                                        BaseOffset + Record[0]);
+                                        remapSLocEntryOffset(*F, Record[0]));
     SrcMgr::FileInfo &FileInfo = SourceMgr.getSLocEntry(FID).getFile();
     FileInfo.NumCreatedFIDs = Record[5];
     if (Record[3])
@@ -2086,7 +2117,8 @@ bool ASTReader::ReadSLocEntry(int ID) {
     if (!Buffer)
       return true;
     FileID FID = SourceMgr.createFileID(std::move(Buffer), FileCharacter, ID,
-                                        BaseOffset + Offset, IncludeLoc);
+                                        remapSLocEntryOffset(*F, Offset),
+                                        IncludeLoc);
     if (Record[3]) {
       auto &FileInfo = SourceMgr.getSLocEntry(FID).getFile();
       FileInfo.setHasLineDirectives();
@@ -2100,7 +2132,7 @@ bool ASTReader::ReadSLocEntry(int ID) {
     SourceLocation ExpansionEnd = ReadSourceLocation(*F, Record[3]);
     SourceMgr.createExpansionLoc(SpellingLoc, ExpansionBegin, ExpansionEnd,
                                  Record[5], Record[4], ID,
-                                 BaseOffset + Record[0]);
+                                 remapSLocEntryOffset(*F, Record[0]));
     break;
   }
   }
@@ -4273,9 +4305,36 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       F.LocalNumSLocEntries = Record[0];
       SourceLocation::UIntTy SLocSpaceSize = Record[1];
       F.SLocEntryOffsetsBase = Record[2] + F.SourceManagerBlockStartOffset;
+      unsigned N = F.LocalNumSLocEntries;
+
+      // De-duplication (Stage 2b): scan this module's SLoc entries *before*
+      // allocating, so we can recognize files already loaded by an earlier
+      // module and reserve less address space for them.
+      SmallVector<uint32_t, 64> Offsets;
+      SmallVector<const FileEntry *, 64> Files;
+      bool Scanned = scanLoadedSLocEntries(F, Offsets, Files);
+
+      // Classify duplicates and compute the reduced allocation request.
+      auto entrySize = [&](unsigned I) -> uint64_t {
+        return (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) - Offsets[I];
+      };
+      unsigned NumDupEntries = 0;
+      uint64_t DupBytes = 0;
+      if (Scanned)
+        for (unsigned I = 0; I != N; ++I)
+          if (SourceMgr.isLoadedFileDuplicate(Files[I])) {
+            uint64_t Size = entrySize(I);
+            SourceMgr.noteDuplicateLoadedFile(Size);
+            DupBytes += Size;
+            ++NumDupEntries;
+          }
+      unsigned ReducedNumEntries = N - NumDupEntries;
+      SourceLocation::UIntTy ReducedSize = SLocSpaceSize - DupBytes;
+
+      // Reserve the reduced amount (equals the full amount when nothing is
+      // de-duplicated).
       std::tie(F.SLocEntryBaseID, F.SLocEntryBaseOffset) =
-          SourceMgr.AllocateLoadedSLocEntries(F.LocalNumSLocEntries,
-                                              SLocSpaceSize);
+          SourceMgr.AllocateLoadedSLocEntries(ReducedNumEntries, ReducedSize);
       if (!F.SLocEntryBaseID) {
         Diags.Report(SourceLocation(), diag::remark_sloc_usage);
         SourceMgr.noteSLocAddressSpaceUsage(Diags);
@@ -4283,49 +4342,81 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
                                        "ran out of source locations");
       }
 
-      // De-duplication (Stage 2a): seed the piecewise offset map with a single
-      // identity segment spanning the whole value range, equivalent to the flat
-      // shift by (BaseOffset - 2). This routes translation through the segment
-      // list with no behavior change; Stage 2b adds redirect/shift segments for
-      // duplicated files.
-      F.SLocRemap.push_back(
-          {/*LocalBegin=*/0, /*LocalEnd=*/~SourceLocation::UIntTy(0),
-           /*Delta=*/static_cast<int64_t>(F.SLocEntryBaseOffset) - 2});
+      if (NumDupEntries == 0) {
+        // No de-duplication: one identity segment (== flat shift) and a linear
+        // local->global ID mapping. Register each file as the canonical copy so
+        // later modules can reuse it.
+        F.SLocRemap.push_back(
+            {/*LocalBegin=*/0, /*LocalEnd=*/~SourceLocation::UIntTy(0),
+             /*Delta=*/static_cast<int64_t>(F.SLocEntryBaseOffset) - 2});
+        if (Scanned)
+          for (unsigned I = 0; I != N; ++I)
+            if (Files[I])
+              SourceMgr.registerCanonicalLoadedFile(
+                  Files[I], F.SLocEntryBaseOffset + Offsets[I],
+                  F.SLocEntryBaseID + (int)I);
+      } else {
+        // De-duplication: build keep/redirect offset segments and the
+        // local->global ID map. Skipped (duplicate) entries get no slot and no
+        // address space; their references are redirected to the canonical copy.
+        F.LocalToGlobalID.assign(N, 0);
+        F.KeptSLocLocalIndex.reserve(ReducedNumEntries);
+        uint64_t DupBefore = 0;
+        unsigned KeptCount = 0;
+        for (unsigned I = 0; I != N; ++I) {
+          SourceLocation::UIntTy LowStart = Offsets[I] + 2;
+          SourceLocation::UIntTy LowEnd =
+              (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) + 2;
+          const FileEntry *FE = Files[I];
+          if (FE && SourceMgr.isLoadedFileDuplicate(FE)) {
+            // Redirect this file's locations into the module that first loaded
+            // it; reserve nothing here.
+            const SourceManager::LoadedFileLoc *Canon =
+                SourceMgr.getCanonicalLoadedFile(FE);
+            F.SLocRemap.push_back({LowStart, LowEnd,
+                                   static_cast<int64_t>(Canon->Offset) -
+                                       static_cast<int64_t>(LowStart)});
+            F.LocalToGlobalID[I] = Canon->ID;
+            DupBefore += LowEnd - LowStart;
+          } else {
+            // Keep: lands in this module's block, shifted down to close gaps
+            // left by skipped duplicates before it.
+            int GlobalID = F.SLocEntryBaseID + (int)KeptCount;
+            SourceLocation::UIntTy GlobalStart =
+                static_cast<SourceLocation::UIntTy>(F.SLocEntryBaseOffset +
+                                                    Offsets[I] - DupBefore);
+            F.SLocRemap.push_back({LowStart, LowEnd,
+                                   static_cast<int64_t>(GlobalStart) -
+                                       static_cast<int64_t>(LowStart)});
+            F.LocalToGlobalID[I] = GlobalID;
+            F.KeptSLocLocalIndex.push_back(I);
+            if (FE)
+              SourceMgr.registerCanonicalLoadedFile(FE, GlobalStart, GlobalID);
+            ++KeptCount;
+          }
+        }
+        // Extend the first/last segments to cover the whole value range so
+        // every translated location matches a segment.
+        F.SLocRemap.front().LocalBegin = 0;
+        F.SLocRemap.back().LocalEnd = ~SourceLocation::UIntTy(0);
+        assert(KeptCount == ReducedNumEntries && "kept count mismatch");
+      }
+
       // Make our entry in the range map. BaseID is negative and growing, so
       // we invert it. Because we invert it, though, we need the other end of
       // the range.
       unsigned RangeStart =
-          unsigned(-F.SLocEntryBaseID) - F.LocalNumSLocEntries + 1;
+          unsigned(-F.SLocEntryBaseID) - ReducedNumEntries + 1;
       GlobalSLocEntryMap.insert(std::make_pair(RangeStart, &F));
       F.FirstLoc = SourceLocation::getFromRawEncoding(F.SLocEntryBaseOffset);
 
       // SLocEntryBaseOffset is lower than MaxLoadedOffset and decreasing.
       assert((F.SLocEntryBaseOffset & SourceLocation::MacroIDBit) == 0);
-      GlobalSLocOffsetMap.insert(
-          std::make_pair(SourceManager::MaxLoadedOffset - F.SLocEntryBaseOffset
-                           - SLocSpaceSize,&F));
-
-      TotalNumSLocEntries += F.LocalNumSLocEntries;
-
-      // De-duplication detection (prototype, Stage 1). Walk this module's SLoc
-      // file entries and recognize when the same file was already loaded by an
-      // earlier module, measuring the address space a future reuse (Stage 2)
-      // could reclaim. Detection only: no allocation/translation change here.
-      {
-        SmallVector<uint32_t, 64> Offsets;
-        SmallVector<const FileEntry *, 64> Files;
-        if (scanLoadedSLocEntries(F, Offsets, Files)) {
-          unsigned N = Offsets.size();
-          for (unsigned I = 0; I != N; ++I) {
-            if (!Files[I])
-              continue;
-            uint64_t Size =
-                (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) - Offsets[I];
-            SourceMgr.noteLoadedFileSLocEntry(
-                Files[I], F.SLocEntryBaseOffset + Offsets[I], Size);
-          }
-        }
-      }
+      GlobalSLocOffsetMap.insert(std::make_pair(
+          SourceManager::MaxLoadedOffset - F.SLocEntryBaseOffset - ReducedSize,
+          &F));
+
+      TotalNumSLocEntries += ReducedNumEntries;
       break;
     }
 
diff --git a/dedup-test/use_err.cpp b/dedup-test/use_err.cpp
new file mode 100644
index 0000000000000..e3cf22fe27df4
--- /dev/null
+++ b/dedup-test/use_err.cpp
@@ -0,0 +1,14 @@
+// Stage 2c: prove the redirect resolves correctly.
+// Include BOTH modules so A loads first (registers shared.h as the canonical
+// copy) and B loads second (its shared.h is de-duplicated / redirected into
+// A's copy). Then trigger a diagnostic whose location lives in shared.h.
+// The error must still point at shared.h:<line> with the right function/line,
+// which only holds if the redirected offsets resolve correctly.
+#include "a.h"
+#include "b.h"
+
+int main() {
+  // shared_fn_1 is defined in shared.h (present in both A and B); pass a bad
+  // argument so overload resolution reports the candidate in shared.h.
+  return a_entry(1) + shared_fn_1("oops");
+}
diff --git a/dedup-test/use_err_rev.cpp b/dedup-test/use_err_rev.cpp
new file mode 100644
index 0000000000000..817bfb4bc2b1e
--- /dev/null
+++ b/dedup-test/use_err_rev.cpp
@@ -0,0 +1,10 @@
+// Stage 2c: load-order independence. Same as use_err.cpp but with the include
+// order flipped so B is seen first. Whichever module loads first becomes the
+// canonical copy; the other redirects into it. Result should be identical:
+// dedup active + the diagnostic still resolves to shared.h:<line>.
+#include "b.h"
+#include "a.h"
+
+int main() {
+  return a_entry(1) + shared_fn_1("oops");
+}

>From 29b5f5f76393a6aef250af3a43f7a69de8c0167c Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 1 Jul 2026 09:53:16 -0400
Subject: [PATCH 04/15] clean up stats files

---
 clang/include/clang/Serialization/ASTReader.h | 3 +--
 two_modules_stats.txt                         | 1 -
 2 files changed, 1 insertion(+), 3 deletions(-)
 delete mode 100644 two_modules_stats.txt

diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index a063904e4567e..ec4312cf1972c 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2399,10 +2399,9 @@ class ASTReader : public ExternalPreprocessorSource,
   llvm::Expected<SourceLocation::UIntTy> readSLocOffset(ModuleFile *F,
                                                         unsigned Index);
 
-  /// Walk module \p F's SLoc entry records (read-only). Fills \p Offsets[i]
+  /// Walk module F's SLoc entry records (read-only). Fills Offsets[i]
   /// with each entry's local offset and \p Files[i] with its FileEntry (null
   /// for non-file entries). Returns false if the records couldn't be read.
-  /// Shared scaffolding for source-location de-duplication.
   bool scanLoadedSLocEntries(ModuleFile &F,
                              SmallVectorImpl<uint32_t> &Offsets,
                              SmallVectorImpl<const FileEntry *> &Files);
diff --git a/two_modules_stats.txt b/two_modules_stats.txt
deleted file mode 100644
index d0df4d9e70b35..0000000000000
--- a/two_modules_stats.txt
+++ /dev/null
@@ -1 +0,0 @@
-zsh: command not found: -std=c++20

>From eb4ad5a33e934afa5e2f190df4d9ccdf1b7f884e Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 1 Jul 2026 09:56:21 -0400
Subject: [PATCH 05/15] clean up stats files

---
 dedup-test/one_module_stats.txt  | 352 --------------------
 dedup-test/two_modules_stats.txt | 535 -------------------------------
 2 files changed, 887 deletions(-)
 delete mode 100644 dedup-test/one_module_stats.txt
 delete mode 100644 dedup-test/two_modules_stats.txt

diff --git a/dedup-test/one_module_stats.txt b/dedup-test/one_module_stats.txt
deleted file mode 100644
index 373fe97b5e1d8..0000000000000
--- a/dedup-test/one_module_stats.txt
+++ /dev/null
@@ -1,352 +0,0 @@
-
-STATISTICS:
-
-*** Semantic Analysis Stats:
-
-Number of memory regions: 0
-Bytes used: 0
-Bytes allocated: 0
-Bytes wasted: 0 (includes alignment, etc)
-
-*** Analysis Based Warnings Stats:
-401 functions analyzed (0 w/o CFGs).
-  1203 CFG blocks built.
-  3 average CFG blocks per function.
-  3 max CFG blocks per function.
-0 functions analyzed for uninitialiazed variables
-  0 variables analyzed.
-  0 average variables per function.
-  0 max variables per function.
-  0 block visits.
-  0 average block visits per function.
-  0 max block visits per function.
-
-*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
-
-
-
-*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
-
-
-Total missing origins: 0
-
-****************************************
-
-*** AST Context Stats:
-  156 types total.
-    120 Builtin types, 32 each (3840 bytes)
-    1 FunctionProto types, 48 each (48 bytes)
-    5 Pointer types, 48 each (240 bytes)
-    1 Record types, 32 each (32 bytes)
-    1 Typedef types, 32 each (32 bytes)
-    28 Vector types, 48 each (1344 bytes)
-Total bytes = 5536
-0/0 implicit default constructors created
-0/0 implicit copy constructors created
-0/0 implicit move constructors created
-0/0 implicit copy assignment operators created
-0/0 implicit move assignment operators created
-0/0 implicit destructors created
-
-*** AST File Statistics:
-
-*** PCH/ModuleFile Remappings:
-
-*** PCH/Modules Loaded:
-
-Number of memory regions: 62
-Bytes used: 248400
-Bytes allocated: 253952
-Bytes wasted: 5552 (includes alignment, etc)
-
-*** Decl Stats:
-  995 decls total.
-    1 TranslationUnit decls, 104 each (104 bytes)
-    1 ExternCContext decls, 72 each (72 bytes)
-    401 Function decls, 168 each (67368 bytes)
-    401 ParmVar decls, 104 each (41704 bytes)
-    8 Field decls, 80 each (640 bytes)
-    2 CXXRecord decls, 144 each (288 bytes)
-    180 Typedef decls, 88 each (15840 bytes)
-    1 Import decls, 56 each (56 bytes)
-Total bytes = 126072
-
-*** Stmt/Expr Stats:
-  2408 stmts/exprs total.
-    1 UnresolvedLookupExpr, 64 each (64 bytes)
-    400 IntegerLiteral, 32 each (12800 bytes)
-    402 DeclRefExpr, 32 each (12864 bytes)
-    402 ImplicitCastExpr, 24 each (9648 bytes)
-    1 CallExpr, 24 each (24 bytes)
-    400 BinaryOperator, 32 each (12800 bytes)
-    401 ReturnStmt, 16 each (6416 bytes)
-    401 CompoundStmt, 16 each (6416 bytes)
-Total bytes = 61032
-
-STATISTICS FOR './module.modulemap':
-
-*** Preprocessor Stats:
-564 directives found:
-  553 #define.
-  0 #undef.
-  #include/#include_next/#import:
-    4 source files entered.
-    1 max include stack depth
-  2 #if/#ifndef/#ifdef.
-  0 #else/#elif/#elifdef/#elifndef.
-  2 #endif.
-  2 #pragma.
-0 #if/#ifndef#ifdef regions skipped
-0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
-0 token paste (##) operations performed, 0 on the fast path.
-
-Preprocessor Memory: 110103B total
-  BumpPtr: 57344
-  Macro Expanded Tokens: 384
-  Predefines Buffer: 32767
-  Macros: 16512
-  #pragma push_macro Info: 2056
-  Poison Reasons: 1032
-  Comment Handlers: 8
-
-*** Identifier Table Stats:
-# Identifiers:   13432
-# Empty Buckets: 19336
-Hash density (#identifiers per bucket): 0.409912
-Ave identifier length: 26.735631
-Max identifier length: 49
-
-Number of memory regions: 182
-Bytes used: 909825
-Bytes allocated: 966656
-Bytes wasted: 56831 (includes alignment, etc)
-
-*** HeaderSearch Stats:
-3 files tracked.
-  0 #import/#pragma once files.
-  2 #include/#include_next/#import.
-    0 #includes skipped due to the multi-include optimization.
-0 framework lookups.
-0 subframework lookups.
-
-*** Source Manager Stats:
-3 files mapped, 3 mem buffers mapped.
-7 local SLocEntries allocated (168 bytes of capacity), 43765B of SLoc address space used.
-0 loaded SLocEntries allocated (0 bytes of capacity), 0B of SLoc address space used.
-20795 bytes of files mapped, 2 files with line #'s computed, 0 files with macro args computed.
-FileID scans: 32 linear, 0 binary.
-
-
-*** File Manager Stats:
-5 real files found, 7 real dirs found.
-0 virtual files found, 0 virtual dirs found.
-28 dir lookups, 9 dir cache misses.
-19 file lookups, 8 file cache misses.
-
-*** Virtual File System Stats:
-14 status() calls
-5 openFileForRead() calls
-0 dir_begin() calls
-1 getRealPath() calls
-0 exists() calls
-0 isLocal() calls
-
-===-------------------------------------------------------------------------===
-                          ... Statistics Collected ...
-===-------------------------------------------------------------------------===
-
-    2 file-search    - Number of attempted #includes.
-43765 source-manager - Maximum number of bytes used by source locations (both loaded and local).
-
-
-STATISTICS:
-
-*** Semantic Analysis Stats:
-
-Number of memory regions: 0
-Bytes used: 0
-Bytes allocated: 0
-Bytes wasted: 0 (includes alignment, etc)
-
-*** Analysis Based Warnings Stats:
-0 functions analyzed (0 w/o CFGs).
-  0 CFG blocks built.
-  0 average CFG blocks per function.
-  0 max CFG blocks per function.
-0 functions analyzed for uninitialiazed variables
-  0 variables analyzed.
-  0 average variables per function.
-  0 max variables per function.
-  0 block visits.
-  0 average block visits per function.
-  0 max block visits per function.
-
-*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
-
-
-
-*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
-
-
-Total missing origins: 0
-
-****************************************
-
-*** AST Context Stats:
-  157 types total.
-    120 Builtin types, 32 each (3840 bytes)
-    2 FunctionProto types, 48 each (96 bytes)
-    6 Pointer types, 48 each (288 bytes)
-    1 Record types, 32 each (32 bytes)
-    28 Vector types, 48 each (1344 bytes)
-Total bytes = 5600
-0/0 implicit default constructors created
-0/0 implicit copy constructors created
-0/0 implicit move constructors created
-0/0 implicit copy assignment operators created
-0/0 implicit move assignment operators created
-0/0 implicit destructors created
-
-*** AST File Statistics:
-  0/6 source location entries read (0.000000%)
-  2/32 types read (6.250000%)
-  516/888 declarations read (58.108109%)
-  7/496 identifiers read (1.411290%)
-  0/2 macros read (0.000000%)
-  7/2407 statements read (0.290818%)
-  0/2 macros read (0.000000%)
-  0/401 lexical declcontexts read (0.000000%)
-
-*** PCH/ModuleFile Remappings:
-Global bit offset map:
-  0 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache1/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
-Global source location entry map:
-  2 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache1/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
-Global submodule map:
-  1 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache1/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
-
-*** PCH/Modules Loaded:
-Module: /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache1/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
-  Base source location offset: 2147439884
-  Base identifier ID: 0
-  Number of identifiers: 496
-  Base macro ID: 0
-  Number of macros: 2
-  Base submodule ID: 0
-  Number of submodules: 1
-  Submodule ID local -> global map:
-    0 -> 0
-  Base selector ID: 0
-  Number of selectors: 0
-  Base preprocessed entity ID: 0
-  Number of preprocessed entities: 0
-  Base type index: 0
-  Number of types: 32
-  Base decl index: 0
-  Number of decls: 888
-
-
-Number of memory regions: 5
-Bytes used: 18452
-Bytes allocated: 20480
-Bytes wasted: 2028 (includes alignment, etc)
-
-*** Decl Stats:
-  1002 decls total.
-    1 TranslationUnit decls, 104 each (104 bytes)
-    2 ExternCContext decls, 72 each (144 bytes)
-    404 Function decls, 168 each (67872 bytes)
-    403 ParmVar decls, 104 each (41912 bytes)
-    8 Field decls, 80 each (640 bytes)
-    2 CXXRecord decls, 144 each (288 bytes)
-    180 Typedef decls, 88 each (15840 bytes)
-    2 Import decls, 56 each (112 bytes)
-Total bytes = 126912
-
-*** Stmt/Expr Stats:
-  2422 stmts/exprs total.
-    2 UnresolvedLookupExpr, 64 each (128 bytes)
-    401 IntegerLiteral, 32 each (12832 bytes)
-    405 DeclRefExpr, 32 each (12960 bytes)
-    405 ImplicitCastExpr, 24 each (9720 bytes)
-    3 CallExpr, 24 each (72 bytes)
-    400 BinaryOperator, 32 each (12800 bytes)
-    403 ReturnStmt, 16 each (6448 bytes)
-    403 CompoundStmt, 16 each (6448 bytes)
-Total bytes = 61408
-
-STATISTICS FOR 'use1.cpp':
-
-*** Preprocessor Stats:
-558 directives found:
-  552 #define.
-  0 #undef.
-  #include/#include_next/#import:
-    2 source files entered.
-    0 max include stack depth
-  0 #if/#ifndef/#ifdef.
-  0 #else/#elif/#elifdef/#elifndef.
-  0 #endif.
-  2 #pragma.
-0 #if/#ifndef#ifdef regions skipped
-0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
-0 token paste (##) operations performed, 0 on the fast path.
-
-Preprocessor Memory: 106007B total
-  BumpPtr: 53248
-  Macro Expanded Tokens: 384
-  Predefines Buffer: 32767
-  Macros: 16512
-  #pragma push_macro Info: 2056
-  Poison Reasons: 1032
-  Comment Handlers: 8
-
-*** Identifier Table Stats:
-# Identifiers:   13033
-# Empty Buckets: 19735
-Hash density (#identifiers per bucket): 0.397736
-Ave identifier length: 27.165119
-Max identifier length: 49
-
-Number of memory regions: 180
-Bytes used: 888396
-Bytes allocated: 950272
-Bytes wasted: 61876 (includes alignment, etc)
-
-*** HeaderSearch Stats:
-3 files tracked.
-  0 #import/#pragma once files.
-  2 #include/#include_next/#import.
-    0 #includes skipped due to the multi-include optimization.
-0 framework lookups.
-0 subframework lookups.
-
-*** Source Manager Stats:
-2 files mapped, 2 mem buffers mapped.
-5 local SLocEntries allocated (168 bytes of capacity), 23108B of SLoc address space used.
-6 loaded SLocEntries allocated (768 bytes of capacity), 43764B of SLoc address space used.
-119 bytes of files mapped, 0 files with line #'s computed, 0 files with macro args computed.
-FileID scans: 11 linear, 0 binary.
-
-
-*** File Manager Stats:
-6 real files found, 8 real dirs found.
-0 virtual files found, 0 virtual dirs found.
-31 dir lookups, 11 dir cache misses.
-27 file lookups, 9 file cache misses.
-
-*** Virtual File System Stats:
-17 status() calls
-6 openFileForRead() calls
-0 dir_begin() calls
-1 getRealPath() calls
-0 exists() calls
-0 isLocal() calls
-
-===-------------------------------------------------------------------------===
-                          ... Statistics Collected ...
-===-------------------------------------------------------------------------===
-
-    2 file-search    - Number of attempted #includes.
-66872 source-manager - Maximum number of bytes used by source locations (both loaded and local).
-
diff --git a/dedup-test/two_modules_stats.txt b/dedup-test/two_modules_stats.txt
deleted file mode 100644
index dd3a1f0c2d3ac..0000000000000
--- a/dedup-test/two_modules_stats.txt
+++ /dev/null
@@ -1,535 +0,0 @@
-
-STATISTICS:
-
-*** Semantic Analysis Stats:
-
-Number of memory regions: 0
-Bytes used: 0
-Bytes allocated: 0
-Bytes wasted: 0 (includes alignment, etc)
-
-*** Analysis Based Warnings Stats:
-401 functions analyzed (0 w/o CFGs).
-  1203 CFG blocks built.
-  3 average CFG blocks per function.
-  3 max CFG blocks per function.
-0 functions analyzed for uninitialiazed variables
-  0 variables analyzed.
-  0 average variables per function.
-  0 max variables per function.
-  0 block visits.
-  0 average block visits per function.
-  0 max block visits per function.
-
-*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
-
-
-
-*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
-
-
-Total missing origins: 0
-
-****************************************
-
-*** AST Context Stats:
-  156 types total.
-    120 Builtin types, 32 each (3840 bytes)
-    1 FunctionProto types, 48 each (48 bytes)
-    5 Pointer types, 48 each (240 bytes)
-    1 Record types, 32 each (32 bytes)
-    1 Typedef types, 32 each (32 bytes)
-    28 Vector types, 48 each (1344 bytes)
-Total bytes = 5536
-0/0 implicit default constructors created
-0/0 implicit copy constructors created
-0/0 implicit move constructors created
-0/0 implicit copy assignment operators created
-0/0 implicit move assignment operators created
-0/0 implicit destructors created
-
-*** AST File Statistics:
-
-*** PCH/ModuleFile Remappings:
-
-*** PCH/Modules Loaded:
-
-Number of memory regions: 62
-Bytes used: 248400
-Bytes allocated: 253952
-Bytes wasted: 5552 (includes alignment, etc)
-
-*** Decl Stats:
-  995 decls total.
-    1 TranslationUnit decls, 104 each (104 bytes)
-    1 ExternCContext decls, 72 each (72 bytes)
-    401 Function decls, 168 each (67368 bytes)
-    401 ParmVar decls, 104 each (41704 bytes)
-    8 Field decls, 80 each (640 bytes)
-    2 CXXRecord decls, 144 each (288 bytes)
-    180 Typedef decls, 88 each (15840 bytes)
-    1 Import decls, 56 each (56 bytes)
-Total bytes = 126072
-
-*** Stmt/Expr Stats:
-  2408 stmts/exprs total.
-    1 UnresolvedLookupExpr, 64 each (64 bytes)
-    400 IntegerLiteral, 32 each (12800 bytes)
-    402 DeclRefExpr, 32 each (12864 bytes)
-    402 ImplicitCastExpr, 24 each (9648 bytes)
-    1 CallExpr, 24 each (24 bytes)
-    400 BinaryOperator, 32 each (12800 bytes)
-    401 ReturnStmt, 16 each (6416 bytes)
-    401 CompoundStmt, 16 each (6416 bytes)
-Total bytes = 61032
-
-STATISTICS FOR './module.modulemap':
-
-*** Preprocessor Stats:
-564 directives found:
-  553 #define.
-  0 #undef.
-  #include/#include_next/#import:
-    4 source files entered.
-    1 max include stack depth
-  2 #if/#ifndef/#ifdef.
-  0 #else/#elif/#elifdef/#elifndef.
-  2 #endif.
-  2 #pragma.
-0 #if/#ifndef#ifdef regions skipped
-0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
-0 token paste (##) operations performed, 0 on the fast path.
-
-Preprocessor Memory: 110103B total
-  BumpPtr: 57344
-  Macro Expanded Tokens: 384
-  Predefines Buffer: 32767
-  Macros: 16512
-  #pragma push_macro Info: 2056
-  Poison Reasons: 1032
-  Comment Handlers: 8
-
-*** Identifier Table Stats:
-# Identifiers:   13432
-# Empty Buckets: 19336
-Hash density (#identifiers per bucket): 0.409912
-Ave identifier length: 26.735631
-Max identifier length: 49
-
-Number of memory regions: 182
-Bytes used: 909825
-Bytes allocated: 966656
-Bytes wasted: 56831 (includes alignment, etc)
-
-*** HeaderSearch Stats:
-3 files tracked.
-  0 #import/#pragma once files.
-  2 #include/#include_next/#import.
-    0 #includes skipped due to the multi-include optimization.
-0 framework lookups.
-0 subframework lookups.
-
-*** Source Manager Stats:
-3 files mapped, 3 mem buffers mapped.
-7 local SLocEntries allocated (168 bytes of capacity), 43765B of SLoc address space used.
-0 loaded SLocEntries allocated (0 bytes of capacity), 0B of SLoc address space used.
-20795 bytes of files mapped, 2 files with line #'s computed, 0 files with macro args computed.
-FileID scans: 32 linear, 0 binary.
-
-
-*** File Manager Stats:
-5 real files found, 7 real dirs found.
-0 virtual files found, 0 virtual dirs found.
-28 dir lookups, 9 dir cache misses.
-19 file lookups, 8 file cache misses.
-
-*** Virtual File System Stats:
-14 status() calls
-5 openFileForRead() calls
-0 dir_begin() calls
-1 getRealPath() calls
-0 exists() calls
-0 isLocal() calls
-
-===-------------------------------------------------------------------------===
-                          ... Statistics Collected ...
-===-------------------------------------------------------------------------===
-
-    2 file-search    - Number of attempted #includes.
-43765 source-manager - Maximum number of bytes used by source locations (both loaded and local).
-
-
-STATISTICS:
-
-*** Semantic Analysis Stats:
-
-Number of memory regions: 0
-Bytes used: 0
-Bytes allocated: 0
-Bytes wasted: 0 (includes alignment, etc)
-
-*** Analysis Based Warnings Stats:
-401 functions analyzed (0 w/o CFGs).
-  1203 CFG blocks built.
-  3 average CFG blocks per function.
-  3 max CFG blocks per function.
-0 functions analyzed for uninitialiazed variables
-  0 variables analyzed.
-  0 average variables per function.
-  0 max variables per function.
-  0 block visits.
-  0 average block visits per function.
-  0 max block visits per function.
-
-*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
-
-
-
-*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
-
-
-Total missing origins: 0
-
-****************************************
-
-*** AST Context Stats:
-  156 types total.
-    120 Builtin types, 32 each (3840 bytes)
-    1 FunctionProto types, 48 each (48 bytes)
-    5 Pointer types, 48 each (240 bytes)
-    1 Record types, 32 each (32 bytes)
-    1 Typedef types, 32 each (32 bytes)
-    28 Vector types, 48 each (1344 bytes)
-Total bytes = 5536
-0/0 implicit default constructors created
-0/0 implicit copy constructors created
-0/0 implicit move constructors created
-0/0 implicit copy assignment operators created
-0/0 implicit move assignment operators created
-0/0 implicit destructors created
-
-*** AST File Statistics:
-
-*** PCH/ModuleFile Remappings:
-
-*** PCH/Modules Loaded:
-
-Number of memory regions: 62
-Bytes used: 248400
-Bytes allocated: 253952
-Bytes wasted: 5552 (includes alignment, etc)
-
-*** Decl Stats:
-  1896 decls total.
-    2 TranslationUnit decls, 104 each (208 bytes)
-    2 ExternCContext decls, 72 each (144 bytes)
-    802 Function decls, 168 each (134736 bytes)
-    802 ParmVar decls, 104 each (83408 bytes)
-    12 Field decls, 80 each (960 bytes)
-    3 CXXRecord decls, 144 each (432 bytes)
-    270 Typedef decls, 88 each (23760 bytes)
-    3 Import decls, 56 each (168 bytes)
-Total bytes = 243816
-
-*** Stmt/Expr Stats:
-  4816 stmts/exprs total.
-    2 UnresolvedLookupExpr, 64 each (128 bytes)
-    800 IntegerLiteral, 32 each (25600 bytes)
-    804 DeclRefExpr, 32 each (25728 bytes)
-    804 ImplicitCastExpr, 24 each (19296 bytes)
-    2 CallExpr, 24 each (48 bytes)
-    800 BinaryOperator, 32 each (25600 bytes)
-    802 ReturnStmt, 16 each (12832 bytes)
-    802 CompoundStmt, 16 each (12832 bytes)
-Total bytes = 122064
-
-STATISTICS FOR './module.modulemap':
-
-*** Preprocessor Stats:
-564 directives found:
-  553 #define.
-  0 #undef.
-  #include/#include_next/#import:
-    4 source files entered.
-    1 max include stack depth
-  2 #if/#ifndef/#ifdef.
-  0 #else/#elif/#elifdef/#elifndef.
-  2 #endif.
-  2 #pragma.
-0 #if/#ifndef#ifdef regions skipped
-0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
-0 token paste (##) operations performed, 0 on the fast path.
-
-Preprocessor Memory: 110103B total
-  BumpPtr: 57344
-  Macro Expanded Tokens: 384
-  Predefines Buffer: 32767
-  Macros: 16512
-  #pragma push_macro Info: 2056
-  Poison Reasons: 1032
-  Comment Handlers: 8
-
-*** Identifier Table Stats:
-# Identifiers:   13432
-# Empty Buckets: 19336
-Hash density (#identifiers per bucket): 0.409912
-Ave identifier length: 26.735631
-Max identifier length: 49
-
-Number of memory regions: 182
-Bytes used: 909825
-Bytes allocated: 966656
-Bytes wasted: 56831 (includes alignment, etc)
-
-*** HeaderSearch Stats:
-3 files tracked.
-  0 #import/#pragma once files.
-  4 #include/#include_next/#import.
-    0 #includes skipped due to the multi-include optimization.
-0 framework lookups.
-0 subframework lookups.
-
-*** Source Manager Stats:
-3 files mapped, 3 mem buffers mapped.
-7 local SLocEntries allocated (168 bytes of capacity), 43765B of SLoc address space used.
-0 loaded SLocEntries allocated (0 bytes of capacity), 0B of SLoc address space used.
-20795 bytes of files mapped, 2 files with line #'s computed, 0 files with macro args computed.
-FileID scans: 32 linear, 0 binary.
-
-
-*** File Manager Stats:
-5 real files found, 7 real dirs found.
-0 virtual files found, 0 virtual dirs found.
-42 dir lookups, 10 dir cache misses.
-38 file lookups, 8 file cache misses.
-
-*** Virtual File System Stats:
-15 status() calls
-8 openFileForRead() calls
-0 dir_begin() calls
-1 getRealPath() calls
-0 exists() calls
-0 isLocal() calls
-
-===-------------------------------------------------------------------------===
-                          ... Statistics Collected ...
-===-------------------------------------------------------------------------===
-
-    4 file-search    - Number of attempted #includes.
-66900 source-manager - Maximum number of bytes used by source locations (both loaded and local).
-
-
-STATISTICS:
-
-*** Semantic Analysis Stats:
-
-Number of memory regions: 0
-Bytes used: 0
-Bytes allocated: 0
-Bytes wasted: 0 (includes alignment, etc)
-
-*** Analysis Based Warnings Stats:
-0 functions analyzed (0 w/o CFGs).
-  0 CFG blocks built.
-  0 average CFG blocks per function.
-  0 max CFG blocks per function.
-0 functions analyzed for uninitialiazed variables
-  0 variables analyzed.
-  0 average variables per function.
-  0 max variables per function.
-  0 block visits.
-  0 average block visits per function.
-  0 max block visits per function.
-
-*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
-
-
-
-*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
-
-
-Total missing origins: 0
-
-****************************************
-
-*** AST Context Stats:
-  157 types total.
-    120 Builtin types, 32 each (3840 bytes)
-    2 FunctionProto types, 48 each (96 bytes)
-    6 Pointer types, 48 each (288 bytes)
-    1 Record types, 32 each (32 bytes)
-    28 Vector types, 48 each (1344 bytes)
-Total bytes = 5600
-0/0 implicit default constructors created
-0/0 implicit copy constructors created
-0/0 implicit move constructors created
-0/0 implicit copy assignment operators created
-0/0 implicit move assignment operators created
-0/0 implicit destructors created
-
-*** AST File Statistics:
-  0/12 source location entries read (0.000000%)
-  4/64 types read (6.250000%)
-  1032/1776 declarations read (58.108109%)
-  14/992 identifiers read (1.411290%)
-  0/4 macros read (0.000000%)
-  14/4814 statements read (0.290818%)
-  0/4 macros read (0.000000%)
-  0/802 lexical declcontexts read (0.000000%)
-
-*** PCH/ModuleFile Remappings:
-Global bit offset map:
-  0 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
-  1144160 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/B-CAAI8SHN1D31.pcm
-Global source location entry map:
-  2 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
-  8 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/B-CAAI8SHN1D31.pcm
-Global submodule map:
-  1 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
-  2 -> /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/B-CAAI8SHN1D31.pcm
-
-*** PCH/Modules Loaded:
-Module: /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/A-CAAI8SHN1D31.pcm
-  Base source location offset: 2147439884
-  Base identifier ID: 0
-  Number of identifiers: 496
-  Base macro ID: 0
-  Number of macros: 2
-  Base submodule ID: 0
-  Number of submodules: 1
-  Submodule ID local -> global map:
-    0 -> 0
-  Base selector ID: 0
-  Number of selectors: 0
-  Base preprocessed entity ID: 0
-  Number of preprocessed entities: 0
-  Base type index: 0
-  Number of types: 32
-  Base decl index: 0
-  Number of decls: 888
-
-Module: /Users/ayokunleamodu/Repos/llvm-project/dedup-test/cache/71GTEYCXW85I/B-CAAI8SHN1D31.pcm
-  Base source location offset: 2147396120
-  Base identifier ID: 496
-  Number of identifiers: 496
-  Base macro ID: 2
-  Number of macros: 2
-  Base submodule ID: 1
-  Number of submodules: 1
-  Submodule ID local -> global map:
-    0 -> 1
-  Base selector ID: 0
-  Number of selectors: 0
-  Base preprocessed entity ID: 0
-  Number of preprocessed entities: 0
-  Base type index: 32
-  Number of types: 32
-  Base decl index: 888
-  Number of decls: 888
-
-
-Number of memory regions: 5
-Bytes used: 19784
-Bytes allocated: 20480
-Bytes wasted: 696 (includes alignment, etc)
-
-*** Decl Stats:
-  1907 decls total.
-    2 TranslationUnit decls, 104 each (208 bytes)
-    3 ExternCContext decls, 72 each (216 bytes)
-    807 Function decls, 168 each (135576 bytes)
-    806 ParmVar decls, 104 each (83824 bytes)
-    12 Field decls, 80 each (960 bytes)
-    3 CXXRecord decls, 144 each (432 bytes)
-    270 Typedef decls, 88 each (23760 bytes)
-    4 Import decls, 56 each (224 bytes)
-Total bytes = 245200
-
-*** Stmt/Expr Stats:
-  4843 stmts/exprs total.
-    4 UnresolvedLookupExpr, 64 each (256 bytes)
-    802 IntegerLiteral, 32 each (25664 bytes)
-    810 DeclRefExpr, 32 each (25920 bytes)
-    810 ImplicitCastExpr, 24 each (19440 bytes)
-    6 CallExpr, 24 each (144 bytes)
-    801 BinaryOperator, 32 each (25632 bytes)
-    805 ReturnStmt, 16 each (12880 bytes)
-    805 CompoundStmt, 16 each (12880 bytes)
-Total bytes = 122816
-
-STATISTICS FOR 'use.cpp':
-
-*** Preprocessor Stats:
-559 directives found:
-  552 #define.
-  0 #undef.
-  #include/#include_next/#import:
-    2 source files entered.
-    0 max include stack depth
-  0 #if/#ifndef/#ifdef.
-  0 #else/#elif/#elifdef/#elifndef.
-  0 #endif.
-  2 #pragma.
-0 #if/#ifndef#ifdef regions skipped
-0/0/0 obj/fn/builtin macros expanded, 0 on the fast path.
-0 token paste (##) operations performed, 0 on the fast path.
-
-Preprocessor Memory: 106007B total
-  BumpPtr: 53248
-  Macro Expanded Tokens: 384
-  Predefines Buffer: 32767
-  Macros: 16512
-  #pragma push_macro Info: 2056
-  Poison Reasons: 1032
-  Comment Handlers: 8
-
-*** Identifier Table Stats:
-# Identifiers:   13037
-# Empty Buckets: 19731
-Hash density (#identifiers per bucket): 0.397858
-Ave identifier length: 27.158472
-Max identifier length: 49
-
-Number of memory regions: 180
-Bytes used: 888582
-Bytes allocated: 950272
-Bytes wasted: 61690 (includes alignment, etc)
-
-*** HeaderSearch Stats:
-3 files tracked.
-  0 #import/#pragma once files.
-  4 #include/#include_next/#import.
-    0 #includes skipped due to the multi-include optimization.
-0 framework lookups.
-0 subframework lookups.
-
-*** Source Manager Stats:
-2 files mapped, 2 mem buffers mapped.
-5 local SLocEntries allocated (168 bytes of capacity), 23136B of SLoc address space used.
-12 loaded SLocEntries allocated (768 bytes of capacity), 87528B of SLoc address space used.
-2 duplicate loaded file SLocEntries detected (20694B of SLoc address space reusable).
-147 bytes of files mapped, 0 files with line #'s computed, 0 files with macro args computed.
-FileID scans: 15 linear, 0 binary.
-
-
-*** File Manager Stats:
-7 real files found, 8 real dirs found.
-0 virtual files found, 0 virtual dirs found.
-46 dir lookups, 11 dir cache misses.
-47 file lookups, 10 file cache misses.
-
-*** Virtual File System Stats:
-18 status() calls
-10 openFileForRead() calls
-0 dir_begin() calls
-1 getRealPath() calls
-0 exists() calls
-0 isLocal() calls
-
-===-------------------------------------------------------------------------===
-                          ... Statistics Collected ...
-===-------------------------------------------------------------------------===
-
-     4 file-search    - Number of attempted #includes.
-110664 source-manager - Maximum number of bytes used by source locations (both loaded and local).
-

>From 26201a329de8246171027cd1ec676b5905a49d23 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Mon, 6 Jul 2026 14:37:08 -0400
Subject: [PATCH 06/15] fix SLoc de-dup miscount for files repeated within a
 module

---
 clang/lib/Serialization/ASTReader.cpp | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index d8041c06a4674..872c5bd458fac 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -4314,15 +4314,20 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       SmallVector<const FileEntry *, 64> Files;
       bool Scanned = scanLoadedSLocEntries(F, Offsets, Files);
 
-      // Classify duplicates and compute the reduced allocation request.
+      // Classify each entry against the canonical map as it stands now, before
+      // this module registers any of its own files, and store the decision per
+      // entry. The build pass reads these flags, so a file that appears
+      // multiple times in this module is classified consistently.
       auto entrySize = [&](unsigned I) -> uint64_t {
         return (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) - Offsets[I];
       };
+      SmallVector<bool, 64> IsDup(N, false);
       unsigned NumDupEntries = 0;
       uint64_t DupBytes = 0;
       if (Scanned)
         for (unsigned I = 0; I != N; ++I)
           if (SourceMgr.isLoadedFileDuplicate(Files[I])) {
+            IsDup[I] = true;
             uint64_t Size = entrySize(I);
             SourceMgr.noteDuplicateLoadedFile(Size);
             DupBytes += Size;
@@ -4368,7 +4373,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
           SourceLocation::UIntTy LowEnd =
               (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) + 2;
           const FileEntry *FE = Files[I];
-          if (FE && SourceMgr.isLoadedFileDuplicate(FE)) {
+          if (IsDup[I]) {
             // Redirect this file's locations into the module that first loaded
             // it; reserve nothing here.
             const SourceManager::LoadedFileLoc *Canon =

>From 027f60306055f085b91655bc590f7dc592f60a2c Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Mon, 6 Jul 2026 14:54:07 -0400
Subject: [PATCH 07/15] clean up comments

---
 clang/include/clang/Basic/SourceManager.h     | 19 ++++-----
 clang/include/clang/Serialization/ASTReader.h | 32 ++++++---------
 .../include/clang/Serialization/ModuleFile.h  | 30 +++++++-------
 clang/lib/Serialization/ASTReader.cpp         | 40 ++++++++-----------
 4 files changed, 56 insertions(+), 65 deletions(-)

diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h
index 75b346018bb67..4809a9dcbbd46 100644
--- a/clang/include/clang/Basic/SourceManager.h
+++ b/clang/include/clang/Basic/SourceManager.h
@@ -755,22 +755,23 @@ class SourceManager : public RefCountedBase<SourceManager> {
   static const SourceLocation::UIntTy MaxLoadedOffset =
       1ULL << (8 * sizeof(SourceLocation::UIntTy) - 1);
 
-  /// --- Source-location de-duplication (prototype, Stage 2) ---
-  /// The canonical loaded location of a file: where its SLoc entry first landed
-  /// in the global address space. Lets a later module reuse it instead of
-  /// re-allocating. Keyed by FileEntry identity (the same identity Clang uses
-  /// to dedup file content).
+  // === Source location de-duplication ===
 public:
+  /// Where a file's SLoc entry first landed in the loaded address space. A file
+  /// shared by several modules is loaded once; later modules reuse this rather
+  /// than allocating their own.
   struct LoadedFileLoc {
-    SourceLocation::UIntTy Offset = 0; ///< global raw start offset
+    SourceLocation::UIntTy Offset = 0; ///< global start offset
     int ID = 0;                        ///< global SLoc entry ID
   };
 
 private:
+  /// The first loaded location of each file, keyed by the FileEntry identity
+  /// Clang already uses to share file contents.
   llvm::DenseMap<const FileEntry *, LoadedFileLoc> CanonicalLoadedFiles;
-  /// Number of loaded file SLoc entries that duplicated an already-loaded file.
+  /// Number of loaded file entries reused from an earlier module.
   unsigned NumDuplicateLoadedFiles = 0;
-  /// SLoc address-space bytes those duplicates reused instead of allocating.
+  /// Address-space bytes reused instead of allocated, for -print-stats.
   uint64_t DuplicateLoadedBytes = 0;
 
 public:
@@ -793,7 +794,7 @@ class SourceManager : public RefCountedBase<SourceManager> {
       CanonicalLoadedFiles.try_emplace(FE, LoadedFileLoc{Offset, ID});
   }
 
-  /// Account for a de-duplicated file entry (for -print-stats reporting).
+  /// Record that a loaded file entry was reused from an earlier module.
   void noteDuplicateLoadedFile(uint64_t Size) {
     ++NumDuplicateLoadedFiles;
     DuplicateLoadedBytes += Size;
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index ec4312cf1972c..a9991091fc659 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2399,17 +2399,16 @@ class ASTReader : public ExternalPreprocessorSource,
   llvm::Expected<SourceLocation::UIntTy> readSLocOffset(ModuleFile *F,
                                                         unsigned Index);
 
-  /// Walk module F's SLoc entry records (read-only). Fills Offsets[i]
-  /// with each entry's local offset and \p Files[i] with its FileEntry (null
-  /// for non-file entries). Returns false if the records couldn't be read.
+  /// Read \p F's SLoc entry records without materializing them, filling
+  /// \p Offsets[i] with each entry's local offset and \p Files[i] with its
+  /// FileEntry (null for non-file entries). Returns false on a malformed record.
   bool scanLoadedSLocEntries(ModuleFile &F,
                              SmallVectorImpl<uint32_t> &Offsets,
                              SmallVectorImpl<const FileEntry *> &Files);
 
-  /// Map a module-local SLoc entry offset (the value stored in an entry record,
-  /// i.e. Record[0]) to its global raw start offset, applying the
-  /// de-duplication remap. Equals (SLocEntryBaseOffset + LocalOffset) when the
-  /// module is not de-duplicated.
+  /// Map a local SLoc entry offset (as stored in the entry record) to its
+  /// global start offset. This is (SLocEntryBaseOffset + LocalOffset) unless a
+  /// file in this module was reused from an earlier one.
   SourceLocation::UIntTy remapSLocEntryOffset(ModuleFile &F,
                                               uint32_t LocalOffset) const;
 
@@ -2507,15 +2506,10 @@ class ASTReader : public ExternalPreprocessorSource,
     // translated or refactor the code to make it clear that
     // TranslateSourceLocation won't be called with translated source location.
 
-    // De-duplication (Stage 2): a module's local->global offset map may be
-    // piecewise rather than a single flat shift, so look up the segment that
-    // covers this location. The seeded identity segment makes this identical to
-    // the flat shift below; redirect segments (Stage 2b) send a duplicated
-    // file's locations into the module that first loaded it.
-    //
-    // Segments are keyed by the *file-offset* part of the location, so strip
-    // the macro bit before matching and re-apply it to the result (a macro
-    // location encodes its position in the low bits with the high bit set).
+    // When a file in this module was reused from an earlier one, the map is
+    // piecewise rather than a single shift, so find the segment covering this
+    // location. A macro location keeps its offset in the low bits with the high
+    // bit set, so match on the offset part and re-apply the bit to the result.
     if (!ModuleFile.SLocRemap.empty()) {
       SourceLocation::UIntTy Raw = Loc.getRawEncoding();
       SourceLocation::UIntTy MacroBit = Raw & SourceLocation::MacroIDBit;
@@ -2550,9 +2544,9 @@ class ASTReader : public ExternalPreprocessorSource,
     assert(FID.ID >= 0 && "Reading non-local FileID.");
     if (FID.isInvalid())
       return FID;
-    // De-duplication (Stage 2b): a local FileID may map to the canonical copy
-    // in an earlier module, so consult the explicit map when present. Local
-    // FileID N corresponds to local entry index N-1.
+    // When a file was reused from an earlier module, its local FileID maps to
+    // that module's copy, so use the explicit map. Local FileID N is local
+    // entry index N-1.
     if (!F.LocalToGlobalID.empty()) {
       assert((unsigned)(FID.ID - 1) < F.LocalToGlobalID.size() &&
              "local FileID out of range");
diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h
index 6b65089cc7e6b..a985c17904aa6 100644
--- a/clang/include/clang/Serialization/ModuleFile.h
+++ b/clang/include/clang/Serialization/ModuleFile.h
@@ -346,31 +346,33 @@ class ModuleFile {
   /// AST file.
   const uint32_t *SLocEntryOffsets = nullptr;
 
-  // === Source-location de-duplication: offset remap (prototype, Stage 2) ===
+  // === Source location de-duplication ===
 
-  /// One offset-remap segment in this module's raw SourceLocation space:
-  /// a raw local location L in [LocalBegin, LocalEnd) maps to global L + Delta.
+  /// One segment of the local-to-global source location map: a local raw
+  /// location L in [LocalBegin, LocalEnd) maps to the global location L + Delta.
   struct SLocRemapSegment {
     SourceLocation::UIntTy LocalBegin;
     SourceLocation::UIntTy LocalEnd;
     int64_t Delta;
   };
 
-  /// Piecewise local->global offset map for this module, sorted by LocalBegin.
-  /// Empty => fall back to the flat shift by (SLocEntryBaseOffset - 2).
-  /// Stage 2a seeds a single identity segment equivalent to the flat shift;
-  /// Stage 2b adds redirect/shift segments for de-duplicated files.
+  /// The local-to-global source location map for this module, sorted by
+  /// LocalBegin. A module whose files are all distinct has a single segment
+  /// equivalent to the flat shift by (SLocEntryBaseOffset - 2); when a file is
+  /// reused from an earlier module, extra segments redirect that file's
+  /// locations into the earlier module. Empty for a module that is not loaded
+  /// through this path, in which case the flat shift is used directly.
   llvm::SmallVector<SLocRemapSegment, 4> SLocRemap;
 
-  /// Maps a local SLoc entry index -> its resulting global SLoc entry ID.
-  /// Kept entries map to their own newly-allocated ID; de-duplicated entries
-  /// map to the ID of the canonical copy in an earlier module. Empty => no
-  /// de-duplication for this module (global ID is SLocEntryBaseID + index).
+  /// Maps a local SLoc entry index to its global SLoc entry ID. A kept entry
+  /// maps to its own ID; a file reused from an earlier module maps to that
+  /// module's copy. Empty when no file was reused (the global ID is then
+  /// SLocEntryBaseID + index).
   std::vector<int> LocalToGlobalID;
 
-  /// For each kept global slot j (0-based, relative to SLocEntryBaseID), the
-  /// original local entry index in this module (index into SLocEntryOffsets).
-  /// Empty => no de-duplication (kept slot j == local index j).
+  /// For each kept entry, in order, its original local index (into
+  /// SLocEntryOffsets). Reused entries have no slot, so this skips them. Empty
+  /// when no file was reused (kept slot j is then local index j).
   std::vector<unsigned> KeptSLocLocalIndex;
 
   // === Identifiers ===
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 872c5bd458fac..f2e69bd42d1cb 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -1913,9 +1913,8 @@ int ASTReader::getSLocEntryID(SourceLocation::UIntTy SLocOffset) {
 
   bool Invalid = false;
 
-  // De-duplication (Stage 2b): the global table holds only this module's kept
-  // entries, so search over kept slots and translate each slot to its on-disk
-  // local entry index when reading the offset.
+  // The table holds only this module's kept entries, so search over kept slots
+  // and map each back to its on-disk local index when reading the offset.
   bool Dedup = !F->KeptSLocLocalIndex.empty();
   unsigned NumSlots =
       Dedup ? F->KeptSLocLocalIndex.size() : F->LocalNumSLocEntries;
@@ -2009,9 +2008,9 @@ bool ASTReader::ReadSLocEntry(int ID) {
   };
 
   ModuleFile *F = GlobalSLocEntryMap.find(-ID)->second;
-  // De-duplication (Stage 2b): with entries skipped, the global slot
-  // (ID - SLocEntryBaseID) is no longer the on-disk local index, so translate
-  // it through the kept-index table when present.
+  // When entries have been skipped, the global slot (ID - SLocEntryBaseID) is
+  // no longer the on-disk local index, so map it back through the kept-index
+  // table.
   unsigned LocalIndex = ID - F->SLocEntryBaseID;
   if (!F->KeptSLocLocalIndex.empty()) {
     assert(LocalIndex < F->KeptSLocLocalIndex.size() && "kept slot out of range");
@@ -2024,8 +2023,6 @@ bool ASTReader::ReadSLocEntry(int ID) {
   }
 
   BitstreamCursor &SLocEntryCursor = F->SLocEntryCursor;
-  SourceLocation::UIntTy BaseOffset = F->SLocEntryBaseOffset;
-  (void)BaseOffset;
 
   ++NumSLocEntriesRead;
   Expected<llvm::BitstreamEntry> MaybeEntry = SLocEntryCursor.advance();
@@ -4307,17 +4304,16 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       F.SLocEntryOffsetsBase = Record[2] + F.SourceManagerBlockStartOffset;
       unsigned N = F.LocalNumSLocEntries;
 
-      // De-duplication (Stage 2b): scan this module's SLoc entries *before*
-      // allocating, so we can recognize files already loaded by an earlier
-      // module and reserve less address space for them.
+      // Scan this module's SLoc entries before allocating, so files already
+      // loaded by an earlier module can be recognized and reserve no space here.
       SmallVector<uint32_t, 64> Offsets;
       SmallVector<const FileEntry *, 64> Files;
       bool Scanned = scanLoadedSLocEntries(F, Offsets, Files);
 
-      // Classify each entry against the canonical map as it stands now, before
-      // this module registers any of its own files, and store the decision per
-      // entry. The build pass reads these flags, so a file that appears
-      // multiple times in this module is classified consistently.
+      // Decide duplicates here, up front. The loop below registers each kept
+      // file as it goes, so checking against the map there would treat a file
+      // that appears more than once in this module as a duplicate of its own
+      // first occurrence.
       auto entrySize = [&](unsigned I) -> uint64_t {
         return (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) - Offsets[I];
       };
@@ -4336,8 +4332,8 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       unsigned ReducedNumEntries = N - NumDupEntries;
       SourceLocation::UIntTy ReducedSize = SLocSpaceSize - DupBytes;
 
-      // Reserve the reduced amount (equals the full amount when nothing is
-      // de-duplicated).
+      // Reserve the reduced amount (equal to the full amount when no file is
+      // reused).
       std::tie(F.SLocEntryBaseID, F.SLocEntryBaseOffset) =
           SourceMgr.AllocateLoadedSLocEntries(ReducedNumEntries, ReducedSize);
       if (!F.SLocEntryBaseID) {
@@ -4348,9 +4344,8 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       }
 
       if (NumDupEntries == 0) {
-        // No de-duplication: one identity segment (== flat shift) and a linear
-        // local->global ID mapping. Register each file as the canonical copy so
-        // later modules can reuse it.
+        // Nothing reused: a single segment equal to the flat shift, and a
+        // linear ID mapping. Record each file so later modules can reuse it.
         F.SLocRemap.push_back(
             {/*LocalBegin=*/0, /*LocalEnd=*/~SourceLocation::UIntTy(0),
              /*Delta=*/static_cast<int64_t>(F.SLocEntryBaseOffset) - 2});
@@ -4361,9 +4356,8 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
                   Files[I], F.SLocEntryBaseOffset + Offsets[I],
                   F.SLocEntryBaseID + (int)I);
       } else {
-        // De-duplication: build keep/redirect offset segments and the
-        // local->global ID map. Skipped (duplicate) entries get no slot and no
-        // address space; their references are redirected to the canonical copy.
+        // Build the offset segments and the ID map. A reused file gets no slot
+        // and no address space; its references point at the earlier copy.
         F.LocalToGlobalID.assign(N, 0);
         F.KeptSLocLocalIndex.reserve(ReducedNumEntries);
         uint64_t DupBefore = 0;

>From 7951a03cf35447e377a2971054fa5f692de05741 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Mon, 6 Jul 2026 19:31:03 -0400
Subject: [PATCH 08/15] add bench scripts

---
 dedup-test/bench/bench.sh    | 124 +++++++++++++++++++++++++++++++++++
 dedup-test/bench/timewrap.sh |  24 +++++++
 2 files changed, 148 insertions(+)
 create mode 100755 dedup-test/bench/bench.sh
 create mode 100755 dedup-test/bench/timewrap.sh

diff --git a/dedup-test/bench/bench.sh b/dedup-test/bench/bench.sh
new file mode 100755
index 0000000000000..68a9667545866
--- /dev/null
+++ b/dedup-test/bench/bench.sh
@@ -0,0 +1,124 @@
+#!/usr/bin/env bash
+# Compare baseline (pre-patch) and patched clang on a modules-enabled build,
+# reporting peak RSS, CPU time, and loaded SourceLocation usage.
+#
+# The current checkout is the patched tree; the baseline comes from a detached
+# worktree at $BASELINE_REF, so the branch and index are left untouched. Both
+# clangs are built Release/no-asserts with the same flags.
+#
+# macOS (/usr/bin/time -l via timewrap.sh).
+set -euo pipefail
+
+REPO="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
+BASELINE_REF="${BASELINE_REF:-main}"
+BASELINE_TREE="${BASELINE_TREE:-$REPO/../llvm-bench-baseline}"
+JOBS="${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
+RUNS="${RUNS:-5}"
+WORKLOAD_TARGET="${WORKLOAD_TARGET:-clangBasic}"
+OUT="${OUT:-$REPO/dedup-test/bench/out}"
+WRAP="$REPO/dedup-test/bench/timewrap.sh"
+chmod +x "$WRAP"
+mkdir -p "$OUT"
+
+echo "baseline=$BASELINE_REF  jobs=$JOBS  runs=$RUNS  workload=$WORKLOAD_TARGET"
+
+# Baseline tree.
+if [ ! -d "$BASELINE_TREE" ]; then
+  git -C "$REPO" worktree add --detach "$BASELINE_TREE" "$BASELINE_REF"
+fi
+
+# Build a Release clang from a source tree.
+build_clang() {  # <src> <tag>
+  local src="$1" tag="$2" bdir="$1/build-bench"
+  echo "building $tag clang"
+  cmake -G Ninja -S "$src/llvm" -B "$bdir" \
+    -DCMAKE_BUILD_TYPE=Release \
+    -DLLVM_ENABLE_PROJECTS=clang \
+    -DLLVM_ENABLE_ASSERTIONS=OFF \
+    -DLLVM_TARGETS_TO_BUILD=Native >/dev/null
+  # Building 'clang' also produces the clang++/clang-cl symlinks.
+  ninja -C "$bdir" clang
+}
+build_clang "$REPO"          patched
+build_clang "$BASELINE_TREE" baseline
+
+# Compile the workload with one clang, timing every invocation. The first build
+# populates the module cache; only subsequent clean rebuilds are recorded, so
+# the numbers reflect module loading rather than first-time module building.
+run_workload() {  # <clang-tree> <tag>
+  local ctree="$1" tag="$2"
+  local cxx="$ctree/build-bench/bin/clang++" cc="$ctree/build-bench/bin/clang"
+  local wdir="$OUT/work-$tag" logdir="$OUT/log-$tag"
+  rm -rf "$wdir" "$logdir"; mkdir -p "$logdir"; : > "$logdir/all.txt"
+
+  cmake -G Ninja -S "$REPO/llvm" -B "$wdir" \
+    -DCMAKE_BUILD_TYPE=Release \
+    -DLLVM_ENABLE_PROJECTS=clang \
+    -DLLVM_ENABLE_MODULES=ON \
+    -DLLVM_TARGETS_TO_BUILD=Native \
+    -DCMAKE_C_COMPILER="$cc" -DCMAKE_CXX_COMPILER="$cxx" \
+    -DCMAKE_C_COMPILER_LAUNCHER="$WRAP" \
+    -DCMAKE_CXX_COMPILER_LAUNCHER="$WRAP" >/dev/null
+
+  # Warm the module cache. Output streams to the terminal (and to warm.log);
+  # pipefail makes a compile failure here abort the run.
+  echo "[$tag] warming module cache"
+  ninja -C "$wdir" "$WORKLOAD_TARGET" 2>&1 | tee "$logdir/warm.log"
+
+  for r in $(seq 1 "$RUNS"); do
+    echo "[$tag] measured run $r/$RUNS"
+    ninja -C "$wdir" -t clean >/dev/null 2>&1
+    rm -f "$logdir"/*.m
+    BENCH_LOG_DIR="$logdir" \
+      ninja -C "$wdir" -j "$JOBS" "$WORKLOAD_TARGET" 2>&1 | tee "$logdir/run$r.log"
+    cat "$logdir"/*.m >> "$logdir/all.txt"
+  done
+}
+run_workload "$REPO"          patched
+run_workload "$BASELINE_TREE" baseline
+PATCHED_LOG="$OUT/log-patched/all.txt"
+BASELINE_LOG="$OUT/log-baseline/all.txt"
+
+# Loaded SourceLocation usage on one representative TU, for each clang.
+sloc_stats() {  # <tag>
+  local wdir="$OUT/work-$1" cmd
+  cmd=$(ninja -C "$wdir" -t commands "$WORKLOAD_TARGET" 2>/dev/null \
+        | grep -m1 -E 'clang\+\+.*\.cpp\.o') || return 0
+  eval "$cmd -Xclang -print-stats" 2>&1 \
+    | awk -v t="$1" '/loaded SLocEntries|de-duplicated/{print "  ["t"] "$0}'
+}
+
+python3 - "$BASELINE_LOG" "$PATCHED_LOG" <<'PY'
+import sys, math, statistics as st
+def load(p):
+    rss=[]; cpu=[]
+    for ln in open(p):
+        a=ln.split()
+        if len(a)==3:
+            rss.append(float(a[0])/1048576.0)      # bytes -> MB
+            cpu.append(float(a[1])+float(a[2]))     # user+sys
+    return rss,cpu
+gm=lambda xs: math.exp(sum(map(math.log,xs))/len(xs)) if xs else 0.0
+br,bc=load(sys.argv[1]); pr,pc=load(sys.argv[2])
+def row(n,b,p,f):
+    d=(p-b)/b*100 if b else 0.0
+    print(f"{n:<32}{f(b):>12}{f(p):>12}{d:>+9.1f}%")
+mb=lambda x:f"{x:,.0f}"; s=lambda x:f"{x:.3f}"
+print(f"\n{'':<32}{'baseline':>12}{'patched':>12}{'delta':>10}")
+print("-"*66)
+print(f"compiles: {len(bc)} baseline / {len(pc)} patched")
+row("peak RSS, max cc1 (MB)", max(br), max(pr), mb)
+row("peak RSS, geomean/TU (MB)", gm(br), gm(pr), mb)
+row("CPU time, total (s)", sum(bc), sum(pc), s)
+row("CPU time, geomean/TU (s)", gm(bc), gm(pc), s)
+row("CPU time, median/TU (s)", st.median(bc), st.median(pc), s)
+PY
+
+echo
+echo "loaded SourceLocation usage (one TU):"
+sloc_stats baseline || true
+sloc_stats patched  || true
+
+echo
+echo "logs: $OUT/log-{baseline,patched}/all.txt"
+echo "drop the baseline tree with: git worktree remove $BASELINE_TREE"
diff --git a/dedup-test/bench/timewrap.sh b/dedup-test/bench/timewrap.sh
new file mode 100755
index 0000000000000..02fa917f6d3b4
--- /dev/null
+++ b/dedup-test/bench/timewrap.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# Compiler launcher. Runs the compile under `time -l` and, when BENCH_LOG_DIR
+# is set, records peak RSS (bytes) and user+sys time (seconds) for it there.
+# With BENCH_LOG_DIR unset (e.g. the warm build) it just runs the compile.
+set -u
+
+t=$(mktemp)
+/usr/bin/time -l "$@" 2>"$t"
+rc=$?
+
+rss=$(awk '/maximum resident set size/{print $1}' "$t")
+read -r usr sys < <(awk '/real/&&/user/&&/sys/{print $3, $5}' "$t")
+
+# Record only measured builds, and only for real compiles (link/archive steps
+# have no RSS line).
+if [ -n "${BENCH_LOG_DIR:-}" ] && [ -n "${rss:-}" ]; then
+  printf '%s %s %s\n' "$rss" "${usr:-0}" "${sys:-0}" \
+    > "$BENCH_LOG_DIR/$(date +%s)-$$-$RANDOM.m"
+fi
+
+# Forward the compiler's own stderr; drop the trailing resource block.
+awk '/real/&&/user/&&/sys/{stop=1} !stop{print}' "$t" >&2
+rm -f "$t"
+exit $rc

>From 10bce021112b205b14e0f97b794d0b6636d9f33a Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 15 Jul 2026 13:14:02 +0000
Subject: [PATCH 09/15] cleaned up comments and variable names

---
 .gitignore                                    |   3 +
 clang/include/clang/Basic/SourceManager.h     |  37 +-----
 clang/include/clang/Serialization/ASTReader.h |  62 ++++++++--
 .../include/clang/Serialization/ModuleFile.h  |  16 +--
 clang/lib/Serialization/ASTReader.cpp         | 109 ++++++++++++------
 5 files changed, 145 insertions(+), 82 deletions(-)

diff --git a/.gitignore b/.gitignore
index a4382c9ea7390..1bceeef94e7e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,9 @@
 # Nested build directory
 /build*
 
+/ Test directory
+/dedup-test
+
 #==============================================================================#
 # Explicit files to ignore (only matches one).
 #==============================================================================#
diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h
index 4809a9dcbbd46..424de78725492 100644
--- a/clang/include/clang/Basic/SourceManager.h
+++ b/clang/include/clang/Basic/SourceManager.h
@@ -755,45 +755,18 @@ class SourceManager : public RefCountedBase<SourceManager> {
   static const SourceLocation::UIntTy MaxLoadedOffset =
       1ULL << (8 * sizeof(SourceLocation::UIntTy) - 1);
 
-  // === Source location de-duplication ===
-public:
-  /// Where a file's SLoc entry first landed in the loaded address space. A file
-  /// shared by several modules is loaded once; later modules reuse this rather
-  /// than allocating their own.
-  struct LoadedFileLoc {
-    SourceLocation::UIntTy Offset = 0; ///< global start offset
-    int ID = 0;                        ///< global SLoc entry ID
-  };
-
+  // Source location de-duplication. A file included into many modules is
+  // serialized into each of their PCMs. ASTReader keeps the first loaded copy
+  // and redirects later modules' references to it instead of allocating a
+  // duplicate SLoc range. These counters record the address space that reuse
+  // saved, for -print-stats.
 private:
-  /// The first loaded location of each file, keyed by the FileEntry identity
-  /// Clang already uses to share file contents.
-  llvm::DenseMap<const FileEntry *, LoadedFileLoc> CanonicalLoadedFiles;
   /// Number of loaded file entries reused from an earlier module.
   unsigned NumDuplicateLoadedFiles = 0;
   /// Address-space bytes reused instead of allocated, for -print-stats.
   uint64_t DuplicateLoadedBytes = 0;
 
 public:
-  /// Whether a file with this identity was already loaded into the address
-  /// space by an earlier module (i.e. its SLoc entry would be a duplicate).
-  bool isLoadedFileDuplicate(const FileEntry *FE) const {
-    return FE && CanonicalLoadedFiles.contains(FE);
-  }
-
-  /// The canonical loaded location of a previously-loaded file, or null.
-  const LoadedFileLoc *getCanonicalLoadedFile(const FileEntry *FE) const {
-    auto It = CanonicalLoadedFiles.find(FE);
-    return It == CanonicalLoadedFiles.end() ? nullptr : &It->second;
-  }
-
-  /// Record the canonical loaded location of a file the first time it loads.
-  void registerCanonicalLoadedFile(const FileEntry *FE,
-                                   SourceLocation::UIntTy Offset, int ID) {
-    if (FE)
-      CanonicalLoadedFiles.try_emplace(FE, LoadedFileLoc{Offset, ID});
-  }
-
   /// Record that a loaded file entry was reused from an earlier module.
   void noteDuplicateLoadedFile(uint64_t Size) {
     ++NumDuplicateLoadedFiles;
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index a9991091fc659..e1020e64fcca8 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2399,12 +2399,49 @@ class ASTReader : public ExternalPreprocessorSource,
   llvm::Expected<SourceLocation::UIntTy> readSLocOffset(ModuleFile *F,
                                                         unsigned Index);
 
+  /// Identity of an input file, taken from serialized metadata without touching
+  /// the filesystem. It uses the stored name, size, and modification time that
+  /// Clang's own input-file staleness check relies on. An empty Name marks a
+  /// non-file entry (a buffer or expansion), which never deduplicates.
+  struct SLocFileIdentity {
+    StringRef Name;
+    off_t Size = 0;
+    time_t Time = 0;
+  };
+
+  /// Where a file's SLoc entry first landed in the loaded address space, kept
+  /// with the identity fields used to confirm a later module's same-named entry
+  /// really is the same file.
+  struct PrimaryLoadedFileLoc {
+    SourceLocation::UIntTy Offset = 0; ///< global start offset
+    int ID = 0;                        ///< global SLoc entry ID
+    off_t Size = 0;
+    time_t Time = 0;
+  };
+
+  /// Files already loaded into the loaded SLoc address space, keyed by stored
+  /// name. Lets a later module reuse an earlier module's copy instead of
+  /// allocating a duplicate range. The key is serialized metadata, so no input
+  /// file is resolved or stat'd at load time.
+  llvm::StringMap<PrimaryLoadedFileLoc> PrimaryLoadedFiles;
+
+  /// The location of a previously-loaded file matching \p Id (same name, size,
+  /// and time), or null if none has been loaded.
+  const PrimaryLoadedFileLoc *
+  getPrimaryLoadedFile(const SLocFileIdentity &Id) const;
+
+  /// Record the location of a file the first time it loads. \p Id must name a
+  /// file entry (non-empty Name). Later duplicates are ignored.
+  void registerPrimaryLoadedFile(const SLocFileIdentity &Id,
+                                 SourceLocation::UIntTy Offset, int ID);
+
   /// Read \p F's SLoc entry records without materializing them, filling
   /// \p Offsets[i] with each entry's local offset and \p Files[i] with its
-  /// FileEntry (null for non-file entries). Returns false on a malformed record.
-  bool scanLoadedSLocEntries(ModuleFile &F,
-                             SmallVectorImpl<uint32_t> &Offsets,
-                             SmallVectorImpl<const FileEntry *> &Files);
+  /// file identity (empty Name for non-file entries). File identity comes from
+  /// serialized metadata only, with no input file resolved on disk. Returns
+  /// false on a malformed record.
+  bool scanLoadedSLocEntries(ModuleFile &F, SmallVectorImpl<uint32_t> &Offsets,
+                             SmallVectorImpl<SLocFileIdentity> &Files);
 
   /// Map a local SLoc entry offset (as stored in the entry record) to its
   /// global start offset. This is (SLocEntryBaseOffset + LocalOffset) unless a
@@ -2514,13 +2551,24 @@ class ASTReader : public ExternalPreprocessorSource,
       SourceLocation::UIntTy Raw = Loc.getRawEncoding();
       SourceLocation::UIntTy MacroBit = Raw & SourceLocation::MacroIDBit;
       SourceLocation::UIntTy Low = Raw & ~SourceLocation::MacroIDBit;
-      for (const auto &Seg : ModuleFile.SLocRemap)
-        if (Low >= Seg.LocalBegin && Low < Seg.LocalEnd)
+      // The list is sorted by LocalBegin and its segments are contiguous, so
+      // the covering segment is the last one whose LocalBegin is <= Low.
+      auto It = llvm::upper_bound(
+          ModuleFile.SLocRemap, Low,
+          [](SourceLocation::UIntTy V,
+             const serialization::ModuleFile::SLocRemapSegment &S) {
+            return V < S.LocalBegin;
+          });
+      if (It != ModuleFile.SLocRemap.begin()) {
+        const auto &Seg = *std::prev(It);
+        if (Low < Seg.LocalEnd)
           return SourceLocation::getFromRawEncoding(
               (static_cast<SourceLocation::UIntTy>(static_cast<int64_t>(Low) +
                                                    Seg.Delta)) |
               MacroBit);
-      // No segment matched (shouldn't happen): fall through to the flat shift.
+      }
+      // No segment matched. This shouldn't happen, so fall through to the
+      // flat shift below.
     }
 
     return Loc.getLocWithOffset(ModuleFile.SLocEntryBaseOffset - 2);
diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h
index a985c17904aa6..76cefc2280936 100644
--- a/clang/include/clang/Serialization/ModuleFile.h
+++ b/clang/include/clang/Serialization/ModuleFile.h
@@ -348,8 +348,8 @@ class ModuleFile {
 
   // === Source location de-duplication ===
 
-  /// One segment of the local-to-global source location map: a local raw
-  /// location L in [LocalBegin, LocalEnd) maps to the global location L + Delta.
+  /// One segment of the local-to-global source location map. A local raw
+  /// location L in [LocalBegin, LocalEnd) maps to global location L + Delta.
   struct SLocRemapSegment {
     SourceLocation::UIntTy LocalBegin;
     SourceLocation::UIntTy LocalEnd;
@@ -357,15 +357,15 @@ class ModuleFile {
   };
 
   /// The local-to-global source location map for this module, sorted by
-  /// LocalBegin. A module whose files are all distinct has a single segment
-  /// equivalent to the flat shift by (SLocEntryBaseOffset - 2); when a file is
-  /// reused from an earlier module, extra segments redirect that file's
-  /// locations into the earlier module. Empty for a module that is not loaded
-  /// through this path, in which case the flat shift is used directly.
+  /// LocalBegin. When a module's files are all distinct it holds a single
+  /// segment equivalent to the flat shift by (SLocEntryBaseOffset - 2). When a
+  /// file is reused from an earlier module, extra segments redirect that file's
+  /// locations into the earlier module. Empty when this path did not run, in
+  /// which case the flat shift is used directly.
   llvm::SmallVector<SLocRemapSegment, 4> SLocRemap;
 
   /// Maps a local SLoc entry index to its global SLoc entry ID. A kept entry
-  /// maps to its own ID; a file reused from an earlier module maps to that
+  /// maps to its own ID. A file reused from an earlier module maps to that
   /// module's copy. Empty when no file was reused (the global ID is then
   /// SLocEntryBaseID + index).
   std::vector<int> LocalToGlobalID;
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index f2e69bd42d1cb..5df770034e7db 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -1815,18 +1815,40 @@ llvm::Error ASTReader::ReadSourceManagerBlock(ModuleFile &F) {
   }
 }
 
+const ASTReader::PrimaryLoadedFileLoc *
+ASTReader::getPrimaryLoadedFile(const SLocFileIdentity &Id) const {
+  assert(!Id.Name.empty() && "querying a non-file entry");
+  auto It = PrimaryLoadedFiles.find(Id.Name);
+  if (It == PrimaryLoadedFiles.end())
+    return nullptr;
+  const PrimaryLoadedFileLoc &Primary = It->second;
+  // A matching name but different size or time is a different file, e.g. two
+  // modules built against different versions of the same path. Don't merge.
+  if (Primary.Size != Id.Size || Primary.Time != Id.Time)
+    return nullptr;
+  return &Primary;
+}
+
+void ASTReader::registerPrimaryLoadedFile(const SLocFileIdentity &Id,
+                                          SourceLocation::UIntTy Offset,
+                                          int ID) {
+  assert(!Id.Name.empty() && "registering a non-file entry");
+  PrimaryLoadedFiles.try_emplace(
+      Id.Name, PrimaryLoadedFileLoc{Offset, ID, Id.Size, Id.Time});
+}
+
 bool ASTReader::scanLoadedSLocEntries(
     ModuleFile &F, SmallVectorImpl<uint32_t> &Offsets,
-    SmallVectorImpl<const FileEntry *> &Files) {
+    SmallVectorImpl<SLocFileIdentity> &Files) {
   unsigned N = F.LocalNumSLocEntries;
   Offsets.assign(N, 0);
-  Files.assign(N, nullptr);
+  Files.assign(N, SLocFileIdentity{});
 
   BitstreamCursor &Cursor = F.SLocEntryCursor;
   SavedStreamPosition SavedPosition(Cursor);
   for (unsigned I = 0; I != N; ++I) {
-    if (llvm::Error Err = Cursor.JumpToBit(F.SLocEntryOffsetsBase +
-                                           F.SLocEntryOffsets[I])) {
+    if (llvm::Error Err =
+            Cursor.JumpToBit(F.SLocEntryOffsetsBase + F.SLocEntryOffsets[I])) {
       consumeError(std::move(Err));
       return false;
     }
@@ -1839,16 +1861,22 @@ bool ASTReader::scanLoadedSLocEntries(
       return false;
 
     RecordData Record;
-    StringRef Blob;
-    Expected<unsigned> Code = Cursor.readRecord(Entry->ID, Record, &Blob);
+    Expected<unsigned> Code = Cursor.readRecord(Entry->ID, Record);
     if (!Code) {
       consumeError(Code.takeError());
       return false;
     }
     Offsets[I] = (uint32_t)Record[0];
-    if (Code.get() == SM_SLOC_FILE_ENTRY)
-      if (OptionalFileEntryRef File = getInputFile(F, Record[4]).getFile())
-        Files[I] = &File->getFileEntry();
+    if (Code.get() == SM_SLOC_FILE_ENTRY) {
+      // Identify the file from serialized metadata only. Resolving it on disk
+      // here (getInputFile) would stat and open every input file of every
+      // module at load, which is prohibitive. The stored name, size, and time
+      // are the identity Clang's own staleness check already uses.
+      InputFileInfo IFI = getInputFileInfo(F, Record[4]);
+      if (IFI.isValid())
+        Files[I] = SLocFileIdentity{IFI.UnresolvedImportedFilename,
+                                    IFI.StoredSize, IFI.StoredTime};
+    }
   }
   return true;
 }
@@ -1859,12 +1887,22 @@ ASTReader::remapSLocEntryOffset(ModuleFile &F, uint32_t LocalOffset) const {
   // are reserved). Find the segment covering it and apply that segment's delta.
   if (!F.SLocRemap.empty()) {
     SourceLocation::UIntTy Low = LocalOffset + 2;
-    for (const auto &Seg : F.SLocRemap)
-      if (Low >= Seg.LocalBegin && Low < Seg.LocalEnd)
+    // The list is sorted by LocalBegin and its segments are contiguous, so the
+    // covering segment is the last one whose LocalBegin is <= Low.
+    auto It = llvm::upper_bound(
+        F.SLocRemap, Low,
+        [](SourceLocation::UIntTy V,
+           const serialization::ModuleFile::SLocRemapSegment &S) {
+          return V < S.LocalBegin;
+        });
+    if (It != F.SLocRemap.begin()) {
+      const auto &Seg = *std::prev(It);
+      if (Low < Seg.LocalEnd)
         return static_cast<SourceLocation::UIntTy>(static_cast<int64_t>(Low) +
                                                    Seg.Delta);
+    }
   }
-  // No remap (or no segment matched): original flat shift.
+  // No remap, or no segment matched. Use the original flat shift.
   return F.SLocEntryBaseOffset + LocalOffset;
 }
 
@@ -2013,7 +2051,8 @@ bool ASTReader::ReadSLocEntry(int ID) {
   // table.
   unsigned LocalIndex = ID - F->SLocEntryBaseID;
   if (!F->KeptSLocLocalIndex.empty()) {
-    assert(LocalIndex < F->KeptSLocLocalIndex.size() && "kept slot out of range");
+    assert(LocalIndex < F->KeptSLocLocalIndex.size() &&
+           "kept slot out of range");
     LocalIndex = F->KeptSLocLocalIndex[LocalIndex];
   }
   if (llvm::Error Err = F->SLocEntryCursor.JumpToBit(
@@ -2113,9 +2152,9 @@ bool ASTReader::ReadSLocEntry(int ID) {
     auto Buffer = ReadBuffer(SLocEntryCursor, Name);
     if (!Buffer)
       return true;
-    FileID FID = SourceMgr.createFileID(std::move(Buffer), FileCharacter, ID,
-                                        remapSLocEntryOffset(*F, Offset),
-                                        IncludeLoc);
+    FileID FID =
+        SourceMgr.createFileID(std::move(Buffer), FileCharacter, ID,
+                               remapSLocEntryOffset(*F, Offset), IncludeLoc);
     if (Record[3]) {
       auto &FileInfo = SourceMgr.getSLocEntry(FID).getFile();
       FileInfo.setHasLineDirectives();
@@ -4305,9 +4344,10 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       unsigned N = F.LocalNumSLocEntries;
 
       // Scan this module's SLoc entries before allocating, so files already
-      // loaded by an earlier module can be recognized and reserve no space here.
+      // loaded by an earlier module can be recognized and reserve no space
+      // here.
       SmallVector<uint32_t, 64> Offsets;
-      SmallVector<const FileEntry *, 64> Files;
+      SmallVector<SLocFileIdentity, 64> Files;
       bool Scanned = scanLoadedSLocEntries(F, Offsets, Files);
 
       // Decide duplicates here, up front. The loop below registers each kept
@@ -4322,7 +4362,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       uint64_t DupBytes = 0;
       if (Scanned)
         for (unsigned I = 0; I != N; ++I)
-          if (SourceMgr.isLoadedFileDuplicate(Files[I])) {
+          if (!Files[I].Name.empty() && getPrimaryLoadedFile(Files[I])) {
             IsDup[I] = true;
             uint64_t Size = entrySize(I);
             SourceMgr.noteDuplicateLoadedFile(Size);
@@ -4344,20 +4384,20 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       }
 
       if (NumDupEntries == 0) {
-        // Nothing reused: a single segment equal to the flat shift, and a
+        // Nothing reused. A single segment equal to the flat shift, plus a
         // linear ID mapping. Record each file so later modules can reuse it.
         F.SLocRemap.push_back(
             {/*LocalBegin=*/0, /*LocalEnd=*/~SourceLocation::UIntTy(0),
              /*Delta=*/static_cast<int64_t>(F.SLocEntryBaseOffset) - 2});
         if (Scanned)
           for (unsigned I = 0; I != N; ++I)
-            if (Files[I])
-              SourceMgr.registerCanonicalLoadedFile(
-                  Files[I], F.SLocEntryBaseOffset + Offsets[I],
-                  F.SLocEntryBaseID + (int)I);
+            if (!Files[I].Name.empty())
+              registerPrimaryLoadedFile(Files[I],
+                                        F.SLocEntryBaseOffset + Offsets[I],
+                                        F.SLocEntryBaseID + (int)I);
       } else {
         // Build the offset segments and the ID map. A reused file gets no slot
-        // and no address space; its references point at the earlier copy.
+        // and no address space, and its references point at the earlier copy.
         F.LocalToGlobalID.assign(N, 0);
         F.KeptSLocLocalIndex.reserve(ReducedNumEntries);
         uint64_t DupBefore = 0;
@@ -4366,20 +4406,19 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
           SourceLocation::UIntTy LowStart = Offsets[I] + 2;
           SourceLocation::UIntTy LowEnd =
               (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) + 2;
-          const FileEntry *FE = Files[I];
+          const SLocFileIdentity &Id = Files[I];
           if (IsDup[I]) {
             // Redirect this file's locations into the module that first loaded
-            // it; reserve nothing here.
-            const SourceManager::LoadedFileLoc *Canon =
-                SourceMgr.getCanonicalLoadedFile(FE);
+            // it and reserve nothing here.
+            const PrimaryLoadedFileLoc *Primary = getPrimaryLoadedFile(Id);
             F.SLocRemap.push_back({LowStart, LowEnd,
-                                   static_cast<int64_t>(Canon->Offset) -
+                                   static_cast<int64_t>(Primary->Offset) -
                                        static_cast<int64_t>(LowStart)});
-            F.LocalToGlobalID[I] = Canon->ID;
+            F.LocalToGlobalID[I] = Primary->ID;
             DupBefore += LowEnd - LowStart;
           } else {
-            // Keep: lands in this module's block, shifted down to close gaps
-            // left by skipped duplicates before it.
+            // Kept entry. It lands in this module's block, shifted down to
+            // close gaps left by skipped duplicates before it.
             int GlobalID = F.SLocEntryBaseID + (int)KeptCount;
             SourceLocation::UIntTy GlobalStart =
                 static_cast<SourceLocation::UIntTy>(F.SLocEntryBaseOffset +
@@ -4389,8 +4428,8 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
                                        static_cast<int64_t>(LowStart)});
             F.LocalToGlobalID[I] = GlobalID;
             F.KeptSLocLocalIndex.push_back(I);
-            if (FE)
-              SourceMgr.registerCanonicalLoadedFile(FE, GlobalStart, GlobalID);
+            if (!Id.Name.empty())
+              registerPrimaryLoadedFile(Id, GlobalStart, GlobalID);
             ++KeptCount;
           }
         }

>From cea3ba0e75e4c7fd3034cd2a32773537c6efd71f Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 15 Jul 2026 13:16:09 +0000
Subject: [PATCH 10/15] Stop tracking dedup-test/

---
 dedup-test/a.h               |   5 -
 dedup-test/b.h               |   5 -
 dedup-test/bench/bench.sh    | 124 -----------
 dedup-test/bench/timewrap.sh |  24 ---
 dedup-test/module.modulemap  |   2 -
 dedup-test/shared.h          | 403 -----------------------------------
 dedup-test/use.cpp           |   3 -
 dedup-test/use1.cpp          |   2 -
 dedup-test/use_err.cpp       |  14 --
 dedup-test/use_err_rev.cpp   |  10 -
 10 files changed, 592 deletions(-)
 delete mode 100644 dedup-test/a.h
 delete mode 100644 dedup-test/b.h
 delete mode 100755 dedup-test/bench/bench.sh
 delete mode 100755 dedup-test/bench/timewrap.sh
 delete mode 100644 dedup-test/module.modulemap
 delete mode 100644 dedup-test/shared.h
 delete mode 100644 dedup-test/use.cpp
 delete mode 100644 dedup-test/use1.cpp
 delete mode 100644 dedup-test/use_err.cpp
 delete mode 100644 dedup-test/use_err_rev.cpp

diff --git a/dedup-test/a.h b/dedup-test/a.h
deleted file mode 100644
index 1fe7fc27db549..0000000000000
--- a/dedup-test/a.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef A_H
-#define A_H
-#include "shared.h"
-inline int a_entry(int x) { return shared_fn_0(x); }
-#endif
diff --git a/dedup-test/b.h b/dedup-test/b.h
deleted file mode 100644
index 85839261fc602..0000000000000
--- a/dedup-test/b.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef B_H
-#define B_H
-#include "shared.h"
-inline int b_entry(int x) { return shared_fn_1(x); }
-#endif
diff --git a/dedup-test/bench/bench.sh b/dedup-test/bench/bench.sh
deleted file mode 100755
index 68a9667545866..0000000000000
--- a/dedup-test/bench/bench.sh
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env bash
-# Compare baseline (pre-patch) and patched clang on a modules-enabled build,
-# reporting peak RSS, CPU time, and loaded SourceLocation usage.
-#
-# The current checkout is the patched tree; the baseline comes from a detached
-# worktree at $BASELINE_REF, so the branch and index are left untouched. Both
-# clangs are built Release/no-asserts with the same flags.
-#
-# macOS (/usr/bin/time -l via timewrap.sh).
-set -euo pipefail
-
-REPO="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
-BASELINE_REF="${BASELINE_REF:-main}"
-BASELINE_TREE="${BASELINE_TREE:-$REPO/../llvm-bench-baseline}"
-JOBS="${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
-RUNS="${RUNS:-5}"
-WORKLOAD_TARGET="${WORKLOAD_TARGET:-clangBasic}"
-OUT="${OUT:-$REPO/dedup-test/bench/out}"
-WRAP="$REPO/dedup-test/bench/timewrap.sh"
-chmod +x "$WRAP"
-mkdir -p "$OUT"
-
-echo "baseline=$BASELINE_REF  jobs=$JOBS  runs=$RUNS  workload=$WORKLOAD_TARGET"
-
-# Baseline tree.
-if [ ! -d "$BASELINE_TREE" ]; then
-  git -C "$REPO" worktree add --detach "$BASELINE_TREE" "$BASELINE_REF"
-fi
-
-# Build a Release clang from a source tree.
-build_clang() {  # <src> <tag>
-  local src="$1" tag="$2" bdir="$1/build-bench"
-  echo "building $tag clang"
-  cmake -G Ninja -S "$src/llvm" -B "$bdir" \
-    -DCMAKE_BUILD_TYPE=Release \
-    -DLLVM_ENABLE_PROJECTS=clang \
-    -DLLVM_ENABLE_ASSERTIONS=OFF \
-    -DLLVM_TARGETS_TO_BUILD=Native >/dev/null
-  # Building 'clang' also produces the clang++/clang-cl symlinks.
-  ninja -C "$bdir" clang
-}
-build_clang "$REPO"          patched
-build_clang "$BASELINE_TREE" baseline
-
-# Compile the workload with one clang, timing every invocation. The first build
-# populates the module cache; only subsequent clean rebuilds are recorded, so
-# the numbers reflect module loading rather than first-time module building.
-run_workload() {  # <clang-tree> <tag>
-  local ctree="$1" tag="$2"
-  local cxx="$ctree/build-bench/bin/clang++" cc="$ctree/build-bench/bin/clang"
-  local wdir="$OUT/work-$tag" logdir="$OUT/log-$tag"
-  rm -rf "$wdir" "$logdir"; mkdir -p "$logdir"; : > "$logdir/all.txt"
-
-  cmake -G Ninja -S "$REPO/llvm" -B "$wdir" \
-    -DCMAKE_BUILD_TYPE=Release \
-    -DLLVM_ENABLE_PROJECTS=clang \
-    -DLLVM_ENABLE_MODULES=ON \
-    -DLLVM_TARGETS_TO_BUILD=Native \
-    -DCMAKE_C_COMPILER="$cc" -DCMAKE_CXX_COMPILER="$cxx" \
-    -DCMAKE_C_COMPILER_LAUNCHER="$WRAP" \
-    -DCMAKE_CXX_COMPILER_LAUNCHER="$WRAP" >/dev/null
-
-  # Warm the module cache. Output streams to the terminal (and to warm.log);
-  # pipefail makes a compile failure here abort the run.
-  echo "[$tag] warming module cache"
-  ninja -C "$wdir" "$WORKLOAD_TARGET" 2>&1 | tee "$logdir/warm.log"
-
-  for r in $(seq 1 "$RUNS"); do
-    echo "[$tag] measured run $r/$RUNS"
-    ninja -C "$wdir" -t clean >/dev/null 2>&1
-    rm -f "$logdir"/*.m
-    BENCH_LOG_DIR="$logdir" \
-      ninja -C "$wdir" -j "$JOBS" "$WORKLOAD_TARGET" 2>&1 | tee "$logdir/run$r.log"
-    cat "$logdir"/*.m >> "$logdir/all.txt"
-  done
-}
-run_workload "$REPO"          patched
-run_workload "$BASELINE_TREE" baseline
-PATCHED_LOG="$OUT/log-patched/all.txt"
-BASELINE_LOG="$OUT/log-baseline/all.txt"
-
-# Loaded SourceLocation usage on one representative TU, for each clang.
-sloc_stats() {  # <tag>
-  local wdir="$OUT/work-$1" cmd
-  cmd=$(ninja -C "$wdir" -t commands "$WORKLOAD_TARGET" 2>/dev/null \
-        | grep -m1 -E 'clang\+\+.*\.cpp\.o') || return 0
-  eval "$cmd -Xclang -print-stats" 2>&1 \
-    | awk -v t="$1" '/loaded SLocEntries|de-duplicated/{print "  ["t"] "$0}'
-}
-
-python3 - "$BASELINE_LOG" "$PATCHED_LOG" <<'PY'
-import sys, math, statistics as st
-def load(p):
-    rss=[]; cpu=[]
-    for ln in open(p):
-        a=ln.split()
-        if len(a)==3:
-            rss.append(float(a[0])/1048576.0)      # bytes -> MB
-            cpu.append(float(a[1])+float(a[2]))     # user+sys
-    return rss,cpu
-gm=lambda xs: math.exp(sum(map(math.log,xs))/len(xs)) if xs else 0.0
-br,bc=load(sys.argv[1]); pr,pc=load(sys.argv[2])
-def row(n,b,p,f):
-    d=(p-b)/b*100 if b else 0.0
-    print(f"{n:<32}{f(b):>12}{f(p):>12}{d:>+9.1f}%")
-mb=lambda x:f"{x:,.0f}"; s=lambda x:f"{x:.3f}"
-print(f"\n{'':<32}{'baseline':>12}{'patched':>12}{'delta':>10}")
-print("-"*66)
-print(f"compiles: {len(bc)} baseline / {len(pc)} patched")
-row("peak RSS, max cc1 (MB)", max(br), max(pr), mb)
-row("peak RSS, geomean/TU (MB)", gm(br), gm(pr), mb)
-row("CPU time, total (s)", sum(bc), sum(pc), s)
-row("CPU time, geomean/TU (s)", gm(bc), gm(pc), s)
-row("CPU time, median/TU (s)", st.median(bc), st.median(pc), s)
-PY
-
-echo
-echo "loaded SourceLocation usage (one TU):"
-sloc_stats baseline || true
-sloc_stats patched  || true
-
-echo
-echo "logs: $OUT/log-{baseline,patched}/all.txt"
-echo "drop the baseline tree with: git worktree remove $BASELINE_TREE"
diff --git a/dedup-test/bench/timewrap.sh b/dedup-test/bench/timewrap.sh
deleted file mode 100755
index 02fa917f6d3b4..0000000000000
--- a/dedup-test/bench/timewrap.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-# Compiler launcher. Runs the compile under `time -l` and, when BENCH_LOG_DIR
-# is set, records peak RSS (bytes) and user+sys time (seconds) for it there.
-# With BENCH_LOG_DIR unset (e.g. the warm build) it just runs the compile.
-set -u
-
-t=$(mktemp)
-/usr/bin/time -l "$@" 2>"$t"
-rc=$?
-
-rss=$(awk '/maximum resident set size/{print $1}' "$t")
-read -r usr sys < <(awk '/real/&&/user/&&/sys/{print $3, $5}' "$t")
-
-# Record only measured builds, and only for real compiles (link/archive steps
-# have no RSS line).
-if [ -n "${BENCH_LOG_DIR:-}" ] && [ -n "${rss:-}" ]; then
-  printf '%s %s %s\n' "$rss" "${usr:-0}" "${sys:-0}" \
-    > "$BENCH_LOG_DIR/$(date +%s)-$$-$RANDOM.m"
-fi
-
-# Forward the compiler's own stderr; drop the trailing resource block.
-awk '/real/&&/user/&&/sys/{stop=1} !stop{print}' "$t" >&2
-rm -f "$t"
-exit $rc
diff --git a/dedup-test/module.modulemap b/dedup-test/module.modulemap
deleted file mode 100644
index 2da1c03a22211..0000000000000
--- a/dedup-test/module.modulemap
+++ /dev/null
@@ -1,2 +0,0 @@
-module A { header "a.h" export * }
-module B { header "b.h" export * }
diff --git a/dedup-test/shared.h b/dedup-test/shared.h
deleted file mode 100644
index 3ac57a45355f9..0000000000000
--- a/dedup-test/shared.h
+++ /dev/null
@@ -1,403 +0,0 @@
-#ifndef SHARED_H
-#define SHARED_H
-inline int shared_fn_0(int x) { return x + 0; }
-inline int shared_fn_1(int x) { return x + 1; }
-inline int shared_fn_2(int x) { return x + 2; }
-inline int shared_fn_3(int x) { return x + 3; }
-inline int shared_fn_4(int x) { return x + 4; }
-inline int shared_fn_5(int x) { return x + 5; }
-inline int shared_fn_6(int x) { return x + 6; }
-inline int shared_fn_7(int x) { return x + 7; }
-inline int shared_fn_8(int x) { return x + 8; }
-inline int shared_fn_9(int x) { return x + 9; }
-inline int shared_fn_10(int x) { return x + 10; }
-inline int shared_fn_11(int x) { return x + 11; }
-inline int shared_fn_12(int x) { return x + 12; }
-inline int shared_fn_13(int x) { return x + 13; }
-inline int shared_fn_14(int x) { return x + 14; }
-inline int shared_fn_15(int x) { return x + 15; }
-inline int shared_fn_16(int x) { return x + 16; }
-inline int shared_fn_17(int x) { return x + 17; }
-inline int shared_fn_18(int x) { return x + 18; }
-inline int shared_fn_19(int x) { return x + 19; }
-inline int shared_fn_20(int x) { return x + 20; }
-inline int shared_fn_21(int x) { return x + 21; }
-inline int shared_fn_22(int x) { return x + 22; }
-inline int shared_fn_23(int x) { return x + 23; }
-inline int shared_fn_24(int x) { return x + 24; }
-inline int shared_fn_25(int x) { return x + 25; }
-inline int shared_fn_26(int x) { return x + 26; }
-inline int shared_fn_27(int x) { return x + 27; }
-inline int shared_fn_28(int x) { return x + 28; }
-inline int shared_fn_29(int x) { return x + 29; }
-inline int shared_fn_30(int x) { return x + 30; }
-inline int shared_fn_31(int x) { return x + 31; }
-inline int shared_fn_32(int x) { return x + 32; }
-inline int shared_fn_33(int x) { return x + 33; }
-inline int shared_fn_34(int x) { return x + 34; }
-inline int shared_fn_35(int x) { return x + 35; }
-inline int shared_fn_36(int x) { return x + 36; }
-inline int shared_fn_37(int x) { return x + 37; }
-inline int shared_fn_38(int x) { return x + 38; }
-inline int shared_fn_39(int x) { return x + 39; }
-inline int shared_fn_40(int x) { return x + 40; }
-inline int shared_fn_41(int x) { return x + 41; }
-inline int shared_fn_42(int x) { return x + 42; }
-inline int shared_fn_43(int x) { return x + 43; }
-inline int shared_fn_44(int x) { return x + 44; }
-inline int shared_fn_45(int x) { return x + 45; }
-inline int shared_fn_46(int x) { return x + 46; }
-inline int shared_fn_47(int x) { return x + 47; }
-inline int shared_fn_48(int x) { return x + 48; }
-inline int shared_fn_49(int x) { return x + 49; }
-inline int shared_fn_50(int x) { return x + 50; }
-inline int shared_fn_51(int x) { return x + 51; }
-inline int shared_fn_52(int x) { return x + 52; }
-inline int shared_fn_53(int x) { return x + 53; }
-inline int shared_fn_54(int x) { return x + 54; }
-inline int shared_fn_55(int x) { return x + 55; }
-inline int shared_fn_56(int x) { return x + 56; }
-inline int shared_fn_57(int x) { return x + 57; }
-inline int shared_fn_58(int x) { return x + 58; }
-inline int shared_fn_59(int x) { return x + 59; }
-inline int shared_fn_60(int x) { return x + 60; }
-inline int shared_fn_61(int x) { return x + 61; }
-inline int shared_fn_62(int x) { return x + 62; }
-inline int shared_fn_63(int x) { return x + 63; }
-inline int shared_fn_64(int x) { return x + 64; }
-inline int shared_fn_65(int x) { return x + 65; }
-inline int shared_fn_66(int x) { return x + 66; }
-inline int shared_fn_67(int x) { return x + 67; }
-inline int shared_fn_68(int x) { return x + 68; }
-inline int shared_fn_69(int x) { return x + 69; }
-inline int shared_fn_70(int x) { return x + 70; }
-inline int shared_fn_71(int x) { return x + 71; }
-inline int shared_fn_72(int x) { return x + 72; }
-inline int shared_fn_73(int x) { return x + 73; }
-inline int shared_fn_74(int x) { return x + 74; }
-inline int shared_fn_75(int x) { return x + 75; }
-inline int shared_fn_76(int x) { return x + 76; }
-inline int shared_fn_77(int x) { return x + 77; }
-inline int shared_fn_78(int x) { return x + 78; }
-inline int shared_fn_79(int x) { return x + 79; }
-inline int shared_fn_80(int x) { return x + 80; }
-inline int shared_fn_81(int x) { return x + 81; }
-inline int shared_fn_82(int x) { return x + 82; }
-inline int shared_fn_83(int x) { return x + 83; }
-inline int shared_fn_84(int x) { return x + 84; }
-inline int shared_fn_85(int x) { return x + 85; }
-inline int shared_fn_86(int x) { return x + 86; }
-inline int shared_fn_87(int x) { return x + 87; }
-inline int shared_fn_88(int x) { return x + 88; }
-inline int shared_fn_89(int x) { return x + 89; }
-inline int shared_fn_90(int x) { return x + 90; }
-inline int shared_fn_91(int x) { return x + 91; }
-inline int shared_fn_92(int x) { return x + 92; }
-inline int shared_fn_93(int x) { return x + 93; }
-inline int shared_fn_94(int x) { return x + 94; }
-inline int shared_fn_95(int x) { return x + 95; }
-inline int shared_fn_96(int x) { return x + 96; }
-inline int shared_fn_97(int x) { return x + 97; }
-inline int shared_fn_98(int x) { return x + 98; }
-inline int shared_fn_99(int x) { return x + 99; }
-inline int shared_fn_100(int x) { return x + 100; }
-inline int shared_fn_101(int x) { return x + 101; }
-inline int shared_fn_102(int x) { return x + 102; }
-inline int shared_fn_103(int x) { return x + 103; }
-inline int shared_fn_104(int x) { return x + 104; }
-inline int shared_fn_105(int x) { return x + 105; }
-inline int shared_fn_106(int x) { return x + 106; }
-inline int shared_fn_107(int x) { return x + 107; }
-inline int shared_fn_108(int x) { return x + 108; }
-inline int shared_fn_109(int x) { return x + 109; }
-inline int shared_fn_110(int x) { return x + 110; }
-inline int shared_fn_111(int x) { return x + 111; }
-inline int shared_fn_112(int x) { return x + 112; }
-inline int shared_fn_113(int x) { return x + 113; }
-inline int shared_fn_114(int x) { return x + 114; }
-inline int shared_fn_115(int x) { return x + 115; }
-inline int shared_fn_116(int x) { return x + 116; }
-inline int shared_fn_117(int x) { return x + 117; }
-inline int shared_fn_118(int x) { return x + 118; }
-inline int shared_fn_119(int x) { return x + 119; }
-inline int shared_fn_120(int x) { return x + 120; }
-inline int shared_fn_121(int x) { return x + 121; }
-inline int shared_fn_122(int x) { return x + 122; }
-inline int shared_fn_123(int x) { return x + 123; }
-inline int shared_fn_124(int x) { return x + 124; }
-inline int shared_fn_125(int x) { return x + 125; }
-inline int shared_fn_126(int x) { return x + 126; }
-inline int shared_fn_127(int x) { return x + 127; }
-inline int shared_fn_128(int x) { return x + 128; }
-inline int shared_fn_129(int x) { return x + 129; }
-inline int shared_fn_130(int x) { return x + 130; }
-inline int shared_fn_131(int x) { return x + 131; }
-inline int shared_fn_132(int x) { return x + 132; }
-inline int shared_fn_133(int x) { return x + 133; }
-inline int shared_fn_134(int x) { return x + 134; }
-inline int shared_fn_135(int x) { return x + 135; }
-inline int shared_fn_136(int x) { return x + 136; }
-inline int shared_fn_137(int x) { return x + 137; }
-inline int shared_fn_138(int x) { return x + 138; }
-inline int shared_fn_139(int x) { return x + 139; }
-inline int shared_fn_140(int x) { return x + 140; }
-inline int shared_fn_141(int x) { return x + 141; }
-inline int shared_fn_142(int x) { return x + 142; }
-inline int shared_fn_143(int x) { return x + 143; }
-inline int shared_fn_144(int x) { return x + 144; }
-inline int shared_fn_145(int x) { return x + 145; }
-inline int shared_fn_146(int x) { return x + 146; }
-inline int shared_fn_147(int x) { return x + 147; }
-inline int shared_fn_148(int x) { return x + 148; }
-inline int shared_fn_149(int x) { return x + 149; }
-inline int shared_fn_150(int x) { return x + 150; }
-inline int shared_fn_151(int x) { return x + 151; }
-inline int shared_fn_152(int x) { return x + 152; }
-inline int shared_fn_153(int x) { return x + 153; }
-inline int shared_fn_154(int x) { return x + 154; }
-inline int shared_fn_155(int x) { return x + 155; }
-inline int shared_fn_156(int x) { return x + 156; }
-inline int shared_fn_157(int x) { return x + 157; }
-inline int shared_fn_158(int x) { return x + 158; }
-inline int shared_fn_159(int x) { return x + 159; }
-inline int shared_fn_160(int x) { return x + 160; }
-inline int shared_fn_161(int x) { return x + 161; }
-inline int shared_fn_162(int x) { return x + 162; }
-inline int shared_fn_163(int x) { return x + 163; }
-inline int shared_fn_164(int x) { return x + 164; }
-inline int shared_fn_165(int x) { return x + 165; }
-inline int shared_fn_166(int x) { return x + 166; }
-inline int shared_fn_167(int x) { return x + 167; }
-inline int shared_fn_168(int x) { return x + 168; }
-inline int shared_fn_169(int x) { return x + 169; }
-inline int shared_fn_170(int x) { return x + 170; }
-inline int shared_fn_171(int x) { return x + 171; }
-inline int shared_fn_172(int x) { return x + 172; }
-inline int shared_fn_173(int x) { return x + 173; }
-inline int shared_fn_174(int x) { return x + 174; }
-inline int shared_fn_175(int x) { return x + 175; }
-inline int shared_fn_176(int x) { return x + 176; }
-inline int shared_fn_177(int x) { return x + 177; }
-inline int shared_fn_178(int x) { return x + 178; }
-inline int shared_fn_179(int x) { return x + 179; }
-inline int shared_fn_180(int x) { return x + 180; }
-inline int shared_fn_181(int x) { return x + 181; }
-inline int shared_fn_182(int x) { return x + 182; }
-inline int shared_fn_183(int x) { return x + 183; }
-inline int shared_fn_184(int x) { return x + 184; }
-inline int shared_fn_185(int x) { return x + 185; }
-inline int shared_fn_186(int x) { return x + 186; }
-inline int shared_fn_187(int x) { return x + 187; }
-inline int shared_fn_188(int x) { return x + 188; }
-inline int shared_fn_189(int x) { return x + 189; }
-inline int shared_fn_190(int x) { return x + 190; }
-inline int shared_fn_191(int x) { return x + 191; }
-inline int shared_fn_192(int x) { return x + 192; }
-inline int shared_fn_193(int x) { return x + 193; }
-inline int shared_fn_194(int x) { return x + 194; }
-inline int shared_fn_195(int x) { return x + 195; }
-inline int shared_fn_196(int x) { return x + 196; }
-inline int shared_fn_197(int x) { return x + 197; }
-inline int shared_fn_198(int x) { return x + 198; }
-inline int shared_fn_199(int x) { return x + 199; }
-inline int shared_fn_200(int x) { return x + 200; }
-inline int shared_fn_201(int x) { return x + 201; }
-inline int shared_fn_202(int x) { return x + 202; }
-inline int shared_fn_203(int x) { return x + 203; }
-inline int shared_fn_204(int x) { return x + 204; }
-inline int shared_fn_205(int x) { return x + 205; }
-inline int shared_fn_206(int x) { return x + 206; }
-inline int shared_fn_207(int x) { return x + 207; }
-inline int shared_fn_208(int x) { return x + 208; }
-inline int shared_fn_209(int x) { return x + 209; }
-inline int shared_fn_210(int x) { return x + 210; }
-inline int shared_fn_211(int x) { return x + 211; }
-inline int shared_fn_212(int x) { return x + 212; }
-inline int shared_fn_213(int x) { return x + 213; }
-inline int shared_fn_214(int x) { return x + 214; }
-inline int shared_fn_215(int x) { return x + 215; }
-inline int shared_fn_216(int x) { return x + 216; }
-inline int shared_fn_217(int x) { return x + 217; }
-inline int shared_fn_218(int x) { return x + 218; }
-inline int shared_fn_219(int x) { return x + 219; }
-inline int shared_fn_220(int x) { return x + 220; }
-inline int shared_fn_221(int x) { return x + 221; }
-inline int shared_fn_222(int x) { return x + 222; }
-inline int shared_fn_223(int x) { return x + 223; }
-inline int shared_fn_224(int x) { return x + 224; }
-inline int shared_fn_225(int x) { return x + 225; }
-inline int shared_fn_226(int x) { return x + 226; }
-inline int shared_fn_227(int x) { return x + 227; }
-inline int shared_fn_228(int x) { return x + 228; }
-inline int shared_fn_229(int x) { return x + 229; }
-inline int shared_fn_230(int x) { return x + 230; }
-inline int shared_fn_231(int x) { return x + 231; }
-inline int shared_fn_232(int x) { return x + 232; }
-inline int shared_fn_233(int x) { return x + 233; }
-inline int shared_fn_234(int x) { return x + 234; }
-inline int shared_fn_235(int x) { return x + 235; }
-inline int shared_fn_236(int x) { return x + 236; }
-inline int shared_fn_237(int x) { return x + 237; }
-inline int shared_fn_238(int x) { return x + 238; }
-inline int shared_fn_239(int x) { return x + 239; }
-inline int shared_fn_240(int x) { return x + 240; }
-inline int shared_fn_241(int x) { return x + 241; }
-inline int shared_fn_242(int x) { return x + 242; }
-inline int shared_fn_243(int x) { return x + 243; }
-inline int shared_fn_244(int x) { return x + 244; }
-inline int shared_fn_245(int x) { return x + 245; }
-inline int shared_fn_246(int x) { return x + 246; }
-inline int shared_fn_247(int x) { return x + 247; }
-inline int shared_fn_248(int x) { return x + 248; }
-inline int shared_fn_249(int x) { return x + 249; }
-inline int shared_fn_250(int x) { return x + 250; }
-inline int shared_fn_251(int x) { return x + 251; }
-inline int shared_fn_252(int x) { return x + 252; }
-inline int shared_fn_253(int x) { return x + 253; }
-inline int shared_fn_254(int x) { return x + 254; }
-inline int shared_fn_255(int x) { return x + 255; }
-inline int shared_fn_256(int x) { return x + 256; }
-inline int shared_fn_257(int x) { return x + 257; }
-inline int shared_fn_258(int x) { return x + 258; }
-inline int shared_fn_259(int x) { return x + 259; }
-inline int shared_fn_260(int x) { return x + 260; }
-inline int shared_fn_261(int x) { return x + 261; }
-inline int shared_fn_262(int x) { return x + 262; }
-inline int shared_fn_263(int x) { return x + 263; }
-inline int shared_fn_264(int x) { return x + 264; }
-inline int shared_fn_265(int x) { return x + 265; }
-inline int shared_fn_266(int x) { return x + 266; }
-inline int shared_fn_267(int x) { return x + 267; }
-inline int shared_fn_268(int x) { return x + 268; }
-inline int shared_fn_269(int x) { return x + 269; }
-inline int shared_fn_270(int x) { return x + 270; }
-inline int shared_fn_271(int x) { return x + 271; }
-inline int shared_fn_272(int x) { return x + 272; }
-inline int shared_fn_273(int x) { return x + 273; }
-inline int shared_fn_274(int x) { return x + 274; }
-inline int shared_fn_275(int x) { return x + 275; }
-inline int shared_fn_276(int x) { return x + 276; }
-inline int shared_fn_277(int x) { return x + 277; }
-inline int shared_fn_278(int x) { return x + 278; }
-inline int shared_fn_279(int x) { return x + 279; }
-inline int shared_fn_280(int x) { return x + 280; }
-inline int shared_fn_281(int x) { return x + 281; }
-inline int shared_fn_282(int x) { return x + 282; }
-inline int shared_fn_283(int x) { return x + 283; }
-inline int shared_fn_284(int x) { return x + 284; }
-inline int shared_fn_285(int x) { return x + 285; }
-inline int shared_fn_286(int x) { return x + 286; }
-inline int shared_fn_287(int x) { return x + 287; }
-inline int shared_fn_288(int x) { return x + 288; }
-inline int shared_fn_289(int x) { return x + 289; }
-inline int shared_fn_290(int x) { return x + 290; }
-inline int shared_fn_291(int x) { return x + 291; }
-inline int shared_fn_292(int x) { return x + 292; }
-inline int shared_fn_293(int x) { return x + 293; }
-inline int shared_fn_294(int x) { return x + 294; }
-inline int shared_fn_295(int x) { return x + 295; }
-inline int shared_fn_296(int x) { return x + 296; }
-inline int shared_fn_297(int x) { return x + 297; }
-inline int shared_fn_298(int x) { return x + 298; }
-inline int shared_fn_299(int x) { return x + 299; }
-inline int shared_fn_300(int x) { return x + 300; }
-inline int shared_fn_301(int x) { return x + 301; }
-inline int shared_fn_302(int x) { return x + 302; }
-inline int shared_fn_303(int x) { return x + 303; }
-inline int shared_fn_304(int x) { return x + 304; }
-inline int shared_fn_305(int x) { return x + 305; }
-inline int shared_fn_306(int x) { return x + 306; }
-inline int shared_fn_307(int x) { return x + 307; }
-inline int shared_fn_308(int x) { return x + 308; }
-inline int shared_fn_309(int x) { return x + 309; }
-inline int shared_fn_310(int x) { return x + 310; }
-inline int shared_fn_311(int x) { return x + 311; }
-inline int shared_fn_312(int x) { return x + 312; }
-inline int shared_fn_313(int x) { return x + 313; }
-inline int shared_fn_314(int x) { return x + 314; }
-inline int shared_fn_315(int x) { return x + 315; }
-inline int shared_fn_316(int x) { return x + 316; }
-inline int shared_fn_317(int x) { return x + 317; }
-inline int shared_fn_318(int x) { return x + 318; }
-inline int shared_fn_319(int x) { return x + 319; }
-inline int shared_fn_320(int x) { return x + 320; }
-inline int shared_fn_321(int x) { return x + 321; }
-inline int shared_fn_322(int x) { return x + 322; }
-inline int shared_fn_323(int x) { return x + 323; }
-inline int shared_fn_324(int x) { return x + 324; }
-inline int shared_fn_325(int x) { return x + 325; }
-inline int shared_fn_326(int x) { return x + 326; }
-inline int shared_fn_327(int x) { return x + 327; }
-inline int shared_fn_328(int x) { return x + 328; }
-inline int shared_fn_329(int x) { return x + 329; }
-inline int shared_fn_330(int x) { return x + 330; }
-inline int shared_fn_331(int x) { return x + 331; }
-inline int shared_fn_332(int x) { return x + 332; }
-inline int shared_fn_333(int x) { return x + 333; }
-inline int shared_fn_334(int x) { return x + 334; }
-inline int shared_fn_335(int x) { return x + 335; }
-inline int shared_fn_336(int x) { return x + 336; }
-inline int shared_fn_337(int x) { return x + 337; }
-inline int shared_fn_338(int x) { return x + 338; }
-inline int shared_fn_339(int x) { return x + 339; }
-inline int shared_fn_340(int x) { return x + 340; }
-inline int shared_fn_341(int x) { return x + 341; }
-inline int shared_fn_342(int x) { return x + 342; }
-inline int shared_fn_343(int x) { return x + 343; }
-inline int shared_fn_344(int x) { return x + 344; }
-inline int shared_fn_345(int x) { return x + 345; }
-inline int shared_fn_346(int x) { return x + 346; }
-inline int shared_fn_347(int x) { return x + 347; }
-inline int shared_fn_348(int x) { return x + 348; }
-inline int shared_fn_349(int x) { return x + 349; }
-inline int shared_fn_350(int x) { return x + 350; }
-inline int shared_fn_351(int x) { return x + 351; }
-inline int shared_fn_352(int x) { return x + 352; }
-inline int shared_fn_353(int x) { return x + 353; }
-inline int shared_fn_354(int x) { return x + 354; }
-inline int shared_fn_355(int x) { return x + 355; }
-inline int shared_fn_356(int x) { return x + 356; }
-inline int shared_fn_357(int x) { return x + 357; }
-inline int shared_fn_358(int x) { return x + 358; }
-inline int shared_fn_359(int x) { return x + 359; }
-inline int shared_fn_360(int x) { return x + 360; }
-inline int shared_fn_361(int x) { return x + 361; }
-inline int shared_fn_362(int x) { return x + 362; }
-inline int shared_fn_363(int x) { return x + 363; }
-inline int shared_fn_364(int x) { return x + 364; }
-inline int shared_fn_365(int x) { return x + 365; }
-inline int shared_fn_366(int x) { return x + 366; }
-inline int shared_fn_367(int x) { return x + 367; }
-inline int shared_fn_368(int x) { return x + 368; }
-inline int shared_fn_369(int x) { return x + 369; }
-inline int shared_fn_370(int x) { return x + 370; }
-inline int shared_fn_371(int x) { return x + 371; }
-inline int shared_fn_372(int x) { return x + 372; }
-inline int shared_fn_373(int x) { return x + 373; }
-inline int shared_fn_374(int x) { return x + 374; }
-inline int shared_fn_375(int x) { return x + 375; }
-inline int shared_fn_376(int x) { return x + 376; }
-inline int shared_fn_377(int x) { return x + 377; }
-inline int shared_fn_378(int x) { return x + 378; }
-inline int shared_fn_379(int x) { return x + 379; }
-inline int shared_fn_380(int x) { return x + 380; }
-inline int shared_fn_381(int x) { return x + 381; }
-inline int shared_fn_382(int x) { return x + 382; }
-inline int shared_fn_383(int x) { return x + 383; }
-inline int shared_fn_384(int x) { return x + 384; }
-inline int shared_fn_385(int x) { return x + 385; }
-inline int shared_fn_386(int x) { return x + 386; }
-inline int shared_fn_387(int x) { return x + 387; }
-inline int shared_fn_388(int x) { return x + 388; }
-inline int shared_fn_389(int x) { return x + 389; }
-inline int shared_fn_390(int x) { return x + 390; }
-inline int shared_fn_391(int x) { return x + 391; }
-inline int shared_fn_392(int x) { return x + 392; }
-inline int shared_fn_393(int x) { return x + 393; }
-inline int shared_fn_394(int x) { return x + 394; }
-inline int shared_fn_395(int x) { return x + 395; }
-inline int shared_fn_396(int x) { return x + 396; }
-inline int shared_fn_397(int x) { return x + 397; }
-inline int shared_fn_398(int x) { return x + 398; }
-inline int shared_fn_399(int x) { return x + 399; }
-#endif
diff --git a/dedup-test/use.cpp b/dedup-test/use.cpp
deleted file mode 100644
index cc00041ff4cc4..0000000000000
--- a/dedup-test/use.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "a.h"
-#include "b.h"
-int main() { return a_entry(1) + b_entry(2); }
diff --git a/dedup-test/use1.cpp b/dedup-test/use1.cpp
deleted file mode 100644
index 1d4c4250df0c7..0000000000000
--- a/dedup-test/use1.cpp
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "a.h"
-int main() { return a_entry(1); }
diff --git a/dedup-test/use_err.cpp b/dedup-test/use_err.cpp
deleted file mode 100644
index e3cf22fe27df4..0000000000000
--- a/dedup-test/use_err.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-// Stage 2c: prove the redirect resolves correctly.
-// Include BOTH modules so A loads first (registers shared.h as the canonical
-// copy) and B loads second (its shared.h is de-duplicated / redirected into
-// A's copy). Then trigger a diagnostic whose location lives in shared.h.
-// The error must still point at shared.h:<line> with the right function/line,
-// which only holds if the redirected offsets resolve correctly.
-#include "a.h"
-#include "b.h"
-
-int main() {
-  // shared_fn_1 is defined in shared.h (present in both A and B); pass a bad
-  // argument so overload resolution reports the candidate in shared.h.
-  return a_entry(1) + shared_fn_1("oops");
-}
diff --git a/dedup-test/use_err_rev.cpp b/dedup-test/use_err_rev.cpp
deleted file mode 100644
index 817bfb4bc2b1e..0000000000000
--- a/dedup-test/use_err_rev.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-// Stage 2c: load-order independence. Same as use_err.cpp but with the include
-// order flipped so B is seen first. Whichever module loads first becomes the
-// canonical copy; the other redirects into it. Result should be identical:
-// dedup active + the diagnostic still resolves to shared.h:<line>.
-#include "b.h"
-#include "a.h"
-
-int main() {
-  return a_entry(1) + shared_fn_1("oops");
-}

>From 4dd7271b2c0f56af6b377e690900b4166ff3e058 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 15 Jul 2026 13:40:40 +0000
Subject: [PATCH 11/15] clean up comments

---
 clang/include/clang/Basic/SourceManager.h     |  2 +-
 clang/include/clang/Serialization/ASTReader.h | 21 +++++++++----------
 .../include/clang/Serialization/ModuleFile.h  |  2 +-
 clang/lib/Basic/SourceManager.cpp             |  2 +-
 clang/lib/Serialization/ASTReader.cpp         |  5 ++++-
 5 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h
index 424de78725492..b776d37b6ba1a 100644
--- a/clang/include/clang/Basic/SourceManager.h
+++ b/clang/include/clang/Basic/SourceManager.h
@@ -755,7 +755,7 @@ class SourceManager : public RefCountedBase<SourceManager> {
   static const SourceLocation::UIntTy MaxLoadedOffset =
       1ULL << (8 * sizeof(SourceLocation::UIntTy) - 1);
 
-  // Source location de-duplication. A file included into many modules is
+  // Source location deduplication. A file included into many modules is
   // serialized into each of their PCMs. ASTReader keeps the first loaded copy
   // and redirects later modules' references to it instead of allocating a
   // duplicate SLoc range. These counters record the address space that reuse
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index e1020e64fcca8..429d7fc3e653f 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2399,10 +2399,11 @@ class ASTReader : public ExternalPreprocessorSource,
   llvm::Expected<SourceLocation::UIntTy> readSLocOffset(ModuleFile *F,
                                                         unsigned Index);
 
-  /// Identity of an input file, taken from serialized metadata without touching
-  /// the filesystem. It uses the stored name, size, and modification time that
-  /// Clang's own input-file staleness check relies on. An empty Name marks a
-  /// non-file entry (a buffer or expansion), which never deduplicates.
+  /// Identity of an input file, from serialized metadata (no filesystem
+  /// access). Uses the stored name, size, and modification time that Clang's
+  /// own input-file staleness check relies on. ContentHash is not used because
+  /// it is zero unless -fvalidate-ast-input-files-content is set. An empty Name
+  /// marks a non-file entry (a buffer or expansion), which never deduplicates.
   struct SLocFileIdentity {
     StringRef Name;
     off_t Size = 0;
@@ -2421,8 +2422,7 @@ class ASTReader : public ExternalPreprocessorSource,
 
   /// Files already loaded into the loaded SLoc address space, keyed by stored
   /// name. Lets a later module reuse an earlier module's copy instead of
-  /// allocating a duplicate range. The key is serialized metadata, so no input
-  /// file is resolved or stat'd at load time.
+  /// allocating a duplicate range.
   llvm::StringMap<PrimaryLoadedFileLoc> PrimaryLoadedFiles;
 
   /// The location of a previously-loaded file matching \p Id (same name, size,
@@ -2437,9 +2437,8 @@ class ASTReader : public ExternalPreprocessorSource,
 
   /// Read \p F's SLoc entry records without materializing them, filling
   /// \p Offsets[i] with each entry's local offset and \p Files[i] with its
-  /// file identity (empty Name for non-file entries). File identity comes from
-  /// serialized metadata only, with no input file resolved on disk. Returns
-  /// false on a malformed record.
+  /// file identity (empty Name for non-file entries). Returns false on a
+  /// malformed record.
   bool scanLoadedSLocEntries(ModuleFile &F, SmallVectorImpl<uint32_t> &Offsets,
                              SmallVectorImpl<SLocFileIdentity> &Files);
 
@@ -2567,8 +2566,8 @@ class ASTReader : public ExternalPreprocessorSource,
                                                    Seg.Delta)) |
               MacroBit);
       }
-      // No segment matched. This shouldn't happen, so fall through to the
-      // flat shift below.
+      // The segments cover the whole value range, so one always matches.
+      llvm_unreachable("SLocRemap segments must cover every source location");
     }
 
     return Loc.getLocWithOffset(ModuleFile.SLocEntryBaseOffset - 2);
diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h
index 76cefc2280936..3d2ef6f2124ac 100644
--- a/clang/include/clang/Serialization/ModuleFile.h
+++ b/clang/include/clang/Serialization/ModuleFile.h
@@ -346,7 +346,7 @@ class ModuleFile {
   /// AST file.
   const uint32_t *SLocEntryOffsets = nullptr;
 
-  // === Source location de-duplication ===
+  // === Source location deduplication ===
 
   /// One segment of the local-to-global source location map. A local raw
   /// location L in [LocalBegin, LocalEnd) maps to global location L + Delta.
diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp
index fc1d0094a84d8..ab20a0914a856 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -2154,7 +2154,7 @@ void SourceManager::PrintStats() const {
                << "B of SLoc address space used.\n";
   if (NumDuplicateLoadedFiles)
     llvm::errs() << NumDuplicateLoadedFiles
-                 << " duplicate loaded file SLocEntries de-duplicated ("
+                 << " duplicate loaded file SLocEntries deduplicated ("
                  << DuplicateLoadedBytes
                  << "B of SLoc address space reused).\n";
 
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 5df770034e7db..3cf5f3d1a5dcc 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -1901,8 +1901,10 @@ ASTReader::remapSLocEntryOffset(ModuleFile &F, uint32_t LocalOffset) const {
         return static_cast<SourceLocation::UIntTy>(static_cast<int64_t>(Low) +
                                                    Seg.Delta);
     }
+    // The segments cover the whole value range, so one always matches.
+    llvm_unreachable("SLocRemap segments must cover every source location");
   }
-  // No remap, or no segment matched. Use the original flat shift.
+  // No remap for this module. Use the flat shift.
   return F.SLocEntryBaseOffset + LocalOffset;
 }
 
@@ -4354,6 +4356,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       // file as it goes, so checking against the map there would treat a file
       // that appears more than once in this module as a duplicate of its own
       // first occurrence.
+      // Entry I's size is the gap to the next offset (block end for the last).
       auto entrySize = [&](unsigned I) -> uint64_t {
         return (I + 1 < N ? Offsets[I + 1] : SLocSpaceSize) - Offsets[I];
       };

>From 8430b38aa3c530ced447636b8a0cdf9c9bfcedb2 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 15 Jul 2026 14:19:29 +0000
Subject: [PATCH 12/15] clean up git ignore

---
 .gitignore | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index 1bceeef94e7e4..a4382c9ea7390 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,9 +31,6 @@
 # Nested build directory
 /build*
 
-/ Test directory
-/dedup-test
-
 #==============================================================================#
 # Explicit files to ignore (only matches one).
 #==============================================================================#

>From 0b6514cc4395a38b3dad8cc5895569bf294281aa Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 15 Jul 2026 14:35:13 +0000
Subject: [PATCH 13/15] clean up comments

---
 clang/include/clang/Serialization/ASTReader.h | 8 +++-----
 clang/lib/Serialization/ASTReader.cpp         | 6 ++----
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 429d7fc3e653f..ac54818e850b7 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2399,11 +2399,9 @@ class ASTReader : public ExternalPreprocessorSource,
   llvm::Expected<SourceLocation::UIntTy> readSLocOffset(ModuleFile *F,
                                                         unsigned Index);
 
-  /// Identity of an input file, from serialized metadata (no filesystem
-  /// access). Uses the stored name, size, and modification time that Clang's
-  /// own input-file staleness check relies on. ContentHash is not used because
-  /// it is zero unless -fvalidate-ast-input-files-content is set. An empty Name
-  /// marks a non-file entry (a buffer or expansion), which never deduplicates.
+  /// Identity of an input file, built from serialized metadata so we touch no
+  /// files at load. An empty Name means a non-file entry like a buffer or 
+  /// expansion, and never deduplicates.
   struct SLocFileIdentity {
     StringRef Name;
     off_t Size = 0;
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 3cf5f3d1a5dcc..e51a56e3de0da 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -1868,10 +1868,8 @@ bool ASTReader::scanLoadedSLocEntries(
     }
     Offsets[I] = (uint32_t)Record[0];
     if (Code.get() == SM_SLOC_FILE_ENTRY) {
-      // Identify the file from serialized metadata only. Resolving it on disk
-      // here (getInputFile) would stat and open every input file of every
-      // module at load, which is prohibitive. The stored name, size, and time
-      // are the identity Clang's own staleness check already uses.
+      // File identity comes from serialized metadata, so no input file is
+      // touched on disk at load.
       InputFileInfo IFI = getInputFileInfo(F, Record[4]);
       if (IFI.isValid())
         Files[I] = SLocFileIdentity{IFI.UnresolvedImportedFilename,

>From e12d6251bfc1eec070da2326a765a7608b1bf62c Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Wed, 15 Jul 2026 11:43:19 -0400
Subject: [PATCH 14/15] run clang format

---
 clang/include/clang/Serialization/ASTReader.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index ac54818e850b7..a2d673beb90c5 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -2400,7 +2400,7 @@ class ASTReader : public ExternalPreprocessorSource,
                                                         unsigned Index);
 
   /// Identity of an input file, built from serialized metadata so we touch no
-  /// files at load. An empty Name means a non-file entry like a buffer or 
+  /// files at load. An empty Name means a non-file entry like a buffer or
   /// expansion, and never deduplicates.
   struct SLocFileIdentity {
     StringRef Name;

>From 7765cb9287be48013699fec86413574fe06b9952 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Thu, 30 Jul 2026 11:57:09 -0400
Subject: [PATCH 15/15] get rid of un needed function

---
 clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp |  9 +++------
 clang/test/CIR/CodeGenHIP/builtins-amdgcn.hip | 16 ++++++++++++++++
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp
index 63bc9d8b6b144..46226eed2c884 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp
@@ -241,12 +241,9 @@ CIRGenFunction::emitAMDGPUBuiltinExpr(unsigned builtinId,
                                        mlir::ValueRange{src0, src1, src2});
   }
   case AMDGPU::BI__builtin_amdgcn_trig_preop:
-  case AMDGPU::BI__builtin_amdgcn_trig_preopf: {
-    cgm.errorNYI(expr->getSourceRange(),
-                 std::string("unimplemented AMDGPU builtin call: ") +
-                     getContext().BuiltinInfo.getName(builtinId));
-    return mlir::Value{};
-  }
+  case AMDGPU::BI__builtin_amdgcn_trig_preopf:
+    return emitBuiltinWithOneOverloadedType<2>(expr, "amdgcn.trig.preop")
+        .getValue();
   case AMDGPU::BI__builtin_amdgcn_rcp:
   case AMDGPU::BI__builtin_amdgcn_rcpf:
   case AMDGPU::BI__builtin_amdgcn_rcph:
diff --git a/clang/test/CIR/CodeGenHIP/builtins-amdgcn.hip b/clang/test/CIR/CodeGenHIP/builtins-amdgcn.hip
index 5dfc95490209b..546e851461ce4 100644
--- a/clang/test/CIR/CodeGenHIP/builtins-amdgcn.hip
+++ b/clang/test/CIR/CodeGenHIP/builtins-amdgcn.hip
@@ -223,3 +223,19 @@ __device__ void test_frexp_mant_f32(float* out, float a) {
 __device__ void test_frexp_mant_f64(double* out, double a) {
   *out = __builtin_amdgcn_frexp_mant(a);
 }
+
+// CIR-LABEL: @_Z19test_trig_preop_f32Pffi
+// CIR: cir.call_llvm_intrinsic "amdgcn.trig.preop" {{.*}} : (!cir.float, !s32i) -> !cir.float
+// LLVM: define{{.*}} void @_Z19test_trig_preop_f32Pffi
+// LLVM: call{{.*}} float @llvm.amdgcn.trig.preop.f32(float %{{.+}}, i32 %{{.+}})
+__device__ void test_trig_preop_f32(float* out, float a, int b) {
+  *out = __builtin_amdgcn_trig_preopf(a, b);
+}
+
+// CIR-LABEL: @_Z19test_trig_preop_f64Pddi
+// CIR: cir.call_llvm_intrinsic "amdgcn.trig.preop" {{.*}} : (!cir.double, !s32i) -> !cir.double
+// LLVM: define{{.*}} void @_Z19test_trig_preop_f64Pddi
+// LLVM: call{{.*}} double @llvm.amdgcn.trig.preop.f64(double %{{.+}}, i32 %{{.+}})
+__device__ void test_trig_preop_f64(double* out, double a, int b) {
+  *out = __builtin_amdgcn_trig_preop(a, b);
+}



More information about the cfe-commits mailing list