Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2020-05-04 21:20:55 -07:00
parent 7f7c8a9bc0
commit bc166c21b5
19 changed files with 545 additions and 210 deletions

View File

@ -1 +1 @@
"v2020.3-dev", "SPIRV-Tools v2020.3-dev ad8e2138c256f6fb0b38e883fe1ae0e2a59c7620"
"v2020.3-dev", "SPIRV-Tools v2020.3-dev 7f7c8a9bc032be95861952467a66f0c77560ec04"

View File

@ -430,6 +430,9 @@ class FactManager::DataSynonymAndIdEquationFacts {
uint32_t maximum_equivalence_class_size);
private:
using OperationSet =
std::unordered_set<Operation, OperationHash, OperationEquals>;
// Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses
// into sub-components of the data descriptors, if they are composites, to
// record that their components are pairwise-synonymous.
@ -448,6 +451,8 @@ class FactManager::DataSynonymAndIdEquationFacts {
opt::IRContext* context, const protobufs::DataDescriptor& dd1,
const protobufs::DataDescriptor& dd2) const;
OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const;
// Requires that |lhs_dd| and every element of |rhs_dds| is present in the
// |synonymous_| equivalence relation, but is not necessarily its own
// representative. Records the fact that the equation
@ -480,9 +485,7 @@ class FactManager::DataSynonymAndIdEquationFacts {
// All data descriptors occurring in equations are required to be present in
// the |synonymous_| equivalence relation, and to be their own representatives
// in that relation.
std::unordered_map<
const protobufs::DataDescriptor*,
std::unordered_set<Operation, OperationHash, OperationEquals>>
std::unordered_map<const protobufs::DataDescriptor*, OperationSet>
id_equations_;
};
@ -520,6 +523,16 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
rhs_dd_ptrs, context);
}
FactManager::DataSynonymAndIdEquationFacts::OperationSet
FactManager::DataSynonymAndIdEquationFacts::GetEquations(
const protobufs::DataDescriptor* lhs) const {
auto existing = id_equations_.find(lhs);
if (existing == id_equations_.end()) {
return OperationSet();
}
return existing->second;
}
void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
@ -538,9 +551,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
if (id_equations_.count(lhs_dd_representative) == 0) {
// We have not seen an equation with this LHS before, so associate the LHS
// with an initially empty set.
id_equations_.insert(
{lhs_dd_representative,
std::unordered_set<Operation, OperationHash, OperationEquals>()});
id_equations_.insert({lhs_dd_representative, OperationSet()});
}
{
@ -562,44 +573,29 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
switch (opcode) {
case SpvOpIAdd: {
// Equation form: "a = b + c"
{
auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
if (existing_first_operand_equations != id_equations_.end()) {
for (auto equation : existing_first_operand_equations->second) {
if (equation.opcode == SpvOpISub) {
// Equation form: "a = (d - e) + c"
if (synonymous_.IsEquivalent(*equation.operands[1],
*rhs_dds[1])) {
// Equation form: "a = (d - c) + c"
// We can thus infer "a = d"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
context);
}
if (synonymous_.IsEquivalent(*equation.operands[0],
*rhs_dds[1])) {
// Equation form: "a = (c - e) + c"
// We can thus infer "a = -e"
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
{equation.operands[1]}, context);
}
}
for (auto equation : GetEquations(rhs_dds[0])) {
if (equation.opcode == SpvOpISub) {
// Equation form: "a = (d - e) + c"
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
// Equation form: "a = (d - c) + c"
// We can thus infer "a = d"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
}
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
// Equation form: "a = (c - e) + c"
// We can thus infer "a = -e"
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
{equation.operands[1]}, context);
}
}
}
{
auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
if (existing_second_operand_equations != id_equations_.end()) {
for (auto equation : existing_second_operand_equations->second) {
if (equation.opcode == SpvOpISub) {
// Equation form: "a = b + (d - e)"
if (synonymous_.IsEquivalent(*equation.operands[1],
*rhs_dds[0])) {
// Equation form: "a = b + (d - b)"
// We can thus infer "a = d"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
context);
}
}
for (auto equation : GetEquations(rhs_dds[1])) {
if (equation.opcode == SpvOpISub) {
// Equation form: "a = b + (d - e)"
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
// Equation form: "a = b + (d - b)"
// We can thus infer "a = d"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
}
}
}
@ -607,73 +603,54 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
}
case SpvOpISub: {
// Equation form: "a = b - c"
{
auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
if (existing_first_operand_equations != id_equations_.end()) {
for (auto equation : existing_first_operand_equations->second) {
if (equation.opcode == SpvOpIAdd) {
// Equation form: "a = (d + e) - c"
if (synonymous_.IsEquivalent(*equation.operands[0],
*rhs_dds[1])) {
// Equation form: "a = (c + e) - c"
// We can thus infer "a = e"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
context);
}
if (synonymous_.IsEquivalent(*equation.operands[1],
*rhs_dds[1])) {
// Equation form: "a = (d + c) - c"
// We can thus infer "a = d"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
context);
}
}
for (auto equation : GetEquations(rhs_dds[0])) {
if (equation.opcode == SpvOpIAdd) {
// Equation form: "a = (d + e) - c"
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
// Equation form: "a = (c + e) - c"
// We can thus infer "a = e"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
}
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
// Equation form: "a = (d + c) - c"
// We can thus infer "a = d"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
}
}
if (equation.opcode == SpvOpISub) {
// Equation form: "a = (d - e) - c"
if (synonymous_.IsEquivalent(*equation.operands[0],
*rhs_dds[1])) {
// Equation form: "a = (c - e) - c"
// We can thus infer "a = -e"
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
{equation.operands[1]}, context);
}
}
if (equation.opcode == SpvOpISub) {
// Equation form: "a = (d - e) - c"
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
// Equation form: "a = (c - e) - c"
// We can thus infer "a = -e"
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
{equation.operands[1]}, context);
}
}
}
{
auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
if (existing_second_operand_equations != id_equations_.end()) {
for (auto equation : existing_second_operand_equations->second) {
if (equation.opcode == SpvOpIAdd) {
// Equation form: "a = b - (d + e)"
if (synonymous_.IsEquivalent(*equation.operands[0],
*rhs_dds[0])) {
// Equation form: "a = b - (b + e)"
// We can thus infer "a = -e"
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
{equation.operands[1]}, context);
}
if (synonymous_.IsEquivalent(*equation.operands[1],
*rhs_dds[0])) {
// Equation form: "a = b - (d + b)"
// We can thus infer "a = -d"
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
{equation.operands[0]}, context);
}
}
if (equation.opcode == SpvOpISub) {
// Equation form: "a = b - (d - e)"
if (synonymous_.IsEquivalent(*equation.operands[0],
*rhs_dds[0])) {
// Equation form: "a = b - (b - e)"
// We can thus infer "a = e"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
context);
}
}
for (auto equation : GetEquations(rhs_dds[1])) {
if (equation.opcode == SpvOpIAdd) {
// Equation form: "a = b - (d + e)"
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
// Equation form: "a = b - (b + e)"
// We can thus infer "a = -e"
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
{equation.operands[1]}, context);
}
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
// Equation form: "a = b - (d + b)"
// We can thus infer "a = -d"
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
{equation.operands[0]}, context);
}
}
if (equation.opcode == SpvOpISub) {
// Equation form: "a = b - (d - e)"
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
// Equation form: "a = b - (b - e)"
// We can thus infer "a = e"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
}
}
}
@ -682,14 +659,11 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
case SpvOpLogicalNot:
case SpvOpSNegate: {
// Equation form: "a = !b" or "a = -b"
auto existing_equations = id_equations_.find(rhs_dds[0]);
if (existing_equations != id_equations_.end()) {
for (auto equation : existing_equations->second) {
if (equation.opcode == opcode) {
// Equation form: "a = !!b" or "a = -(-b)"
// We can thus infer "a = b"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
}
for (auto equation : GetEquations(rhs_dds[0])) {
if (equation.opcode == opcode) {
// Equation form: "a = !!b" or "a = -(-b)"
// We can thus infer "a = b"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
}
}
break;
@ -1116,9 +1090,7 @@ void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
// equations about |still_representative|; create an empty set of equations
// if this is the case.
if (!id_equations_.count(still_representative)) {
id_equations_.insert(
{still_representative,
std::unordered_set<Operation, OperationHash, OperationEquals>()});
id_equations_.insert({still_representative, OperationSet()});
}
auto still_representative_id_equations =
id_equations_.find(still_representative);

View File

@ -68,9 +68,8 @@ void FuzzerPassAddStores::Apply() {
// Not a pointer.
return false;
}
if (type_inst->GetSingleWordInOperand(0) ==
SpvStorageClassInput) {
// Read-only: cannot store to it.
if (instruction->IsReadOnlyPointer()) {
// Read only: cannot store to it.
return false;
}
switch (instruction->result_id()) {

View File

@ -107,7 +107,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
}
// Initial binary should be valid.
if (!tools.Validate(&binary_in[0], binary_in.size())) {
if (!tools.Validate(&binary_in[0], binary_in.size(),
impl_->validator_options)) {
impl_->consumer(SPV_MSG_INFO, nullptr, {},
"Initial binary is invalid; stopping.");
return Shrinker::ShrinkerResultStatus::kInitialBinaryInvalid;

View File

@ -810,8 +810,10 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
->GetType(index_type_inst->result_id())
->AsInteger();
if (index_inst->opcode() != SpvOpConstant) {
// The index is non-constant so we need to clamp it.
if (index_inst->opcode() != SpvOpConstant ||
index_inst->GetSingleWordInOperand(0) >= bound) {
// The index is either non-constant or an out-of-bounds constant, so we
// need to clamp it.
assert(should_be_composite_type->opcode() != SpvOpTypeStruct &&
"Access chain indices into structures are required to be "
"constants.");
@ -864,21 +866,6 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
access_chain_inst->SetInOperand(index, {select_id});
fuzzerutil::UpdateModuleIdBound(ir_context, compare_id);
fuzzerutil::UpdateModuleIdBound(ir_context, select_id);
} else {
// TODO(afd): At present the SPIR-V spec is not clear on whether
// statically out-of-bounds indices mean that a module is invalid (so
// that it should be rejected by the validator), or that such accesses
// yield undefined results. Via the following assertion, we assume that
// functions added to the module do not feature statically out-of-bounds
// accesses.
// Assert that the index is smaller (unsigned) than this value.
// Return false if it is not (to keep compilers happy).
if (index_inst->GetSingleWordInOperand(0) >= bound) {
assert(false &&
"The function has a statically out-of-bounds access; "
"this should not occur.");
return false;
}
}
should_be_composite_type =
FollowCompositeIndex(ir_context, *should_be_composite_type, index_id);

View File

@ -200,21 +200,13 @@ bool TransformationOutlineFunction::IsApplicable(
// It is OK (and typically expected) for the exit block of the region to
// have successors outside the region.
//
// It is also OK for the exit block to head a structured control flow
// construct - the block containing the call to the outlined function will
// end up heading this construct if outlining takes place. However, we
// must ensure that if the exit block heads a loop, the continue target
// for this loop is outside the region.
if (auto loop_merge = block.GetLoopMergeInst()) {
// The exit block heads a loop
auto continue_target =
ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1));
if (region_set.count(continue_target)) {
// The continue target for the loop is in the region.
return false;
}
// It is also OK for the exit block to head a selection construct: the
// block containing the call to the outlined function will end up heading
// this construct if outlining takes place. However, it is not OK for
// the exit block to head a loop construct.
if (block.GetLoopMergeInst()) {
return false;
}
continue;
}

View File

@ -170,6 +170,15 @@ bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
return false;
}
}
if (use_instruction->opcode() == SpvOpImageTexelPointer &&
use_in_operand_index == 2) {
// The OpImageTexelPointer instruction has a Sample parameter that in some
// situations must be an id for the value 0. To guard against disrupting
// that requirement, we do not replace this argument to that instruction.
return false;
}
return true;
}

View File

@ -50,7 +50,7 @@ bool TransformationStore::IsApplicable(
}
// The pointer must not be read only.
if (pointer_type->GetSingleWordInOperand(0) == SpvStorageClassInput) {
if (pointer->IsReadOnlyPointer()) {
return false;
}

View File

@ -34,6 +34,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
dead_variable_elimination.h
decompose_initialized_variables_pass.h
decoration_manager.h
debug_info_manager.h
def_use_manager.h
desc_sroa.h
dominator_analysis.h
@ -141,6 +142,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
dead_variable_elimination.cpp
decompose_initialized_variables_pass.cpp
decoration_manager.cpp
debug_info_manager.cpp
def_use_manager.cpp
desc_sroa.cpp
dominator_analysis.cpp

View File

@ -177,7 +177,7 @@ bool CodeSinkingPass::ReferencesMutableMemory(Instruction* inst) {
return true;
}
if (base_ptr->IsReadOnlyVariable()) {
if (base_ptr->IsReadOnlyPointer()) {
return false;
}

View File

@ -0,0 +1,208 @@
// Copyright (c) 2020 Google LLC
//
// 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 "source/opt/debug_info_manager.h"
#include <cassert>
#include "source/opt/ir_context.h"
// Constants for OpenCL.DebugInfo.100 extension instructions.
static const uint32_t kOpLineOperandLineIndex = 1;
static const uint32_t kLineOperandIndexDebugFunction = 7;
static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
namespace spvtools {
namespace opt {
namespace analysis {
DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
AnalyzeDebugInsts(*c->module());
}
Instruction* DebugInfoManager::GetDbgInst(uint32_t id) {
auto dbg_inst_it = id_to_dbg_inst_.find(id);
return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second;
}
void DebugInfoManager::RegisterDbgInst(Instruction* inst) {
assert(
inst->NumInOperands() != 0 &&
context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
inst->GetInOperand(0).words[0] &&
"Given instruction is not a debug instruction");
id_to_dbg_inst_[inst->result_id()] = inst;
}
void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction &&
"inst is not a DebugFunction");
auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
assert(
fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() &&
"Register DebugFunction for a function that already has DebugFunction");
fn_id_to_dbg_fn_[fn_id] = inst;
}
uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
const DebugScope& scope) {
if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
0)
return kNoInlinedAt;
uint32_t line_number = 0;
if (line == nullptr) {
auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope());
if (lexical_scope_inst == nullptr) return kNoInlinedAt;
OpenCLDebugInfo100Instructions debug_opcode =
lexical_scope_inst->GetOpenCL100DebugOpcode();
switch (debug_opcode) {
case OpenCLDebugInfo100DebugFunction:
line_number = lexical_scope_inst->GetSingleWordOperand(
kLineOperandIndexDebugFunction);
break;
case OpenCLDebugInfo100DebugLexicalBlock:
line_number = lexical_scope_inst->GetSingleWordOperand(
kLineOperandIndexDebugLexicalBlock);
break;
case OpenCLDebugInfo100DebugTypeComposite:
case OpenCLDebugInfo100DebugCompilationUnit:
assert(false &&
"DebugTypeComposite and DebugCompilationUnit are lexical "
"scopes, but we inline functions into a function or a block "
"of a function, not into a struct/class or a global scope.");
break;
default:
assert(false &&
"Unreachable. a debug extension instruction for a "
"lexical scope must be DebugFunction, DebugTypeComposite, "
"DebugLexicalBlock, or DebugCompilationUnit.");
break;
}
} else {
line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
}
uint32_t result_id = context()->TakeNextId();
std::unique_ptr<Instruction> inlined_at(new Instruction(
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
{
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{context()
->get_feature_mgr()
->GetExtInstImportId_OpenCL100DebugInfo()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugInlinedAt)}},
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}},
}));
// |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt
// into the Inlined operand of this new DebugInlinedAt.
if (scope.GetInlinedAt() != kNoInlinedAt) {
inlined_at->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
{scope.GetInlinedAt()}});
}
RegisterDbgInst(inlined_at.get());
context()->module()->AddExtInstDebugInfo(std::move(inlined_at));
return result_id;
}
Instruction* DebugInfoManager::GetDebugInfoNone() {
if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
uint32_t result_id = context()->TakeNextId();
std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction(
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
{
{SPV_OPERAND_TYPE_RESULT_ID,
{context()
->get_feature_mgr()
->GetExtInstImportId_OpenCL100DebugInfo()}},
{SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
}));
// Add to the front of |ext_inst_debuginfo_|.
debug_info_none_inst_ =
context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
std::move(dbg_info_none_inst));
RegisterDbgInst(debug_info_none_inst_);
return debug_info_none_inst_;
}
Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
if (inlined_at == nullptr) return nullptr;
if (inlined_at->GetOpenCL100DebugOpcode() !=
OpenCLDebugInfo100DebugInlinedAt) {
return nullptr;
}
return inlined_at;
}
Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
Instruction* insert_before) {
auto* inlined_at = GetDebugInlinedAt(clone_inlined_at_id);
if (inlined_at == nullptr) return nullptr;
std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
new_inlined_at->SetResultId(context()->TakeNextId());
RegisterDbgInst(new_inlined_at.get());
if (insert_before != nullptr)
return insert_before->InsertBefore(std::move(new_inlined_at));
return context()->module()->ext_inst_debuginfo_end()->InsertBefore(
std::move(new_inlined_at));
}
void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
return;
RegisterDbgInst(dbg_inst);
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
assert(GetDebugFunction(dbg_inst->GetSingleWordOperand(
kDebugFunctionOperandFunctionIndex)) == nullptr &&
"Two DebugFunction instruction exists for a single OpFunction.");
RegisterDbgFunction(dbg_inst);
}
if (debug_info_none_inst_ == nullptr &&
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = dbg_inst;
}
}
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
debug_info_none_inst_ = nullptr;
module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
// Move |debug_info_none_inst_| to the beginning of the debug instruction
// list.
if (debug_info_none_inst_ != nullptr &&
debug_info_none_inst_->PreviousNode() != nullptr &&
debug_info_none_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
OpenCLDebugInfo100InstructionsMax) {
debug_info_none_inst_->InsertBefore(
&*context()->module()->ext_inst_debuginfo_begin());
}
}
} // namespace analysis
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,115 @@
// Copyright (c) 2020 Google LLC
//
// 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.
#ifndef SOURCE_OPT_DEBUG_INFO_MANAGER_H_
#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
#include <unordered_map>
#include "source/opt/instruction.h"
#include "source/opt/module.h"
namespace spvtools {
namespace opt {
namespace analysis {
// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 extension
// instructions.
class DebugInfoManager {
public:
// Constructs a debug information manager from the given |context|.
DebugInfoManager(IRContext* context);
DebugInfoManager(const DebugInfoManager&) = delete;
DebugInfoManager(DebugInfoManager&&) = delete;
DebugInfoManager& operator=(const DebugInfoManager&) = delete;
DebugInfoManager& operator=(DebugInfoManager&&) = delete;
friend bool operator==(const DebugInfoManager&, const DebugInfoManager&);
friend bool operator!=(const DebugInfoManager& lhs,
const DebugInfoManager& rhs) {
return !(lhs == rhs);
}
// Analyzes OpenCL.DebugInfo.100 instruction |dbg_inst|.
void AnalyzeDebugInst(Instruction* dbg_inst);
// Creates new DebugInlinedAt and returns its id. Its line operand is the
// line number of |line| if |line| is not nullptr. Otherwise, its line operand
// is the line number of lexical scope of |scope|. Its Scope and Inlined
// operands are Scope and Inlined of |scope|.
uint32_t CreateDebugInlinedAt(const Instruction* line,
const DebugScope& scope);
// Returns a DebugInfoNone instruction.
Instruction* GetDebugInfoNone();
// Returns DebugInlinedAt whose id is |dbg_inlined_at_id|. If it does not
// exist or it is not a DebugInlinedAt instruction, return nullptr.
Instruction* GetDebugInlinedAt(uint32_t dbg_inlined_at_id);
// Returns DebugFunction whose Function operand is |fn_id|. If it does not
// exist, return nullptr.
Instruction* GetDebugFunction(uint32_t fn_id) {
auto dbg_fn_it = fn_id_to_dbg_fn_.find(fn_id);
return dbg_fn_it == fn_id_to_dbg_fn_.end() ? nullptr : dbg_fn_it->second;
}
// Clones DebugInlinedAt whose id is |clone_inlined_at_id|. If
// |clone_inlined_at_id| is not an id of DebugInlinedAt, returns nullptr.
// If |insert_before| is given, inserts the new DebugInlinedAt before it.
// Otherwise, inserts the new DebugInlinedAt into the debug instruction
// section of the module.
Instruction* CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
Instruction* insert_before = nullptr);
private:
IRContext* context() { return context_; }
// Analyzes OpenCL.DebugInfo.100 instructions in the given |module| and
// populates data structures in this class.
void AnalyzeDebugInsts(Module& module);
// Returns the debug instruction whose id is |id|. Returns |nullptr| if one
// does not exists.
Instruction* GetDbgInst(uint32_t id);
// Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of
// |inst| as a key.
void RegisterDbgInst(Instruction* inst);
// Register the DebugFunction instruction |inst|. The function referenced
// in |inst| must not already be registered.
void RegisterDbgFunction(Instruction* inst);
IRContext* context_;
// Mapping from ids of OpenCL.DebugInfo.100 extension instructions
// to their Instruction instances.
std::unordered_map<uint32_t, Instruction*> id_to_dbg_inst_;
// Mapping from function's ids to DebugFunction instructions whose
// operand is the function.
std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
// DebugInfoNone instruction. We need only a single DebugInfoNone.
// To reuse the existing one, we keep it using this member variable.
Instruction* debug_info_none_inst_;
};
} // namespace analysis
} // namespace opt
} // namespace spvtools
#endif // SOURCE_OPT_DEBUG_INFO_MANAGER_H_

