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=-1)"), + cl::init(-1)); + 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 8 bytes + // if the code model is not large and it's not PIC relocation model. + if ((SSThreshold == -1) && + (TM.getCodeModel() != CodeModel::Large) && + (TM.getRelocationModel() != Reloc::PIC_)) { + SSThreshold = 8; + } else if (SSThreshold < 0) + SSThreshold = 0; +} + +// 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,36 @@ +; 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=riscv32 -code-model=large < %s | FileCheck -check-prefix=RV32-LARGE %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 +; RUN: llc -mtriple=riscv64 -code-model=large < %s | FileCheck -check-prefix=RV64-LARGE %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 +; RV32-LARGE-NOT: .section .sbss +; RV32-LARGE-NOT: .section .sdata +; RV64: .section .sbss +; RV64: .section .sdata +; RV64-G0-NOT: .section .sbss +; RV64-G0-NOT: .section .sdata +; RV64-LARGE-NOT: .section .sbss +; RV64-LARGE-NOT: .section .sdata