Index: include/llvm/Target/TargetLoweringObjectFile.h =================================================================== --- include/llvm/Target/TargetLoweringObjectFile.h +++ include/llvm/Target/TargetLoweringObjectFile.h @@ -79,6 +79,9 @@ /// 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, Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -257,6 +257,9 @@ const_cast(getObjFileLowering()) .Initialize(OutContext, TM); + const_cast(getObjFileLowering()) + .getModuleMetadata(M); + OutStreamer->InitSections(false); // Emit the version-min deployment target directive if needed. Index: lib/Target/RISCV/RISCVTargetObjectFile.h =================================================================== --- lib/Target/RISCV/RISCVTargetObjectFile.h +++ lib/Target/RISCV/RISCVTargetObjectFile.h @@ -16,7 +16,31 @@ /// 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 Index: lib/Target/RISCV/RISCVTargetObjectFile.cpp =================================================================== --- lib/Target/RISCV/RISCVTargetObjectFile.cpp +++ 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 @@ 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); } Index: test/CodeGen/RISCV/sdata-limit-0.ll =================================================================== --- /dev/null +++ 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 not 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 Index: test/CodeGen/RISCV/sdata-limit-4.ll =================================================================== --- /dev/null +++ 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 put in sbss, +; but @r won't 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 Index: test/CodeGen/RISCV/sdata-limit-8.ll =================================================================== --- /dev/null +++ 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 put in sbss +; and @r will 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 Index: test/CodeGen/RISCV/sdata-local-sym.ll =================================================================== --- /dev/null +++ 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 put in sbss +; and @r will 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