Skip to content

[AIX] Support per global code model. #79202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions llvm/include/llvm/MC/MCSymbolXCOFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class MCSymbolXCOFF : public MCSymbol {

static bool classof(const MCSymbol *S) { return S->isXCOFF(); }

enum CodeModel : uint8_t { CM_Small, CM_Large };

static StringRef getUnqualifiedName(StringRef Name) {
if (Name.back() == ']') {
StringRef Lhs, Rhs;
Expand Down Expand Up @@ -72,8 +74,22 @@ class MCSymbolXCOFF : public MCSymbol {

void setEHInfo() const { modifyFlags(SF_EHInfo, SF_EHInfo); }

bool hasPerSymbolCodeModel() const { return PerSymbolCodeModel.has_value(); }

CodeModel getPerSymbolCodeModel() const {
assert(hasPerSymbolCodeModel() &&
"Requested code model for symbol without one");
return *PerSymbolCodeModel;
}

void setPerSymbolCodeModel(MCSymbolXCOFF::CodeModel Model) {
PerSymbolCodeModel = Model;
}

private:
std::optional<XCOFF::StorageClass> StorageClass;
std::optional<CodeModel> PerSymbolCodeModel;

MCSectionXCOFF *RepresentedCsect = nullptr;
XCOFF::VisibilityType VisibilityType = XCOFF::SYM_V_UNSPECIFIED;
StringRef SymbolTableName;
Expand Down
39 changes: 26 additions & 13 deletions llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2680,21 +2680,34 @@ MCSection *TargetLoweringObjectFileXCOFF::getSectionForFunctionDescriptor(

MCSection *TargetLoweringObjectFileXCOFF::getSectionForTOCEntry(
const MCSymbol *Sym, const TargetMachine &TM) const {
// Use TE storage-mapping class when large code model is enabled so that
// the chance of needing -bbigtoc is decreased. Also, the toc-entry for
// EH info is never referenced directly using instructions so it can be
// allocated with TE storage-mapping class.
// The "_$TLSML" symbol for TLS local-dynamic mode requires XMC_TC, otherwise
// the AIX assembler will complain.
const XCOFF::StorageMappingClass SMC = [](const MCSymbol *Sym,
const TargetMachine &TM) {
const MCSymbolXCOFF *XSym = cast<MCSymbolXCOFF>(Sym);

// The "_$TLSML" symbol for TLS local-dynamic mode requires XMC_TC,
// otherwise the AIX assembler will complain.
if (XSym->getSymbolTableName() == "_$TLSML")
return XCOFF::XMC_TC;

// Use large code model toc entries for ehinfo symbols as they are
// never referenced directly. The runtime loads their TOC entry
// addresses from the trace-back table.
if (XSym->isEHInfo())
return XCOFF::XMC_TE;

// If the symbol does not have a code model specified use the module value.
if (!XSym->hasPerSymbolCodeModel())
return TM.getCodeModel() == CodeModel::Large ? XCOFF::XMC_TE
: XCOFF::XMC_TC;

return XSym->getPerSymbolCodeModel() == MCSymbolXCOFF::CM_Large
? XCOFF::XMC_TE
: XCOFF::XMC_TC;
}(Sym, TM);

return getContext().getXCOFFSection(
cast<MCSymbolXCOFF>(Sym)->getSymbolTableName(), SectionKind::getData(),
XCOFF::CsectProperties(
((TM.getCodeModel() == CodeModel::Large &&
cast<MCSymbolXCOFF>(Sym)->getSymbolTableName() != "_$TLSML") ||
cast<MCSymbolXCOFF>(Sym)->isEHInfo())
? XCOFF::XMC_TE
: XCOFF::XMC_TC,
XCOFF::XTY_SD));
XCOFF::CsectProperties(SMC, XCOFF::XTY_SD));
}

MCSection *TargetLoweringObjectFileXCOFF::getSectionForLSDA(
Expand Down
77 changes: 63 additions & 14 deletions llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,35 @@ static void collectTOCStats(PPCAsmPrinter::TOCEntryType Type) {
}
}

static CodeModel::Model getCodeModel(const PPCSubtarget &S,
const TargetMachine &TM,
const MachineOperand &MO) {
CodeModel::Model ModuleModel = TM.getCodeModel();

// If the operand is not a global address then there is no
// global variable to carry an attribute.
if (!(MO.getType() == MachineOperand::MO_GlobalAddress))
return ModuleModel;

const GlobalValue *GV = MO.getGlobal();
assert(GV && "expected global for MO_GlobalAddress");

return S.getCodeModel(TM, GV);
}

static void setOptionalCodeModel(MCSymbolXCOFF *XSym, CodeModel::Model CM) {
switch (CM) {
case CodeModel::Large:
XSym->setPerSymbolCodeModel(MCSymbolXCOFF::CM_Large);
return;
case CodeModel::Small:
XSym->setPerSymbolCodeModel(MCSymbolXCOFF::CM_Small);
return;
default:
report_fatal_error("Invalid code model for AIX");
}
}

/// 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.
Expand Down Expand Up @@ -1014,7 +1043,7 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
// relative to the toc-base.
if (IsAIX) {
assert(
TM.getCodeModel() == CodeModel::Small &&
getCodeModel(*Subtarget, TM, MO) == CodeModel::Small &&
"This pseudo should only be selected for 32-bit small code model.");
Exp = getTOCEntryLoadingExprForXCOFF(MOSymbol, Exp, VK);
TmpInst.getOperand(1) = MCOperand::createExpr(Exp);
Expand Down Expand Up @@ -1098,7 +1127,12 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
return;
}
case PPC::ADDIStocHA: {
assert((IsAIX && !IsPPC64 && TM.getCodeModel() == CodeModel::Large) &&
const MachineOperand &MO = MI->getOperand(2);

assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
"Invalid operand for ADDIStocHA.");
assert((IsAIX && !IsPPC64 &&
getCodeModel(*Subtarget, TM, MO) == CodeModel::Large) &&
"This pseudo should only be selected for 32-bit large code model on"
" AIX.");

Expand All @@ -1108,10 +1142,6 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
// Change the opcode to ADDIS.
TmpInst.setOpcode(PPC::ADDIS);

const MachineOperand &MO = MI->getOperand(2);
assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
"Invalid operand for ADDIStocHA.");

// Map the machine operand to its corresponding MCSymbol.
MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this);

Expand All @@ -1131,7 +1161,12 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
return;
}
case PPC::LWZtocL: {
assert(IsAIX && !IsPPC64 && TM.getCodeModel() == CodeModel::Large &&
const MachineOperand &MO = MI->getOperand(1);

assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
"Invalid operand for LWZtocL.");
assert(IsAIX && !IsPPC64 &&
getCodeModel(*Subtarget, TM, MO) == CodeModel::Large &&
"This pseudo should only be selected for 32-bit large code model on"
" AIX.");

Expand All @@ -1141,10 +1176,6 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
// Change the opcode to lwz.
TmpInst.setOpcode(PPC::LWZ);

const MachineOperand &MO = MI->getOperand(1);
assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
"Invalid operand for LWZtocL.");

// Map the machine operand to its corresponding MCSymbol.
MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this);

