2019-08-16 13:18:34 +00:00
|
|
|
// Copyright (c) 2019 The Khronos Group Inc.
|
|
|
|
// Copyright (c) 2019 Valve Corporation
|
|
|
|
// Copyright (c) 2019 LunarG Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
#include "inst_buff_addr_check_pass.h"
|
|
|
|
|
|
|
|
namespace spvtools {
|
|
|
|
namespace opt {
|
|
|
|
|
|
|
|
uint32_t InstBuffAddrCheckPass::CloneOriginalReference(
|
|
|
|
Instruction* ref_inst, InstructionBuilder* builder) {
|
|
|
|
// Clone original ref with new result id (if load)
|
2022-11-04 21:27:10 +00:00
|
|
|
assert((ref_inst->opcode() == spv::Op::OpLoad ||
|
|
|
|
ref_inst->opcode() == spv::Op::OpStore) &&
|
|
|
|
"unexpected ref");
|
2019-08-16 13:18:34 +00:00
|
|
|
std::unique_ptr<Instruction> new_ref_inst(ref_inst->Clone(context()));
|
|
|
|
uint32_t ref_result_id = ref_inst->result_id();
|
|
|
|
uint32_t new_ref_id = 0;
|
|
|
|
if (ref_result_id != 0) {
|
|
|
|
new_ref_id = TakeNextId();
|
|
|
|
new_ref_inst->SetResultId(new_ref_id);
|
|
|
|
}
|
|
|
|
// Register new reference and add to new block
|
|
|
|
Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst));
|
|
|
|
uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()];
|
|
|
|
if (new_ref_id != 0)
|
|
|
|
get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id);
|
|
|
|
return new_ref_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) {
|
2022-11-04 21:27:10 +00:00
|
|
|
if (ref_inst->opcode() != spv::Op::OpLoad &&
|
|
|
|
ref_inst->opcode() != spv::Op::OpStore)
|
2019-08-16 13:18:34 +00:00
|
|
|
return false;
|
|
|
|
uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0);
|
|
|
|
analysis::DefUseManager* du_mgr = get_def_use_mgr();
|
|
|
|
Instruction* ptr_inst = du_mgr->GetDef(ptr_id);
|
2022-11-04 21:27:10 +00:00
|
|
|
if (ptr_inst->opcode() != spv::Op::OpAccessChain) return false;
|
2019-08-16 13:18:34 +00:00
|
|
|
uint32_t ptr_ty_id = ptr_inst->type_id();
|
|
|
|
Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id);
|
2022-11-04 21:27:10 +00:00
|
|
|
if (spv::StorageClass(ptr_ty_inst->GetSingleWordInOperand(0)) !=
|
|
|
|
spv::StorageClass::PhysicalStorageBufferEXT)
|
2019-08-16 13:18:34 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ??
|
|
|
|
void InstBuffAddrCheckPass::GenCheckCode(
|
2023-09-20 16:50:30 +00:00
|
|
|
uint32_t check_id, Instruction* ref_inst,
|
2019-08-16 13:18:34 +00:00
|
|
|
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
|
BasicBlock* back_blk_ptr = &*new_blocks->back();
|
|
|
|
InstructionBuilder builder(
|
|
|
|
context(), back_blk_ptr,
|
|
|
|
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
|
|
|
// Gen conditional branch on check_id. Valid branch generates original
|
|
|
|
// reference. Invalid generates debug output and zero result (if needed).
|
|
|
|
uint32_t merge_blk_id = TakeNextId();
|
|
|
|
uint32_t valid_blk_id = TakeNextId();
|
|
|
|
uint32_t invalid_blk_id = TakeNextId();
|
|
|
|
std::unique_ptr<Instruction> merge_label(NewLabel(merge_blk_id));
|
|
|
|
std::unique_ptr<Instruction> valid_label(NewLabel(valid_blk_id));
|
|
|
|
std::unique_ptr<Instruction> invalid_label(NewLabel(invalid_blk_id));
|
2022-11-04 21:27:10 +00:00
|
|
|
(void)builder.AddConditionalBranch(
|
|
|
|
check_id, valid_blk_id, invalid_blk_id, merge_blk_id,
|
|
|
|
uint32_t(spv::SelectionControlMask::MaskNone));
|
2019-08-16 13:18:34 +00:00
|
|
|
// Gen valid branch
|
|
|
|
std::unique_ptr<BasicBlock> new_blk_ptr(
|
|
|
|
new BasicBlock(std::move(valid_label)));
|
|
|
|
builder.SetInsertPoint(&*new_blk_ptr);
|
|
|
|
uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder);
|
|
|
|
(void)builder.AddBranch(merge_blk_id);
|
|
|
|
new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
|
// Gen invalid block
|
|
|
|
new_blk_ptr.reset(new BasicBlock(std::move(invalid_label)));
|
|
|
|
builder.SetInsertPoint(&*new_blk_ptr);
|
2019-10-04 16:26:38 +00:00
|
|
|
// 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;
|
|
|
|
if (new_ref_id != 0) {
|
|
|
|
uint32_t ref_type_id = ref_inst->type_id();
|
|
|
|
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
|
|
|
analysis::Type* ref_type = type_mgr->GetType(ref_type_id);
|
|
|
|
if (ref_type->AsPointer() != nullptr) {
|
|
|
|
uint32_t null_u64_id = GetNullId(GetUint64Id());
|
2022-11-04 21:27:10 +00:00
|
|
|
Instruction* null_ptr_inst = builder.AddUnaryOp(
|
|
|
|
ref_type_id, spv::Op::OpConvertUToPtr, null_u64_id);
|
2019-10-04 16:26:38 +00:00
|
|
|
null_id = null_ptr_inst->result_id();
|
|
|
|
} else {
|
|
|
|
null_id = GetNullId(ref_type_id);
|
|
|
|
}
|
|
|
|
}
|
2019-08-16 13:18:34 +00:00
|
|
|
(void)builder.AddBranch(merge_blk_id);
|
|
|
|
new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
|
// Gen merge block
|
|
|
|
new_blk_ptr.reset(new BasicBlock(std::move(merge_label)));
|
|
|
|
builder.SetInsertPoint(&*new_blk_ptr);
|
|
|
|
// Gen phi of new reference and zero, if necessary, and replace the
|
|
|
|
// result id of the original reference with that of the Phi. Kill original
|
|
|
|
// reference.
|
|
|
|
if (new_ref_id != 0) {
|
2019-10-04 16:26:38 +00:00
|
|
|
Instruction* phi_inst =
|
|
|
|
builder.AddPhi(ref_inst->type_id(),
|
|
|
|
{new_ref_id, valid_blk_id, null_id, invalid_blk_id});
|
2019-08-16 13:18:34 +00:00
|
|
|
context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id());
|
|
|
|
}
|
|
|
|
new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
|
context()->KillInst(ref_inst);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) {
|
|
|
|
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
|
|
|
switch (type_inst->opcode()) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeFloat:
|
|
|
|
case spv::Op::OpTypeInt:
|
2019-08-16 13:18:34 +00:00
|
|
|
return type_inst->GetSingleWordInOperand(0) / 8u;
|
2023-06-14 22:14:46 +00:00
|
|
|
case spv::Op::OpTypeVector:
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeMatrix:
|
2019-08-16 13:18:34 +00:00
|
|
|
return type_inst->GetSingleWordInOperand(1) *
|
|
|
|
GetTypeLength(type_inst->GetSingleWordInOperand(0));
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypePointer:
|
|
|
|
assert(spv::StorageClass(type_inst->GetSingleWordInOperand(0)) ==
|
|
|
|
spv::StorageClass::PhysicalStorageBufferEXT &&
|
2019-08-16 13:18:34 +00:00
|
|
|
"unexpected pointer type");
|
|
|
|
return 8u;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeArray: {
|
2021-09-23 16:59:38 +00:00
|
|
|
uint32_t const_id = type_inst->GetSingleWordInOperand(1);
|
|
|
|
Instruction* const_inst = get_def_use_mgr()->GetDef(const_id);
|
|
|
|
uint32_t cnt = const_inst->GetSingleWordInOperand(0);
|
|
|
|
return cnt * GetTypeLength(type_inst->GetSingleWordInOperand(0));
|
|
|
|
}
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeStruct: {
|
2023-06-14 22:14:46 +00:00
|
|
|
// Figure out the location of the last byte of the last member of the
|
|
|
|
// structure.
|
|
|
|
uint32_t last_offset = 0, last_len = 0;
|
|
|
|
|
|
|
|
get_decoration_mgr()->ForEachDecoration(
|
|
|
|
type_id, uint32_t(spv::Decoration::Offset),
|
|
|
|
[&last_offset](const Instruction& deco_inst) {
|
|
|
|
last_offset = deco_inst.GetSingleWordInOperand(3);
|
|
|
|
});
|
|
|
|
type_inst->ForEachInId([&last_len, this](const uint32_t* iid) {
|
|
|
|
last_len = GetTypeLength(*iid);
|
2021-09-23 16:59:38 +00:00
|
|
|
});
|
2023-06-14 22:14:46 +00:00
|
|
|
return last_offset + last_len;
|
2021-09-23 16:59:38 +00:00
|
|
|
}
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeRuntimeArray:
|
2019-08-16 13:18:34 +00:00
|
|
|
default:
|
2021-09-23 16:59:38 +00:00
|
|
|
assert(false && "unexpected type");
|
2019-08-16 13:18:34 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void InstBuffAddrCheckPass::AddParam(uint32_t type_id,
|
|
|
|
std::vector<uint32_t>* param_vec,
|
|
|
|
std::unique_ptr<Function>* input_func) {
|
|
|
|
uint32_t pid = TakeNextId();
|
|
|
|
param_vec->push_back(pid);
|
|
|
|
std::unique_ptr<Instruction> param_inst(new Instruction(
|
2022-11-04 21:27:10 +00:00
|
|
|
get_module()->context(), spv::Op::OpFunctionParameter, type_id, pid, {}));
|
2019-08-16 13:18:34 +00:00
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst);
|
|
|
|
(*input_func)->AddParameter(std::move(param_inst));
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:50:30 +00:00
|
|
|
// 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
|
2019-08-16 13:18:34 +00:00
|
|
|
uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() {
|
2023-09-20 16:50:30 +00:00
|
|
|
enum {
|
|
|
|
kShaderId = 0,
|
|
|
|
kInstructionIndex = 1,
|
|
|
|
kStageInfo = 2,
|
|
|
|
kRefPtr = 3,
|
|
|
|
kLength = 4,
|
|
|
|
kNumArgs
|
|
|
|
};
|
|
|
|
if (search_test_func_id_ != 0) {
|
|
|
|
return search_test_func_id_;
|
2019-08-16 13:18:34 +00:00
|
|
|
}
|
2023-09-20 16:50:30 +00:00
|
|
|
// 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;
|
2019-08-16 13:18:34 +00:00
|
|
|
return search_test_func_id_;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst,
|
|
|
|
InstructionBuilder* builder,
|
2023-09-20 16:50:30 +00:00
|
|
|
uint32_t* ref_uptr_id,
|
|
|
|
uint32_t stage_idx) {
|
2019-08-16 13:18:34 +00:00
|
|
|
// Enable Int64 if necessary
|
|
|
|
// Convert reference pointer to uint64
|
2023-09-20 16:50:30 +00:00
|
|
|
const uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0);
|
2019-08-16 13:18:34 +00:00
|
|
|
Instruction* ref_uptr_inst =
|
2022-11-04 21:27:10 +00:00
|
|
|
builder->AddUnaryOp(GetUint64Id(), spv::Op::OpConvertPtrToU, ref_ptr_id);
|
2019-08-16 13:18:34 +00:00
|
|
|
*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);
|
2023-09-20 16:50:30 +00:00
|
|
|
const uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id();
|
2019-08-16 13:18:34 +00:00
|
|
|
Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id);
|
2023-09-20 16:50:30 +00:00
|
|
|
const uint32_t ref_len =
|
|
|
|
GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1));
|
2019-08-16 13:18:34 +00:00
|
|
|
// Gen call to search and test function
|
2023-09-20 16:50:30 +00:00
|
|
|
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);
|
2019-08-16 13:18:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstBuffAddrCheckPass::GenBuffAddrCheckCode(
|
|
|
|
BasicBlock::iterator ref_inst_itr,
|
|
|
|
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
|
|
|
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
|
// Look for reference through indexed descriptor. If found, analyze and
|
|
|
|
// save components. If not, return.
|
|
|
|
Instruction* ref_inst = &*ref_inst_itr;
|
|
|
|
if (!IsPhysicalBuffAddrReference(ref_inst)) return;
|
|
|
|
// Move original block's preceding instructions into first new block
|
|
|
|
std::unique_ptr<BasicBlock> new_blk_ptr;
|
|
|
|
MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
|
|
|
|
InstructionBuilder builder(
|
|
|
|
context(), &*new_blk_ptr,
|
|
|
|
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
|
|
|
new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
|
// 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;
|
2023-09-20 16:50:30 +00:00
|
|
|
uint32_t valid_id =
|
|
|
|
GenSearchAndTest(ref_inst, &builder, &ref_uptr_id, stage_idx);
|
2019-08-16 13:18:34 +00:00
|
|
|
// Generate test of search results with true branch
|
|
|
|
// being full reference and false branch being debug output and zero
|
|
|
|
// for the referenced value.
|
2023-09-20 16:50:30 +00:00
|
|
|
GenCheckCode(valid_id, ref_inst, new_blocks);
|
|
|
|
|
2019-08-16 13:18:34 +00:00
|
|
|
// Move original block's remaining code into remainder/merge block and add
|
|
|
|
// to new blocks
|
|
|
|
BasicBlock* back_blk_ptr = &*new_blocks->back();
|
|
|
|
MovePostludeCode(ref_block_itr, back_blk_ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InstBuffAddrCheckPass::InitInstBuffAddrCheck() {
|
|
|
|
// Initialize base class
|
|
|
|
InitializeInstrument();
|
|
|
|
// Initialize class
|
|
|
|
search_test_func_id_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Pass::Status InstBuffAddrCheckPass::ProcessImpl() {
|
2023-09-20 16:50:30 +00:00
|
|
|
// 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");
|
|
|
|
}
|
|
|
|
|
2023-10-02 15:15:39 +00:00
|
|
|
context()->AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
|
|
|
|
Instruction* memory_model = get_module()->GetMemoryModel();
|
|
|
|
memory_model->SetInOperand(
|
|
|
|
0u, {uint32_t(spv::AddressingModel::PhysicalStorageBuffer64)});
|
|
|
|
|
2023-09-20 16:50:30 +00:00
|
|
|
context()->AddCapability(spv::Capability::Int64);
|
|
|
|
context()->AddCapability(spv::Capability::Linkage);
|
2019-08-16 13:18:34 +00:00
|
|
|
// Perform bindless bounds check on each entry point function in module
|
|
|
|
InstProcessFunction pfn =
|
|
|
|
[this](BasicBlock::iterator ref_inst_itr,
|
|
|
|
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
|
|
|
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
|
return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
|
|
|
|
new_blocks);
|
|
|
|
};
|
2023-10-02 15:15:39 +00:00
|
|
|
InstProcessEntryPointCallTree(pfn);
|
|
|
|
// This pass always changes the memory model, so that linking will work
|
|
|
|
// properly.
|
|
|
|
return Status::SuccessWithChange;
|
2019-08-16 13:18:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Pass::Status InstBuffAddrCheckPass::Process() {
|
|
|
|
InitInstBuffAddrCheck();
|
|
|
|
return ProcessImpl();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|