View File

@ -192,13 +192,13 @@ inline void Function::AddBasicBlocks(T src_begin, T src_end, iterator ip) {
}
inline void Function::MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip) {
auto block_to_move = std::move(*FindBlock(id).Get());
std::unique_ptr<BasicBlock> block_to_move = std::move(*FindBlock(id).Get());
blocks_.erase(std::find(std::begin(blocks_), std::end(blocks_), nullptr));
assert(block_to_move->GetParent() == ip->GetParent() &&
"Both blocks have to be in the same function.");
InsertBasicBlockAfter(std::move(block_to_move), ip);
blocks_.erase(std::find(std::begin(blocks_), std::end(blocks_), nullptr));
}
inline void Function::RemoveEmptyBlocks() {

View File

@ -29,7 +29,7 @@ namespace {
// Indices used to get particular operands out of instructions using InOperand.
const uint32_t kTypeImageDimIndex = 1;
const uint32_t kLoadBaseIndex = 0;
const uint32_t kVariableStorageClassIndex = 0;
const uint32_t kPointerTypeStorageClassIndex = 0;
const uint32_t kTypeImageSampledIndex = 5;
// Constants for OpenCL.DebugInfo.100 extension instructions.
@ -187,7 +187,7 @@ bool Instruction::IsReadOnlyLoad() const {
}
if (address_def->opcode() == SpvOpVariable) {
if (address_def->IsReadOnlyVariable()) {
if (address_def->IsReadOnlyPointer()) {
return true;
}
}
@ -232,11 +232,11 @@ Instruction* Instruction::GetBaseAddress() const {
return base_inst;
}
bool Instruction::IsReadOnlyVariable() const {
bool Instruction::IsReadOnlyPointer() const {
if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
return IsReadOnlyVariableShaders();
return IsReadOnlyPointerShaders();
else
return IsReadOnlyVariableKernel();
return IsReadOnlyPointerKernel();
}
bool Instruction::IsVulkanStorageImage() const {
@ -244,7 +244,8 @@ bool Instruction::IsVulkanStorageImage() const {
return false;
}
uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
uint32_t storage_class =
GetSingleWordInOperand(kPointerTypeStorageClassIndex);
if (storage_class != SpvStorageClassUniformConstant) {
return false;
}
@ -278,7 +279,8 @@ bool Instruction::IsVulkanSampledImage() const {
return false;
}
uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
uint32_t storage_class =
GetSingleWordInOperand(kPointerTypeStorageClassIndex);
if (storage_class != SpvStorageClassUniformConstant) {
return false;
}
@ -312,7 +314,8 @@ bool Instruction::IsVulkanStorageTexelBuffer() const {
return false;
}
uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
uint32_t storage_class =
GetSingleWordInOperand(kPointerTypeStorageClassIndex);
if (storage_class != SpvStorageClassUniformConstant) {
return false;
}
@ -361,7 +364,8 @@ bool Instruction::IsVulkanStorageBuffer() const {
return false;
}
uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
uint32_t storage_class =
GetSingleWordInOperand(kPointerTypeStorageClassIndex);
if (storage_class == SpvStorageClassUniform) {
bool is_buffer_block = false;
context()->get_decoration_mgr()->ForEachDecoration(
@ -383,7 +387,8 @@ bool Instruction::IsVulkanUniformBuffer() const {
return false;
}
uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
uint32_t storage_class =
GetSingleWordInOperand(kPointerTypeStorageClassIndex);
if (storage_class != SpvStorageClassUniform) {
return false;
}
@ -409,9 +414,18 @@ bool Instruction::IsVulkanUniformBuffer() const {
return is_block;
}
bool Instruction::IsReadOnlyVariableShaders() const {
uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
bool Instruction::IsReadOnlyPointerShaders() const {
if (type_id() == 0) {
return false;
}
Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
if (type_def->opcode() != SpvOpTypePointer) {
return false;
}
uint32_t storage_class =
type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex);
switch (storage_class) {
case SpvStorageClassUniformConstant:
@ -439,8 +453,19 @@ bool Instruction::IsReadOnlyVariableShaders() const {
return is_nonwritable;
}
bool Instruction::IsReadOnlyVariableKernel() const {
uint32_t storage_class = GetSingleWordInOperand(kVariableStorageClassIndex);
bool Instruction::IsReadOnlyPointerKernel() const {
if (type_id() == 0) {
return false;
}
Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id());
if (type_def->opcode() != SpvOpTypePointer) {
return false;
}
uint32_t storage_class =
type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex);
return storage_class == SpvStorageClassUniformConstant;
}

View File

@ -92,6 +92,19 @@ struct Operand {
// Returns a string operand as a std::string.
std::string AsString() const { return AsCString(); }
// Returns a literal integer operand as a uint64_t
uint64_t AsLiteralUint64() const {
assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER);
assert(1 <= words.size());
assert(words.size() <= 2);
// Load the low word.
uint64_t result = uint64_t(words[0]);
if (words.size() > 1) {
result = result | (uint64_t(words[1]) << 32);
}
return result;
}
friend bool operator==(const Operand& o1, const Operand& o2) {
return o1.type == o2.type && o1.words == o2.words;
}
@ -383,8 +396,14 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Memory-to-memory instructions are not considered loads.
inline bool IsLoad() const;
// Returns true if the instruction declares a variable that is read-only.
bool IsReadOnlyVariable() const;
// Returns true if the instruction generates a pointer that is definitely
// read-only. This is determined by analysing the pointer type's storage
// class and decorations that target the pointer's id. It does not analyse
// other instructions that the pointer may be derived from. Thus if 'true' is
// returned, the pointer is definitely read-only, while if 'false' is returned
// it is possible that the pointer may actually be read-only if it is derived
// from another pointer that is decorated as read-only.
bool IsReadOnlyPointer() const;
// The following functions check for the various descriptor types defined in
// the Vulkan specification section 13.1.
@ -513,11 +532,12 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
return 0;
}
// Returns true if the instruction declares a variable that is read-only. The
// first version assumes the module is a shader module. The second assumes a
// Returns true if the instruction generates a read-only pointer, with the
// same caveats documented in the comment for IsReadOnlyPointer. The first
// version assumes the module is a shader module. The second assumes a
// kernel.
bool IsReadOnlyVariableShaders() const;
bool IsReadOnlyVariableKernel() const;
bool IsReadOnlyPointerShaders() const;
bool IsReadOnlyPointerKernel() const;
// Returns true if the result of |inst| can be used as the base image for an
// instruction that samples a image, reads an image, or writes to an image.

View File

@ -85,6 +85,9 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
if (set & kAnalysisTypes) {
BuildTypeManager();
}
if (set & kAnalysisDebugInfo) {
BuildDebugInfoManager();
}
}
void IRContext::InvalidateAnalysesExceptFor(
@ -98,6 +101,7 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
// away, the ConstantManager has to go away.
if (analyses_to_invalidate & kAnalysisTypes) {
analyses_to_invalidate |= kAnalysisConstants;
analyses_to_invalidate |= kAnalysisDebugInfo;
}
// The dominator analysis hold the psuedo entry and exit nodes from the CFG.
@ -148,6 +152,10 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
type_mgr_.reset(nullptr);
}
if (analyses_to_invalidate & kAnalysisDebugInfo) {
debug_info_mgr_.reset(nullptr);
}
valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
}
@ -373,27 +381,6 @@ void IRContext::KillNamesAndDecorates(Instruction* inst) {
KillNamesAndDecorates(rId);
}
Instruction* IRContext::GetOpenCL100DebugInfoNone() {
if (debug_info_none_inst_) return debug_info_none_inst_;
assert(get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() &&
"Module does not include debug info extension instruction.");
// Create a new DebugInfoNone.
std::unique_ptr<Instruction> dbg_info_none(new Instruction(
this, SpvOpExtInst, get_type_mgr()->GetVoidTypeId(), TakeNextId(),
{
{SPV_OPERAND_TYPE_RESULT_ID,
{get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()}},
{SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
}));
// Add to the front of |ext_inst_debuginfo_|.
debug_info_none_inst_ = module()->ext_inst_debuginfo_begin()->InsertBefore(
std::move(dbg_info_none));
return debug_info_none_inst_;
}
void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
const auto opcode = inst->opcode();
const uint32_t id = inst->result_id();
@ -405,7 +392,8 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
continue;
auto& operand = it->GetOperand(kDebugFunctionOperandFunctionIndex);
if (operand.words[0] == id) {
operand.words[0] = GetOpenCL100DebugInfoNone()->result_id();
operand.words[0] =
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
}
}
}
@ -418,7 +406,8 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
continue;
auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex);
if (operand.words[0] == id) {
operand.words[0] = GetOpenCL100DebugInfoNone()->result_id();
operand.words[0] =
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
}
}
}

View File

@ -29,6 +29,7 @@
#include "source/assembly_grammar.h"
#include "source/opt/cfg.h"
#include "source/opt/constants.h"
#include "source/opt/debug_info_manager.h"
#include "source/opt/decoration_manager.h"
#include "source/opt/def_use_manager.h"
#include "source/opt/dominator_analysis.h"
@ -78,7 +79,8 @@ class IRContext {
kAnalysisIdToFuncMapping = 1 << 13,
kAnalysisConstants = 1 << 14,
kAnalysisTypes = 1 << 15,
kAnalysisEnd = 1 << 16
kAnalysisDebugInfo = 1 << 16,
kAnalysisEnd = 1 << 17
};
using ProcessFunction = std::function<bool(Function*)>;
@ -102,8 +104,7 @@ class IRContext {
id_to_name_(nullptr),
max_id_bound_(kDefaultMaxIdBound),
preserve_bindings_(false),
preserve_spec_constants_(false),
debug_info_none_inst_(nullptr) {
preserve_spec_constants_(false) {
SetContextMessageConsumer(syntax_context_, consumer_);
module_->SetContext(this);
}
@ -120,8 +121,7 @@ class IRContext {
id_to_name_(nullptr),
max_id_bound_(kDefaultMaxIdBound),
preserve_bindings_(false),
preserve_spec_constants_(false),
debug_info_none_inst_(nullptr) {
preserve_spec_constants_(false) {
SetContextMessageConsumer(syntax_context_, consumer_);
module_->SetContext(this);
InitializeCombinators();
@ -328,6 +328,17 @@ class IRContext {
return type_mgr_.get();
}
// Returns a pointer to the debug information manager. If no debug
// information manager has been created yet, it creates one.
// NOTE: Once created, the debug information manager remains active
// it is never re-built.
analysis::DebugInfoManager* get_debug_info_mgr() {
if (!AreAnalysesValid(kAnalysisDebugInfo)) {
BuildDebugInfoManager();
}
return debug_info_mgr_.get();
}
// Returns a pointer to the scalar evolution analysis. If it is invalid it
// will be rebuilt first.
ScalarEvolutionAnalysis* GetScalarEvolutionAnalysis() {
@ -657,6 +668,13 @@ class IRContext {
valid_analyses_ = valid_analyses_ | kAnalysisTypes;
}
// Builds the debug information manager from scratch, even if it was
// already valid.
void BuildDebugInfoManager() {
debug_info_mgr_ = MakeUnique<analysis::DebugInfoManager>(this);
valid_analyses_ = valid_analyses_ | kAnalysisDebugInfo;
}
// Removes all computed dominator and post-dominator trees. This will force
// the context to rebuild the trees on demand.
void ResetDominatorAnalysis() {
@ -710,9 +728,6 @@ class IRContext {
// Add |var_id| to all entry points in module.
void AddVarToEntryPoints(uint32_t var_id);
// Get the existing DebugInfoNone. If it is null, create one and keep it.
Instruction* GetOpenCL100DebugInfoNone();
// The SPIR-V syntax context containing grammar tables for opcodes and
// operands.
spv_context syntax_context_;
@ -782,6 +797,9 @@ class IRContext {
// Type manager for |module_|.
std::unique_ptr<analysis::TypeManager> type_mgr_;
// Debug information manager for |module_|.
std::unique_ptr<analysis::DebugInfoManager> debug_info_mgr_;
// A map from an id to its corresponding OpName and OpMemberName instructions.
std::unique_ptr<std::multimap<uint32_t, Instruction*>> id_to_name_;
@ -806,10 +824,6 @@ class IRContext {
// Whether all specialization constants within |module_|
// should be preserved.
bool preserve_spec_constants_;
// DebugInfoNone instruction. We need only a single DebugInfoNone.
// To reuse the existing one, we keep it using this member variable.
Instruction* debug_info_none_inst_;
};
inline IRContext::Analysis operator|(IRContext::Analysis lhs,

View File

@ -135,6 +135,8 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
Error(consumer_, src, loc, "terminator instruction outside basic block");
return false;
}
if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
spv_inst->SetDebugScope(last_dbg_scope_);
block_->AddInstruction(std::move(spv_inst));
function_->AddBasicBlock(std::move(block_));
block_ = nullptr;

View File

@ -19,7 +19,7 @@
namespace {
// The default maximum number of steps the reducer will take before giving up.
const uint32_t kDefaultStepLimit = 250;
const uint32_t kDefaultStepLimit = 2500;
} // namespace
spv_reducer_options_t::spv_reducer_options_t()