1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 02:33:06 +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:
Sidharth Baveja 2021-04-30 14:48:02 +00:00
parent 15c04ad2d8
commit f8a477d8bf
10 changed files with 280 additions and 9 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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.");

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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.

View 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.

View 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" }

View 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" }

View 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" }