Index: llvm/include/llvm/BinaryFormat/ELFRelocs/PowerPC64.def =================================================================== --- llvm/include/llvm/BinaryFormat/ELFRelocs/PowerPC64.def +++ llvm/include/llvm/BinaryFormat/ELFRelocs/PowerPC64.def @@ -98,6 +98,7 @@ #undef R_PPC64_DTPREL16_HIGHA #undef R_PPC64_REL24_NOTOC #undef R_PPC64_PCREL34 +#undef R_PPC64_GOT_PCREL34 #undef R_PPC64_IRELATIVE #undef R_PPC64_REL16 #undef R_PPC64_REL16_LO @@ -194,6 +195,7 @@ ELF_RELOC(R_PPC64_DTPREL16_HIGHA, 115) ELF_RELOC(R_PPC64_REL24_NOTOC, 116) ELF_RELOC(R_PPC64_PCREL34, 132) +ELF_RELOC(R_PPC64_GOT_PCREL34, 133) ELF_RELOC(R_PPC64_IRELATIVE, 248) ELF_RELOC(R_PPC64_REL16, 249) ELF_RELOC(R_PPC64_REL16_LO, 250) Index: llvm/include/llvm/MC/MCExpr.h =================================================================== --- llvm/include/llvm/MC/MCExpr.h +++ llvm/include/llvm/MC/MCExpr.h @@ -282,6 +282,7 @@ VK_PPC_GOT_TLSLD_LO, // symbol@got@tlsld@l VK_PPC_GOT_TLSLD_HI, // symbol@got@tlsld@h VK_PPC_GOT_TLSLD_HA, // symbol@got@tlsld@ha + VK_PPC_GOT_PCREL, // symbol@got@pcrel VK_PPC_TLSLD, // symbol@tlsld VK_PPC_LOCAL, // symbol@local VK_PPC_NOTOC, // symbol@notoc Index: llvm/lib/MC/MCExpr.cpp =================================================================== --- llvm/lib/MC/MCExpr.cpp +++ llvm/lib/MC/MCExpr.cpp @@ -317,6 +317,7 @@ case VK_PPC_GOT_TLSLD_LO: return "got@tlsld@l"; case VK_PPC_GOT_TLSLD_HI: return "got@tlsld@h"; case VK_PPC_GOT_TLSLD_HA: return "got@tlsld@ha"; + case VK_PPC_GOT_PCREL: return "got@pcrel"; case VK_PPC_TLSLD: return "tlsld"; case VK_PPC_LOCAL: return "local"; case VK_PPC_NOTOC: return "notoc"; Index: llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp =================================================================== --- llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp +++ llvm/lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp @@ -126,7 +126,15 @@ errs() << '\n'; report_fatal_error("Invalid PC-relative half16ds relocation"); case PPC::fixup_ppc_pcrel34: - Type = ELF::R_PPC64_PCREL34; + switch (Modifier) { + default: llvm_unreachable("Unsupported Modifier for fixup_ppc_pcrel34"); + case MCSymbolRefExpr::VK_PCREL: + Type = ELF::R_PPC64_PCREL34; + break; + case MCSymbolRefExpr::VK_PPC_GOT_PCREL: + Type = ELF::R_PPC64_GOT_PCREL34; + break; + } break; case FK_Data_4: case FK_PCRel_4: Index: llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp =================================================================== --- llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp +++ llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCCodeEmitter.cpp @@ -192,8 +192,9 @@ if (MO.isExpr()) { const MCExpr *Expr = MO.getExpr(); const MCSymbolRefExpr *SRE = cast(Expr); - assert(SRE->getKind() == MCSymbolRefExpr::VK_PCREL && - "VariantKind must be VK_PCREL"); + assert((SRE->getKind() == MCSymbolRefExpr::VK_PCREL || + SRE->getKind() == MCSymbolRefExpr::VK_PPC_GOT_PCREL) && + "VariantKind must be VK_PCREL or VK_PPC_GOT_PCREL"); Fixups.push_back( MCFixup::create(IsLittleEndian ? 0 : 1, Expr, static_cast(PPC::fixup_ppc_pcrel34))); Index: llvm/lib/Target/PowerPC/PPC.h =================================================================== --- llvm/lib/Target/PowerPC/PPC.h +++ llvm/lib/Target/PowerPC/PPC.h @@ -102,6 +102,11 @@ /// the current instruction address(pc), e.g., var@pcrel. Fixup is VK_PCREL. MO_PCREL_FLAG = 16, + /// MO_GOT_FLAG - If this bit is set the symbol reference is to be computed + /// via the GOT. For example when combined with the MO_PCREL_FLAG it should + /// produce the relocation @got@pcrel. Fixup is VK_PPC_GOT_PCREL. + MO_GOT_FLAG = 32, + /// The next are not flags but distinct values. MO_ACCESS_MASK = 0xf00, Index: llvm/lib/Target/PowerPC/PPCISelLowering.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -2551,9 +2551,9 @@ ConstantPoolSDNode *ConstPoolNode = dyn_cast(N.getNode()); bool ConstPool = ConstPoolNode && - ConstPoolNode->getTargetFlags() == PPCII::MO_PCREL_FLAG; + ConstPoolNode->getTargetFlags() | PPCII::MO_PCREL_FLAG; GlobalAddressSDNode *GSDN = dyn_cast(N.getNode()); - bool Global = GSDN && GSDN->getTargetFlags() == PPCII::MO_PCREL_FLAG; + bool Global = GSDN && GSDN->getTargetFlags() | PPCII::MO_PCREL_FLAG; if (ConstPool || Global) { Base = N; return true; @@ -3009,11 +3009,21 @@ // 64-bit SVR4 ABI & AIX ABI code is always position-independent. // The actual address of the GlobalValue is stored in the TOC. if (Subtarget.is64BitELFABI() || Subtarget.isAIXABI()) { - if (!isAccessedAsGotIndirect(Op) && Subtarget.hasPCRelativeMemops()) { + if (Subtarget.hasPCRelativeMemops()) { EVT Ty = getPointerTy(DAG.getDataLayout()); - SDValue GA = DAG.getTargetGlobalAddress(GV, DL, Ty, GSDN->getOffset(), - PPCII::MO_PCREL_FLAG); - return DAG.getNode(PPCISD::MAT_PCREL_ADDR, DL, Ty, GA); + if (isAccessedAsGotIndirect(Op)) { + SDValue GA = DAG.getTargetGlobalAddress(GV, DL, Ty, GSDN->getOffset(), + PPCII::MO_PCREL_FLAG | + PPCII::MO_GOT_FLAG); + SDValue MatPCRel = DAG.getNode(PPCISD::MAT_PCREL_ADDR, DL, Ty, GA); + SDValue Load = DAG.getLoad(MVT::i64, DL, DAG.getEntryNode(), + MatPCRel, MachinePointerInfo()); + return Load; + } else { + SDValue GA = DAG.getTargetGlobalAddress(GV, DL, Ty, GSDN->getOffset(), + PPCII::MO_PCREL_FLAG); + return DAG.getNode(PPCISD::MAT_PCREL_ADDR, DL, Ty, GA); + } } setUsesTOCBasePtr(DAG); SDValue GA = DAG.getTargetGlobalAddress(GV, DL, PtrVT, GSDN->getOffset()); Index: llvm/lib/Target/PowerPC/PPCInstrInfo.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCInstrInfo.cpp +++ llvm/lib/Target/PowerPC/PPCInstrInfo.cpp @@ -2048,7 +2048,8 @@ static const std::pair TargetFlags[] = { {MO_PLT, "ppc-plt"}, {MO_PIC_FLAG, "ppc-pic"}, - {MO_PCREL_FLAG, "ppc-pcrel"}}; + {MO_PCREL_FLAG, "ppc-pcrel"}, + {MO_GOT_FLAG, "ppc-got"}}; return makeArrayRef(TargetFlags); } Index: llvm/lib/Target/PowerPC/PPCMCInstLower.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCMCInstLower.cpp +++ llvm/lib/Target/PowerPC/PPCMCInstLower.cpp @@ -82,6 +82,8 @@ RefKind = MCSymbolRefExpr::VK_PLT; else if (MO.getTargetFlags() == PPCII::MO_PCREL_FLAG) RefKind = MCSymbolRefExpr::VK_PCREL; + else if (MO.getTargetFlags() == (PPCII::MO_PCREL_FLAG | PPCII::MO_GOT_FLAG)) + RefKind = MCSymbolRefExpr::VK_PPC_GOT_PCREL; const MachineFunction *MF = MO.getParent()->getParent()->getParent(); const Module *M = MF->getFunction().getParent(); Index: llvm/test/CodeGen/PowerPC/pcrel-got-indirect.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/pcrel-got-indirect.ll @@ -0,0 +1,253 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-linux-gnu \ +; RUN: -mcpu=future -enable-ppc-quad-precision -ppc-asm-full-reg-names \ +; RUN: -ppc-vsr-nums-as-vr < %s | FileCheck %s + +%struct.Struct = type { i8, i16, i32 } + +@valChar = external local_unnamed_addr global i8, align 1 +@valShort = external local_unnamed_addr global i16, align 2 +@valInt = external global i32, align 4 +@valUnsigned = external local_unnamed_addr global i32, align 4 +@valLong = external local_unnamed_addr global i64, align 8 +@ptr = external local_unnamed_addr global i32*, align 8 +@array = external local_unnamed_addr global [10 x i32], align 4 +@structure = external local_unnamed_addr global %struct.Struct, align 4 +@ptrfunc = external local_unnamed_addr global void (...)*, align 8 + +define dso_local signext i32 @ReadGlobalVarChar() local_unnamed_addr { +; CHECK-LABEL: ReadGlobalVarChar: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valChar@got@pcrel(0), 1 +; CHECK-NEXT: lbz r3, 0(r3) +; CHECK-NEXT: blr +entry: + %0 = load i8, i8* @valChar, align 1 + %conv = zext i8 %0 to i32 + ret i32 %conv +} + +define dso_local void @WriteGlobalVarChar() local_unnamed_addr { +; CHECK-LABEL: WriteGlobalVarChar: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valChar@got@pcrel(0), 1 +; CHECK-NEXT: li r4, 3 +; CHECK-NEXT: stb r4, 0(r3) +; CHECK-NEXT: blr +entry: + store i8 3, i8* @valChar, align 1 + ret void +} + +define dso_local signext i32 @ReadGlobalVarShort() local_unnamed_addr { +; CHECK-LABEL: ReadGlobalVarShort: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valShort@got@pcrel(0), 1 +; CHECK-NEXT: lha r3, 0(r3) +; CHECK-NEXT: blr +entry: + %0 = load i16, i16* @valShort, align 2 + %conv = sext i16 %0 to i32 + ret i32 %conv +} + +define dso_local void @WriteGlobalVarShort() local_unnamed_addr { +; CHECK-LABEL: WriteGlobalVarShort: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valShort@got@pcrel(0), 1 +; CHECK-NEXT: li r4, 3 +; CHECK-NEXT: sth r4, 0(r3) +; CHECK-NEXT: blr +entry: + store i16 3, i16* @valShort, align 2 + ret void +} + +define dso_local signext i32 @ReadGlobalVarInt() local_unnamed_addr { +; CHECK-LABEL: ReadGlobalVarInt: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valInt@got@pcrel(0), 1 +; CHECK-NEXT: lwa r3, 0(r3) +; CHECK-NEXT: blr +entry: + %0 = load i32, i32* @valInt, align 4 + ret i32 %0 +} + +define dso_local void @WriteGlobalVarInt() local_unnamed_addr { +; CHECK-LABEL: WriteGlobalVarInt: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valInt@got@pcrel(0), 1 +; CHECK-NEXT: li r4, 33 +; CHECK-NEXT: stw r4, 0(r3) +; CHECK-NEXT: blr +entry: + store i32 33, i32* @valInt, align 4 + ret void +} + +define dso_local signext i32 @ReadGlobalVarUnsigned() local_unnamed_addr { +; CHECK-LABEL: ReadGlobalVarUnsigned: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valUnsigned@got@pcrel(0), 1 +; CHECK-NEXT: lwa r3, 0(r3) +; CHECK-NEXT: blr +entry: + %0 = load i32, i32* @valUnsigned, align 4 + ret i32 %0 +} + +define dso_local void @WriteGlobalVarUnsigned() local_unnamed_addr { +; CHECK-LABEL: WriteGlobalVarUnsigned: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valUnsigned@got@pcrel(0), 1 +; CHECK-NEXT: li r4, 33 +; CHECK-NEXT: stw r4, 0(r3) +; CHECK-NEXT: blr +entry: + store i32 33, i32* @valUnsigned, align 4 + ret void +} + +define dso_local signext i32 @ReadGlobalVarLong() local_unnamed_addr { +; CHECK-LABEL: ReadGlobalVarLong: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valLong@got@pcrel(0), 1 +; CHECK-NEXT: lwa r3, 0(r3) +; CHECK-NEXT: blr +entry: + %0 = load i64, i64* @valLong, align 8 + %conv = trunc i64 %0 to i32 + ret i32 %conv +} + +define dso_local void @WriteGlobalVarLong() local_unnamed_addr { +; CHECK-LABEL: WriteGlobalVarLong: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valLong@got@pcrel(0), 1 +; CHECK-NEXT: li r4, 3333 +; CHECK-NEXT: std r4, 0(r3) +; CHECK-NEXT: blr +entry: + store i64 3333, i64* @valLong, align 8 + ret void +} + +define dso_local i32* @ReadGlobalPtr() local_unnamed_addr { +; CHECK-LABEL: ReadGlobalPtr: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, ptr@got@pcrel(0), 1 +; CHECK-NEXT: ld r3, 0(r3) +; CHECK-NEXT: blr +entry: + %0 = load i32*, i32** @ptr, align 8 + ret i32* %0 +} + +define dso_local void @WriteGlobalPtr() local_unnamed_addr { +; CHECK-LABEL: WriteGlobalPtr: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, ptr@got@pcrel(0), 1 +; CHECK-NEXT: li r4, 3 +; CHECK-NEXT: ld r3, 0(r3) +; CHECK-NEXT: stw r4, 0(r3) +; CHECK-NEXT: blr +entry: + %0 = load i32*, i32** @ptr, align 8 + store i32 3, i32* %0, align 4 + ret void +} + +define dso_local nonnull i32* @GlobalVarAddr() local_unnamed_addr { +; CHECK-LABEL: GlobalVarAddr: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, valInt@got@pcrel(0), 1 +; CHECK-NEXT: blr +entry: + ret i32* @valInt +} + +define dso_local signext i32 @ReadGlobalArray() local_unnamed_addr { +; CHECK-LABEL: ReadGlobalArray: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, array@got@pcrel(0), 1 +; CHECK-NEXT: lwa r3, 12(r3) +; CHECK-NEXT: blr +entry: + %0 = load i32, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @array, i64 0, i64 3), align 4 + ret i32 %0 +} + +define dso_local void @WriteGlobalArray() local_unnamed_addr { +; CHECK-LABEL: WriteGlobalArray: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, array@got@pcrel(0), 1 +; CHECK-NEXT: li r4, 5 +; CHECK-NEXT: stw r4, 12(r3) +; CHECK-NEXT: blr +entry: + store i32 5, i32* getelementptr inbounds ([10 x i32], [10 x i32]* @array, i64 0, i64 3), align 4 + ret void +} + +define dso_local signext i32 @ReadGlobalStruct() local_unnamed_addr { +; CHECK-LABEL: ReadGlobalStruct: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, structure@got@pcrel(0), 1 +; CHECK-NEXT: lwa r3, 4(r3) +; CHECK-NEXT: blr +entry: + %0 = load i32, i32* getelementptr inbounds (%struct.Struct, %struct.Struct* @structure, i64 0, i32 2), align 4 + ret i32 %0 +} + +define dso_local void @WriteGlobalStruct() local_unnamed_addr { +; CHECK-LABEL: WriteGlobalStruct: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, structure@got@pcrel(0), 1 +; CHECK-NEXT: li r4, 3 +; CHECK-NEXT: stw r4, 4(r3) +; CHECK-NEXT: blr +entry: + store i32 3, i32* getelementptr inbounds (%struct.Struct, %struct.Struct* @structure, i64 0, i32 2), align 4 + ret void +} + +define dso_local void @ReadFuncPtr() local_unnamed_addr { +; CHECK-LABEL: ReadFuncPtr: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mflr r0 +; CHECK-NEXT: std r0, 16(r1) +; CHECK-NEXT: stdu r1, -32(r1) +; CHECK-NEXT: std r2, 24(r1) +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: .cfi_offset lr, 16 +; CHECK-NEXT: pld r3, ptrfunc@got@pcrel(0), 1 +; CHECK-NEXT: ld r12, 0(r3) +; CHECK-NEXT: mtctr r12 +; CHECK-NEXT: bctrl +; CHECK-NEXT: ld 2, 24(r1) +; CHECK-NEXT: addi r1, r1, 32 +; CHECK-NEXT: ld r0, 16(r1) +; CHECK-NEXT: mtlr r0 +; CHECK-NEXT: blr +entry: + %0 = load void ()*, void ()** bitcast (void (...)** @ptrfunc to void ()**), align 8 + tail call void %0() + ret void +} + +define dso_local void @WriteFuncPtr() local_unnamed_addr { +; CHECK-LABEL: WriteFuncPtr: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pld r3, ptrfunc@got@pcrel(0), 1 +; CHECK-NEXT: pld r4, function@got@pcrel(0), 1 +; CHECK-NEXT: std r4, 0(r3) +; CHECK-NEXT: blr +entry: + store void (...)* @function, void (...)** @ptrfunc, align 8 + ret void +} + +declare void @function(...) +