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) const {} + /// 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,8 @@ const_cast(getObjFileLowering()) .Initialize(OutContext, TM); + 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,28 @@ /// This implementation is used for RISCV ELF targets. class RISCVELFTargetObjectFile : public TargetLoweringObjectFileELF { + MCSection *SmallDataSection; + MCSection *SmallBSSSection; + +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) const override; }; } // end namespace llvm Index: lib/Target/RISCV/RISCVTargetObjectFile.cpp =================================================================== --- lib/Target/RISCV/RISCVTargetObjectFile.cpp +++ lib/Target/RISCV/RISCVTargetObjectFile.cpp @@ -8,11 +8,116 @@ #include "RISCVTargetObjectFile.h" #include "RISCVTargetMachine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSectionELF.h" using namespace llvm; +// riscv-ssection-threshold will be passed by clang if has -G flag. +static cl::opt SSThreshold( + "riscv-ssection-threshold", cl::Hidden, + cl::desc("Small data and bss section threshold size (default=8)"), + cl::init(8)); + 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. +static bool isInSmallSection(uint64_t Size) { + // 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->hasLocalLinkage()) + 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) const { + 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-module-flag.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/sdata-module-flag.ll @@ -0,0 +1,29 @@ +; 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 + +define i32 @test(i32 %k) { +entry: + %k.addr = alloca i32, align 4 + store i32 %k, i32* %k.addr, align 4 + %0 = load i32, i32* %k.addr, align 4 + %1 = load i32, i32* @v, align 4 + %add = add nsw i32 %0, %1 + %conv = sext i32 %add to i64 + %2 = load i64, i64* @r, align 8 + %add1 = add nsw i64 %conv, %2 + %conv2 = trunc i64 %add1 to i32 + ret i32 %conv2 +} + +; 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.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/sdata.ll @@ -0,0 +1,30 @@ +; RUN: llc -mtriple=riscv32 < %s | FileCheck -check-prefix=RV32 %s +; RUN: llc -mtriple=riscv32 -riscv-ssection-threshold=0 < %s | FileCheck -check-prefix=RV32-G0 %s +; RUN: llc -mtriple=riscv64 < %s | FileCheck -check-prefix=RV64 %s +; RUN: llc -mtriple=riscv64 -riscv-ssection-threshold=0 < %s | FileCheck -check-prefix=RV64-G0 %s + +@v = dso_local global i32 0, align 4 +@r = dso_local global i64 7, align 8 + +define i32 @test(i32 %k) { +entry: + %k.addr = alloca i32, align 4 + store i32 %k, i32* %k.addr, align 4 + %0 = load i32, i32* %k.addr, align 4 + %1 = load i32, i32* @v, align 4 + %add = add nsw i32 %0, %1 + %conv = sext i32 %add to i64 + %2 = load i64, i64* @r, align 8 + %add1 = add nsw i64 %conv, %2 + %conv2 = trunc i64 %add1 to i32 + ret i32 %conv2 +} + +; RV32: .section .sbss +; RV32: .section .sdata +; RV32-G0-NOT: .section .sbss +; RV32-G0-NOT: .section .sdata +; RV64: .section .sbss +; RV64: .section .sdata +; RV64-G0-NOT: .section .sbss +; RV64-G0-NOT: .section .sdata