Skip to content

[HLSL] Analyze update counter usage #130356

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

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ee05b44
[HLSL] Analyze update counter usage
V-FEXrt Mar 6, 2025
28e7369
format
V-FEXrt Mar 6, 2025
9062182
Add Diag and tests
V-FEXrt Mar 7, 2025
7c77771
format
V-FEXrt Mar 7, 2025
df79e62
Merge branch 'main' into hlsl-114130-counter-analysis
V-FEXrt Mar 10, 2025
11c731e
Use Module Pass
V-FEXrt Mar 10, 2025
955ce1b
comments
V-FEXrt Mar 11, 2025
92f2a46
format
V-FEXrt Mar 11, 2025
176b862
Add location to error message
V-FEXrt Mar 12, 2025
5d1648a
format
V-FEXrt Mar 12, 2025
67c76a0
Mark resources with invalid use in map
V-FEXrt Mar 12, 2025
ef30e9a
format
V-FEXrt Mar 12, 2025
173dd50
Merge branch 'main' into hlsl-114130-counter-analysis
V-FEXrt Mar 17, 2025
b1a4f4d
Fix enum name
V-FEXrt Mar 17, 2025
a4d14d3
Merge branch 'main' into hlsl-114130-counter-analysis
V-FEXrt Mar 21, 2025
1f011b7
address comments
V-FEXrt Mar 24, 2025
f7b82c0
address comments
V-FEXrt Mar 24, 2025
c82249d
address comments
V-FEXrt Mar 24, 2025
61d371f
address comments
V-FEXrt Mar 24, 2025
1202e86
address comments
V-FEXrt Mar 24, 2025
940b928
Update llvm/include/llvm/Analysis/DXILResource.h
V-FEXrt Mar 25, 2025
3366897
address comments
V-FEXrt Mar 25, 2025
6de3c49
address comments
V-FEXrt Mar 26, 2025
f6a39c7
address comments
V-FEXrt Mar 26, 2025
6ae40ec
Remove unnecessary functions
V-FEXrt Mar 26, 2025
34a9b12
Raise error for each invalid use
V-FEXrt Mar 27, 2025
649b5d4
stable_sort no longer needed
V-FEXrt Mar 27, 2025
cbe278b
remove some test boilerplate
V-FEXrt Mar 27, 2025
f210433
Pare back to analysis only
V-FEXrt Mar 27, 2025
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
78 changes: 78 additions & 0 deletions llvm/include/llvm/Analysis/DXILResource.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include "llvm/Support/Alignment.h"
#include "llvm/Support/DXILABI.h"

#include <algorithm>
#include <cstdint>