Expand Down Expand Up @@ -1183,8 +1214,12 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {

const bool GlobalToc =
MO.isGlobal() && Subtarget->isGVIndirectSymbol(MO.getGlobal());

const CodeModel::Model CM =
IsAIX ? getCodeModel(*Subtarget, TM, MO) : TM.getCodeModel();

if (GlobalToc || MO.isJTI() || MO.isBlockAddress() ||
(MO.isCPI() && TM.getCodeModel() == CodeModel::Large))
(MO.isCPI() && CM == CodeModel::Large))
MOSymbol = lookUpOrCreateTOCEntry(MOSymbol, getTOCEntryTypeForMO(MO), VK);

VK = IsAIX ? MCSymbolRefExpr::VK_PPC_U : MCSymbolRefExpr::VK_PPC_TOC_HA;
Expand Down Expand Up @@ -1225,8 +1260,9 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
const MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this);

MCSymbolRefExpr::VariantKind VK = GetVKForMO(MO);

if (!MO.isCPI() || TM.getCodeModel() == CodeModel::Large)
CodeModel::Model CM =
IsAIX ? getCodeModel(*Subtarget, TM, MO) : TM.getCodeModel();
if (!MO.isCPI() || CM == CodeModel::Large)
MOSymbol = lookUpOrCreateTOCEntry(MOSymbol, getTOCEntryTypeForMO(MO), VK);

VK = IsAIX ? MCSymbolRefExpr::VK_PPC_L : MCSymbolRefExpr::VK_PPC_TOC_LO;
Expand Down Expand Up @@ -2984,6 +3020,10 @@ bool PPCAIXAsmPrinter::doInitialization(Module &M) {
}

setCsectAlignment(&G);
std::optional<CodeModel::Model> OptionalCodeModel = G.getCodeModel();
if (OptionalCodeModel)
setOptionalCodeModel(cast<MCSymbolXCOFF>(getSymbol(&G)),
*OptionalCodeModel);
}

for (const auto &F : M)
Expand All @@ -3005,6 +3045,15 @@ bool PPCAIXAsmPrinter::doInitialization(Module &M) {
false);
}

const GlobalVariable *GVar =
dyn_cast_or_null<GlobalVariable>(Alias.getAliaseeObject());
if (GVar) {
std::optional<CodeModel::Model> OptionalCodeModel = GVar->getCodeModel();
if (OptionalCodeModel)
setOptionalCodeModel(cast<MCSymbolXCOFF>(getSymbol(&Alias)),
*OptionalCodeModel);
}

