[llvm] [PPC] Don't emit overflow error on branches accross different sections. (PR #206812)

Sean Fertile via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 19 11:00:29 PDT 2026


https://github.com/mandlebug updated https://github.com/llvm/llvm-project/pull/206812

>From 373b0b1fea3b2215b77b82e51a45754e5a7e0061 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Wed, 24 Jun 2026 21:55:52 -0400
Subject: [PATCH 1/6] [PPC] Don't emit overflow error on branches accross
 different sections.

If a branch is to a symbol in a different sections we have to give the
linker a chance to fixup the overflow. On AIX in particualr we have to
emit the masked off overflowed value into the bits to be relocated as
the linker uses them in its final relocation calculation.
---
 .../PowerPC/MCTargetDesc/PPCAsmBackend.cpp    | 23 +++++++++++++++++--
 .../CodeGen/PowerPC/call-overflow-error.ll    | 22 ++++++++++++++++++
 2 files changed, 43 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/PowerPC/call-overflow-error.ll

diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
index 72a5e60a01d87..0b561926ba498 100644
--- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
+++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
@@ -26,7 +26,8 @@
 using namespace llvm;
 
 static uint64_t adjustFixupValue(MCContext &Ctx, const MCFixup &Fixup,
-                                 unsigned Kind, uint64_t Value) {
+                                 unsigned Kind, uint64_t Value,
+                                 const MCFragment &F, const MCValue &Target) {
   auto checkBrFixup = [&](unsigned Bits) {
     int64_t SVal = int64_t(Value);
     if ((Value & 3) != 0) {
@@ -37,6 +38,24 @@ static uint64_t adjustFixupValue(MCContext &Ctx, const MCFixup &Fixup,
 
     // Low two bits are not encoded.
     if (!isIntN(Bits + 2, Value)) {
+      // Do not emit an error when the target of the branch is in a
+      // different section then the branch instruction. The linker may insert
+      // a trampoline or rearrange sections to avoid the overflow.
+      const MCSection *FragSection = F.getParent();
+      const MCSymbol *TargetSym = Target.getAddSym();
+
+      if (TargetSym) {
+        // Branch to an externally defined symbol.
+        if (!TargetSym->isDefined()) {
+          return;
+        }
+
+        const MCSection *TargetSection = TargetSym->getFragment()->getParent();
+        // Branch across sections.
+        if (TargetSection != FragSection)
+          return;
+      }
+
       Ctx.reportError(Fixup.getLoc(), "branch target out of range (" +
                                           Twine(SVal) + " not between " +
                                           Twine(minIntN(Bits) * 4) + " and " +
@@ -231,7 +250,7 @@ void PPCAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup,
   MCFixupKind Kind = Fixup.getKind();
   if (mc::isRelocation(Kind))
     return;
-  Value = adjustFixupValue(getContext(), Fixup, Kind, Value);
+  Value = adjustFixupValue(getContext(), Fixup, Kind, Value, F, TargetVal);
   if (!Value)
     return; // Doesn't change encoding.
 
diff --git a/llvm/test/CodeGen/PowerPC/call-overflow-error.ll b/llvm/test/CodeGen/PowerPC/call-overflow-error.ll
new file mode 100644
index 0000000000000..3619b8b2dc4af
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/call-overflow-error.ll
@@ -0,0 +1,22 @@
+; RUN: not llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc-ibm-aix \
+; RUN:   2>&1 -filetype=obj < %s | FileCheck %s --check-prefix=ERROR
+
+; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc-ibm-aix \
+; RUN:   --function-sections -filetype=obj -o %t.o < %s
+; RUN: llvm-objdump -Dr %t.o | FileCheck %s
+
+define signext i32 @bar() {
+entry:
+  ret i32 42
+}
+
+define signext i32 @foo() {
+entry:
+  call void asm sideeffect ".space 0x2000100", ""()
+  %call = call signext i32 @bar()
+  ret i32 %call
+}
+
+; ERROR: error: branch target out of range (-33554732 not between -33554432 and 33554428)
+; CHECK: 200012c: 49 ff fe d5   bl 0x4000000 <.bar+0x3ffffe0>
+; CHECK:                        0200012c:  R_RBR        .bar

>From 550f4da9477e4e14c466f669472a9885bd01884c Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Tue, 14 Jul 2026 13:44:34 -0400
Subject: [PATCH 2/6] Normalize overflowed call offset in XCOFF object writer.

---
 llvm/lib/MC/XCOFFObjectWriter.cpp             | 39 +++++++++++++++++++
 .../PowerPC/MCTargetDesc/PPCAsmBackend.cpp    | 23 +----------
 .../CodeGen/PowerPC/call-overflow-error.ll    | 16 ++++++--
 3 files changed, 54 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp
index d9f47d6f4ba25..ad7c3f5365cc1 100644
--- a/llvm/lib/MC/XCOFFObjectWriter.cpp
+++ b/llvm/lib/MC/XCOFFObjectWriter.cpp
@@ -553,6 +553,37 @@ static MCSectionXCOFF *getContainingCsect(const MCSymbolXCOFF *XSym) {
   return XSym->getRepresentedCsect();
 }
 
+// Checks that the branhc target symbol has a different section then the one
+// containing the fragment F.
+static bool haveDifferentSections(const MCFragment &F, MCValue Target) {
+  const MCSymbol *TargetSym = Target.getAddSym();
+  if (!TargetSym)
+    return false;
+
+  // Branch to an externally defined symbol.
+  if (!TargetSym->isDefined())
+    return true;
+
+  // Branch across sections.
+  const MCSection *FragSection = F.getParent();
+  const MCSection *TargetSection = TargetSym->getFragment()->getParent();
+  return TargetSection != FragSection;
+}
+
+static bool CallOverflows(MCFixupKindInfo Info, uint64_t FixedValue) {
+  if (Info.TargetSize != 24)
+    report_fatal_error("Unexepected call fixup kind for XCOFF");
+
+  return !isIntN(26, FixedValue);
+}
+
+static uint64_t NormalizeCallOffset(MCFixupKindInfo Info, uint64_t Offset) {
+  if (Info.TargetSize != 24)
+    report_fatal_error("Unexepected call fixup kind for XCOFF");
+
+  return llvm::SignExtend64<26>(Offset & 0x3ffffff);
+}
+
 void XCOFFWriter::executePostLayoutBinding() {
   for (const auto &S : *Asm) {
     auto *MCSec = static_cast<const MCSectionXCOFF *>(&S);
@@ -760,6 +791,14 @@ void XCOFFWriter::recordRelocation(const MCFragment &F, const MCFixup &Fixup,
     // and BR instr address plus any constant value.
     FixedValue = getVirtualAddress(SymA, SymASec) - BRInstrAddress +
                  Target.getConstant();
+
+    // If the offset overflows but the call crosses sections then we need to
+    // normalize the offset to be a valid value for encoding. Since the call
+    // crosses a CSECT boundary the linker may fix the overflow by using a
+    // trampoline, or rearranging the sections in the output file.
+    MCFixupKindInfo Info = Asm->getBackend().getFixupKindInfo(Fixup.getKind());
+    if (CallOverflows(Info, FixedValue) && haveDifferentSections(F, Target))
+      FixedValue = NormalizeCallOffset(Info, FixedValue);
   } else if (Type == XCOFF::RelocationType::R_REF) {
     // The FixedValue and FixupOffsetInCsect should always be 0 since it
     // specifies a nonrelocating reference.
diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
index 0b561926ba498..72a5e60a01d87 100644
--- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
+++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCAsmBackend.cpp
@@ -26,8 +26,7 @@
 using namespace llvm;
 
 static uint64_t adjustFixupValue(MCContext &Ctx, const MCFixup &Fixup,
-                                 unsigned Kind, uint64_t Value,
-                                 const MCFragment &F, const MCValue &Target) {
+                                 unsigned Kind, uint64_t Value) {
   auto checkBrFixup = [&](unsigned Bits) {
     int64_t SVal = int64_t(Value);
     if ((Value & 3) != 0) {
@@ -38,24 +37,6 @@ static uint64_t adjustFixupValue(MCContext &Ctx, const MCFixup &Fixup,
 
     // Low two bits are not encoded.
     if (!isIntN(Bits + 2, Value)) {
-      // Do not emit an error when the target of the branch is in a
-      // different section then the branch instruction. The linker may insert
-      // a trampoline or rearrange sections to avoid the overflow.
-      const MCSection *FragSection = F.getParent();
-      const MCSymbol *TargetSym = Target.getAddSym();
-
-      if (TargetSym) {
-        // Branch to an externally defined symbol.
-        if (!TargetSym->isDefined()) {
-          return;
-        }
-
-        const MCSection *TargetSection = TargetSym->getFragment()->getParent();
-        // Branch across sections.
-        if (TargetSection != FragSection)
-          return;
-      }
-
       Ctx.reportError(Fixup.getLoc(), "branch target out of range (" +
                                           Twine(SVal) + " not between " +
                                           Twine(minIntN(Bits) * 4) + " and " +
@@ -250,7 +231,7 @@ void PPCAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup,
   MCFixupKind Kind = Fixup.getKind();
   if (mc::isRelocation(Kind))
     return;
-  Value = adjustFixupValue(getContext(), Fixup, Kind, Value, F, TargetVal);
+  Value = adjustFixupValue(getContext(), Fixup, Kind, Value);
   if (!Value)
     return; // Doesn't change encoding.
 
diff --git a/llvm/test/CodeGen/PowerPC/call-overflow-error.ll b/llvm/test/CodeGen/PowerPC/call-overflow-error.ll
index 3619b8b2dc4af..382c66038aba4 100644
--- a/llvm/test/CodeGen/PowerPC/call-overflow-error.ll
+++ b/llvm/test/CodeGen/PowerPC/call-overflow-error.ll
@@ -5,6 +5,13 @@
 ; RUN:   --function-sections -filetype=obj -o %t.o < %s
 ; RUN: llvm-objdump -Dr %t.o | FileCheck %s
 
+declare void @baz()
+
+define i32 @padding() {
+  entry:
+  ret i32 55
+}
+
 define signext i32 @bar() {
 entry:
   ret i32 42
@@ -14,9 +21,12 @@ define signext i32 @foo() {
 entry:
   call void asm sideeffect ".space 0x2000100", ""()
   %call = call signext i32 @bar()
+  call void @baz()
   ret i32 %call
 }
 
-; ERROR: error: branch target out of range (-33554732 not between -33554432 and 33554428)
-; CHECK: 200012c: 49 ff fe d5   bl 0x4000000 <.bar+0x3ffffe0>
-; CHECK:                        0200012c:  R_RBR        .bar
+; ERROR: error: branch target out of range (-33554736 not between -33554432 and 33554428)
+; CHECK: 2000170: 49 ff fe d1   bl 0x4000040
+; CHECK:                        02000170:  R_RBR        .bar
+; CHECK: 200017c: 49 ff fe 85   bl 0x4000000
+; CHECK:                        0200017c:  R_RBR        .baz

>From f9703597003d3922bb494446f9c703e53a6233f2 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Wed, 15 Jul 2026 15:02:54 -0400
Subject: [PATCH 3/6] Fix comments on haveDifferentSections.

---
 llvm/lib/MC/XCOFFObjectWriter.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp
index ad7c3f5365cc1..9ecbfdcb00249 100644
--- a/llvm/lib/MC/XCOFFObjectWriter.cpp
+++ b/llvm/lib/MC/XCOFFObjectWriter.cpp
@@ -553,18 +553,17 @@ static MCSectionXCOFF *getContainingCsect(const MCSymbolXCOFF *XSym) {
   return XSym->getRepresentedCsect();
 }
 
-// Checks that the branhc target symbol has a different section then the one
-// containing the fragment F.
+// Checks that the section that contains the fragment F and the section
+// that contains the Target are different.
 static bool haveDifferentSections(const MCFragment &F, MCValue Target) {
   const MCSymbol *TargetSym = Target.getAddSym();
   if (!TargetSym)
     return false;
 
-  // Branch to an externally defined symbol.
+  // Target is externally defined.
   if (!TargetSym->isDefined())
     return true;
 
-  // Branch across sections.
   const MCSection *FragSection = F.getParent();
   const MCSection *TargetSection = TargetSym->getFragment()->getParent();
   return TargetSection != FragSection;

>From 8b5714a3670aaf8beec8a018358f922f280c8721 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Tue, 21 Jul 2026 13:34:02 -0400
Subject: [PATCH 4/6] No need to mask input to SignExtend64.

---
 llvm/lib/MC/XCOFFObjectWriter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp
index 9ecbfdcb00249..ebbd808bca0a0 100644
--- a/llvm/lib/MC/XCOFFObjectWriter.cpp
+++ b/llvm/lib/MC/XCOFFObjectWriter.cpp
@@ -580,7 +580,7 @@ static uint64_t NormalizeCallOffset(MCFixupKindInfo Info, uint64_t Offset) {
   if (Info.TargetSize != 24)
     report_fatal_error("Unexepected call fixup kind for XCOFF");
 
-  return llvm::SignExtend64<26>(Offset & 0x3ffffff);
+  return llvm::SignExtend64<26>(Offset);
 }
 
 void XCOFFWriter::executePostLayoutBinding() {

>From 8c7e446bbe1dd87fa26da9a8bfad11b436f9f8dd Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Wed, 19 Aug 2026 10:09:40 -0400
Subject: [PATCH 5/6] Apply suggestion from @nikic

Co-authored-by: Nikita Popov <github at npopov.com>
---
 llvm/lib/MC/XCOFFObjectWriter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp
index ebbd808bca0a0..1b97d367e3cda 100644
--- a/llvm/lib/MC/XCOFFObjectWriter.cpp
+++ b/llvm/lib/MC/XCOFFObjectWriter.cpp
@@ -569,7 +569,7 @@ static bool haveDifferentSections(const MCFragment &F, MCValue Target) {
   return TargetSection != FragSection;
 }
 
-static bool CallOverflows(MCFixupKindInfo Info, uint64_t FixedValue) {
+static bool callOverflows(MCFixupKindInfo Info, uint64_t FixedValue) {
   if (Info.TargetSize != 24)
     report_fatal_error("Unexepected call fixup kind for XCOFF");
 

>From 269d7e904aadae5112ddbd6bd68c2eac7ecd52a9 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Wed, 19 Aug 2026 13:59:20 -0400
Subject: [PATCH 6/6] Properly camelcase new function names.

---
 llvm/lib/MC/XCOFFObjectWriter.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp
index 1b97d367e3cda..e634de233b279 100644
--- a/llvm/lib/MC/XCOFFObjectWriter.cpp
+++ b/llvm/lib/MC/XCOFFObjectWriter.cpp
@@ -576,7 +576,7 @@ static bool callOverflows(MCFixupKindInfo Info, uint64_t FixedValue) {
   return !isIntN(26, FixedValue);
 }
 
-static uint64_t NormalizeCallOffset(MCFixupKindInfo Info, uint64_t Offset) {
+static uint64_t normalizeCallOffset(MCFixupKindInfo Info, uint64_t Offset) {
   if (Info.TargetSize != 24)
     report_fatal_error("Unexepected call fixup kind for XCOFF");
 
@@ -796,8 +796,8 @@ void XCOFFWriter::recordRelocation(const MCFragment &F, const MCFixup &Fixup,
     // crosses a CSECT boundary the linker may fix the overflow by using a
     // trampoline, or rearranging the sections in the output file.
     MCFixupKindInfo Info = Asm->getBackend().getFixupKindInfo(Fixup.getKind());
-    if (CallOverflows(Info, FixedValue) && haveDifferentSections(F, Target))
-      FixedValue = NormalizeCallOffset(Info, FixedValue);
+    if (callOverflows(Info, FixedValue) && haveDifferentSections(F, Target))
+      FixedValue = normalizeCallOffset(Info, FixedValue);
   } else if (Type == XCOFF::RelocationType::R_REF) {
     // The FixedValue and FixupOffsetInCsect should always be 0 since it
     // specifies a nonrelocating reference.



More information about the llvm-commits mailing list