[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


================
@@ -351,19 +351,34 @@ void TextOutputSection::finalize() {
       uint64_t highVA = callVA + forwardBranchRange;
       // Calculate our call referent address
       auto *funcSym = cast<Symbol *>(r.referent);
-      ThunkInfo &thunkInfo = thunkMap[funcSym];
-      // The referent is not reachable, so we need to use a thunk ...
-      if ((funcSym->isInStubs() ||
+      ThunkInfo &thunkInfo = thunkMap[ThunkKey{funcSym, r.addend}];
+      // The referent is not reachable, so we need to use a thunk... unless we
+      // are close enough to the end that branch target sections (__stubs,
+      // __objc_stubs) are now within range of a simple forward branch -- BUT
+      // only for zero-addend branches. The writer's resolveSymbolOffsetVA()
+      // resolves non-zero-addend branches against the symbol body rather than
+      // the stub, so __stubs reachability says nothing about whether such a
+      // call can be emitted directly. Hence the `r.addend == 0` guard below.
+      // See INTERP check lines in arm64-thunk-branch-addend.s.
+      if (r.addend == 0 &&
+          (funcSym->isInStubs() ||
            (in.objcStubs && in.objcStubs->isNeeded() &&
             ObjCStubsSection::isObjCStubSymbol(funcSym))) &&
           callVA >= branchTargetThresholdVA) {
         assert(callVA != TargetInfo::outOfRangeVA);
-        // ... Oh, wait! We are close enough to the end that branch target
-        // sections (__stubs, __objc_stubs) are now within range of a simple
-        // forward branch.
         continue;
       }
-      uint64_t funcVA = funcSym->resolveBranchVA();
+      // For non-zero addends, branch directly to the symbol body rather than
+      // through any stub (`resolveBranchVA()` would prefer the stub VA when
+      // present). This mirrors the writer-side rule in `resolveSymbolOffsetVA`:
+      // branching to the interior of a stub trampoline is meaningless, so for
+      // `bl _func+N` we resolve against `_func`'s own body. The two helpers
+      // only diverge when `funcSym` is a Defined that is *also* in stubs (e.g.
+      // an interposable extern), but `finalize()` has to agree with the writer
+      // in that case or reachability analysis drifts.
+      // See INTERP check lines in arm64-thunk-branch-addend.s.
+      uint64_t funcVA = r.addend != 0 ? funcSym->getVA() + r.addend
+                                      : funcSym->resolveBranchVA();
----------------
ellishg wrote:

Can we use `resolveSymbolOffsetVA()` directly to avoid code duplication? I assume it will be inlined anyway, so the non `RelocAttrBits::BRANCH` cases will probably be dead-stripped anyway.

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


More information about the llvm-commits mailing list