diff --git a/lib/Target/SystemZ/AsmPrinter/SystemZAsmPrinter.cpp b/lib/Target/SystemZ/AsmPrinter/SystemZAsmPrinter.cpp index 1a911dbc1d6..af3ce1780da 100644 --- a/lib/Target/SystemZ/AsmPrinter/SystemZAsmPrinter.cpp +++ b/lib/Target/SystemZ/AsmPrinter/SystemZAsmPrinter.cpp @@ -50,6 +50,7 @@ namespace { void printOperand(const MachineInstr *MI, int OpNum, const char* Modifier = 0); + void printPCRelImmOperand(const MachineInstr *MI, int OpNum); void printRIAddrOperand(const MachineInstr *MI, int OpNum, const char* Modifier = 0); void printRRIAddrOperand(const MachineInstr *MI, int OpNum, @@ -186,6 +187,40 @@ void SystemZAsmPrinter::printMachineInstruction(const MachineInstr *MI) { assert(0 && "Should not happen"); } +void SystemZAsmPrinter::printPCRelImmOperand(const MachineInstr *MI, int OpNum) { + const MachineOperand &MO = MI->getOperand(OpNum); + switch (MO.getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MO.getGlobal(); + std::string Name = Mang->getValueName(GV); + + O << Name; + + // Assemble calls via PLT for externally visible symbols if PIC. + if (TM.getRelocationModel() == Reloc::PIC_ && + !GV->hasHiddenVisibility() && !GV->hasProtectedVisibility() && + !GV->hasLocalLinkage()) + O << "@PLT"; + + printOffset(MO.getOffset()); + return; + } + case MachineOperand::MO_ExternalSymbol: { + std::string Name(TAI->getGlobalPrefix()); + Name += MO.getSymbolName(); + O << Name; + + if (TM.getRelocationModel() == Reloc::PIC_) + O << "@PLT"; + + return; + } + default: + assert(0 && "Not implemented yet!"); + } +} + + void SystemZAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, const char* Modifier) { const MachineOperand &MO = MI->getOperand(OpNum); @@ -219,23 +254,31 @@ void SystemZAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, return; case MachineOperand::MO_GlobalAddress: { const GlobalValue *GV = MO.getGlobal(); - std::string Name = Mang->getValueName(GV); - assert(MO.getOffset() == 0 && "No offsets allowed!"); O << Name; - - return; + break; } case MachineOperand::MO_ExternalSymbol: { std::string Name(TAI->getGlobalPrefix()); Name += MO.getSymbolName(); O << Name; - return; + break; } default: assert(0 && "Not implemented yet!"); } + + switch (MO.getTargetFlags()) { + default: + assert(0 && "Unknown target flag on GV operand"); + case SystemZII::MO_NO_FLAG: + break; + case SystemZII::MO_GOTENT: O << "@GOTENT"; break; + case SystemZII::MO_PLT: O << "@PLT"; break; + } + + printOffset(MO.getOffset()); } void SystemZAsmPrinter::printRIAddrOperand(const MachineInstr *MI, int OpNum, diff --git a/lib/Target/SystemZ/SystemZISelLowering.cpp b/lib/Target/SystemZ/SystemZISelLowering.cpp index 6dea471dd95..ae1c8b72e42 100644 --- a/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -545,10 +545,38 @@ SDValue SystemZTargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) { DebugLoc dl = Op.getDebugLoc(); GlobalValue *GV = cast(Op)->getGlobal(); - SDValue GA = DAG.getTargetGlobalAddress(GV, getPointerTy()); + int64_t Offset = cast(Op)->getOffset(); - // FIXME: Verify stuff for constant globals entries - return DAG.getNode(SystemZISD::PCRelativeWrapper, dl, getPointerTy(), GA); + bool IsPic = getTargetMachine().getRelocationModel() == Reloc::PIC_; + bool ExtraLoadRequired = + Subtarget.GVRequiresExtraLoad(GV, getTargetMachine(), false); + + SDValue Result; + if (!IsPic && !ExtraLoadRequired) { + Result = DAG.getTargetGlobalAddress(GV, getPointerTy(), Offset); + Offset = 0; + } else { + unsigned char OpFlags = 0; + if (ExtraLoadRequired) + OpFlags = SystemZII::MO_GOTENT; + + Result = DAG.getTargetGlobalAddress(GV, getPointerTy(), 0, OpFlags); + } + + Result = DAG.getNode(SystemZISD::PCRelativeWrapper, dl, + getPointerTy(), Result); + + if (ExtraLoadRequired) + Result = DAG.getLoad(getPointerTy(), dl, DAG.getEntryNode(), Result, + PseudoSourceValue::getGOT(), 0); + + // If there was a non-zero offset that we didn't fold, create an explicit + // addition for it. + if (Offset != 0) + Result = DAG.getNode(ISD::ADD, dl, getPointerTy(), Result, + DAG.getConstant(Offset, getPointerTy())); + + return Result; } diff --git a/lib/Target/SystemZ/SystemZInstrInfo.h b/lib/Target/SystemZ/SystemZInstrInfo.h index 541c38bab79..812509a0ce9 100644 --- a/lib/Target/SystemZ/SystemZInstrInfo.h +++ b/lib/Target/SystemZ/SystemZInstrInfo.h @@ -23,6 +23,30 @@ namespace llvm { class SystemZTargetMachine; +/// SystemZII - This namespace holds all of the target specific flags that +/// instruction info tracks. +/// +namespace SystemZII { + enum { + //===------------------------------------------------------------------===// + // SystemZ Specific MachineOperand flags. + + MO_NO_FLAG = 0, + + /// MO_GOTENT - On a symbol operand this indicates that the immediate is + /// the offset to the location of the symbol name from the base of the GOT. + /// + /// SYMBOL_LABEL @GOTENT + MO_GOTENT = 1, + + /// MO_PLT - On a symbol operand this indicates that the immediate is + /// offset to the PLT entry of symbol name from the current code location. + /// + /// SYMBOL_LABEL @PLT + MO_PLT = 2 + }; +} + class SystemZInstrInfo : public TargetInstrInfoImpl { const SystemZRegisterInfo RI; SystemZTargetMachine &TM; diff --git a/lib/Target/SystemZ/SystemZInstrInfo.td b/lib/Target/SystemZ/SystemZInstrInfo.td index 1784331e3be..b4e25e6c3ad 100644 --- a/lib/Target/SystemZ/SystemZInstrInfo.td +++ b/lib/Target/SystemZ/SystemZInstrInfo.td @@ -134,7 +134,7 @@ let isCall = 1 in // All calls clobber the non-callee saved registers. Uses for argument // registers are added manually. let Defs = [R0D, R1D, R2D, R3D, R4D, R5D, R14D] in { - def CALLi : Pseudo<(outs), (ins i64imm:$dst, variable_ops), + def CALLi : Pseudo<(outs), (ins imm_pcrel:$dst, variable_ops), "brasl\t%r14, $dst", [(SystemZcall imm:$dst)]>; def CALLr : Pseudo<(outs), (ins ADDR64:$dst, variable_ops), "basr\t%r14, $dst", [(SystemZcall ADDR64:$dst)]>; diff --git a/lib/Target/SystemZ/SystemZOperands.td b/lib/Target/SystemZ/SystemZOperands.td index 8eccd3a4edf..55afbed8bc9 100644 --- a/lib/Target/SystemZ/SystemZOperands.td +++ b/lib/Target/SystemZ/SystemZOperands.td @@ -244,6 +244,10 @@ def s32imm64 : Operand { let PrintMethod = "printS32ImmOperand"; } +def imm_pcrel : Operand { + let PrintMethod = "printPCRelImmOperand"; +} + //===----------------------------------------------------------------------===// // SystemZ Operand Definitions. //===----------------------------------------------------------------------===// diff --git a/lib/Target/SystemZ/SystemZSubtarget.cpp b/lib/Target/SystemZ/SystemZSubtarget.cpp index 9c3262e7221..18a8e1633da 100644 --- a/lib/Target/SystemZ/SystemZSubtarget.cpp +++ b/lib/Target/SystemZ/SystemZSubtarget.cpp @@ -14,6 +14,7 @@ #include "SystemZSubtarget.h" #include "SystemZ.h" #include "SystemZGenSubtarget.inc" +#include "llvm/GlobalValue.h" #include "llvm/Target/TargetMachine.h" using namespace llvm; @@ -26,3 +27,21 @@ SystemZSubtarget::SystemZSubtarget(const TargetMachine &TM, const Module &M, // Parse features string. ParseSubtargetFeatures(FS, CPU); } + +/// True if accessing the GV requires an extra load. +bool SystemZSubtarget::GVRequiresExtraLoad(const GlobalValue* GV, + const TargetMachine& TM, + bool isDirectCall) const { + if (TM.getRelocationModel() == Reloc::PIC_) { + // Extra load is needed for all externally visible. + if (isDirectCall) + return false; + + if (GV->hasLocalLinkage() || GV->hasHiddenVisibility()) + return false; + + return true; + } + + return false; +} diff --git a/lib/Target/SystemZ/SystemZSubtarget.h b/lib/Target/SystemZ/SystemZSubtarget.h index 41a3741a354..fd8212c8727 100644 --- a/lib/Target/SystemZ/SystemZSubtarget.h +++ b/lib/Target/SystemZ/SystemZSubtarget.h @@ -21,6 +21,7 @@ namespace llvm { class Module; class TargetMachine; +class GlobalValue; class SystemZSubtarget : public TargetSubtarget { bool HasZ10Insts; @@ -37,6 +38,9 @@ public: const std::string &CPU); bool isZ10() const { return HasZ10Insts; } + + bool GVRequiresExtraLoad(const GlobalValue* GV, const TargetMachine& TM, + bool isDirectCall) const; }; } // End llvm namespace diff --git a/lib/Target/SystemZ/SystemZTargetMachine.cpp b/lib/Target/SystemZ/SystemZTargetMachine.cpp index e8aa6b573b3..4be8ea99364 100644 --- a/lib/Target/SystemZ/SystemZTargetMachine.cpp +++ b/lib/Target/SystemZ/SystemZTargetMachine.cpp @@ -42,6 +42,9 @@ SystemZTargetMachine::SystemZTargetMachine(const Module &M, const std::string &F DataLayout("E-p:64:64:64-i8:8:16-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-a0:16:16"), InstrInfo(*this), TLInfo(*this), FrameInfo(TargetFrameInfo::StackGrowsDown, 8, -160) { + + if (getRelocationModel() == Reloc::Default) + setRelocationModel(Reloc::Static); } bool SystemZTargetMachine::addInstSelector(PassManagerBase &PM, diff --git a/test/CodeGen/SystemZ/10-FuncsPic.ll b/test/CodeGen/SystemZ/10-FuncsPic.ll new file mode 100644 index 00000000000..a3fb4dcc3a0 --- /dev/null +++ b/test/CodeGen/SystemZ/10-FuncsPic.ll @@ -0,0 +1,27 @@ +; RUN: llvm-as < %s | llc -relocation-model=pic | grep GOTENT | count 3 +; RUN: llvm-as < %s | llc -relocation-model=pic | grep PLT | count 1 + +target datalayout = "E-p:64:64:64-i8:8:16-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-a0:16:16" +target triple = "s390x-linux" +@ptr = external global void (...)* ; [#uses=2] + +define void @foo1() nounwind { +entry: + store void (...)* @func, void (...)** @ptr + ret void +} + +declare void @func(...) + +define void @foo2() nounwind { +entry: + tail call void (...)* @func() nounwind + ret void +} + +define void @foo3() nounwind { +entry: + %tmp = load void (...)** @ptr ; [#uses=1] + tail call void (...)* %tmp() nounwind + ret void +} diff --git a/test/CodeGen/SystemZ/10-GlobalsPic.ll b/test/CodeGen/SystemZ/10-GlobalsPic.ll new file mode 100644 index 00000000000..434e9226b34 --- /dev/null +++ b/test/CodeGen/SystemZ/10-GlobalsPic.ll @@ -0,0 +1,29 @@ +; RUN: llvm-as < %s | llc -relocation-model=pic | grep GOTENT | count 6 + +target datalayout = "E-p:64:64:64-i8:8:16-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-a0:16:16" +target triple = "s390x-linux" +@src = external global i32 ; [#uses=2] +@dst = external global i32 ; [#uses=2] +@ptr = external global i32* ; [#uses=2] + +define void @foo1() nounwind { +entry: + %tmp = load i32* @src ; [#uses=1] + store i32 %tmp, i32* @dst + ret void +} + +define void @foo2() nounwind { +entry: + store i32* @dst, i32** @ptr + ret void +} + +define void @foo3() nounwind { +entry: + %tmp = load i32* @src ; [#uses=1] + %tmp1 = load i32** @ptr ; [#uses=1] + %arrayidx = getelementptr i32* %tmp1, i64 1 ; [#uses=1] + store i32 %tmp, i32* %arrayidx + ret void +}