[lld] [lld][MachO] Key branch-extension thunks on (referent, addend) (PR #191808)

Ellis Hoag via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 14 17:01:36 PDT 2026


================
@@ -112,32 +112,60 @@ NamePair maybeRenameSection(NamePair key);
 // of ConcatOutputSection, so must have deterministic iteration order.
 extern llvm::MapVector<NamePair, ConcatOutputSection *> concatOutputSections;
 
-// After ICF, multiple Defined symbols may point to the same (isec, value) yet
-// remain as distinct Symbol pointers.  Using bare Symbol* as thunkMap keys
-// would create redundant thunks for the same target. This DenseMapInfo
+// Branch-extension thunks are keyed by both the target referent and the
+// branch relocation's addend.  Two call sites that branch to the same
+// symbol with different addends (e.g. `bl _func` and `bl _func+8`) target
+// distinct addresses and therefore need distinct thunks.
+//
+// After ICF, multiple Defined symbols may point to the same (isec, value)
+// yet remain as distinct Symbol pointers.  The equality predicate below
 // canonicalizes Defined symbols by (isec, value) so that ICF-folded copies
-// share a single thunkMap entry.
-struct ThunkMapKeyInfo : llvm::DenseMapInfo<Symbol *> {
-  static unsigned getHashValue(const Symbol *sym) {
-    if (const auto *d = dyn_cast<Defined>(sym))
-      return llvm::hash_combine(d->isec(), d->value);
-    return llvm::DenseMapInfo<Symbol *>::getHashValue(sym);
+// still share a single thunkMap entry when their addends match.
+struct ThunkKey {
+  Symbol *sym;
+  int64_t addend;
+
+  static ThunkKey getEmptyKey() {
+    return {llvm::DenseMapInfo<Symbol *>::getEmptyKey(), 0};
+  }
+  static ThunkKey getTombstoneKey() {
+    return {llvm::DenseMapInfo<Symbol *>::getTombstoneKey(), 0};
   }
-  static bool isEqual(const Symbol *lhs, const Symbol *rhs) {
-    if (lhs == rhs)
+  bool isSentinel() const {
+    return sym == llvm::DenseMapInfo<Symbol *>::getEmptyKey() ||
+           sym == llvm::DenseMapInfo<Symbol *>::getTombstoneKey();
+  }
+  bool operator==(const ThunkKey &other) const {
+    if (addend != other.addend)
+      return false;
+    if (sym == other.sym)
       return true;
-    if (lhs == getEmptyKey() || lhs == getTombstoneKey() ||
-        rhs == getEmptyKey() || rhs == getTombstoneKey())
+    if (isSentinel() || other.isSentinel())
       return false;
-    const auto *dl = dyn_cast<Defined>(lhs);
-    const auto *dr = dyn_cast<Defined>(rhs);
+    const auto *dl = dyn_cast<Defined>(sym);
+    const auto *dr = dyn_cast<Defined>(other.sym);
     if (dl && dr)
       return dl->isec() == dr->isec() && dl->value == dr->value;
     return false;
   }
 };
 
-extern llvm::DenseMap<Symbol *, ThunkInfo, ThunkMapKeyInfo> thunkMap;
+struct ThunkMapKeyInfo {
+  static ThunkKey getEmptyKey() { return ThunkKey::getEmptyKey(); }
+  static ThunkKey getTombstoneKey() { return ThunkKey::getTombstoneKey(); }
+  static unsigned getHashValue(const ThunkKey &k) {
+    if (k.isSentinel())
+      return llvm::hash_value(k.sym);
+    if (const auto *d = dyn_cast<Defined>(k.sym))
+      return llvm::hash_combine(d->isec(), d->value, k.addend);
+    return llvm::hash_combine(k.sym, k.addend);
+  }
+  static bool isEqual(const ThunkKey &lhs, const ThunkKey &rhs) {
+    return lhs == rhs;
+  }
+};
----------------
ellishg wrote:

I'm not sure, but if we implement `getHashValue()` for `ThunkKey` will we get this info struct for free? Can we then remove `ThunkMapKeyInfo` from the definition below?

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


More information about the llvm-commits mailing list