blob: 9ad4bd3c77ab4e91632b0ab42cf690f283871f71 [file] [log] [blame]
//===- AMDGPUMCResourceInfo.cpp --- MC Resource Info ----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file
/// \brief MC infrastructure to propagate the function level resource usage
/// info.
///
//===----------------------------------------------------------------------===//
#include "AMDGPUMCResourceInfo.h"
#include "Utils/AMDGPUBaseInfo.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Target/TargetMachine.h"
#define DEBUG_TYPE "amdgpu-mc-resource-usage"
using namespace llvm;
MCSymbol *MCResourceInfo::getSymbol(StringRef FuncName, ResourceInfoKind RIK,
MCContext &OutContext, bool IsLocal) {
auto GOCS = [FuncName, &OutContext, IsLocal](StringRef Suffix) {
StringRef Prefix =
IsLocal ? OutContext.getAsmInfo()->getPrivateGlobalPrefix() : "";
return OutContext.getOrCreateSymbol(Twine(Prefix) + FuncName +
Twine(Suffix));
};
switch (RIK) {
case RIK_NumVGPR:
return GOCS(".num_vgpr");
case RIK_NumAGPR:
return GOCS(".num_agpr");
case RIK_NumSGPR:
return GOCS(".numbered_sgpr");
case RIK_PrivateSegSize:
return GOCS(".private_seg_size");
case RIK_UsesVCC:
return GOCS(".uses_vcc");
case RIK_UsesFlatScratch:
return GOCS(".uses_flat_scratch");
case RIK_HasDynSizedStack:
return GOCS(".has_dyn_sized_stack");
case RIK_HasRecursion:
return GOCS(".has_recursion");
case RIK_HasIndirectCall:
return GOCS(".has_indirect_call");
}
llvm_unreachable("Unexpected ResourceInfoKind.");
}
const MCExpr *MCResourceInfo::getSymRefExpr(StringRef FuncName,
ResourceInfoKind RIK,
MCContext &Ctx, bool IsLocal) {
return MCSymbolRefExpr::create(getSymbol(FuncName, RIK, Ctx, IsLocal), Ctx);
}
void MCResourceInfo::assignMaxRegs(MCContext &OutContext) {
// Assign expression to get the max register use to the max_num_Xgpr symbol.
MCSymbol *MaxVGPRSym = getMaxVGPRSymbol(OutContext);
MCSymbol *MaxAGPRSym = getMaxAGPRSymbol(OutContext);
MCSymbol *MaxSGPRSym = getMaxSGPRSymbol(OutContext);
auto assignMaxRegSym = [&OutContext](MCSymbol *Sym, int32_t RegCount) {
const MCExpr *MaxExpr = MCConstantExpr::create(RegCount, OutContext);
Sym->setVariableValue(MaxExpr);
};
assignMaxRegSym(MaxVGPRSym, MaxVGPR);
assignMaxRegSym(MaxAGPRSym, MaxAGPR);
assignMaxRegSym(MaxSGPRSym, MaxSGPR);
}
void MCResourceInfo::reset() { *this = MCResourceInfo(); }
void MCResourceInfo::finalize(MCContext &OutContext) {
assert(!Finalized && "Cannot finalize ResourceInfo again.");
Finalized = true;
assignMaxRegs(OutContext);
}
MCSymbol *MCResourceInfo::getMaxVGPRSymbol(MCContext &OutContext) {
return OutContext.getOrCreateSymbol("amdgpu.max_num_vgpr");
}
MCSymbol *MCResourceInfo::getMaxAGPRSymbol(MCContext &OutContext) {
return OutContext.getOrCreateSymbol("amdgpu.max_num_agpr");
}
MCSymbol *MCResourceInfo::getMaxSGPRSymbol(MCContext &OutContext) {
return OutContext.getOrCreateSymbol("amdgpu.max_num_sgpr");
}
void MCResourceInfo::assignResourceInfoExpr(
int64_t LocalValue, ResourceInfoKind RIK, AMDGPUMCExpr::VariantKind Kind,
const MachineFunction &MF, const SmallVectorImpl<const Function *> &Callees,
MCContext &OutContext) {
const TargetMachine &TM = MF.getTarget();
bool IsLocal = MF.getFunction().hasLocalLinkage();
MCSymbol *FnSym = TM.getSymbol(&MF.getFunction());
const MCConstantExpr *LocalConstExpr =
MCConstantExpr::create(LocalValue, OutContext);
const MCExpr *SymVal = LocalConstExpr;
MCSymbol *Sym = getSymbol(FnSym->getName(), RIK, OutContext, IsLocal);
LLVM_DEBUG(dbgs() << "MCResUse: " << Sym->getName() << ": Adding "
<< LocalValue << " as function local usage\n");
if (!Callees.empty()) {
SmallVector<const MCExpr *, 8> ArgExprs;
SmallPtrSet<const Function *, 8> Seen;
ArgExprs.push_back(LocalConstExpr);
for (const Function *Callee : Callees) {
if (!Seen.insert(Callee).second)
continue;
bool IsCalleeLocal = Callee->hasLocalLinkage();
MCSymbol *CalleeFnSym = TM.getSymbol(&Callee->getFunction());
MCSymbol *CalleeValSym =
getSymbol(CalleeFnSym->getName(), RIK, OutContext, IsCalleeLocal);
// Avoid constructing recursive definitions by detecting whether `Sym` is
// found transitively within any of its `CalleeValSym`.
if (!CalleeValSym->isVariable() ||
!CalleeValSym->getVariableValue(/*isUsed=*/false)
->isSymbolUsedInExpression(Sym)) {
LLVM_DEBUG(dbgs() << "MCResUse: " << Sym->getName() << ": Adding "
<< CalleeValSym->getName() << " as callee\n");
ArgExprs.push_back(MCSymbolRefExpr::create(CalleeValSym, OutContext));
} else {
LLVM_DEBUG(
dbgs() << "MCResUse: " << Sym->getName()
<< ": Recursion found, falling back to module maximum\n");
// In case of recursion: make sure to use conservative register counts
// (i.e., specifically for VGPR/SGPR/AGPR).
switch (RIK) {
default:
break;
case RIK_NumVGPR:
ArgExprs.push_back(MCSymbolRefExpr::create(
getMaxVGPRSymbol(OutContext), OutContext));
break;
case RIK_NumSGPR:
ArgExprs.push_back(MCSymbolRefExpr::create(
getMaxSGPRSymbol(OutContext), OutContext));
break;
case RIK_NumAGPR:
ArgExprs.push_back(MCSymbolRefExpr::create(
getMaxAGPRSymbol(OutContext), OutContext));
break;
}
}
}
if (ArgExprs.size() > 1)
SymVal = AMDGPUMCExpr::create(Kind, ArgExprs, OutContext);
}
Sym->setVariableValue(SymVal);
}
void MCResourceInfo::gatherResourceInfo(
const MachineFunction &MF,
const AMDGPUResourceUsageAnalysis::SIFunctionResourceInfo &FRI,
MCContext &OutContext) {
// Worst case VGPR use for non-hardware-entrypoints.
MCSymbol *MaxVGPRSym = getMaxVGPRSymbol(OutContext);
MCSymbol *MaxAGPRSym = getMaxAGPRSymbol(OutContext);
MCSymbol *MaxSGPRSym = getMaxSGPRSymbol(OutContext);
bool IsLocal = MF.getFunction().hasLocalLinkage();
if (!AMDGPU::isEntryFunctionCC(MF.getFunction().getCallingConv())) {
addMaxVGPRCandidate(FRI.NumVGPR);
addMaxAGPRCandidate(FRI.NumAGPR);
addMaxSGPRCandidate(FRI.NumExplicitSGPR);
}
const TargetMachine &TM = MF.getTarget();
MCSymbol *FnSym = TM.getSymbol(&MF.getFunction());
LLVM_DEBUG(dbgs() << "MCResUse: Gathering resource information for "
<< FnSym->getName() << '\n');
LLVM_DEBUG({
if (!FRI.Callees.empty()) {
dbgs() << "MCResUse: Callees:\n";
for (const Function *Callee : FRI.Callees) {
MCSymbol *CalleeFnSym = TM.getSymbol(&Callee->getFunction());
dbgs() << "MCResUse: " << CalleeFnSym->getName() << '\n';
}
}
});
auto SetMaxReg = [&](MCSymbol *MaxSym, int32_t numRegs,
ResourceInfoKind RIK) {
if (!FRI.HasIndirectCall) {
assignResourceInfoExpr(numRegs, RIK, AMDGPUMCExpr::AGVK_Max, MF,
FRI.Callees, OutContext);
} else {
const MCExpr *SymRef = MCSymbolRefExpr::create(MaxSym, OutContext);
MCSymbol *LocalNumSym =
getSymbol(FnSym->getName(), RIK, OutContext, IsLocal);
const MCExpr *MaxWithLocal = AMDGPUMCExpr::createMax(
{MCConstantExpr::create(numRegs, OutContext), SymRef}, OutContext);
LocalNumSym->setVariableValue(MaxWithLocal);
LLVM_DEBUG(dbgs() << "MCResUse: " << LocalNumSym->getName()
<< ": Indirect callee within, using module maximum\n");
}
};
LLVM_DEBUG(dbgs() << "MCResUse: " << FnSym->getName() << '\n');
SetMaxReg(MaxVGPRSym, FRI.NumVGPR, RIK_NumVGPR);
SetMaxReg(MaxAGPRSym, FRI.NumAGPR, RIK_NumAGPR);
SetMaxReg(MaxSGPRSym, FRI.NumExplicitSGPR, RIK_NumSGPR);
{
// The expression for private segment size should be: FRI.PrivateSegmentSize
// + max(FRI.Callees, FRI.CalleeSegmentSize)
SmallVector<const MCExpr *, 8> ArgExprs;
MCSymbol *Sym =
getSymbol(FnSym->getName(), RIK_PrivateSegSize, OutContext, IsLocal);
if (FRI.CalleeSegmentSize) {
LLVM_DEBUG(dbgs() << "MCResUse: " << Sym->getName() << ": Adding "
<< FRI.CalleeSegmentSize
<< " for indirect/recursive callees within\n");
ArgExprs.push_back(
MCConstantExpr::create(FRI.CalleeSegmentSize, OutContext));
}
SmallPtrSet<const Function *, 8> Seen;
Seen.insert(&MF.getFunction());
for (const Function *Callee : FRI.Callees) {
if (!Seen.insert(Callee).second)
continue;
if (!Callee->isDeclaration()) {
bool IsCalleeLocal = Callee->hasLocalLinkage();
MCSymbol *CalleeFnSym = TM.getSymbol(&Callee->getFunction());
MCSymbol *CalleeValSym =
getSymbol(CalleeFnSym->getName(), RIK_PrivateSegSize, OutContext,
IsCalleeLocal);
// Avoid constructing recursive definitions by detecting whether `Sym`
// is found transitively within any of its `CalleeValSym`.
if (!CalleeValSym->isVariable() ||
!CalleeValSym->getVariableValue(/*isUsed=*/false)
->isSymbolUsedInExpression(Sym)) {
LLVM_DEBUG(dbgs() << "MCResUse: " << Sym->getName() << ": Adding "
<< CalleeValSym->getName() << " as callee\n");
ArgExprs.push_back(MCSymbolRefExpr::create(CalleeValSym, OutContext));
}
}
}
const MCExpr *localConstExpr =
MCConstantExpr::create(FRI.PrivateSegmentSize, OutContext);
LLVM_DEBUG(dbgs() << "MCResUse: " << Sym->getName() << ": Adding "
<< FRI.PrivateSegmentSize
<< " as function local usage\n");
if (!ArgExprs.empty()) {
const AMDGPUMCExpr *transitiveExpr =
AMDGPUMCExpr::createMax(ArgExprs, OutContext);
localConstExpr =
MCBinaryExpr::createAdd(localConstExpr, transitiveExpr, OutContext);
}
Sym->setVariableValue(localConstExpr);
}
auto SetToLocal = [&](int64_t LocalValue, ResourceInfoKind RIK) {
MCSymbol *Sym = getSymbol(FnSym->getName(), RIK, OutContext, IsLocal);
LLVM_DEBUG(
dbgs() << "MCResUse: " << Sym->getName() << ": Adding " << LocalValue
<< ", no further propagation as indirect callee found within\n");
Sym->setVariableValue(MCConstantExpr::create(LocalValue, OutContext));
};
if (!FRI.HasIndirectCall) {
assignResourceInfoExpr(FRI.UsesVCC, ResourceInfoKind::RIK_UsesVCC,
AMDGPUMCExpr::AGVK_Or, MF, FRI.Callees, OutContext);
assignResourceInfoExpr(FRI.UsesFlatScratch,
ResourceInfoKind::RIK_UsesFlatScratch,
AMDGPUMCExpr::AGVK_Or, MF, FRI.Callees, OutContext);
assignResourceInfoExpr(FRI.HasDynamicallySizedStack,
ResourceInfoKind::RIK_HasDynSizedStack,
AMDGPUMCExpr::AGVK_Or, MF, FRI.Callees, OutContext);
assignResourceInfoExpr(FRI.HasRecursion, ResourceInfoKind::RIK_HasRecursion,
AMDGPUMCExpr::AGVK_Or, MF, FRI.Callees, OutContext);
assignResourceInfoExpr(FRI.HasIndirectCall,
ResourceInfoKind::RIK_HasIndirectCall,
AMDGPUMCExpr::AGVK_Or, MF, FRI.Callees, OutContext);
} else {
SetToLocal(FRI.UsesVCC, ResourceInfoKind::RIK_UsesVCC);
SetToLocal(FRI.UsesFlatScratch, ResourceInfoKind::RIK_UsesFlatScratch);
SetToLocal(FRI.HasDynamicallySizedStack,
ResourceInfoKind::RIK_HasDynSizedStack);
SetToLocal(FRI.HasRecursion, ResourceInfoKind::RIK_HasRecursion);
SetToLocal(FRI.HasIndirectCall, ResourceInfoKind::RIK_HasIndirectCall);
}
}
const MCExpr *MCResourceInfo::createTotalNumVGPRs(const MachineFunction &MF,
MCContext &Ctx) {
const TargetMachine &TM = MF.getTarget();
MCSymbol *FnSym = TM.getSymbol(&MF.getFunction());
bool IsLocal = MF.getFunction().hasLocalLinkage();
return AMDGPUMCExpr::createTotalNumVGPR(
getSymRefExpr(FnSym->getName(), RIK_NumAGPR, Ctx, IsLocal),
getSymRefExpr(FnSym->getName(), RIK_NumVGPR, Ctx, IsLocal), Ctx);
}
const MCExpr *MCResourceInfo::createTotalNumSGPRs(const MachineFunction &MF,
bool hasXnack,
MCContext &Ctx) {
const TargetMachine &TM = MF.getTarget();
MCSymbol *FnSym = TM.getSymbol(&MF.getFunction());
bool IsLocal = MF.getFunction().hasLocalLinkage();
return MCBinaryExpr::createAdd(
getSymRefExpr(FnSym->getName(), RIK_NumSGPR, Ctx, IsLocal),
AMDGPUMCExpr::createExtraSGPRs(
getSymRefExpr(FnSym->getName(), RIK_UsesVCC, Ctx, IsLocal),
getSymRefExpr(FnSym->getName(), RIK_UsesFlatScratch, Ctx, IsLocal),
hasXnack, Ctx),
Ctx);
}