Handle NonSemantic.Shader Debug[No]Line (#4530)

Debug[No]Line are tracked and optimized using the same mechanism that tracks
and optimizes Op[No]Line.

Also:
    - Fix missing DebugScope at top of block.
    - Allow scalar replacement of access chain in DebugDeclare
This commit is contained in:
Greg Fischer 2021-09-24 08:56:08 -06:00 committed by GitHub
parent f125452cf8
commit 19dc86c48c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 413 additions and 64 deletions

View File

@ -441,6 +441,14 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) {
// Perform closure on live instruction set.
while (!worklist_.empty()) {
Instruction* liveInst = worklist_.front();
// Add all operand instructions of Debug[No]Lines
for (auto& lineInst : liveInst->dbg_line_insts()) {
if (lineInst.IsDebugLineInst()) {
lineInst.ForEachInId([this](const uint32_t* iid) {
AddToWorklist(get_def_use_mgr()->GetDef(*iid));
});
}
}
// Add all operand instructions if not already live
liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) {
Instruction* inInst = get_def_use_mgr()->GetDef(*iid);

View File

@ -173,7 +173,9 @@ void MergeWithSuccessor(IRContext* context, Function* func,
auto& vec = terminator->dbg_line_insts();
auto& new_vec = merge_inst->dbg_line_insts();
new_vec.insert(new_vec.end(), vec.begin(), vec.end());
terminator->clear_dbg_line_insts();
terminator->ClearDbgLineInsts();
for (auto& l_inst : new_vec)
context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst);
// Move the merge instruction to just before the terminator.
merge_inst->InsertBefore(terminator);

View File

@ -86,9 +86,12 @@ Pass::Status CompactIdsPass::Process() {
},
true);
if (modified)
if (modified) {
context()->module()->SetIdBound(
static_cast<uint32_t>(result_id_mapping.size() + 1));
// There are ids in the feature manager that could now be invalid
context()->ResetFeatureManager();
}
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}

View File

@ -86,7 +86,7 @@ uint32_t DebugInfoManager::GetDbgSetImportId() {
context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
if (setId == 0) {
setId =
context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo();
context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
}
return setId;
}
@ -158,7 +158,7 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
// In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant,
// not literals.
if (setId ==
context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo())
context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo())
line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID;
uint32_t line_number = 0;

View File

@ -58,7 +58,7 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) {
case SPV_OPERAND_TYPE_SCOPE_ID: {
uint32_t use_id = inst->GetSingleWordOperand(i);
Instruction* def = GetDef(use_id);
assert(def && "Definition is not registered.");
if (!def) assert(false && "Definition is not registered.");
id_to_users_.insert(UserEntry(def, inst));
used_ids->push_back(use_id);
} break;
@ -71,6 +71,9 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) {
void DefUseManager::AnalyzeInstDefUse(Instruction* inst) {
AnalyzeInstDef(inst);
AnalyzeInstUse(inst);
// Analyze lines last otherwise they will be cleared when inst is
// cleared by preceding two calls
for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst);
}
void DefUseManager::UpdateDefUse(Instruction* inst) {
@ -224,9 +227,11 @@ void DefUseManager::AnalyzeDefUse(Module* module) {
if (!module) return;
// Analyze all the defs before any uses to catch forward references.
module->ForEachInst(
std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1));
std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1),
true);
module->ForEachInst(
std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1));
std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1),
true);
}
void DefUseManager::ClearInst(Instruction* inst) {
@ -261,6 +266,16 @@ void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) {
bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) {
if (lhs.id_to_def_ != rhs.id_to_def_) {
for (auto p : lhs.id_to_def_) {
if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) {
return false;
}
}
for (auto p : rhs.id_to_def_) {
if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) {
return false;
}
}
return false;
}

View File

