// function entry point. We choose to always return a function descriptor
// here.
if (const GlobalObject *GO = dyn_cast<GlobalObject>(GV)) {
+ if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV))
+ if (GVar->hasAttribute("toc-data"))
+ return cast<MCSectionXCOFF>(
+ SectionForGlobal(GVar, SectionKind::getData(), TM))
+ ->getQualNameSymbol();
+
if (GO->isDeclarationForLinker())
return cast<MCSectionXCOFF>(getSectionForExternalReference(GO, TM))
->getQualNameSymbol();
report_fatal_error("#pragma clang section is not yet supported");
StringRef SectionName = GO->getSection();
+
+ // Handle the XCOFF::TD case first, then deal with the rest.
+ if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GO))
+ if (GVar->hasAttribute("toc-data"))
+ return getContext().getXCOFFSection(
+ SectionName, Kind,
+ XCOFF::CsectProperties(/*MappingClass*/ XCOFF::XMC_TD, XCOFF::XTY_SD),
+ /* MultiSymbolsAllowed*/ true);
+
XCOFF::StorageMappingClass MappingClass;
if (Kind.isText())
MappingClass = XCOFF::XMC_PR;
MCSection *TargetLoweringObjectFileXCOFF::SelectSectionForGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
+ // Handle the XCOFF::TD case first, then deal with the rest.
+ if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GO))
+ if (GVar->hasAttribute("toc-data")) {
+ SmallString<128> Name;
+ getNameWithPrefix(Name, GO, TM);
+ return getContext().getXCOFFSection(
+ Name, Kind, XCOFF::CsectProperties(XCOFF::XMC_TD, XCOFF::XTY_SD),
+ /* MultiSymbolsAllowed*/ true);
+ }
+
// Common symbols go into a csect with matching name which will get mapped
// into the .bss section.
// Zero-initialized local TLS symbols go into a csect with matching name which
switch (getMappingClass()) {
case XCOFF::XMC_RW:
case XCOFF::XMC_DS:
+ case XCOFF::XMC_TD:
printCsectDirective(OS);
break;
case XCOFF::XMC_TC:
return;
}
+ if (isCsect() && getMappingClass() == XCOFF::XMC_TD) {
+ assert((getKind().isBSSExtern() || getKind().isBSSLocal()) &&
+ "Unexepected section kind for toc-data");
+ printCsectDirective(OS);
+ return;
+ }
// Common csect type (uninitialized storage) does not have to print csect
// directive for section switching.
if (isCsect() && getCSectType() == XCOFF::XTY_CM) {
assert(!TOCCsects.empty() &&
"We should at least have a TOC-base in this CsectGroup.");
return TOCCsects;
+ case XCOFF::XMC_TD:
+ report_fatal_error("toc-data not yet supported when writing object files.");
default:
report_fatal_error("Unhandled mapping of csect to section.");
}
TargetObjectWriter->getRelocTypeAndSignSize(Target, Fixup, IsPCRel);
const MCSectionXCOFF *SymASec = getContainingCsect(cast<MCSymbolXCOFF>(SymA));
+
+ if (SymASec->isCsect() && SymASec->getMappingClass() == XCOFF::XMC_TD)
+ report_fatal_error("toc-data not yet supported when writing object files.");
+
assert(SectionMap.find(SymASec) != SectionMap.end() &&
"Expected containing csect to exist in map.");
void emitTracebackTable();
+ SmallVector<const GlobalVariable *, 8> TOCDataGlobalVars;
+
+ void emitGlobalVariableHelper(const GlobalVariable *);
+
public:
PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
: PPCAsmPrinter(TM, std::move(Streamer)) {
EmitToStreamer(*OutStreamer, TmpInst);
return;
}
+ case PPC::ADDItoc: {
+ assert(IsAIX && TM.getCodeModel() == CodeModel::Small &&
+ "Operand only valid in AIX 32 bit mode");
+
+ // Transform %rN = ADDItoc @op1, %r2.
+ LowerPPCMachineInstrToMCInst(MI, TmpInst, *this);
+
+ // Change the opcode to load address.
+ TmpInst.setOpcode(PPC::LA);
+
+ const MachineOperand &MO = MI->getOperand(1);
+ assert(MO.isGlobal() && "Invalid operand for ADDItoc.");
+
+ // Map the operand to its corresponding MCSymbol.
+ const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this);
+
+ const MCExpr *Exp =
+ MCSymbolRefExpr::create(MOSymbol, MCSymbolRefExpr::VK_None, OutContext);
+
+ TmpInst.getOperand(1) = TmpInst.getOperand(2);
+ TmpInst.getOperand(2) = MCOperand::createExpr(Exp);
+ EmitToStreamer(*OutStreamer, TmpInst);
+ return;
+ }
case PPC::LDtocJTI:
case PPC::LDtocCPT:
case PPC::LDtocBA:
if (isSpecialLLVMGlobalArrayToSkip(GV) || isSpecialLLVMGlobalArrayForStaticInit(GV))
return;
+ // If the Global Variable has the toc-data attribute, it needs to be emitted
+ // when we emit the .toc section.
+ if (GV->hasAttribute("toc-data")) {
+ TOCDataGlobalVars.push_back(GV);
+ return;
+ }
+
+ emitGlobalVariableHelper(GV);
+}
+
+void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) {
assert(!GV->getName().startswith("llvm.") &&
"Unhandled intrinsic global variable.");
}
void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) {
- // If there are no functions in this module, we will never need to reference
- // the TOC base.
- if (M.empty())
+ // If there are no functions and there are no toc-data definitions in this
+ // module, we will never need to reference the TOC base.
+ if (M.empty() && TOCDataGlobalVars.empty())
return;
// Switch to section to emit TOC base.
if (TS != nullptr)
TS->emitTCEntry(*I.first.first, I.first.second);
}
+
+ for (const auto *GV : TOCDataGlobalVars)
+ emitGlobalVariableHelper(GV);
}
bool PPCAIXAsmPrinter::doInitialization(Module &M) {
.getNode();
}
+// Check if a SDValue has the toc-data attribute.
+static bool hasTocDataAttr(SDValue Val, unsigned PointerSize) {
+ GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Val);
+ if (!GA)
+ return false;
+
+ const GlobalVariable *GV = dyn_cast_or_null<GlobalVariable>(GA->getGlobal());
+ if (!GV)
+ return false;
+
+ if (!GV->hasAttribute("toc-data"))
+ return false;
+
+ // TODO: These asserts should be updated as more support for the toc data
+ // transformation is added (64 bit, struct support, etc.).
+
+ assert(PointerSize == 4 && "Only 32 Bit Codegen is currently supported by "
+ "the toc data transformation.");
+
+ assert(PointerSize >= GV->getAlign().valueOrOne().value() &&
+ "GlobalVariables with an alignment requirement stricter then 4-bytes "
+ "not supported by the toc data transformation.");
+
+ Type *PtrType = GV->getType();
+ assert(PtrType->isPointerTy() &&
+ "GlobalVariables always have pointer type!.");
+
+ Type *GVType = dyn_cast<PointerType>(PtrType)->getElementType();
+
+ assert(GVType->isSized() && "A GlobalVariable's size must be known to be "
+ "supported by the toc data transformation.");
+
+ if (GVType->isVectorTy())
+ report_fatal_error("A GlobalVariable of Vector type is not currently "
+ "supported by the toc data transformation.");
+
+ if (GVType->isArrayTy())
+ report_fatal_error("A GlobalVariable of Array type is not currently "
+ "supported by the toc data transformation.");
+
+ if (GVType->isStructTy())
+ report_fatal_error("A GlobalVariable of Struct type is not currently "
+ "supported by the toc data transformation.");
+
+ assert(GVType->getPrimitiveSizeInBits() <= PointerSize * 8 &&
+ "A GlobalVariable with size larger than 32 bits is not currently "
+ "supported by the toc data transformation.");
+
+ if (GV->hasLocalLinkage() || GV->hasPrivateLinkage())
+ report_fatal_error("A GlobalVariable with private or local linkage is not "
+ "currently supported by the toc data transformation.");
+
+ assert(!GV->hasCommonLinkage() &&
+ "Tentative definitions cannot have the mapping class XMC_TD.");
+
+ return true;
+}
+
/// isInt32Immediate - This method tests to see if the node is a 32-bit constant
/// operand. If so Imm will receive the 32-bit value.
static bool isInt32Immediate(SDNode *N, unsigned &Imm) {
// Handle 32-bit small code model.
if (!isPPC64) {
- // Transforms the ISD::TOC_ENTRY node to a PPCISD::LWZtoc.
- auto replaceWithLWZtoc = [this, &dl](SDNode *TocEntry) {
+ // Transforms the ISD::TOC_ENTRY node to passed in Opcode, either
+ // PPC::ADDItoc, or PPC::LWZtoc
+ auto replaceWith = [this, &dl](unsigned OpCode, SDNode *TocEntry) {
SDValue GA = TocEntry->getOperand(0);
SDValue TocBase = TocEntry->getOperand(1);
- SDNode *MN = CurDAG->getMachineNode(PPC::LWZtoc, dl, MVT::i32, GA,
- TocBase);
+ SDNode *MN = CurDAG->getMachineNode(OpCode, dl, MVT::i32, GA, TocBase);
transferMemOperands(TocEntry, MN);
ReplaceNode(TocEntry, MN);
};
"32-bit ELF can only have TOC entries in position independent"
" code.");
// 32-bit ELF always uses a small code model toc access.
- replaceWithLWZtoc(N);
+ replaceWith(PPC::LWZtoc, N);
return;
}
if (isAIXABI && CModel == CodeModel::Small) {
- replaceWithLWZtoc(N);
+ if (hasTocDataAttr(N->getOperand(0),
+ CurDAG->getDataLayout().getPointerSize()))
+ replaceWith(PPC::ADDItoc, N);
+ else
+ replaceWith(PPC::LWZtoc, N);
+
return;
}
}
"#ADDIStocHA",
[(set i32:$rD,
(PPCtoc_entry i32:$reg, tglobaladdr:$disp))]>;
+// Local Data Transform
+def ADDItoc : PPCEmitTimePseudo<(outs gprc:$rD), (ins tocentry32:$disp, gprc:$reg),
+ "#ADDItoc",
+ [(set i32:$rD,
+ (PPCtoc_entry tglobaladdr:$disp, i32:$reg))]>;
// Get Global (GOT) Base Register offset, from the word immediately preceding
// the function label.
--- /dev/null
+; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s
+; RUN: not --crash llc -filetype=obj -mtriple powerpc-ibm-aix-xcoff \
+; RUN: -verify-machineinstrs < %s 2>&1 | \
+; RUN: FileCheck %s --check-prefix=OBJ
+
+@i = global i32 55, align 4 #0
+
+attributes #0 = { "toc-data" }
+; CHECK: .toc
+; CHECK-NEXT: .csect i[TD],2
+; CHECK-NEXT: .globl i[TD]
+; CHECK-NEXT: .align 2
+; CHECK-NEXT: .vbyte 4, 55
+
+; OBJ: LLVM ERROR: toc-data not yet supported when writing object files.
--- /dev/null
+; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s
+; RUN: not --crash llc -filetype=obj -mtriple powerpc-ibm-aix-xcoff \
+; RUN: -verify-machineinstrs < %s 2>&1 | \
+; RUN: FileCheck %s --check-prefix=OBJ
+
+@i = external global i32, align 4 #0
+
+; Function Attrs: noinline nounwind optnone
+define i32* @get() {
+ entry:
+ ret i32* @i
+}
+
+; CHECK: la 3, i[TD](2)
+; CHECK: .toc
+; CHECK-NEXT: .extern i[TD]
+
+; OBJ: LLVM ERROR: toc-data not yet supported when writing object files.
+
+attributes #0 = { "toc-data" }
--- /dev/null
+; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs \
+; RUN: < %s 2>&1 | FileCheck %s
+
+@ilocal = internal global i32 0, align 4 #0
+
+define dso_local i32 @read_i32_local_linkage() {
+ entry:
+ %0 = load i32, i32* @ilocal, align 4
+ ret i32 %0
+}
+
+; CHECK: LLVM ERROR: A GlobalVariable with private or local linkage is not currently supported by the toc data transformation.
+
+attributes #0 = { "toc-data" }
--- /dev/null
+; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s \
+; RUN: -stop-before=ppc-ctr-loops-verify | FileCheck %s
+; RUN: llc -mtriple powerpc-ibm-aix-xcoff -verify-machineinstrs < %s | FileCheck %s --check-prefix TEST
+
+@i = dso_local global i32 0, align 4 #0
+@d = dso_local local_unnamed_addr global double 3.141590e+00, align 8
+@f = dso_local local_unnamed_addr global float 0x4005BE76C0000000, align 4 #0
+@ll = dso_local local_unnamed_addr global i64 55, align 8
+@ilocal = internal global i32 0, align 4
+
+define dso_local void @write_int(i32 signext %in) {
+ entry:
+ store i32 %in, i32* @i, align 4
+ ret void
+}
+; CHECK: name: write_int
+; CHECK: %[[SCRATCH:[0-9]+]]:gprc_and_gprc_nor0 = ADDItoc @i, $r2
+; CHECK-NEXT: STW %{{[0-9]+}}, 0, killed %[[SCRATCH]] :: (store 4 into @i)
+
+; TEST: .write_int:
+; TEST: la 4, i[TD](2)
+; TEST-NEXT: stw 3, 0(4)
+
+define dso_local i64 @read_ll() {
+ entry:
+ %0 = load i64, i64* @ll, align 8
+ ret i64 %0
+}
+; CHECK: name: read_ll
+; CHECK: LWZtoc @ll, $r2 :: (load 4 from got)
+
+; TEST: .read_ll:
+; TEST: lwz 4, L..C0(2)
+; TEST-NEXT: lwz 3, 0(4)
+; TEST-NEXT: lwz 4, 4(4)
+
+define dso_local float @read_float() {
+ entry:
+ %0 = load float, float* @f, align 4
+ ret float %0
+}
+; CHECK: name: read_float
+; CHECK: %[[SCRATCH:[0-9]+]]:gprc_and_gprc_nor0 = ADDItoc @f, $r2
+; CHECK: %{{[0-9]+}}:f4rc = LFS 0, killed %[[SCRATCH]] :: (dereferenceable load 4 from @f)
+
+; TEST: .read_float:
+; TEST: la 3, f[TD](2)
+; TEST-NEXT: lfs 1, 0(3)
+
+define dso_local void @write_double(double %in) {
+ entry:
+ store double %in, double* @d, align 8
+ ret void
+}
+; CHECK: name: write_double
+; CHECK: LWZtoc @d, $r2 :: (load 4 from got)
+
+; TEST: .write_double
+; TEST: lwz 3, L..C1(2)
+; TEST-NEXT: stfd 1, 0(3)
+
+define dso_local nonnull i32* @addr() {
+ entry:
+ ret i32* @i
+}
+; CHECK: name: addr
+; CHECK: %[[SCRATCH:[0-9]+]]:gprc = ADDItoc @i, $r2
+; CHECK-NEXT: $r3 = COPY %[[SCRATCH]]
+
+; TEST: .addr
+; TEST: la 3, i[TD](2)
+
+
+attributes #0 = { "toc-data" }