mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 04:02:41 +01:00
[XCOFF][AIX] Add Global Variables Directly to TOC for 32 bit AIX
Summary: This patch implements the backend implementation of adding global variables directly to the table of contents (TOC), rather than adding the address of the variable to the TOC. Currently, this patch will look for the "toc-data" attribute on symbols in the IR, and then add those symbols to the TOC. ATM, this is implemented for 32 bit AIX. Reviewers: sfertile Differential Revision: https://reviews.llvm.org/D101178
This commit is contained in:
parent
15c04ad2d8
commit
f8a477d8bf
@ -2171,6 +2171,12 @@ TargetLoweringObjectFileXCOFF::getTargetSymbol(const GlobalValue *GV,
|
||||
// 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();
|
||||
@ -2196,6 +2202,15 @@ MCSection *TargetLoweringObjectFileXCOFF::getExplicitSectionGlobal(
|
||||
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;
|
||||
@ -2232,6 +2247,16 @@ MCSection *TargetLoweringObjectFileXCOFF::getSectionForExternalReference(
|
||||
|
||||
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
|
||||
|
@ -53,6 +53,7 @@ void MCSectionXCOFF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
|
||||
switch (getMappingClass()) {
|
||||
case XCOFF::XMC_RW:
|
||||
case XCOFF::XMC_DS:
|
||||
case XCOFF::XMC_TD:
|
||||
printCsectDirective(OS);
|
||||
break;
|
||||
case XCOFF::XMC_TC:
|
||||
@ -68,6 +69,12 @@ void MCSectionXCOFF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
|
||||
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) {
|
||||
|
@ -329,6 +329,8 @@ CsectGroup &XCOFFObjectWriter::getCsectGroup(const MCSectionXCOFF *MCSec) {
|
||||
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.");
|
||||
}
|
||||
@ -439,6 +441,10 @@ void XCOFFObjectWriter::recordRelocation(MCAssembler &Asm,
|
||||
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.");
|
||||
|
||||
|
@ -200,6 +200,10 @@ private:
|
||||
|
||||
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)) {
|
||||
@ -855,6 +859,30 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
|
||||
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:
|
||||
@ -2125,6 +2153,17 @@ void PPCAIXAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
|
||||
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.");
|
||||
|
||||
@ -2232,9 +2271,9 @@ void PPCAIXAsmPrinter::emitFunctionEntryLabel() {
|
||||
}
|
||||
|
||||
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.
|
||||
@ -2266,6 +2305,9 @@ void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) {
|
||||
if (TS != nullptr)
|
||||
TS->emitTCEntry(*I.first.first, I.first.second);
|
||||
}
|
||||
|
||||
for (const auto *GV : TOCDataGlobalVars)
|
||||
emitGlobalVariableHelper(GV);
|
||||
}
|
||||
|
||||
bool PPCAIXAsmPrinter::doInitialization(Module &M) {
|
||||
|
@ -433,6 +433,64 @@ SDNode *PPCDAGToDAGISel::getGlobalBaseReg() {
|
||||
.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) {
|
||||
@ -5546,12 +5604,12 @@ void PPCDAGToDAGISel::Select(SDNode *N) {
|
||||
|
||||
// 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);
|
||||
};
|
||||
@ -5561,12 +5619,17 @@ void PPCDAGToDAGISel::Select(SDNode *N) {
|
||||
"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;
|
||||
}
|
||||
}
|
||||
|
@ -3532,6 +3532,11 @@ def ADDIStocHA : PPCEmitTimePseudo<(outs gprc:$rD), (ins gprc_nor0:$reg, tocentr
|
||||
"#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.
|
||||
|
15
test/CodeGen/PowerPC/basic-toc-data-def.ll
Normal file
15
test/CodeGen/PowerPC/basic-toc-data-def.ll
Normal file
@ -0,0 +1,15 @@
|
||||
; 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.
|
20
test/CodeGen/PowerPC/basic-toc-data-extern.ll
Normal file
20
test/CodeGen/PowerPC/basic-toc-data-extern.ll
Normal file
@ -0,0 +1,20 @@
|
||||
; 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" }
|
14
test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll
Normal file
14
test/CodeGen/PowerPC/basic-toc-data-local-linkage.ll
Normal file
@ -0,0 +1,14 @@
|
||||
; 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" }
|
74
test/CodeGen/PowerPC/toc-data.ll
Normal file
74
test/CodeGen/PowerPC/toc-data.ll
Normal file
@ -0,0 +1,74 @@
|
||||
; 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" }
|
Loading…
Reference in New Issue
Block a user