@ -80,7 +80,7 @@ void FeatureManager::AddExtInstImportIds(Module* module) {
extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450");
extinst_importid_OpenCL100DebugInfo_ =
module->GetExtInstImportId("OpenCL.DebugInfo.100");
extinst_importid_Vulkan100DebugInfo_ =
extinst_importid_Shader100DebugInfo_ =
module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100");
}
@ -109,8 +109,8 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) {
return false;
}
if (a.extinst_importid_Vulkan100DebugInfo_ !=
b.extinst_importid_Vulkan100DebugInfo_) {
if (a.extinst_importid_Shader100DebugInfo_ !=
b.extinst_importid_Shader100DebugInfo_) {
return false;
}

View File

@ -55,8 +55,8 @@ class FeatureManager {
return extinst_importid_OpenCL100DebugInfo_;
}
uint32_t GetExtInstImportId_Vulkan100DebugInfo() const {
return extinst_importid_Vulkan100DebugInfo_;
uint32_t GetExtInstImportId_Shader100DebugInfo() const {
return extinst_importid_Shader100DebugInfo_;
}
friend bool operator==(const FeatureManager& a, const FeatureManager& b);
@ -99,7 +99,7 @@ class FeatureManager {
// Common NonSemanticShader100DebugInfo external instruction import ids,
// cached for performance.
uint32_t extinst_importid_Vulkan100DebugInfo_ = 0;
uint32_t extinst_importid_Shader100DebugInfo_ = 0;
};
} // namespace opt

View File

@ -129,6 +129,7 @@ Pass::Status IfConversion::Process() {
Instruction* select = builder.AddSelect(phi->type_id(), condition,
true_value->result_id(),
false_value->result_id());
context()->get_def_use_mgr()->AnalyzeInstDefUse(select);
select->UpdateDebugInfoFrom(phi);
context()->ReplaceAllUsesWith(phi->result_id(), select->result_id());
to_kill.push_back(phi);

View File

@ -92,7 +92,7 @@ void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
if (line_inst != nullptr) {
newStore->dbg_line_insts().push_back(*line_inst);
newStore->AddDebugLine(line_inst);
}
newStore->SetDebugScope(dbg_scope);
(*block_ptr)->AddInstruction(std::move(newStore));
@ -106,7 +106,7 @@ void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id,
new Instruction(context(), SpvOpLoad, type_id, resultId,
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}}));
if (line_inst != nullptr) {
newLoad->dbg_line_insts().push_back(*line_inst);
newLoad->AddDebugLine(line_inst);
}
newLoad->SetDebugScope(dbg_scope);
(*block_ptr)->AddInstruction(std::move(newLoad));

View File

