diff --git a/llvm/include/llvm/Target/TargetLoweringObjectFile.h b/llvm/include/llvm/Target/TargetLoweringObjectFile.h index f96e3532dfcf7..7e094a1738b71 100644 --- a/llvm/include/llvm/Target/TargetLoweringObjectFile.h +++ b/llvm/include/llvm/Target/TargetLoweringObjectFile.h @@ -79,6 +79,9 @@ class TargetLoweringObjectFile : public MCObjectFileInfo { /// Emit the module-level metadata that the platform cares about. virtual void emitModuleMetadata(MCStreamer &Streamer, Module &M) const {} + /// Get the module-level metadata that the platform cares about. + virtual void getModuleMetadata(Module &M) {} + /// Given a constant with the SectionKind, return a section that it should be /// placed in. virtual MCSection *getSectionForConstant(const DataLayout &DL, diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index fe22e946c166c..d34254699ed5b 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -259,6 +259,9 @@ bool AsmPrinter::doInitialization(Module &M) { const_cast(getObjFileLowering()) .Initialize(OutContext, TM); + const_cast(getObjFileLowering()) + .getModuleMetadata(M); + OutStreamer->InitSections(false); // Emit the version-min deployment target directive if needed. diff --git a/llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp b/llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp index ec61ab76096b9..bbd45c970d3dc 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp @@ -8,6 +8,9 @@ #include "RISCVTargetObjectFile.h" #include "RISCVTargetMachine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSectionELF.h" using namespace llvm; @@ -15,4 +18,97 @@ void RISCVELFTargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM) { TargetLoweringObjectFileELF::Initialize(Ctx, TM); InitializeELF(TM.Options.UseInitArray); + + SmallDataSection = getContext().getELFSection( + ".sdata", ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC); + SmallBSSSection = getContext().getELFSection(".sbss", ELF::SHT_NOBITS, + ELF::SHF_WRITE | ELF::SHF_ALLOC); +} + +// A address must be loaded from a small section if its size is less than the +// small section size threshold. Data in this section could be addressed by +// using gp_rel operator. +bool RISCVELFTargetObjectFile::isInSmallSection(uint64_t Size) const { + // gcc has traditionally not treated zero-sized objects as small data, so this + // is effectively part of the ABI. + return Size > 0 && Size <= SSThreshold; +} + +// Return true if this global address should be placed into small data/bss +// section. +bool RISCVELFTargetObjectFile::isGlobalInSmallSection( + const GlobalObject *GO, const TargetMachine &TM) const { + // Only global variables, not functions. + const GlobalVariable *GVA = dyn_cast(GO); + if (!GVA) + return false; + + // If the variable has an explicit section, it is placed in that section. + if (GVA->hasSection()) { + StringRef Section = GVA->getSection(); + + // Explicitly placing any variable in the small data section overrides + // the global -G value. + if (Section == ".sdata" || Section == ".sbss") + return true; + + // Otherwise reject putting the variable to small section if it has an + // explicit section name. + return false; + } + + if (((GVA->hasExternalLinkage() && GVA->isDeclaration()) || + GVA->hasCommonLinkage())) + return false; + + Type *Ty = GVA->getValueType(); + // It is possible that the type of the global is unsized, i.e. a declaration + // of a extern struct. In this case don't presume it is in the small data + // section. This happens e.g. when building the FreeBSD kernel. + if (!Ty->isSized()) + return false; + + return isInSmallSection( + GVA->getParent()->getDataLayout().getTypeAllocSize(Ty)); +} + +MCSection *RISCVELFTargetObjectFile::SelectSectionForGlobal( + const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + // Handle Small Section classification here. + if (Kind.isBSS() && isGlobalInSmallSection(GO, TM)) + return SmallBSSSection; + if (Kind.isData() && isGlobalInSmallSection(GO, TM)) + return SmallDataSection; + + // Otherwise, we work the same as ELF. + return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM); +} + +void RISCVELFTargetObjectFile::getModuleMetadata(Module &M) { + SmallVector ModuleFlags; + M.getModuleFlagsMetadata(ModuleFlags); + + for (const auto &MFE : ModuleFlags) { + StringRef Key = MFE.Key->getString(); + if (Key == "SmallDataLimit") { + SSThreshold = mdconst::extract(MFE.Val)->getZExtValue(); + break; + } + } +} + +/// Return true if this constant should be placed into small data section. +bool RISCVELFTargetObjectFile::isConstantInSmallSection( + const DataLayout &DL, const Constant *CN) const { + return isInSmallSection(DL.getTypeAllocSize(CN->getType())); +} + +MCSection *RISCVELFTargetObjectFile::getSectionForConstant( + const DataLayout &DL, SectionKind Kind, const Constant *C, + unsigned &Align) const { + if (isConstantInSmallSection(DL, C)) + return SmallDataSection; + + // Otherwise, we work the same as ELF. + return TargetLoweringObjectFileELF::getSectionForConstant(DL, Kind, C, Align); } diff --git a/llvm/lib/Target/RISCV/RISCVTargetObjectFile.h b/llvm/lib/Target/RISCV/RISCVTargetObjectFile.h index e622c5a6c1a3b..b2daaaa9d364d 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetObjectFile.h +++ b/llvm/lib/Target/RISCV/RISCVTargetObjectFile.h @@ -16,7 +16,31 @@ class RISCVTargetMachine; /// This implementation is used for RISCV ELF targets. class RISCVELFTargetObjectFile : public TargetLoweringObjectFileELF { + MCSection *SmallDataSection; + MCSection *SmallBSSSection; + unsigned SSThreshold = 8; + +public: void Initialize(MCContext &Ctx, const TargetMachine &TM) override; + + /// Return true if this global address should be placed into small data/bss + /// section. + bool isGlobalInSmallSection(const GlobalObject *GO, + const TargetMachine &TM) const; + + MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind, + const TargetMachine &TM) const override; + + /// Return true if this constant should be placed into small data section. + bool isConstantInSmallSection(const DataLayout &DL, const Constant *CN) const; + + MCSection *getSectionForConstant(const DataLayout &DL, SectionKind Kind, + const Constant *C, + unsigned &Align) const override; + + void getModuleMetadata(Module &M) override; + + bool isInSmallSection(uint64_t Size) const; }; } // end namespace llvm diff --git a/llvm/test/CodeGen/RISCV/sdata-limit-0.ll b/llvm/test/CodeGen/RISCV/sdata-limit-0.ll new file mode 100644 index 0000000000000..2c90cb9666a2c --- /dev/null +++ b/llvm/test/CodeGen/RISCV/sdata-limit-0.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=riscv32 < %s | FileCheck -check-prefix=RV32 %s +; RUN: llc -mtriple=riscv64 < %s | FileCheck -check-prefix=RV64 %s + +@v = dso_local global i32 0, align 4 +@r = dso_local global i64 7, align 8 + +; SmallDataLimit set to 0, so we expect no data will put in sbss and sdata. +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"SmallDataLimit", i32 0} + +; RV32-NOT: .section .sbss +; RV32-NOT: .section .sdata +; RV64-NOT: .section .sbss +; RV64-NOT: .section .sdata diff --git a/llvm/test/CodeGen/RISCV/sdata-limit-4.ll b/llvm/test/CodeGen/RISCV/sdata-limit-4.ll new file mode 100644 index 0000000000000..ec482cf2ae964 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/sdata-limit-4.ll @@ -0,0 +1,15 @@ +; RUN: llc -mtriple=riscv32 < %s | FileCheck -check-prefix=RV32 %s +; RUN: llc -mtriple=riscv64 < %s | FileCheck -check-prefix=RV64 %s + +@v = dso_local global i32 0, align 4 +@r = dso_local global i64 7, align 8 + +; SmallDataLimit set to 4, so we expect @v will be put in sbss, +; but @r won't be put in sdata. +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"SmallDataLimit", i32 4} + +; RV32: .section .sbss +; RV32-NOT: .section .sdata +; RV64: .section .sbss +; RV64-NOT: .section .sdata diff --git a/llvm/test/CodeGen/RISCV/sdata-limit-8.ll b/llvm/test/CodeGen/RISCV/sdata-limit-8.ll new file mode 100644 index 0000000000000..1c67addf10704 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/sdata-limit-8.ll @@ -0,0 +1,15 @@ +; RUN: llc -mtriple=riscv32 < %s | FileCheck -check-prefix=RV32 %s +; RUN: llc -mtriple=riscv64 < %s | FileCheck -check-prefix=RV64 %s + +@v = dso_local global i32 0, align 4 +@r = dso_local global i64 7, align 8 + +; SmallDataLimit set to 8, so we expect @v will be put in sbss +; and @r will be put in sdata. +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"SmallDataLimit", i32 8} + +; RV32: .section .sbss +; RV32: .section .sdata +; RV64: .section .sbss +; RV64: .section .sdata diff --git a/llvm/test/CodeGen/RISCV/sdata-local-sym.ll b/llvm/test/CodeGen/RISCV/sdata-local-sym.ll new file mode 100644 index 0000000000000..b5032aecefed8 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/sdata-local-sym.ll @@ -0,0 +1,16 @@ +; RUN: llc -mtriple=riscv32 < %s | FileCheck -check-prefix=RV32 %s +; RUN: llc -mtriple=riscv64 < %s | FileCheck -check-prefix=RV64 %s + +@v = internal global i32 0, align 4 +@r = internal global i64 7, align 8 + +; @v and @r are local symbols. +; SmallDataLimit set to 8, so we expect @v will be put in sbss +; and @r will be put in sdata. +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"SmallDataLimit", i32 8} + +; RV32: .section .sbss +; RV32: .section .sdata +; RV64: .section .sbss +; RV64: .section .sdata