Index: lib/Target/RISCV/RISCVTargetObjectFile.h =================================================================== --- lib/Target/RISCV/RISCVTargetObjectFile.h +++ lib/Target/RISCV/RISCVTargetObjectFile.h @@ -16,7 +16,26 @@ /// 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; }; } // end namespace llvm Index: lib/Target/RISCV/RISCVTargetObjectFile.cpp =================================================================== --- lib/Target/RISCV/RISCVTargetObjectFile.cpp +++ lib/Target/RISCV/RISCVTargetObjectFile.cpp @@ -8,11 +8,111 @@ #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=0)"), + cl::init(0)); + 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); + // Setting default SSThreshold as integer size + // if the code model is not large. + if ((SSThreshold == 0) && (TM.getCodeModel() != CodeModel::Large)) { + if (TM.getTargetTriple().isArch32Bit()) + SSThreshold = 4; + else if (TM.getTargetTriple().isArch64Bit()) + SSThreshold = 8; + } +} + +// 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); +} + +/// 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.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/sdata.ll @@ -0,0 +1,17 @@ +; RUN: llc -mtriple=riscv32 < %s | FileCheck -check-prefix=RV32 %s +; RUN: llc -mtriple=riscv64 < %s | FileCheck -check-prefix=RV64 %s + +@var0 = dso_local local_unnamed_addr global i32 0, align 4 +@var1 = dso_local local_unnamed_addr global i32 7, align 4 + +define i32 @main(i32 signext %arvc) { +entry: + %0 = load i32, i32* @var0, align 4 + %add = add nsw i32 %0, %arvc + store i32 %add, i32* @var1, align 4 + ret i32 0 +} +; RV32: .section .sbss +; RV32: .section .sdata +; RV64: .section .sbss +; RV64: .section .sdata