#include "AMDGPUMemoryUtils.h"
#include "AMDGPU.h"
#include "AMDGPUBaseInfo.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/MemorySSA.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/IntrinsicsAMDGPU.h"
#include "llvm/IR/ReplaceConstant.h"
#define DEBUG_TYPE "amdgpu-memory-utils"
using namespace llvm;
namespace llvm {
namespace AMDGPU {
Align getAlign(DataLayout const &DL, const GlobalVariable *GV) {
return DL.getValueOrABITypeAlignment(GV->getPointerAlignment(DL),
GV->getValueType());
}
static void collectFunctionUses(User *U, const Function *F,
SetVector<Instruction *> &InstUsers) {
SmallVector<User *> Stack{U};
while (!Stack.empty()) {
U = Stack.pop_back_val();
if (auto *I = dyn_cast<Instruction>(U)) {
if (I->getFunction() == F)
InstUsers.insert(I);
continue;
}
if (!isa<ConstantExpr>(U))
continue;
append_range(Stack, U->users());
}
}
void replaceConstantUsesInFunction(ConstantExpr *C, const Function *F) {
SetVector<Instruction *> InstUsers;
collectFunctionUses(C, F, InstUsers);
for (Instruction *I : InstUsers) {
convertConstantExprsToInstructions(I, C);
}
}
static bool shouldLowerLDSToStruct(const GlobalVariable &GV,
const Function *F) {
if (F && GV.getName() == "llvm.amdgcn.module.lds")
return false;
bool Ret = false;
SmallPtrSet<const User *, 8> Visited;
SmallVector<const User *, 16> Stack(GV.users());
assert(!F || isKernelCC(F));
while (!Stack.empty()) {
const User *V = Stack.pop_back_val();
Visited.insert(V);
if (isa<GlobalValue>(V)) {
continue;
}
if (auto *I = dyn_cast<Instruction>(V)) {
const Function *UF = I->getFunction();
if (UF == F) {
Ret = true;
} else if (!F) {
Ret |= !isKernelCC(UF);
}
continue;
}
assert(isa<Constant>(V) && "Expected a constant.");
append_range(Stack, V->users());
}
return Ret;
}
std::vector<GlobalVariable *> findVariablesToLower(Module &M,
const Function *F) {
std::vector<llvm::GlobalVariable *> LocalVars;
for (auto &GV : M.globals()) {
if (GV.getType()->getPointerAddressSpace() != AMDGPUAS::LOCAL_ADDRESS) {
continue;
}
if (!GV.hasInitializer()) {
continue;
}
if (!isa<UndefValue>(GV.getInitializer())) {
continue;
}
if (GV.isConstant()) {
continue;
}
if (!shouldLowerLDSToStruct(GV, F)) {
continue;
}
LocalVars.push_back(&GV);
}
return LocalVars;
}
bool isReallyAClobber(const Value *Ptr, MemoryDef *Def, AAResults *AA) {
Instruction *DefInst = Def->getMemoryInst();
if (isa<FenceInst>(DefInst))
return false;
if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(DefInst)) {
switch (II->getIntrinsicID()) {
case Intrinsic::amdgcn_s_barrier:
case Intrinsic::amdgcn_wave_barrier:
case Intrinsic::amdgcn_sched_barrier:
return false;
default:
break;
}
}
const auto checkNoAlias = [AA, Ptr](auto I) -> bool {
return I && AA->isNoAlias(I->getPointerOperand(), Ptr);
};
if (checkNoAlias(dyn_cast<AtomicCmpXchgInst>(DefInst)) ||
checkNoAlias(dyn_cast<AtomicRMWInst>(DefInst)))
return false;
return true;
}
bool isClobberedInFunction(const LoadInst *Load, MemorySSA *MSSA,
AAResults *AA) {
MemorySSAWalker *Walker = MSSA->getWalker();
SmallVector<MemoryAccess *> WorkList{Walker->getClobberingMemoryAccess(Load)};
SmallSet<MemoryAccess *, 8> Visited;
MemoryLocation Loc(MemoryLocation::get(Load));
LLVM_DEBUG(dbgs() << "Checking clobbering of: " << *Load << '\n');
while (!WorkList.empty()) {
MemoryAccess *MA = WorkList.pop_back_val();
if (!Visited.insert(MA).second)
continue;
if (MSSA->isLiveOnEntryDef(MA))
continue;
if (MemoryDef *Def = dyn_cast<MemoryDef>(MA)) {
LLVM_DEBUG(dbgs() << " Def: " << *Def->getMemoryInst() << '\n');
if (isReallyAClobber(Load->getPointerOperand(), Def, AA)) {
LLVM_DEBUG(dbgs() << " -> load is clobbered\n");
return true;
}
WorkList.push_back(
Walker->getClobberingMemoryAccess(Def->getDefiningAccess(), Loc));
continue;
}
const MemoryPhi *Phi = cast<MemoryPhi>(MA);
for (auto &Use : Phi->incoming_values())
WorkList.push_back(cast<MemoryAccess>(&Use));
}
LLVM_DEBUG(dbgs() << " -> no clobber\n");
return false;
}
}
}