From 69c8d08a06a7702057b22edd19a2cb5f9c79388a Mon Sep 17 00:00:00 2001 From: Sanjin Sijaric Date: Mon, 29 Sep 2025 11:38:58 -0700 Subject: [PATCH 1/3] Put large common blocks into .lbss for the medium and large code models For the medium and large code models on x86-64, the bss is split into two parts, .bss for regular sized data, and .lbss for data larger than that specified by -large-data-threshold (for the medium code model). This change tries to mimick what gcc does for the medium and large code models for Fortran COMMON blocks: program test implicit integer (i-n) implicit real*8 (a-h, o-z) parameter (n1=77777, n2=77777) common v2(n1,n2) common /ccc/ v6 v6 = 1 v2(6777,6777) = 2 write (*,*) v6, v2(6777, 6777) end program test Currently, we generate: 0000000000000008 O *COM* 0000000000000008 ccc_ 0000000b44834508 O *COM* 0000000000000008 __BLNK and ld and lld both fail to link with -mcmodel=medium. /usr/bin/ld: failed to convert GOTPCREL relocation; relink with --no-relax or ld.lld: error: /usr/lib/gcc/x86_64-redhat-linux/11/crtbeginS.o:(.text+0x76): relocation R_X86_64_PC32 out of range: 48394103061 is not in [-2147483648, 2147483647]; references section '.bss' With this change, __BLNK is marked as LARGE_COMMON (SHN_AMD64_LCOMMON), and ends up in .lbss. *.o: Sections: ... 1 .lbss 00000000 0000000000000000 0000000000000000 000000fb 2**0 ALLOC ... SYMBOL TABLE: 0000000000000008 O *COM* 0000000000000008 ccc_ 0000000b44834508 O LARGE_COMMON 0000000000000008 __BLNK__ a.out: Sections: ... 25 .bss 00000009 0000000000003cf0 0000000000003cf0 00000cf0 2**3 ALLOC 26 .lbss b44834508 0000000000003d00 0000000000003d00 00000cf0 2**3 ALLOC SYMBOL TABLE: 0000000000003cf0 g O .bss 0000000000000008 ccc_ 0000000000003d00 g O .lbss 0000000b44834508 __BLNK__ In the assembly, this will appear as: .section .lbss,"awl",@nobits .type __BLNK__,@object .largecomm __BLNK__,48394093832,8 .type ccc_,@object .comm ccc_,8,8 For the large code model, all common blocks will end up in lbss. Previously, the above example wouldn't link statically with GNU ld. The change also includes support for parsing the largecomm directive. There will be a PR for the chages to lld to handle the SHN_AMD64_LCOMMON section. --- llvm/include/llvm/BinaryFormat/ELF.h | 5 ++ llvm/include/llvm/MC/MCELFStreamer.h | 2 + llvm/include/llvm/MC/MCStreamer.h | 8 +++ llvm/include/llvm/MC/MCSymbolELF.h | 4 ++ llvm/include/llvm/Object/ELFTypes.h | 2 + .../llvm/Target/TargetLoweringObjectFile.h | 6 ++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 17 ++++-- llvm/lib/MC/ELFObjectWriter.cpp | 6 +- llvm/lib/MC/MCAsmStreamer.cpp | 16 ++++- llvm/lib/MC/MCELFStreamer.cpp | 17 ++++++ llvm/lib/MC/MCParser/ELFAsmParser.cpp | 50 ++++++++++++++++ llvm/lib/MC/MCStreamer.cpp | 2 + llvm/lib/Target/X86/X86TargetObjectFile.cpp | 13 +++++ llvm/lib/Target/X86/X86TargetObjectFile.h | 3 + llvm/test/MC/X86/largecomm.ll | 58 +++++++++++++++++++ llvm/tools/llvm-readobj/ELFDumper.cpp | 5 ++ 16 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 llvm/test/MC/X86/largecomm.ll diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index e619b186dfe3d..ca6ec30e1f95e 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -589,6 +589,11 @@ enum { SHN_MIPS_SUNDEFINED = 0xff04 // Undefined symbols for global data area }; +// x86-64 speciifc section index +enum { + SHN_AMD64_LCOMMON = 0xff02, // Large FORTRAN COMMON variables +}; + // ELF Relocation types for Mips enum { #include "ELFRelocs/Mips.def" diff --git a/llvm/include/llvm/MC/MCELFStreamer.h b/llvm/include/llvm/MC/MCELFStreamer.h index 144f6bc3bd91c..1b6e4273b1e28 100644 --- a/llvm/include/llvm/MC/MCELFStreamer.h +++ b/llvm/include/llvm/MC/MCELFStreamer.h @@ -56,6 +56,8 @@ class MCELFStreamer : public MCObjectStreamer { bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, Align ByteAlignment) override; + void emitLargeCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment) override; void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override; void emitELFSymverDirective(const MCSymbol *OriginalSym, StringRef Name, diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 79c715e3820a6..194cc799d5650 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -677,6 +677,14 @@ class LLVM_ABI MCStreamer { virtual void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, Align ByteAlignment) = 0; + /// Emit a large common (.largecomm) symbol. + /// + /// \param Symbol - The common symbol to emit. + /// \param Size - The size of the common symbol. + /// \param ByteAlignment - The alignment of the common symbol in bytes. + virtual void emitLargeCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment); + /// Emit a local common (.lcomm) symbol. /// /// \param Symbol - The common symbol to emit. diff --git a/llvm/include/llvm/MC/MCSymbolELF.h b/llvm/include/llvm/MC/MCSymbolELF.h index 1af175a18e8ec..e2377eff688c6 100644 --- a/llvm/include/llvm/MC/MCSymbolELF.h +++ b/llvm/include/llvm/MC/MCSymbolELF.h @@ -17,6 +17,7 @@ class MCSymbolELF : public MCSymbol { /// An expression describing how to calculate the size of a symbol. If a /// symbol has no size this field will be NULL. const MCExpr *SymbolSize = nullptr; + bool IsLargeCommon = false; public: MCSymbolELF(const MCSymbolTableEntry *Name, bool isTemporary) @@ -48,6 +49,9 @@ class MCSymbolELF : public MCSymbol { void setMemtag(bool Tagged); bool isMemtag() const; + bool isLargeCommon() const { return IsLargeCommon; } + void setIsLargeCommon(bool V) { IsLargeCommon = V; } + private: void setIsBindingSet() const; }; diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h index 5a26e2fc31458..ed4695dccb6a8 100644 --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -252,6 +252,8 @@ struct Elf_Sym_Impl : Elf_Sym_Base { return getType() == ELF::STT_COMMON || st_shndx == ELF::SHN_COMMON; } + bool isLargeCommon() const { return st_shndx == ELF::SHN_AMD64_LCOMMON; } + bool isDefined() const { return !isUndefined(); } bool isProcessorSpecific() const { diff --git a/llvm/include/llvm/Target/TargetLoweringObjectFile.h b/llvm/include/llvm/Target/TargetLoweringObjectFile.h index 4d6cbc5540131..d1c16a3f4578f 100644 --- a/llvm/include/llvm/Target/TargetLoweringObjectFile.h +++ b/llvm/include/llvm/Target/TargetLoweringObjectFile.h @@ -305,6 +305,12 @@ class LLVM_ABI TargetLoweringObjectFile : public MCObjectFileInfo { return nullptr; } + virtual MCSection *LargeSectionForCommon(const GlobalObject *GO, + SectionKind Kind, + const TargetMachine &TM) const { + return nullptr; + } + protected: virtual MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind, diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 701a6a2f0f7a0..45ee5fd9003d8 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -782,11 +782,18 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { OutContext.reportError(SMLoc(), "symbol '" + Twine(GVSym->getName()) + "' is already defined"); + SectionKind GVKind = TargetLoweringObjectFile::getKindForGlobal(GV, TM); + + // Place the large common variables in the .lbss section for the medium and + // large code models on x86_64. (AMD64 ABI, 9.2.5 COMMON blocks) + MCSection *largeCommonSec = + getObjFileLowering().LargeSectionForCommon(GV, GVKind, TM); + if (largeCommonSec) + OutStreamer->switchSection(largeCommonSec); + if (MAI->hasDotTypeDotSizeDirective()) OutStreamer->emitSymbolAttribute(EmittedSym, MCSA_ELF_TypeObject); - SectionKind GVKind = TargetLoweringObjectFile::getKindForGlobal(GV, TM); - const DataLayout &DL = GV->getDataLayout(); uint64_t Size = DL.getTypeAllocSize(GV->getValueType()); @@ -802,10 +809,12 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { if (GVKind.isCommon()) { if (Size == 0) Size = 1; // .comm Foo, 0 is undefined, avoid it. // .comm _foo, 42, 4 - OutStreamer->emitCommonSymbol(GVSym, Size, Alignment); + if (largeCommonSec) + OutStreamer->emitLargeCommonSymbol(GVSym, Size, Alignment); + else + OutStreamer->emitCommonSymbol(GVSym, Size, Alignment); return; } - // Determine to which section this global should be emitted. MCSection *TheSection = getObjFileLowering().SectionForGlobal(GV, GVKind, TM); diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index 759d3e0e14291..aa19e92ad74d3 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -27,6 +27,7 @@ #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSectionELF.h" @@ -544,7 +545,10 @@ void ELFWriter::computeSymbolTable(const RevGroupMapTy &RevGroupMap) { auto Shndx = Symbol.getIndex(); if (!Shndx) { assert(!Local); - Shndx = ELF::SHN_COMMON; + if (Symbol.isLargeCommon()) + Shndx = ELF::SHN_AMD64_LCOMMON; + else + Shndx = ELF::SHN_COMMON; } MSD.SectionIndex = Shndx; } else if (Symbol.isUndefined()) { diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index be8c022f39ad1..903ddcc8cdd39 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -228,7 +228,8 @@ class MCAsmStreamer final : public MCStreamer { void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override; void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, Align ByteAlignment) override; - + void emitLargeCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment) override; /// Emit a local common (.lcomm) symbol. /// /// @param Symbol - The common symbol to emit. @@ -1075,6 +1076,19 @@ void MCAsmStreamer::emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, } } +void MCAsmStreamer::emitLargeCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment) { + OS << "\t.largecomm\t"; + Symbol->print(OS, MAI); + OS << ',' << Size; + + if (MAI->getCOMMDirectiveAlignmentIsInBytes()) + OS << ',' << ByteAlignment.value(); + else + OS << ',' << Log2(ByteAlignment); + EmitEOL(); +} + void MCAsmStreamer::emitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, Align ByteAlign) { OS << "\t.lcomm\t"; diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp index 2881d7cfab4ba..8314fe4d2f14c 100644 --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -244,6 +244,23 @@ bool MCELFStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) { return true; } +void MCELFStreamer::emitLargeCommonSymbol(MCSymbol *S, uint64_t Size, + Align ByteAlignment) { + auto *Symbol = static_cast(S); + getAssembler().registerSymbol(*Symbol); + + if (!Symbol->isBindingSet()) + Symbol->setBinding(ELF::STB_GLOBAL); + + Symbol->setType(ELF::STT_OBJECT); + + if (Symbol->declareCommon(Size, ByteAlignment)) + report_fatal_error(Twine("Symbol: ") + Symbol->getName() + + " redeclared as different type"); + Symbol->setIsLargeCommon(true); + Symbol->setSize(MCConstantExpr::create(Size, getContext())); +} + void MCELFStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size, Align ByteAlignment) { auto *Symbol = static_cast(S); diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp index 6195355626fd5..97287b01d1b4f 100644 --- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp +++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp @@ -77,6 +77,7 @@ class ELFAsmParser : public MCAsmParserExtension { &ELFAsmParser::parseDirectiveSymbolAttribute>(".hidden"); addDirectiveHandler<&ELFAsmParser::parseDirectiveSubsection>(".subsection"); addDirectiveHandler<&ELFAsmParser::parseDirectiveCGProfile>(".cg_profile"); + addDirectiveHandler<&ELFAsmParser::parseDirectiveLargecomm>(".largecomm"); } // FIXME: Part of this logic is duplicated in the MCELFStreamer. What is @@ -126,6 +127,7 @@ class ELFAsmParser : public MCAsmParserExtension { bool parseDirectiveSymbolAttribute(StringRef, SMLoc); bool parseDirectiveSubsection(StringRef, SMLoc); bool parseDirectiveCGProfile(StringRef, SMLoc); + bool parseDirectiveLargecomm(StringRef, SMLoc); private: bool parseSectionName(StringRef &SectionName); @@ -886,6 +888,54 @@ bool ELFAsmParser::parseDirectiveCGProfile(StringRef S, SMLoc Loc) { return MCAsmParserExtension::parseDirectiveCGProfile(S, Loc); } +bool ELFAsmParser::parseDirectiveLargecomm(StringRef s, SMLoc Loc) { + if (getParser().checkForValidSection()) + return true; + + MCSymbol *Sym; + if (getParser().parseSymbol(Sym)) + return TokError("expected identifier in directive"); + + if (getLexer().isNot(AsmToken::Comma)) + return TokError("expected a comma"); + Lex(); + + int64_t Size; + SMLoc SizeLoc = getLexer().getLoc(); + if (getParser().parseAbsoluteExpression(Size)) + return true; + + int64_t Pow2Alignment = 0; + SMLoc Pow2AlignmentLoc; + if (getLexer().is(AsmToken::Comma)) { + Lex(); + Pow2AlignmentLoc = getLexer().getLoc(); + if (getParser().parseAbsoluteExpression(Pow2Alignment)) + return true; + + // If this target takes alignments in bytes (not log) validate and convert. + if (getLexer().getMAI().getCOMMDirectiveAlignmentIsInBytes()) { + if (!isPowerOf2_64(Pow2Alignment)) + return Error(Pow2AlignmentLoc, "alignment must be a power of 2"); + Pow2Alignment = Log2_64(Pow2Alignment); + } + } + + if (parseEOL()) + return true; + + if (Size < 0) + return Error(SizeLoc, "size must be non-negative"); + + Sym->redefineIfPossible(); + if (!Sym->isUndefined()) + return Error(Loc, "invalid symbol redefinition"); + + getStreamer().emitLargeCommonSymbol(Sym, Size, Align(1ULL << Pow2Alignment)); + + return false; +} + namespace llvm { MCAsmParserExtension *createELFAsmParser() { diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index bc7398120096e..f4f7d78ec557f 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -1309,6 +1309,8 @@ void MCStreamer::emitELFSymverDirective(const MCSymbol *OriginalSym, StringRef Name, bool KeepOriginalSym) {} void MCStreamer::emitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, Align ByteAlignment) {} +void MCStreamer::emitLargeCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment) {} void MCStreamer::emitZerofill(MCSection *, MCSymbol *, uint64_t, Align, SMLoc) { } void MCStreamer::emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, diff --git a/llvm/lib/Target/X86/X86TargetObjectFile.cpp b/llvm/lib/Target/X86/X86TargetObjectFile.cpp index fc3c06fcc1c9b..60e6f090652de 100644 --- a/llvm/lib/Target/X86/X86TargetObjectFile.cpp +++ b/llvm/lib/Target/X86/X86TargetObjectFile.cpp @@ -8,8 +8,11 @@ #include "X86TargetObjectFile.h" #include "MCTargetDesc/X86MCAsmInfo.h" +#include "X86TargetMachine.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCValue.h" #include "llvm/Target/TargetMachine.h" @@ -71,3 +74,13 @@ const MCExpr *X86_64ELFTargetObjectFile::getIndirectSymViaGOTPCRel( const MCExpr *Off = MCConstantExpr::create(FinalOffset, getContext()); return MCBinaryExpr::createAdd(Res, Off, getContext()); } + +MCSection *X86_64ELFTargetObjectFile::LargeSectionForCommon( + const GlobalObject *GV, SectionKind Kind, const TargetMachine &TM) const { + auto &X86_TM = static_cast(TM); + if (GV && Kind.isCommon() && TM.isLargeGlobalValue(GV) && !X86_TM.isJIT()) { + unsigned Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE | ELF::SHF_X86_64_LARGE; + return getContext().getELFSection(".lbss", ELF::SHT_NOBITS, Flags); + } + return nullptr; +} diff --git a/llvm/lib/Target/X86/X86TargetObjectFile.h b/llvm/lib/Target/X86/X86TargetObjectFile.h index cb096f4a9febb..4fc944302fcf6 100644 --- a/llvm/lib/Target/X86/X86TargetObjectFile.h +++ b/llvm/lib/Target/X86/X86TargetObjectFile.h @@ -56,6 +56,9 @@ namespace llvm { const MCValue &MV, int64_t Offset, MachineModuleInfo *MMI, MCStreamer &Streamer) const override; + + MCSection *LargeSectionForCommon(const GlobalObject *GO, SectionKind Kind, + const TargetMachine &TM) const override; }; } // end namespace llvm diff --git a/llvm/test/MC/X86/largecomm.ll b/llvm/test/MC/X86/largecomm.ll new file mode 100644 index 0000000000000..00fdfc9f361da --- /dev/null +++ b/llvm/test/MC/X86/largecomm.ll @@ -0,0 +1,58 @@ +; RUN: llc -filetype=asm -code-model=medium %s --large-data-threshold=65636 -o - | FileCheck %s --check-prefix=CHECKASM-MEDIUM +; RUN: llc -filetype=asm -code-model=large %s -o - | FileCheck %s --check-prefix=CHECKASM-LARGE +; RUN: llc -filetype=asm -code-model=medium %s --large-data-threshold=65636 -o - | llvm-mc -filetype=obj - | llvm-readelf -s - | FileCheck %s --check-prefix=CHECKOBJ-MEDIUM +; RUN: llc -filetype=asm -code-model=large %s -o - | llvm-mc -filetype=obj - | llvm-readelf -s - | FileCheck %s --check-prefix=CHECKOBJ-LARGE + +; CHECKASM-MEDIUM: .section .lbss,"awl",@nobits +; CHECKASM-MEDIUM-NEXT: .type __BLNK__,@object # @__BLNK__ +; CHECKASM-MEDIUM-NEXT: .largecomm __BLNK__,48394093832,8 +; CHECKASM-MEDIUM-NEXT: .type ccc_,@object # @ccc_ +; CHECKASM-MEDIUM-NEXT: .comm ccc_,8,8 + +; CHECKASM-LARGE: .section .lbss,"awl",@nobits +; CHECKASM-LARGE-NEXT: .type __BLNK__,@object # @__BLNK__ +; CHECKASM-LARGE-NEXT: .largecomm __BLNK__,48394093832,8 +; CHECKASM-LARGE-NEXT: .type ccc_,@object # @ccc_ +; CHECKASM-LARGE-NEXT: .largecomm ccc_,8,8 + +; CHECKOBJ-MEDIUM: 8 OBJECT GLOBAL DEFAULT COM ccc_ +; CHECKOBJ-MEDIUM: 48394093832 OBJECT GLOBAL DEFAULT LARGE_COMMON __BLNK + +; CHECKOBJ-LARGE: 8 OBJECT GLOBAL DEFAULT LARGE_COMMON ccc_ +; CHECKOBJ-LARGE: 48394093832 OBJECT GLOBAL DEFAULT LARGE_COMMON __BLNK__ + +source_filename = "FIRModule" +target triple = "x86_64-unknown-linux-gnu" + +@__BLNK__ = common global [48394093832 x i8] zeroinitializer, align 8 +@ccc_ = common global [8 x i8] zeroinitializer, align 8 +@_QFECn1 = internal constant i32 77777 +@_QFECn2 = internal constant i32 77777 + +define void @_QQmain() #0 { + store double 1.000000e+00, ptr @ccc_, align 8 + store double 2.000000e+00, ptr getelementptr inbounds nuw (i8, ptr @__BLNK__, i64 61600176), align 8 + ret void +} + +declare void @_FortranAProgramStart(i32, ptr, ptr, ptr) #1 + +declare void @_FortranAProgramEndStatement() #1 + +define i32 @main(i32 %0, ptr %1, ptr %2) #0 { + call void @_FortranAProgramStart(i32 %0, ptr %1, ptr %2, ptr null) + call void @_QQmain() + call void @_FortranAProgramEndStatement() + ret i32 0 +} + +attributes #0 = { "frame-pointer"="all" "target-cpu"="x86-64" } +attributes #1 = { "frame-pointer"="all" } + +!llvm.ident = !{!0} +!llvm.module.flags = !{!1, !2, !3} + +!0 = !{!"flang version 22.0.0 (https://github.com/llvm/llvm-project.git e1afe25356b8d2ee14f5f88bdb6c2a1526ed14ef)"} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = !{i32 8, !"PIC Level", i32 2} +!3 = !{i32 7, !"PIE Level", i32 2} diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index ab93316907cc6..7d34727f07f6d 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1052,6 +1052,8 @@ ELFDumper::getSymbolSectionIndex(const Elf_Sym &Symbol, unsigned SymIndex, return CreateErr("SHN_ABS"); if (Ndx == ELF::SHN_COMMON) return CreateErr("SHN_COMMON"); + if (Ndx == ELF::SHN_AMD64_LCOMMON) + return CreateErr("SHN_AMD64_LCOMMON"); return CreateErr("SHN_LORESERVE", Ndx - SHN_LORESERVE); } @@ -4308,6 +4310,9 @@ std::string GNUELFDumper::getSymbolSectionNdx( default: // Find if: // Processor specific + if (this->Obj.getHeader().e_machine == EM_X86_64 && + SectionIndex == ELF::SHN_AMD64_LCOMMON) + return "LARGE_COMMON"; if (SectionIndex >= ELF::SHN_LOPROC && SectionIndex <= ELF::SHN_HIPROC) return std::string("PRC[0x") + to_string(format_hex_no_prefix(SectionIndex, 4)) + "]"; From 7bff05f5670bdcf9ff041ca3e2dd8cdd0637fd16 Mon Sep 17 00:00:00 2001 From: Sanjin Sijaric Date: Wed, 1 Oct 2025 08:45:26 -0700 Subject: [PATCH 2/3] Fix the test case on Windows; change variable names --- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 8 ++++---- llvm/lib/MC/MCParser/ELFAsmParser.cpp | 2 +- llvm/test/MC/X86/largecomm.ll | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 45ee5fd9003d8..9240e18e12443 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -786,10 +786,10 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { // Place the large common variables in the .lbss section for the medium and // large code models on x86_64. (AMD64 ABI, 9.2.5 COMMON blocks) - MCSection *largeCommonSec = + MCSection *LargeCommonSec = getObjFileLowering().LargeSectionForCommon(GV, GVKind, TM); - if (largeCommonSec) - OutStreamer->switchSection(largeCommonSec); + if (LargeCommonSec) + OutStreamer->switchSection(LargeCommonSec); if (MAI->hasDotTypeDotSizeDirective()) OutStreamer->emitSymbolAttribute(EmittedSym, MCSA_ELF_TypeObject); @@ -809,7 +809,7 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { if (GVKind.isCommon()) { if (Size == 0) Size = 1; // .comm Foo, 0 is undefined, avoid it. // .comm _foo, 42, 4 - if (largeCommonSec) + if (LargeCommonSec) OutStreamer->emitLargeCommonSymbol(GVSym, Size, Alignment); else OutStreamer->emitCommonSymbol(GVSym, Size, Alignment); diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp index 97287b01d1b4f..237b430e3d7b4 100644 --- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp +++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp @@ -888,7 +888,7 @@ bool ELFAsmParser::parseDirectiveCGProfile(StringRef S, SMLoc Loc) { return MCAsmParserExtension::parseDirectiveCGProfile(S, Loc); } -bool ELFAsmParser::parseDirectiveLargecomm(StringRef s, SMLoc Loc) { +bool ELFAsmParser::parseDirectiveLargecomm(StringRef, SMLoc Loc) { if (getParser().checkForValidSection()) return true; diff --git a/llvm/test/MC/X86/largecomm.ll b/llvm/test/MC/X86/largecomm.ll index 00fdfc9f361da..52d51d562c47e 100644 --- a/llvm/test/MC/X86/largecomm.ll +++ b/llvm/test/MC/X86/largecomm.ll @@ -1,7 +1,7 @@ ; RUN: llc -filetype=asm -code-model=medium %s --large-data-threshold=65636 -o - | FileCheck %s --check-prefix=CHECKASM-MEDIUM ; RUN: llc -filetype=asm -code-model=large %s -o - | FileCheck %s --check-prefix=CHECKASM-LARGE -; RUN: llc -filetype=asm -code-model=medium %s --large-data-threshold=65636 -o - | llvm-mc -filetype=obj - | llvm-readelf -s - | FileCheck %s --check-prefix=CHECKOBJ-MEDIUM -; RUN: llc -filetype=asm -code-model=large %s -o - | llvm-mc -filetype=obj - | llvm-readelf -s - | FileCheck %s --check-prefix=CHECKOBJ-LARGE +; RUN: llc -filetype=asm -code-model=medium %s --large-data-threshold=65636 -o - | llvm-mc -triple x86_64-linux-gnu -filetype=obj - | llvm-readelf -s - | FileCheck %s --check-prefix=CHECKOBJ-MEDIUM +; RUN: llc -filetype=asm -code-model=large %s -o - | llvm-mc -triple x86_64-linux-gnu -filetype=obj - | llvm-readelf -s - | FileCheck %s --check-prefix=CHECKOBJ-LARGE ; CHECKASM-MEDIUM: .section .lbss,"awl",@nobits ; CHECKASM-MEDIUM-NEXT: .type __BLNK__,@object # @__BLNK__ From 8738b3e244ccc5e2aea993e9d5872c99d4df6c92 Mon Sep 17 00:00:00 2001 From: Sanjin Sijaric Date: Fri, 3 Oct 2025 15:45:55 -0700 Subject: [PATCH 3/3] Move the large common indicator; Rename SHN_AMD64_LCOMMON; Undo the ELFDumper change from this PR --- llvm/include/llvm/BinaryFormat/ELF.h | 2 +- llvm/include/llvm/MC/MCSymbolELF.h | 5 ++--- llvm/include/llvm/Object/ELFTypes.h | 2 +- llvm/lib/MC/ELFObjectWriter.cpp | 2 +- llvm/lib/MC/MCELFStreamer.cpp | 2 +- llvm/lib/MC/MCSymbolELF.cpp | 12 ++++++++++++ llvm/test/MC/X86/largecomm.ll | 8 -------- llvm/tools/llvm-readobj/ELFDumper.cpp | 5 ----- 8 files changed, 18 insertions(+), 20 deletions(-) diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index ca6ec30e1f95e..60e85ce5fae99 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -591,7 +591,7 @@ enum { // x86-64 speciifc section index enum { - SHN_AMD64_LCOMMON = 0xff02, // Large FORTRAN COMMON variables + SHN_X86_64_LCOMMON = 0xff02, // Large FORTRAN COMMON variables }; // ELF Relocation types for Mips diff --git a/llvm/include/llvm/MC/MCSymbolELF.h b/llvm/include/llvm/MC/MCSymbolELF.h index e2377eff688c6..7b48f312d04f0 100644 --- a/llvm/include/llvm/MC/MCSymbolELF.h +++ b/llvm/include/llvm/MC/MCSymbolELF.h @@ -17,7 +17,6 @@ class MCSymbolELF : public MCSymbol { /// An expression describing how to calculate the size of a symbol. If a /// symbol has no size this field will be NULL. const MCExpr *SymbolSize = nullptr; - bool IsLargeCommon = false; public: MCSymbolELF(const MCSymbolTableEntry *Name, bool isTemporary) @@ -49,8 +48,8 @@ class MCSymbolELF : public MCSymbol { void setMemtag(bool Tagged); bool isMemtag() const; - bool isLargeCommon() const { return IsLargeCommon; } - void setIsLargeCommon(bool V) { IsLargeCommon = V; } + void setIsLargeCommon(); + bool isLargeCommon() const; private: void setIsBindingSet() const; diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h index ed4695dccb6a8..b389fda625c25 100644 --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -252,7 +252,7 @@ struct Elf_Sym_Impl : Elf_Sym_Base { return getType() == ELF::STT_COMMON || st_shndx == ELF::SHN_COMMON; } - bool isLargeCommon() const { return st_shndx == ELF::SHN_AMD64_LCOMMON; } + bool isLargeCommon() const { return st_shndx == ELF::SHN_X86_64_LCOMMON; } bool isDefined() const { return !isUndefined(); } diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index aa19e92ad74d3..7465d1b2aee8f 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -546,7 +546,7 @@ void ELFWriter::computeSymbolTable(const RevGroupMapTy &RevGroupMap) { if (!Shndx) { assert(!Local); if (Symbol.isLargeCommon()) - Shndx = ELF::SHN_AMD64_LCOMMON; + Shndx = ELF::SHN_X86_64_LCOMMON; else Shndx = ELF::SHN_COMMON; } diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp index 8314fe4d2f14c..3e282f55a04a0 100644 --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -257,7 +257,7 @@ void MCELFStreamer::emitLargeCommonSymbol(MCSymbol *S, uint64_t Size, if (Symbol->declareCommon(Size, ByteAlignment)) report_fatal_error(Twine("Symbol: ") + Symbol->getName() + " redeclared as different type"); - Symbol->setIsLargeCommon(true); + Symbol->setIsLargeCommon(); Symbol->setSize(MCConstantExpr::create(Size, getContext())); } diff --git a/llvm/lib/MC/MCSymbolELF.cpp b/llvm/lib/MC/MCSymbolELF.cpp index 79774db0cc8e6..d1ef0143b0f2e 100644 --- a/llvm/lib/MC/MCSymbolELF.cpp +++ b/llvm/lib/MC/MCSymbolELF.cpp @@ -37,6 +37,9 @@ enum { // One bit. ELF_IsMemoryTagged_Shift = 13, + + // One bit. + ELF_LargeCommon_Shift = 14, }; } @@ -206,4 +209,13 @@ void MCSymbolELF::setMemtag(bool Tagged) { else setFlags(OtherFlags); } + +bool MCSymbolELF::isLargeCommon() const { + return getFlags() & (0x1 << ELF_LargeCommon_Shift); +} + +void MCSymbolELF::setIsLargeCommon() { + uint32_t OtherFlags = getFlags() & ~(0x1 << ELF_LargeCommon_Shift); + setFlags(OtherFlags | (1 << ELF_LargeCommon_Shift)); +} } diff --git a/llvm/test/MC/X86/largecomm.ll b/llvm/test/MC/X86/largecomm.ll index 52d51d562c47e..e977a66f85698 100644 --- a/llvm/test/MC/X86/largecomm.ll +++ b/llvm/test/MC/X86/largecomm.ll @@ -1,7 +1,5 @@ ; RUN: llc -filetype=asm -code-model=medium %s --large-data-threshold=65636 -o - | FileCheck %s --check-prefix=CHECKASM-MEDIUM ; RUN: llc -filetype=asm -code-model=large %s -o - | FileCheck %s --check-prefix=CHECKASM-LARGE -; RUN: llc -filetype=asm -code-model=medium %s --large-data-threshold=65636 -o - | llvm-mc -triple x86_64-linux-gnu -filetype=obj - | llvm-readelf -s - | FileCheck %s --check-prefix=CHECKOBJ-MEDIUM -; RUN: llc -filetype=asm -code-model=large %s -o - | llvm-mc -triple x86_64-linux-gnu -filetype=obj - | llvm-readelf -s - | FileCheck %s --check-prefix=CHECKOBJ-LARGE ; CHECKASM-MEDIUM: .section .lbss,"awl",@nobits ; CHECKASM-MEDIUM-NEXT: .type __BLNK__,@object # @__BLNK__ @@ -15,12 +13,6 @@ ; CHECKASM-LARGE-NEXT: .type ccc_,@object # @ccc_ ; CHECKASM-LARGE-NEXT: .largecomm ccc_,8,8 -; CHECKOBJ-MEDIUM: 8 OBJECT GLOBAL DEFAULT COM ccc_ -; CHECKOBJ-MEDIUM: 48394093832 OBJECT GLOBAL DEFAULT LARGE_COMMON __BLNK - -; CHECKOBJ-LARGE: 8 OBJECT GLOBAL DEFAULT LARGE_COMMON ccc_ -; CHECKOBJ-LARGE: 48394093832 OBJECT GLOBAL DEFAULT LARGE_COMMON __BLNK__ - source_filename = "FIRModule" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 7d34727f07f6d..ab93316907cc6 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1052,8 +1052,6 @@ ELFDumper::getSymbolSectionIndex(const Elf_Sym &Symbol, unsigned SymIndex, return CreateErr("SHN_ABS"); if (Ndx == ELF::SHN_COMMON) return CreateErr("SHN_COMMON"); - if (Ndx == ELF::SHN_AMD64_LCOMMON) - return CreateErr("SHN_AMD64_LCOMMON"); return CreateErr("SHN_LORESERVE", Ndx - SHN_LORESERVE); } @@ -4310,9 +4308,6 @@ std::string GNUELFDumper::getSymbolSectionNdx( default: // Find if: // Processor specific - if (this->Obj.getHeader().e_machine == EM_X86_64 && - SectionIndex == ELF::SHN_AMD64_LCOMMON) - return "LARGE_COMMON"; if (SectionIndex >= ELF::SHN_LOPROC && SectionIndex <= ELF::SHN_HIPROC) return std::string("PRC[0x") + to_string(format_hex_no_prefix(SectionIndex, 4)) + "]";