[SystemZ] Support all TLS access models - CodeGen part

The current SystemZ back-end only supports the local-exec TLS access model.
This patch adds all required CodeGen support for the other TLS models, which
means in particular:

- Expand initial-exec TLS accesses by loading TLS offsets from the GOT
  using @indntpoff relocations.

- Expand general-dynamic and local-dynamic accesses by generating the
  appropriate calls to __tls_get_offset.  Note that this routine has
  a non-standard ABI and requires loading the GOT pointer into %r12,
  so the patch also adds support for the GLOBAL_OFFSET_TABLE ISD node.

- Add a new platform-specific optimization pass to remove redundant
  __tls_get_offset calls in the local-dynamic model (modeled after
  the corresponding X86 pass).

- Add test cases verifying all access models and optimizations.



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@229654 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Ulrich Weigand
2015-02-18 09:13:27 +00:00
parent 5d27960454
commit bebd59c74b
21 changed files with 482 additions and 19 deletions

View File

@@ -20,6 +20,7 @@ add_llvm_target(SystemZCodeGen
SystemZISelDAGToDAG.cpp SystemZISelDAGToDAG.cpp
SystemZISelLowering.cpp SystemZISelLowering.cpp
SystemZInstrInfo.cpp SystemZInstrInfo.cpp
SystemZLDCleanup.cpp
SystemZLongBranch.cpp SystemZLongBranch.cpp
SystemZMachineFunctionInfo.cpp SystemZMachineFunctionInfo.cpp
SystemZMCInstLower.cpp SystemZMCInstLower.cpp

View File

@@ -111,6 +111,7 @@ FunctionPass *createSystemZISelDag(SystemZTargetMachine &TM,
FunctionPass *createSystemZElimComparePass(SystemZTargetMachine &TM); FunctionPass *createSystemZElimComparePass(SystemZTargetMachine &TM);
FunctionPass *createSystemZShortenInstPass(SystemZTargetMachine &TM); FunctionPass *createSystemZShortenInstPass(SystemZTargetMachine &TM);
FunctionPass *createSystemZLongBranchPass(SystemZTargetMachine &TM); FunctionPass *createSystemZLongBranchPass(SystemZTargetMachine &TM);
FunctionPass *createSystemZLDCleanupPass(SystemZTargetMachine &TM);
} // end namespace llvm } // end namespace llvm
#endif #endif

View File

@@ -66,6 +66,20 @@ static MCInst lowerRIEfLow(const MachineInstr *MI, unsigned Opcode) {
.addImm(MI->getOperand(5).getImm()); .addImm(MI->getOperand(5).getImm());
} }
static const MCSymbolRefExpr *getTLSGetOffset(MCContext &Context) {
StringRef Name = "__tls_get_offset";
return MCSymbolRefExpr::Create(Context.GetOrCreateSymbol(Name),
MCSymbolRefExpr::VK_PLT,
Context);
}
static const MCSymbolRefExpr *getGlobalOffsetTable(MCContext &Context) {
StringRef Name = "_GLOBAL_OFFSET_TABLE_";
return MCSymbolRefExpr::Create(Context.GetOrCreateSymbol(Name),
MCSymbolRefExpr::VK_None,
Context);
}
void SystemZAsmPrinter::EmitInstruction(const MachineInstr *MI) { void SystemZAsmPrinter::EmitInstruction(const MachineInstr *MI) {
SystemZMCInstLower Lower(MF->getContext(), *this); SystemZMCInstLower Lower(MF->getContext(), *this);
MCInst LoweredMI; MCInst LoweredMI;
@@ -95,6 +109,26 @@ void SystemZAsmPrinter::EmitInstruction(const MachineInstr *MI) {
LoweredMI = MCInstBuilder(SystemZ::BR).addReg(SystemZ::R1D); LoweredMI = MCInstBuilder(SystemZ::BR).addReg(SystemZ::R1D);
break; break;
case SystemZ::TLS_GDCALL:
LoweredMI = MCInstBuilder(SystemZ::BRASL)
.addReg(SystemZ::R14D)
.addExpr(getTLSGetOffset(MF->getContext()))
.addExpr(Lower.getExpr(MI->getOperand(0), MCSymbolRefExpr::VK_TLSGD));
break;
case SystemZ::TLS_LDCALL:
LoweredMI = MCInstBuilder(SystemZ::BRASL)
.addReg(SystemZ::R14D)
.addExpr(getTLSGetOffset(MF->getContext()))
.addExpr(Lower.getExpr(MI->getOperand(0), MCSymbolRefExpr::VK_TLSLDM));
break;
case SystemZ::GOT:
LoweredMI = MCInstBuilder(SystemZ::LARL)
.addReg(MI->getOperand(0).getReg())
.addExpr(getGlobalOffsetTable(MF->getContext()));
break;
case SystemZ::IILF64: case SystemZ::IILF64:
LoweredMI = MCInstBuilder(SystemZ::IILF) LoweredMI = MCInstBuilder(SystemZ::IILF)
.addReg(SystemZMC::getRegAsGR32(MI->getOperand(0).getReg())) .addReg(SystemZMC::getRegAsGR32(MI->getOperand(0).getReg()))
@@ -172,6 +206,9 @@ void SystemZAsmPrinter::EmitInstruction(const MachineInstr *MI) {
static MCSymbolRefExpr::VariantKind static MCSymbolRefExpr::VariantKind
getModifierVariantKind(SystemZCP::SystemZCPModifier Modifier) { getModifierVariantKind(SystemZCP::SystemZCPModifier Modifier) {
switch (Modifier) { switch (Modifier) {
case SystemZCP::TLSGD: return MCSymbolRefExpr::VK_TLSGD;
case SystemZCP::TLSLDM: return MCSymbolRefExpr::VK_TLSLDM;
case SystemZCP::DTPOFF: return MCSymbolRefExpr::VK_DTPOFF;
case SystemZCP::NTPOFF: return MCSymbolRefExpr::VK_NTPOFF; case SystemZCP::NTPOFF: return MCSymbolRefExpr::VK_NTPOFF;
} }
llvm_unreachable("Invalid SystemCPModifier!"); llvm_unreachable("Invalid SystemCPModifier!");

View File

@@ -28,6 +28,11 @@ SystemZConstantPoolValue::Create(const GlobalValue *GV,
unsigned SystemZConstantPoolValue::getRelocationInfo() const { unsigned SystemZConstantPoolValue::getRelocationInfo() const {
switch (Modifier) { switch (Modifier) {
case SystemZCP::TLSGD:
case SystemZCP::TLSLDM:
case SystemZCP::DTPOFF:
// May require a dynamic relocation.
return 2;
case SystemZCP::NTPOFF: case SystemZCP::NTPOFF:
// May require a relocation, but the relocations are always resolved // May require a relocation, but the relocations are always resolved
// by the static linker. // by the static linker.

View File

@@ -19,13 +19,17 @@ class GlobalValue;
namespace SystemZCP { namespace SystemZCP {
enum SystemZCPModifier { enum SystemZCPModifier {
TLSGD,
TLSLDM,
DTPOFF,
NTPOFF NTPOFF
}; };
} // end namespace SystemZCP } // end namespace SystemZCP
/// A SystemZ-specific constant pool value. At present, the only /// A SystemZ-specific constant pool value. At present, the only
/// defined constant pool values are offsets of thread-local variables /// defined constant pool values are module IDs or offsets of
/// (written x@NTPOFF). /// thread-local variables (written x@TLSGD, x@TLSLDM, x@DTPOFF,
/// or x@NTPOFF).
class SystemZConstantPoolValue : public MachineConstantPoolValue { class SystemZConstantPoolValue : public MachineConstantPoolValue {
const GlobalValue *GV; const GlobalValue *GV;
SystemZCP::SystemZCPModifier Modifier; SystemZCP::SystemZCPModifier Modifier;

View File

@@ -1830,6 +1830,52 @@ SDValue SystemZTargetLowering::lowerGlobalAddress(GlobalAddressSDNode *Node,
return Result; return Result;
} }
SDValue SystemZTargetLowering::lowerTLSGetOffset(GlobalAddressSDNode *Node,
SelectionDAG &DAG,
unsigned Opcode,
SDValue GOTOffset) const {
SDLoc DL(Node);
EVT PtrVT = getPointerTy();
SDValue Chain = DAG.getEntryNode();
SDValue Glue;
// __tls_get_offset takes the GOT offset in %r2 and the GOT in %r12.
SDValue GOT = DAG.getGLOBAL_OFFSET_TABLE(PtrVT);
Chain = DAG.getCopyToReg(Chain, DL, SystemZ::R12D, GOT, Glue);
Glue = Chain.getValue(1);
Chain = DAG.getCopyToReg(Chain, DL, SystemZ::R2D, GOTOffset, Glue);
Glue = Chain.getValue(1);
// The first call operand is the chain and the second is the TLS symbol.
SmallVector<SDValue, 8> Ops;
Ops.push_back(Chain);
Ops.push_back(DAG.getTargetGlobalAddress(Node->getGlobal(), DL,
Node->getValueType(0),
0, 0));
// Add argument registers to the end of the list so that they are
// known live into the call.
Ops.push_back(DAG.getRegister(SystemZ::R2D, PtrVT));
Ops.push_back(DAG.getRegister(SystemZ::R12D, PtrVT));
// Add a register mask operand representing the call-preserved registers.
const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo();
const uint32_t *Mask = TRI->getCallPreservedMask(CallingConv::C);
assert(Mask && "Missing call preserved mask for calling convention");
Ops.push_back(DAG.getRegisterMask(Mask));
// Glue the call to the argument copies.
Ops.push_back(Glue);
// Emit the call.
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
Chain = DAG.getNode(Opcode, DL, NodeTys, Ops);
Glue = Chain.getValue(1);
// Copy the return value from %r2.
return DAG.getCopyFromReg(Chain, DL, SystemZ::R2D, PtrVT, Glue);
}
SDValue SystemZTargetLowering::lowerGlobalTLSAddress(GlobalAddressSDNode *Node, SDValue SystemZTargetLowering::lowerGlobalTLSAddress(GlobalAddressSDNode *Node,
SelectionDAG &DAG) const { SelectionDAG &DAG) const {
SDLoc DL(Node); SDLoc DL(Node);
@@ -1837,9 +1883,6 @@ SDValue SystemZTargetLowering::lowerGlobalTLSAddress(GlobalAddressSDNode *Node,
EVT PtrVT = getPointerTy(); EVT PtrVT = getPointerTy();
TLSModel::Model model = DAG.getTarget().getTLSModel(GV); TLSModel::Model model = DAG.getTarget().getTLSModel(GV);
if (model != TLSModel::LocalExec)
llvm_unreachable("only local-exec TLS mode supported");
// The high part of the thread pointer is in access register 0. // The high part of the thread pointer is in access register 0.
SDValue TPHi = DAG.getNode(SystemZISD::EXTRACT_ACCESS, DL, MVT::i32, SDValue TPHi = DAG.getNode(SystemZISD::EXTRACT_ACCESS, DL, MVT::i32,
DAG.getConstant(0, MVT::i32)); DAG.getConstant(0, MVT::i32));
@@ -1855,15 +1898,82 @@ SDValue SystemZTargetLowering::lowerGlobalTLSAddress(GlobalAddressSDNode *Node,
DAG.getConstant(32, PtrVT)); DAG.getConstant(32, PtrVT));
SDValue TP = DAG.getNode(ISD::OR, DL, PtrVT, TPHiShifted, TPLo); SDValue TP = DAG.getNode(ISD::OR, DL, PtrVT, TPHiShifted, TPLo);
// Get the offset of GA from the thread pointer. // Get the offset of GA from the thread pointer, based on the TLS model.
SystemZConstantPoolValue *CPV = SDValue Offset;
SystemZConstantPoolValue::Create(GV, SystemZCP::NTPOFF); switch (model) {
case TLSModel::GeneralDynamic: {
// Load the GOT offset of the tls_index (module ID / per-symbol offset).
SystemZConstantPoolValue *CPV =
SystemZConstantPoolValue::Create(GV, SystemZCP::TLSGD);
// Force the offset into the constant pool and load it from there. Offset = DAG.getConstantPool(CPV, PtrVT, 8);
SDValue CPAddr = DAG.getConstantPool(CPV, PtrVT, 8); Offset = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(),
SDValue Offset = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Offset, MachinePointerInfo::getConstantPool(),
CPAddr, MachinePointerInfo::getConstantPool(), false, false, false, 0);
false, false, false, 0);
// Call __tls_get_offset to retrieve the offset.
Offset = lowerTLSGetOffset(Node, DAG, SystemZISD::TLS_GDCALL, Offset);
break;
}
case TLSModel::LocalDynamic: {
// Load the GOT offset of the module ID.
SystemZConstantPoolValue *CPV =
SystemZConstantPoolValue::Create(GV, SystemZCP::TLSLDM);
Offset = DAG.getConstantPool(CPV, PtrVT, 8);
Offset = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(),
Offset, MachinePointerInfo::getConstantPool(),
false, false, false, 0);
// Call __tls_get_offset to retrieve the module base offset.
Offset = lowerTLSGetOffset(Node, DAG, SystemZISD::TLS_LDCALL, Offset);
// Note: The SystemZLDCleanupPass will remove redundant computations
// of the module base offset. Count total number of local-dynamic
// accesses to trigger execution of that pass.
SystemZMachineFunctionInfo* MFI =
DAG.getMachineFunction().getInfo<SystemZMachineFunctionInfo>();
MFI->incNumLocalDynamicTLSAccesses();
// Add the per-symbol offset.
CPV = SystemZConstantPoolValue::Create(GV, SystemZCP::DTPOFF);
SDValue DTPOffset = DAG.getConstantPool(CPV, PtrVT, 8);
DTPOffset = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(),
DTPOffset, MachinePointerInfo::getConstantPool(),
false, false, false, 0);
Offset = DAG.getNode(ISD::ADD, DL, PtrVT, Offset, DTPOffset);
break;
}
case TLSModel::InitialExec: {
// Load the offset from the GOT.
Offset = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0,
SystemZII::MO_INDNTPOFF);
Offset = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Offset);
Offset = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(),
Offset, MachinePointerInfo::getGOT(),
false, false, false, 0);
break;
}
case TLSModel::LocalExec: {
// Force the offset into the constant pool and load it from there.
SystemZConstantPoolValue *CPV =
SystemZConstantPoolValue::Create(GV, SystemZCP::NTPOFF);
Offset = DAG.getConstantPool(CPV, PtrVT, 8);
Offset = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(),
Offset, MachinePointerInfo::getConstantPool(),
false, false, false, 0);
break;
}
default:
llvm_unreachable("Unknown TLS model.");
}
// Add the base and offset together. // Add the base and offset together.
return DAG.getNode(ISD::ADD, DL, PtrVT, TP, Offset); return DAG.getNode(ISD::ADD, DL, PtrVT, TP, Offset);

View File

@@ -34,6 +34,11 @@ enum {
CALL, CALL,
SIBCALL, SIBCALL,
// TLS calls. Like regular calls, except operand 1 is the TLS symbol.
// (The call target is implicitly __tls_get_offset.)
TLS_GDCALL,
TLS_LDCALL,
// Wraps a TargetGlobalAddress that should be loaded using PC-relative // Wraps a TargetGlobalAddress that should be loaded using PC-relative
// accesses (LARL). Operand 0 is the address. // accesses (LARL). Operand 0 is the address.
PCREL_WRAPPER, PCREL_WRAPPER,
@@ -258,6 +263,9 @@ private:
SDValue lowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const; SDValue lowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerGlobalAddress(GlobalAddressSDNode *Node, SDValue lowerGlobalAddress(GlobalAddressSDNode *Node,
SelectionDAG &DAG) const; SelectionDAG &DAG) const;
SDValue lowerTLSGetOffset(GlobalAddressSDNode *Node,
SelectionDAG &DAG, unsigned Opcode,
SDValue GOTOffset) const;
SDValue lowerGlobalTLSAddress(GlobalAddressSDNode *Node, SDValue lowerGlobalTLSAddress(GlobalAddressSDNode *Node,
SelectionDAG &DAG) const; SelectionDAG &DAG) const;
SDValue lowerBlockAddress(BlockAddressSDNode *Node, SDValue lowerBlockAddress(BlockAddressSDNode *Node,

View File

@@ -56,10 +56,13 @@ static inline unsigned getCompareZeroCCMask(unsigned int Flags) {
// SystemZ MachineOperand target flags. // SystemZ MachineOperand target flags.
enum { enum {
// Masks out the bits for the access model. // Masks out the bits for the access model.
MO_SYMBOL_MODIFIER = (1 << 0), MO_SYMBOL_MODIFIER = (3 << 0),
// @GOT (aka @GOTENT) // @GOT (aka @GOTENT)
MO_GOT = (1 << 0) MO_GOT = (1 << 0),
// @INDNTPOFF
MO_INDNTPOFF = (2 << 0)
}; };
// Classifies a branch. // Classifies a branch.
enum BranchType { enum BranchType {

View File

@@ -249,6 +249,15 @@ let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1 in {
def CallBR : Alias<2, (outs), (ins), [(z_sibcall R1D)]>; def CallBR : Alias<2, (outs), (ins), [(z_sibcall R1D)]>;
} }
// TLS calls. These will be lowered into a call to __tls_get_offset,
// with an extra relocation specifying the TLS symbol.
let isCall = 1, Defs = [R14D, CC] in {
def TLS_GDCALL : Alias<6, (outs), (ins tlssym:$I2, variable_ops),
[(z_tls_gdcall tglobaltlsaddr:$I2)]>;
def TLS_LDCALL : Alias<6, (outs), (ins tlssym:$I2, variable_ops),
[(z_tls_ldcall tglobaltlsaddr:$I2)]>;
}
// Define the general form of the call instructions for the asm parser. // Define the general form of the call instructions for the asm parser.
// These instructions don't hard-code %r14 as the return address register. // These instructions don't hard-code %r14 as the return address register.
// Allow an optional TLS marker symbol to generate TLS call relocations. // Allow an optional TLS marker symbol to generate TLS call relocations.
@@ -588,6 +597,12 @@ let hasSideEffects = 0, isAsCheapAsAMove = 1, isMoveImm = 1,
[(set GR64:$R1, pcrel32:$I2)]>; [(set GR64:$R1, pcrel32:$I2)]>;
} }
// Load the Global Offset Table address. This will be lowered into a
// larl $R1, _GLOBAL_OFFSET_TABLE_
// instruction.
def GOT : Alias<6, (outs GR64:$R1), (ins),
[(set GR64:$R1, (global_offset_table))]>;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Absolute and Negation // Absolute and Negation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@@ -0,0 +1,143 @@
//===-- SystemZLDCleanup.cpp - Clean up local-dynamic TLS accesses --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass combines multiple accesses to local-dynamic TLS variables so that
// the TLS base address for the module is only fetched once per execution path
// through the function.
//
//===----------------------------------------------------------------------===//
#include "SystemZTargetMachine.h"
#include "SystemZMachineFunctionInfo.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetRegisterInfo.h"
using namespace llvm;
namespace {
class SystemZLDCleanup : public MachineFunctionPass {
public:
static char ID;
SystemZLDCleanup(const SystemZTargetMachine &tm)
: MachineFunctionPass(ID), TII(nullptr), MF(nullptr) {}
const char *getPassName() const override {
return "SystemZ Local Dynamic TLS Access Clean-up";
}
bool runOnMachineFunction(MachineFunction &MF) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
private:
bool VisitNode(MachineDomTreeNode *Node, unsigned TLSBaseAddrReg);
MachineInstr *ReplaceTLSCall(MachineInstr *I, unsigned TLSBaseAddrReg);
MachineInstr *SetRegister(MachineInstr *I, unsigned *TLSBaseAddrReg);
const SystemZInstrInfo *TII;
MachineFunction *MF;
};
char SystemZLDCleanup::ID = 0;
} // end anonymous namespace
FunctionPass *llvm::createSystemZLDCleanupPass(SystemZTargetMachine &TM) {
return new SystemZLDCleanup(TM);
}
void SystemZLDCleanup::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesCFG();
AU.addRequired<MachineDominatorTree>();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool SystemZLDCleanup::runOnMachineFunction(MachineFunction &F) {
TII = static_cast<const SystemZInstrInfo *>(F.getSubtarget().getInstrInfo());
MF = &F;
SystemZMachineFunctionInfo* MFI = F.getInfo<SystemZMachineFunctionInfo>();
if (MFI->getNumLocalDynamicTLSAccesses() < 2) {
// No point folding accesses if there isn't at least two.
return false;
}
MachineDominatorTree *DT = &getAnalysis<MachineDominatorTree>();
return VisitNode(DT->getRootNode(), 0);
}
// Visit the dominator subtree rooted at Node in pre-order.
// If TLSBaseAddrReg is non-null, then use that to replace any
// TLS_LDCALL instructions. Otherwise, create the register
// when the first such instruction is seen, and then use it
// as we encounter more instructions.
bool SystemZLDCleanup::VisitNode(MachineDomTreeNode *Node,
unsigned TLSBaseAddrReg) {
MachineBasicBlock *BB = Node->getBlock();
bool Changed = false;
// Traverse the current block.
for (auto I = BB->begin(), E = BB->end(); I != E; ++I) {
switch (I->getOpcode()) {
case SystemZ::TLS_LDCALL:
if (TLSBaseAddrReg)
I = ReplaceTLSCall(I, TLSBaseAddrReg);
else
I = SetRegister(I, &TLSBaseAddrReg);
Changed = true;
break;
default:
break;
}
}
// Visit the children of this block in the dominator tree.
for (auto I = Node->begin(), E = Node->end(); I != E; ++I)
Changed |= VisitNode(*I, TLSBaseAddrReg);
return Changed;
}
// Replace the TLS_LDCALL instruction I with a copy from TLSBaseAddrReg,
// returning the new instruction.
MachineInstr *SystemZLDCleanup::ReplaceTLSCall(MachineInstr *I,
unsigned TLSBaseAddrReg) {
// Insert a Copy from TLSBaseAddrReg to R2.
MachineInstr *Copy = BuildMI(*I->getParent(), I, I->getDebugLoc(),
TII->get(TargetOpcode::COPY), SystemZ::R2D)
.addReg(TLSBaseAddrReg);
// Erase the TLS_LDCALL instruction.
I->eraseFromParent();
return Copy;
}
// Create a virtal register in *TLSBaseAddrReg, and populate it by
// inserting a copy instruction after I. Returns the new instruction.
MachineInstr *SystemZLDCleanup::SetRegister(MachineInstr *I,
unsigned *TLSBaseAddrReg) {
// Create a virtual register for the TLS base address.
MachineRegisterInfo &RegInfo = MF->getRegInfo();
*TLSBaseAddrReg = RegInfo.createVirtualRegister(&SystemZ::GR64BitRegClass);
// Insert a copy from R2 to TLSBaseAddrReg.
MachineInstr *Next = I->getNextNode();
MachineInstr *Copy = BuildMI(*I->getParent(), Next, I->getDebugLoc(),
TII->get(TargetOpcode::COPY), *TLSBaseAddrReg)
.addReg(SystemZ::R2D);
return Copy;
}

View File

@@ -22,6 +22,8 @@ static MCSymbolRefExpr::VariantKind getVariantKind(unsigned Flags) {
return MCSymbolRefExpr::VK_None; return MCSymbolRefExpr::VK_None;
case SystemZII::MO_GOT: case SystemZII::MO_GOT:
return MCSymbolRefExpr::VK_GOT; return MCSymbolRefExpr::VK_GOT;
case SystemZII::MO_INDNTPOFF:
return MCSymbolRefExpr::VK_INDNTPOFF;
} }
llvm_unreachable("Unrecognised MO_ACCESS_MODEL"); llvm_unreachable("Unrecognised MO_ACCESS_MODEL");
} }

View File

@@ -23,11 +23,13 @@ class SystemZMachineFunctionInfo : public MachineFunctionInfo {
unsigned VarArgsFrameIndex; unsigned VarArgsFrameIndex;
unsigned RegSaveFrameIndex; unsigned RegSaveFrameIndex;
bool ManipulatesSP; bool ManipulatesSP;
unsigned NumLocalDynamics;
public: public:
explicit SystemZMachineFunctionInfo(MachineFunction &MF) explicit SystemZMachineFunctionInfo(MachineFunction &MF)
: LowSavedGPR(0), HighSavedGPR(0), VarArgsFirstGPR(0), VarArgsFirstFPR(0), : LowSavedGPR(0), HighSavedGPR(0), VarArgsFirstGPR(0), VarArgsFirstFPR(0),
VarArgsFrameIndex(0), RegSaveFrameIndex(0), ManipulatesSP(false) {} VarArgsFrameIndex(0), RegSaveFrameIndex(0), ManipulatesSP(false),
NumLocalDynamics(0) {}
// Get and set the first call-saved GPR that should be saved and restored // Get and set the first call-saved GPR that should be saved and restored
// by this function. This is 0 if no GPRs need to be saved or restored. // by this function. This is 0 if no GPRs need to be saved or restored.
@@ -61,6 +63,10 @@ public:
// e.g. through STACKSAVE or STACKRESTORE. // e.g. through STACKSAVE or STACKRESTORE.
bool getManipulatesSP() const { return ManipulatesSP; } bool getManipulatesSP() const { return ManipulatesSP; }
void setManipulatesSP(bool MSP) { ManipulatesSP = MSP; } void setManipulatesSP(bool MSP) { ManipulatesSP = MSP; }
// Count number of local-dynamic TLS symbols used.
unsigned getNumLocalDynamicTLSAccesses() const { return NumLocalDynamics; }
void incNumLocalDynamicTLSAccesses() { ++NumLocalDynamics; }
}; };
} // end namespace llvm } // end namespace llvm

View File

@@ -90,6 +90,7 @@ def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_CallSeqStart,
def callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_CallSeqEnd, def callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_CallSeqEnd,
[SDNPHasChain, SDNPSideEffect, SDNPOptInGlue, [SDNPHasChain, SDNPSideEffect, SDNPOptInGlue,
SDNPOutGlue]>; SDNPOutGlue]>;
def global_offset_table : SDNode<"ISD::GLOBAL_OFFSET_TABLE", SDTPtrLeaf>;
// Nodes for SystemZISD::*. See SystemZISelLowering.h for more details. // Nodes for SystemZISD::*. See SystemZISelLowering.h for more details.
def z_retflag : SDNode<"SystemZISD::RET_FLAG", SDTNone, def z_retflag : SDNode<"SystemZISD::RET_FLAG", SDTNone,
@@ -100,6 +101,12 @@ def z_call : SDNode<"SystemZISD::CALL", SDT_ZCall,
def z_sibcall : SDNode<"SystemZISD::SIBCALL", SDT_ZCall, def z_sibcall : SDNode<"SystemZISD::SIBCALL", SDT_ZCall,
[SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue,
SDNPVariadic]>; SDNPVariadic]>;
def z_tls_gdcall : SDNode<"SystemZISD::TLS_GDCALL", SDT_ZCall,
[SDNPHasChain, SDNPInGlue, SDNPOutGlue,
SDNPVariadic]>;
def z_tls_ldcall : SDNode<"SystemZISD::TLS_LDCALL", SDT_ZCall,
[SDNPHasChain, SDNPInGlue, SDNPOutGlue,
SDNPVariadic]>;
def z_pcrel_wrapper : SDNode<"SystemZISD::PCREL_WRAPPER", SDT_ZWrapPtr, []>; def z_pcrel_wrapper : SDNode<"SystemZISD::PCREL_WRAPPER", SDT_ZWrapPtr, []>;
def z_pcrel_offset : SDNode<"SystemZISD::PCREL_OFFSET", def z_pcrel_offset : SDNode<"SystemZISD::PCREL_OFFSET",
SDT_ZWrapOffset, []>; SDT_ZWrapOffset, []>;

View File

@@ -61,6 +61,10 @@ void SystemZPassConfig::addIRPasses() {
bool SystemZPassConfig::addInstSelector() { bool SystemZPassConfig::addInstSelector() {
addPass(createSystemZISelDag(getSystemZTargetMachine(), getOptLevel())); addPass(createSystemZISelDag(getSystemZTargetMachine(), getOptLevel()));
if (getOptLevel() != CodeGenOpt::None)
addPass(createSystemZLDCleanupPass(getSystemZTargetMachine()));
return false; return false;
} }

View File

@@ -1,7 +1,7 @@
; Test initial-exec TLS accesses. ; Test local-exec TLS accesses.
; ;
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s -check-prefix=CHECK-MAIN ; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu | FileCheck %s -check-prefix=CHECK-MAIN
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s -check-prefix=CHECK-CP ; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu | FileCheck %s -check-prefix=CHECK-CP
@x = thread_local global i32 0 @x = thread_local global i32 0

View File

@@ -0,0 +1,18 @@
; Test initial-exec TLS accesses.
;
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | FileCheck %s -check-prefix=CHECK-MAIN
@x = thread_local(initialexec) global i32 0
; The offset must be loaded from the GOT. This TLS access model does
; not use literal pool constants.
define i32 *@foo() {
; CHECK-MAIN-LABEL: foo:
; CHECK-MAIN: ear [[HIGH:%r[0-5]]], %a0
; CHECK-MAIN: sllg %r2, [[HIGH]], 32
; CHECK-MAIN: ear %r2, %a1
; CHECK-MAIN: larl %r1, x@INDNTPOFF
; CHECK-MAIN: ag %r2, 0(%r1)
; CHECK-MAIN: br %r14
ret i32 *@x
}

View File

@@ -0,0 +1,23 @@
; Test general-dynamic TLS accesses.
;
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | FileCheck %s -check-prefix=CHECK-MAIN
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | FileCheck %s -check-prefix=CHECK-CP
@x = thread_local global i32 0
; Call __tls_get_offset to retrieve the symbol's TLS offset.
define i32 *@foo() {
; CHECK-CP: .LCP{{.*}}:
; CHECK-CP: .quad x@TLSGD
;
; CHECK-MAIN-LABEL: foo:
; CHECK-MAIN-DAG: larl %r12, _GLOBAL_OFFSET_TABLE_
; CHECK-MAIN-DAG: lgrl %r2, .LCP{{.*}}
; CHECK-MAIN: brasl %r14, __tls_get_offset@PLT:tls_gdcall:x
; CHECK-MAIN: ear [[HIGH:%r[0-5]]], %a0
; CHECK-MAIN: sllg [[TP:%r[0-5]]], [[HIGH]], 32
; CHECK-MAIN: ear [[TP]], %a1
; CHECK-MAIN: agr %r2, [[TP]]
; CHECK-MAIN: br %r14
ret i32 *@x
}

View File

@@ -0,0 +1,28 @@
; Test local-dynamic TLS accesses.
;
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | FileCheck %s -check-prefix=CHECK-MAIN
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | FileCheck %s -check-prefix=CHECK-CP
@x = thread_local(localdynamic) global i32 0
; Call __tls_get_offset to retrieve the module's TLS base offset.
; Add the per-symbol offset and the thread pointer.
define i32 *@foo() {
; CHECK-CP: .LCP{{.*}}_0:
; CHECK-CP: .quad x@TLSLDM
; CHECK-CP: .LCP{{.*}}_1:
; CHECK-CP: .quad x@DTPOFF
;
; CHECK-MAIN-LABEL: foo:
; CHECK-MAIN-DAG: larl %r12, _GLOBAL_OFFSET_TABLE_
; CHECK-MAIN-DAG: lgrl %r2, .LCP{{.*}}_0
; CHECK-MAIN: brasl %r14, __tls_get_offset@PLT:tls_ldcall:x
; CHECK-MAIN: larl %r1, .LCP{{.*}}_1
; CHECK-MAIN: ag %r2, 0(%r1)
; CHECK-MAIN: ear [[HIGH:%r[0-5]]], %a0
; CHECK-MAIN: sllg [[TP:%r[0-5]]], [[HIGH]], 32
; CHECK-MAIN: ear [[TP]], %a1
; CHECK-MAIN: agr %r2, [[TP]]
; CHECK-MAIN: br %r14
ret i32 *@x
}

View File

@@ -0,0 +1,15 @@
; Test general-dynamic TLS access optimizations.
;
; If we access the same TLS variable twice, there should only be
; a single call to __tls_get_offset.
;
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | grep "__tls_get_offset" | count 1
@x = thread_local global i32 0
define i32 @foo() {
%val = load i32* @x
%inc = add nsw i32 %val, 1
store i32 %inc, i32* @x
ret i32 %val
}

View File

@@ -0,0 +1,17 @@
; Test general-dynamic TLS access optimizations.
;
; If we access two different TLS variables, we need two calls to
; __tls_get_offset, but should load _GLOBAL_OFFSET_TABLE only once.
;
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | grep "__tls_get_offset" | count 2
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | grep "_GLOBAL_OFFSET_TABLE_" | count 1
@x = thread_local global i32 0
@y = thread_local global i32 0
define i32 @foo() {
%valx = load i32* @x
%valy = load i32* @y
%add = add nsw i32 %valx, %valy
ret i32 %add
}

View File

@@ -0,0 +1,16 @@
; Test local-dynamic TLS access optimizations.
;
; If we access two different local-dynamic TLS variables, we only
; need a single call to __tls_get_offset.
;
; RUN: llc < %s -mcpu=z10 -mtriple=s390x-linux-gnu -relocation-model=pic | grep "__tls_get_offset" | count 1
@x = thread_local(localdynamic) global i32 0
@y = thread_local(localdynamic) global i32 0
define i32 @foo() {
%valx = load i32* @x
%valy = load i32* @y
%add = add nsw i32 %valx, %valy
ret i32 %add
}