|
|
@@ -40,21 +40,74 @@ uint32_t InstBindlessCheckPass::GenDebugReadLength(
|
|
|
uint32_t desc_set_idx =
|
|
|
var2desc_set_[var_id] + kDebugInputBindlessOffsetLengths;
|
|
|
uint32_t desc_set_idx_id = builder->GetUintConstantId(desc_set_idx);
|
|
|
- uint32_t desc_set_offset_id = GenDebugDirectRead(desc_set_idx_id, builder);
|
|
|
- Instruction* binding_idx_inst =
|
|
|
- builder->AddBinaryOp(GetUintId(), SpvOpIAdd, desc_set_offset_id,
|
|
|
- builder->GetUintConstantId(var2binding_[var_id]));
|
|
|
- return GenDebugDirectRead(binding_idx_inst->result_id(), builder);
|
|
|
+ uint32_t binding_idx_id = builder->GetUintConstantId(var2binding_[var_id]);
|
|
|
+ return GenDebugDirectRead({desc_set_idx_id, binding_idx_id}, builder);
|
|
|
}
|
|
|
|
|
|
-void InstBindlessCheckPass::GenBindlessCheckCode(
|
|
|
- BasicBlock::iterator ref_inst_itr,
|
|
|
- UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t instruction_idx,
|
|
|
- uint32_t stage_idx, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
- // Look for reference through bindless descriptor. If not, return.
|
|
|
- std::unique_ptr<BasicBlock> new_blk_ptr;
|
|
|
- uint32_t image_id;
|
|
|
- switch (ref_inst_itr->opcode()) {
|
|
|
+uint32_t InstBindlessCheckPass::GenDebugReadInit(uint32_t var_id,
|
|
|
+ uint32_t desc_idx_id,
|
|
|
+ InstructionBuilder* builder) {
|
|
|
+ uint32_t desc_set_base_id =
|
|
|
+ builder->GetUintConstantId(kDebugInputBindlessInitOffset);
|
|
|
+ uint32_t desc_set_idx_id = builder->GetUintConstantId(var2desc_set_[var_id]);
|
|
|
+ uint32_t binding_idx_id = builder->GetUintConstantId(var2binding_[var_id]);
|
|
|
+ uint32_t u_desc_idx_id = GenUintCastCode(desc_idx_id, builder);
|
|
|
+ return GenDebugDirectRead(
|
|
|
+ {desc_set_base_id, desc_set_idx_id, binding_idx_id, u_desc_idx_id},
|
|
|
+ builder);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t InstBindlessCheckPass::CloneOriginalReference(
|
|
|
+ ref_analysis* ref, InstructionBuilder* builder) {
|
|
|
+ // Clone descriptor load
|
|
|
+ Instruction* load_inst = get_def_use_mgr()->GetDef(ref->load_id);
|
|
|
+ Instruction* new_load_inst =
|
|
|
+ builder->AddLoad(load_inst->type_id(),
|
|
|
+ load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx));
|
|
|
+ uid2offset_[new_load_inst->unique_id()] = uid2offset_[load_inst->unique_id()];
|
|
|
+ uint32_t new_load_id = new_load_inst->result_id();
|
|
|
+ get_decoration_mgr()->CloneDecorations(load_inst->result_id(), new_load_id);
|
|
|
+ uint32_t new_image_id = new_load_id;
|
|
|
+ // Clone Image/SampledImage with new load, if needed
|
|
|
+ if (ref->image_id != 0) {
|
|
|
+ Instruction* image_inst = get_def_use_mgr()->GetDef(ref->image_id);
|
|
|
+ if (image_inst->opcode() == SpvOp::SpvOpSampledImage) {
|
|
|
+ Instruction* new_image_inst = builder->AddBinaryOp(
|
|
|
+ image_inst->type_id(), SpvOpSampledImage, new_load_id,
|
|
|
+ image_inst->GetSingleWordInOperand(kSpvSampledImageSamplerIdInIdx));
|
|
|
+ uid2offset_[new_image_inst->unique_id()] =
|
|
|
+ uid2offset_[image_inst->unique_id()];
|
|
|
+ new_image_id = new_image_inst->result_id();
|
|
|
+ } else {
|
|
|
+ assert(image_inst->opcode() == SpvOp::SpvOpImage && "expecting OpImage");
|
|
|
+ Instruction* new_image_inst =
|
|
|
+ builder->AddUnaryOp(image_inst->type_id(), SpvOpImage, new_load_id);
|
|
|
+ uid2offset_[new_image_inst->unique_id()] =
|
|
|
+ uid2offset_[image_inst->unique_id()];
|
|
|
+ new_image_id = new_image_inst->result_id();
|
|
|
+ }
|
|
|
+ get_decoration_mgr()->CloneDecorations(ref->image_id, new_image_id);
|
|
|
+ }
|
|
|
+ // Clone original reference using new image code
|
|
|
+ std::unique_ptr<Instruction> new_ref_inst(ref->ref_inst->Clone(context()));
|
|
|
+ uint32_t ref_result_id = ref->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);
|
|
|
+ }
|
|
|
+ new_ref_inst->SetInOperand(kSpvImageSampleImageIdInIdx, {new_image_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->ref_inst->unique_id()];
|
|
|
+ if (new_ref_id != 0)
|
|
|
+ get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id);
|
|
|
+ return new_ref_id;
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t InstBindlessCheckPass::GetDescriptorValueId(Instruction* inst) {
|
|
|
+ switch (inst->opcode()) {
|
|
|
case SpvOp::SpvOpImageSampleImplicitLod:
|
|
|
case SpvOp::SpvOpImageSampleExplicitLod:
|
|
|
case SpvOp::SpvOpImageSampleDrefImplicitLod:
|
|
|
@@ -87,162 +140,205 @@ void InstBindlessCheckPass::GenBindlessCheckCode(
|
|
|
case SpvOp::SpvOpImageSparseFetch:
|
|
|
case SpvOp::SpvOpImageSparseRead:
|
|
|
case SpvOp::SpvOpImageWrite:
|
|
|
- image_id =
|
|
|
- ref_inst_itr->GetSingleWordInOperand(kSpvImageSampleImageIdInIdx);
|
|
|
- break;
|
|
|
+ return inst->GetSingleWordInOperand(kSpvImageSampleImageIdInIdx);
|
|
|
default:
|
|
|
- return;
|
|
|
+ break;
|
|
|
}
|
|
|
- Instruction* image_inst = get_def_use_mgr()->GetDef(image_id);
|
|
|
- uint32_t load_id;
|
|
|
- Instruction* load_inst;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+bool InstBindlessCheckPass::AnalyzeDescriptorReference(Instruction* ref_inst,
|
|
|
+ ref_analysis* ref) {
|
|
|
+ ref->image_id = GetDescriptorValueId(ref_inst);
|
|
|
+ if (ref->image_id == 0) return false;
|
|
|
+ Instruction* image_inst = get_def_use_mgr()->GetDef(ref->image_id);
|
|
|
if (image_inst->opcode() == SpvOp::SpvOpSampledImage) {
|
|
|
- load_id = image_inst->GetSingleWordInOperand(kSpvSampledImageImageIdInIdx);
|
|
|
- load_inst = get_def_use_mgr()->GetDef(load_id);
|
|
|
+ ref->load_id =
|
|
|
+ image_inst->GetSingleWordInOperand(kSpvSampledImageImageIdInIdx);
|
|
|
} else if (image_inst->opcode() == SpvOp::SpvOpImage) {
|
|
|
- load_id = image_inst->GetSingleWordInOperand(kSpvImageSampledImageIdInIdx);
|
|
|
- load_inst = get_def_use_mgr()->GetDef(load_id);
|
|
|
+ ref->load_id =
|
|
|
+ image_inst->GetSingleWordInOperand(kSpvImageSampledImageIdInIdx);
|
|
|
} else {
|
|
|
- load_id = image_id;
|
|
|
- load_inst = image_inst;
|
|
|
- image_id = 0;
|
|
|
+ ref->load_id = ref->image_id;
|
|
|
+ ref->image_id = 0;
|
|
|
}
|
|
|
+ Instruction* load_inst = get_def_use_mgr()->GetDef(ref->load_id);
|
|
|
if (load_inst->opcode() != SpvOp::SpvOpLoad) {
|
|
|
- // TODO(greg-lunarg): Handle additional possibilities
|
|
|
- return;
|
|
|
+ // TODO(greg-lunarg): Handle additional possibilities?
|
|
|
+ return false;
|
|
|
}
|
|
|
- uint32_t ptr_id = load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx);
|
|
|
- Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id);
|
|
|
- if (ptr_inst->opcode() != SpvOp::SpvOpAccessChain) return;
|
|
|
- if (ptr_inst->NumInOperands() != 2) {
|
|
|
- assert(false && "unexpected bindless index number");
|
|
|
- return;
|
|
|
- }
|
|
|
- uint32_t index_id =
|
|
|
- ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx);
|
|
|
- ptr_id = ptr_inst->GetSingleWordInOperand(kSpvAccessChainBaseIdInIdx);
|
|
|
- ptr_inst = get_def_use_mgr()->GetDef(ptr_id);
|
|
|
- if (ptr_inst->opcode() != SpvOpVariable) {
|
|
|
- assert(false && "unexpected bindless base");
|
|
|
- return;
|
|
|
- }
|
|
|
- uint32_t var_type_id = ptr_inst->type_id();
|
|
|
- Instruction* var_type_inst = get_def_use_mgr()->GetDef(var_type_id);
|
|
|
- uint32_t ptr_type_id =
|
|
|
- var_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
|
|
|
- Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
|
|
|
- // If index and bound both compile-time constants and index < bound,
|
|
|
- // return without changing
|
|
|
- uint32_t length_id = 0;
|
|
|
- if (ptr_type_inst->opcode() == SpvOpTypeArray) {
|
|
|
- length_id =
|
|
|
- ptr_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx);
|
|
|
- Instruction* index_inst = get_def_use_mgr()->GetDef(index_id);
|
|
|
- Instruction* length_inst = get_def_use_mgr()->GetDef(length_id);
|
|
|
- if (index_inst->opcode() == SpvOpConstant &&
|
|
|
- length_inst->opcode() == SpvOpConstant &&
|
|
|
- index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) <
|
|
|
- length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx))
|
|
|
- return;
|
|
|
- } else if (!runtime_array_enabled_ ||
|
|
|
- ptr_type_inst->opcode() != SpvOpTypeRuntimeArray) {
|
|
|
- return;
|
|
|
+ ref->ptr_id = load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx);
|
|
|
+ Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref->ptr_id);
|
|
|
+ if (ptr_inst->opcode() == SpvOp::SpvOpVariable) {
|
|
|
+ ref->index_id = 0;
|
|
|
+ ref->var_id = ref->ptr_id;
|
|
|
+ } else if (ptr_inst->opcode() == SpvOp::SpvOpAccessChain) {
|
|
|
+ if (ptr_inst->NumInOperands() != 2) {
|
|
|
+ assert(false && "unexpected bindless index number");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ ref->index_id =
|
|
|
+ ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx);
|
|
|
+ ref->var_id = ptr_inst->GetSingleWordInOperand(kSpvAccessChainBaseIdInIdx);
|
|
|
+ Instruction* var_inst = get_def_use_mgr()->GetDef(ref->var_id);
|
|
|
+ if (var_inst->opcode() != SpvOpVariable) {
|
|
|
+ assert(false && "unexpected bindless base");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // TODO(greg-lunarg): Handle additional possibilities?
|
|
|
+ return false;
|
|
|
}
|
|
|
- // Generate full runtime bounds test code with true branch
|
|
|
- // being full reference and false branch being debug output and zero
|
|
|
- // for the referenced value.
|
|
|
- MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
|
|
|
+ ref->ref_inst = ref_inst;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void InstBindlessCheckPass::GenCheckCode(
|
|
|
+ uint32_t check_id, uint32_t error_id, uint32_t length_id,
|
|
|
+ uint32_t stage_idx, ref_analysis* ref,
|
|
|
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
+ BasicBlock* back_blk_ptr = &*new_blocks->back();
|
|
|
InstructionBuilder builder(
|
|
|
- context(), &*new_blk_ptr,
|
|
|
+ context(), back_blk_ptr,
|
|
|
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
|
|
- uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBounds);
|
|
|
- // If length id not yet set, descriptor array is runtime size so
|
|
|
- // generate load of length from stage's debug input buffer.
|
|
|
- if (length_id == 0) {
|
|
|
- assert(ptr_type_inst->opcode() == SpvOpTypeRuntimeArray &&
|
|
|
- "unexpected bindless type");
|
|
|
- length_id = GenDebugReadLength(ptr_id, &builder);
|
|
|
- }
|
|
|
- Instruction* ult_inst =
|
|
|
- builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, index_id, length_id);
|
|
|
+ // 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));
|
|
|
- (void)builder.AddConditionalBranch(ult_inst->result_id(), valid_blk_id,
|
|
|
- invalid_blk_id, merge_blk_id,
|
|
|
- SpvSelectionControlMaskNone);
|
|
|
- // Close selection block and gen valid reference block
|
|
|
- new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
- new_blk_ptr.reset(new BasicBlock(std::move(valid_label)));
|
|
|
+ (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id,
|
|
|
+ merge_blk_id, SpvSelectionControlMaskNone);
|
|
|
+ // Gen valid bounds branch
|
|
|
+ std::unique_ptr<BasicBlock> new_blk_ptr(
|
|
|
+ new BasicBlock(std::move(valid_label)));
|
|
|
builder.SetInsertPoint(&*new_blk_ptr);
|
|
|
- // Clone descriptor load
|
|
|
- Instruction* new_load_inst =
|
|
|
- builder.AddLoad(load_inst->type_id(),
|
|
|
- load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx));
|
|
|
- uint32_t new_load_id = new_load_inst->result_id();
|
|
|
- get_decoration_mgr()->CloneDecorations(load_inst->result_id(), new_load_id);
|
|
|
- uint32_t new_image_id = new_load_id;
|
|
|
- // Clone Image/SampledImage with new load, if needed
|
|
|
- if (image_id != 0) {
|
|
|
- if (image_inst->opcode() == SpvOp::SpvOpSampledImage) {
|
|
|
- Instruction* new_image_inst = builder.AddBinaryOp(
|
|
|
- image_inst->type_id(), SpvOpSampledImage, new_load_id,
|
|
|
- image_inst->GetSingleWordInOperand(kSpvSampledImageSamplerIdInIdx));
|
|
|
- new_image_id = new_image_inst->result_id();
|
|
|
- } else {
|
|
|
- assert(image_inst->opcode() == SpvOp::SpvOpImage && "expecting OpImage");
|
|
|
- Instruction* new_image_inst =
|
|
|
- builder.AddUnaryOp(image_inst->type_id(), SpvOpImage, new_load_id);
|
|
|
- new_image_id = new_image_inst->result_id();
|
|
|
- }
|
|
|
- get_decoration_mgr()->CloneDecorations(image_id, new_image_id);
|
|
|
- }
|
|
|
- // Clone original reference using new image code
|
|
|
- std::unique_ptr<Instruction> new_ref_inst(ref_inst_itr->Clone(context()));
|
|
|
- uint32_t ref_result_id = ref_inst_itr->result_id();
|
|
|
- uint32_t new_ref_id = 0;
|
|
|
- if (ref_result_id != 0) {
|
|
|
- new_ref_id = TakeNextId();
|
|
|
- new_ref_inst->SetResultId(new_ref_id);
|
|
|
- }
|
|
|
- new_ref_inst->SetInOperand(kSpvImageSampleImageIdInIdx, {new_image_id});
|
|
|
- // Register new reference and add to new block
|
|
|
- builder.AddInstruction(std::move(new_ref_inst));
|
|
|
- if (new_ref_id != 0)
|
|
|
- get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id);
|
|
|
- // Close valid block and gen invalid block
|
|
|
+ uint32_t new_ref_id = CloneOriginalReference(ref, &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);
|
|
|
- uint32_t u_index_id = GenUintCastCode(index_id, &builder);
|
|
|
- GenDebugStreamWrite(instruction_idx, stage_idx,
|
|
|
+ uint32_t u_index_id = GenUintCastCode(ref->index_id, &builder);
|
|
|
+ GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx,
|
|
|
{error_id, u_index_id, length_id}, &builder);
|
|
|
// Remember last invalid block id
|
|
|
uint32_t last_invalid_blk_id = new_blk_ptr->GetLabelInst()->result_id();
|
|
|
// Gen zero for invalid reference
|
|
|
- uint32_t ref_type_id = ref_inst_itr->type_id();
|
|
|
- // Close invalid block and gen merge block
|
|
|
+ uint32_t ref_type_id = ref->ref_inst->type_id();
|
|
|
(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 and move in remainder of original block.
|
|
|
+ // reference.
|
|
|
if (new_ref_id != 0) {
|
|
|
Instruction* phi_inst = builder.AddPhi(
|
|
|
ref_type_id, {new_ref_id, valid_blk_id, builder.GetNullId(ref_type_id),
|
|
|
last_invalid_blk_id});
|
|
|
- context()->ReplaceAllUsesWith(ref_result_id, phi_inst->result_id());
|
|
|
+ context()->ReplaceAllUsesWith(ref->ref_inst->result_id(),
|
|
|
+ phi_inst->result_id());
|
|
|
}
|
|
|
- context()->KillInst(&*ref_inst_itr);
|
|
|
- MovePostludeCode(ref_block_itr, &new_blk_ptr);
|
|
|
- // Add remainder/merge block to new blocks
|
|
|
new_blocks->push_back(std::move(new_blk_ptr));
|
|
|
+ context()->KillInst(ref->ref_inst);
|
|
|
+}
|
|
|
+
|
|
|
+void InstBindlessCheckPass::GenBoundsCheckCode(
|
|
|
+ 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.
|
|
|
+ ref_analysis ref;
|
|
|
+ if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return;
|
|
|
+ Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id);
|
|
|
+ if (ptr_inst->opcode() != SpvOp::SpvOpAccessChain) return;
|
|
|
+ // If index and bound both compile-time constants and index < bound,
|
|
|
+ // return without changing
|
|
|
+ Instruction* var_inst = get_def_use_mgr()->GetDef(ref.var_id);
|
|
|
+ uint32_t var_type_id = var_inst->type_id();
|
|
|
+ Instruction* var_type_inst = get_def_use_mgr()->GetDef(var_type_id);
|
|
|
+ uint32_t desc_type_id =
|
|
|
+ var_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
|
|
|
+ Instruction* desc_type_inst = get_def_use_mgr()->GetDef(desc_type_id);
|
|
|
+ uint32_t length_id = 0;
|
|
|
+ if (desc_type_inst->opcode() == SpvOpTypeArray) {
|
|
|
+ length_id =
|
|
|
+ desc_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx);
|
|
|
+ Instruction* index_inst = get_def_use_mgr()->GetDef(ref.index_id);
|
|
|
+ Instruction* length_inst = get_def_use_mgr()->GetDef(length_id);
|
|
|
+ if (index_inst->opcode() == SpvOpConstant &&
|
|
|
+ length_inst->opcode() == SpvOpConstant &&
|
|
|
+ index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) <
|
|
|
+ length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx))
|
|
|
+ return;
|
|
|
+ } else if (!input_length_enabled_ ||
|
|
|
+ desc_type_inst->opcode() != SpvOpTypeRuntimeArray) {
|
|
|
+ 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));
|
|
|
+ uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBounds);
|
|
|
+ // If length id not yet set, descriptor array is runtime size so
|
|
|
+ // generate load of length from stage's debug input buffer.
|
|
|
+ if (length_id == 0) {
|
|
|
+ assert(desc_type_inst->opcode() == SpvOpTypeRuntimeArray &&
|
|
|
+ "unexpected bindless type");
|
|
|
+ length_id = GenDebugReadLength(ref.var_id, &builder);
|
|
|
+ }
|
|
|
+ // Generate full runtime bounds test code with true branch
|
|
|
+ // being full reference and false branch being debug output and zero
|
|
|
+ // for the referenced value.
|
|
|
+ Instruction* ult_inst =
|
|
|
+ builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, ref.index_id, length_id);
|
|
|
+ GenCheckCode(ult_inst->result_id(), error_id, length_id, stage_idx, &ref,
|
|
|
+ new_blocks);
|
|
|
+ // 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 InstBindlessCheckPass::GenInitCheckCode(
|
|
|
+ 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 descriptor. If not, return.
|
|
|
+ ref_analysis ref;
|
|
|
+ if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) 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));
|
|
|
+ // Read initialization status from debug input buffer. If index id not yet
|
|
|
+ // set, binding is single descriptor, so set index to constant 0.
|
|
|
+ uint32_t zero_id = builder.GetUintConstantId(0u);
|
|
|
+ if (ref.index_id == 0) ref.index_id = zero_id;
|
|
|
+ uint32_t init_id = GenDebugReadInit(ref.var_id, ref.index_id, &builder);
|
|
|
+ // Generate full runtime non-zero init test code with true branch
|
|
|
+ // being full reference and false branch being debug output and zero
|
|
|
+ // for the referenced value.
|
|
|
+ Instruction* uneq_inst =
|
|
|
+ builder.AddBinaryOp(GetBoolId(), SpvOpINotEqual, init_id, zero_id);
|
|
|
+ uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessUninit);
|
|
|
+ GenCheckCode(uneq_inst->result_id(), error_id, zero_id, stage_idx, &ref,
|
|
|
+ new_blocks);
|
|
|
+ // 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 InstBindlessCheckPass::InitializeInstBindlessCheck() {
|
|
|
@@ -258,9 +354,10 @@ void InstBindlessCheckPass::InitializeInstBindlessCheck() {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- // If descriptor indexing extension and runtime array support enabled,
|
|
|
- // create variable to descriptor set mapping.
|
|
|
- if (ext_descriptor_indexing_defined_ && runtime_array_enabled_)
|
|
|
+ // If descriptor indexing extension and runtime array length support enabled,
|
|
|
+ // create variable mappings. Length support is always enabled if descriptor
|
|
|
+ // init check is enabled.
|
|
|
+ if (ext_descriptor_indexing_defined_ && input_length_enabled_)
|
|
|
for (auto& anno : get_module()->annotations())
|
|
|
if (anno.opcode() == SpvOpDecorate) {
|
|
|
if (anno.GetSingleWordInOperand(1u) == SpvDecorationDescriptorSet)
|
|
|
@@ -273,18 +370,27 @@ void InstBindlessCheckPass::InitializeInstBindlessCheck() {
|
|
|
}
|
|
|
|
|
|
Pass::Status InstBindlessCheckPass::ProcessImpl() {
|
|
|
- // Perform instrumentation on each entry point function in module
|
|
|
+ // 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 instruction_idx, uint32_t stage_idx,
|
|
|
+ UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
|
|
|
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
|
- return GenBindlessCheckCode(ref_inst_itr, ref_block_itr,
|
|
|
- instruction_idx, stage_idx, new_blocks);
|
|
|
+ return GenBoundsCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
|
|
|
+ new_blocks);
|
|
|
};
|
|
|
bool modified = InstProcessEntryPointCallTree(pfn);
|
|
|
- // This pass does not update inst->blk info
|
|
|
- context()->InvalidateAnalyses(IRContext::kAnalysisInstrToBlockMapping);
|
|
|
+ if (ext_descriptor_indexing_defined_ && input_init_enabled_) {
|
|
|
+ // Perform descriptor initialization check on each entry point function in
|
|
|
+ // module
|
|
|
+ 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 GenInitCheckCode(ref_inst_itr, ref_block_itr, stage_idx,
|
|
|
+ new_blocks);
|
|
|
+ };
|
|
|
+ modified |= InstProcessEntryPointCallTree(pfn);
|
|
|
+ }
|
|
|
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
|
|
}
|
|
|
|