namespace llvm {
class CallInst;
class DataLayout;
Expand Down Expand Up @@ -574,6 +577,81 @@ class DXILResourceBindingWrapperPass : public ModulePass {

ModulePass *createDXILResourceBindingWrapperPassPass();

enum ResourceCounterDirection {
Increment,
Decrement,
Unknown,
MyInvalid,
};

class DXILResourceCounterDirectionMap {
std::vector<std::pair<dxil::ResourceBindingInfo, ResourceCounterDirection>>
CounterDirections;

public:
void populate(Module &M, DXILBindingMap &DBM);

ResourceCounterDirection
operator[](const dxil::ResourceBindingInfo &Info) const {
auto Lower = std::lower_bound(
CounterDirections.begin(), CounterDirections.end(),
std::pair{Info, ResourceCounterDirection::Unknown},
[](auto LHS, auto RHS) { return LHS.first < RHS.first; });

if (Lower == CounterDirections.end()) {
return ResourceCounterDirection::Unknown;
}

if (Lower->first != Info) {
return ResourceCounterDirection::Unknown;
}

return Lower->second;
}
};

class DXILResourceCounterDirectionAnalysis
: public AnalysisInfoMixin<DXILResourceCounterDirectionAnalysis> {
friend AnalysisInfoMixin<DXILResourceCounterDirectionAnalysis>;

static AnalysisKey Key;

public:
using Result = DXILResourceCounterDirectionMap;

DXILResourceCounterDirectionMap run(Module &M, ModuleAnalysisManager &AM) {
DXILResourceCounterDirectionMap DRCDM{};
DXILBindingMap &DBM = AM.getResult<DXILResourceBindingAnalysis>(M);
DRCDM.populate(M, DBM);
return DRCDM;
}
};

class DXILResourceCounterDirectionWrapperPass : public ModulePass {
std::unique_ptr<DXILResourceCounterDirectionMap> Map;

public:
static char ID;
DXILResourceCounterDirectionWrapperPass();
~DXILResourceCounterDirectionWrapperPass() override = default;

DXILResourceCounterDirectionMap &getResourceCounterDirectionMap() {
return *Map;
}
const DXILResourceCounterDirectionMap &
getResourceCounterDirectionMap() const {
return *Map;
}

void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnModule(Module &M) override;
void releaseMemory() override;

void print(raw_ostream &OS, const Module *M) const override;
};

ModulePass *createDXILResourceCounterDirectionWrapperPassPass();

} // namespace llvm

#endif // LLVM_ANALYSIS_DXILRESOURCE_H
1 change: 1 addition & 0 deletions llvm/include/llvm/InitializePasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ void initializeDCELegacyPassPass(PassRegistry &);
void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &);
void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &);
void initializeDXILResourceBindingWrapperPassPass(PassRegistry &);
void initializeDXILResourceCounterDirectionWrapperPassPass(PassRegistry &);
void initializeDXILResourceTypeWrapperPassPass(PassRegistry &);
void initializeDeadMachineInstructionElimPass(PassRegistry &);
void initializeDebugifyMachineModulePass(PassRegistry &);
Expand Down
165 changes: 165 additions & 0 deletions llvm/lib/Analysis/DXILResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include "llvm/Analysis/DXILResource.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Instructions.h"
Expand All @@ -19,6 +21,8 @@
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/FormatVariadic.h"
#include <algorithm>
#include <iterator>

#define DEBUG_TYPE "dxil-resource"

