https://github.com/Jiang-XueZhi created https://github.com/llvm/llvm-project/pull/200221
Fix #133246 Emit `__attribute__((constructor))` functions into `.text.startup` and `__attribute__((destructor))` functions into `.text.exit` on ELF targets, matching GCC's behavior. GCC groups constructor functions in `.text.startup` and destructor functions in `.text.exit`. This improves memory utilization at runtime: - Startup-only code is aggregated into a contiguous region, allowing the operating system to load it as a unit during initialization and subsequently reclaim those pages. - Exit-only code is similarly isolated, preventing it from polluting the hot code path. - ibache utilization is improved because code that runs only once (at startup or exit) does not displace frequently-executed code from the instruction cache. Clang currently places both constructor and destructor attribute functions in the generic `.text` section alongside all other code, losing this optimization opportunity entirely. In systems with many constructor attribute functions, this fragmentation causes measurable memory pressure: constructor bodies scattered throughout `.text` remain resident long after initialization completes, increasing the resident set size and the risk of future page faults. >From a12d794403b9161c16db76a0db15fdaddbc4c588 Mon Sep 17 00:00:00 2001 From: Jiang-XueZhi <[email protected]> Date: Fri, 29 May 2026 00:34:30 +0800 Subject: [PATCH] [Clang] Place constructor/destructor functions in .text.startup/.text.exit Emit `__attribute__((constructor))` functions into `.text.startup` and `__attribute__((destructor))` functions into `.text.exit` on ELF targets, matching GCC's behavior. GCC groups constructor functions in `.text.startup` and destructor functions in `.text.exit`. This improves memory utilization at runtime: - Startup-only code is aggregated into a contiguous region, allowing the operating system to load it as a unit during initialization and subsequently reclaim those pages. - Exit-only code is similarly isolated, preventing it from polluting the hot code path. - ibache utilization is improved because code that runs only once (at startup or exit) does not displace frequently-executed code from the instruction cache. Clang currently places both constructor and destructor attribute functions in the generic `.text` section alongside all other code, losing this optimization opportunity entirely. In systems with many constructor attribute functions, this fragmentation causes measurable memory pressure: constructor bodies scattered throughout `.text` remain resident long after initialization completes, increasing the resident set size and the risk of future page faults. --- clang/lib/CodeGen/CodeGenModule.cpp | 15 ++++++++++ .../test/CodeGen/constructor-section-prefix.c | 28 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 clang/test/CodeGen/constructor-section-prefix.c diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b8841f75a5c19..741252df74acc 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6860,6 +6860,18 @@ void CodeGenModule::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { EmitTopLevelDecl(VD); } +/// Return the ELF section prefix for a function based on the function's +/// code-region affinity: "startup" for functions that run exclusively at +/// program startup, "exit" for functions that run exclusively at program +/// exit. Returns nullptr when the function has no such affinity. +static const char *getCodeRegionSectionPrefix(const FunctionDecl *D) { + if (D->hasAttr<ConstructorAttr>()) + return "startup"; + if (D->hasAttr<DestructorAttr>()) + return "exit"; + return nullptr; +} + void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { const auto *D = cast<FunctionDecl>(GD.getDecl()); @@ -6929,6 +6941,9 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD, AddGlobalCtor(Fn, GetPriority(CA)); if (const DestructorAttr *DA = D->getAttr<DestructorAttr>()) AddGlobalDtor(Fn, GetPriority(DA), true); + if (!Fn->hasSection()) + if (const char *Prefix = getCodeRegionSectionPrefix(D)) + Fn->setSectionPrefix(Prefix); if (getLangOpts().OpenMP && D->hasAttr<OMPDeclareTargetDeclAttr>()) getOpenMPRuntime().emitDeclareTargetFunction(D, GV); } diff --git a/clang/test/CodeGen/constructor-section-prefix.c b/clang/test/CodeGen/constructor-section-prefix.c new file mode 100644 index 0000000000000..851092d69d474 --- /dev/null +++ b/clang/test/CodeGen/constructor-section-prefix.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-IR %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -S -ffunction-sections %s -o - | FileCheck --check-prefix=CHECK-ASM %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-EXPLICIT %s + +// CHECK-IR: define{{.*}} void @plain_ctor(){{.*}} !section_prefix ![[CTOR:[0-9]+]] +// CHECK-IR: define{{.*}} void @plain_dtor(){{.*}} !section_prefix ![[DTOR:[0-9]+]] +// CHECK-IR: define{{.*}} void @ctor_prio(){{.*}} !section_prefix ![[CTOR]] +// CHECK-IR: define{{.*}} void @dtor_prio(){{.*}} !section_prefix ![[DTOR]] +// CHECK-IR: ![[CTOR]] = !{!"section_prefix", !"startup"} +// CHECK-IR: ![[DTOR]] = !{!"section_prefix", !"exit"} + +// CHECK-ASM: .section .text.startup.plain_ctor,"ax",@progbits +// CHECK-ASM: .section .text.exit.plain_dtor,"ax",@progbits +// CHECK-ASM: .section .text.startup.ctor_prio,"ax",@progbits +// CHECK-ASM: .section .text.exit.dtor_prio,"ax",@progbits + +// CHECK-EXPLICIT: define{{.*}} void @ctor_explicit_section(){{.*}} section ".my_section" +// CHECK-EXPLICIT-NOT: define{{.*}} void @ctor_explicit_section(){{.*}} section_prefix + +void __attribute__((constructor)) plain_ctor(void) {} + +void __attribute__((destructor)) plain_dtor(void) {} + +void __attribute__((constructor(101))) ctor_prio(void) {} + +void __attribute__((destructor(202))) dtor_prio(void) {} + +void __attribute__((constructor, section(".my_section"))) ctor_explicit_section(void) {} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
