Use direct-resource-access for workgroup and module-scope variables
Changing how workgroup vars are generated
* Generate new intrinsic functions when descriptors are allocated for
workgroup variables
* assigns spec ids then
* Apply DRA to workgroup variables
Fixed up first part of change.
* Generates workgroup variables based on allocations in
AllocateResourceDescriptors
* accessor function codegen'd as access chain instead of implicit
generation
Don't access null pointer
* Return early if the local SpecId metadata does not exist
Enable DRA on local vars
* Works, but still end up with two access chains because of how I
codegen
Reworked codegen of workgroup accessors
* Now the accessor function returns the full type of the variable
(pointer to array) and an appropriate gep is generated with it in
AllocateResourceDescriptors
* Accessor function is codegen'd in SPIR-V as an OpCopyObject of the
variable
* DirectResourceAccess collapses the geps so that they can produce a
simple access chain
* Passes ClspvTest for local memory without combine-access-chains
Don't generate OpCopyObject
* Instead just map the workgroup accessor call to the workgroup variable
id
Avoid generating unnecessary runtime array types
* improved comments
* removed dead code
* changed type generation to avoid generating types for the return of
the workgroup accessor function type
Updated tests
Enabled sharing of ptr-to-local SpecIds
* AllocateDescriptorsPass now shares SpecIds for pointer-to-local args
in different kernels if they share a type
* Modified the SPIR-V producer to track the updated way that spec ids
are assigned
* Updated a few tests
* DRA/local_arg now generates a direct access to a single workgroup
variable
* Removed some debug output from DRA
Apply DRA to global variables
* Handle global variables in DRA
* updated a couple tests and added a new one
diff --git a/lib/DirectResourceAccessPass.cpp b/lib/DirectResourceAccessPass.cpp
index ab1f267..04dd0b5 100644
--- a/lib/DirectResourceAccessPass.cpp
+++ b/lib/DirectResourceAccessPass.cpp
@@ -34,6 +34,7 @@
#include "clspv/Passes.h"
#include "ArgKind.h"
+#include "Constants.h"
using namespace llvm;
@@ -192,6 +193,7 @@
case clspv::ArgKind::ReadOnlyImage:
case clspv::ArgKind::WriteOnlyImage:
case clspv::ArgKind::Sampler:
+ case clspv::ArgKind::Local:
Changed |= RewriteAccessesForArg(fn, arg_index, arg);
break;
default:
@@ -215,8 +217,9 @@
// either a direct call to a clspv.resource.var.* or if it a GEP of
// such a thing (where the GEP can only have zero indices).
struct ParamInfo {
- // The resource-access builtin function. (@clspv.resource.var.*)
- Function *var_fn;
+ // The base value. It is either a global variable or a resource-access
+ // builtin function. (@clspv.resource.var.* or @clspv.local.var.*)
+ Value *base;
// The descriptor set.
uint32_t set;
// The binding.
@@ -239,7 +242,7 @@
seen_one = true;
return true;
}
- return pi.var_fn == common.var_fn && pi.set == common.set &&
+ return pi.base == common.base && pi.set == common.set &&
pi.binding == common.binding &&
pi.num_gep_zeroes == common.num_gep_zeroes;
};
@@ -270,19 +273,28 @@
// If the call is a call to a @clspv.resource.var.* function, then try
// to merge it, assuming the given number of GEP zero-indices so far.
if (call->getCalledFunction()->getName().startswith(
- "clspv.resource.var.")) {
+ clspv::ResourceAccessorFunction())) {
const auto set = uint32_t(
dyn_cast<ConstantInt>(call->getOperand(0))->getZExtValue());
const auto binding = uint32_t(
dyn_cast<ConstantInt>(call->getOperand(1))->getZExtValue());
if (!merge_param_info({call->getCalledFunction(), set, binding,
- num_gep_zeroes, call})) {
+ num_gep_zeroes, call}))
return false;
- }
+ } else if (call->getCalledFunction()->getName().startswith(
+ clspv::WorkgroupAccessorFunction())) {
+ const uint32_t spec_id = uint32_t(
+ dyn_cast<ConstantInt>(call->getOperand(0))->getZExtValue());
+ if (!merge_param_info({call->getCalledFunction(), spec_id, 0,
+ num_gep_zeroes, call}))
+ return false;
} else {
// A call but not to a resource access builtin function.
return false;
}
+ } else if (isa<GlobalValue>(value)) {
+ if (!merge_param_info({value, 0, 0, num_gep_zeroes, nullptr}))
+ return false;
} else {
// Not a call.
return false;
@@ -295,7 +307,7 @@
if (ShowDRA) {
if (seen_one) {
outs() << "DRA: Rewrite " << fn->getName() << " arg " << arg_index << " "
- << arg.getName() << ": " << common.var_fn->getName() << " ("
+ << arg.getName() << ": " << common.base->getName() << " ("
<< common.set << "," << common.binding
<< ") zeroes: " << common.num_gep_zeroes << " sample-call "
<< *(common.sample_call) << "\n";
@@ -309,19 +321,25 @@
auto *zero = Builder.getInt32(0);
Builder.SetInsertPoint(fn->getEntryBlock().getFirstNonPHI());
- // Create the call.
- SmallVector<Value *, 8> args(common.sample_call->arg_begin(),
- common.sample_call->arg_end());
- Value *replacement = Builder.CreateCall(common.var_fn, args);
- if (ShowDRA) {
- outs() << "DRA: Replace: call " << *replacement << "\n";
+ Value *replacement = common.base;
+ if (Function* function = dyn_cast<Function>(replacement)) {
+ // Create the call.
+ SmallVector<Value *, 8> args(common.sample_call->arg_begin(),
+ common.sample_call->arg_end());
+ replacement = Builder.CreateCall(function, args);
+ if (ShowDRA) {
+ outs() << "DRA: Replace: call " << *replacement << "\n";
+ }
}
if (common.num_gep_zeroes) {
SmallVector<Value *, 3> zeroes;
for (unsigned i = 0; i < common.num_gep_zeroes; i++) {
zeroes.push_back(zero);
}
- replacement = Builder.CreateGEP(replacement, zeroes);
+ // Builder.CreateGEP is not used to avoid creating a GEPConstantExpr in the
+ // case of global variables.
+ replacement = GetElementPtrInst::Create(nullptr, replacement, zeroes);
+ Builder.Insert(cast<Instruction>(replacement));
if (ShowDRA) {
outs() << "DRA: Replace: gep " << *replacement << "\n";
}