Index: lib/Target/Mips/MipsISelLowering.h =================================================================== --- lib/Target/Mips/MipsISelLowering.h +++ lib/Target/Mips/MipsISelLowering.h @@ -329,6 +329,21 @@ DAG.getNode(MipsISD::Lo, DL, Ty, Lo)); } + // This method creates the following nodes, which are necessary for + // computing a symbol's address using gp-relative addressing: + // + // (add $gp, %gp_rel(sym)) + template + SDValue getAddrGPRel(NodeTy *N, EVT Ty, SelectionDAG &DAG) const { + SDLoc DL(N); + assert(Ty == MVT::i32); + SDValue GPRel = getTargetNode(N, Ty, DAG, MipsII::MO_GPREL); + return DAG.getNode(ISD::ADD, DL, Ty, + DAG.getRegister(Mips::GP, Ty), + DAG.getNode(MipsISD::GPRel, DL, DAG.getVTList(Ty), + GPRel)); + } + /// This function fills Ops, which is the list of operands that will later /// be used when a function call node is created. It also generates /// copyToReg nodes to set up argument registers. Index: lib/Target/Mips/MipsISelLowering.cpp =================================================================== --- lib/Target/Mips/MipsISelLowering.cpp +++ lib/Target/Mips/MipsISelLowering.cpp @@ -1643,8 +1643,6 @@ SDValue MipsTargetLowering::lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { - // FIXME there isn't actually debug info here - SDLoc DL(Op); EVT Ty = Op.getValueType(); GlobalAddressSDNode *N = cast(Op); const GlobalValue *GV = N->getGlobal(); @@ -1654,15 +1652,9 @@ const MipsTargetObjectFile &TLOF = (const MipsTargetObjectFile&)getObjFileLowering(); - // %gp_rel relocation - if (TLOF.IsGlobalInSmallSection(GV, getTargetMachine())) { - SDValue GA = DAG.getTargetGlobalAddress(GV, DL, MVT::i32, 0, - MipsII::MO_GPREL); - SDValue GPRelNode = DAG.getNode(MipsISD::GPRel, DL, - DAG.getVTList(MVT::i32), GA); - SDValue GPReg = DAG.getRegister(Mips::GP, MVT::i32); - return DAG.getNode(ISD::ADD, DL, MVT::i32, GPReg, GPRelNode); - } + if (TLOF.IsGlobalInSmallSection(GV, getTargetMachine())) + // %gp_rel relocation + return getAddrGPRel(N, Ty, DAG); // %hi/%lo relocation return getAddrNonPIC(N, Ty, DAG); @@ -1793,21 +1785,20 @@ SDValue MipsTargetLowering:: lowerConstantPool(SDValue Op, SelectionDAG &DAG) const { - // gp_rel relocation - // FIXME: we should reference the constant pool using small data sections, - // but the asm printer currently doesn't support this feature without - // hacking it. This feature should come soon so we can uncomment the - // stuff below. - //if (IsInSmallSection(C->getType())) { - // SDValue GPRelNode = DAG.getNode(MipsISD::GPRel, MVT::i32, CP); - // SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(MVT::i32); - // ResNode = DAG.getNode(ISD::ADD, MVT::i32, GOT, GPRelNode); ConstantPoolSDNode *N = cast(Op); EVT Ty = Op.getValueType(); if (getTargetMachine().getRelocationModel() != Reloc::PIC_ && - !Subtarget.isABI_N64()) + !Subtarget.isABI_N64()) { + const MipsTargetObjectFile &TLOF = + (const MipsTargetObjectFile&)getObjFileLowering(); + + if (TLOF.IsConstantInSmallSection(N->getConstVal(), getTargetMachine())) + // %gp_rel relocation + return getAddrGPRel(N, Ty, DAG); + return getAddrNonPIC(N, Ty, DAG); + } return getAddrLocal(N, Ty, DAG, Subtarget.isABI_N32() || Subtarget.isABI_N64()); Index: lib/Target/Mips/MipsSubtarget.cpp =================================================================== --- lib/Target/Mips/MipsSubtarget.cpp +++ lib/Target/Mips/MipsSubtarget.cpp @@ -58,6 +58,10 @@ cl::desc("MIPS: mips16 constant islands enable."), cl::init(true)); +static cl::opt +GPOpt("mgpopt", cl::Hidden, + cl::desc("MIPS: Enable gp-relative addressing of small data items")); + /// Select the Mips CPU for the given triple and cpu name. /// FIXME: Merge with the copy in MipsMCTargetDesc.cpp static StringRef selectMipsCPU(Triple TT, StringRef CPU) { @@ -171,10 +175,16 @@ if (TT.find("linux") == std::string::npos) IsLinux = false; + if (NoABICalls && TM->getRelocationModel() == Reloc::PIC_) + report_fatal_error("position-independent code requires '-mabicalls'"); + // Set UseSmallSection. - // TODO: Investigate the IsLinux check. I suspect it's really checking for - // bare-metal. - UseSmallSection = !IsLinux && (TM->getRelocationModel() == Reloc::Static); + UseSmallSection = GPOpt; + if (!NoABICalls && GPOpt) { + errs() << "warning: cannot use small-data accesses for '-mabicalls'" + << "\n"; + UseSmallSection = false; + } } /// This overrides the PostRAScheduler bit in the SchedModel for any CPU. Index: lib/Target/Mips/MipsTargetObjectFile.h =================================================================== --- lib/Target/Mips/MipsTargetObjectFile.h +++ lib/Target/Mips/MipsTargetObjectFile.h @@ -17,21 +17,30 @@ class MipsTargetObjectFile : public TargetLoweringObjectFileELF { const MCSection *SmallDataSection; const MCSection *SmallBSSSection; + const TargetMachine *TM; public: void Initialize(MCContext &Ctx, const TargetMachine &TM) override; - - /// IsGlobalInSmallSection - Return true if this global address should be - /// placed into small data/bss section. - bool IsGlobalInSmallSection(const GlobalValue *GV, - const TargetMachine &TM, SectionKind Kind)const; + /// Return true if this global address should be placed into small data/bss + /// section. + bool IsGlobalInSmallSection(const GlobalValue *GV, const TargetMachine &TM, + SectionKind Kind) const; bool IsGlobalInSmallSection(const GlobalValue *GV, const TargetMachine &TM) const; + bool IsGlobalInSmallSectionImpl(const GlobalValue *GV, + const TargetMachine &TM) const; const MCSection *SelectSectionForGlobal(const GlobalValue *GV, SectionKind Kind, Mangler &Mang, const TargetMachine &TM) const override; + + /// Return true if this constant should be placed into small data section. + bool IsConstantInSmallSection(const Constant *CN, + const TargetMachine &TM) const; + + const MCSection *getSectionForConstant(SectionKind Kind, + const Constant *C) const override; }; } // end namespace llvm Index: lib/Target/Mips/MipsTargetObjectFile.cpp =================================================================== --- lib/Target/Mips/MipsTargetObjectFile.cpp +++ lib/Target/Mips/MipsTargetObjectFile.cpp @@ -24,6 +24,17 @@ cl::desc("Small data and bss section threshold size (default=8)"), cl::init(8)); +static cl::opt +LocalSData("mlocal-sdata", cl::Hidden, + cl::desc("MIPS: Use gp_rel for object-local data."), + cl::init(true)); + +static cl::opt +ExternSData("mextern-sdata", cl::Hidden, + cl::desc("MIPS: Use gp_rel for data that is not defined by the " + "current object."), + cl::init(true)); + void MipsTargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM){ TargetLoweringObjectFileELF::Initialize(Ctx, TM); InitializeELF(TM.Options.UseInitArray); @@ -37,29 +48,46 @@ getContext().getELFSection(".sbss", ELF::SHT_NOBITS, ELF::SHF_WRITE |ELF::SHF_ALLOC, SectionKind::getBSS()); + this->TM = &TM; } // A address must be loaded from a small section if its size is less than the // small section size threshold. Data in this section must be addressed 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; } -bool MipsTargetObjectFile::IsGlobalInSmallSection(const GlobalValue *GV, - const TargetMachine &TM) const { +/// Return true if this global address should be placed into small data/bss +/// section. +bool MipsTargetObjectFile:: +IsGlobalInSmallSection(const GlobalValue *GV, const TargetMachine &TM) const { + // We first check the case where global is a declaration, because finding + // section kind using getKindForGlobal() is only allowed for global + // definitions. if (GV->isDeclaration() || GV->hasAvailableExternallyLinkage()) - return false; + return IsGlobalInSmallSectionImpl(GV, TM); return IsGlobalInSmallSection(GV, TM, getKindForGlobal(GV, TM)); } -/// IsGlobalInSmallSection - Return true if this global address should be -/// placed into small data/bss section. +/// Return true if this global address should be placed into small data/bss +/// section. bool MipsTargetObjectFile:: IsGlobalInSmallSection(const GlobalValue *GV, const TargetMachine &TM, SectionKind Kind) const { + return (IsGlobalInSmallSectionImpl(GV, TM) && + (Kind.isDataRel() || Kind.isBSS() || Kind.isCommon())); +} +/// Return true if this global address should be placed into small data/bss +/// section. This method does all the work, except for checking the section +/// kind. +bool MipsTargetObjectFile:: +IsGlobalInSmallSectionImpl(const GlobalValue *GV, + const TargetMachine &TM) const { const MipsSubtarget &Subtarget = TM.getSubtarget(); // Return if small section is not available. @@ -71,13 +99,13 @@ if (!GVA) return false; - // We can only do this for datarel or BSS objects for now. - if (!Kind.isBSS() && !Kind.isDataRel()) + // Enforce -mlocal-sdata. + if (!LocalSData && GV->hasLocalLinkage()) return false; - // If this is a internal constant string, there is a special - // section for it, but not in small data/bss. - if (Kind.isMergeable1ByteCString()) + // Enforce -mextern-sdata. + if (!ExternSData && ((GV->hasExternalLinkage() && GV->isDeclaration()) || + GV->hasCommonLinkage())) return false; Type *Ty = GV->getType()->getElementType(); @@ -85,8 +113,6 @@ TM.getSubtargetImpl()->getDataLayout()->getTypeAllocSize(Ty)); } - - const MCSection *MipsTargetObjectFile:: SelectSectionForGlobal(const GlobalValue *GV, SectionKind Kind, Mangler &Mang, const TargetMachine &TM) const { @@ -96,9 +122,27 @@ // Handle Small Section classification here. if (Kind.isBSS() && IsGlobalInSmallSection(GV, TM, Kind)) return SmallBSSSection; - if (Kind.isDataNoRel() && IsGlobalInSmallSection(GV, TM, Kind)) + if (Kind.isDataRel() && IsGlobalInSmallSection(GV, TM, Kind)) return SmallDataSection; // Otherwise, we work the same as ELF. return TargetLoweringObjectFileELF::SelectSectionForGlobal(GV, Kind, Mang,TM); } + +/// Return true if this constant should be placed into small data section. +bool MipsTargetObjectFile:: +IsConstantInSmallSection(const Constant *CN, const TargetMachine &TM) const { + return (TM.getSubtarget().useSmallSection() && + LocalSData && + IsInSmallSection(TM.getSubtargetImpl()->getDataLayout() + ->getTypeAllocSize(CN->getType()))); +} + +const MCSection *MipsTargetObjectFile:: +getSectionForConstant(SectionKind Kind, const Constant *C) const { + if (IsConstantInSmallSection(C, *TM)) + return SmallDataSection; + + // Otherwise, we work the same as ELF. + return TargetLoweringObjectFileELF::getSectionForConstant(Kind, C); +} Index: test/CodeGen/Mips/gprel.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/gprel.ll @@ -0,0 +1,196 @@ +; RUN: llc -march=mipsel -mcpu=mips32 -mattr=+noabicalls -relocation-model=static -mgpopt < %s | FileCheck %s -check-prefix=ALL -check-prefix=NOABICALLS-STATIC +; RUN: llc -march=mipsel -mcpu=mips32 -mattr=+noabicalls -relocation-model=static -mgpopt -mlocal-sdata=0 < %s | FileCheck %s -check-prefix=ALL -check-prefix=NOABICALLS-STATIC-NOLOCAL +; RUN: llc -march=mipsel -mcpu=mips32 -mattr=+noabicalls -relocation-model=static -mgpopt -mextern-sdata=0 < %s | FileCheck %s -check-prefix=ALL -check-prefix=NOABICALLS-STATIC-NOEXTERN +; RUN: llc -march=mipsel -mcpu=mips32 -mattr=+noabicalls -relocation-model=static -mgpopt -mips-ssection-threshold=0 < %s | FileCheck %s -check-prefix=ALL -check-prefix=NOABICALLS-STATIC-ZEROSIZE +; RUN: not llc -march=mipsel -mcpu=mips32 -mattr=+noabicalls -relocation-model=pic -mgpopt < %s 2>&1 | FileCheck %s -check-prefix=NOABICALLS-PIC +; RUN: llc -march=mipsel -mcpu=mips32 -relocation-model=static -mgpopt < %s 2>&1 | FileCheck %s -check-prefix=ALL -check-prefix=ABICALLS-STATIC +; RUN: llc -march=mipsel -mcpu=mips32 -relocation-model=pic -mgpopt < %s 2>&1 | FileCheck %s -check-prefix=ALL -check-prefix=ABICALLS-PIC +; RUN: llc -mtriple=mipsel-sde-elf -mcpu=mips32 -mattr=+noabicalls -relocation-model=static -mgpopt < %s | FileCheck %s -check-prefix=ALL -check-prefix=SDE-ELF-GPOPT +; RUN: llc -mtriple=mipsel-sde-elf -mcpu=mips32 -mattr=+noabicalls -relocation-model=static < %s | FileCheck %s -check-prefix=ALL -check-prefix=SDE-ELF-NOGPOPT + + +; NOABICALLS-PIC: LLVM ERROR: position-independent code requires '-mabicalls' + +@x = global i32 1, align 4 +@y = global i32 0, align 4 +@z = common global i32 0, align 4 +@f = internal global float 1.000000e+00, align 4 +@d = external global double + +define i32 @data() { +entry: + %0 = load i32* @x, align 4 + ret i32 %0 + +; ALL-LABEL: data + +; NOABICALLS-STATIC: lw $2, %gp_rel(x)($gp) +; NOABICALLS-STATIC-NOLOCAL: lw $2, %gp_rel(x)($gp) +; NOABICALLS-STATIC-NOEXTERN: lw $2, %gp_rel(x)($gp) +; NOABICALLS-STATIC-ZEROSIZE: hi(x) +; ABICALLS-STATIC: warning: cannot use small-data accesses for '-mabicalls' +; ABICALLS-STATIC: hi(x) +; ABICALLS-PIC: warning: cannot use small-data accesses for '-mabicalls' +; ABICALLS-PIC: got(x) +; SDE-ELF-GPOPT: lw $2, %gp_rel(x)($gp) +; SDE-ELF-NOGPOPT: hi(x) +} + +define i32 @bss() { +entry: + %0 = load i32* @y, align 4 + ret i32 %0 + +; ALL-LABEL: bss + +; NOABICALLS-STATIC: lw $2, %gp_rel(y)($gp) +; NOABICALLS-STATIC-NOLOCAL: lw $2, %gp_rel(y)($gp) +; NOABICALLS-STATIC-NOEXTERN: lw $2, %gp_rel(y)($gp) +; NOABICALLS-STATIC-ZEROSIZE: hi(y) +; ABICALLS-STATIC: hi(y) +; ABICALLS-PIC: got(y) +; SDE-ELF-GPOPT: lw $2, %gp_rel(y)($gp) +; SDE-ELF-NOGPOPT: hi(y) +} + +define i32 @common() { +entry: + %0 = load i32* @z, align 4 + ret i32 %0 + +; ALL-LABEL: common + +; NOABICALLS-STATIC: lw $2, %gp_rel(z)($gp) +; NOABICALLS-STATIC-NOLOCAL: lw $2, %gp_rel(z)($gp) +; NOABICALLS-STATIC-NOEXTERN: hi(z) +; NOABICALLS-STATIC-ZEROSIZE: hi(z) +; ABICALLS-STATIC: hi(z) +; ABICALLS-PIC: got(z) +; SDE-ELF-GPOPT: lw $2, %gp_rel(z)($gp) +; SDE-ELF-NOGPOPT: hi(z) +} + +define float @internal() { +entry: + %0 = load float* @f, align 4 + ret float %0 + +; ALL-LABEL: internal + +; NOABICALLS-STATIC: lwc1 $f0, %gp_rel(f)($gp) +; NOABICALLS-STATIC-NOLOCAL: hi(f) +; NOABICALLS-STATIC-NOEXTERN: lwc1 $f0, %gp_rel(f)($gp) +; NOABICALLS-STATIC-ZEROSIZE: hi(f) +; ABICALLS-STATIC: hi(f) +; ABICALLS-PIC: got(f) +; SDE-ELF-GPOPT: lwc1 $f0, %gp_rel(f)($gp) +; SDE-ELF-NOGPOPT: hi(f) +} + +define double @external() { +entry: + %0 = load double* @d, align 8 + ret double %0 + +; ALL-LABEL: external + +; NOABICALLS-STATIC: ldc1 $f0, %gp_rel(d)($gp) +; NOABICALLS-STATIC-NOLOCAL: ldc1 $f0, %gp_rel(d)($gp) +; NOABICALLS-STATIC-NOEXTERN: hi(d) +; NOABICALLS-STATIC-ZEROSIZE: hi(d) +; ABICALLS-STATIC: hi(d) +; ABICALLS-PIC: got(d) +; SDE-ELF-GPOPT: ldc1 $f0, %gp_rel(d)($gp) +; SDE-ELF-NOGPOPT: hi(d) +} + +define float @constant() { +entry: + ret float 2.000000e+00 + +; ALL-LABEL: constant + +; NOABICALLS-STATIC: lwc1 $f0, %gp_rel($CPI{{[0-9_]+}})($gp) +; NOABICALLS-STATIC-NOLOCAL: hi($CPI{{[0-9_]+}}) +; NOABICALLS-STATIC-NOEXTERN: lwc1 $f0, %gp_rel($CPI{{[0-9_]+}})($gp) +; NOABICALLS-STATIC-ZEROSIZE: hi($CPI{{[0-9_]+}}) +; ABICALLS-STATIC: hi($CPI{{[0-9_]+}}) +; ABICALLS-PIC: got($CPI{{[0-9_]+}}) +; SDE-ELF-GPOPT: lwc1 $f0, %gp_rel($CPI{{[0-9_]+}})($gp) +; SDE-ELF-NOGPOPT: hi($CPI{{[0-9_]+}}) +} + + +; Check the sections. + +; NOABICALLS-STATIC: .type x,@object +; NOABICALLS-STATIC-NEXT: .section .sdata,"aw",@progbits +; NOABICALLS-STATIC: .type y,@object +; NOABICALLS-STATIC-NEXT: .section .sbss,"aw",@nobits +; NOABICALLS-STATIC: .type z,@object +; NOABICALLS-STATIC-NEXT: .comm z,4,4 +; NOABICALLS-STATIC: .type f,@object +; NOABICALLS-STATIC-NEXT: .section .sdata,"aw",@progbits + +; NOABICALLS-STATIC-NOLOCAL: .type x,@object +; NOABICALLS-STATIC-NOLOCAL-NEXT: .section .sdata,"aw",@progbits +; NOABICALLS-STATIC-NOLOCAL: .type y,@object +; NOABICALLS-STATIC-NOLOCAL-NEXT: .section .sbss,"aw",@nobits +; NOABICALLS-STATIC-NOLOCAL: .type z,@object +; NOABICALLS-STATIC-NOLOCAL-NEXT: .comm z,4,4 +; NOABICALLS-STATIC-NOLOCAL: .type f,@object +; NOABICALLS-STATIC-NOLOCAL-NEXT: .data + +; NOABICALLS-STATIC-NOEXTERN: .type x,@object +; NOABICALLS-STATIC-NOEXTERN-NEXT: .section .sdata,"aw",@progbits +; NOABICALLS-STATIC-NOEXTERN: .type y,@object +; NOABICALLS-STATIC-NOEXTERN-NEXT: .section .sbss,"aw",@nobits +; NOABICALLS-STATIC-NOEXTERN: .type z,@object +; NOABICALLS-STATIC-NOEXTERN-NEXT: .comm z,4,4 +; NOABICALLS-STATIC-NOEXTERN: .type f,@object +; NOABICALLS-STATIC-NOEXTERN-NEXT: .section .sdata,"aw",@progbits + +; NOABICALLS-STATIC-ZEROSIZE: .type x,@object +; NOABICALLS-STATIC-ZEROSIZE-NEXT: .data +; NOABICALLS-STATIC-ZEROSIZE: .type y,@object +; NOABICALLS-STATIC-ZEROSIZE-NEXT: .bss +; NOABICALLS-STATIC-ZEROSIZE: .type z,@object +; NOABICALLS-STATIC-ZEROSIZE-NEXT: .comm z,4,4 +; NOABICALLS-STATIC-ZEROSIZE: .type f,@object +; NOABICALLS-STATIC-ZEROSIZE-NEXT: .data + +; ABICALLS-STATIC: .type x,@object +; ABICALLS-STATIC-NEXT: .data +; ABICALLS-STATIC: .type y,@object +; ABICALLS-STATIC-NEXT: .bss +; ABICALLS-STATIC: .type z,@object +; ABICALLS-STATIC-NEXT: .comm z,4,4 +; ABICALLS-STATIC: .type f,@object +; ABICALLS-STATIC-NEXT: .data + +; ABICALLS-PIC: .type x,@object +; ABICALLS-PIC-NEXT: .data +; ABICALLS-PIC: .type y,@object +; ABICALLS-PIC-NEXT: .bss +; ABICALLS-PIC: .type z,@object +; ABICALLS-PIC-NEXT: .comm z,4,4 +; ABICALLS-PIC: .type f,@object +; ABICALLS-PIC-NEXT: .data + +; SDE-ELF-GPOPT: .type x,@object +; SDE-ELF-GPOPT-NEXT: .section .sdata,"aw",@progbits +; SDE-ELF-GPOPT: .type y,@object +; SDE-ELF-GPOPT-NEXT: .section .sbss,"aw",@nobits +; SDE-ELF-GPOPT: .type z,@object +; SDE-ELF-GPOPT-NEXT: .comm z,4,4 +; SDE-ELF-GPOPT: .type f,@object +; SDE-ELF-GPOPT-NEXT: .section .sdata,"aw",@progbits + +; SDE-ELF-NOGPOPT: .type x,@object +; SDE-ELF-NOGPOPT-NEXT: .data +; SDE-ELF-NOGPOPT: .type y,@object +; SDE-ELF-NOGPOPT-NEXT: .bss +; SDE-ELF-NOGPOPT: .type z,@object +; SDE-ELF-NOGPOPT-NEXT: .comm z,4,4 +; SDE-ELF-NOGPOPT: .type f,@object +; SDE-ELF-NOGPOPT-NEXT: .data Index: test/CodeGen/Mips/small-section-reserve-gp.ll =================================================================== --- test/CodeGen/Mips/small-section-reserve-gp.ll +++ test/CodeGen/Mips/small-section-reserve-gp.ll @@ -1,4 +1,4 @@ -; RUN: llc -mtriple=mipsel-sde-elf -march=mipsel -relocation-model=static < %s \ +; RUN: llc -mtriple=mipsel-sde-elf -march=mipsel -relocation-model=static -mattr=+noabicalls -mgpopt < %s \ ; RUN: | FileCheck %s @i = internal unnamed_addr global i32 0, align 4