mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 11:40:05 +00:00
instrument: Use Import linkage for instrumentation functions (#5355)
These functions are getting far too complicated to code in SPIRV-Tools C++. Replace them with import stubs so that the real implementations can live in Vulkan-ValidationLayers where they belong. VVL will need to define these functions in spirv and link them to the instrumented version of the user's shader. From here on out, VVL can redefine the functions and any data they use without updating SPIRV-Tools. Changing the function declarations will still require both VVL and SPIRV-Tools to be updated in lock step.
This commit is contained in:
parent
a40483d313
commit
ee7598d497
@ -133,71 +133,6 @@ static const int kInstTaskOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
|
||||
// Size of Common and Stage-specific Members
|
||||
static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
|
||||
|
||||
// Validation Error Code Offset
|
||||
//
|
||||
// This identifies the validation error. It also helps to identify
|
||||
// how many words follow in the record and their meaning.
|
||||
static const int kInstValidationOutError = kInstStageOutCnt;
|
||||
|
||||
// Validation-specific Output Record Offsets
|
||||
//
|
||||
// Each different validation will generate a potentially different
|
||||
// number of words at the end of the record giving more specifics
|
||||
// about the validation error.
|
||||
//
|
||||
// A bindless bounds error will output the index and the bound.
|
||||
static const int kInstBindlessBoundsOutDescSet = kInstStageOutCnt + 1;
|
||||
static const int kInstBindlessBoundsOutDescBinding = kInstStageOutCnt + 2;
|
||||
static const int kInstBindlessBoundsOutDescIndex = kInstStageOutCnt + 3;
|
||||
static const int kInstBindlessBoundsOutDescBound = kInstStageOutCnt + 4;
|
||||
static const int kInstBindlessBoundsOutUnused = kInstStageOutCnt + 5;
|
||||
static const int kInstBindlessBoundsOutCnt = kInstStageOutCnt + 6;
|
||||
|
||||
// A descriptor uninitialized error will output the index.
|
||||
static const int kInstBindlessUninitOutDescSet = kInstStageOutCnt + 1;
|
||||
static const int kInstBindlessUninitOutBinding = kInstStageOutCnt + 2;
|
||||
static const int kInstBindlessUninitOutDescIndex = kInstStageOutCnt + 3;
|
||||
static const int kInstBindlessUninitOutUnused = kInstStageOutCnt + 4;
|
||||
static const int kInstBindlessUninitOutUnused2 = kInstStageOutCnt + 5;
|
||||
static const int kInstBindlessUninitOutCnt = kInstStageOutCnt + 6;
|
||||
|
||||
// A buffer out-of-bounds error will output the descriptor
|
||||
// index, the buffer offset and the buffer size
|
||||
static const int kInstBindlessBuffOOBOutDescSet = kInstStageOutCnt + 1;
|
||||
static const int kInstBindlessBuffOOBOutDescBinding = kInstStageOutCnt + 2;
|
||||
static const int kInstBindlessBuffOOBOutDescIndex = kInstStageOutCnt + 3;
|
||||
static const int kInstBindlessBuffOOBOutBuffOff = kInstStageOutCnt + 4;
|
||||
static const int kInstBindlessBuffOOBOutBuffSize = kInstStageOutCnt + 5;
|
||||
static const int kInstBindlessBuffOOBOutCnt = kInstStageOutCnt + 6;
|
||||
|
||||
// A buffer address unalloc error will output the 64-bit pointer in
|
||||
// two 32-bit pieces, lower bits first.
|
||||
static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1;
|
||||
static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2;
|
||||
static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3;
|
||||
|
||||
// Maximum Output Record Member Count
|
||||
static const int kInstMaxOutCnt = kInstStageOutCnt + 6;
|
||||
|
||||
// Validation Error Codes
|
||||
//
|
||||
// These are the possible validation error codes.
|
||||
static const int kInstErrorBindlessBounds = 1;
|
||||
static const int kInstErrorBindlessUninit = 2;
|
||||
static const int kInstErrorBuffAddrUnallocRef = 3;
|
||||
static const int kInstErrorOOB = 4;
|
||||
static const int kInstErrorMax = kInstErrorOOB;
|
||||
|
||||
// Direct Input Buffer Offsets
|
||||
//
|
||||
// The following values provide member offsets into the input buffers
|
||||
// consumed by InstrumentPass::GenDebugDirectRead(). This method is utilized
|
||||
// by InstBindlessCheckPass.
|
||||
//
|
||||
// The only object in an input buffer is a runtime array of unsigned
|
||||
// integers. Each validation will have its own formatting of this array.
|
||||
static const int kDebugInputDataOffset = 0;
|
||||
|
||||
// Debug Buffer Bindings
|
||||
//
|
||||
// These are the bindings for the different buffers which are
|
||||
@ -216,63 +151,6 @@ static const int kDebugInputBindingBuffAddr = 2;
|
||||
// This is the output buffer written by InstDebugPrintfPass.
|
||||
static const int kDebugOutputPrintfStream = 3;
|
||||
|
||||
// clang-format off
|
||||
// Bindless Validation Input Buffer Format
|
||||
//
|
||||
// An input buffer for bindless validation has this structure:
|
||||
// GLSL:
|
||||
// layout(buffer_reference, std430, buffer_reference_align = 8) buffer DescriptorSetData {
|
||||
// uint num_bindings;
|
||||
// uint data[];
|
||||
// };
|
||||
//
|
||||
// layout(set = 7, binding = 1, std430) buffer inst_bindless_InputBuffer
|
||||
// {
|
||||
// DescriptorSetData desc_sets[32];
|
||||
// } inst_bindless_input_buffer;
|
||||
//
|
||||
//
|
||||
// To look up the length of a binding:
|
||||
// uint length = inst_bindless_input_buffer[set].data[binding];
|
||||
// Scalar bindings have a length of 1.
|
||||
//
|
||||
// To look up the initialization state of a descriptor in a binding:
|
||||
// uint num_bindings = inst_bindless_input_buffer[set].num_bindings;
|
||||
// uint binding_state_start = inst_bindless_input_buffer[set].data[num_bindings + binding];
|
||||
// uint init_state = inst_bindless_input_buffer[set].data[binding_state_start + index];
|
||||
//
|
||||
// For scalar bindings, use 0 for the index.
|
||||
// clang-format on
|
||||
//
|
||||
// The size of the inst_bindless_input_buffer array, regardless of how many
|
||||
// descriptor sets the device supports.
|
||||
static const int kDebugInputBindlessMaxDescSets = 32;
|
||||
|
||||
// Buffer Device Address Input Buffer Format
|
||||
//
|
||||
// An input buffer for buffer device address validation consists of a single
|
||||
// array of unsigned 64-bit integers we will call Data[]. This array is
|
||||
// formatted as follows:
|
||||
//
|
||||
// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer
|
||||
// addresses. The list is terminated with the address 0xffffffffffffffff.
|
||||
// If 0x0 is not a valid buffer address, this address is inserted at the
|
||||
// start of the list.
|
||||
//
|
||||
static const int kDebugInputBuffAddrPtrOffset = 1;
|
||||
//
|
||||
// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which
|
||||
// gives an offset to the start of the buffer length data. More
|
||||
// specifically, for a buffer whose pointer is located at input buffer offset
|
||||
// i, the length is located at:
|
||||
//
|
||||
// Data[ i - kDebugInputBuffAddrPtrOffset
|
||||
// + Data[ kDebugInputBuffAddrLengthOffset ] ]
|
||||
//
|
||||
// The length associated with the 0xffffffffffffffff address is zero. If
|
||||
// not a valid buffer, the length associated with the 0x0 address is zero.
|
||||
static const int kDebugInputBuffAddrLengthOffset = 0;
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_
|
||||
|
@ -766,11 +766,9 @@ Optimizer::PassToken CreateCombineAccessChainsPass();
|
||||
// potentially de-optimizing the instrument code, for example, inlining
|
||||
// the debug record output function throughout the module.
|
||||
//
|
||||
// The instrumentation will read and write buffers in debug
|
||||
// descriptor set |desc_set|. It will write |shader_id| in each output record
|
||||
// The instrumentation will write |shader_id| in each output record
|
||||
// to identify the shader module which generated the record.
|
||||
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
|
||||
uint32_t shader_id);
|
||||
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t shader_id);
|
||||
|
||||
// Create a pass to instrument physical buffer address checking
|
||||
// This pass instruments all physical buffer address references to check that
|
||||
@ -791,8 +789,7 @@ Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
|
||||
// The instrumentation will read and write buffers in debug
|
||||
// descriptor set |desc_set|. It will write |shader_id| in each output record
|
||||
// to identify the shader module which generated the record.
|
||||
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
|
||||
uint32_t shader_id);
|
||||
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t shader_id);
|
||||
|
||||
// Create a pass to instrument OpDebugPrintf instructions.
|
||||
// This pass replaces all OpDebugPrintf instructions with instructions to write
|
||||
|
@ -39,149 +39,11 @@ constexpr int kSpvTypeImageArrayed = 3;
|
||||
constexpr int kSpvTypeImageMS = 4;
|
||||
} // namespace
|
||||
|
||||
void InstBindlessCheckPass::SetupInputBufferIds() {
|
||||
if (input_buffer_id_ != 0) {
|
||||
return;
|
||||
}
|
||||
AddStorageBufferExt();
|
||||
if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) {
|
||||
context()->AddExtension("SPV_KHR_physical_storage_buffer");
|
||||
}
|
||||
context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
|
||||
Instruction* memory_model = get_module()->GetMemoryModel();
|
||||
// TODO should this be just Physical64?
|
||||
memory_model->SetInOperand(
|
||||
0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)});
|
||||
|
||||
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
constexpr uint32_t width = 32u;
|
||||
|
||||
// declare the DescriptorSetData struct
|
||||
analysis::Struct* desc_set_struct =
|
||||
GetStruct({type_mgr->GetUIntType(), GetUintRuntimeArrayType(width)});
|
||||
desc_set_type_id_ = type_mgr->GetTypeInstruction(desc_set_struct);
|
||||
// By the Vulkan spec, a pre-existing struct containing a RuntimeArray
|
||||
// must be a block, and will therefore be decorated with Block. Therefore
|
||||
// the undecorated type returned here will not be pre-existing and can
|
||||
// safely be decorated. Since this type is now decorated, it is out of
|
||||
// sync with the TypeManager and therefore the TypeManager must be
|
||||
// invalidated after this pass.
|
||||
assert(context()->get_def_use_mgr()->NumUses(desc_set_type_id_) == 0 &&
|
||||
"used struct type returned");
|
||||
deco_mgr->AddDecoration(desc_set_type_id_, uint32_t(spv::Decoration::Block));
|
||||
deco_mgr->AddMemberDecoration(desc_set_type_id_, 0,
|
||||
uint32_t(spv::Decoration::Offset), 0);
|
||||
deco_mgr->AddMemberDecoration(desc_set_type_id_, 1,
|
||||
uint32_t(spv::Decoration::Offset), 4);
|
||||
context()->AddDebug2Inst(
|
||||
NewGlobalName(desc_set_type_id_, "DescriptorSetData"));
|
||||
context()->AddDebug2Inst(NewMemberName(desc_set_type_id_, 0, "num_bindings"));
|
||||
context()->AddDebug2Inst(NewMemberName(desc_set_type_id_, 1, "data"));
|
||||
|
||||
// declare buffer address reference to DescriptorSetData
|
||||
desc_set_ptr_id_ = type_mgr->FindPointerToType(
|
||||
desc_set_type_id_, spv::StorageClass::PhysicalStorageBuffer);
|
||||
// runtime array of buffer addresses
|
||||
analysis::Type* rarr_ty = GetArray(type_mgr->GetType(desc_set_ptr_id_),
|
||||
kDebugInputBindlessMaxDescSets);
|
||||
deco_mgr->AddDecorationVal(type_mgr->GetId(rarr_ty),
|
||||
uint32_t(spv::Decoration::ArrayStride), 8u);
|
||||
|
||||
// declare the InputBuffer type, a struct wrapper around the runtime array
|
||||
analysis::Struct* input_buffer_struct = GetStruct({rarr_ty});
|
||||
input_buffer_struct_id_ = type_mgr->GetTypeInstruction(input_buffer_struct);
|
||||
deco_mgr->AddDecoration(input_buffer_struct_id_,
|
||||
uint32_t(spv::Decoration::Block));
|
||||
deco_mgr->AddMemberDecoration(input_buffer_struct_id_, 0,
|
||||
uint32_t(spv::Decoration::Offset), 0);
|
||||
context()->AddDebug2Inst(
|
||||
NewGlobalName(input_buffer_struct_id_, "InputBuffer"));
|
||||
context()->AddDebug2Inst(
|
||||
NewMemberName(input_buffer_struct_id_, 0, "desc_sets"));
|
||||
|
||||
input_buffer_ptr_id_ = type_mgr->FindPointerToType(
|
||||
input_buffer_struct_id_, spv::StorageClass::StorageBuffer);
|
||||
|
||||
// declare the input_buffer global variable
|
||||
input_buffer_id_ = TakeNextId();
|
||||
|
||||
const std::vector<Operand> var_operands = {
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{uint32_t(spv::StorageClass::StorageBuffer)}},
|
||||
};
|
||||
auto new_var_op = spvtools::MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpVariable, input_buffer_ptr_id_, input_buffer_id_,
|
||||
var_operands);
|
||||
|
||||
context()->AddGlobalValue(std::move(new_var_op));
|
||||
context()->AddDebug2Inst(NewGlobalName(input_buffer_id_, "input_buffer"));
|
||||
deco_mgr->AddDecorationVal(
|
||||
input_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_);
|
||||
deco_mgr->AddDecorationVal(input_buffer_id_,
|
||||
uint32_t(spv::Decoration::Binding),
|
||||
GetInputBufferBinding());
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Add the new buffer to all entry points.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}});
|
||||
context()->AnalyzeUses(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a stub function for use with Import linkage
|
||||
// clang-format off
|
||||
// GLSL:
|
||||
//bool inst_bindless_check_desc(uint shader_id, uint inst_num, uvec4 stage_info, uint desc_set, uint binding, uint desc_index,
|
||||
// uint byte_offset)
|
||||
//{
|
||||
// uint error = 0u;
|
||||
// uint param5 = 0u;
|
||||
// uint param6 = 0u;
|
||||
// uint num_bindings = 0u;
|
||||
// uint init_state = 0u;
|
||||
// if (desc_set >= 32u) {
|
||||
// error = 1u;
|
||||
// }
|
||||
// inst_bindless_DescriptorSetData set_data;
|
||||
// if (error == 0u) {
|
||||
// set_data = inst_bindless_input_buffer.desc_sets[desc_set];
|
||||
// uvec2 ptr_vec = uvec2(set_data);
|
||||
// if ((ptr_vec.x == 0u) && (ptr_vec.y == 0u)) {
|
||||
// error = 1u;
|
||||
// }
|
||||
// }
|
||||
// if (error == 0u) {
|
||||
// num_bindings = set_data.num_bindings;
|
||||
// if (binding >= num_bindings) {
|
||||
// error = 1u;
|
||||
// }
|
||||
// }
|
||||
// if (error == 0u) {
|
||||
// if (desc_index >= set_data.data[binding]) {
|
||||
// error = 1u;
|
||||
// param5 = set_data.data[binding];
|
||||
// }
|
||||
// }
|
||||
// if (0u == error) {
|
||||
// uint state_index = set_data.data[num_bindings + binding] + desc_index;
|
||||
// init_state = set_data.data[state_index];
|
||||
// if (init_state == 0u) {
|
||||
// error = 2u;
|
||||
// }
|
||||
// }
|
||||
// if (error == 0u) {
|
||||
// if (byte_offset >= init_state) {
|
||||
// error = 4u;
|
||||
// param5 = byte_offset;
|
||||
// param6 = init_state;
|
||||
// }
|
||||
// }
|
||||
// if (0u != error) {
|
||||
// inst_bindless_stream_write_6(shader_id, inst_num, stage_info, error, desc_set, binding, desc_index, param5, param6);
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
//bool inst_bindless_check_desc(const uint shader_id, const uint inst_num, const uvec4 stage_info, const uint desc_set,
|
||||
// const uint binding, const uint desc_index, const uint byte_offset) {
|
||||
//}
|
||||
// clang-format on
|
||||
uint32_t InstBindlessCheckPass::GenDescCheckFunctionId() {
|
||||
@ -195,11 +57,10 @@ uint32_t InstBindlessCheckPass::GenDescCheckFunctionId() {
|
||||
kByteOffset = 6,
|
||||
kNumArgs
|
||||
};
|
||||
if (desc_check_func_id_ != 0) {
|
||||
return desc_check_func_id_;
|
||||
if (check_desc_func_id_ != 0) {
|
||||
return check_desc_func_id_;
|
||||
}
|
||||
|
||||
SetupInputBufferIds();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
const analysis::Integer* uint_type = GetInteger(32, false);
|
||||
const analysis::Vector v4uint(uint_type, 4);
|
||||
@ -211,454 +72,32 @@ uint32_t InstBindlessCheckPass::GenDescCheckFunctionId() {
|
||||
std::unique_ptr<Function> func =
|
||||
StartFunction(func_id, type_mgr->GetBoolType(), param_types);
|
||||
|
||||
const std::vector<uint32_t> param_ids = AddParameters(*func, param_types);
|
||||
|
||||
const uint32_t func_uint_ptr =
|
||||
type_mgr->FindPointerToType(GetUintId(), spv::StorageClass::Function);
|
||||
// Create block
|
||||
auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId()));
|
||||
InstructionBuilder builder(
|
||||
context(), new_blk_ptr.get(),
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
Instruction* inst;
|
||||
const uint32_t zero_id = builder.GetUintConstantId(0);
|
||||
const uint32_t false_id = builder.GetBoolConstantId(false);
|
||||
const uint32_t true_id = builder.GetBoolConstantId(true);
|
||||
const uint32_t uint_ptr = type_mgr->FindPointerToType(
|
||||
GetUintId(), spv::StorageClass::PhysicalStorageBuffer);
|
||||
|
||||
inst = builder.AddBinaryOp(func_uint_ptr, spv::Op::OpVariable,
|
||||
uint32_t(spv::StorageClass::Function), zero_id);
|
||||
const uint32_t error_var = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(func_uint_ptr, spv::Op::OpVariable,
|
||||
uint32_t(spv::StorageClass::Function), zero_id);
|
||||
const uint32_t param5_var = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(func_uint_ptr, spv::Op::OpVariable,
|
||||
uint32_t(spv::StorageClass::Function), zero_id);
|
||||
const uint32_t param6_var = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(func_uint_ptr, spv::Op::OpVariable,
|
||||
uint32_t(spv::StorageClass::Function), zero_id);
|
||||
const uint32_t num_bindings_var = inst->result_id();
|
||||
inst = builder.AddBinaryOp(func_uint_ptr, spv::Op::OpVariable,
|
||||
uint32_t(spv::StorageClass::Function), zero_id);
|
||||
const uint32_t init_status_var = inst->result_id();
|
||||
|
||||
const uint32_t desc_set_ptr_ptr = type_mgr->FindPointerToType(
|
||||
desc_set_ptr_id_, spv::StorageClass::Function);
|
||||
|
||||
inst = builder.AddUnaryOp(desc_set_ptr_ptr, spv::Op::OpVariable,
|
||||
uint32_t(spv::StorageClass::Function));
|
||||
const uint32_t desc_set_ptr_var = inst->result_id();
|
||||
get_decoration_mgr()->AddDecoration(
|
||||
desc_set_ptr_var, uint32_t(spv::Decoration::AliasedPointer));
|
||||
|
||||
uint32_t check_label_id = TakeNextId();
|
||||
auto check_label = NewLabel(check_label_id);
|
||||
uint32_t skip_label_id = TakeNextId();
|
||||
auto skip_label = NewLabel(skip_label_id);
|
||||
inst = builder.AddBinaryOp(
|
||||
GetBoolId(), spv::Op::OpUGreaterThanEqual, param_ids[kDescSet],
|
||||
builder.GetUintConstantId(kDebugInputBindlessMaxDescSets));
|
||||
const uint32_t desc_cmp_id = inst->result_id();
|
||||
|
||||
(void)builder.AddConditionalBranch(desc_cmp_id, check_label_id, skip_label_id,
|
||||
skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
// set error
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(check_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddStore(error_var,
|
||||
builder.GetUintConstantId(kInstErrorBindlessBounds));
|
||||
builder.AddBranch(skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
// check descriptor set table entry is non-null
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(skip_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
|
||||
check_label_id = TakeNextId();
|
||||
check_label = NewLabel(check_label_id);
|
||||
skip_label_id = TakeNextId();
|
||||
skip_label = NewLabel(skip_label_id);
|
||||
inst = builder.AddLoad(GetUintId(), error_var);
|
||||
uint32_t error_val_id = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, error_val_id,
|
||||
zero_id);
|
||||
uint32_t no_error_id = inst->result_id();
|
||||
(void)builder.AddConditionalBranch(no_error_id, check_label_id, skip_label_id,
|
||||
skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(check_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
|
||||
{
|
||||
const uint32_t desc_set_ptr_ptr_sb = type_mgr->FindPointerToType(
|
||||
desc_set_ptr_id_, spv::StorageClass::StorageBuffer);
|
||||
|
||||
inst = builder.AddAccessChain(desc_set_ptr_ptr_sb, input_buffer_id_,
|
||||
{zero_id, param_ids[kDescSet]});
|
||||
const uint32_t set_access_chain_id = inst->result_id();
|
||||
|
||||
inst = builder.AddLoad(desc_set_ptr_id_, set_access_chain_id);
|
||||
const uint32_t desc_set_ptr_id = inst->result_id();
|
||||
|
||||
builder.AddStore(desc_set_ptr_var, desc_set_ptr_id);
|
||||
|
||||
inst = builder.AddUnaryOp(GetVecUintId(2), spv::Op::OpBitcast,
|
||||
desc_set_ptr_id);
|
||||
const uint32_t ptr_as_uvec_id = inst->result_id();
|
||||
|
||||
inst = builder.AddCompositeExtract(GetUintId(), ptr_as_uvec_id, {0});
|
||||
const uint32_t uvec_x = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, uvec_x, zero_id);
|
||||
const uint32_t x_is_zero_id = inst->result_id();
|
||||
|
||||
inst = builder.AddCompositeExtract(GetUintId(), ptr_as_uvec_id, {1});
|
||||
const uint32_t uvec_y = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, uvec_y, zero_id);
|
||||
const uint32_t y_is_zero_id = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpLogicalAnd, x_is_zero_id,
|
||||
y_is_zero_id);
|
||||
const uint32_t is_null_id = inst->result_id();
|
||||
|
||||
const uint32_t error_label_id = TakeNextId();
|
||||
auto error_label = NewLabel(error_label_id);
|
||||
const uint32_t merge_label_id = TakeNextId();
|
||||
auto merge_label = NewLabel(merge_label_id);
|
||||
(void)builder.AddConditionalBranch(is_null_id, error_label_id,
|
||||
merge_label_id, merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
// set error
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddStore(error_var,
|
||||
builder.GetUintConstantId(kInstErrorBindlessBounds));
|
||||
builder.AddBranch(merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddBranch(skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
}
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(skip_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
|
||||
check_label_id = TakeNextId();
|
||||
check_label = NewLabel(check_label_id);
|
||||
skip_label_id = TakeNextId();
|
||||
skip_label = NewLabel(skip_label_id);
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), error_var);
|
||||
error_val_id = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, error_val_id,
|
||||
zero_id);
|
||||
no_error_id = inst->result_id();
|
||||
(void)builder.AddConditionalBranch(no_error_id, check_label_id, skip_label_id,
|
||||
skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
// check binding is in range
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(check_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
{
|
||||
inst = builder.AddLoad(desc_set_ptr_id_, desc_set_ptr_var);
|
||||
const uint32_t desc_set_ptr_id = inst->result_id();
|
||||
|
||||
inst = builder.AddAccessChain(uint_ptr, desc_set_ptr_id, {zero_id});
|
||||
const uint32_t binding_access_chain_id = inst->result_id();
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), binding_access_chain_id, 8);
|
||||
const uint32_t num_bindings_id = inst->result_id();
|
||||
|
||||
builder.AddStore(num_bindings_var, num_bindings_id);
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual,
|
||||
param_ids[kDescBinding], num_bindings_id);
|
||||
const uint32_t bindings_cmp_id = inst->result_id();
|
||||
|
||||
const uint32_t error_label_id = TakeNextId();
|
||||
auto error_label = NewLabel(error_label_id);
|
||||
const uint32_t merge_label_id = TakeNextId();
|
||||
auto merge_label = NewLabel(merge_label_id);
|
||||
(void)builder.AddConditionalBranch(bindings_cmp_id, error_label_id,
|
||||
merge_label_id, merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
// set error
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddStore(error_var,
|
||||
builder.GetUintConstantId(kInstErrorBindlessBounds));
|
||||
builder.AddBranch(merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddBranch(skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
}
|
||||
|
||||
// read binding length
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(skip_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
|
||||
check_label_id = TakeNextId();
|
||||
check_label = NewLabel(check_label_id);
|
||||
skip_label_id = TakeNextId();
|
||||
skip_label = NewLabel(skip_label_id);
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), error_var);
|
||||
error_val_id = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, error_val_id,
|
||||
zero_id);
|
||||
no_error_id = inst->result_id();
|
||||
(void)builder.AddConditionalBranch(no_error_id, check_label_id, skip_label_id,
|
||||
skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(check_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
{
|
||||
inst = builder.AddLoad(desc_set_ptr_id_, desc_set_ptr_var);
|
||||
const uint32_t desc_set_ptr_id = inst->result_id();
|
||||
|
||||
inst = builder.AddAccessChain(
|
||||
uint_ptr, desc_set_ptr_id,
|
||||
{{builder.GetUintConstantId(1), param_ids[kDescBinding]}});
|
||||
const uint32_t length_ac_id = inst->result_id();
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), length_ac_id, sizeof(uint32_t));
|
||||
const uint32_t length_id = inst->result_id();
|
||||
|
||||
// Check descriptor index in bounds
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual,
|
||||
param_ids[kDescIndex], length_id);
|
||||
const uint32_t desc_idx_range_id = inst->result_id();
|
||||
|
||||
const uint32_t error_label_id = TakeNextId();
|
||||
auto error_label = NewLabel(error_label_id);
|
||||
const uint32_t merge_label_id = TakeNextId();
|
||||
auto merge_label = NewLabel(merge_label_id);
|
||||
(void)builder.AddConditionalBranch(desc_idx_range_id, error_label_id,
|
||||
merge_label_id, merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
// set error
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddStore(error_var,
|
||||
builder.GetUintConstantId(kInstErrorBindlessBounds));
|
||||
builder.AddStore(param5_var, length_id);
|
||||
builder.AddBranch(merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddBranch(skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
}
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(skip_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
inst = builder.AddLoad(GetUintId(), error_var);
|
||||
error_val_id = inst->result_id();
|
||||
|
||||
check_label_id = TakeNextId();
|
||||
check_label = NewLabel(check_label_id);
|
||||
skip_label_id = TakeNextId();
|
||||
skip_label = NewLabel(skip_label_id);
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), error_var);
|
||||
error_val_id = inst->result_id();
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, zero_id,
|
||||
error_val_id);
|
||||
no_error_id = inst->result_id();
|
||||
|
||||
(void)builder.AddConditionalBranch(no_error_id, check_label_id, skip_label_id,
|
||||
skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
// Read descriptor init status
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(check_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
{
|
||||
inst = builder.AddLoad(desc_set_ptr_id_, desc_set_ptr_var);
|
||||
const uint32_t desc_set_ptr_id = inst->result_id();
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), num_bindings_var);
|
||||
const uint32_t num_bindings_id = inst->result_id();
|
||||
|
||||
inst =
|
||||
builder.AddIAdd(GetUintId(), num_bindings_id, param_ids[kDescBinding]);
|
||||
const uint32_t state_offset_id = inst->result_id();
|
||||
|
||||
inst = builder.AddAccessChain(
|
||||
uint_ptr, desc_set_ptr_id,
|
||||
{{builder.GetUintConstantId(1), state_offset_id}});
|
||||
const uint32_t state_start_ac_id = inst->result_id();
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), state_start_ac_id, sizeof(uint32_t));
|
||||
const uint32_t state_start_id = inst->result_id();
|
||||
|
||||
inst = builder.AddIAdd(GetUintId(), state_start_id, param_ids[kDescIndex]);
|
||||
const uint32_t state_entry_id = inst->result_id();
|
||||
|
||||
// Note: length starts from the beginning of the buffer, not the beginning
|
||||
// of the data array
|
||||
inst = builder.AddAccessChain(
|
||||
uint_ptr, desc_set_ptr_id,
|
||||
{{builder.GetUintConstantId(1), state_entry_id}});
|
||||
const uint32_t init_ac_id = inst->result_id();
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), init_ac_id, sizeof(uint32_t));
|
||||
const uint32_t init_status_id = inst->result_id();
|
||||
|
||||
builder.AddStore(init_status_var, init_status_id);
|
||||
|
||||
// Check for uninitialized descriptor
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, init_status_id,
|
||||
zero_id);
|
||||
const uint32_t uninit_check_id = inst->result_id();
|
||||
const uint32_t error_label_id = TakeNextId();
|
||||
auto error_label = NewLabel(error_label_id);
|
||||
const uint32_t merge_label_id = TakeNextId();
|
||||
auto merge_label = NewLabel(merge_label_id);
|
||||
(void)builder.AddConditionalBranch(uninit_check_id, error_label_id,
|
||||
merge_label_id, merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddStore(error_var,
|
||||
builder.GetUintConstantId(kInstErrorBindlessUninit));
|
||||
builder.AddBranch(merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddBranch(skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
}
|
||||
|
||||
// Check for OOB.
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(skip_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
|
||||
check_label_id = TakeNextId();
|
||||
check_label = NewLabel(check_label_id);
|
||||
skip_label_id = TakeNextId();
|
||||
skip_label = NewLabel(skip_label_id);
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), error_var);
|
||||
error_val_id = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpIEqual, error_val_id,
|
||||
zero_id);
|
||||
no_error_id = inst->result_id();
|
||||
(void)builder.AddConditionalBranch(no_error_id, check_label_id, skip_label_id,
|
||||
skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(check_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
{
|
||||
inst = builder.AddLoad(GetUintId(), init_status_var);
|
||||
const uint32_t init_status_id = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThanEqual,
|
||||
param_ids[kByteOffset], init_status_id);
|
||||
const uint32_t buf_offset_range_id = inst->result_id();
|
||||
|
||||
const uint32_t error_label_id = TakeNextId();
|
||||
const uint32_t merge_label_id = TakeNextId();
|
||||
auto error_label = NewLabel(error_label_id);
|
||||
auto merge_label = NewLabel(merge_label_id);
|
||||
(void)builder.AddConditionalBranch(buf_offset_range_id, error_label_id,
|
||||
merge_label_id, merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
// set error
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddStore(error_var, builder.GetUintConstantId(kInstErrorOOB));
|
||||
builder.AddStore(param5_var, param_ids[kByteOffset]);
|
||||
builder.AddStore(param6_var, init_status_id);
|
||||
builder.AddBranch(merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
builder.AddBranch(skip_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
}
|
||||
|
||||
// check for error
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(skip_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
inst = builder.AddLoad(GetUintId(), error_var);
|
||||
error_val_id = inst->result_id();
|
||||
|
||||
inst = builder.AddBinaryOp(GetBoolId(), spv::Op::OpINotEqual, zero_id,
|
||||
error_val_id);
|
||||
const uint32_t is_error_id = inst->result_id();
|
||||
|
||||
const uint32_t error_label_id = TakeNextId();
|
||||
auto error_label = NewLabel(error_label_id);
|
||||
const uint32_t merge_label_id = TakeNextId();
|
||||
auto merge_label = NewLabel(merge_label_id);
|
||||
(void)builder.AddConditionalBranch(is_error_id, error_label_id,
|
||||
merge_label_id, merge_label_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(error_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
|
||||
// error output
|
||||
inst = builder.AddLoad(GetUintId(), param5_var);
|
||||
const uint32_t param5_val_id = inst->result_id();
|
||||
|
||||
inst = builder.AddLoad(GetUintId(), param6_var);
|
||||
const uint32_t param6_val_id = inst->result_id();
|
||||
|
||||
GenDebugStreamWrite(
|
||||
param_ids[kShaderId], param_ids[kInstructionIndex], param_ids[kStageInfo],
|
||||
{error_val_id, param_ids[kDescSet], param_ids[kDescBinding],
|
||||
param_ids[kDescIndex], param5_val_id, param6_val_id},
|
||||
&builder);
|
||||
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, false_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
// Success return
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, true_id);
|
||||
func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
|
||||
func->SetFunctionEnd(EndFunction());
|
||||
|
||||
context()->AddFunction(std::move(func));
|
||||
context()->AddDebug2Inst(NewGlobalName(func_id, "desc_check"));
|
||||
static const std::string func_name{"inst_bindless_check_desc"};
|
||||
context()->AddFunctionDeclaration(std::move(func));
|
||||
context()->AddDebug2Inst(NewName(func_id, func_name));
|
||||
std::vector<Operand> operands{
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {func_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{uint32_t(spv::Decoration::LinkageAttributes)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_STRING,
|
||||
utils::MakeVector(func_name.c_str())},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LINKAGE_TYPE,
|
||||
{uint32_t(spv::LinkageType::Import)}},
|
||||
};
|
||||
get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, operands);
|
||||
|
||||
desc_check_func_id_ = func_id;
|
||||
check_desc_func_id_ = func_id;
|
||||
// Make sure function doesn't get processed by
|
||||
// InstrumentPass::InstProcessCallTreeFromRoots()
|
||||
param2output_func_id_[3] = func_id;
|
||||
return desc_check_func_id_;
|
||||
return check_desc_func_id_;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
// GLSL:
|
||||
// result = inst_bindless_desc_check(shader_id, inst_idx, stage_info, desc_set, binding, desc_idx, offset);
|
||||
// result = inst_bindless_check_desc(shader_id, inst_idx, stage_info, desc_set, binding, desc_idx, offset);
|
||||
//
|
||||
// clang-format on
|
||||
uint32_t InstBindlessCheckPass::GenDescCheckCall(
|
||||
@ -1134,8 +573,7 @@ uint32_t InstBindlessCheckPass::GenLastByteIdx(RefAnalysis* ref,
|
||||
}
|
||||
|
||||
void InstBindlessCheckPass::GenCheckCode(
|
||||
uint32_t check_id, uint32_t error_id, uint32_t offset_id,
|
||||
uint32_t length_id, uint32_t stage_idx, RefAnalysis* ref,
|
||||
uint32_t check_id, RefAnalysis* ref,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
||||
BasicBlock* back_blk_ptr = &*new_blocks->back();
|
||||
InstructionBuilder builder(
|
||||
@ -1164,31 +602,7 @@ void InstBindlessCheckPass::GenCheckCode(
|
||||
// Gen invalid block
|
||||
new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
if (error_id != 0) {
|
||||
const uint32_t u_shader_id = builder.GetUintConstantId(shader_id_);
|
||||
const uint32_t u_inst_id =
|
||||
builder.GetUintConstantId(ref->ref_inst->unique_id());
|
||||
const uint32_t shader_info_id = GenStageInfo(stage_idx, &builder);
|
||||
const uint32_t u_set_id = builder.GetUintConstantId(ref->set);
|
||||
const uint32_t u_binding_id = builder.GetUintConstantId(ref->binding);
|
||||
const uint32_t u_index_id = GenUintCastCode(ref->desc_idx_id, &builder);
|
||||
const uint32_t u_length_id = GenUintCastCode(length_id, &builder);
|
||||
if (offset_id != 0) {
|
||||
const uint32_t u_offset_id = GenUintCastCode(offset_id, &builder);
|
||||
// Buffer OOB
|
||||
GenDebugStreamWrite(u_shader_id, u_inst_id, shader_info_id,
|
||||
{error_id, u_set_id, u_binding_id, u_index_id,
|
||||
u_offset_id, u_length_id},
|
||||
&builder);
|
||||
} else {
|
||||
// Uninitialized Descriptor - Return additional unused zero so all error
|
||||
// modes will use same debug stream write function
|
||||
GenDebugStreamWrite(u_shader_id, u_inst_id, shader_info_id,
|
||||
{error_id, u_set_id, u_binding_id, u_index_id,
|
||||
u_length_id, builder.GetUintConstantId(0)},
|
||||
&builder);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a ConstantNull, converting to uint64 if the type cannot be a null.
|
||||
if (new_ref_id != 0) {
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
@ -1283,7 +697,7 @@ void InstBindlessCheckPass::GenDescCheckCode(
|
||||
// Generate runtime initialization/bounds test code with true branch
|
||||
// being full reference and false branch being zero
|
||||
// for the referenced value.
|
||||
GenCheckCode(check_id, 0, 0, 0, stage_idx, &ref, new_blocks);
|
||||
GenCheckCode(check_id, &ref, new_blocks);
|
||||
|
||||
// Move original block's remaining code into remainder/merge block and add
|
||||
// to new blocks
|
||||
@ -1311,6 +725,20 @@ void InstBindlessCheckPass::InitializeInstBindlessCheck() {
|
||||
|
||||
Pass::Status InstBindlessCheckPass::ProcessImpl() {
|
||||
bool modified = false;
|
||||
// The memory model and linkage must always be updated for spirv-link to work
|
||||
// correctly.
|
||||
AddStorageBufferExt();
|
||||
if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) {
|
||||
context()->AddExtension("SPV_KHR_physical_storage_buffer");
|
||||
}
|
||||
|
||||
context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
|
||||
Instruction* memory_model = get_module()->GetMemoryModel();
|
||||
memory_model->SetInOperand(
|
||||
0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)});
|
||||
|
||||
context()->AddCapability(spv::Capability::Linkage);
|
||||
|
||||
InstProcessFunction pfn =
|
||||
[this](BasicBlock::iterator ref_inst_itr,
|
||||
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
||||
|
@ -28,8 +28,8 @@ namespace opt {
|
||||
// external design may change as the layer evolves.
|
||||
class InstBindlessCheckPass : public InstrumentPass {
|
||||
public:
|
||||
InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id)
|
||||
: InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, true) {}
|
||||
InstBindlessCheckPass(uint32_t shader_id)
|
||||
: InstrumentPass(0, shader_id, true) {}
|
||||
|
||||
~InstBindlessCheckPass() override = default;
|
||||
|
||||
@ -44,8 +44,6 @@ class InstBindlessCheckPass : public InstrumentPass {
|
||||
uint32_t stage_idx,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
|
||||
|
||||
void SetupInputBufferIds();
|
||||
|
||||
uint32_t GenDescCheckFunctionId();
|
||||
|
||||
uint32_t GenDescCheckCall(uint32_t inst_idx, uint32_t stage_idx,
|
||||
@ -107,8 +105,7 @@ class InstBindlessCheckPass : public InstrumentPass {
|
||||
// writes debug error output utilizing |ref|, |error_id|, |length_id| and
|
||||
// |stage_idx|. Generate merge block for valid and invalid branches. Kill
|
||||
// original reference.
|
||||
void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t offset_id,
|
||||
uint32_t length_id, uint32_t stage_idx, RefAnalysis* ref,
|
||||
void GenCheckCode(uint32_t check_id, RefAnalysis* ref,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
|
||||
|
||||
// Initialize state for instrumenting bindless checking
|
||||
@ -124,11 +121,7 @@ class InstBindlessCheckPass : public InstrumentPass {
|
||||
// Mapping from variable to binding
|
||||
std::unordered_map<uint32_t, uint32_t> var2binding_;
|
||||
|
||||
uint32_t desc_check_func_id_{0};
|
||||
uint32_t desc_set_type_id_{0};
|
||||
uint32_t desc_set_ptr_id_{0};
|
||||
uint32_t input_buffer_struct_id_{0};
|
||||
uint32_t input_buffer_ptr_id_{0};
|
||||
uint32_t check_desc_func_id_{0};
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -19,24 +19,6 @@
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
bool InstBuffAddrCheckPass::InstrumentFunction(Function* func,
|
||||
uint32_t stage_idx,
|
||||
InstProcessFunction& pfn) {
|
||||
// The bindless instrumentation pass adds functions that use
|
||||
// BufferDeviceAddress They should not be instrumented by this pass.
|
||||
Instruction* func_name_inst =
|
||||
context()->GetNames(func->DefInst().result_id()).begin()->second;
|
||||
if (func_name_inst) {
|
||||
static const std::string kPrefix{"inst_bindless_"};
|
||||
std::string func_name = func_name_inst->GetOperand(1).AsString();
|
||||
if (func_name.size() >= kPrefix.size() &&
|
||||
func_name.compare(0, kPrefix.size(), kPrefix) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return InstrumentPass::InstrumentFunction(func, stage_idx, pfn);
|
||||
}
|
||||
|
||||
uint32_t InstBuffAddrCheckPass::CloneOriginalReference(
|
||||
Instruction* ref_inst, InstructionBuilder* builder) {
|
||||
// Clone original ref with new result id (if load)
|
||||
@ -76,8 +58,7 @@ bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) {
|
||||
|
||||
// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ??
|
||||
void InstBuffAddrCheckPass::GenCheckCode(
|
||||
uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id,
|
||||
uint32_t stage_idx, Instruction* ref_inst,
|
||||
uint32_t check_id, Instruction* ref_inst,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
||||
BasicBlock* back_blk_ptr = &*new_blocks->back();
|
||||
InstructionBuilder builder(
|
||||
@ -104,20 +85,6 @@ void InstBuffAddrCheckPass::GenCheckCode(
|
||||
// Gen invalid block
|
||||
new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
// Convert uptr from uint64 to 2 uint32
|
||||
Instruction* lo_uptr_inst =
|
||||
builder.AddUnaryOp(GetUintId(), spv::Op::OpUConvert, ref_uptr_id);
|
||||
Instruction* rshift_uptr_inst =
|
||||
builder.AddBinaryOp(GetUint64Id(), spv::Op::OpShiftRightLogical,
|
||||
ref_uptr_id, builder.GetUintConstantId(32));
|
||||
Instruction* hi_uptr_inst = builder.AddUnaryOp(
|
||||
GetUintId(), spv::Op::OpUConvert, rshift_uptr_inst->result_id());
|
||||
GenDebugStreamWrite(
|
||||
builder.GetUintConstantId(shader_id_),
|
||||
builder.GetUintConstantId(uid2offset_[ref_inst->unique_id()]),
|
||||
GenStageInfo(stage_idx, &builder),
|
||||
{error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()},
|
||||
&builder);
|
||||
// Gen zero for invalid load. If pointer type, need to convert uint64
|
||||
// zero to pointer; cannot create ConstantNull of pointer type.
|
||||
uint32_t null_id = 0;
|
||||
@ -206,201 +173,86 @@ void InstBuffAddrCheckPass::AddParam(uint32_t type_id,
|
||||
(*input_func)->AddParameter(std::move(param_inst));
|
||||
}
|
||||
|
||||
// This is a stub function for use with Import linkage
|
||||
// clang-format off
|
||||
// GLSL:
|
||||
//bool inst_bindless_search_and_test(const uint shader_id, const uint inst_num, const uvec4 stage_info,
|
||||
// const uint64 ref_ptr, const uint length) {
|
||||
//}
|
||||
// clang-format on
|
||||
uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
|
||||
if (search_test_func_id_ == 0) {
|
||||
// Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
|
||||
// which searches input buffer for buffer which most likely contains the
|
||||
// pointer value |ref_ptr| and verifies that the entire reference of
|
||||
// length |len| bytes is contained in the buffer.
|
||||
search_test_func_id_ = TakeNextId();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
std::vector<const analysis::Type*> param_types = {
|
||||
type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())};
|
||||
analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types);
|
||||
analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
|
||||
std::unique_ptr<Instruction> func_inst(
|
||||
new Instruction(get_module()->context(), spv::Op::OpFunction,
|
||||
GetBoolId(), search_test_func_id_,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{uint32_t(spv::FunctionControlMask::MaskNone)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{type_mgr->GetTypeInstruction(reg_func_ty)}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
|
||||
std::unique_ptr<Function> input_func =
|
||||
MakeUnique<Function>(std::move(func_inst));
|
||||
std::vector<uint32_t> param_vec;
|
||||
// Add ref_ptr and length parameters
|
||||
AddParam(GetUint64Id(), ¶m_vec, &input_func);
|
||||
AddParam(GetUintId(), ¶m_vec, &input_func);
|
||||
// Empty first block.
|
||||
uint32_t first_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> first_blk_label(NewLabel(first_blk_id));
|
||||
std::unique_ptr<BasicBlock> first_blk_ptr =
|
||||
MakeUnique<BasicBlock>(std::move(first_blk_label));
|
||||
InstructionBuilder builder(
|
||||
context(), &*first_blk_ptr,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
uint32_t hdr_blk_id = TakeNextId();
|
||||
// Branch to search loop header
|
||||
std::unique_ptr<Instruction> hdr_blk_label(NewLabel(hdr_blk_id));
|
||||
(void)builder.AddBranch(hdr_blk_id);
|
||||
input_func->AddBasicBlock(std::move(first_blk_ptr));
|
||||
// Linear search loop header block
|
||||
// TODO(greg-lunarg): Implement binary search
|
||||
std::unique_ptr<BasicBlock> hdr_blk_ptr =
|
||||
MakeUnique<BasicBlock>(std::move(hdr_blk_label));
|
||||
builder.SetInsertPoint(&*hdr_blk_ptr);
|
||||
// Phi for search index. Starts with 1.
|
||||
uint32_t cont_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> cont_blk_label(NewLabel(cont_blk_id));
|
||||
// Deal with def-use cycle caused by search loop index computation.
|
||||
// Create Add and Phi instructions first, then do Def analysis on Add.
|
||||
// Add Phi and Add instructions and do Use analysis later.
|
||||
uint32_t idx_phi_id = TakeNextId();
|
||||
uint32_t idx_inc_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> idx_inc_inst(new Instruction(
|
||||
context(), spv::Op::OpIAdd, GetUintId(), idx_inc_id,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{builder.GetUintConstantId(1u)}}}));
|
||||
std::unique_ptr<Instruction> idx_phi_inst(new Instruction(
|
||||
context(), spv::Op::OpPhi, GetUintId(), idx_phi_id,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{builder.GetUintConstantId(1u)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst);
|
||||
// Add (previously created) search index phi
|
||||
(void)builder.AddInstruction(std::move(idx_phi_inst));
|
||||
// LoopMerge
|
||||
uint32_t bound_test_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> bound_test_blk_label(
|
||||
NewLabel(bound_test_blk_id));
|
||||
(void)builder.AddLoopMerge(bound_test_blk_id, cont_blk_id,
|
||||
uint32_t(spv::LoopControlMask::MaskNone));
|
||||
// Branch to continue/work block
|
||||
(void)builder.AddBranch(cont_blk_id);
|
||||
input_func->AddBasicBlock(std::move(hdr_blk_ptr));
|
||||
// Continue/Work Block. Read next buffer pointer and break if greater
|
||||
// than ref_ptr arg.
|
||||
std::unique_ptr<BasicBlock> cont_blk_ptr =
|
||||
MakeUnique<BasicBlock>(std::move(cont_blk_label));
|
||||
builder.SetInsertPoint(&*cont_blk_ptr);
|
||||
// Add (previously created) search index increment now.
|
||||
(void)builder.AddInstruction(std::move(idx_inc_inst));
|
||||
// Load next buffer address from debug input buffer
|
||||
uint32_t ibuf_id = GetInputBufferId();
|
||||
uint32_t ibuf_ptr_id = GetInputBufferPtrId();
|
||||
Instruction* uptr_ac_inst = builder.AddTernaryOp(
|
||||
ibuf_ptr_id, spv::Op::OpAccessChain, ibuf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id);
|
||||
uint32_t ibuf_type_id = GetInputBufferTypeId();
|
||||
Instruction* uptr_load_inst = builder.AddUnaryOp(
|
||||
ibuf_type_id, spv::Op::OpLoad, uptr_ac_inst->result_id());
|
||||
// If loaded address greater than ref_ptr arg, break, else branch back to
|
||||
// loop header
|
||||
Instruction* uptr_test_inst =
|
||||
builder.AddBinaryOp(GetBoolId(), spv::Op::OpUGreaterThan,
|
||||
uptr_load_inst->result_id(), param_vec[0]);
|
||||
(void)builder.AddConditionalBranch(
|
||||
uptr_test_inst->result_id(), bound_test_blk_id, hdr_blk_id, kInvalidId,
|
||||
uint32_t(spv::SelectionControlMask::MaskNone));
|
||||
input_func->AddBasicBlock(std::move(cont_blk_ptr));
|
||||
// Bounds test block. Read length of selected buffer and test that
|
||||
// all len arg bytes are in buffer.
|
||||
std::unique_ptr<BasicBlock> bound_test_blk_ptr =
|
||||
MakeUnique<BasicBlock>(std::move(bound_test_blk_label));
|
||||
builder.SetInsertPoint(&*bound_test_blk_ptr);
|
||||
// Decrement index to point to previous/candidate buffer address
|
||||
Instruction* cand_idx_inst =
|
||||
builder.AddBinaryOp(GetUintId(), spv::Op::OpISub, idx_inc_id,
|
||||
builder.GetUintConstantId(1u));
|
||||
// Load candidate buffer address
|
||||
Instruction* cand_ac_inst =
|
||||
builder.AddTernaryOp(ibuf_ptr_id, spv::Op::OpAccessChain, ibuf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset),
|
||||
cand_idx_inst->result_id());
|
||||
Instruction* cand_load_inst = builder.AddUnaryOp(
|
||||
ibuf_type_id, spv::Op::OpLoad, cand_ac_inst->result_id());
|
||||
// Compute offset of ref_ptr from candidate buffer address
|
||||
Instruction* offset_inst =
|
||||
builder.AddBinaryOp(ibuf_type_id, spv::Op::OpISub, param_vec[0],
|
||||
cand_load_inst->result_id());
|
||||
// Convert ref length to uint64
|
||||
Instruction* ref_len_64_inst =
|
||||
builder.AddUnaryOp(ibuf_type_id, spv::Op::OpUConvert, param_vec[1]);
|
||||
// Add ref length to ref offset to compute end of reference
|
||||
Instruction* ref_end_inst = builder.AddBinaryOp(
|
||||
ibuf_type_id, spv::Op::OpIAdd, offset_inst->result_id(),
|
||||
ref_len_64_inst->result_id());
|
||||
// Load starting index of lengths in input buffer and convert to uint32
|
||||
Instruction* len_start_ac_inst =
|
||||
builder.AddTernaryOp(ibuf_ptr_id, spv::Op::OpAccessChain, ibuf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset),
|
||||
builder.GetUintConstantId(0u));
|
||||
Instruction* len_start_load_inst = builder.AddUnaryOp(
|
||||
ibuf_type_id, spv::Op::OpLoad, len_start_ac_inst->result_id());
|
||||
Instruction* len_start_32_inst = builder.AddUnaryOp(
|
||||
GetUintId(), spv::Op::OpUConvert, len_start_load_inst->result_id());
|
||||
// Decrement search index to get candidate buffer length index
|
||||
Instruction* cand_len_idx_inst = builder.AddBinaryOp(
|
||||
GetUintId(), spv::Op::OpISub, cand_idx_inst->result_id(),
|
||||
builder.GetUintConstantId(1u));
|
||||
// Add candidate length index to start index
|
||||
Instruction* len_idx_inst = builder.AddBinaryOp(
|
||||
GetUintId(), spv::Op::OpIAdd, cand_len_idx_inst->result_id(),
|
||||
len_start_32_inst->result_id());
|
||||
// Load candidate buffer length
|
||||
Instruction* len_ac_inst =
|
||||
builder.AddTernaryOp(ibuf_ptr_id, spv::Op::OpAccessChain, ibuf_id,
|
||||
builder.GetUintConstantId(kDebugInputDataOffset),
|
||||
len_idx_inst->result_id());
|
||||
Instruction* len_load_inst = builder.AddUnaryOp(
|
||||
ibuf_type_id, spv::Op::OpLoad, len_ac_inst->result_id());
|
||||
// Test if reference end within candidate buffer length
|
||||
Instruction* len_test_inst = builder.AddBinaryOp(
|
||||
GetBoolId(), spv::Op::OpULessThanEqual, ref_end_inst->result_id(),
|
||||
len_load_inst->result_id());
|
||||
// Return test result
|
||||
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue,
|
||||
len_test_inst->result_id());
|
||||
// Close block
|
||||
input_func->AddBasicBlock(std::move(bound_test_blk_ptr));
|
||||
// Close function and add function to module
|
||||
std::unique_ptr<Instruction> func_end_inst(new Instruction(
|
||||
get_module()->context(), spv::Op::OpFunctionEnd, 0, 0, {}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
|
||||
input_func->SetFunctionEnd(std::move(func_end_inst));
|
||||
context()->AddFunction(std::move(input_func));
|
||||
context()->AddDebug2Inst(
|
||||
NewGlobalName(search_test_func_id_, "search_and_test"));
|
||||
enum {
|
||||
kShaderId = 0,
|
||||
kInstructionIndex = 1,
|
||||
kStageInfo = 2,
|
||||
kRefPtr = 3,
|
||||
kLength = 4,
|
||||
kNumArgs
|
||||
};
|
||||
if (search_test_func_id_ != 0) {
|
||||
return search_test_func_id_;
|
||||
}
|
||||
// Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)"
|
||||
// which searches input buffer for buffer which most likely contains the
|
||||
// pointer value |ref_ptr| and verifies that the entire reference of
|
||||
// length |len| bytes is contained in the buffer.
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
const analysis::Integer* uint_type = GetInteger(32, false);
|
||||
const analysis::Vector v4uint(uint_type, 4);
|
||||
const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint);
|
||||
|
||||
std::vector<const analysis::Type*> param_types = {
|
||||
uint_type, uint_type, v4uint_type, type_mgr->GetType(GetUint64Id()),
|
||||
uint_type};
|
||||
|
||||
const std::string func_name{"inst_buff_addr_search_and_test"};
|
||||
const uint32_t func_id = TakeNextId();
|
||||
std::unique_ptr<Function> func =
|
||||
StartFunction(func_id, type_mgr->GetBoolType(), param_types);
|
||||
func->SetFunctionEnd(EndFunction());
|
||||
context()->AddFunctionDeclaration(std::move(func));
|
||||
context()->AddDebug2Inst(NewName(func_id, func_name));
|
||||
|
||||
std::vector<Operand> operands{
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {func_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{uint32_t(spv::Decoration::LinkageAttributes)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_STRING,
|
||||
utils::MakeVector(func_name.c_str())},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LINKAGE_TYPE,
|
||||
{uint32_t(spv::LinkageType::Import)}},
|
||||
};
|
||||
get_decoration_mgr()->AddDecoration(spv::Op::OpDecorate, operands);
|
||||
|
||||
search_test_func_id_ = func_id;
|
||||
return search_test_func_id_;
|
||||
}
|
||||
|
||||
uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst,
|
||||
InstructionBuilder* builder,
|
||||
uint32_t* ref_uptr_id) {
|
||||
uint32_t* ref_uptr_id,
|
||||
uint32_t stage_idx) {
|
||||
// Enable Int64 if necessary
|
||||
context()->AddCapability(spv::Capability::Int64);
|
||||
// Convert reference pointer to uint64
|
||||
uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
|
||||
const uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
|
||||
Instruction* ref_uptr_inst =
|
||||
builder->AddUnaryOp(GetUint64Id(), spv::Op::OpConvertPtrToU, ref_ptr_id);
|
||||
*ref_uptr_id = ref_uptr_inst->result_id();
|
||||
// Compute reference length in bytes
|
||||
analysis::DefUseManager* du_mgr = get_def_use_mgr();
|
||||
Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id);
|
||||
uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
|
||||
const uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
|
||||
Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id);
|
||||
uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
|
||||
uint32_t ref_len_id = builder->GetUintConstantId(ref_len);
|
||||
const uint32_t ref_len =
|
||||
GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
|
||||
// Gen call to search and test function
|
||||
Instruction* call_inst = builder->AddFunctionCall(
|
||||
GetBoolId(), GetSearchAndTestFuncId(), {*ref_uptr_id, ref_len_id});
|
||||
uint32_t retval = call_inst->result_id();
|
||||
return retval;
|
||||
const uint32_t func_id = GetSearchAndTestFuncId();
|
||||
const std::vector<uint32_t> args = {
|
||||
builder->GetUintConstantId(shader_id_),
|
||||
builder->GetUintConstantId(ref_inst->unique_id()),
|
||||
GenStageInfo(stage_idx, builder), *ref_uptr_id,
|
||||
builder->GetUintConstantId(ref_len)};
|
||||
return GenReadFunctionCall(GetBoolId(), func_id, args, builder);
|
||||
}
|
||||
|
||||
void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
|
||||
@ -418,16 +270,16 @@ void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
|
||||
context(), &*new_blk_ptr,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef);
|
||||
// Generate code to do search and test if all bytes of reference
|
||||
// are within a listed buffer. Return reference pointer converted to uint64.
|
||||
uint32_t ref_uptr_id;
|
||||
uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id);
|
||||
uint32_t valid_id =
|
||||
GenSearchAndTest(ref_inst, &builder, &ref_uptr_id, stage_idx);
|
||||
// Generate test of search results with true branch
|
||||
// being full reference and false branch being debug output and zero
|
||||
// for the referenced value.
|
||||
GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst,
|
||||
new_blocks);
|
||||
GenCheckCode(valid_id, ref_inst, new_blocks);
|
||||
|
||||
// Move original block's remaining code into remainder/merge block and add
|
||||
// to new blocks
|
||||
BasicBlock* back_blk_ptr = &*new_blocks->back();
|
||||
@ -442,6 +294,15 @@ void InstBuffAddrCheckPass::InitInstBuffAddrCheck() {
|
||||
}
|
||||
|
||||
Pass::Status InstBuffAddrCheckPass::ProcessImpl() {
|
||||
// The memory model and linkage must always be updated for spirv-link to work
|
||||
// correctly.
|
||||
AddStorageBufferExt();
|
||||
if (!get_feature_mgr()->HasExtension(kSPV_KHR_physical_storage_buffer)) {
|
||||
context()->AddExtension("SPV_KHR_physical_storage_buffer");
|
||||
}
|
||||
|
||||
context()->AddCapability(spv::Capability::Int64);
|
||||
context()->AddCapability(spv::Capability::Linkage);
|
||||
// Perform bindless bounds check on each entry point function in module
|
||||
InstProcessFunction pfn =
|
||||
[this](BasicBlock::iterator ref_inst_itr,
|
||||
|
@ -29,10 +29,9 @@ namespace opt {
|
||||
class InstBuffAddrCheckPass : public InstrumentPass {
|
||||
public:
|
||||
// For test harness only
|
||||
InstBuffAddrCheckPass() : InstrumentPass(7, 23, kInstValidationIdBuffAddr) {}
|
||||
InstBuffAddrCheckPass() : InstrumentPass(0, 23) {}
|
||||
// For all other interfaces
|
||||
InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id)
|
||||
: InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {}
|
||||
InstBuffAddrCheckPass(uint32_t shader_id) : InstrumentPass(0, shader_id) {}
|
||||
|
||||
~InstBuffAddrCheckPass() override = default;
|
||||
|
||||
@ -41,9 +40,6 @@ class InstBuffAddrCheckPass : public InstrumentPass {
|
||||
|
||||
const char* name() const override { return "inst-buff-addr-check-pass"; }
|
||||
|
||||
bool InstrumentFunction(Function* func, uint32_t stage_idx,
|
||||
InstProcessFunction& pfn) override;
|
||||
|
||||
private:
|
||||
// Return byte length of type |type_id|. Must be int, float, vector, matrix,
|
||||
// struct, array or physical pointer. Uses std430 alignment and sizes.
|
||||
@ -61,7 +57,7 @@ class InstBuffAddrCheckPass : public InstrumentPass {
|
||||
// are within the buffer. Returns id of boolean value which is true if
|
||||
// search and test is successful, false otherwise.
|
||||
uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder,
|
||||
uint32_t* ref_uptr_id);
|
||||
uint32_t* ref_uptr_id, uint32_t stage_idx);
|
||||
|
||||
// This function does checking instrumentation on a single
|
||||
// instruction which references through a physical storage buffer address.
|
||||
@ -114,8 +110,7 @@ class InstBuffAddrCheckPass : public InstrumentPass {
|
||||
// writes debug error output utilizing |ref_inst|, |error_id| and
|
||||
// |stage_idx|. Generate merge block for valid and invalid reference blocks.
|
||||
// Kill original reference.
|
||||
void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id,
|
||||
uint32_t stage_idx, Instruction* ref_inst,
|
||||
void GenCheckCode(uint32_t check_id, Instruction* ref_inst,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
|
||||
|
||||
// Initialize state for instrumenting physical buffer address checking
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "inst_debug_printf_pass.h"
|
||||
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/util/string_utils.h"
|
||||
#include "spirv/unified1/NonSemanticDebugPrintf.h"
|
||||
|
||||
@ -210,9 +211,244 @@ void InstDebugPrintfPass::GenDebugPrintfCode(
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
}
|
||||
|
||||
// Return id for output buffer
|
||||
uint32_t InstDebugPrintfPass::GetOutputBufferId() {
|
||||
if (output_buffer_id_ == 0) {
|
||||
// If not created yet, create one
|
||||
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::RuntimeArray* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
|
||||
analysis::Integer* reg_uint_ty = GetInteger(32, false);
|
||||
analysis::Type* reg_buf_ty =
|
||||
GetStruct({reg_uint_ty, reg_uint_ty, reg_uint_rarr_ty});
|
||||
uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
|
||||
// By the Vulkan spec, a pre-existing struct containing a RuntimeArray
|
||||
// must be a block, and will therefore be decorated with Block. Therefore
|
||||
// the undecorated type returned here will not be pre-existing and can
|
||||
// safely be decorated. Since this type is now decorated, it is out of
|
||||
// sync with the TypeManager and therefore the TypeManager must be
|
||||
// invalidated after this pass.
|
||||
assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 &&
|
||||
"used struct type returned");
|
||||
deco_mgr->AddDecoration(obufTyId, uint32_t(spv::Decoration::Block));
|
||||
deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputFlagsOffset,
|
||||
uint32_t(spv::Decoration::Offset), 0);
|
||||
deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset,
|
||||
uint32_t(spv::Decoration::Offset), 4);
|
||||
deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset,
|
||||
uint32_t(spv::Decoration::Offset), 8);
|
||||
uint32_t obufTyPtrId_ =
|
||||
type_mgr->FindPointerToType(obufTyId, spv::StorageClass::StorageBuffer);
|
||||
output_buffer_id_ = TakeNextId();
|
||||
std::unique_ptr<Instruction> newVarOp(new Instruction(
|
||||
context(), spv::Op::OpVariable, obufTyPtrId_, output_buffer_id_,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{uint32_t(spv::StorageClass::StorageBuffer)}}}));
|
||||
context()->AddGlobalValue(std::move(newVarOp));
|
||||
context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer"));
|
||||
context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "flags"));
|
||||
context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "written_count"));
|
||||
context()->AddDebug2Inst(NewMemberName(obufTyId, 2, "data"));
|
||||
context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer"));
|
||||
deco_mgr->AddDecorationVal(
|
||||
output_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_);
|
||||
deco_mgr->AddDecorationVal(output_buffer_id_,
|
||||
uint32_t(spv::Decoration::Binding),
|
||||
GetOutputBufferBinding());
|
||||
AddStorageBufferExt();
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Add the new buffer to all entry points.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}});
|
||||
context()->AnalyzeUses(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output_buffer_id_;
|
||||
}
|
||||
|
||||
uint32_t InstDebugPrintfPass::GetOutputBufferPtrId() {
|
||||
if (output_buffer_ptr_id_ == 0) {
|
||||
output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
|
||||
GetUintId(), spv::StorageClass::StorageBuffer);
|
||||
}
|
||||
return output_buffer_ptr_id_;
|
||||
}
|
||||
|
||||
uint32_t InstDebugPrintfPass::GetOutputBufferBinding() {
|
||||
return kDebugOutputPrintfStream;
|
||||
}
|
||||
|
||||
void InstDebugPrintfPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
|
||||
uint32_t field_offset,
|
||||
uint32_t field_value_id,
|
||||
InstructionBuilder* builder) {
|
||||
// Cast value to 32-bit unsigned if necessary
|
||||
uint32_t val_id = GenUintCastCode(field_value_id, builder);
|
||||
// Store value
|
||||
Instruction* data_idx_inst = builder->AddIAdd(
|
||||
GetUintId(), base_offset_id, builder->GetUintConstantId(field_offset));
|
||||
uint32_t buf_id = GetOutputBufferId();
|
||||
uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
|
||||
Instruction* achain_inst = builder->AddAccessChain(
|
||||
buf_uint_ptr_id, buf_id,
|
||||
{builder->GetUintConstantId(kDebugOutputDataOffset),
|
||||
data_idx_inst->result_id()});
|
||||
(void)builder->AddStore(achain_inst->result_id(), val_id);
|
||||
}
|
||||
|
||||
uint32_t InstDebugPrintfPass::GetStreamWriteFunctionId(uint32_t param_cnt) {
|
||||
enum {
|
||||
kShaderId = 0,
|
||||
kInstructionIndex = 1,
|
||||
kStageInfo = 2,
|
||||
kFirstParam = 3,
|
||||
};
|
||||
// Total param count is common params plus validation-specific
|
||||
// params
|
||||
if (param2output_func_id_[param_cnt] == 0) {
|
||||
// Create function
|
||||
param2output_func_id_[param_cnt] = TakeNextId();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
|
||||
const analysis::Type* uint_type = GetInteger(32, false);
|
||||
const analysis::Vector v4uint(uint_type, 4);
|
||||
const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint);
|
||||
|
||||
std::vector<const analysis::Type*> param_types(kFirstParam + param_cnt,
|
||||
uint_type);
|
||||
param_types[kStageInfo] = v4uint_type;
|
||||
std::unique_ptr<Function> output_func = StartFunction(
|
||||
param2output_func_id_[param_cnt], type_mgr->GetVoidType(), param_types);
|
||||
|
||||
std::vector<uint32_t> param_ids = AddParameters(*output_func, param_types);
|
||||
|
||||
// Create first block
|
||||
auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId()));
|
||||
|
||||
InstructionBuilder builder(
|
||||
context(), &*new_blk_ptr,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
// Gen test if debug output buffer size will not be exceeded.
|
||||
const uint32_t val_spec_offset = kInstStageOutCnt;
|
||||
const uint32_t obuf_record_sz = val_spec_offset + param_cnt;
|
||||
const uint32_t buf_id = GetOutputBufferId();
|
||||
const uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
|
||||
Instruction* obuf_curr_sz_ac_inst = builder.AddAccessChain(
|
||||
buf_uint_ptr_id, buf_id,
|
||||
{builder.GetUintConstantId(kDebugOutputSizeOffset)});
|
||||
// Fetch the current debug buffer written size atomically, adding the
|
||||
// size of the record to be written.
|
||||
uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz);
|
||||
uint32_t mask_none_id =
|
||||
builder.GetUintConstantId(uint32_t(spv::MemoryAccessMask::MaskNone));
|
||||
uint32_t scope_invok_id =
|
||||
builder.GetUintConstantId(uint32_t(spv::Scope::Invocation));
|
||||
Instruction* obuf_curr_sz_inst = builder.AddQuadOp(
|
||||
GetUintId(), spv::Op::OpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(),
|
||||
scope_invok_id, mask_none_id, obuf_record_sz_id);
|
||||
uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id();
|
||||
// Compute new written size
|
||||
Instruction* obuf_new_sz_inst =
|
||||
builder.AddIAdd(GetUintId(), obuf_curr_sz_id,
|
||||
builder.GetUintConstantId(obuf_record_sz));
|
||||
// Fetch the data bound
|
||||
Instruction* obuf_bnd_inst =
|
||||
builder.AddIdLiteralOp(GetUintId(), spv::Op::OpArrayLength,
|
||||
GetOutputBufferId(), kDebugOutputDataOffset);
|
||||
// Test that new written size is less than or equal to debug output
|
||||
// data bound
|
||||
Instruction* obuf_safe_inst = builder.AddBinaryOp(
|
||||
GetBoolId(), spv::Op::OpULessThanEqual, obuf_new_sz_inst->result_id(),
|
||||
obuf_bnd_inst->result_id());
|
||||
uint32_t merge_blk_id = TakeNextId();
|
||||
uint32_t write_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
|
||||
std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id));
|
||||
(void)builder.AddConditionalBranch(
|
||||
obuf_safe_inst->result_id(), write_blk_id, merge_blk_id, merge_blk_id,
|
||||
uint32_t(spv::SelectionControlMask::MaskNone));
|
||||
// Close safety test block and gen write block
|
||||
output_func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
// Generate common and stage-specific debug record members
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutSize,
|
||||
builder.GetUintConstantId(obuf_record_sz),
|
||||
&builder);
|
||||
// Store Shader Id
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutShaderId,
|
||||
param_ids[kShaderId], &builder);
|
||||
// Store Instruction Idx
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutInstructionIdx,
|
||||
param_ids[kInstructionIndex], &builder);
|
||||
// Store stage info. Stage Idx + 3 words of stage-specific data.
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
Instruction* field =
|
||||
builder.AddCompositeExtract(GetUintId(), param_ids[kStageInfo], {i});
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutStageIdx + i,
|
||||
field->result_id(), &builder);
|
||||
}
|
||||
// Gen writes of validation specific data
|
||||
for (uint32_t i = 0; i < param_cnt; ++i) {
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i,
|
||||
param_ids[kFirstParam + i], &builder);
|
||||
}
|
||||
// Close write block and gen merge block
|
||||
(void)builder.AddBranch(merge_blk_id);
|
||||
output_func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
// Close merge block and function and add function to module
|
||||
(void)builder.AddNullaryOp(0, spv::Op::OpReturn);
|
||||
|
||||
output_func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
output_func->SetFunctionEnd(EndFunction());
|
||||
context()->AddFunction(std::move(output_func));
|
||||
|
||||
std::string name("stream_write_");
|
||||
name += std::to_string(param_cnt);
|
||||
|
||||
context()->AddDebug2Inst(
|
||||
NewGlobalName(param2output_func_id_[param_cnt], name));
|
||||
}
|
||||
return param2output_func_id_[param_cnt];
|
||||
}
|
||||
|
||||
void InstDebugPrintfPass::GenDebugStreamWrite(
|
||||
uint32_t shader_id, uint32_t instruction_idx_id, uint32_t stage_info_id,
|
||||
const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) {
|
||||
// Call debug output function. Pass func_idx, instruction_idx and
|
||||
// validation ids as args.
|
||||
uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size());
|
||||
std::vector<uint32_t> args = {shader_id, instruction_idx_id, stage_info_id};
|
||||
(void)args.insert(args.end(), validation_ids.begin(), validation_ids.end());
|
||||
(void)builder->AddFunctionCall(GetVoidId(),
|
||||
GetStreamWriteFunctionId(val_id_cnt), args);
|
||||
}
|
||||
|
||||
std::unique_ptr<Instruction> InstDebugPrintfPass::NewGlobalName(
|
||||
uint32_t id, const std::string& name_str) {
|
||||
std::string prefixed_name{"inst_printf_"};
|
||||
prefixed_name += name_str;
|
||||
return NewName(id, prefixed_name);
|
||||
}
|
||||
|
||||
std::unique_ptr<Instruction> InstDebugPrintfPass::NewMemberName(
|
||||
uint32_t id, uint32_t member_index, const std::string& name_str) {
|
||||
return MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpMemberName, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}});
|
||||
}
|
||||
|
||||
void InstDebugPrintfPass::InitializeInstDebugPrintf() {
|
||||
// Initialize base class
|
||||
InitializeInstrument();
|
||||
output_buffer_id_ = 0;
|
||||
output_buffer_ptr_id_ = 0;
|
||||
}
|
||||
|
||||
Pass::Status InstDebugPrintfPass::ProcessImpl() {
|
||||
|
@ -28,10 +28,10 @@ namespace opt {
|
||||
class InstDebugPrintfPass : public InstrumentPass {
|
||||
public:
|
||||
// For test harness only
|
||||
InstDebugPrintfPass() : InstrumentPass(7, 23, kInstValidationIdDebugPrintf) {}
|
||||
InstDebugPrintfPass() : InstrumentPass(7, 23) {}
|
||||
// For all other interfaces
|
||||
InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id)
|
||||
: InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf) {}
|
||||
: InstrumentPass(desc_set, shader_id) {}
|
||||
|
||||
~InstDebugPrintfPass() override = default;
|
||||
|
||||
@ -41,6 +41,104 @@ class InstDebugPrintfPass : public InstrumentPass {
|
||||
const char* name() const override { return "inst-printf-pass"; }
|
||||
|
||||
private:
|
||||
// Gen code into |builder| to write |field_value_id| into debug output
|
||||
// buffer at |base_offset_id| + |field_offset|.
|
||||
void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset,
|
||||
uint32_t field_value_id,
|
||||
InstructionBuilder* builder);
|
||||
|
||||
// Generate instructions in |builder| which will atomically fetch and
|
||||
// increment the size of the debug output buffer stream of the current
|
||||
// validation and write a record to the end of the stream, if enough space
|
||||
// in the buffer remains. The record will contain the index of the function
|
||||
// and instruction within that function |func_idx, instruction_idx| which
|
||||
// generated the record. It will also contain additional information to
|
||||
// identify the instance of the shader, depending on the stage |stage_idx|
|
||||
// of the shader. Finally, the record will contain validation-specific
|
||||
// data contained in |validation_ids| which will identify the validation
|
||||
// error as well as the values involved in the error.
|
||||
//
|
||||
// The output buffer binding written to by the code generated by the function
|
||||
// is determined by the validation id specified when each specific
|
||||
// instrumentation pass is created.
|
||||
//
|
||||
// The output buffer is a sequence of 32-bit values with the following
|
||||
// format (where all elements are unsigned 32-bit unless otherwise noted):
|
||||
//
|
||||
// Size
|
||||
// Record0
|
||||
// Record1
|
||||
// Record2
|
||||
// ...
|
||||
//
|
||||
// Size is the number of 32-bit values that have been written or
|
||||
// attempted to be written to the output buffer, excluding the Size. It is
|
||||
// initialized to 0. If the size of attempts to write the buffer exceeds
|
||||
// the actual size of the buffer, it is possible that this field can exceed
|
||||
// the actual size of the buffer.
|
||||
//
|
||||
// Each Record* is a variable-length sequence of 32-bit values with the
|
||||
// following format defined using static const offsets in the .cpp file:
|
||||
//
|
||||
// Record Size
|
||||
// Shader ID
|
||||
// Instruction Index
|
||||
// Stage
|
||||
// Stage-specific Word 0
|
||||
// Stage-specific Word 1
|
||||
// ...
|
||||
// Validation Error Code
|
||||
// Validation-specific Word 0
|
||||
// Validation-specific Word 1
|
||||
// Validation-specific Word 2
|
||||
// ...
|
||||
//
|
||||
// Each record consists of three subsections: members common across all
|
||||
// validation, members specific to the stage, and members specific to a
|
||||
// validation.
|
||||
//
|
||||
// The Record Size is the number of 32-bit words in the record, including
|
||||
// the Record Size word.
|
||||
//
|
||||
// Shader ID is a value that identifies which shader has generated the
|
||||
// validation error. It is passed when the instrumentation pass is created.
|
||||
//
|
||||
// The Instruction Index is the position of the instruction within the
|
||||
// SPIR-V file which is in error.
|
||||
//
|
||||
// The Stage is the pipeline stage which has generated the error as defined
|
||||
// by the SpvExecutionModel_ enumeration. This is used to interpret the
|
||||
// following Stage-specific words.
|
||||
//
|
||||
// The Stage-specific Words identify which invocation of the shader generated
|
||||
// the error. Every stage will write a fixed number of words. Vertex shaders
|
||||
// will write the Vertex and Instance ID. Fragment shaders will write
|
||||
// FragCoord.xy. Compute shaders will write the GlobalInvocation ID.
|
||||
// The tessellation eval shader will write the Primitive ID and TessCoords.uv.
|
||||
// The tessellation control shader and geometry shader will write the
|
||||
// Primitive ID and Invocation ID.
|
||||
//
|
||||
// The Validation Error Code specifies the exact error which has occurred.
|
||||
// These are enumerated with the kInstError* static consts. This allows
|
||||
// multiple validation layers to use the same, single output buffer.
|
||||
//
|
||||
// The Validation-specific Words are a validation-specific number of 32-bit
|
||||
// words which give further information on the validation error that
|
||||
// occurred. These are documented further in each file containing the
|
||||
// validation-specific class which derives from this base class.
|
||||
//
|
||||
// Because the code that is generated checks against the size of the buffer
|
||||
// before writing, the size of the debug out buffer can be used by the
|
||||
// validation layer to control the number of error records that are written.
|
||||
void GenDebugStreamWrite(uint32_t shader_id, uint32_t instruction_idx_id,
|
||||
uint32_t stage_info_id,
|
||||
const std::vector<uint32_t>& validation_ids,
|
||||
InstructionBuilder* builder);
|
||||
|
||||
// Return id for output function. Define if it doesn't exist with
|
||||
// |val_spec_param_cnt| validation-specific uint32 parameters.
|
||||
uint32_t GetStreamWriteFunctionId(uint32_t val_spec_param_cnt);
|
||||
|
||||
// Generate instructions for OpDebugPrintf.
|
||||
//
|
||||
// If |ref_inst_itr| is an OpDebugPrintf, return in |new_blocks| the result
|
||||
@ -80,13 +178,37 @@ class InstDebugPrintfPass : public InstrumentPass {
|
||||
void GenOutputCode(Instruction* printf_inst, uint32_t stage_idx,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
|
||||
|
||||
// Set the name for a function or global variable, names will be
|
||||
// prefixed to identify which instrumentation pass generated them.
|
||||
std::unique_ptr<Instruction> NewGlobalName(uint32_t id,
|
||||
const std::string& name_str);
|
||||
|
||||
// Set the name for a structure member
|
||||
std::unique_ptr<Instruction> NewMemberName(uint32_t id, uint32_t member_index,
|
||||
const std::string& name_str);
|
||||
|
||||
// Return id for debug output buffer
|
||||
uint32_t GetOutputBufferId();
|
||||
|
||||
// Return id for buffer uint type
|
||||
uint32_t GetOutputBufferPtrId();
|
||||
|
||||
// Return binding for output buffer for current validation.
|
||||
uint32_t GetOutputBufferBinding();
|
||||
|
||||
// Initialize state for instrumenting bindless checking
|
||||
void InitializeInstDebugPrintf();
|
||||
|
||||
// Apply GenDebugPrintfCode to every instruction in module.
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
uint32_t ext_inst_printf_id_;
|
||||
uint32_t ext_inst_printf_id_{0};
|
||||
|
||||
// id for output buffer variable
|
||||
uint32_t output_buffer_id_{0};
|
||||
|
||||
// ptr type id for output buffer element
|
||||
uint32_t output_buffer_ptr_id_{0};
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -131,38 +131,6 @@ std::unique_ptr<Instruction> InstrumentPass::NewName(
|
||||
{SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}});
|
||||
}
|
||||
|
||||
std::unique_ptr<Instruction> InstrumentPass::NewGlobalName(
|
||||
uint32_t id, const std::string& name_str) {
|
||||
std::string prefixed_name;
|
||||
switch (validation_id_) {
|
||||
case kInstValidationIdBindless:
|
||||
prefixed_name = "inst_bindless_";
|
||||
break;
|
||||
case kInstValidationIdBuffAddr:
|
||||
prefixed_name = "inst_buff_addr_";
|
||||
break;
|
||||
case kInstValidationIdDebugPrintf:
|
||||
prefixed_name = "inst_printf_";
|
||||
break;
|
||||
default:
|
||||
assert(false); // add new instrumentation pass here
|
||||
prefixed_name = "inst_pass_";
|
||||
break;
|
||||
}
|
||||
prefixed_name += name_str;
|
||||
return NewName(id, prefixed_name);
|
||||
}
|
||||
|
||||
std::unique_ptr<Instruction> InstrumentPass::NewMemberName(
|
||||
uint32_t id, uint32_t member_index, const std::string& name_str) {
|
||||
return MakeUnique<Instruction>(
|
||||
context(), spv::Op::OpMemberName, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}});
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::Gen32BitCvtCode(uint32_t val_id,
|
||||
InstructionBuilder* builder) {
|
||||
// Convert integer value to 32-bit if necessary
|
||||
@ -195,24 +163,6 @@ uint32_t InstrumentPass::GenUintCastCode(uint32_t val_id,
|
||||
->result_id();
|
||||
}
|
||||
|
||||
void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
|
||||
uint32_t field_offset,
|
||||
uint32_t field_value_id,
|
||||
InstructionBuilder* builder) {
|
||||
// Cast value to 32-bit unsigned if necessary
|
||||
uint32_t val_id = GenUintCastCode(field_value_id, builder);
|
||||
// Store value
|
||||
Instruction* data_idx_inst = builder->AddIAdd(
|
||||
GetUintId(), base_offset_id, builder->GetUintConstantId(field_offset));
|
||||
uint32_t buf_id = GetOutputBufferId();
|
||||
uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
|
||||
Instruction* achain_inst = builder->AddAccessChain(
|
||||
buf_uint_ptr_id, buf_id,
|
||||
{builder->GetUintConstantId(kDebugOutputDataOffset),
|
||||
data_idx_inst->result_id()});
|
||||
(void)builder->AddStore(achain_inst->result_id(), val_id);
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GenVarLoad(uint32_t var_id,
|
||||
InstructionBuilder* builder) {
|
||||
Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
|
||||
@ -329,18 +279,6 @@ uint32_t InstrumentPass::GenStageInfo(uint32_t stage_idx,
|
||||
return builder->AddCompositeConstruct(GetVec4UintId(), ids)->result_id();
|
||||
}
|
||||
|
||||
void InstrumentPass::GenDebugStreamWrite(
|
||||
uint32_t shader_id, uint32_t instruction_idx_id, uint32_t stage_info_id,
|
||||
const std::vector<uint32_t>& validation_ids, InstructionBuilder* builder) {
|
||||
// Call debug output function. Pass func_idx, instruction_idx and
|
||||
// validation ids as args.
|
||||
uint32_t val_id_cnt = static_cast<uint32_t>(validation_ids.size());
|
||||
std::vector<uint32_t> args = {shader_id, instruction_idx_id, stage_info_id};
|
||||
(void)args.insert(args.end(), validation_ids.begin(), validation_ids.end());
|
||||
(void)builder->AddFunctionCall(GetVoidId(),
|
||||
GetStreamWriteFunctionId(val_id_cnt), args);
|
||||
}
|
||||
|
||||
bool InstrumentPass::AllConstant(const std::vector<uint32_t>& ids) {
|
||||
for (auto& id : ids) {
|
||||
Instruction* id_inst = context()->get_def_use_mgr()->GetDef(id);
|
||||
@ -349,14 +287,6 @@ bool InstrumentPass::AllConstant(const std::vector<uint32_t>& ids) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GenDebugDirectRead(
|
||||
const std::vector<uint32_t>& offset_ids, InstructionBuilder* builder) {
|
||||
// Call debug input function. Pass func_idx and offset ids as args.
|
||||
const uint32_t off_id_cnt = static_cast<uint32_t>(offset_ids.size());
|
||||
const uint32_t input_func_id = GetDirectReadFunctionId(off_id_cnt);
|
||||
return GenReadFunctionCall(GetUintId(), input_func_id, offset_ids, builder);
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GenReadFunctionCall(
|
||||
uint32_t return_id, uint32_t func_id,
|
||||
const std::vector<uint32_t>& func_call_args,
|
||||
@ -450,53 +380,6 @@ void InstrumentPass::UpdateSucceedingPhis(
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetOutputBufferPtrId() {
|
||||
if (output_buffer_ptr_id_ == 0) {
|
||||
output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
|
||||
GetUintId(), spv::StorageClass::StorageBuffer);
|
||||
}
|
||||
return output_buffer_ptr_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetInputBufferTypeId() {
|
||||
return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id()
|
||||
: GetUintId();
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetInputBufferPtrId() {
|
||||
if (input_buffer_ptr_id_ == 0) {
|
||||
input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
|
||||
GetInputBufferTypeId(), spv::StorageClass::StorageBuffer);
|
||||
}
|
||||
return input_buffer_ptr_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetOutputBufferBinding() {
|
||||
switch (validation_id_) {
|
||||
case kInstValidationIdBindless:
|
||||
return kDebugOutputBindingStream;
|
||||
case kInstValidationIdBuffAddr:
|
||||
return kDebugOutputBindingStream;
|
||||
case kInstValidationIdDebugPrintf:
|
||||
return kDebugOutputPrintfStream;
|
||||
default:
|
||||
assert(false && "unexpected validation id");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetInputBufferBinding() {
|
||||
switch (validation_id_) {
|
||||
case kInstValidationIdBindless:
|
||||
return kDebugInputBindingBindless;
|
||||
case kInstValidationIdBuffAddr:
|
||||
return kDebugInputBindingBuffAddr;
|
||||
default:
|
||||
assert(false && "unexpected validation id");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
analysis::Integer* InstrumentPass::GetInteger(uint32_t width, bool is_signed) {
|
||||
analysis::Integer i(width, is_signed);
|
||||
analysis::Type* type = context()->get_type_mgr()->GetRegisteredType(&i);
|
||||
@ -577,110 +460,6 @@ void InstrumentPass::AddStorageBufferExt() {
|
||||
storage_buffer_ext_defined_ = true;
|
||||
}
|
||||
|
||||
// Return id for output buffer
|
||||
uint32_t InstrumentPass::GetOutputBufferId() {
|
||||
if (output_buffer_id_ == 0) {
|
||||
// If not created yet, create one
|
||||
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::RuntimeArray* reg_uint_rarr_ty = GetUintRuntimeArrayType(32);
|
||||
analysis::Integer* reg_uint_ty = GetInteger(32, false);
|
||||
analysis::Type* reg_buf_ty =
|
||||
GetStruct({reg_uint_ty, reg_uint_ty, reg_uint_rarr_ty});
|
||||
uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
|
||||
// By the Vulkan spec, a pre-existing struct containing a RuntimeArray
|
||||
// must be a block, and will therefore be decorated with Block. Therefore
|
||||
// the undecorated type returned here will not be pre-existing and can
|
||||
// safely be decorated. Since this type is now decorated, it is out of
|
||||
// sync with the TypeManager and therefore the TypeManager must be
|
||||
// invalidated after this pass.
|
||||
assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 &&
|
||||
"used struct type returned");
|
||||
deco_mgr->AddDecoration(obufTyId, uint32_t(spv::Decoration::Block));
|
||||
deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputFlagsOffset,
|
||||
uint32_t(spv::Decoration::Offset), 0);
|
||||
deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset,
|
||||
uint32_t(spv::Decoration::Offset), 4);
|
||||
deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset,
|
||||
uint32_t(spv::Decoration::Offset), 8);
|
||||
uint32_t obufTyPtrId_ =
|
||||
type_mgr->FindPointerToType(obufTyId, spv::StorageClass::StorageBuffer);
|
||||
output_buffer_id_ = TakeNextId();
|
||||
std::unique_ptr<Instruction> newVarOp(new Instruction(
|
||||
context(), spv::Op::OpVariable, obufTyPtrId_, output_buffer_id_,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{uint32_t(spv::StorageClass::StorageBuffer)}}}));
|
||||
context()->AddGlobalValue(std::move(newVarOp));
|
||||
context()->AddDebug2Inst(NewGlobalName(obufTyId, "OutputBuffer"));
|
||||
context()->AddDebug2Inst(NewMemberName(obufTyId, 0, "flags"));
|
||||
context()->AddDebug2Inst(NewMemberName(obufTyId, 1, "written_count"));
|
||||
context()->AddDebug2Inst(NewMemberName(obufTyId, 2, "data"));
|
||||
context()->AddDebug2Inst(NewGlobalName(output_buffer_id_, "output_buffer"));
|
||||
deco_mgr->AddDecorationVal(
|
||||
output_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_);
|
||||
deco_mgr->AddDecorationVal(output_buffer_id_,
|
||||
uint32_t(spv::Decoration::Binding),
|
||||
GetOutputBufferBinding());
|
||||
AddStorageBufferExt();
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Add the new buffer to all entry points.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}});
|
||||
context()->AnalyzeUses(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output_buffer_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetInputBufferId() {
|
||||
if (input_buffer_id_ == 0) {
|
||||
// If not created yet, create one
|
||||
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u;
|
||||
analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width);
|
||||
analysis::Struct* reg_buf_ty = GetStruct({reg_uint_rarr_ty});
|
||||
uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
|
||||
// By the Vulkan spec, a pre-existing struct containing a RuntimeArray
|
||||
// must be a block, and will therefore be decorated with Block. Therefore
|
||||
// the undecorated type returned here will not be pre-existing and can
|
||||
// safely be decorated. Since this type is now decorated, it is out of
|
||||
// sync with the TypeManager and therefore the TypeManager must be
|
||||
// invalidated after this pass.
|
||||
assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 &&
|
||||
"used struct type returned");
|
||||
deco_mgr->AddDecoration(ibufTyId, uint32_t(spv::Decoration::Block));
|
||||
deco_mgr->AddMemberDecoration(ibufTyId, 0,
|
||||
uint32_t(spv::Decoration::Offset), 0);
|
||||
uint32_t ibufTyPtrId_ =
|
||||
type_mgr->FindPointerToType(ibufTyId, spv::StorageClass::StorageBuffer);
|
||||
input_buffer_id_ = TakeNextId();
|
||||
std::unique_ptr<Instruction> newVarOp(new Instruction(
|
||||
context(), spv::Op::OpVariable, ibufTyPtrId_, input_buffer_id_,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{uint32_t(spv::StorageClass::StorageBuffer)}}}));
|
||||
context()->AddGlobalValue(std::move(newVarOp));
|
||||
context()->AddDebug2Inst(NewGlobalName(ibufTyId, "InputBuffer"));
|
||||
context()->AddDebug2Inst(NewMemberName(ibufTyId, 0, "data"));
|
||||
context()->AddDebug2Inst(NewGlobalName(input_buffer_id_, "input_buffer"));
|
||||
deco_mgr->AddDecorationVal(
|
||||
input_buffer_id_, uint32_t(spv::Decoration::DescriptorSet), desc_set_);
|
||||
deco_mgr->AddDecorationVal(input_buffer_id_,
|
||||
uint32_t(spv::Decoration::Binding),
|
||||
GetInputBufferBinding());
|
||||
AddStorageBufferExt();
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Add the new buffer to all entry points.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}});
|
||||
context()->AnalyzeUses(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return input_buffer_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetFloatId() {
|
||||
if (float_id_ == 0) {
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
@ -773,181 +552,6 @@ uint32_t InstrumentPass::GetVoidId() {
|
||||
return void_id_;
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t param_cnt) {
|
||||
enum {
|
||||
kShaderId = 0,
|
||||
kInstructionIndex = 1,
|
||||
kStageInfo = 2,
|
||||
kFirstParam = 3,
|
||||
};
|
||||
// Total param count is common params plus validation-specific
|
||||
// params
|
||||
if (param2output_func_id_[param_cnt] == 0) {
|
||||
// Create function
|
||||
param2output_func_id_[param_cnt] = TakeNextId();
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
|
||||
const analysis::Type* uint_type = GetInteger(32, false);
|
||||
const analysis::Vector v4uint(uint_type, 4);
|
||||
const analysis::Type* v4uint_type = type_mgr->GetRegisteredType(&v4uint);
|
||||
|
||||
std::vector<const analysis::Type*> param_types(kFirstParam + param_cnt,
|
||||
uint_type);
|
||||
param_types[kStageInfo] = v4uint_type;
|
||||
std::unique_ptr<Function> output_func = StartFunction(
|
||||
param2output_func_id_[param_cnt], type_mgr->GetVoidType(), param_types);
|
||||
|
||||
std::vector<uint32_t> param_ids = AddParameters(*output_func, param_types);
|
||||
|
||||
// Create first block
|
||||
auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId()));
|
||||
|
||||
InstructionBuilder builder(
|
||||
context(), &*new_blk_ptr,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
// Gen test if debug output buffer size will not be exceeded.
|
||||
const uint32_t val_spec_offset = kInstStageOutCnt;
|
||||
const uint32_t obuf_record_sz = val_spec_offset + param_cnt;
|
||||
const uint32_t buf_id = GetOutputBufferId();
|
||||
const uint32_t buf_uint_ptr_id = GetOutputBufferPtrId();
|
||||
Instruction* obuf_curr_sz_ac_inst = builder.AddAccessChain(
|
||||
buf_uint_ptr_id, buf_id,
|
||||
{builder.GetUintConstantId(kDebugOutputSizeOffset)});
|
||||
// Fetch the current debug buffer written size atomically, adding the
|
||||
// size of the record to be written.
|
||||
uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz);
|
||||
uint32_t mask_none_id =
|
||||
builder.GetUintConstantId(uint32_t(spv::MemoryAccessMask::MaskNone));
|
||||
uint32_t scope_invok_id =
|
||||
builder.GetUintConstantId(uint32_t(spv::Scope::Invocation));
|
||||
Instruction* obuf_curr_sz_inst = builder.AddQuadOp(
|
||||
GetUintId(), spv::Op::OpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(),
|
||||
scope_invok_id, mask_none_id, obuf_record_sz_id);
|
||||
uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id();
|
||||
// Compute new written size
|
||||
Instruction* obuf_new_sz_inst =
|
||||
builder.AddIAdd(GetUintId(), obuf_curr_sz_id,
|
||||
builder.GetUintConstantId(obuf_record_sz));
|
||||
// Fetch the data bound
|
||||
Instruction* obuf_bnd_inst =
|
||||
builder.AddIdLiteralOp(GetUintId(), spv::Op::OpArrayLength,
|
||||
GetOutputBufferId(), kDebugOutputDataOffset);
|
||||
// Test that new written size is less than or equal to debug output
|
||||
// data bound
|
||||
Instruction* obuf_safe_inst = builder.AddBinaryOp(
|
||||
GetBoolId(), spv::Op::OpULessThanEqual, obuf_new_sz_inst->result_id(),
|
||||
obuf_bnd_inst->result_id());
|
||||
uint32_t merge_blk_id = TakeNextId();
|
||||
uint32_t write_blk_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
|
||||
std::unique_ptr<Instruction> write_label(NewLabel(write_blk_id));
|
||||
(void)builder.AddConditionalBranch(
|
||||
obuf_safe_inst->result_id(), write_blk_id, merge_blk_id, merge_blk_id,
|
||||
uint32_t(spv::SelectionControlMask::MaskNone));
|
||||
// Close safety test block and gen write block
|
||||
output_func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(write_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
// Generate common and stage-specific debug record members
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutSize,
|
||||
builder.GetUintConstantId(obuf_record_sz),
|
||||
&builder);
|
||||
// Store Shader Id
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutShaderId,
|
||||
param_ids[kShaderId], &builder);
|
||||
// Store Instruction Idx
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutInstructionIdx,
|
||||
param_ids[kInstructionIndex], &builder);
|
||||
// Store stage info. Stage Idx + 3 words of stage-specific data.
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
Instruction* field =
|
||||
builder.AddCompositeExtract(GetUintId(), param_ids[kStageInfo], {i});
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, kInstCommonOutStageIdx + i,
|
||||
field->result_id(), &builder);
|
||||
}
|
||||
// Gen writes of validation specific data
|
||||
for (uint32_t i = 0; i < param_cnt; ++i) {
|
||||
GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i,
|
||||
param_ids[kFirstParam + i], &builder);
|
||||
}
|
||||
// Close write block and gen merge block
|
||||
(void)builder.AddBranch(merge_blk_id);
|
||||
output_func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
new_blk_ptr = MakeUnique<BasicBlock>(std::move(merge_label));
|
||||
builder.SetInsertPoint(&*new_blk_ptr);
|
||||
// Close merge block and function and add function to module
|
||||
(void)builder.AddNullaryOp(0, spv::Op::OpReturn);
|
||||
|
||||
output_func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
output_func->SetFunctionEnd(EndFunction());
|
||||
context()->AddFunction(std::move(output_func));
|
||||
|
||||
std::string name("stream_write_");
|
||||
name += std::to_string(param_cnt);
|
||||
|
||||
context()->AddDebug2Inst(
|
||||
NewGlobalName(param2output_func_id_[param_cnt], name));
|
||||
}
|
||||
return param2output_func_id_[param_cnt];
|
||||
}
|
||||
|
||||
uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
|
||||
uint32_t func_id = param2input_func_id_[param_cnt];
|
||||
if (func_id != 0) return func_id;
|
||||
// Create input function for param_cnt.
|
||||
func_id = TakeNextId();
|
||||
analysis::Integer* uint_type = GetInteger(32, false);
|
||||
std::vector<const analysis::Type*> param_types(param_cnt, uint_type);
|
||||
|
||||
std::unique_ptr<Function> input_func =
|
||||
StartFunction(func_id, uint_type, param_types);
|
||||
std::vector<uint32_t> param_ids = AddParameters(*input_func, param_types);
|
||||
|
||||
// Create block
|
||||
auto new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(TakeNextId()));
|
||||
InstructionBuilder builder(
|
||||
context(), &*new_blk_ptr,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
// For each offset parameter, generate new offset with parameter, adding last
|
||||
// loaded value if it exists, and load value from input buffer at new offset.
|
||||
// Return last loaded value.
|
||||
uint32_t ibuf_type_id = GetInputBufferTypeId();
|
||||
uint32_t buf_id = GetInputBufferId();
|
||||
uint32_t buf_ptr_id = GetInputBufferPtrId();
|
||||
uint32_t last_value_id = 0;
|
||||
for (uint32_t p = 0; p < param_cnt; ++p) {
|
||||
uint32_t offset_id;
|
||||
if (p == 0) {
|
||||
offset_id = param_ids[0];
|
||||
} else {
|
||||
if (ibuf_type_id != GetUintId()) {
|
||||
last_value_id =
|
||||
builder.AddUnaryOp(GetUintId(), spv::Op::OpUConvert, last_value_id)
|
||||
->result_id();
|
||||
}
|
||||
offset_id = builder.AddIAdd(GetUintId(), last_value_id, param_ids[p])
|
||||
->result_id();
|
||||
}
|
||||
Instruction* ac_inst = builder.AddAccessChain(
|
||||
buf_ptr_id, buf_id,
|
||||
{builder.GetUintConstantId(kDebugInputDataOffset), offset_id});
|
||||
last_value_id =
|
||||
builder.AddLoad(ibuf_type_id, ac_inst->result_id())->result_id();
|
||||
}
|
||||
(void)builder.AddUnaryOp(0, spv::Op::OpReturnValue, last_value_id);
|
||||
// Close block and function and add function to module
|
||||
input_func->AddBasicBlock(std::move(new_blk_ptr));
|
||||
input_func->SetFunctionEnd(EndFunction());
|
||||
context()->AddFunction(std::move(input_func));
|
||||
|
||||
std::string name("direct_read_");
|
||||
name += std::to_string(param_cnt);
|
||||
context()->AddDebug2Inst(NewGlobalName(func_id, name));
|
||||
|
||||
param2input_func_id_[param_cnt] = func_id;
|
||||
return func_id;
|
||||
}
|
||||
|
||||
void InstrumentPass::SplitBlock(
|
||||
BasicBlock::iterator inst_itr, UptrVectorIterator<BasicBlock> block_itr,
|
||||
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
||||
@ -1091,10 +695,6 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
|
||||
}
|
||||
|
||||
void InstrumentPass::InitializeInstrument() {
|
||||
output_buffer_id_ = 0;
|
||||
output_buffer_ptr_id_ = 0;
|
||||
input_buffer_ptr_id_ = 0;
|
||||
input_buffer_id_ = 0;
|
||||
float_id_ = 0;
|
||||
v4float_id_ = 0;
|
||||
uint_id_ = 0;
|
||||
|
@ -55,14 +55,6 @@
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
// Validation Ids
|
||||
// These are used to identify the general validation being done and map to
|
||||
// its output buffers.
|
||||
constexpr uint32_t kInstValidationIdBindless = 0;
|
||||
constexpr uint32_t kInstValidationIdBuffAddr = 1;
|
||||
constexpr uint32_t kInstValidationIdDebugPrintf = 2;
|
||||
} // namespace
|
||||
|
||||
class InstrumentPass : public Pass {
|
||||
using cbb_ptr = const BasicBlock*;
|
||||
@ -85,12 +77,11 @@ class InstrumentPass : public Pass {
|
||||
// set |desc_set| for debug input and output buffers and writes |shader_id|
|
||||
// into debug output records. |opt_direct_reads| indicates that the pass
|
||||
// will see direct input buffer reads and should prepare to optimize them.
|
||||
InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id,
|
||||
InstrumentPass(uint32_t desc_set, uint32_t shader_id,
|
||||
bool opt_direct_reads = false)
|
||||
: Pass(),
|
||||
desc_set_(desc_set),
|
||||
shader_id_(shader_id),
|
||||
validation_id_(validation_id),
|
||||
opt_direct_reads_(opt_direct_reads) {}
|
||||
|
||||
// Initialize state for instrumentation of module.
|
||||
@ -113,108 +104,9 @@ class InstrumentPass : public Pass {
|
||||
void MovePostludeCode(UptrVectorIterator<BasicBlock> ref_block_itr,
|
||||
BasicBlock* new_blk_ptr);
|
||||
|
||||
// Generate instructions in |builder| which will atomically fetch and
|
||||
// increment the size of the debug output buffer stream of the current
|
||||
// validation and write a record to the end of the stream, if enough space
|
||||
// in the buffer remains. The record will contain the index of the function
|
||||
// and instruction within that function |func_idx, instruction_idx| which
|
||||
// generated the record. It will also contain additional information to
|
||||
// identify the instance of the shader, depending on the stage |stage_idx|
|
||||
// of the shader. Finally, the record will contain validation-specific
|
||||
// data contained in |validation_ids| which will identify the validation
|
||||
// error as well as the values involved in the error.
|
||||
//
|
||||
// The output buffer binding written to by the code generated by the function
|
||||
// is determined by the validation id specified when each specific
|
||||
// instrumentation pass is created.
|
||||
//
|
||||
// The output buffer is a sequence of 32-bit values with the following
|
||||
// format (where all elements are unsigned 32-bit unless otherwise noted):
|
||||
//
|
||||
// Size
|
||||
// Record0
|
||||
// Record1
|
||||
// Record2
|
||||
// ...
|
||||
//
|
||||
// Size is the number of 32-bit values that have been written or
|
||||
// attempted to be written to the output buffer, excluding the Size. It is
|
||||
// initialized to 0. If the size of attempts to write the buffer exceeds
|
||||
// the actual size of the buffer, it is possible that this field can exceed
|
||||
// the actual size of the buffer.
|
||||
//
|
||||
// Each Record* is a variable-length sequence of 32-bit values with the
|
||||
// following format defined using static const offsets in the .cpp file:
|
||||
//
|
||||
// Record Size
|
||||
// Shader ID
|
||||
// Instruction Index
|
||||
// Stage
|
||||
// Stage-specific Word 0
|
||||
// Stage-specific Word 1
|
||||
// ...
|
||||
// Validation Error Code
|
||||
// Validation-specific Word 0
|
||||
// Validation-specific Word 1
|
||||
// Validation-specific Word 2
|
||||
// ...
|
||||
//
|
||||
// Each record consists of three subsections: members common across all
|
||||
// validation, members specific to the stage, and members specific to a
|
||||
// validation.
|
||||
//
|
||||
// The Record Size is the number of 32-bit words in the record, including
|
||||
// the Record Size word.
|
||||
//
|
||||
// Shader ID is a value that identifies which shader has generated the
|
||||
// validation error. It is passed when the instrumentation pass is created.
|
||||
//
|
||||
// The Instruction Index is the position of the instruction within the
|
||||
// SPIR-V file which is in error.
|
||||
//
|
||||
// The Stage is the pipeline stage which has generated the error as defined
|
||||
// by the SpvExecutionModel_ enumeration. This is used to interpret the
|
||||
// following Stage-specific words.
|
||||
//
|
||||
// The Stage-specific Words identify which invocation of the shader generated
|
||||
// the error. Every stage will write a fixed number of words. Vertex shaders
|
||||
// will write the Vertex and Instance ID. Fragment shaders will write
|
||||
// FragCoord.xy. Compute shaders will write the GlobalInvocation ID.
|
||||
// The tessellation eval shader will write the Primitive ID and TessCoords.uv.
|
||||
// The tessellation control shader and geometry shader will write the
|
||||
// Primitive ID and Invocation ID.
|
||||
//
|
||||
// The Validation Error Code specifies the exact error which has occurred.
|
||||
// These are enumerated with the kInstError* static consts. This allows
|
||||
// multiple validation layers to use the same, single output buffer.
|
||||
//
|
||||
// The Validation-specific Words are a validation-specific number of 32-bit
|
||||
// words which give further information on the validation error that
|
||||
// occurred. These are documented further in each file containing the
|
||||
// validation-specific class which derives from this base class.
|
||||
//
|
||||
// Because the code that is generated checks against the size of the buffer
|
||||
// before writing, the size of the debug out buffer can be used by the
|
||||
// validation layer to control the number of error records that are written.
|
||||
void GenDebugStreamWrite(uint32_t shader_id, uint32_t instruction_idx_id,
|
||||
uint32_t stage_info_id,
|
||||
const std::vector<uint32_t>& validation_ids,
|
||||
InstructionBuilder* builder);
|
||||
|
||||
// Return true if all instructions in |ids| are constants or spec constants.
|
||||
bool AllConstant(const std::vector<uint32_t>& ids);
|
||||
|
||||
// Generate in |builder| instructions to read the unsigned integer from the
|
||||
// input buffer specified by the offsets in |offset_ids|. Given offsets
|
||||
// o0, o1, ... oN, and input buffer ibuf, return the id for the value:
|
||||
//
|
||||
// ibuf[...ibuf[ibuf[o0]+o1]...+oN]
|
||||
//
|
||||
// The binding and the format of the input buffer is determined by each
|
||||
// specific validation, which is specified at the creation of the pass.
|
||||
uint32_t GenDebugDirectRead(const std::vector<uint32_t>& offset_ids,
|
||||
InstructionBuilder* builder);
|
||||
|
||||
uint32_t GenReadFunctionCall(uint32_t return_id, uint32_t func_id,
|
||||
const std::vector<uint32_t>& args,
|
||||
InstructionBuilder* builder);
|
||||
@ -243,15 +135,6 @@ class InstrumentPass : public Pass {
|
||||
std::unique_ptr<Instruction> NewName(uint32_t id,
|
||||
const std::string& name_str);
|
||||
|
||||
// Set the name for a function or global variable, names will be
|
||||
// prefixed to identify which instrumentation pass generated them.
|
||||
std::unique_ptr<Instruction> NewGlobalName(uint32_t id,
|
||||
const std::string& name_str);
|
||||
|
||||
// Set the name for a structure member
|
||||
std::unique_ptr<Instruction> NewMemberName(uint32_t id, uint32_t member_index,
|
||||
const std::string& name_str);
|
||||
|
||||
// Return id for 32-bit unsigned type
|
||||
uint32_t GetUintId();
|
||||
|
||||
@ -283,30 +166,9 @@ class InstrumentPass : public Pass {
|
||||
// Return pointer to type for runtime array of uint
|
||||
analysis::RuntimeArray* GetUintRuntimeArrayType(uint32_t width);
|
||||
|
||||
// Return id for buffer uint type
|
||||
uint32_t GetOutputBufferPtrId();
|
||||
|
||||
// Return id for buffer uint type
|
||||
uint32_t GetInputBufferTypeId();
|
||||
|
||||
// Return id for buffer uint type
|
||||
uint32_t GetInputBufferPtrId();
|
||||
|
||||
// Return binding for output buffer for current validation.
|
||||
uint32_t GetOutputBufferBinding();
|
||||
|
||||
// Return binding for input buffer for current validation.
|
||||
uint32_t GetInputBufferBinding();
|
||||
|
||||
// Add storage buffer extension if needed
|
||||
void AddStorageBufferExt();
|
||||
|
||||
// Return id for debug output buffer
|
||||
uint32_t GetOutputBufferId();
|
||||
|
||||
// Return id for debug input buffer
|
||||
uint32_t GetInputBufferId();
|
||||
|
||||
// Return id for 32-bit float type
|
||||
uint32_t GetFloatId();
|
||||
|
||||
@ -322,14 +184,6 @@ class InstrumentPass : public Pass {
|
||||
// Return id for v3uint type
|
||||
uint32_t GetVec3UintId();
|
||||
|
||||
// Return id for output function. Define if it doesn't exist with
|
||||
// |val_spec_param_cnt| validation-specific uint32 parameters.
|
||||
uint32_t GetStreamWriteFunctionId(uint32_t val_spec_param_cnt);
|
||||
|
||||
// Return id for input function taking |param_cnt| uint32 parameters. Define
|
||||
// if it doesn't exist.
|
||||
uint32_t GetDirectReadFunctionId(uint32_t param_cnt);
|
||||
|
||||
// Split block |block_itr| into two new blocks where the second block
|
||||
// contains |inst_itr| and place in |new_blocks|.
|
||||
void SplitBlock(BasicBlock::iterator inst_itr,
|
||||
@ -349,12 +203,6 @@ class InstrumentPass : public Pass {
|
||||
std::queue<uint32_t>* roots,
|
||||
uint32_t stage_idx);
|
||||
|
||||
// Gen code into |builder| to write |field_value_id| into debug output
|
||||
// buffer at |base_offset_id| + |field_offset|.
|
||||
void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset,
|
||||
uint32_t field_value_id,
|
||||
InstructionBuilder* builder);
|
||||
|
||||
// Generate instructions into |builder| which will load |var_id| and return
|
||||
// its result id.
|
||||
uint32_t GenVarLoad(uint32_t var_id, InstructionBuilder* builder);
|
||||
@ -395,62 +243,47 @@ class InstrumentPass : public Pass {
|
||||
// Map from instruction's unique id to offset in original file.
|
||||
std::unordered_map<uint32_t, uint32_t> uid2offset_;
|
||||
|
||||
// result id for OpConstantFalse
|
||||
uint32_t validation_id_;
|
||||
|
||||
// id for output buffer variable
|
||||
uint32_t output_buffer_id_;
|
||||
|
||||
// ptr type id for output buffer element
|
||||
uint32_t output_buffer_ptr_id_;
|
||||
|
||||
// ptr type id for input buffer element
|
||||
uint32_t input_buffer_ptr_id_;
|
||||
|
||||
// id for debug output function
|
||||
std::unordered_map<uint32_t, uint32_t> param2output_func_id_;
|
||||
|
||||
// ids for debug input functions
|
||||
std::unordered_map<uint32_t, uint32_t> param2input_func_id_;
|
||||
|
||||
// id for input buffer variable
|
||||
uint32_t input_buffer_id_;
|
||||
|
||||
// id for 32-bit float type
|
||||
uint32_t float_id_;
|
||||
uint32_t float_id_{0};
|
||||
|
||||
// id for v4float type
|
||||
uint32_t v4float_id_;
|
||||
uint32_t v4float_id_{0};
|
||||
|
||||
// id for v4uint type
|
||||
uint32_t v4uint_id_;
|
||||
uint32_t v4uint_id_{0};
|
||||
|
||||
// id for v3uint type
|
||||
uint32_t v3uint_id_;
|
||||
uint32_t v3uint_id_{0};
|
||||
|
||||
// id for 32-bit unsigned type
|
||||
uint32_t uint_id_;
|
||||
uint32_t uint_id_{0};
|
||||
|
||||
// id for 64-bit unsigned type
|
||||
uint32_t uint64_id_;
|
||||
uint32_t uint64_id_{0};
|
||||
|
||||
// id for 8-bit unsigned type
|
||||
uint32_t uint8_id_;
|
||||
uint32_t uint8_id_{0};
|
||||
|
||||
// id for bool type
|
||||
uint32_t bool_id_;
|
||||
uint32_t bool_id_{0};
|
||||
|
||||
// id for void type
|
||||
uint32_t void_id_;
|
||||
uint32_t void_id_{0};
|
||||
|
||||
// boolean to remember storage buffer extension
|
||||
bool storage_buffer_ext_defined_;
|
||||
bool storage_buffer_ext_defined_{false};
|
||||
|
||||
// runtime array of uint type
|
||||
analysis::RuntimeArray* uint64_rarr_ty_;
|
||||
analysis::RuntimeArray* uint64_rarr_ty_{nullptr};
|
||||
|
||||
// runtime array of uint type
|
||||
analysis::RuntimeArray* uint32_rarr_ty_;
|
||||
analysis::RuntimeArray* uint32_rarr_ty_{nullptr};
|
||||
|
||||
// Pre-instrumentation same-block insts
|
||||
std::unordered_map<uint32_t, Instruction*> same_block_pre_;
|
||||
@ -475,11 +308,11 @@ class InstrumentPass : public Pass {
|
||||
std::unordered_map<std::vector<uint32_t>, uint32_t, vector_hash_> call2id_;
|
||||
|
||||
// Function currently being instrumented
|
||||
Function* curr_func_;
|
||||
Function* curr_func_{nullptr};
|
||||
|
||||
// Optimize direct debug input buffer reads. Specifically, move all such
|
||||
// reads with constant args to first block and reuse them.
|
||||
bool opt_direct_reads_;
|
||||
bool opt_direct_reads_{false};
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -252,6 +252,8 @@ class IRContext {
|
||||
inline void AddType(std::unique_ptr<Instruction>&& t);
|
||||
// Appends a constant, global variable, or OpUndef instruction to this module.
|
||||
inline void AddGlobalValue(std::unique_ptr<Instruction>&& v);
|
||||
// Prepends a function declaration to this module.
|
||||
inline void AddFunctionDeclaration(std::unique_ptr<Function>&& f);
|
||||
// Appends a function to this module.
|
||||
inline void AddFunction(std::unique_ptr<Function>&& f);
|
||||
|
||||
@ -1213,6 +1215,10 @@ void IRContext::AddGlobalValue(std::unique_ptr<Instruction>&& v) {
|
||||
module()->AddGlobalValue(std::move(v));
|
||||
}
|
||||
|
||||
void IRContext::AddFunctionDeclaration(std::unique_ptr<Function>&& f) {
|
||||
module()->AddFunctionDeclaration(std::move(f));
|
||||
}
|
||||
|
||||
void IRContext::AddFunction(std::unique_ptr<Function>&& f) {
|
||||
module()->AddFunction(std::move(f));
|
||||
}
|
||||
|
@ -120,6 +120,9 @@ class Module {
|
||||
// Appends a constant, global variable, or OpUndef instruction to this module.
|
||||
inline void AddGlobalValue(std::unique_ptr<Instruction> v);
|
||||
|
||||
// Prepends a function declaration to this module.
|
||||
inline void AddFunctionDeclaration(std::unique_ptr<Function> f);
|
||||
|
||||
// Appends a function to this module.
|
||||
inline void AddFunction(std::unique_ptr<Function> f);
|
||||
|
||||
@ -380,6 +383,11 @@ inline void Module::AddGlobalValue(std::unique_ptr<Instruction> v) {
|
||||
types_values_.push_back(std::move(v));
|
||||
}
|
||||
|
||||
inline void Module::AddFunctionDeclaration(std::unique_ptr<Function> f) {
|
||||
// function declarations must come before function definitions.
|
||||
functions_.emplace(functions_.begin(), std::move(f));
|
||||
}
|
||||
|
||||
inline void Module::AddFunction(std::unique_ptr<Function> f) {
|
||||
functions_.emplace_back(std::move(f));
|
||||
}
|
||||
|
@ -434,13 +434,13 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
||||
pass_name == "inst-desc-idx-check" ||
|
||||
pass_name == "inst-buff-oob-check") {
|
||||
// preserve legacy names
|
||||
RegisterPass(CreateInstBindlessCheckPass(7, 23));
|
||||
RegisterPass(CreateInstBindlessCheckPass(23));
|
||||
RegisterPass(CreateSimplificationPass());
|
||||
RegisterPass(CreateDeadBranchElimPass());
|
||||
RegisterPass(CreateBlockMergePass());
|
||||
RegisterPass(CreateAggressiveDCEPass(true));
|
||||
} else if (pass_name == "inst-buff-addr-check") {
|
||||
RegisterPass(CreateInstBuffAddrCheckPass(7, 23));
|
||||
RegisterPass(CreateInstBuffAddrCheckPass(23));
|
||||
RegisterPass(CreateAggressiveDCEPass(true));
|
||||
} else if (pass_name == "convert-relaxed-to-half") {
|
||||
RegisterPass(CreateConvertRelaxedToHalfPass());
|
||||
@ -980,10 +980,9 @@ Optimizer::PassToken CreateUpgradeMemoryModelPass() {
|
||||
MakeUnique<opt::UpgradeMemoryModel>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
|
||||
uint32_t shader_id) {
|
||||
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t shader_id) {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id));
|
||||
MakeUnique<opt::InstBindlessCheckPass>(shader_id));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
|
||||
@ -992,10 +991,9 @@ Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
|
||||
MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
|
||||
uint32_t shader_id) {
|
||||
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t shader_id) {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id));
|
||||
MakeUnique<opt::InstBuffAddrCheckPass>(shader_id));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -26,123 +26,14 @@ namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
static const std::string kOutputDecorations = R"(
|
||||
; CHECK: OpDecorate [[output_buffer_type:%inst_buff_addr_OutputBuffer]] Block
|
||||
; CHECK: OpMemberDecorate [[output_buffer_type]] 0 Offset 0
|
||||
; CHECK: OpMemberDecorate [[output_buffer_type]] 1 Offset 4
|
||||
; CHECK: OpDecorate [[output_buffer_var:%\w+]] DescriptorSet 7
|
||||
; CHECK: OpDecorate [[output_buffer_var]] Binding 0
|
||||
static const std::string kFuncName = "inst_buff_addr_search_and_test";
|
||||
static const std::string kImportDeco = R"(
|
||||
;CHECK: OpDecorate %)" + kFuncName + R"( LinkageAttributes ")" +
|
||||
kFuncName + R"(" Import
|
||||
)";
|
||||
|
||||
static const std::string kOutputGlobals = R"(
|
||||
; CHECK: [[output_buffer_type]] = OpTypeStruct %uint %uint %_runtimearr_uint
|
||||
; CHECK: [[output_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[output_buffer_type]]
|
||||
; CHECK: [[output_buffer_var]] = OpVariable [[output_ptr_type]] StorageBuffer
|
||||
)";
|
||||
|
||||
static const std::string kStreamWrite3 = R"(
|
||||
; CHECK: %inst_buff_addr_stream_write_3 = OpFunction %void None {{%\w+}}
|
||||
; CHECK: [[sw_shader_id:%\w+]] = OpFunctionParameter %uint
|
||||
; CHECK: [[sw_inst_idx:%\w+]] = OpFunctionParameter %uint
|
||||
; CHECK: [[sw_stage_info:%\w+]] = OpFunctionParameter %v4uint
|
||||
; CHECK: [[sw_param_1:%\w+]] = OpFunctionParameter %uint
|
||||
; CHECK: [[sw_param_2:%\w+]] = OpFunctionParameter %uint
|
||||
; CHECK: [[sw_param_3:%\w+]] = OpFunctionParameter %uint
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_1
|
||||
; CHECK: {{%\w+}} = OpAtomicIAdd %uint {{%\w+}} %uint_4 %uint_0 %uint_10
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_10
|
||||
; CHECK: {{%\w+}} = OpArrayLength %uint [[output_buffer_var]] 2
|
||||
; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpSelectionMerge {{%\w+}} None
|
||||
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_0
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} %uint_10
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} [[sw_shader_id]]
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_2
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} [[sw_inst_idx]]
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 0
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_3
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 1
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_4
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 2
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_5
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint [[sw_stage_info]] 3
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_6
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_7
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} [[sw_param_1]]
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_8
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} [[sw_param_2]]
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_9
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_uint [[output_buffer_var]] %uint_2 {{%\w+}}
|
||||
; CHECK: OpStore {{%\w+}} [[sw_param_3]]
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: OpReturn
|
||||
; CHECK: OpFunctionEnd
|
||||
)";
|
||||
|
||||
static const std::string kInputDecorations = R"(
|
||||
; CHECK: OpDecorate [[input_buffer_type:%inst_buff_addr_InputBuffer]] Block
|
||||
; CHECK: OpMemberDecorate [[input_buffer_type]] 0 Offset 0
|
||||
; CHECK: OpDecorate [[input_buffer_var:%\w+]] DescriptorSet 7
|
||||
; CHECK: OpDecorate [[input_buffer_var]] Binding 2
|
||||
)";
|
||||
|
||||
static const std::string kInputGlobals = R"(
|
||||
; CHECK: [[input_buffer_type]] = OpTypeStruct %_runtimearr_ulong
|
||||
; CHECK: [[input_ptr_type:%\w+]] = OpTypePointer StorageBuffer [[input_buffer_type]]
|
||||
; CHECK: [[input_buffer_var]] = OpVariable [[input_ptr_type]] StorageBuffer
|
||||
)";
|
||||
|
||||
static const std::string kSearchAndTest = R"(
|
||||
; CHECK: {{%\w+}} = OpFunction %bool None {{%\w+}}
|
||||
; CHECK: [[param_1:%\w+]] = OpFunctionParameter %ulong
|
||||
; CHECK: [[param_2:%\w+]] = OpFunctionParameter %uint
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpPhi %uint %uint_1 {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpLoopMerge {{%\w+}} {{%\w+}} None
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} %uint_1
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpUGreaterThan %bool {{%\w+}} [[param_1]]
|
||||
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpISub %ulong [[param_1]] {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpUConvert %ulong [[param_2]]
|
||||
; CHECK: {{%\w+}} = OpIAdd %ulong {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 %uint_0
|
||||
; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpISub %uint {{%\w+}} %uint_1
|
||||
; CHECK: {{%\w+}} = OpIAdd %uint {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpAccessChain %_ptr_StorageBuffer_ulong [[input_buffer_var]] %uint_0 {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLoad %ulong {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpULessThanEqual %bool {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpReturnValue {{%\w+}}
|
||||
; CHECK: OpFunctionEnd
|
||||
static const std::string kImportStub = R"(
|
||||
;CHECK: %)" + kFuncName + R"( = OpFunction %bool None {{%\w+}}
|
||||
;CHECK: OpFunctionEnd
|
||||
)";
|
||||
// clang-format on
|
||||
|
||||
@ -171,13 +62,13 @@ TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) {
|
||||
const std::string defs = R"(
|
||||
OpCapability Shader
|
||||
OpCapability PhysicalStorageBufferAddresses
|
||||
; CHECK: OpCapability Int64
|
||||
;CHECK: OpCapability Int64
|
||||
OpExtension "SPV_EXT_physical_storage_buffer"
|
||||
; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel PhysicalStorageBuffer64 GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
|
||||
;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpSource GLSL 450
|
||||
OpSourceExtension "GL_EXT_buffer_reference"
|
||||
@ -202,11 +93,8 @@ OpMemberDecorate %bufStruct 1 Offset 32
|
||||
OpDecorate %bufStruct Block
|
||||
OpDecorate %u_info DescriptorSet 0
|
||||
OpDecorate %u_info Binding 0
|
||||
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
|
||||
)" + kInputDecorations + R"(
|
||||
; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
||||
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
|
||||
)" + kOutputDecorations + R"(
|
||||
)" + kImportDeco + R"(
|
||||
;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
||||
)";
|
||||
|
||||
const std::string globals = R"(
|
||||
@ -227,17 +115,11 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_3239 = OpConstant %int 3239
|
||||
%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
|
||||
; CHECK: %ulong = OpTypeInt 64 0
|
||||
; CHECK: %bool = OpTypeBool
|
||||
; CHECK: %_runtimearr_ulong = OpTypeRuntimeArray %ulong
|
||||
)" + kInputGlobals + R"(
|
||||
; CHECK: %_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong
|
||||
; CHECK: %v3uint = OpTypeVector %uint 3
|
||||
; CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
|
||||
; CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
||||
; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
|
||||
)" + kOutputGlobals + R"(
|
||||
; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
|
||||
;CHECK: %ulong = OpTypeInt 64 0
|
||||
;CHECK: %bool = OpTypeBool
|
||||
;CHECK: %v3uint = OpTypeVector %uint 3
|
||||
;CHECK: %_ptr_Input_v3uint = OpTypePointer Input %v3uint
|
||||
;CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
||||
)";
|
||||
// clang-format off
|
||||
|
||||
@ -247,41 +129,35 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
|
||||
%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
|
||||
%18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
|
||||
%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
|
||||
; CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
|
||||
; CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
|
||||
; CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
|
||||
; CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
|
||||
; CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
|
||||
; CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
|
||||
; CHECK: {{%\w+}} = OpConvertPtrToU %ulong %22
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %bool %inst_buff_addr_search_and_test {{%\w+}} %uint_4
|
||||
; CHECK: OpSelectionMerge {{%\w+}} None
|
||||
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK-NOT: %17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
|
||||
;CHECK-NOT: %18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17
|
||||
;CHECK-NOT: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1
|
||||
;CHECK: %20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0
|
||||
;CHECK: %21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20
|
||||
;CHECK: %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1
|
||||
;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %22
|
||||
;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
|
||||
;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_49 {{%\w+}} {{%\w+}} %uint_4
|
||||
;CHECK: OpSelectionMerge {{%\w+}} None
|
||||
;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
OpStore %22 %int_3239 Aligned 16
|
||||
; CHECK: OpStore %22 %int_3239 Aligned 16
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
|
||||
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_48 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpStore %22 %int_3239 Aligned 16
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string output_funcs = kSearchAndTest + kStreamWrite3;
|
||||
|
||||
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<InstBuffAddrCheckPass>(
|
||||
defs + decorates + globals + main_func + output_funcs, true, 7u, 23u);
|
||||
defs + decorates + globals + kImportStub + main_func, true, 23u);
|
||||
}
|
||||
|
||||
TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
|
||||
@ -311,7 +187,7 @@ TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) {
|
||||
const std::string defs = R"(
|
||||
OpCapability Shader
|
||||
OpCapability PhysicalStorageBufferAddresses
|
||||
; CHECK: OpCapability Int64
|
||||
;CHECK: OpCapability Int64
|
||||
OpExtension "SPV_EXT_physical_storage_buffer"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
@ -321,7 +197,7 @@ OpExecutionMode %main LocalSize 1 1 1
|
||||
OpSource GLSL 450
|
||||
OpSourceExtension "GL_EXT_buffer_reference"
|
||||
OpName %main "main"
|
||||
; CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
|
||||
;CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
|
||||
OpName %blockType "blockType"
|
||||
OpMemberName %blockType 0 "x"
|
||||
OpMemberName %blockType 1 "next"
|
||||
@ -339,12 +215,9 @@ OpMemberDecorate %rootBlock 0 Offset 0
|
||||
OpDecorate %rootBlock Block
|
||||
OpDecorate %r DescriptorSet 0
|
||||
OpDecorate %r Binding 0
|
||||
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
|
||||
)" + kInputDecorations + R"(
|
||||
; CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
||||
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
|
||||
)" + kOutputDecorations;
|
||||
// clang-format on
|
||||
)" + kImportDeco + R"(
|
||||
;CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
||||
)";
|
||||
|
||||
const std::string globals = R"(
|
||||
%void = OpTypeVoid
|
||||
@ -362,7 +235,7 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
|
||||
%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType
|
||||
%int_531 = OpConstant %int 531
|
||||
%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
|
||||
)" + kInputGlobals + kOutputGlobals;
|
||||
)";
|
||||
|
||||
const std::string main_func = R"(
|
||||
%main = OpFunction %void None %3
|
||||
@ -373,58 +246,49 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer
|
||||
%22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
|
||||
%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
|
||||
OpStore %26 %int_531 Aligned 16
|
||||
; CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
|
||||
; CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
|
||||
; CHECK: %30 = OpConvertPtrToU %ulong %21
|
||||
; CHECK: %67 = OpFunctionCall %bool %inst_buff_addr_search_and_test %30 %uint_8
|
||||
; CHECK: OpSelectionMerge %68 None
|
||||
; CHECK: OpBranchConditional %67 %69 %70
|
||||
; CHECK: %69 = OpLabel
|
||||
; CHECK: %71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
|
||||
; CHECK: OpBranch %68
|
||||
; CHECK: %70 = OpLabel
|
||||
; CHECK: %72 = OpUConvert %uint %30
|
||||
; CHECK: %74 = OpShiftRightLogical %ulong %30 %uint_32
|
||||
; CHECK: %75 = OpUConvert %uint %74
|
||||
; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
|
||||
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_44 {{%\w+}} %uint_3 %72 %75
|
||||
; CHECK: {{%\w+}} = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType {{%\w+}}
|
||||
; CHECK: OpBranch %68
|
||||
; CHECK: %68 = OpLabel
|
||||
; CHECK: {{%\w+}} = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 {{%\w+}} %70
|
||||
; CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int {{%\w+}} %int_0
|
||||
; CHECK: {{%\w+}} = OpConvertPtrToU %ulong %26
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %bool %inst_buff_addr_search_and_test {{%\w+}} %uint_4
|
||||
; CHECK: OpSelectionMerge {{%\w+}} None
|
||||
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: OpStore %26 %int_531 Aligned 16
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
|
||||
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_46 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK-NOT: %22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
|
||||
;CHECK-NOT: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0
|
||||
;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %21
|
||||
;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
|
||||
;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_45 {{%\w+}} {{%\w+}} %uint_8
|
||||
;CHECK: OpSelectionMerge {{%\w+}} None
|
||||
;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: {{%\w+}} = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: {{%\w+}} = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %52
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: {{%\w+}} = OpPhi %_ptr_PhysicalStorageBuffer_blockType {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: %26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int {{%\w+}} %int_0
|
||||
;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %26
|
||||
;CHECK: {{%\w+}} = OpLoad %v3uint %gl_GlobalInvocationID
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 2
|
||||
;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_5 {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_47 {{%\w+}} {{%\w+}} %uint_4
|
||||
;CHECK: OpSelectionMerge {{%\w+}} None
|
||||
;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpStore %26 %int_531 Aligned 16
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string output_funcs = kSearchAndTest + kStreamWrite3;
|
||||
// clang-format on
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<InstBuffAddrCheckPass>(
|
||||
defs + decorates + globals + main_func + output_funcs, true, 7u, 23u);
|
||||
defs + decorates + globals + kImportStub + main_func, true, 23u);
|
||||
}
|
||||
|
||||
TEST_F(InstBuffAddrTest, StructLoad) {
|
||||
@ -451,11 +315,11 @@ TEST_F(InstBuffAddrTest, StructLoad) {
|
||||
OpCapability Shader
|
||||
OpCapability Int64
|
||||
OpCapability PhysicalStorageBufferAddresses
|
||||
; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel PhysicalStorageBuffer64 GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
; CHECK: OpEntryPoint Fragment %main "main" %inst_buff_addr_input_buffer %gl_FragCoord %inst_buff_addr_output_buffer
|
||||
;CHECK: OpEntryPoint Fragment %main "main" %gl_FragCoord
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 450
|
||||
OpSourceExtension "GL_ARB_gpu_shader_int64"
|
||||
@ -474,11 +338,9 @@ OpMemberName %TestBuffer 0 "test"
|
||||
OpMemberDecorate %Test_0 0 Offset 0
|
||||
OpMemberDecorate %TestBuffer 0 Offset 0
|
||||
OpDecorate %TestBuffer Block
|
||||
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
|
||||
)" + kInputDecorations + R"(
|
||||
; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
|
||||
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
|
||||
)" + kOutputDecorations;
|
||||
)" + kImportDeco + R"(
|
||||
;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
|
||||
)";
|
||||
|
||||
const std::string globals = R"(
|
||||
%void = OpTypeVoid
|
||||
@ -494,53 +356,44 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffe
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0
|
||||
%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704
|
||||
; CHECK: %47 = OpTypeFunction %bool %ulong %uint
|
||||
)" + kInputGlobals + kOutputGlobals + R"(
|
||||
; CHECK: {{%\w+}} = OpConstantNull %Test_0
|
||||
;CHECK: {{%\w+}} = OpConstantNull %Test_0
|
||||
)";
|
||||
// clang-format on
|
||||
|
||||
const std::string main_func =
|
||||
R"(
|
||||
const std::string main_func = R"(
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%37 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_TestBuffer %ulong_18446744073172680704
|
||||
%38 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %37 %int_0
|
||||
%39 = OpLoad %Test_0 %38 Aligned 16
|
||||
; CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16
|
||||
; CHECK: {{%\w+}} = OpConvertPtrToU %ulong %38
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %bool %inst_buff_addr_search_and_test {{%\w+}} %uint_4
|
||||
; CHECK: OpSelectionMerge {{%\w+}} None
|
||||
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpLoad %Test_0 %38 Aligned 16
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
|
||||
; CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
|
||||
; CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
|
||||
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_37 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK-NOT: %39 = OpLoad %Test_0 %38 Aligned 16
|
||||
;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %38
|
||||
;CHECK: {{%\w+}} = OpLoad %v4float %gl_FragCoord
|
||||
;CHECK: {{%\w+}} = OpBitcast %v4uint {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 0
|
||||
;CHECK: {{%\w+}} = OpCompositeExtract %uint {{%\w+}} 1
|
||||
;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_4 {{%\w+}} {{%\w+}} %uint_0
|
||||
;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_38 {{%\w+}} {{%\w+}} %uint_4
|
||||
;CHECK: OpSelectionMerge {{%\w+}} None
|
||||
;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: {{%\w+}} = OpLoad %Test_0 %38 Aligned 16
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
%40 = OpCopyLogical %Test %39
|
||||
; CHECK-NOT: %40 = OpCopyLogical %Test %39
|
||||
; CHECK: %40 = OpCopyLogical %Test [[phi_result]]
|
||||
;CHECK-NOT: %40 = OpCopyLogical %Test %39
|
||||
;CHECK: %40 = OpCopyLogical %Test [[phi_result]]
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
// clang-format on
|
||||
|
||||
const std::string output_funcs = kSearchAndTest + kStreamWrite3;
|
||||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_2);
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<InstBuffAddrCheckPass>(
|
||||
defs + decorates + globals + main_func + output_funcs, true);
|
||||
defs + decorates + globals + kImportStub + main_func, true);
|
||||
}
|
||||
|
||||
TEST_F(InstBuffAddrTest, PaddedStructLoad) {
|
||||
@ -599,12 +452,9 @@ OpMemberDecorate %Test_0 0 Offset 0
|
||||
OpMemberDecorate %Test_0 1 Offset 16
|
||||
OpMemberDecorate %Test_0 2 Offset 24
|
||||
OpMemberDecorate %TestBuffer 0 Offset 0
|
||||
; CHECK: OpDecorate %_runtimearr_ulong ArrayStride 8
|
||||
)" + kInputDecorations + R"(
|
||||
; CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
|
||||
; CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
|
||||
; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
|
||||
)" + kOutputDecorations + R"(
|
||||
)" + kImportDeco + R"(
|
||||
;CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
|
||||
;CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
|
||||
)";
|
||||
|
||||
const std::string globals = R"(
|
||||
@ -627,13 +477,10 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_TestBuffer PhysicalStorageBuffe
|
||||
%_ptr_PhysicalStorageBuffer_Test_0 = OpTypePointer PhysicalStorageBuffer %Test_0
|
||||
%_ptr_Function_Test = OpTypePointer Function %Test
|
||||
%ulong_18446744073172680704 = OpConstant %ulong 18446744073172680704
|
||||
)" + kInputGlobals + kOutputGlobals + R"(
|
||||
; CHECK: {{%\w+}} = OpConstantNull %Test_0
|
||||
;CHECK: {{%\w+}} = OpConstantNull %Test_0
|
||||
)";
|
||||
// clang-format on
|
||||
|
||||
const std::string main_func =
|
||||
R"(
|
||||
const std::string main_func = R"(
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%param = OpVariable %_ptr_Function_ulong Function
|
||||
@ -650,40 +497,35 @@ OpFunctionEnd
|
||||
%25 = OpAccessChain %_ptr_PhysicalStorageBuffer_Test_0 %21 %int_0
|
||||
%26 = OpLoad %Test_0 %25 Aligned 16
|
||||
%29 = OpCopyLogical %Test %26
|
||||
; CHECK-NOT: %30 = OpLoad %Test %28
|
||||
; CHECK-NOT: %26 = OpLoad %Test_0 %25 Aligned 16
|
||||
; CHECK-NOT: %29 = OpCopyLogical %Test %26
|
||||
; CHECK: {{%\w+}} = OpConvertPtrToU %ulong %25
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %bool %inst_buff_addr_search_and_test {{%\w+}} %uint_28
|
||||
; CHECK: OpSelectionMerge {{%\w+}} None
|
||||
; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpLoad %Test_0 %25 Aligned 16
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
|
||||
; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
|
||||
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_62 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: %29 = OpCopyLogical %Test [[phi_result]]
|
||||
;CHECK-NOT: %30 = OpLoad %Test %28
|
||||
;CHECK-NOT: %26 = OpLoad %Test_0 %25 Aligned 16
|
||||
;CHECK-NOT: %29 = OpCopyLogical %Test %26
|
||||
;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %25
|
||||
;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
|
||||
;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
|
||||
;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
|
||||
;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_63 {{%\w+}} {{%\w+}} %uint_28
|
||||
;CHECK: OpSelectionMerge {{%\w+}} None
|
||||
;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: {{%\w+}} = OpLoad %Test_0 %25 Aligned 16
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: [[phi_result:%\w+]] = OpPhi %Test_0 {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: %29 = OpCopyLogical %Test [[phi_result]]
|
||||
OpStore %28 %29
|
||||
%30 = OpLoad %Test %28
|
||||
OpReturnValue %30
|
||||
OpFunctionEnd
|
||||
)";
|
||||
// clang-format on
|
||||
|
||||
const std::string output_funcs = kSearchAndTest + kStreamWrite3;
|
||||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_2);
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<InstBuffAddrCheckPass>(
|
||||
defs + decorates + globals + main_func + output_funcs, true);
|
||||
defs + decorates + globals + kImportStub + main_func, true);
|
||||
}
|
||||
|
||||
TEST_F(InstBuffAddrTest, DeviceBufferAddressOOB) {
|
||||
@ -710,7 +552,7 @@ OpCapability PhysicalStorageBufferAddresses
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel PhysicalStorageBuffer64 GLSL450
|
||||
OpEntryPoint Vertex %main "main" %u_info
|
||||
;CHECK: OpEntryPoint Vertex %main "main" %u_info %inst_buff_addr_input_buffer %gl_VertexIndex %gl_InstanceIndex %inst_buff_addr_output_buffer
|
||||
;CHECK: OpEntryPoint Vertex %main "main" %u_info %gl_VertexIndex %gl_InstanceIndex
|
||||
OpSource GLSL 450
|
||||
OpSourceExtension "GL_EXT_buffer_reference"
|
||||
OpName %main "main"
|
||||
@ -729,7 +571,7 @@ OpMemberDecorate %bufStruct 0 Offset 0
|
||||
OpDecorate %bufStruct Block
|
||||
OpDecorate %u_info DescriptorSet 0
|
||||
OpDecorate %u_info Binding 0
|
||||
)" + kInputDecorations + kOutputDecorations + R"(
|
||||
)" + kImportDeco + R"(
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
@ -750,7 +592,7 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer
|
||||
%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct
|
||||
%int_n559035791 = OpConstant %int -559035791
|
||||
%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int
|
||||
)" + kInputGlobals + kOutputGlobals + R"(
|
||||
)" + kImportStub + R"(
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%i = OpVariable %_ptr_Function_int Function
|
||||
@ -770,21 +612,18 @@ OpBranchConditional %29 %11 %12
|
||||
%32 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %31
|
||||
%33 = OpLoad %int %i
|
||||
%36 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %32 %int_0 %33
|
||||
;CHECK: %41 = OpConvertPtrToU %ulong %36
|
||||
;CHECK: %76 = OpFunctionCall %bool %inst_buff_addr_search_and_test %41 %uint_4
|
||||
;CHECK: OpSelectionMerge %77 None
|
||||
;CHECK: OpBranchConditional %76 %78 %79
|
||||
;CHECK: %78 = OpLabel
|
||||
OpStore %36 %int_n559035791 Aligned 16
|
||||
;CHECK: OpBranch %77
|
||||
;CHECK: %79 = OpLabel
|
||||
;CHECK: %80 = OpUConvert %uint %41
|
||||
;CHECK: %82 = OpShiftRightLogical %ulong %41 %uint_32
|
||||
;CHECK: %83 = OpUConvert %uint %82
|
||||
;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %36
|
||||
;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
|
||||
;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
|
||||
;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
|
||||
;CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_62 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_63 {{%\w+}} {{%\w+}} %uint_4
|
||||
;CHECK: OpSelectionMerge {{%\w+}} None
|
||||
;CHECK: OpBranchConditional {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpStore %36 %int_n559035791 Aligned 16
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
OpBranch %13
|
||||
@ -795,12 +634,12 @@ OpStore %i %38
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd)" + kSearchAndTest + kStreamWrite3;
|
||||
OpFunctionEnd)";
|
||||
// clang-format on
|
||||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_2);
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 7, 23);
|
||||
SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 23);
|
||||
}
|
||||
|
||||
TEST_F(InstBuffAddrTest, UVec3ScalarAddressOOB) {
|
||||
@ -849,7 +688,7 @@ OpMemberDecorate %IndexBuffer 0 Offset 0
|
||||
OpDecorate %IndexBuffer Block
|
||||
OpDecorate %u_info DescriptorSet 0
|
||||
OpDecorate %u_info Binding 0
|
||||
)" + kInputDecorations + kOutputDecorations + R"(
|
||||
)" + kImportDeco + R"(
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
@ -867,7 +706,7 @@ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_IndexBuffer PhysicalStorageBuff
|
||||
%int_1 = OpConstant %int 1
|
||||
%_ptr_Uniform_int = OpTypePointer Uniform %int
|
||||
%bool = OpTypeBool
|
||||
)" + kInputGlobals + kOutputGlobals + R"(
|
||||
)" + kImportStub + R"(
|
||||
%_ptr_Function_v3uint = OpTypePointer Function %v3uint
|
||||
%_ptr_Uniform__ptr_PhysicalStorageBuffer_IndexBuffer = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_IndexBuffer
|
||||
%_ptr_PhysicalStorageBuffer_v3uint = OpTypePointer PhysicalStorageBuffer %v3uint
|
||||
@ -893,27 +732,23 @@ OpBranchConditional %29 %11 %12
|
||||
%37 = OpAccessChain %_ptr_PhysicalStorageBuffer_v3uint %34 %int_0 %35
|
||||
%38 = OpLoad %v3uint %37 Aligned 4
|
||||
OpStore %readvec %38
|
||||
; CHECK-NOT: %38 = OpLoad %v3uint %37 Aligned 4
|
||||
; CHECK-NOT: OpStore %readvec %38
|
||||
; CHECK: {{%\w+}} = OpConvertPtrToU %ulong %37
|
||||
; CHECK: [[test_result:%\w+]] = OpFunctionCall %bool %inst_buff_addr_search_and_test {{%\w+}} %uint_12
|
||||
; CHECK: OpSelectionMerge {{%\w+}} None
|
||||
; CHECK: OpBranchConditional [[test_result]] {{%\w+}} {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpLoad %v3uint %37 Aligned 4
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpShiftRightLogical %ulong {{%\w+}} %uint_32
|
||||
; CHECK: {{%\w+}} = OpUConvert %uint {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
|
||||
; CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
|
||||
; CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
|
||||
; CHECK: {{%\w+}} = OpFunctionCall %void %inst_buff_addr_stream_write_3 %uint_23 %uint_66 {{%\w+}} %uint_3 {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpBranch {{%\w+}}
|
||||
; CHECK: {{%\w+}} = OpLabel
|
||||
; CHECK: [[phi_result:%\w+]] = OpPhi %v3uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
; CHECK: OpStore %readvec [[phi_result]]
|
||||
;CHECK-NOT: %38 = OpLoad %v3uint %37 Aligned 4
|
||||
;CHECK-NOT: OpStore %readvec %38
|
||||
;CHECK: {{%\w+}} = OpConvertPtrToU %ulong %37
|
||||
;CHECK: {{%\w+}} = OpLoad %uint %gl_VertexIndex
|
||||
;CHECK: {{%\w+}} = OpLoad %uint %gl_InstanceIndex
|
||||
;CHECK: {{%\w+}} = OpCompositeConstruct %v4uint %uint_0 {{%\w+}} {{%\w+}} %uint_0
|
||||
;CHECK: [[test_result:%\w+]] = OpFunctionCall %bool %)" + kFuncName + R"( %uint_23 %uint_67 {{%\w+}} {{%\w+}} %uint_12
|
||||
;CHECK: OpSelectionMerge {{%\w+}} None
|
||||
;CHECK: OpBranchConditional [[test_result]] {{%\w+}} {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: {{%\w+}} = OpLoad %v3uint %37 Aligned 4
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: OpBranch {{%\w+}}
|
||||
;CHECK: {{%\w+}} = OpLabel
|
||||
;CHECK: [[phi_result:%\w+]] = OpPhi %v3uint {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
|
||||
;CHECK: OpStore %readvec [[phi_result]]
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%39 = OpLoad %int %i
|
||||
@ -923,13 +758,13 @@ OpBranch %10
|
||||
%12 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)" + kSearchAndTest + kStreamWrite3;
|
||||
)";
|
||||
// clang-format on
|
||||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_2);
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
ValidatorOptions()->scalar_block_layout = true;
|
||||
SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 7, 23);
|
||||
SinglePassRunAndMatch<InstBuffAddrCheckPass>(text, true, 23);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
Loading…
Reference in New Issue
Block a user