Expand Down Expand Up @@ -823,8 +827,153 @@ DXILBindingMap::findByUse(const Value *Key) const {

//===----------------------------------------------------------------------===//

static bool isUpdateCounterIntrinsic(Function &F) {
return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter;
}

void DXILResourceCounterDirectionMap::populate(Module &M, DXILBindingMap &DBM) {
std::vector<std::tuple<dxil::ResourceBindingInfo, ResourceCounterDirection,
const Function *, const CallInst *>>
DiagCounterDirs;

for (Function &F : M.functions()) {
if (!isUpdateCounterIntrinsic(F))
continue;

for (const User *U : F.users()) {
const CallInst *CI = dyn_cast<CallInst>(U);
assert(CI && "Users of dx_resource_updateCounter must be call instrs");

// Determine if the use is an increment or decrement
Value *CountArg = CI->getArgOperand(1);
ConstantInt *CountValue = dyn_cast<ConstantInt>(CountArg);
int64_t CountLiteral = CountValue->getSExtValue();

ResourceCounterDirection Direction = ResourceCounterDirection::Unknown;
if (CountLiteral > 0)
Direction = ResourceCounterDirection::Increment;
if (CountLiteral < 0)
Direction = ResourceCounterDirection::Decrement;

// Collect all potential creation points for the handle arg
Value *HandleArg = CI->getArgOperand(0);
SmallVector<dxil::ResourceBindingInfo> RBInfos = DBM.findByUse(HandleArg);
for (const dxil::ResourceBindingInfo RBInfo : RBInfos)
DiagCounterDirs.emplace_back(RBInfo, Direction, &F, CI);
}
}

// An entry that is not in the map is considered unknown so its wasted
// overhead and increased complexity to keep an entry explicitly marked
// unknown
const auto RemoveEnd = std::remove_if(
DiagCounterDirs.begin(), DiagCounterDirs.end(), [](const auto &Item) {
return std::get<ResourceCounterDirection>(Item) ==
ResourceCounterDirection::Unknown;
});

// Sort by the Binding and Direction for fast lookup
std::sort(DiagCounterDirs.begin(), RemoveEnd,
[](const auto &LHS, const auto &RHS) {
const auto L = std::pair{std::get<dxil::ResourceBindingInfo>(LHS),
std::get<ResourceCounterDirection>(LHS)};
const auto R = std::pair{std::get<dxil::ResourceBindingInfo>(RHS),
std::get<ResourceCounterDirection>(RHS)};
return L < R;
});

// Remove the duplicate entries. Since direction is considered for equality
// a unique resource with more than one direction will not be deduped.
const auto UniqueEnd = std::unique(
DiagCounterDirs.begin(), RemoveEnd, [](const auto &LHS, const auto &RHS) {
const auto L = std::pair{std::get<dxil::ResourceBindingInfo>(LHS),
std::get<ResourceCounterDirection>(LHS)};
const auto R = std::pair{std::get<dxil::ResourceBindingInfo>(RHS),
std::get<ResourceCounterDirection>(RHS)};
return L == R;
});

// Actually erase the items invalidated by remove_if + unique
DiagCounterDirs.erase(UniqueEnd, DiagCounterDirs.end());

// If any duplicate entries still exist at this point then it must be a
// resource that was both incremented and decremented which is not allowed.
// Mark all those entries as invalid.
{
auto DupFirst = DiagCounterDirs.begin();
auto DupNext = DupFirst + 1;
auto DupLast = DiagCounterDirs.end();
for (; DupFirst < DupLast && DupNext < DupLast; ++DupFirst, ++DupNext) {
if (std::get<dxil::ResourceBindingInfo>(*DupFirst) ==
std::get<dxil::ResourceBindingInfo>(*DupNext)) {
std::get<ResourceCounterDirection>(*DupFirst) =
ResourceCounterDirection::MyInvalid;
std::get<ResourceCounterDirection>(*DupNext) =
ResourceCounterDirection::MyInvalid;
}
}
}

// Raise an error for every invalid entry
for (const auto Entry : DiagCounterDirs) {
ResourceCounterDirection Dir = std::get<ResourceCounterDirection>(Entry);
const Function *F = std::get<const Function *>(Entry);
const CallInst *CI = std::get<const CallInst *>(Entry);

if (Dir != ResourceCounterDirection::MyInvalid)
continue;

StringRef Message = "RWStructuredBuffers may increment or decrement their "
"counters, but not both.";
M.getContext().diagnose(
DiagnosticInfoGenericWithLoc(Message, *F, CI->getDebugLoc()));
Copy link
Contributor

Choose a reason for hiding this comment

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

With the uniquing above, does this emit a diagnostic for exactly one (arbitrarily chosen) increment and one decrement? We should think a little bit about the user experience here and make sure it's going to be reasonable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahhhhh yep. That's not ideal. I think it may still be an improvement over dxc where the diag is only emitted for a single call. Not sure if its arbitrary or consistent on which one is picked

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you have any opinions on what we should do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

stable_sort should make it at least consistent but its still going to be arbitrary which one is picked

Copy link
Member

Choose a reason for hiding this comment

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

What does DXC do if there are multiple mismatched increments or decrements? Does it report multiple errors?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks to be fairly arbitrary what it does. Here are some links:

https://godbolt.org/z/cns8ToYGb
https://godbolt.org/z/o9qz4fdaG
https://godbolt.org/z/cKdbzdcKn
https://godbolt.org/z/7W8YTbfsx

I thought it might be picking the direction with the least total number of uses but
https://godbolt.org/z/qnG9ahdrr
disproves that

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, it looks like it is not picking the direction with least total number of uses, which would be nice. However, it is reporting multiple errors, and I think we should do that too. If I have a shader with 2 invalid decrements, I would prefer to get 2 errors. And if I fix one of them, I'd expect the number of errors to go from 2 to 1.

}

// Copy the results into the final vec
CounterDirections.clear();
CounterDirections.reserve(DiagCounterDirs.size());
std::transform(DiagCounterDirs.begin(), DiagCounterDirs.end(),
std::back_inserter(CounterDirections), [](const auto &Item) {
return std::pair{std::get<dxil::ResourceBindingInfo>(Item),
std::get<ResourceCounterDirection>(Item)};
});
}

void DXILResourceCounterDirectionWrapperPass::getAnalysisUsage(
AnalysisUsage &AU) const {
AU.addRequiredTransitive<DXILResourceBindingWrapperPass>();
AU.setPreservesAll();
}

bool DXILResourceCounterDirectionWrapperPass::runOnModule(Module &M) {
Map.reset(new DXILResourceCounterDirectionMap());

auto DBM = getAnalysis<DXILResourceBindingWrapperPass>().getBindingMap();
Map->populate(M, DBM);

return false;
}

void DXILResourceCounterDirectionWrapperPass::releaseMemory() { Map.reset(); }

void DXILResourceCounterDirectionWrapperPass::print(raw_ostream &OS,
const Module *M) const {
if (!Map) {
OS << "No resource directions have been built!\n";
return;
}
// Map->print(OS, *DRTM, M->getDataLayout());
}

// #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
// LLVM_DUMP_METHOD
// void DXILResourceCounterDirectionWrapperPass::dump() const { print(dbgs(),
// nullptr); } #endif
//===----------------------------------------------------------------------===//

AnalysisKey DXILResourceTypeAnalysis::Key;
AnalysisKey DXILResourceBindingAnalysis::Key;
AnalysisKey DXILResourceCounterDirectionAnalysis::Key;

DXILBindingMap DXILResourceBindingAnalysis::run(Module &M,
ModuleAnalysisManager &AM) {
Expand All @@ -843,6 +992,17 @@ DXILResourceBindingPrinterPass::run(Module &M, ModuleAnalysisManager &AM) {
return PreservedAnalyses::all();
}

INITIALIZE_PASS(DXILResourceCounterDirectionWrapperPass,
"dxil-resource-counter", "DXIL Resource Counter Analysis",
false, true)

DXILResourceCounterDirectionWrapperPass::
DXILResourceCounterDirectionWrapperPass()
: ModulePass(ID) {
initializeDXILResourceCounterDirectionWrapperPassPass(
*PassRegistry::getPassRegistry());
}

void DXILResourceTypeWrapperPass::anchor() {}

DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass() : ImmutablePass(ID) {
Expand All @@ -852,11 +1012,16 @@ DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass() : ImmutablePass(ID) {
INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type",
"DXIL Resource Type Analysis", false, true)
char DXILResourceTypeWrapperPass::ID = 0;
char DXILResourceCounterDirectionWrapperPass::ID = 0;

ModulePass *llvm::createDXILResourceTypeWrapperPassPass() {
return new DXILResourceTypeWrapperPass();
}

ModulePass *llvm::createDXILResourceCounterDirectionWrapperPassPass() {
return new DXILResourceCounterDirectionWrapperPass();
}

DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()
: ModulePass(ID) {
initializeDXILResourceBindingWrapperPassPass(
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ MODULE_ANALYSIS("ctx-prof-analysis", CtxProfAnalysis())
MODULE_ANALYSIS("dxil-metadata", DXILMetadataAnalysis())
MODULE_ANALYSIS("dxil-resource-binding", DXILResourceBindingAnalysis())
MODULE_ANALYSIS("dxil-resource-type", DXILResourceTypeAnalysis())
MODULE_ANALYSIS("dxil-resource-counter-direction", DXILResourceCounterDirectionAnalysis())
MODULE_ANALYSIS("inline-advisor", InlineAdvisorAnalysis())
MODULE_ANALYSIS("ir-similarity", IRSimilarityAnalysis())
MODULE_ANALYSIS("last-run-tracking", LastRunTrackingAnalysis())
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/DirectX/DXILOpLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,8 @@ class OpLowerer {
PreservedAnalyses DXILOpLowering::run(Module &M, ModuleAnalysisManager &MAM) {
DXILBindingMap &DBM = MAM.getResult<DXILResourceBindingAnalysis>(M);
DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(M);
DXILResourceCounterDirectionMap &DRCDM =
MAM.getResult<DXILResourceCounterDirectionAnalysis>(M);

bool MadeChanges = OpLowerer(M, DBM, DRTM).lowerIntrinsics();
if (!MadeChanges)
Expand Down
13 changes: 13 additions & 0 deletions llvm/test/CodeGen/DirectX/resource_counter_error.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
; RUN: not opt -S -passes='dxil-op-lower' -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
; CHECK: <unknown>:0:0: RWStructuredBuffers may increment or decrement their counters, but not both.

define void @inc_and_dec() {
entry:
%handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 2, i32 3, i32 4, i1 false)
call i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1)
call i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %handle, i8 1)
ret void
}

declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1)
declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8)
Loading
Loading