CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
251 lines
9.0 KiB
C++
251 lines
9.0 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Lower LDS global variables with target extension type "amdgpu.named.barrier"
|
|
// that require specialized address assignment. It assigns a unique
|
|
// barrier identifier to each named-barrier LDS variable and encodes
|
|
// this identifier within the !absolute_symbol metadata of that global.
|
|
// This encoding ensures that subsequent LDS lowering passes can process these
|
|
// barriers correctly without conflicts.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AMDGPU.h"
|
|
#include "AMDGPUMemoryUtils.h"
|
|
#include "AMDGPUTargetMachine.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/Analysis/CallGraph.h"
|
|
#include "llvm/CodeGen/TargetPassConfig.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/ReplaceConstant.h"
|
|
#include "llvm/InitializePasses.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#define DEBUG_TYPE "amdgpu-lower-exec-sync"
|
|
|
|
using namespace llvm;
|
|
using namespace AMDGPU;
|
|
|
|
namespace {
|
|
|
|
// Write the specified address into metadata where it can be retrieved by
|
|
// the assembler. Format is a half open range, [Address Address+1)
|
|
static void recordLDSAbsoluteAddress(Module *M, GlobalVariable *GV,
|
|
uint32_t Address) {
|
|
LLVMContext &Ctx = M->getContext();
|
|
auto *IntTy = M->getDataLayout().getIntPtrType(Ctx, AMDGPUAS::LOCAL_ADDRESS);
|
|
auto *MinC = ConstantAsMetadata::get(ConstantInt::get(IntTy, Address));
|
|
auto *MaxC = ConstantAsMetadata::get(ConstantInt::get(IntTy, Address + 1));
|
|
GV->setMetadata(LLVMContext::MD_absolute_symbol,
|
|
MDNode::get(Ctx, {MinC, MaxC}));
|
|
}
|
|
|
|
/// Get next available ID for sync object. The ID allocation is tracked in \p
|
|
/// MaxNumGroup groups by \p NextAvailableIDTracker. Each call of the function
|
|
/// will ask for \p IDCnt against all the \p Kernels, it will return the
|
|
/// maximum of the available ones and update the ID tracker.
|
|
template <typename T>
|
|
unsigned allocateExecSyncID(T &NextAvailableIDTracker,
|
|
ArrayRef<Function *> Kernels, unsigned GroupID,
|
|
unsigned MaxNumGroup, unsigned IDCnt) {
|
|
constexpr unsigned InitialVal = 1;
|
|
unsigned NextID = InitialVal;
|
|
for (Function *F : Kernels) {
|
|
const SmallVectorImpl<unsigned> &NextAvailableID =
|
|
NextAvailableIDTracker.lookup(F);
|
|
unsigned ID = InitialVal;
|
|
if (!NextAvailableID.empty())
|
|
ID = NextAvailableID[GroupID];
|
|
|
|
if (ID > NextID)
|
|
NextID = ID;
|
|
}
|
|
|
|
// Bump the next available id for the kernels.
|
|
for (Function *F : Kernels) {
|
|
auto Inserted = NextAvailableIDTracker.try_emplace(F);
|
|
// Initialize on first insertion.
|
|
if (Inserted.second)
|
|
Inserted.first->second.assign(MaxNumGroup, InitialVal);
|
|
// Update the available ID.
|
|
Inserted.first->second[GroupID] = NextID + IDCnt;
|
|
}
|
|
return NextID;
|
|
}
|
|
|
|
// Main utility function for special LDS variables lowering.
|
|
static bool lowerExecSyncGlobalVariables(Module &M,
|
|
LDSUsesInfoTy &LDSUsesInfo) {
|
|
bool Changed = false;
|
|
const DataLayout &DL = M.getDataLayout();
|
|
|
|
constexpr unsigned NumBarScopes = 1;
|
|
MapVector<GlobalVariable *, SmallVector<Function *>> AllocationQ;
|
|
DenseMap<Function *, SmallVector<unsigned, NumBarScopes>> KernelBarrierIDs;
|
|
|
|
for (auto &[F, GVs] : LDSUsesInfo.indirect_access) {
|
|
for (auto *GV : GVs) {
|
|
if (!isNamedBarrier(*GV) || GV->isAbsoluteSymbolRef())
|
|
continue;
|
|
auto Iter = AllocationQ.find(GV);
|
|
if (Iter == AllocationQ.end())
|
|
AllocationQ.insert({GV, {F}});
|
|
else
|
|
Iter->second.push_back(F);
|
|
}
|
|
}
|
|
|
|
for (auto &[F, GVs] : LDSUsesInfo.direct_access) {
|
|
for (auto *GV : GVs) {
|
|
if (!isNamedBarrier(*GV) || GV->isAbsoluteSymbolRef())
|
|
continue;
|
|
auto Iter = AllocationQ.find(GV);
|
|
if (Iter == AllocationQ.end())
|
|
AllocationQ.insert({GV, {F}});
|
|
else
|
|
Iter->second.push_back(F);
|
|
}
|
|
}
|
|
|
|
sort(AllocationQ, [](std::pair<GlobalVariable *, SmallVector<Function *>> A,
|
|
std::pair<GlobalVariable *, SmallVector<Function *>> B) {
|
|
// First order by number of kernels that access the GlobalVariable.
|
|
if (A.second.size() != B.second.size())
|
|
return A.second.size() > B.second.size();
|
|
|
|
// Then order by their names so we always get a deterministic order.
|
|
return A.first->getName() < B.first->getName();
|
|
});
|
|
|
|
for (auto &[GV, Kernels] : AllocationQ) {
|
|
unsigned Offset;
|
|
if (TargetExtType *ExtTy = isNamedBarrier(*GV)) {
|
|
unsigned BarrierScope = ExtTy->getIntParameter(0);
|
|
unsigned BarCnt = GV->getGlobalSize(DL) / 16;
|
|
|
|
unsigned BarID = allocateExecSyncID(KernelBarrierIDs, Kernels,
|
|
BarrierScope, NumBarScopes, BarCnt);
|
|
|
|
LLVM_DEBUG(GV->printAsOperand(dbgs(), false);
|
|
dbgs() << " was assigned barrier id: " << BarID
|
|
<< " id-count: " << BarCnt << "\n");
|
|
// 4 bits for alignment, 5 bits for the barrier num,
|
|
// 3 bits for the barrier scope
|
|
Offset = 0x802000u | BarrierScope << 9 | BarID << 4;
|
|
} else {
|
|
llvm_unreachable("Unhandled special variable type.");
|
|
}
|
|
|
|
recordLDSAbsoluteAddress(&M, GV, Offset);
|
|
}
|
|
|
|
// Also erase those special LDS variables from indirect_access.
|
|
for (auto &K : LDSUsesInfo.indirect_access) {
|
|
assert(isKernel(*K.first));
|
|
K.second.remove_if([](GlobalVariable *GV) { return isNamedBarrier(*GV); });
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
// With object linking, barrier ID assignment is deferred to the linker.
|
|
// Externalize named barrier globals and emit self-contained metadata so the
|
|
// AsmPrinter can generate the callgraph entries the linker needs.
|
|
static bool handleNamedBarriersForObjectLinking(Module &M) {
|
|
DenseMap<GlobalVariable *, DenseSet<Function *>> BarrierToFuncs;
|
|
for (GlobalVariable &GV : M.globals()) {
|
|
if (!isNamedBarrier(GV) || GV.use_empty())
|
|
continue;
|
|
for (User *U : GV.users()) {
|
|
if (auto *I = dyn_cast<Instruction>(U))
|
|
BarrierToFuncs[&GV].insert(I->getFunction());
|
|
}
|
|
}
|
|
if (BarrierToFuncs.empty())
|
|
return false;
|
|
|
|
LLVMContext &Ctx = M.getContext();
|
|
NamedMDNode *BarMD = M.getOrInsertNamedMetadata("amdgpu.named_barrier.uses");
|
|
|
|
std::string ModuleId;
|
|
ModuleId = getUniqueModuleId(&M);
|
|
assert(!ModuleId.empty() &&
|
|
"modules with named barriers should have a unique ID");
|
|
for (auto &[V, Funcs] : BarrierToFuncs) {
|
|
if (V->hasLocalLinkage())
|
|
V->setName("__amdgpu_named_barrier." + V->getName() + ModuleId);
|
|
else if (!V->getName().starts_with("__amdgpu_named_barrier"))
|
|
V->setName("__amdgpu_named_barrier." + V->getName());
|
|
V->setInitializer(nullptr);
|
|
V->setLinkage(GlobalValue::ExternalLinkage);
|
|
|
|
SmallVector<Metadata *, 4> Ops;
|
|
Ops.push_back(ValueAsMetadata::get(V));
|
|
for (Function *F : Funcs)
|
|
Ops.push_back(ValueAsMetadata::get(F));
|
|
BarMD->addOperand(MDNode::get(Ctx, Ops));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool runLowerExecSyncGlobals(Module &M) {
|
|
if (AMDGPUTargetMachine::EnableObjectLinking)
|
|
return handleNamedBarriersForObjectLinking(M);
|
|
|
|
CallGraph CG = CallGraph(M);
|
|
bool Changed = false;
|
|
Changed |= eliminateConstantExprUsesOfLDSFromAllInstructions(M);
|
|
|
|
// For each kernel, what variables does it access directly or through
|
|
// callees
|
|
LDSUsesInfoTy LDSUsesInfo = getTransitiveUsesOfLDS(CG, M);
|
|
|
|
if (LDSUsesInfo.HasSpecialGVs) {
|
|
// Special LDS variables need special address assignment
|
|
Changed |= lowerExecSyncGlobalVariables(M, LDSUsesInfo);
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
class AMDGPULowerExecSyncLegacy : public ModulePass {
|
|
public:
|
|
static char ID;
|
|
AMDGPULowerExecSyncLegacy() : ModulePass(ID) {}
|
|
bool runOnModule(Module &M) override;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
char AMDGPULowerExecSyncLegacy::ID = 0;
|
|
char &llvm::AMDGPULowerExecSyncLegacyPassID = AMDGPULowerExecSyncLegacy::ID;
|
|
|
|
INITIALIZE_PASS_BEGIN(AMDGPULowerExecSyncLegacy, DEBUG_TYPE,
|
|
"AMDGPU lowering of execution synchronization", false,
|
|
false)
|
|
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
|
|
INITIALIZE_PASS_END(AMDGPULowerExecSyncLegacy, DEBUG_TYPE,
|
|
"AMDGPU lowering of execution synchronization", false,
|
|
false)
|
|
|
|
bool AMDGPULowerExecSyncLegacy::runOnModule(Module &M) {
|
|
return runLowerExecSyncGlobals(M);
|
|
}
|
|
|
|
ModulePass *llvm::createAMDGPULowerExecSyncLegacyPass() {
|
|
return new AMDGPULowerExecSyncLegacy();
|
|
}
|
|
|
|
PreservedAnalyses AMDGPULowerExecSyncPass::run(Module &M,
|
|
ModuleAnalysisManager &AM) {
|
|
return runLowerExecSyncGlobals(M) ? PreservedAnalyses::none()
|
|
: PreservedAnalyses::all();
|
|
}
|