x86: Place functions in .ltext section under -mcmodel=large

Message ID 20260407040920.3452221-1-fmzakari@meta.com
State New
Headers
Series x86: Place functions in .ltext section under -mcmodel=large |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply

Commit Message

Farid Zakaria April 7, 2026, 4:09 a.m. UTC
  When using -mcmodel=large, code can reside anywhere in the 64-bit
address space.  Currently GCC places data into .ldata/.lbss/.lrodata
sections (which receive the SHF_X86_64_LARGE ELF flag), but functions
always go to .text regardless of code model.  This means .text does
not get the SHF_X86_64_LARGE flag, which can cause issues with linkers
and post-link tools that need to distinguish large sections.

This discrepancy between GCC and Clang came up while adding
-mcmodel=large support for BOLT (https://github.com/llvm/llvm-project/pull/190685).
The difference is visible at https://godbolt.org/z/rhsddfbKv — Clang
emits .ltext while GCC does not.

This patch adds .ltext support: under -mcmodel=large, functions are
emitted into .ltext (or .ltext.<name> with -ffunction-sections), which
GAS recognizes and marks with SHF_X86_64_LARGE.  This matches the
behavior of Clang/LLVM.

Only -mcmodel=large is affected; -mcmodel=medium continues to assume
code fits in the low 2GB and uses .text as before.

gcc/ChangeLog:

	* config/i386/i386.cc (ix86_function_section): New function.
	Returns .ltext section for CM_LARGE and CM_LARGE_PIC code
	models, delegates to default_function_section otherwise.
	(x86_64_elf_section_type_flags): Recognize .ltext, .ltext.*,
	and .gnu.linkonce.lt.* section names and set SECTION_LARGE.
	(x86_64_elf_unique_section): Handle FUNCTION_DECL under large
	code model by assigning .ltext.<name> section names.
	* config/i386/x86-64.h (TARGET_ASM_FUNCTION_SECTION): Define
	to ix86_function_section.

gcc/testsuite/ChangeLog:

	* gcc.target/i386/large-text.c: New test.
	* gcc.target/i386/large-text-fsections.c: New test.

Signed-off-by: Farid Zakaria <fmzakari@meta.com>
---
 gcc/config/i386/i386.cc                       | 43 +++++++++++++++++++
 gcc/config/i386/x86-64.h                      |  3 ++
 .../gcc.target/i386/large-text-fsections.c    |  7 +++
 gcc/testsuite/gcc.target/i386/large-text.c    |  7 +++
 4 files changed, 60 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/i386/large-text-fsections.c
 create mode 100644 gcc/testsuite/gcc.target/i386/large-text.c
  

Patch

diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 4c32e21788a..b2b2de18f07 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -832,6 +832,26 @@  x86_64_elf_select_section (tree decl, int reloc,
   return default_elf_select_section (decl, reloc, align);
 }
 
+/* Return the function section for DECL.  In the large code model,
+   use .ltext so that GAS applies SHF_X86_64_LARGE.  */
+
+static section * ATTRIBUTE_UNUSED
+ix86_function_section (tree decl, enum node_frequency freq,
+		       bool startup, bool exit)
+{
+  if (ix86_cmodel != CM_LARGE && ix86_cmodel != CM_LARGE_PIC)
+    return default_function_section (decl, freq, startup, exit);
+
+  /* If the function already has an explicit section name (from an
+     attribute or -ffunction-sections), let hot_function_section
+     pick it up -- x86_64_elf_unique_section already set it to
+     .ltext.<name>.  */
+  if (decl && DECL_SECTION_NAME (decl))
+    return NULL;
+
+  return get_named_section (decl, ".ltext", 0);
+}
+
 /* Select a set of attributes for section NAME based on the properties
    of DECL and whether or not RELOC indicates that DECL's initializer
    might contain runtime relocations.  */
@@ -854,6 +874,11 @@  x86_64_elf_section_type_flags (tree decl, const char *name, int reloc)
       || startswith (name, ".gnu.linkonce.lb."))
     flags |= SECTION_BSS;
 
+  if (strcmp (name, ".ltext") == 0
+      || startswith (name, ".ltext.")
+      || startswith (name, ".gnu.linkonce.lt."))
+    flags |= SECTION_LARGE;
+
   return flags;
 }
 
@@ -918,6 +943,24 @@  x86_64_elf_unique_section (tree decl, int reloc)
 	  return;
 	}
     }
+
+  /* In the large code model, place functions in .ltext sections.  */
+  if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC)
+      && TREE_CODE (decl) == FUNCTION_DECL)
+    {
+      const char *prefix;
+      bool one_only = DECL_COMDAT_GROUP (decl) && !HAVE_COMDAT_GROUP;
+      prefix = one_only ? ".lt" : ".ltext";
+
+      const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+      name = targetm.strip_name_encoding (name);
+      const char *linkonce = one_only ? ".gnu.linkonce" : "";
+      char *string = ACONCAT ((linkonce, prefix, ".", name, NULL));
+
+      set_decl_section_name (decl, string);
+      return;
+    }
+
   default_unique_section (decl, reloc);
 }
 
diff --git a/gcc/config/i386/x86-64.h b/gcc/config/i386/x86-64.h
index a223b701e6a..63ee8a1dad0 100644
--- a/gcc/config/i386/x86-64.h
+++ b/gcc/config/i386/x86-64.h
@@ -89,3 +89,6 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #undef TARGET_SECTION_TYPE_FLAGS
 #define TARGET_SECTION_TYPE_FLAGS  x86_64_elf_section_type_flags
+
+#undef TARGET_ASM_FUNCTION_SECTION
+#define TARGET_ASM_FUNCTION_SECTION  ix86_function_section
diff --git a/gcc/testsuite/gcc.target/i386/large-text-fsections.c b/gcc/testsuite/gcc.target/i386/large-text-fsections.c
new file mode 100644
index 00000000000..482deabf6fc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/large-text-fsections.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-O2 -mcmodel=large -ffunction-sections" } */
+/* { dg-skip-if "PR90698" "*-*-darwin*" } */
+/* { dg-final { scan-assembler {\.ltext\.foo} } } */
+
+void foo (void) {}
diff --git a/gcc/testsuite/gcc.target/i386/large-text.c b/gcc/testsuite/gcc.target/i386/large-text.c
new file mode 100644
index 00000000000..b76adddd78e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/large-text.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-options "-O2 -mcmodel=large" } */
+/* { dg-skip-if "PR90698" "*-*-darwin*" } */
+/* { dg-final { scan-assembler {\.ltext} } } */
+
+void foo (void) {}