@ -73,8 +73,6 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
unique_id_(c->TakeNextUniqueId()),
dbg_line_insts_(std::move(dbg_line)),
dbg_scope_(kNoDebugScope, kNoInlinedAt) {
assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) &&
"Op(No)Line attaching to Op(No)Line found");
for (uint32_t i = 0; i < inst.num_operands; ++i) {
const auto& current_payload = inst.operands[i];
std::vector<uint32_t> words(
@ -82,6 +80,8 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
inst.words + current_payload.offset + current_payload.num_words);
operands_.emplace_back(current_payload.type, std::move(words));
}
assert((!IsLineInst() || dbg_line.empty()) &&
"Op(No)Line attaching to Op(No)Line found");
}
Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
@ -158,6 +158,10 @@ Instruction* Instruction::Clone(IRContext* c) const {
clone->unique_id_ = c->TakeNextUniqueId();
clone->operands_ = operands_;
clone->dbg_line_insts_ = dbg_line_insts_;
for (auto& i : clone->dbg_line_insts_) {
i.unique_id_ = c->TakeNextUniqueId();
if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId());
}
clone->dbg_scope_ = dbg_scope_;
return clone;
}
@ -511,7 +515,7 @@ void Instruction::UpdateLexicalScope(uint32_t scope) {
for (auto& i : dbg_line_insts_) {
i.dbg_scope_.SetLexicalScope(scope);
}
if (!IsDebugLineInst(opcode()) &&
if (!IsLineInst() &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
@ -522,24 +526,61 @@ void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
for (auto& i : dbg_line_insts_) {
i.dbg_scope_.SetInlinedAt(new_inlined_at);
}
if (!IsDebugLineInst(opcode()) &&
if (!IsLineInst() &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
}
void Instruction::ClearDbgLineInsts() {
if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
auto def_use_mgr = context()->get_def_use_mgr();
for (auto& l_inst : dbg_line_insts_) def_use_mgr->ClearInst(&l_inst);
}
clear_dbg_line_insts();
}
void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
if (from == nullptr) return;
clear_dbg_line_insts();
ClearDbgLineInsts();
if (!from->dbg_line_insts().empty())
dbg_line_insts().push_back(from->dbg_line_insts().back());
AddDebugLine(&from->dbg_line_insts().back());
SetDebugScope(from->GetDebugScope());
if (!IsDebugLineInst(opcode()) &&
if (!IsLineInst() &&
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
}
}
void Instruction::AddDebugLine(const Instruction* inst) {
dbg_line_insts_.push_back(*inst);
dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId();
if (inst->IsDebugLineInst())
dbg_line_insts_.back().SetResultId(context_->TakeNextId());
if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back());
}
bool Instruction::IsDebugLineInst() const {
NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) ||
(ext_opt == NonSemanticShaderDebugInfo100DebugNoLine));
}
bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); }
bool Instruction::IsLine() const {
if (opcode() == SpvOpLine) return true;
NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
return ext_opt == NonSemanticShaderDebugInfo100DebugLine;
}
bool Instruction::IsNoLine() const {
if (opcode() == SpvOpNoLine) return true;
NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine;
}
Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& inst) {
inst.get()->InsertBefore(this);
return inst.release();
@ -629,12 +670,12 @@ NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
return NonSemanticShaderDebugInfo100InstructionsMax;
}
if (!context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) {
if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
return NonSemanticShaderDebugInfo100InstructionsMax;
}
if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo()) {
context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
return NonSemanticShaderDebugInfo100InstructionsMax;
}
@ -649,16 +690,16 @@ CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
const uint32_t opencl_set_id =
context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
const uint32_t vulkan_set_id =
context()->get_feature_mgr()->GetExtInstImportId_Vulkan100DebugInfo();
const uint32_t shader_set_id =
context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
if (!opencl_set_id && !vulkan_set_id) {
if (!opencl_set_id && !shader_set_id) {
return CommonDebugInfoInstructionsMax;
}
const uint32_t used_set_id = GetSingleWordInOperand(kExtInstSetIdInIdx);
if (used_set_id != opencl_set_id && used_set_id != vulkan_set_id) {
if (used_set_id != opencl_set_id && used_set_id != shader_set_id) {
return CommonDebugInfoInstructionsMax;
}

View File

@ -252,11 +252,6 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Clear line-related debug instructions attached to this instruction.
void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
// Set line-related debug instructions.
void set_dbg_line_insts(const std::vector<Instruction>& lines) {
dbg_line_insts_ = lines;
}
// Same semantics as in the base class except the list the InstructionList
// containing |pos| will now assume ownership of |this|.
// inline void MoveBefore(Instruction* pos);
@ -306,8 +301,21 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Sets DebugScope.
inline void SetDebugScope(const DebugScope& scope);
inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
// Add debug line inst. Renew result id if Debug[No]Line
void AddDebugLine(const Instruction* inst);
// Updates DebugInlinedAt of DebugScope and OpLine.
void UpdateDebugInlinedAt(uint32_t new_inlined_at);
// Clear line-related debug instructions attached to this instruction
// along with def-use entries.
void ClearDbgLineInsts();
// Return true if Shader100:Debug[No]Line
bool IsDebugLineInst() const;
// Return true if Op[No]Line or Shader100:Debug[No]Line
bool IsLineInst() const;
// Return true if OpLine or Shader100:DebugLine
bool IsLine() const;
// Return true if OpNoLine or Shader100:DebugNoLine
bool IsNoLine() const;
inline uint32_t GetDebugInlinedAt() const {
return dbg_scope_.GetInlinedAt();
}
@ -609,9 +617,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
uint32_t unique_id_; // Unique instruction id
// All logical operands, including result type id and result id.
OperandList operands_;
// Opline and OpNoLine instructions preceding this instruction. Note that for
// Instructions representing OpLine or OpNonLine itself, this field should be
// empty.
// Op[No]Line or Debug[No]Line instructions preceding this instruction. Note
// that for Instructions representing Op[No]Line or Debug[No]Line themselves,
// this field should be empty.
std::vector<Instruction> dbg_line_insts_;
// DebugScope that wraps this instruction.

View File

@ -171,7 +171,9 @@ Instruction* IRContext::KillInst(Instruction* inst) {
KillOperandFromDebugInstructions(inst);
if (AreAnalysesValid(kAnalysisDefUse)) {
get_def_use_mgr()->ClearInst(inst);
analysis::DefUseManager* def_use_mgr = get_def_use_mgr();
def_use_mgr->ClearInst(inst);
for (auto& l_inst : inst->dbg_line_insts()) def_use_mgr->ClearInst(&l_inst);
}
if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
instr_to_block_.erase(inst);
@ -218,6 +220,8 @@ Instruction* IRContext::KillInst(Instruction* inst) {
void IRContext::CollectNonSemanticTree(
Instruction* inst, std::unordered_set<Instruction*>* to_kill) {
if (!inst->HasResultId()) return;
// Debug[No]Line result id is not used, so we are done
if (inst->IsDebugLineInst()) return;
std::vector<Instruction*> work_list;
std::unordered_set<Instruction*> seen;
work_list.push_back(inst);
@ -930,7 +934,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) {
while (line_inst != nullptr) { // Stop at the beginning of the basic block.
if (!line_inst->dbg_line_insts().empty()) {
line_inst = &line_inst->dbg_line_insts().back();
if (line_inst->opcode() == SpvOpNoLine) {
if (line_inst->IsNoLine()) {
line_inst = nullptr;
}
break;

View File

@ -98,6 +98,7 @@ class IRContext {
module_(new Module()),
consumer_(std::move(c)),
def_use_mgr_(nullptr),
feature_mgr_(nullptr),
valid_analyses_(kAnalysisNone),
constant_mgr_(nullptr),
type_mgr_(nullptr),
@ -116,6 +117,7 @@ class IRContext {
module_(std::move(m)),
consumer_(std::move(c)),
def_use_mgr_(nullptr),
feature_mgr_(nullptr),
valid_analyses_(kAnalysisNone),
type_mgr_(nullptr),
id_to_name_(nullptr),
@ -769,6 +771,8 @@ class IRContext {
// The instruction decoration manager for |module_|.
std::unique_ptr<analysis::DecorationManager> decoration_mgr_;
// The feature manager for |module_|.
std::unique_ptr<FeatureManager> feature_mgr_;
// A map from instructions to the basic block they belong to. This mapping is

View File

@ -19,6 +19,7 @@
#include "DebugInfo.h"
#include "OpenCLDebugInfo100.h"
#include "source/ext_inst.h"
#include "source/opt/ir_context.h"
#include "source/opt/log.h"
#include "source/opt/reflect.h"
#include "source/util/make_unique.h"
@ -37,20 +38,32 @@ IrLoader::IrLoader(const MessageConsumer& consumer, Module* m)
inst_index_(0),
last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
bool IsLineInst(const spv_parsed_instruction_t* inst) {
const auto opcode = static_cast<SpvOp>(inst->opcode);
if (IsOpLineInst(opcode)) return true;
if (opcode != SpvOpExtInst) return false;
if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100)
return false;
const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
return ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine;
}
bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
++inst_index_;
const auto opcode = static_cast<SpvOp>(inst->opcode);
if (IsDebugLineInst(opcode)) {
if (IsLineInst(inst)) {
module()->SetContainsDebugInfo();
last_line_inst_.reset();
dbg_line_info_.push_back(
Instruction(module()->context(), *inst, last_dbg_scope_));
dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_);
return true;
}
// If it is a DebugScope or DebugNoScope of debug extension, we do not
// create a new instruction, but simply keep the information in
// struct DebugScope.
const auto opcode = static_cast<SpvOp>(inst->opcode);
if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) {
const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
@ -96,14 +109,20 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
new Instruction(module()->context(), *inst, std::move(dbg_line_info_)));
if (!spv_inst->dbg_line_insts().empty()) {
if (extra_line_tracking_ &&
(spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine)) {
(!spv_inst->dbg_line_insts().back().IsNoLine())) {
last_line_inst_ = std::unique_ptr<Instruction>(
spv_inst->dbg_line_insts().back().Clone(module()->context()));
if (last_line_inst_->IsDebugLineInst())
last_line_inst_->SetResultId(module()->context()->TakeNextId());
}
dbg_line_info_.clear();
} else if (last_line_inst_ != nullptr) {
last_line_inst_->SetDebugScope(last_dbg_scope_);
spv_inst->dbg_line_insts().push_back(*last_line_inst_);
last_line_inst_ = std::unique_ptr<Instruction>(
spv_inst->dbg_line_insts().back().Clone(module()->context()));
if (last_line_inst_->IsDebugLineInst())
last_line_inst_->SetResultId(module()->context()->TakeNextId());
}
const char* src = source_.c_str();

View File

@ -760,7 +760,7 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
IRContext::Analysis::kAnalysisInstrToBlockMapping);
Instruction* new_branch = builder.AddBranch(new_target);
new_branch->set_dbg_line_insts(lines);
if (!lines.empty()) new_branch->AddDebugLine(&lines.back());
new_branch->SetDebugScope(scope);
}
@ -873,6 +873,10 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
def_use_mgr->AnalyzeInstDefUse(basic_block->GetLabelInst());
for (Instruction& inst : *basic_block) {
// Do def/use analysis on new lines
for (auto& line : inst.dbg_line_insts())
def_use_mgr->AnalyzeInstDefUse(&line);
uint32_t old_id = inst.result_id();
// Ignore stores etc.

View File

@ -151,16 +151,15 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
this](const Instruction* i) {
// Skip emitting line instructions between merge and branch instructions.
auto opcode = i->opcode();
if (between_merge_and_branch &&
(opcode == SpvOpLine || opcode == SpvOpNoLine)) {
if (between_merge_and_branch && i->IsLineInst()) {
return;
}
between_merge_and_branch = false;
if (last_line_inst != nullptr) {
// If the current instruction is OpLine and it is the same with
// the last line instruction that is still effective (can be applied
// If the current instruction is OpLine or DebugLine and it is the same
// as the last line instruction that is still effective (can be applied
// to the next instruction), we skip writing the current instruction.
if (opcode == SpvOpLine) {
if (i->IsLine()) {
uint32_t operand_index = 0;
if (last_line_inst->WhileEachInOperand(
[&operand_index, i](const uint32_t* word) {
@ -169,11 +168,22 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
})) {
return;
}
} else if (opcode != SpvOpNoLine && i->dbg_line_insts().empty()) {
} else if (!i->IsNoLine() && i->dbg_line_insts().empty()) {
// If the current instruction does not have the line information,
// the last line information is not effective any more. Emit OpNoLine
// to specify it.
binary->push_back((1 << 16) | static_cast<uint16_t>(SpvOpNoLine));
// or DebugNoLine to specify it.
uint32_t shader_set_id = context()
->get_feature_mgr()
->GetExtInstImportId_Shader100DebugInfo();
if (shader_set_id != 0) {
binary->push_back((5 << 16) | static_cast<uint16_t>(SpvOpExtInst));
binary->push_back(context()->get_type_mgr()->GetVoidTypeId());
binary->push_back(context()->TakeNextId());
binary->push_back(shader_set_id);
binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine);
} else {
binary->push_back((1 << 16) | static_cast<uint16_t>(SpvOpNoLine));
}
last_line_inst = nullptr;
}
}
@ -181,7 +191,7 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
if (opcode == SpvOpLabel) {
between_label_and_phi_var = true;
} else if (opcode != SpvOpVariable && opcode != SpvOpPhi &&
opcode != SpvOpLine && opcode != SpvOpNoLine) {
!spvtools::opt::IsOpLineInst(opcode)) {
between_label_and_phi_var = false;
}
@ -190,7 +200,7 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
if (scope != last_scope) {
// Can only emit nonsemantic instructions after all phi instructions
// in a block so don't emit scope instructions before phi instructions
// for Vulkan.NonSemantic.DebugInfo.100.
// for NonSemantic.Shader.DebugInfo.100.
if (!between_label_and_phi_var ||
context()
->get_feature_mgr()
@ -206,18 +216,19 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const {
i->ToBinaryWithoutAttachedDebugInsts(binary);
}
// Update the last line instruction.
if (spvOpcodeIsBlockTerminator(opcode) || opcode == SpvOpNoLine) {
if (spvOpcodeIsBlockTerminator(opcode) || i->IsNoLine()) {
last_line_inst = nullptr;
} else if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) {
between_merge_and_branch = true;
last_line_inst = nullptr;
} else if (opcode == SpvOpLine) {
} else if (i->IsLine()) {
last_line_inst = i;
}
};
ForEachInst(write_inst, true);
// We create new instructions for DebugScope. The bound must be updated.
// We create new instructions for DebugScope and DebugNoLine. The bound must
// be updated.
binary->data()[bound_idx] = header_.bound;
}

View File

@ -43,8 +43,8 @@ Pass::Status Pass::Run(IRContext* ctx) {
if (status == Status::SuccessWithChange) {
ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses());
}
assert((status == Status::Failure || ctx->IsConsistent()) &&
"An analysis in the context is out of date.");
if (!(status == Status::Failure || ctx->IsConsistent()))
assert(false && "An analysis in the context is out of date.");
return status;
}

View File

@ -34,7 +34,7 @@ inline bool IsDebug2Inst(SpvOp opcode) {
inline bool IsDebug3Inst(SpvOp opcode) {
return opcode == SpvOpModuleProcessed;
}
inline bool IsDebugLineInst(SpvOp opcode) {
inline bool IsOpLineInst(SpvOp opcode) {
return opcode == SpvOpLine || opcode == SpvOpNoLine;
}
inline bool IsAnnotationInst(SpvOp opcode) {

View File

@ -71,10 +71,10 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function,
function->ForEachInst(
[model, &modified, &last_line_dbg_inst, this](Instruction* inst) {
// Track the debug information so we can have a meaningful message.
if (inst->opcode() == SpvOpLabel || inst->opcode() == SpvOpNoLine) {
if (inst->opcode() == SpvOpLabel || inst->IsNoLine()) {
last_line_dbg_inst = nullptr;
return;
} else if (inst->opcode() == SpvOpLine) {
} else if (inst->IsLine()) {
last_line_dbg_inst = inst;
return;
}
@ -100,8 +100,18 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function,
ReplaceInstruction(inst, nullptr, 0, 0);
} else {
// Get the name of the source file.
Instruction* file_name = context()->get_def_use_mgr()->GetDef(
last_line_dbg_inst->GetSingleWordInOperand(0));
uint32_t file_name_id = 0;
if (last_line_dbg_inst->opcode() == SpvOpLine) {
file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0);
} else { // Shader100::DebugLine
uint32_t debug_source_id =
last_line_dbg_inst->GetSingleWordInOperand(2);
Instruction* debug_source_inst =
context()->get_def_use_mgr()->GetDef(debug_source_id);
file_name_id = debug_source_inst->GetSingleWordInOperand(2);
}
Instruction* file_name =
context()->get_def_use_mgr()->GetDef(file_name_id);
const char* source = reinterpret_cast<const char*>(
&file_name->GetInOperand(0).words[0]);

View File

@ -27,6 +27,7 @@
static const uint32_t kDebugValueOperandValueIndex = 5;
static const uint32_t kDebugValueOperandExpressionIndex = 6;
static const uint32_t kDebugDeclareOperandVariableIndex = 5;
namespace spvtools {
namespace opt {
@ -868,6 +869,11 @@ bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const {
case SpvOpImageTexelPointer:
if (!CheckImageTexelPointer(index)) ok = false;
break;
case SpvOpExtInst:
if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare ||
!CheckDebugDeclare(index))
ok = false;
break;
default:
ok = false;
break;
@ -898,6 +904,12 @@ bool ScalarReplacementPass::CheckStore(const Instruction* inst,
return false;
return true;
}
bool ScalarReplacementPass::CheckDebugDeclare(uint32_t index) const {
if (index != kDebugDeclareOperandVariableIndex) return false;
return true;
}
bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const {
if (max_num_elements_ == 0) {
return false;

View File

@ -142,6 +142,9 @@ class ScalarReplacementPass : public Pass {
// of |inst| and the store is not to volatile memory.
bool CheckStore(const Instruction* inst, uint32_t index) const;
// Returns true if the DebugDeclare can be scalarized at |index|.
bool CheckDebugDeclare(uint32_t index) const;
// Returns true if |index| is the pointer operand of an OpImageTexelPointer
// instruction.
bool CheckImageTexelPointer(uint32_t index) const;

View File

@ -58,6 +58,8 @@ spv_result_t ModuleScopedInstructions(ValidationState_t& _,
ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugValue ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine ||
ext_inst_key ==
NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
local_debug_info = true;
@ -263,6 +265,8 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugValue ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine ||
ext_inst_key ==
NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
local_debug_info = true;

View File

@ -7141,7 +7141,15 @@ TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) {
%uint_16 = OpConstant %uint 16
%uint_17 = OpConstant %uint 17
%uint_19 = OpConstant %uint 19
%uint_20 = OpConstant %uint 20
%uint_21 = OpConstant %uint 21
%uint_25 = OpConstant %uint 25
%uint_29 = OpConstant %uint 29
%uint_30 = OpConstant %uint 30
%uint_35 = OpConstant %uint 35
%uint_41 = OpConstant %uint 41
%uint_48 = OpConstant %uint 48
%uint_53 = OpConstant %uint 53
%uint_64 = OpConstant %uint 64
%45 = OpTypeFunction %void
%PS_INPUT = OpTypeStruct %v2float
@ -7198,20 +7206,33 @@ TEST_F(AggressiveDCETest, ShaderDebugInfoKeepInFunctionElimStoreVar) {
;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69
;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52
%300 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30
;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30
%88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
%89 = OpLoad %v2float %88
%301 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
OpStore %79 %89
;CHECK-NOT: OpStore %79 %89
%302 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
%106 = OpExtInst %void %1 DebugValue %70 %89 %52
;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52
%303 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_32
%91 = OpLoad %type_2d_image %g_tColor
%304 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_41 %uint_48
%92 = OpLoad %type_sampler %g_sAniso
%305 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_53
%94 = OpSampledImage %type_sampled_image %91 %92
%95 = OpImageSampleImplicitLod %v4float %94 %89 None
%306 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_5 %uint_53
%96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
OpStore %96 %95
%307 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_12 %uint_20
%97 = OpLoad %PS_OUTPUT %78
%308 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_5 %uint_20
OpStore %81 %97
%309 = OpExtInst %void %1 DebugNoLine
;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoLine
%111 = OpExtInst %void %1 DebugNoScope
;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope
%100 = OpCompositeExtract %v4float %97 0

View File

@ -378,6 +378,185 @@ OpFunctionEnd)";
SinglePassRunAndMatch<LoopUnroller>(text, true);
}
TEST_F(PassClassTest, SimpleFullyUnrollWithShaderDebugInstructions) {
// We must preserve the debug information including
// NonSemantic.Shader.DebugInfo.100 instructions and DebugLine instructions.
const std::string text = R"(
OpCapability Shader
OpExtension "SPV_KHR_non_semantic_info"
%1 = OpExtInstImport "GLSL.std.450"
%ext = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
%file_name = OpString "test"
%float_name = OpString "float"
%main_name = OpString "main"
%f_name = OpString "f"
%i_name = OpString "i"
OpName %2 "main"
OpName %5 "x"
OpName %3 "c"
OpDecorate %3 Location 0
%6 = OpTypeVoid
%7 = OpTypeFunction %6
%8 = OpTypeInt 32 1
%9 = OpTypePointer Function %8
%10 = OpConstant %8 0
%11 = OpConstant %8 4
%12 = OpTypeBool
%13 = OpTypeFloat 32
%14 = OpTypeInt 32 0
%uint_0 = OpConstant %14 0
%uint_1 = OpConstant %14 1
%uint_2 = OpConstant %14 2
%uint_3 = OpConstant %14 3
%uint_4 = OpConstant %14 4
%uint_5 = OpConstant %14 5
%uint_6 = OpConstant %14 6
%uint_7 = OpConstant %14 7
%uint_8 = OpConstant %14 8
%uint_10 = OpConstant %14 10
%uint_32 = OpConstant %14 32
%15 = OpConstant %14 4
%16 = OpTypeArray %13 %15
%17 = OpTypePointer Function %16
%18 = OpConstant %13 1
%19 = OpTypePointer Function %13
%20 = OpConstant %8 1
%21 = OpTypeVector %13 4
%22 = OpTypePointer Output %21
%3 = OpVariable %22 Output
%null_expr = OpExtInst %6 %ext DebugExpression
%deref = OpExtInst %6 %ext DebugOperation %uint_0
%deref_expr = OpExtInst %6 %ext DebugExpression %deref
%src = OpExtInst %6 %ext DebugSource %file_name
%cu = OpExtInst %6 %ext DebugCompilationUnit %uint_1 %uint_4 %src %uint_5
%dbg_tf = OpExtInst %6 %ext DebugTypeBasic %float_name %uint_32 %uint_3 %uint_0
%dbg_v4f = OpExtInst %6 %ext DebugTypeVector %dbg_tf %uint_4
%main_ty = OpExtInst %6 %ext DebugTypeFunction %uint_3 %dbg_v4f %dbg_v4f
%dbg_main = OpExtInst %6 %ext DebugFunction %main_name %main_ty %src %uint_0 %uint_0 %cu %main_name %uint_3 %uint_10
%bb = OpExtInst %6 %ext DebugLexicalBlock %src %uint_0 %uint_0 %dbg_main
%dbg_f = OpExtInst %6 %ext DebugLocalVariable %f_name %dbg_v4f %src %uint_0 %uint_0 %dbg_main %uint_4
%dbg_i = OpExtInst %6 %ext DebugLocalVariable %i_name %dbg_v4f %src %uint_1 %uint_0 %bb %uint_4
; CHECK: [[f:%\w+]] = OpString "f"
; CHECK: [[i:%\w+]] = OpString "i"
; CHECK: [[int_0:%\w+]] = OpConstant {{%\w+}} 0
; CHECK: [[null_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression
; CHECK: [[deref:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugOperation %uint_0
; CHECK: [[deref_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression [[deref]]
; CHECK: [[dbg_fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction
; CHECK: [[dbg_bb:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLexicalBlock
; CHECK: [[dbg_f:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[f]] {{%\w+}} {{%\w+}} %uint_0 %uint_0 [[dbg_fn]]
; CHECK: [[dbg_i:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[i]] {{%\w+}} {{%\w+}} %uint_1 %uint_0 [[dbg_bb]]
%2 = OpFunction %6 None %7
%23 = OpLabel
; The first block has DebugDeclare and DebugValue with Deref
;
; CHECK: OpLabel
; CHECK: %x = OpVariable %_ptr_Function__arr_float_uint_4_0 Function
; CHECK: OpBranch
; CHECK: OpLabel
; CHECK: DebugScope [[dbg_fn]]
; CHECK: DebugValue [[dbg_f]] [[int_0]] [[null_expr]]
; CHECK: OpBranch
; CHECK: DebugScope [[dbg_fn]]
; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1
; CHECK: OpSLessThan
; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0
; CHECK: OpBranch
; CHECK: OpLabel
; CHECK: DebugScope [[dbg_bb]]
; CHECK: DebugDeclare [[dbg_f]] %x [[null_expr]]
; CHECK: DebugValue [[dbg_i]] %x [[deref_expr]]
; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0
;
; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0
; CHECK: [[add:%\w+]] = OpIAdd
; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]]
; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0
; Other blocks do not have DebugDeclare and DebugValue with Deref
;
; CHECK: DebugScope [[dbg_fn]]
; CHECK: DebugLine {{%\w+}} %uint_1 %uint_1 %uint_1 %uint_1
; CHECK: OpSLessThan
; CHECK: DebugLine {{%\w+}} %uint_2 %uint_2 %uint_0 %uint_0
; CHECK: OpBranch
; CHECK: OpLabel
;
; CHECK: DebugScope [[dbg_bb]]
; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]]
; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]]
; CHECK: DebugLine {{%\w+}} %uint_3 %uint_3 %uint_0 %uint_0
;
; CHECK: DebugLine {{%\w+}} %uint_6 %uint_6 %uint_0 %uint_0
; CHECK: [[add:%\w+]] = OpIAdd
; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]]
; CHECK: DebugLine {{%\w+}} %uint_7 %uint_7 %uint_0 %uint_0
;
; CHECK-NOT: DebugDeclare [[dbg_f]] %x [[null_expr]]
; CHECK-NOT: DebugValue [[dbg_i]] %x [[deref_expr]]
; CHECK: DebugScope [[dbg_fn]]
; CHECK: DebugLine {{%\w+}} %uint_8 %uint_8 %uint_0 %uint_0
; CHECK: OpReturn
%5 = OpVariable %17 Function
OpBranch %24
%24 = OpLabel
%35 = OpPhi %8 %10 %23 %34 %26
%s1 = OpExtInst %6 %ext DebugScope %dbg_main
%d10 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_0 %uint_0
%value0 = OpExtInst %6 %ext DebugValue %dbg_f %35 %null_expr
OpLoopMerge %25 %26 Unroll
OpBranch %27
%27 = OpLabel
%s2 = OpExtInst %6 %ext DebugScope %dbg_main
%d1 = OpExtInst %6 %ext DebugLine %file_name %uint_1 %uint_1 %uint_1 %uint_1
%29 = OpSLessThan %12 %35 %11
%d2 = OpExtInst %6 %ext DebugLine %file_name %uint_2 %uint_2 %uint_0 %uint_0
OpBranchConditional %29 %30 %25
%30 = OpLabel
%s3 = OpExtInst %6 %ext DebugScope %bb
%decl0 = OpExtInst %6 %ext DebugDeclare %dbg_f %5 %null_expr
%decl1 = OpExtInst %6 %ext DebugValue %dbg_i %5 %deref_expr
%d3 = OpExtInst %6 %ext DebugLine %file_name %uint_3 %uint_3 %uint_0 %uint_0
%32 = OpAccessChain %19 %5 %35
%d4 = OpExtInst %6 %ext DebugLine %file_name %uint_4 %uint_4 %uint_0 %uint_0
OpStore %32 %18
%d5 = OpExtInst %6 %ext DebugLine %file_name %uint_5 %uint_5 %uint_0 %uint_0
OpBranch %26
%26 = OpLabel
%s4 = OpExtInst %6 %ext DebugScope %dbg_main
%d6 = OpExtInst %6 %ext DebugLine %file_name %uint_6 %uint_6 %uint_0 %uint_0
%34 = OpIAdd %8 %35 %20
%value1 = OpExtInst %6 %ext DebugValue %dbg_f %34 %null_expr
%d7 = OpExtInst %6 %ext DebugLine %file_name %uint_7 %uint_7 %uint_0 %uint_0
OpBranch %24
%25 = OpLabel
%s5 = OpExtInst %6 %ext DebugScope %dbg_main
%d8 = OpExtInst %6 %ext DebugLine %file_name %uint_8 %uint_8 %uint_0 %uint_0
OpReturn
OpFunctionEnd)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
Module* module = context->module();
EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
<< text << std::endl;
LoopUnroller loop_unroller;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
SinglePassRunAndMatch<LoopUnroller>(text, true);
}
template <int factor>
class PartialUnrollerTestPass : public Pass {
public: