mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 19:50:05 +00:00
b5d60826e9
Remove stage specific debug info that is only needed by GPU-AV. This allows debug printfs to be used in multi-stage shader modules. Fixes #4892
484 lines
21 KiB
C++
484 lines
21 KiB
C++
// Copyright (c) 2020 The Khronos Group Inc.
|
|
// Copyright (c) 2020 Valve Corporation
|
|
// Copyright (c) 2020 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_debug_printf_pass.h"
|
|
|
|
#include "source/spirv_constant.h"
|
|
#include "source/util/string_utils.h"
|
|
#include "spirv/unified1/NonSemanticDebugPrintf.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
|
|
void InstDebugPrintfPass::GenOutputValues(Instruction* val_inst,
|
|
std::vector<uint32_t>* val_ids,
|
|
InstructionBuilder* builder) {
|
|
uint32_t val_ty_id = val_inst->type_id();
|
|
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
|
analysis::Type* val_ty = type_mgr->GetType(val_ty_id);
|
|
switch (val_ty->kind()) {
|
|
case analysis::Type::kVector: {
|
|
analysis::Vector* v_ty = val_ty->AsVector();
|
|
const analysis::Type* c_ty = v_ty->element_type();
|
|
uint32_t c_ty_id = type_mgr->GetId(c_ty);
|
|
for (uint32_t c = 0; c < v_ty->element_count(); ++c) {
|
|
Instruction* c_inst =
|
|
builder->AddCompositeExtract(c_ty_id, val_inst->result_id(), {c});
|
|
GenOutputValues(c_inst, val_ids, builder);
|
|
}
|
|
return;
|
|
}
|
|
case analysis::Type::kBool: {
|
|
// Select between uint32 zero or one
|
|
uint32_t zero_id = builder->GetUintConstantId(0);
|
|
uint32_t one_id = builder->GetUintConstantId(1);
|
|
Instruction* sel_inst = builder->AddSelect(
|
|
GetUintId(), val_inst->result_id(), one_id, zero_id);
|
|
val_ids->push_back(sel_inst->result_id());
|
|
return;
|
|
}
|
|
case analysis::Type::kFloat: {
|
|
analysis::Float* f_ty = val_ty->AsFloat();
|
|
switch (f_ty->width()) {
|
|
case 16: {
|
|
// Convert float16 to float32 and recurse
|
|
Instruction* f32_inst = builder->AddUnaryOp(
|
|
GetFloatId(), spv::Op::OpFConvert, val_inst->result_id());
|
|
GenOutputValues(f32_inst, val_ids, builder);
|
|
return;
|
|
}
|
|
case 64: {
|
|
// Bitcast float64 to uint64 and recurse
|
|
Instruction* ui64_inst = builder->AddUnaryOp(
|
|
GetUint64Id(), spv::Op::OpBitcast, val_inst->result_id());
|
|
GenOutputValues(ui64_inst, val_ids, builder);
|
|
return;
|
|
}
|
|
case 32: {
|
|
// Bitcase float32 to uint32
|
|
Instruction* bc_inst = builder->AddUnaryOp(
|
|
GetUintId(), spv::Op::OpBitcast, val_inst->result_id());
|
|
val_ids->push_back(bc_inst->result_id());
|
|
return;
|
|
}
|
|
default:
|
|
assert(false && "unsupported float width");
|
|
return;
|
|
}
|
|
}
|
|
case analysis::Type::kInteger: {
|
|
analysis::Integer* i_ty = val_ty->AsInteger();
|
|
switch (i_ty->width()) {
|
|
case 64: {
|
|
Instruction* ui64_inst = val_inst;
|
|
if (i_ty->IsSigned()) {
|
|
// Bitcast sint64 to uint64
|
|
ui64_inst = builder->AddUnaryOp(GetUint64Id(), spv::Op::OpBitcast,
|
|
val_inst->result_id());
|
|
}
|
|
// Break uint64 into 2x uint32
|
|
Instruction* lo_ui64_inst = builder->AddUnaryOp(
|
|
GetUintId(), spv::Op::OpUConvert, ui64_inst->result_id());
|
|
Instruction* rshift_ui64_inst = builder->AddBinaryOp(
|
|
GetUint64Id(), spv::Op::OpShiftRightLogical,
|
|
ui64_inst->result_id(), builder->GetUintConstantId(32));
|
|
Instruction* hi_ui64_inst = builder->AddUnaryOp(
|
|
GetUintId(), spv::Op::OpUConvert, rshift_ui64_inst->result_id());
|
|
val_ids->push_back(lo_ui64_inst->result_id());
|
|
val_ids->push_back(hi_ui64_inst->result_id());
|
|
return;
|
|
}
|
|
case 8: {
|
|
Instruction* ui8_inst = val_inst;
|
|
if (i_ty->IsSigned()) {
|
|
// Bitcast sint8 to uint8
|
|
ui8_inst = builder->AddUnaryOp(GetUint8Id(), spv::Op::OpBitcast,
|
|
val_inst->result_id());
|
|
}
|
|
// Convert uint8 to uint32
|
|
Instruction* ui32_inst = builder->AddUnaryOp(
|
|
GetUintId(), spv::Op::OpUConvert, ui8_inst->result_id());
|
|
val_ids->push_back(ui32_inst->result_id());
|
|
return;
|
|
}
|
|
case 32: {
|
|
Instruction* ui32_inst = val_inst;
|
|
if (i_ty->IsSigned()) {
|
|
// Bitcast sint32 to uint32
|
|
ui32_inst = builder->AddUnaryOp(GetUintId(), spv::Op::OpBitcast,
|
|
val_inst->result_id());
|
|
}
|
|
// uint32 needs no further processing
|
|
val_ids->push_back(ui32_inst->result_id());
|
|
return;
|
|
}
|
|
default:
|
|
// TODO(greg-lunarg): Support non-32-bit int
|
|
assert(false && "unsupported int width");
|
|
return;
|
|
}
|
|
}
|
|
default:
|
|
assert(false && "unsupported type");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void InstDebugPrintfPass::GenOutputCode(
|
|
Instruction* printf_inst,
|
|
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 debug printf record validation-specific values. The format string
|
|
// will have its id written. Vectors will need to be broken down into
|
|
// component values. float16 will need to be converted to float32. Pointer
|
|
// and uint64 will need to be converted to two uint32 values. float32 will
|
|
// need to be bitcast to uint32. int32 will need to be bitcast to uint32.
|
|
std::vector<uint32_t> val_ids;
|
|
bool is_first_operand = false;
|
|
printf_inst->ForEachInId(
|
|
[&is_first_operand, &val_ids, &builder, this](const uint32_t* iid) {
|
|
// skip set operand
|
|
if (!is_first_operand) {
|
|
is_first_operand = true;
|
|
return;
|
|
}
|
|
Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid);
|
|
if (opnd_inst->opcode() == spv::Op::OpString) {
|
|
uint32_t string_id_id = builder.GetUintConstantId(*iid);
|
|
val_ids.push_back(string_id_id);
|
|
} else {
|
|
GenOutputValues(opnd_inst, &val_ids, &builder);
|
|
}
|
|
});
|
|
GenDebugStreamWrite(
|
|
builder.GetUintConstantId(shader_id_),
|
|
builder.GetUintConstantId(uid2offset_[printf_inst->unique_id()]), val_ids,
|
|
&builder);
|
|
context()->KillInst(printf_inst);
|
|
}
|
|
|
|
void InstDebugPrintfPass::GenDebugPrintfCode(
|
|
BasicBlock::iterator ref_inst_itr,
|
|
UptrVectorIterator<BasicBlock> ref_block_itr,
|
|
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
// If not DebugPrintf OpExtInst, return.
|
|
Instruction* printf_inst = &*ref_inst_itr;
|
|
if (printf_inst->opcode() != spv::Op::OpExtInst) return;
|
|
if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return;
|
|
if (printf_inst->GetSingleWordInOperand(1) !=
|
|
NonSemanticDebugPrintfDebugPrintf)
|
|
return;
|
|
// Initialize DefUse manager before dismantling module
|
|
(void)get_def_use_mgr();
|
|
// 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);
|
|
new_blocks->push_back(std::move(new_blk_ptr));
|
|
// Generate instructions to output printf args to printf buffer
|
|
GenOutputCode(printf_inst, new_blocks);
|
|
// Caller expects at least two blocks with last block containing remaining
|
|
// code, so end block after instrumentation, create remainder block, and
|
|
// branch to it
|
|
uint32_t rem_blk_id = TakeNextId();
|
|
std::unique_ptr<Instruction> rem_label(NewLabel(rem_blk_id));
|
|
BasicBlock* back_blk_ptr = &*new_blocks->back();
|
|
InstructionBuilder builder(
|
|
context(), back_blk_ptr,
|
|
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
|
(void)builder.AddBranch(rem_blk_id);
|
|
// Gen remainder block
|
|
new_blk_ptr.reset(new BasicBlock(std::move(rem_label)));
|
|
builder.SetInsertPoint(&*new_blk_ptr);
|
|
// Move original block's remaining code into remainder block and add
|
|
// to new blocks
|
|
MovePostludeCode(ref_block_itr, &*new_blk_ptr);
|
|
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,
|
|
kFirstParam = 2,
|
|
};
|
|
// 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);
|
|
|
|
std::vector<const analysis::Type*> param_types(kFirstParam + param_cnt,
|
|
uint_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 first_param_offset = kInstCommonOutInstructionIdx + 1;
|
|
const uint32_t obuf_record_sz = first_param_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);
|
|
// Gen writes of validation specific data
|
|
for (uint32_t i = 0; i < param_cnt; ++i) {
|
|
GenDebugOutputFieldCode(obuf_curr_sz_id, first_param_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,
|
|
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};
|
|
(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() {
|
|
// Perform printf instrumentation on each entry point function in module
|
|
InstProcessFunction pfn =
|
|
[this](BasicBlock::iterator ref_inst_itr,
|
|
UptrVectorIterator<BasicBlock> ref_block_itr,
|
|
[[maybe_unused]] uint32_t stage_idx,
|
|
std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
|
|
return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, new_blocks);
|
|
};
|
|
(void)InstProcessEntryPointCallTree(pfn);
|
|
// Remove DebugPrintf OpExtInstImport instruction
|
|
Instruction* ext_inst_import_inst =
|
|
get_def_use_mgr()->GetDef(ext_inst_printf_id_);
|
|
context()->KillInst(ext_inst_import_inst);
|
|
// If no remaining non-semantic instruction sets, remove non-semantic debug
|
|
// info extension from module and feature manager
|
|
bool non_sem_set_seen = false;
|
|
for (auto c_itr = context()->module()->ext_inst_import_begin();
|
|
c_itr != context()->module()->ext_inst_import_end(); ++c_itr) {
|
|
const std::string set_name = c_itr->GetInOperand(0).AsString();
|
|
if (spvtools::utils::starts_with(set_name, "NonSemantic.")) {
|
|
non_sem_set_seen = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!non_sem_set_seen) {
|
|
context()->RemoveExtension(kSPV_KHR_non_semantic_info);
|
|
}
|
|
return Status::SuccessWithChange;
|
|
}
|
|
|
|
Pass::Status InstDebugPrintfPass::Process() {
|
|
ext_inst_printf_id_ =
|
|
get_module()->GetExtInstImportId("NonSemantic.DebugPrintf");
|
|
if (ext_inst_printf_id_ == 0) return Status::SuccessWithoutChange;
|
|
InitializeInstDebugPrintf();
|
|
return ProcessImpl();
|
|
}
|
|
|
|
} // namespace opt
|
|
} // namespace spvtools
|