GOAliasMap[Aliasee].push_back(&Alias);
}

Expand Down
21 changes: 20 additions & 1 deletion llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,24 @@ static bool hasTocDataAttr(SDValue Val, unsigned PointerSize) {
return true;
}

static CodeModel::Model getCodeModel(const PPCSubtarget &Subtarget,
const TargetMachine &TM,
const SDNode *Node) {
// If there isn't an attribute to override the module code model
// this will be the effective code model.
CodeModel::Model ModuleModel = TM.getCodeModel();

GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Node->getOperand(0));
if (!GA)
return ModuleModel;

const GlobalValue *GV = GA->getGlobal();
if (!GV)
return ModuleModel;

return Subtarget.getCodeModel(TM, GV);
}

/// 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) {
Expand Down Expand Up @@ -6059,7 +6077,8 @@ void PPCDAGToDAGISel::Select(SDNode *N) {
const bool isAIXABI = Subtarget->isAIXABI();

// PowerPC only support small, medium and large code model.
const CodeModel::Model CModel = TM.getCodeModel();
const CodeModel::Model CModel = getCodeModel(*Subtarget, TM, N);

assert(!(CModel == CodeModel::Tiny || CModel == CodeModel::Kernel) &&
"PowerPC doesn't support tiny or kernel code models.");

Expand Down
49 changes: 49 additions & 0 deletions llvm/lib/Target/PowerPC/PPCSubtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,63 @@ bool PPCSubtarget::enableSubRegLiveness() const {
}

bool PPCSubtarget::isGVIndirectSymbol(const GlobalValue *GV) const {
if (isAIXABI()) {
if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV))
// On AIX the only symbols that aren't indirect are toc-data.
return !GVar->hasAttribute("toc-data");

return true;
}

// Large code model always uses the TOC even for local symbols.
if (TM.getCodeModel() == CodeModel::Large)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the TM.getCodeModel is large, but the specific GV is set to small?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a really good question. I think originally I added this because it was triggering an assertion in the PPCAsmPrinter when we would emit the large code model nodes when the module code model was small. In reality both small and large code model use an indirect. That is true for both XCOFF and ELF - ELF has medium code model where we accessing things toc-relative instead of got indirect. I changed the AIX code to check for the toc-data attribute, since that is the only time a symbol would not be indirected on AIX.

The existing code is slightly wrong in that we should be returning true for both small and large code model, but because of where (and how) this function is used that change will be untestable. We can update it in a separate NFC patch to make it correct at least, and when we add support for per global code model for ELF then it would be come testable.

return true;

if (TM.shouldAssumeDSOLocal(GV))
return false;
return true;
}

CodeModel::Model PPCSubtarget::getCodeModel(const TargetMachine &TM,
const GlobalValue *GV) const {
// If there isn't an attribute to override the module code model
// this will be the effective code model.
CodeModel::Model ModuleModel = TM.getCodeModel();

// Initially support per global code model for AIX only.
if (!isAIXABI())
return ModuleModel;

// Only GlobalVariables carry an attribute which can override the module code
// model.
assert(GV && "Unexpected NULL GlobalValue");
const GlobalVariable *GlobalVar =
[](const GlobalValue *GV) -> const GlobalVariable * {
const GlobalVariable *Var = dyn_cast<GlobalVariable>(GV);
if (Var)
return Var;

const GlobalAlias *Alias = dyn_cast<GlobalAlias>(GV);
if (Alias)
return dyn_cast<GlobalVariable>(Alias->getAliaseeObject());

return nullptr;
}(GV);

if (!GlobalVar)
return ModuleModel;

std::optional<CodeModel::Model> MaybeCodeModel = GlobalVar->getCodeModel();
if (MaybeCodeModel) {
CodeModel::Model CM = *MaybeCodeModel;
assert((CM == CodeModel::Small || CM == CodeModel::Large) &&
"invalid code model for AIX");
return CM;
}

return ModuleModel;
}

bool PPCSubtarget::isELFv2ABI() const { return TM.isELFv2ABI(); }
bool PPCSubtarget::isPPC64() const { return TM.isPPC64(); }

Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/PowerPC/PPCSubtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ class PPCSubtarget : public PPCGenSubtargetInfo {
/// True if the GV will be accessed via an indirect symbol.
bool isGVIndirectSymbol(const GlobalValue *GV) const;

/// Calculates the effective code model for argument GV.
CodeModel::Model getCodeModel(const TargetMachine &TM,
const GlobalValue *GV) const;

/// True if the ABI is descriptor based.
bool usesFunctionDescriptors() const {
// Both 32-bit and 64-bit AIX are descriptor based. For ELF only the 64-bit
Expand Down
Loading