summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/MC/MCExpr.h4
-rw-r--r--include/llvm/Support/ELF.h5
-rw-r--r--lib/MC/MCExpr.cpp2
-rw-r--r--lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp16
-rw-r--r--lib/Target/PowerPC/PPCAsmPrinter.cpp133
-rw-r--r--lib/Target/PowerPC/PPCISelDAGToDAG.cpp43
-rw-r--r--lib/Target/PowerPC/PPCISelLowering.cpp3
-rw-r--r--lib/Target/PowerPC/PPCISelLowering.h16
-rw-r--r--lib/Target/PowerPC/PPCInstr64Bit.td26
-rw-r--r--lib/Target/PowerPC/PPCInstrInfo.td6
-rw-r--r--test/CodeGen/PowerPC/mcm-1.ll26
-rw-r--r--test/CodeGen/PowerPC/mcm-2.ll26
-rw-r--r--test/CodeGen/PowerPC/mcm-3.ll28
-rw-r--r--test/CodeGen/PowerPC/mcm-4.ll19
-rw-r--r--test/CodeGen/PowerPC/mcm-5.ll59
-rw-r--r--test/CodeGen/PowerPC/mcm-6.ll27
-rw-r--r--test/CodeGen/PowerPC/mcm-7.ll25
-rw-r--r--test/CodeGen/PowerPC/mcm-obj.ll193
18 files changed, 645 insertions, 12 deletions
diff --git a/include/llvm/MC/MCExpr.h b/include/llvm/MC/MCExpr.h
index 1007aa5264..fbaa740f82 100644
--- a/include/llvm/MC/MCExpr.h
+++ b/include/llvm/MC/MCExpr.h
@@ -174,9 +174,11 @@ public:
VK_PPC_DARWIN_HA16, // ha16(symbol)
VK_PPC_DARWIN_LO16, // lo16(symbol)
VK_PPC_GAS_HA16, // symbol@ha
- VK_PPC_GAS_LO16, // symbol@l
+ VK_PPC_GAS_LO16, // symbol@l
VK_PPC_TPREL16_HA, // symbol@tprel@ha
VK_PPC_TPREL16_LO, // symbol@tprel@l
+ VK_PPC_TOC16_HA, // symbol@toc@ha
+ VK_PPC_TOC16_LO, // symbol@toc@l
VK_Mips_GPREL,
VK_Mips_GOT_CALL,
diff --git a/include/llvm/Support/ELF.h b/include/llvm/Support/ELF.h
index 2cd267116c..18f8d6dfb1 100644
--- a/include/llvm/Support/ELF.h
+++ b/include/llvm/Support/ELF.h
@@ -472,8 +472,11 @@ enum {
R_PPC64_ADDR16_HIGHER = 39,
R_PPC64_ADDR16_HIGHEST = 41,
R_PPC64_TOC16 = 47,
+ R_PPC64_TOC16_LO = 48,
+ R_PPC64_TOC16_HA = 50,
R_PPC64_TOC = 51,
- R_PPC64_TOC16_DS = 63
+ R_PPC64_TOC16_DS = 63,
+ R_PPC64_TOC16_LO_DS = 64
};
// ARM Specific e_flags
diff --git a/lib/MC/MCExpr.cpp b/lib/MC/MCExpr.cpp
index de2f375aab..d601d4e94a 100644
--- a/lib/MC/MCExpr.cpp
+++ b/lib/MC/MCExpr.cpp
@@ -209,6 +209,8 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
case VK_PPC_GAS_LO16: return "l";
case VK_PPC_TPREL16_HA: return "tprel@ha";
case VK_PPC_TPREL16_LO: return "tprel@l";
+ case VK_PPC_TOC16_HA: return "toc@ha";
+ case VK_PPC_TOC16_LO: return "toc@l";
case VK_Mips_GPREL: return "GPREL";
case VK_Mips_GOT_CALL: return "GOT_CALL";
case VK_Mips_GOT16: return "GOT16";
diff --git a/lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp b/lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp
index dc93f7124a..0fc0dd9f7c 100644
--- a/lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp
+++ b/lib/Target/PowerPC/MCTargetDesc/PPCELFObjectWriter.cpp
@@ -82,6 +82,9 @@ unsigned PPCELFObjectWriter::getRelocTypeInner(const MCValue &Target,
case MCSymbolRefExpr::VK_None:
Type = ELF::R_PPC_ADDR16_HA;
break;
+ case MCSymbolRefExpr::VK_PPC_TOC16_HA:
+ Type = ELF::R_PPC64_TOC16_HA;
+ break;
}
break;
case PPC::fixup_ppc_lo16:
@@ -93,6 +96,9 @@ unsigned PPCELFObjectWriter::getRelocTypeInner(const MCValue &Target,
case MCSymbolRefExpr::VK_None:
Type = ELF::R_PPC_ADDR16_LO;
break;
+ case MCSymbolRefExpr::VK_PPC_TOC16_LO:
+ Type = ELF::R_PPC64_TOC16_LO;
+ break;
}
break;
case PPC::fixup_ppc_lo14:
@@ -105,7 +111,15 @@ unsigned PPCELFObjectWriter::getRelocTypeInner(const MCValue &Target,
Type = ELF::R_PPC64_TOC16;
break;
case PPC::fixup_ppc_toc16_ds:
- Type = ELF::R_PPC64_TOC16_DS;
+ switch (Modifier) {
+ default: llvm_unreachable("Unsupported Modifier");
+ case MCSymbolRefExpr::VK_PPC_TOC_ENTRY:
+ Type = ELF::R_PPC64_TOC16_DS;
+ break;
+ case MCSymbolRefExpr::VK_PPC_TOC16_LO:
+ Type = ELF::R_PPC64_TOC16_LO_DS;
+ break;
+ }
break;
case FK_Data_8:
switch (Modifier) {
diff --git a/lib/Target/PowerPC/PPCAsmPrinter.cpp b/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 3900c8bab4..5430fbd523 100644
--- a/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -73,6 +73,7 @@ namespace {
return "PowerPC Assembly Printer";
}
+ MCSymbol *lookUpOrCreateTOCEntry(MCSymbol *Sym);
virtual void EmitInstruction(const MachineInstr *MI);
@@ -310,6 +311,25 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
}
+/// lookUpOrCreateTOCEntry -- Given a symbol, look up whether a TOC entry
+/// exists for it. If not, create one. Then return a symbol that references
+/// the TOC entry.
+MCSymbol *PPCAsmPrinter::lookUpOrCreateTOCEntry(MCSymbol *Sym) {
+
+ MCSymbol *&TOCEntry = TOC[Sym];
+
+ // To avoid name clash check if the name already exists.
+ while (TOCEntry == 0) {
+ if (OutContext.LookupSymbol(Twine(MAI->getPrivateGlobalPrefix()) +
+ "C" + Twine(TOCLabelID++)) == 0) {
+ TOCEntry = GetTempSymbol("C", TOCLabelID);
+ }
+ }
+
+ return TOCEntry;
+}
+
+
/// EmitInstruction -- Print out a single PowerPC MI in Darwin syntax to
/// the current output stream.
///
@@ -379,14 +399,8 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) {
MOSymbol = GetCPISymbol(MO.getIndex());
else if (MO.isJTI())
MOSymbol = GetJTISymbol(MO.getIndex());
- MCSymbol *&TOCEntry = TOC[MOSymbol];
- // To avoid name clash check if the name already exists.
- while (TOCEntry == 0) {
- if (OutContext.LookupSymbol(Twine(MAI->getPrivateGlobalPrefix()) +
- "C" + Twine(TOCLabelID++)) == 0) {
- TOCEntry = GetTempSymbol("C", TOCLabelID);
- }
- }
+
+ MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(MOSymbol);
const MCExpr *Exp =
MCSymbolRefExpr::Create(TOCEntry, MCSymbolRefExpr::VK_PPC_TOC_ENTRY,
@@ -396,6 +410,109 @@ void PPCAsmPrinter::EmitInstruction(const MachineInstr *MI) {
return;
}
+ case PPC::ADDIStocHA: {
+ // Transform %Xd = ADDIStocHA %X2, <ga:@sym>
+ LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, Subtarget.isDarwin());
+
+ // Change the opcode to ADDIS8. If the global address is external,
+ // has common linkage, is a function address, or is a jump table
+ // address, then generate a TOC entry and reference that. Otherwise
+ // reference the symbol directly.
+ TmpInst.setOpcode(PPC::ADDIS8);
+ const MachineOperand &MO = MI->getOperand(2);
+ assert((MO.isGlobal() || MO.isCPI() || MO.isJTI()) &&
+ "Invalid operand for ADDIStocHA!");
+ MCSymbol *MOSymbol = 0;
+ bool IsExternal = false;
+ bool IsFunction = false;
+ bool IsCommon = false;
+
+ if (MO.isGlobal()) {
+ const GlobalValue *GValue = MO.getGlobal();
+ MOSymbol = Mang->getSymbol(GValue);
+ const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GValue);
+ IsExternal = GVar && !GVar->hasInitializer();
+ IsCommon = GVar && GValue->hasCommonLinkage();
+ IsFunction = !GVar;
+ } else if (MO.isCPI())
+ MOSymbol = GetCPISymbol(MO.getIndex());
+ else if (MO.isJTI())
+ MOSymbol = GetJTISymbol(MO.getIndex());
+
+ if (IsExternal || IsFunction || IsCommon || MO.isJTI())
+ MOSymbol = lookUpOrCreateTOCEntry(MOSymbol);
+
+ const MCExpr *Exp =
+ MCSymbolRefExpr::Create(MOSymbol, MCSymbolRefExpr::VK_PPC_TOC16_HA,
+ OutContext);
+ TmpInst.getOperand(2) = MCOperand::CreateExpr(Exp);
+ OutStreamer.EmitInstruction(TmpInst);
+ return;
+ }
+ case PPC::LDtocL: {
+ // Transform %Xd = LDtocL <ga:@sym>, %Xs
+ LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, Subtarget.isDarwin());
+
+ // Change the opcode to LDrs, which is a form of LD with the offset
+ // specified by a SymbolLo. If the global address is external, has
+ // common linkage, or is a jump table address, then reference the
+ // associated TOC entry. Otherwise reference the symbol directly.
+ TmpInst.setOpcode(PPC::LDrs);
+ const MachineOperand &MO = MI->getOperand(1);
+ assert((MO.isGlobal() || MO.isJTI()) && "Invalid operand for LDtocL!");
+ MCSymbol *MOSymbol = 0;
+
+ if (MO.isJTI())
+ MOSymbol = lookUpOrCreateTOCEntry(GetJTISymbol(MO.getIndex()));
+ else {
+ const GlobalValue *GValue = MO.getGlobal();
+ MOSymbol = Mang->getSymbol(GValue);
+ const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GValue);
+
+ if (!GVar || !GVar->hasInitializer() || GValue->hasCommonLinkage())
+ MOSymbol = lookUpOrCreateTOCEntry(MOSymbol);
+ }
+
+ const MCExpr *Exp =
+ MCSymbolRefExpr::Create(MOSymbol, MCSymbolRefExpr::VK_PPC_TOC16_LO,
+ OutContext);
+ TmpInst.getOperand(1) = MCOperand::CreateExpr(Exp);
+ OutStreamer.EmitInstruction(TmpInst);
+ return;
+ }
+ case PPC::ADDItocL: {
+ // Transform %Xd = ADDItocL %Xs, <ga:@sym>
+ LowerPPCMachineInstrToMCInst(MI, TmpInst, *this, Subtarget.isDarwin());
+
+ // Change the opcode to ADDI8L. If the global address is external, then
+ // generate a TOC entry and reference that. Otherwise reference the
+ // symbol directly.
+ TmpInst.setOpcode(PPC::ADDI8L);
+ const MachineOperand &MO = MI->getOperand(2);
+ assert((MO.isGlobal() || MO.isCPI()) && "Invalid operand for ADDItocL");
+ MCSymbol *MOSymbol = 0;
+ bool IsExternal = false;
+ bool IsFunction = false;
+
+ if (MO.isGlobal()) {
+ const GlobalValue *GValue = MO.getGlobal();
+ MOSymbol = Mang->getSymbol(GValue);
+ const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GValue);
+ IsExternal = GVar && !GVar->hasInitializer();
+ IsFunction = !GVar;
+ } else if (MO.isCPI())
+ MOSymbol = GetCPISymbol(MO.getIndex());
+
+ if (IsFunction || IsExternal)
+ MOSymbol = lookUpOrCreateTOCEntry(MOSymbol);
+
+ const MCExpr *Exp =
+ MCSymbolRefExpr::Create(MOSymbol, MCSymbolRefExpr::VK_PPC_TOC16_LO,
+ OutContext);
+ TmpInst.getOperand(2) = MCOperand::CreateExpr(Exp);
+ OutStreamer.EmitInstruction(TmpInst);
+ return;
+ }
case PPC::MFCRpseud:
case PPC::MFCR8pseud:
// Transform: %R3 = MFCRpseud %CR7
diff --git a/lib/Target/PowerPC/PPCISelDAGToDAG.cpp b/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
index 254fea67fc..ae8a9348fb 100644
--- a/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
+++ b/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
@@ -25,6 +25,7 @@
#include "llvm/Constants.h"
#include "llvm/Function.h"
#include "llvm/GlobalValue.h"
+#include "llvm/GlobalVariable.h"
#include "llvm/Intrinsics.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
@@ -1268,6 +1269,48 @@ SDNode *PPCDAGToDAGISel::Select(SDNode *N) {
Chain), 0);
return CurDAG->SelectNodeTo(N, Reg, MVT::Other, Chain);
}
+ case PPCISD::TOC_ENTRY: {
+ assert (PPCSubTarget.isPPC64() && "Only supported for 64-bit ABI");
+
+ // For medium code model, we generate two instructions as described
+ // below. Otherwise we allow SelectCodeCommon to handle this, selecting
+ // one of LDtoc, LDtocJTI, and LDtocCPT.
+ if (TM.getCodeModel() != CodeModel::Medium)
+ break;
+
+ // The first source operand is a TargetGlobalAddress or a
+ // TargetJumpTable. If it is an externally defined symbol, a symbol
+ // with common linkage, a function address, or a jump table address,
+ // we generate:
+ // LDtocL(<ga:@sym>, ADDIStocHA(%X2, <ga:@sym>))
+ // Otherwise we generate:
+ // ADDItocL(ADDIStocHA(%X2, <ga:@sym>), <ga:@sym>)
+ SDValue GA = N->getOperand(0);
+ SDValue TOCbase = N->getOperand(1);
+ SDNode *Tmp = CurDAG->getMachineNode(PPC::ADDIStocHA, dl, MVT::i64,
+ TOCbase, GA);
+
+ if (isa<JumpTableSDNode>(GA))
+ return CurDAG->getMachineNode(PPC::LDtocL, dl, MVT::i64, GA,
+ SDValue(Tmp, 0));
+
+ if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(GA)) {
+ const GlobalValue *GValue = G->getGlobal();
+ const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GValue);
+ assert((GVar || isa<Function>(GValue)) &&
+ "Unexpected global value subclass!");
+
+ // An external variable is one without an initializer. For these,
+ // for variables with common linkage, and for Functions, generate
+ // the LDtocL form.
+ if (!GVar || !GVar->hasInitializer() || GValue->hasCommonLinkage())
+ return CurDAG->getMachineNode(PPC::LDtocL, dl, MVT::i64, GA,
+ SDValue(Tmp, 0));
+ }
+
+ return CurDAG->getMachineNode(PPC::ADDItocL, dl, MVT::i64,
+ SDValue(Tmp, 0), GA);
+ }
}
return SelectCode(N);
diff --git a/lib/Target/PowerPC/PPCISelLowering.cpp b/lib/Target/PowerPC/PPCISelLowering.cpp
index 7d97450676..1c8524d3f9 100644
--- a/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -575,6 +575,9 @@ const char *PPCTargetLowering::getTargetNodeName(unsigned Opcode) const {
case PPCISD::TC_RETURN: return "PPCISD::TC_RETURN";
case PPCISD::CR6SET: return "PPCISD::CR6SET";
case PPCISD::CR6UNSET: return "PPCISD::CR6UNSET";
+ case PPCISD::ADDIS_TOC_HA: return "PPCISD::ADDIS_TOC_HA";
+ case PPCISD::LD_TOC_L: return "PPCISD::LD_TOC_L";
+ case PPCISD::ADDI_TOC_L: return "PPCISD::ADDI_TOC_L";
}
}
diff --git a/lib/Target/PowerPC/PPCISelLowering.h b/lib/Target/PowerPC/PPCISelLowering.h
index b3c7f9c28d..b187f856f3 100644
--- a/lib/Target/PowerPC/PPCISelLowering.h
+++ b/lib/Target/PowerPC/PPCISelLowering.h
@@ -191,7 +191,21 @@ namespace llvm {
/// byte-swapping load instruction. It loads "Type" bits, byte swaps it,
/// then puts it in the bottom bits of the GPRC. TYPE can be either i16
/// or i32.
- LBRX
+ LBRX,
+
+ /// G8RC = ADDIS_TOC_HA %X2, Symbol - For medium code model, produces
+ /// an ADDIS8 instruction that adds the TOC base register to sym@toc@ha.
+ ADDIS_TOC_HA,
+
+ /// G8RC = LD_TOC_L Symbol, G8RReg - For medium code model, produces a
+ /// LD instruction with base register G8RReg and offset sym@toc@l.
+ /// Preceded by an ADDIS_TOC_HA to form a full 32-bit offset.
+ LD_TOC_L,
+
+ /// G8RC = ADDI_TOC_L G8RReg, Symbol - For medium code model, produces
+ /// an ADDI8 instruction that adds G8RReg to sym@toc@l.
+ /// Preceded by an ADDIS_TOC_HA to form a full 32-bit offset.
+ ADDI_TOC_L
};
}
diff --git a/lib/Target/PowerPC/PPCInstr64Bit.td b/lib/Target/PowerPC/PPCInstr64Bit.td
index 9711452ec4..78844f5edd 100644
--- a/lib/Target/PowerPC/PPCInstr64Bit.td
+++ b/lib/Target/PowerPC/PPCInstr64Bit.td
@@ -32,6 +32,11 @@ def symbolLo64 : Operand<i64> {
def tocentry : Operand<iPTR> {
let MIOperandInfo = (ops i32imm:$imm);
}
+def memrs : Operand<iPTR> { // memri where the immediate is a symbolLo64
+ let PrintMethod = "printMemRegImm";
+ let EncoderMethod = "getMemRIXEncoding";
+ let MIOperandInfo = (ops symbolLo64:$off, ptr_rc:$reg);
+}
//===----------------------------------------------------------------------===//
// 64-bit transformation functions.
@@ -625,6 +630,12 @@ let canFoldAsLoad = 1, PPC970_Unit = 2 in {
def LD : DSForm_1<58, 0, (outs G8RC:$rD), (ins memrix:$src),
"ld $rD, $src", LdStLD,
[(set G8RC:$rD, (load ixaddr:$src))]>, isPPC64;
+def LDrs : DSForm_1<58, 0, (outs G8RC:$rD), (ins memrs:$src),
+ "ld $rD, $src", LdStLD,
+ []>, isPPC64;
+// The following three definitions are selected for small code model only.
+// Otherwise, we need to create two instructions to form a 32-bit offset,
+// so we have a custom matcher for TOC_ENTRY in PPCDAGToDAGIsel::Select().
def LDtoc: Pseudo<(outs G8RC:$rD), (ins tocentry:$disp, G8RC:$reg),
"#LDtoc",
[(set G8RC:$rD,
@@ -671,6 +682,21 @@ def : Pat<(PPCload ixaddr:$src),
def : Pat<(PPCload xaddr:$src),
(LDX xaddr:$src)>;
+// Support for medium code model.
+def ADDIStocHA: Pseudo<(outs G8RC:$rD), (ins G8RC:$reg, tocentry:$disp),
+ "#ADDIStocHA",
+ [(set G8RC:$rD,
+ (PPCaddisTocHA G8RC:$reg, tglobaladdr:$disp))]>,
+ isPPC64;
+def LDtocL: Pseudo<(outs G8RC:$rD), (ins tocentry:$disp, G8RC:$reg),
+ "#LDtocL",
+ [(set G8RC:$rD,
+ (PPCldTocL tglobaladdr:$disp, G8RC:$reg))]>, isPPC64;
+def ADDItocL: Pseudo<(outs G8RC:$rD), (ins G8RC:$reg, tocentry:$disp),
+ "#ADDItocL",
+ [(set G8RC:$rD,
+ (PPCaddiTocL G8RC:$reg, tglobaladdr:$disp))]>, isPPC64;
+
let PPC970_Unit = 2 in {
// Truncating stores.
def STB8 : DForm_1<38, (outs), (ins G8RC:$rS, memri:$src),
diff --git a/lib/Target/PowerPC/PPCInstrInfo.td b/lib/Target/PowerPC/PPCInstrInfo.td
index 6ee045a2c7..937ed0d90b 100644
--- a/lib/Target/PowerPC/PPCInstrInfo.td
+++ b/lib/Target/PowerPC/PPCInstrInfo.td
@@ -167,6 +167,12 @@ def PPClarx : SDNode<"PPCISD::LARX", SDT_PPClarx,
def PPCstcx : SDNode<"PPCISD::STCX", SDT_PPCstcx,
[SDNPHasChain, SDNPMayStore]>;
+// Instructions to support medium code model
+def PPCaddisTocHA : SDNode<"PPCISD::ADDIS_TOC_HA", SDTIntBinOp, []>;
+def PPCldTocL : SDNode<"PPCISD::LD_TOC_L", SDTIntBinOp, [SDNPMayLoad]>;
+def PPCaddiTocL : SDNode<"PPCISD::ADDI_TOC_L", SDTIntBinOp, []>;
+
+
// Instructions to support dynamic alloca.
def SDTDynOp : SDTypeProfile<1, 2, []>;
def PPCdynalloc : SDNode<"PPCISD::DYNALLOC", SDTDynOp, [SDNPHasChain]>;
diff --git a/test/CodeGen/PowerPC/mcm-1.ll b/test/CodeGen/PowerPC/mcm-1.ll
new file mode 100644
index 0000000000..62fe88c2b8
--- /dev/null
+++ b/test/CodeGen/PowerPC/mcm-1.ll
@@ -0,0 +1,26 @@
+; RUN: llc -mcpu=pwr7 -O0 -code-model=medium <%s | FileCheck %s
+
+; Test correct code generation for medium code model (32-bit TOC offsets)
+; for loading and storing an external variable.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+@ei = external global i32
+
+define signext i32 @test_external() nounwind {
+entry:
+ %0 = load i32* @ei, align 4
+ %inc = add nsw i32 %0, 1
+ store i32 %inc, i32* @ei, align 4
+ ret i32 %0
+}
+
+; CHECK: test_external:
+; CHECK: addis [[REG1:[0-9]+]], 2, .LC[[TOCNUM:[0-9]+]]@toc@ha
+; CHECK: ld [[REG2:[0-9]+]], .LC[[TOCNUM]]@toc@l([[REG1]])
+; CHECK: lwz {{[0-9]+}}, 0([[REG2]])
+; CHECK: stw {{[0-9]+}}, 0([[REG2]])
+; CHECK: .section .toc
+; CHECK: .LC[[TOCNUM]]:
+; CHECK: .tc {{[a-z0-9A-Z_.]+}}[TC],{{[a-z0-9A-Z_.]+}}
diff --git a/test/CodeGen/PowerPC/mcm-2.ll b/test/CodeGen/PowerPC/mcm-2.ll
new file mode 100644
index 0000000000..45df0ab14f
--- /dev/null
+++ b/test/CodeGen/PowerPC/mcm-2.ll
@@ -0,0 +1,26 @@
+; RUN: llc -mcpu=pwr7 -O0 -code-model=medium <%s | FileCheck %s
+
+; Test correct code generation for medium code model (32-bit TOC offsets)
+; for loading and storing a static variable scoped to a function.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+@test_fn_static.si = internal global i32 0, align 4
+
+define signext i32 @test_fn_static() nounwind {
+entry:
+ %0 = load i32* @test_fn_static.si, align 4
+ %inc = add nsw i32 %0, 1
+ store i32 %inc, i32* @test_fn_static.si, align 4
+ ret i32 %0
+}
+
+; CHECK: test_fn_static:
+; CHECK: addis [[REG1:[0-9]+]], 2, [[VAR:[a-z0-9A-Z_.]+]]@toc@ha
+; CHECK: addi [[REG2:[0-9]+]], [[REG1]], [[VAR]]@toc@l
+; CHECK: lwz {{[0-9]+}}, 0([[REG2]])
+; CHECK: stw {{[0-9]+}}, 0([[REG2]])
+; CHECK: .type [[VAR]],@object
+; CHECK: .local [[VAR]]
+; CHECK: .comm [[VAR]],4,4
diff --git a/test/CodeGen/PowerPC/mcm-3.ll b/test/CodeGen/PowerPC/mcm-3.ll
new file mode 100644
index 0000000000..0e7bbe798b
--- /dev/null
+++ b/test/CodeGen/PowerPC/mcm-3.ll
@@ -0,0 +1,28 @@
+; RUN: llc -mcpu=pwr7 -O0 -code-model=medium <%s | FileCheck %s
+
+; Test correct code generation for medium code model (32-bit TOC offsets)
+; for loading and storing a file-scope static variable.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+@gi = global i32 5, align 4
+
+define signext i32 @test_file_static() nounwind {
+entry:
+ %0 = load i32* @gi, align 4
+ %inc = add nsw i32 %0, 1
+ store i32 %inc, i32* @gi, align 4
+ ret i32 %0
+}
+
+; CHECK: test_file_static:
+; CHECK: addis [[REG1:[0-9]+]], 2, [[VAR:[a-z0-9A-Z_.]+]]@toc@ha
+; CHECK: addi [[REG2:[0-9]+]], [[REG1]], [[VAR]]@toc@l
+; CHECK: lwz {{[0-9]+}}, 0([[REG2]])
+; CHECK: stw {{[0-9]+}}, 0([[REG2]])
+; CHECK: .type [[VAR]],@object
+; CHECK: .data
+; CHECK: .globl [[VAR]]
+; CHECK: [[VAR]]:
+; CHECK: .long 5
diff --git a/test/CodeGen/PowerPC/mcm-4.ll b/test/CodeGen/PowerPC/mcm-4.ll
new file mode 100644
index 0000000000..db36d0bcf7
--- /dev/null
+++ b/test/CodeGen/PowerPC/mcm-4.ll
@@ -0,0 +1,19 @@
+; RUN: llc -mcpu=pwr7 -O0 -code-model=medium <%s | FileCheck %s
+
+; Test correct code generation for medium code model (32-bit TOC offsets)
+; for loading a value from the constant pool (TOC-relative).
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+define double @test_double_const() nounwind {
+entry:
+ ret double 0x3F4FD4920B498CF0
+}
+
+; CHECK: [[VAR:[a-z0-9A-Z_.]+]]:
+; CHECK: .quad 4562098671269285104
+; CHECK: test_double_const:
+; CHECK: addis [[REG1:[0-9]+]], 2, [[VAR]]@toc@ha
+; CHECK: addi [[REG2:[0-9]+]], [[REG1]], [[VAR]]@toc@l
+; CHECK: lfd {{[0-9]+}}, 0([[REG2]])
diff --git a/test/CodeGen/PowerPC/mcm-5.ll b/test/CodeGen/PowerPC/mcm-5.ll
new file mode 100644
index 0000000000..10d89f5215
--- /dev/null
+++ b/test/CodeGen/PowerPC/mcm-5.ll
@@ -0,0 +1,59 @@
+; RUN: llc -mcpu=pwr7 -O0 -code-model=medium <%s | FileCheck %s
+
+; Test correct code generation for medium code model (32-bit TOC offsets)
+; for loading the address of a jump table from the TOC.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+define signext i32 @test_jump_table(i32 signext %i) nounwind {
+entry:
+ %i.addr = alloca i32, align 4
+ store i32 %i, i32* %i.addr, align 4
+ %0 = load i32* %i.addr, align 4
+ switch i32 %0, label %sw.default [
+ i32 3, label %sw.bb
+ i32 4, label %sw.bb1
+ i32 5, label %sw.bb2
+ i32 6, label %sw.bb3
+ ]
+
+sw.default: ; preds = %entry
+ br label %sw.epilog
+
+sw.bb: ; preds = %entry
+ %1 = load i32* %i.addr, align 4
+ %mul = mul nsw i32 %1, 7
+ store i32 %mul, i32* %i.addr, align 4
+ br label %sw.bb1
+
+sw.bb1: ; preds = %entry, %sw.bb
+ %2 = load i32* %i.addr, align 4
+ %dec = add nsw i32 %2, -1
+ store i32 %dec, i32* %i.addr, align 4
+ br label %sw.bb2
+
+sw.bb2: ; preds = %entry, %sw.bb1
+ %3 = load i32* %i.addr, align 4
+ %add = add nsw i32 %3, 3
+ store i32 %add, i32* %i.addr, align 4
+ br label %sw.bb3
+
+sw.bb3: ; preds = %entry, %sw.bb2
+ %4 = load i32* %i.addr, align 4
+ %shl = shl i32 %4, 1
+ store i32 %shl, i32* %i.addr, align 4
+ br label %sw.epilog
+
+sw.epilog: ; preds = %sw.bb3, %sw.default
+ %5 = load i32* %i.addr, align 4
+ ret i32 %5
+}
+
+; CHECK: test_jump_table:
+; CHECK: addis [[REG1:[0-9]+]], 2, .LC[[TOCNUM:[0-9]+]]@toc@ha
+; CHECK: ld [[REG2:[0-9]+]], .LC[[TOCNUM]]@toc@l([[REG1]])
+; CHECK: ldx {{[0-9]+}}, {{[0-9]+}}, [[REG2]]
+; CHECK: .section .toc
+; CHECK: .LC[[TOCNUM]]:
+; CHECK: .tc {{[a-z0-9A-Z_.]+}}[TC],{{[a-z0-9A-Z_.]+}}
diff --git a/test/CodeGen/PowerPC/mcm-6.ll b/test/CodeGen/PowerPC/mcm-6.ll
new file mode 100644
index 0000000000..0a7fa762d4
--- /dev/null
+++ b/test/CodeGen/PowerPC/mcm-6.ll
@@ -0,0 +1,27 @@
+; RUN: llc -mcpu=pwr7 -O0 -code-model=medium < %s | FileCheck %s
+
+; Test correct code generation for medium code model (32-bit TOC offsets)
+; for loading and storing a tentatively defined variable.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+@ti = common global i32 0, align 4
+
+define signext i32 @test_tentative() nounwind {
+entry:
+ %0 = load i32* @ti, align 4
+ %inc = add nsw i32 %0, 1
+ store i32 %inc, i32* @ti, align 4
+ ret i32 %0
+}
+
+; CHECK: test_tentative:
+; CHECK: addis [[REG1:[0-9]+]], 2, .LC[[TOCNUM:[0-9]+]]@toc@ha
+; CHECK: ld [[REG2:[0-9]+]], .LC[[TOCNUM]]@toc@l([[REG1]])
+; CHECK: lwz {{[0-9]+}}, 0([[REG2]])
+; CHECK: stw {{[0-9]+}}, 0([[REG2]])
+; CHECK: .section .toc
+; CHECK: .LC[[TOCNUM]]:
+; CHECK: .tc [[VAR:[a-z0-9A-Z_.]+]][TC],{{[a-z0-9A-Z_.]+}}
+; CHECK: .comm [[VAR]],4,4
diff --git a/test/CodeGen/PowerPC/mcm-7.ll b/test/CodeGen/PowerPC/mcm-7.ll
new file mode 100644
index 0000000000..0e9fa2b38b
--- /dev/null
+++ b/test/CodeGen/PowerPC/mcm-7.ll
@@ -0,0 +1,25 @@
+; RUN: llc -mcpu=pwr7 -O0 -code-model=medium < %s | FileCheck %s
+
+; Test correct code generation for medium code model (32-bit TOC offsets)
+; for loading a function address.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+define i8* @test_fnaddr() nounwind {
+entry:
+ %func = alloca i32 (i32)*, align 8
+ store i32 (i32)* @foo, i32 (i32)** %func, align 8
+ %0 = load i32 (i32)** %func, align 8
+ %1 = bitcast i32 (i32)* %0 to i8*
+ ret i8* %1
+}
+
+declare signext i32 @foo(i32 signext)
+
+; CHECK: test_fnaddr:
+; CHECK: addis [[REG1:[0-9]+]], 2, .LC[[TOCNUM:[0-9]+]]@toc@ha
+; CHECK: ld [[REG2:[0-9]+]], .LC[[TOCNUM]]@toc@l([[REG1]])
+; CHECK: .section .toc
+; CHECK: .LC[[TOCNUM]]:
+; CHECK: .tc {{[a-z0-9A-Z_.]+}}[TC],{{[a-z0-9A-Z_.]+}}
diff --git a/test/CodeGen/PowerPC/mcm-obj.ll b/test/CodeGen/PowerPC/mcm-obj.ll
new file mode 100644
index 0000000000..ec1b7b0084
--- /dev/null
+++ b/test/CodeGen/PowerPC/mcm-obj.ll
@@ -0,0 +1,193 @@
+; RUN: llc -O0 -mcpu=pwr7 -code-model=medium -filetype=obj %s -o - | \
+; RUN: elf-dump --dump-section-data | FileCheck %s
+
+; FIXME: When asm-parse is available, could make this an assembly test.
+
+target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f128:128:128-v128:128:128-n32:64"
+target triple = "powerpc64-unknown-linux-gnu"
+
+@ei = external global i32
+
+define signext i32 @test_external() nounwind {
+entry:
+ %0 = load i32* @ei, align 4
+ %inc = add nsw i32 %0, 1
+ store i32 %inc, i32* @ei, align 4
+ ret i32 %0
+}
+
+; Verify generation of R_PPC64_TOC16_HA and R_PPC64_TOC16_LO_DS for
+; accessing external variable ei.
+;
+; CHECK: '.rela.text'
+; CHECK: Relocation 0
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM1:[0-9]+]]
+; CHECK-NEXT: 'r_type', 0x00000032
+; CHECK: Relocation 1
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM1]]
+; CHECK-NEXT: 'r_type', 0x00000040
+
+@test_fn_static.si = internal global i32 0, align 4
+
+define signext i32 @test_fn_static() nounwind {
+entry:
+ %0 = load i32* @test_fn_static.si, align 4
+ %inc = add nsw i32 %0, 1
+ store i32 %inc, i32* @test_fn_static.si, align 4
+ ret i32 %0
+}
+
+; Verify generation of R_PPC64_TOC16_HA and R_PPC64_TOC16_LO for
+; accessing function-scoped variable si.
+;
+; CHECK: Relocation 2
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM2:[0-9]+]]
+; CHECK-NEXT: 'r_type', 0x00000032
+; CHECK: Relocation 3
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM2]]
+; CHECK-NEXT: 'r_type', 0x00000030
+
+@gi = global i32 5, align 4
+
+define signext i32 @test_file_static() nounwind {
+entry:
+ %0 = load i32* @gi, align 4
+ %inc = add nsw i32 %0, 1
+ store i32 %inc, i32* @gi, align 4
+ ret i32 %0
+}
+
+; Verify generation of R_PPC64_TOC16_HA and R_PPC64_TOC16_LO for
+; accessing file-scope variable gi.
+;
+; CHECK: Relocation 4
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM3:[0-9]+]]
+; CHECK-NEXT: 'r_type', 0x00000032
+; CHECK: Relocation 5
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM3]]
+; CHECK-NEXT: 'r_type', 0x00000030
+
+define double @test_double_const() nounwind {
+entry:
+ ret double 0x3F4FD4920B498CF0
+}
+
+; Verify generation of R_PPC64_TOC16_HA and R_PPC64_TOC16_LO for
+; accessing a constant.
+;
+; CHECK: Relocation 6
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM4:[0-9]+]]
+; CHECK-NEXT: 'r_type', 0x00000032
+; CHECK: Relocation 7
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM4]]
+; CHECK-NEXT: 'r_type', 0x00000030
+
+define signext i32 @test_jump_table(i32 signext %i) nounwind {
+entry:
+ %i.addr = alloca i32, align 4
+ store i32 %i, i32* %i.addr, align 4
+ %0 = load i32* %i.addr, align 4
+ switch i32 %0, label %sw.default [
+ i32 3, label %sw.bb
+ i32 4, label %sw.bb1
+ i32 5, label %sw.bb2
+ i32 6, label %sw.bb3
+ ]
+
+sw.default: ; preds = %entry
+ br label %sw.epilog
+
+sw.bb: ; preds = %entry
+ %1 = load i32* %i.addr, align 4
+ %mul = mul nsw i32 %1, 7
+ store i32 %mul, i32* %i.addr, align 4
+ br label %sw.bb1
+
+sw.bb1: ; preds = %entry, %sw.bb
+ %2 = load i32* %i.addr, align 4
+ %dec = add nsw i32 %2, -1
+ store i32 %dec, i32* %i.addr, align 4
+ br label %sw.bb2
+
+sw.bb2: ; preds = %entry, %sw.bb1
+ %3 = load i32* %i.addr, align 4
+ %add = add nsw i32 %3, 3
+ store i32 %add, i32* %i.addr, align 4
+ br label %sw.bb3
+
+sw.bb3: ; preds = %entry, %sw.bb2
+ %4 = load i32* %i.addr, align 4
+ %shl = shl i32 %4, 1
+ store i32 %shl, i32* %i.addr, align 4
+ br label %sw.epilog
+
+sw.epilog: ; preds = %sw.bb3, %sw.default
+ %5 = load i32* %i.addr, align 4
+ ret i32 %5
+}
+
+; Verify generation of R_PPC64_TOC16_HA and R_PPC64_TOC16_LO_DS for
+; accessing a jump table address.
+;
+; CHECK: Relocation 8
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM5:[0-9]+]]
+; CHECK-NEXT: 'r_type', 0x00000032
+; CHECK: Relocation 9
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM5]]
+; CHECK-NEXT: 'r_type', 0x00000040
+
+@ti = common global i32 0, align 4
+
+define signext i32 @test_tentative() nounwind {
+entry:
+ %0 = load i32* @ti, align 4
+ %inc = add nsw i32 %0, 1
+ store i32 %inc, i32* @ti, align 4
+ ret i32 %0
+}
+
+; Verify generation of R_PPC64_TOC16_HA and R_PPC64_TOC16_LO_DS for
+; accessing tentatively declared variable ti.
+;
+; CHECK: Relocation 10
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM6:[0-9]+]]
+; CHECK-NEXT: 'r_type', 0x00000032
+; CHECK: Relocation 11
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM6]]
+; CHECK-NEXT: 'r_type', 0x00000040
+
+define i8* @test_fnaddr() nounwind {
+entry:
+ %func = alloca i32 (i32)*, align 8
+ store i32 (i32)* @foo, i32 (i32)** %func, align 8
+ %0 = load i32 (i32)** %func, align 8
+ %1 = bitcast i32 (i32)* %0 to i8*
+ ret i8* %1
+}
+
+declare signext i32 @foo(i32 signext)
+
+; Verify generation of R_PPC64_TOC16_HA and R_PPC64_TOC16_LO_DS for
+; accessing function address foo.
+;
+; CHECK: Relocation 12
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM7:[0-9]+]]
+; CHECK-NEXT: 'r_type', 0x00000032
+; CHECK: Relocation 13
+; CHECK-NEXT: 'r_offset'
+; CHECK-NEXT: 'r_sym', 0x[[SYM7]]
+; CHECK-NEXT: 'r_type', 0x00000040
+