-
Notifications
You must be signed in to change notification settings - Fork 14.8k
[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
Changes from 12 commits
ee05b44
28e7369
9062182
7c77771
df79e62
11c731e
955ce1b
92f2a46
176b862
5d1648a
67c76a0
ef30e9a
173dd50
b1a4f4d
a4d14d3
1f011b7
f7b82c0
c82249d
61d371f
1202e86
940b928
3366897
6de3c49
f6a39c7
6ae40ec
34a9b12
649b5d4
cbe278b
f210433
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
|
@@ -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" | ||
|
||
|
@@ -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; | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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(); | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ResourceCounterDirection Direction = ResourceCounterDirection::Unknown; | ||
if (CountLiteral > 0) | ||
Direction = ResourceCounterDirection::Increment; | ||
if (CountLiteral < 0) | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
// An entry that is not in the map is considered unknown so its wasted | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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, | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[](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) = | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ResourceCounterDirection::MyInvalid; | ||
std::get<ResourceCounterDirection>(*DupNext) = | ||
ResourceCounterDirection::MyInvalid; | ||
} | ||
} | ||
} | ||
|
||
// Raise an error for every invalid entry | ||
for (const auto Entry : DiagCounterDirs) { | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have any opinions on what we should do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 I thought it might be picking the direction with the least total number of uses but There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// 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()); | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | ||
// LLVM_DUMP_METHOD | ||
// void DXILResourceCounterDirectionWrapperPass::dump() const { print(dbgs(), | ||
// nullptr); } #endif | ||
//===----------------------------------------------------------------------===// | ||
V-FEXrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
AnalysisKey DXILResourceTypeAnalysis::Key; | ||
AnalysisKey DXILResourceBindingAnalysis::Key; | ||
AnalysisKey DXILResourceCounterDirectionAnalysis::Key; | ||
|
||
DXILBindingMap DXILResourceBindingAnalysis::run(Module &M, | ||
ModuleAnalysisManager &AM) { | ||
|
@@ -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) { | ||
|
@@ -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( | ||
|
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) |
Uh oh!
There was an error while loading. Please reload this page.