diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index e619b186dfe3d..60e85ce5fae99 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_X86_64_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..7b48f312d04f0 100644 --- a/llvm/include/llvm/MC/MCSymbolELF.h +++ b/llvm/include/llvm/MC/MCSymbolELF.h @@ -48,6 +48,9 @@ class MCSymbolELF : public MCSymbol { void setMemtag(bool Tagged); bool isMemtag() const; + 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 5a26e2fc31458..b389fda625c25 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_X86_64_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..9240e18e12443 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..7465d1b2aee8f 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_X86_64_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..3e282f55a04a0 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(); + 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..237b430e3d7b4 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, 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/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/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..e977a66f85698 --- /dev/null +++ b/llvm/test/MC/X86/largecomm.ll @@ -0,0 +1,50 @@ +; 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 + +; 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 + +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}