[llvm] [x86] Handle implicit sections when determining if a global is large (PR #206210)
Arthur Eubanks via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 22 15:49:02 PDT 2026
https://github.com/aeubanks updated https://github.com/llvm/llvm-project/pull/206210
>From e2cd05387af9ffb6d7e479b6d7dd526d477dbd11 Mon Sep 17 00:00:00 2001
From: Arthur Eubanks <aeubanks at google.com>
Date: Wed, 24 Jun 2026 17:31:23 -0700
Subject: [PATCH 1/2] [x86] Handle implicit sections when determining if a
global is large
Reland of #204247, with a fix to not crash on declarations with an
explicit section. Implicit sections are not applied to declarations, so
don't check isDeclarationForLinker() globals for implicit sections.
Just like explicit sections.
We were seeing globals with implicit sections marked large under the
medium code model.
Assisted-by: Gemini
---
llvm/include/llvm/IR/GlobalVariable.h | 2 +
.../llvm/Target/TargetLoweringObjectFile.h | 5 +
.../CodeGen/TargetLoweringObjectFileImpl.cpp | 29 +---
llvm/lib/Target/TargetLoweringObjectFile.cpp | 21 +++
llvm/lib/Target/TargetMachine.cpp | 21 +--
...l-variable-declaration-explicit-section.ll | 25 ++++
.../CodeGen/X86/large-implicit-section.ll | 134 ++++++++++++++++++
7 files changed, 205 insertions(+), 32 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/global-variable-declaration-explicit-section.ll
create mode 100644 llvm/test/CodeGen/X86/large-implicit-section.ll
diff --git a/llvm/include/llvm/IR/GlobalVariable.h b/llvm/include/llvm/IR/GlobalVariable.h
index 5124b3f2206d2..2488173ee0565 100644
--- a/llvm/include/llvm/IR/GlobalVariable.h
+++ b/llvm/include/llvm/IR/GlobalVariable.h
@@ -277,6 +277,8 @@ class GlobalVariable : public GlobalObject, public ilist_node<GlobalVariable> {
/// Check if section name is present
bool hasImplicitSection() const {
+ if (isDeclarationForLinker())
+ return false;
return getAttributes().hasAttribute("bss-section") ||
getAttributes().hasAttribute("data-section") ||
getAttributes().hasAttribute("relro-section") ||
diff --git a/llvm/include/llvm/Target/TargetLoweringObjectFile.h b/llvm/include/llvm/Target/TargetLoweringObjectFile.h
index 800bbe45c6a97..3cce5974e6705 100644
--- a/llvm/include/llvm/Target/TargetLoweringObjectFile.h
+++ b/llvm/include/llvm/Target/TargetLoweringObjectFile.h
@@ -132,6 +132,11 @@ class LLVM_ABI TargetLoweringObjectFile : public MCObjectFileInfo {
static SectionKind getKindForGlobal(const GlobalObject *GO,
const TargetMachine &TM);
+ /// Return the section name specified by '#pragma clang section' or the
+ /// section attribute.
+ static StringRef getCustomSectionName(const GlobalObject *GO,
+ const TargetMachine &TM);
+
/// This method computes the appropriate section to emit the specified global
/// variable or function definition. This should not be passed external (or
/// available externally) globals.
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index f983c3205f927..394fcae311220 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -816,34 +816,14 @@ getGlobalObjectInfo(const GlobalObject *GO, const TargetMachine &TM,
return {Group, IsComdat, Flags, Type, EntrySize};
}
-static StringRef handlePragmaClangSection(const GlobalObject *GO,
- SectionKind Kind) {
- // Check if '#pragma clang section' name is applicable.
- // Note that pragma directive overrides -ffunction-section, -fdata-section
- // and so section name is exactly as user specified and not uniqued.
- const GlobalVariable *GV = dyn_cast<GlobalVariable>(GO);
- if (GV && GV->hasImplicitSection()) {
- auto Attrs = GV->getAttributes();
- if (Attrs.hasAttribute("bss-section") && Kind.isBSS())
- return Attrs.getAttribute("bss-section").getValueAsString();
- else if (Attrs.hasAttribute("rodata-section") && Kind.isReadOnly())
- return Attrs.getAttribute("rodata-section").getValueAsString();
- else if (Attrs.hasAttribute("relro-section") && Kind.isReadOnlyWithRel())
- return Attrs.getAttribute("relro-section").getValueAsString();
- else if (Attrs.hasAttribute("data-section") && Kind.isData())
- return Attrs.getAttribute("data-section").getValueAsString();
- }
-
- return GO->getSection();
-}
-
static MCSection *selectExplicitSectionGlobal(const GlobalObject *GO,
SectionKind Kind,
const TargetMachine &TM,
MCContext &Ctx, Mangler &Mang,
unsigned &NextUniqueID,
bool Retain, bool ForceUnique) {
- StringRef SectionName = handlePragmaClangSection(GO, Kind);
+ StringRef SectionName =
+ TargetLoweringObjectFile::getCustomSectionName(GO, TM);
// Infer section flags from the section name if we can.
Kind = getELFKindForNamedSection(SectionName, Kind);
@@ -1384,7 +1364,8 @@ static void checkMachOComdat(const GlobalValue *GV) {
MCSection *TargetLoweringObjectFileMachO::getExplicitSectionGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
- StringRef SectionName = handlePragmaClangSection(GO, Kind);
+ StringRef SectionName =
+ TargetLoweringObjectFile::getCustomSectionName(GO, TM);
// Parse the section specifier and create it if valid.
StringRef Segment, Section;
@@ -1750,7 +1731,7 @@ static int getSelectionForCOFF(const GlobalValue *GV) {
MCSection *TargetLoweringObjectFileCOFF::getExplicitSectionGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
- StringRef Name = handlePragmaClangSection(GO, Kind);
+ StringRef Name = TargetLoweringObjectFile::getCustomSectionName(GO, TM);
if (Name == getInstrProfSectionName(IPSK_covmap, Triple::COFF,
/*AddSegmentInfo=*/false) ||
Name == getInstrProfSectionName(IPSK_covfun, Triple::COFF,
diff --git a/llvm/lib/Target/TargetLoweringObjectFile.cpp b/llvm/lib/Target/TargetLoweringObjectFile.cpp
index 43649a0cd95c7..286f183daf979 100644
--- a/llvm/lib/Target/TargetLoweringObjectFile.cpp
+++ b/llvm/lib/Target/TargetLoweringObjectFile.cpp
@@ -13,6 +13,7 @@
#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/IR/Attributes.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
@@ -368,6 +369,26 @@ SectionKind TargetLoweringObjectFile::getKindForGlobal(const GlobalObject *GO,
return SectionKind::getData();
}
+StringRef
+TargetLoweringObjectFile::getCustomSectionName(const GlobalObject *GO,
+ const TargetMachine &TM) {
+ const GlobalVariable *GV = dyn_cast<GlobalVariable>(GO);
+ if (GV && GV->hasImplicitSection()) {
+ SectionKind Kind = getKindForGlobal(GO, TM);
+ auto Attrs = GV->getAttributes();
+ if (Attrs.hasAttribute("bss-section") && Kind.isBSS())
+ return Attrs.getAttribute("bss-section").getValueAsString();
+ else if (Attrs.hasAttribute("rodata-section") && Kind.isReadOnly())
+ return Attrs.getAttribute("rodata-section").getValueAsString();
+ else if (Attrs.hasAttribute("relro-section") && Kind.isReadOnlyWithRel())
+ return Attrs.getAttribute("relro-section").getValueAsString();
+ else if (Attrs.hasAttribute("data-section") && Kind.isData())
+ return Attrs.getAttribute("data-section").getValueAsString();
+ }
+
+ return GO->getSection();
+}
+
/// This method computes the appropriate section to emit the specified global
/// variable or function definition. This should not be passed external (or
/// available externally) globals.
diff --git a/llvm/lib/Target/TargetMachine.cpp b/llvm/lib/Target/TargetMachine.cpp
index 9ec10b5be0fd4..7483a2e322368 100644
--- a/llvm/lib/Target/TargetMachine.cpp
+++ b/llvm/lib/Target/TargetMachine.cpp
@@ -98,14 +98,19 @@ bool TargetMachine::isLargeGlobalValue(const GlobalValue *GVal) const {
return true;
}
- // Treat all globals in explicit sections as small, except for the standard
- // large sections of .lbss, .ldata, .lrodata. This reduces the risk of linking
- // together small and large sections, resulting in small references to large
- // data sections. The code model attribute overrides this above.
- if (GV->hasSection()) {
- StringRef Name = GV->getSection();
- return IsPrefix(Name, ".lbss") || IsPrefix(Name, ".ldata") ||
- IsPrefix(Name, ".lrodata");
+ // Treat all globals in user-defined sections as small, except for the
+ // standard large sections of .lbss, .ldata, .lrodata. This reduces the risk
+ // of linking together small and large sections, resulting in small
+ // references to large data sections. The code model attribute overrides this
+ // above.
+ if (GV->hasSection() || GV->hasImplicitSection()) {
+ StringRef SectionName =
+ TargetLoweringObjectFile::getCustomSectionName(GV, *this);
+ if (!SectionName.empty()) {
+ return IsPrefix(SectionName, ".lbss") ||
+ IsPrefix(SectionName, ".ldata") ||
+ IsPrefix(SectionName, ".lrodata");
+ }
}
// Respect large data threshold for medium and large code models.
diff --git a/llvm/test/CodeGen/X86/global-variable-declaration-explicit-section.ll b/llvm/test/CodeGen/X86/global-variable-declaration-explicit-section.ll
new file mode 100644
index 0000000000000..321a8b65077e2
--- /dev/null
+++ b/llvm/test/CodeGen/X86/global-variable-declaration-explicit-section.ll
@@ -0,0 +1,25 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-linux-gnu -code-model=medium -relocation-model=pic < %s | FileCheck %s
+
+ at small_decl = external dso_local global i32, section ".data.foo"
+ at large_decl = external dso_local global i32, section ".ldata.foo"
+
+define i32 @test_small() {
+; CHECK-LABEL: test_small:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl small_decl(%rip), %eax
+; CHECK-NEXT: retq
+ %v = load i32, ptr @small_decl
+ ret i32 %v
+}
+
+define i32 @test_large() {
+; CHECK-LABEL: test_large:
+; CHECK: # %bb.0:
+; CHECK-NEXT: leaq _GLOBAL_OFFSET_TABLE_(%rip), %rax
+; CHECK-NEXT: movabsq $large_decl at GOTOFF, %rcx
+; CHECK-NEXT: movl (%rax,%rcx), %eax
+; CHECK-NEXT: retq
+ %v = load i32, ptr @large_decl
+ ret i32 %v
+}
diff --git a/llvm/test/CodeGen/X86/large-implicit-section.ll b/llvm/test/CodeGen/X86/large-implicit-section.ll
new file mode 100644
index 0000000000000..87e384c88c2ee
--- /dev/null
+++ b/llvm/test/CodeGen/X86/large-implicit-section.ll
@@ -0,0 +1,134 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-linux-gnu -code-model=medium -relocation-model=static -large-data-threshold=16 < %s | FileCheck %s --check-prefix=STATIC
+; RUN: llc -mtriple=x86_64-linux-gnu -code-model=medium -relocation-model=pic -large-data-threshold=16 < %s | FileCheck %s --check-prefix=PIC
+
+ at small_in_large_bss = dso_local global i32 0 "bss-section"=".lbss.my_bss"
+ at small_in_large_data = dso_local global i32 1 "data-section"=".ldata.my_data"
+ at small_in_large_rodata = dso_local constant i32 2 "rodata-section"=".lrodata.my_rodata"
+
+ at ptr = dso_local global i32 0
+ at small_in_large_relro = dso_local constant ptr @ptr "relro-section"=".ldata.rel.ro.my_relro"
+
+ at large_in_small_implicit_bss = dso_local global [4 x i64] zeroinitializer "bss-section"="my_bss"
+ at large_in_small_implicit_data = dso_local global [4 x i64] [i64 1, i64 0, i64 0, i64 0] "data-section"="my_data"
+ at large_in_small_implicit_rodata = dso_local constant [4 x i64] zeroinitializer "rodata-section"="my_rodata"
+ at large_in_small_implicit_relro = dso_local constant [4 x ptr] [ptr @ptr, ptr null, ptr null, ptr null] "relro-section"="my_relro"
+
+define i32 @load_bss() {
+; STATIC-LABEL: load_bss:
+; STATIC: # %bb.0:
+; STATIC-NEXT: movabsq $small_in_large_bss, %rax
+; STATIC-NEXT: movl (%rax), %eax
+; STATIC-NEXT: retq
+;
+; PIC-LABEL: load_bss:
+; PIC: # %bb.0:
+; PIC-NEXT: leaq _GLOBAL_OFFSET_TABLE_(%rip), %rax
+; PIC-NEXT: movabsq $.Lsmall_in_large_bss$local at GOTOFF, %rcx
+; PIC-NEXT: movl (%rax,%rcx), %eax
+; PIC-NEXT: retq
+ %v = load i32, ptr @small_in_large_bss
+ ret i32 %v
+}
+
+define i32 @load_data() {
+; STATIC-LABEL: load_data:
+; STATIC: # %bb.0:
+; STATIC-NEXT: movabsq $small_in_large_data, %rax
+; STATIC-NEXT: movl (%rax), %eax
+; STATIC-NEXT: retq
+;
+; PIC-LABEL: load_data:
+; PIC: # %bb.0:
+; PIC-NEXT: leaq _GLOBAL_OFFSET_TABLE_(%rip), %rax
+; PIC-NEXT: movabsq $.Lsmall_in_large_data$local at GOTOFF, %rcx
+; PIC-NEXT: movl (%rax,%rcx), %eax
+; PIC-NEXT: retq
+ %v = load i32, ptr @small_in_large_data
+ ret i32 %v
+}
+
+define i32 @load_rodata() {
+; STATIC-LABEL: load_rodata:
+; STATIC: # %bb.0:
+; STATIC-NEXT: movabsq $small_in_large_rodata, %rax
+; STATIC-NEXT: movl (%rax), %eax
+; STATIC-NEXT: retq
+;
+; PIC-LABEL: load_rodata:
+; PIC: # %bb.0:
+; PIC-NEXT: leaq _GLOBAL_OFFSET_TABLE_(%rip), %rax
+; PIC-NEXT: movabsq $.Lsmall_in_large_rodata$local at GOTOFF, %rcx
+; PIC-NEXT: movl (%rax,%rcx), %eax
+; PIC-NEXT: retq
+ %v = load i32, ptr @small_in_large_rodata
+ ret i32 %v
+}
+
+define ptr @load_relro() {
+; STATIC-LABEL: load_relro:
+; STATIC: # %bb.0:
+; STATIC-NEXT: movq small_in_large_relro(%rip), %rax
+; STATIC-NEXT: retq
+;
+; PIC-LABEL: load_relro:
+; PIC: # %bb.0:
+; PIC-NEXT: leaq _GLOBAL_OFFSET_TABLE_(%rip), %rax
+; PIC-NEXT: movabsq $.Lsmall_in_large_relro$local at GOTOFF, %rcx
+; PIC-NEXT: movq (%rax,%rcx), %rax
+; PIC-NEXT: retq
+ %v = load ptr, ptr @small_in_large_relro
+ ret ptr %v
+}
+
+define ptr @lea_large_in_small_implicit_bss() {
+; STATIC-LABEL: lea_large_in_small_implicit_bss:
+; STATIC: # %bb.0:
+; STATIC-NEXT: movl $large_in_small_implicit_bss, %eax
+; STATIC-NEXT: retq
+;
+; PIC-LABEL: lea_large_in_small_implicit_bss:
+; PIC: # %bb.0:
+; PIC-NEXT: leaq .Llarge_in_small_implicit_bss$local(%rip), %rax
+; PIC-NEXT: retq
+ ret ptr @large_in_small_implicit_bss
+}
+
+define ptr @lea_large_in_small_implicit_data() {
+; STATIC-LABEL: lea_large_in_small_implicit_data:
+; STATIC: # %bb.0:
+; STATIC-NEXT: movl $large_in_small_implicit_data, %eax
+; STATIC-NEXT: retq
+;
+; PIC-LABEL: lea_large_in_small_implicit_data:
+; PIC: # %bb.0:
+; PIC-NEXT: leaq .Llarge_in_small_implicit_data$local(%rip), %rax
+; PIC-NEXT: retq
+ ret ptr @large_in_small_implicit_data
+}
+
+define ptr @lea_large_in_small_implicit_rodata() {
+; STATIC-LABEL: lea_large_in_small_implicit_rodata:
+; STATIC: # %bb.0:
+; STATIC-NEXT: movl $large_in_small_implicit_rodata, %eax
+; STATIC-NEXT: retq
+;
+; PIC-LABEL: lea_large_in_small_implicit_rodata:
+; PIC: # %bb.0:
+; PIC-NEXT: leaq .Llarge_in_small_implicit_rodata$local(%rip), %rax
+; PIC-NEXT: retq
+ ret ptr @large_in_small_implicit_rodata
+}
+
+define ptr @lea_large_in_small_implicit_relro() {
+; STATIC-LABEL: lea_large_in_small_implicit_relro:
+; STATIC: # %bb.0:
+; STATIC-NEXT: movabsq $large_in_small_implicit_relro, %rax
+; STATIC-NEXT: retq
+;
+; PIC-LABEL: lea_large_in_small_implicit_relro:
+; PIC: # %bb.0:
+; PIC-NEXT: leaq .Llarge_in_small_implicit_relro$local(%rip), %rax
+; PIC-NEXT: retq
+ ret ptr @large_in_small_implicit_relro
+}
>From 53b131987c71f50510eaa99b94841a8ba25b3be3 Mon Sep 17 00:00:00 2001
From: Arthur Eubanks <aeubanks at google.com>
Date: Wed, 22 Jul 2026 22:48:27 +0000
Subject: [PATCH 2/2] add back comment
---
llvm/lib/Target/TargetLoweringObjectFile.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/llvm/lib/Target/TargetLoweringObjectFile.cpp b/llvm/lib/Target/TargetLoweringObjectFile.cpp
index 286f183daf979..f4c69cb8ad279 100644
--- a/llvm/lib/Target/TargetLoweringObjectFile.cpp
+++ b/llvm/lib/Target/TargetLoweringObjectFile.cpp
@@ -372,6 +372,9 @@ SectionKind TargetLoweringObjectFile::getKindForGlobal(const GlobalObject *GO,
StringRef
TargetLoweringObjectFile::getCustomSectionName(const GlobalObject *GO,
const TargetMachine &TM) {
+ // Check if '#pragma clang section' name is applicable.
+ // Note that pragma directive overrides -ffunction-section, -fdata-section
+ // and so section name is exactly as user specified and not uniqued.
const GlobalVariable *GV = dyn_cast<GlobalVariable>(GO);
if (GV && GV->hasImplicitSection()) {
SectionKind Kind = getKindForGlobal(GO, TM);
More information about the llvm-commits
mailing list