Updated spirv-tools.

This commit is contained in:
Бранимир Караџић
2020-04-19 18:49:04 -07:00
parent 0778398f58
commit e262d3cf93
19 changed files with 240 additions and 36 deletions

View File

@@ -1 +1 @@
"v2020.3-dev", "SPIRV-Tools v2020.3-dev 36e37dd5b53636024ccb44310450d4128dae0842"
"v2020.3-dev", "SPIRV-Tools v2020.3-dev 0778398f58d93ce8a861aeeb7a6a60324383525e"

View File

@@ -124,6 +124,7 @@ SpvStorageClass FuzzerPassDonateModules::AdaptStorageClass(
case SpvStorageClassUniform:
case SpvStorageClassUniformConstant:
case SpvStorageClassPushConstant:
case SpvStorageClassImage:
// We change these to Private
return SpvStorageClassPrivate;
default:
@@ -721,11 +722,12 @@ bool FuzzerPassDonateModules::CanDonateInstruction(
// We do not have a mapped result id for this id operand. That either
// means that it is a forward reference (which is OK), that we skipped
// the instruction that generated it (which is not OK), or that it is
// the id of a function that we did not donate (which is not OK). We
// check for the latter two cases.
// the id of a function or global value that we did not donate (which
// is not OK). We check for the latter two cases.
if (skipped_instructions.count(*in_id) ||
donor_ir_context->get_def_use_mgr()->GetDef(*in_id)->opcode() ==
SpvOpFunction) {
// A function or global value does not have an associated basic
// block.
!donor_ir_context->get_instr_block(*in_id)) {
result = false;
return false;
}

View File

@@ -66,7 +66,7 @@ class FuzzerPassDonateModules : public FuzzerPass {
opt::IRContext* donor_ir_context,
std::map<uint32_t, uint32_t>* original_id_to_donated_id);
// TODO comment
// Helper method for HandleTypesAndValues, to handle a single type/value.
void HandleTypeOrValue(
const opt::Instruction& type_or_value,
std::map<uint32_t, uint32_t>* original_id_to_donated_id);

View File

@@ -218,6 +218,12 @@ bool CanInsertOpcodeBeforeInstruction(
}
bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
if (inst->opcode() == SpvOpSampledImage) {
// The SPIR-V data rules say that only very specific instructions may
// may consume the result id of an OpSampledImage, and this excludes the
// instructions that are used for making synonyms.
return false;
}
if (!inst->HasResultId()) {
// We can only make a synonym of an instruction that generates an id.
return false;

View File

@@ -198,10 +198,23 @@ bool TransformationOutlineFunction::IsApplicable(
for (auto& block : *entry_block->GetParent()) {
if (&block == exit_block) {
// 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.
// 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;
}
}
continue;
}
@@ -274,12 +287,20 @@ bool TransformationOutlineFunction::IsApplicable(
}
// For each region output id -- i.e. every id defined inside the region but
// used outside the region -- there needs to be a corresponding fresh id that
// can hold the value for this id computed in the outlined function.
// used outside the region, ...
std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
PairSequenceToMap(message_.output_id_to_fresh_id());
for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) {
if (output_id_to_fresh_id_map.count(id) == 0) {
if (
// ... there needs to be a corresponding fresh id that can hold the
// value for this id computed in the outlined function, and ...
output_id_to_fresh_id_map.count(id) == 0
// ... the output id must not have pointer type (to avoid creating a
// struct with pointer members to pass data out of the outlined
// function)
|| ir_context->get_def_use_mgr()
->GetDef(fuzzerutil::GetTypeId(ir_context, id))
->opcode() == SpvOpTypePointer) {
return false;
}
}

View File

@@ -125,6 +125,9 @@ bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
} else if (composite_type_being_accessed->AsArray()) {
composite_type_being_accessed =
composite_type_being_accessed->AsArray()->element_type();
} else if (composite_type_being_accessed->AsRuntimeArray()) {
composite_type_being_accessed =
composite_type_being_accessed->AsRuntimeArray()->element_type();
} else {
assert(composite_type_being_accessed->AsStruct());
auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef(

View File

@@ -76,8 +76,34 @@ bool TransformationSplitBlock::IsApplicable(
}
// We cannot split before an OpPhi unless the OpPhi has exactly one
// associated incoming edge.
return !(split_before->opcode() == SpvOpPhi &&
split_before->NumInOperands() != 2);
if (split_before->opcode() == SpvOpPhi &&
split_before->NumInOperands() != 2) {
return false;
}
// Splitting the block must not separate the definition of an OpSampledImage
// from its use: the SPIR-V data rules require them to be in the same block.
std::set<uint32_t> sampled_image_result_ids;
bool before_split = true;
for (auto& instruction : *block_to_split) {
if (&instruction == &*split_before) {
before_split = false;
}
if (before_split) {
if (instruction.opcode() == SpvOpSampledImage) {
sampled_image_result_ids.insert(instruction.result_id());
}
} else {
if (!instruction.WhileEachInId(
[&sampled_image_result_ids](uint32_t* id) -> bool {
return !sampled_image_result_ids.count(*id);
})) {
return false;
}
}
}
return true;
}
void TransformationSplitBlock::Apply(

View File

@@ -78,6 +78,8 @@ void FeatureManager::AddCapabilities(Module* module) {
void FeatureManager::AddExtInstImportIds(Module* module) {
extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450");
extinst_importid_OpenCL100DebugInfo_ =
module->GetExtInstImportId("OpenCL.DebugInfo.100");
}
bool operator==(const FeatureManager& a, const FeatureManager& b) {
@@ -100,6 +102,11 @@ bool operator==(const FeatureManager& a, const FeatureManager& b) {
return false;
}
if (a.extinst_importid_OpenCL100DebugInfo_ !=
b.extinst_importid_OpenCL100DebugInfo_) {
return false;
}
return true;
}
} // namespace opt

View File

@@ -51,6 +51,10 @@ class FeatureManager {
return extinst_importid_GLSLstd450_;
}
uint32_t GetExtInstImportId_OpenCL100DebugInfo() const {
return extinst_importid_OpenCL100DebugInfo_;
}
friend bool operator==(const FeatureManager& a, const FeatureManager& b);
friend bool operator!=(const FeatureManager& a, const FeatureManager& b) {
return !(a == b);
@@ -84,6 +88,10 @@ class FeatureManager {
// Common external instruction import ids, cached for performance.
uint32_t extinst_importid_GLSLstd450_ = 0;
// Common OpenCL100DebugInfo external instruction import ids, cached
// for performance.
uint32_t extinst_importid_OpenCL100DebugInfo_ = 0;
};
} // namespace opt

View File

@@ -84,9 +84,12 @@ bool Function::WhileEachInst(const std::function<bool(Instruction*)>& f,
}
}
for (auto& di : debug_insts_in_header_) {
if (!di.WhileEachInst(f, run_on_debug_line_insts)) {
return false;
if (!debug_insts_in_header_.empty()) {
Instruction* di = &debug_insts_in_header_.front();
while (di != nullptr) {
Instruction* next_instruction = di->NextNode();
if (!di->WhileEachInst(f, run_on_debug_line_insts)) return false;
di = next_instruction;
}
}
@@ -118,9 +121,9 @@ bool Function::WhileEachInst(const std::function<bool(const Instruction*)>& f,
}
for (const auto& di : debug_insts_in_header_) {
if (!di.WhileEachInst(f, run_on_debug_line_insts)) {
if (!static_cast<const Instruction*>(&di)->WhileEachInst(
f, run_on_debug_line_insts))
return false;
}
}
for (const auto& bb : blocks_) {

View File

@@ -802,8 +802,11 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer(
opt::Instruction* image_texel_pointer) {
// TODO(dneto): Write tests for this code.
// TODO(dneto): Use signed-clamp
(void)(image_texel_pointer);
return SPV_SUCCESS;
// Do not compile this code until it is ready to be used.
#if 0
// Example:
// %texel_ptr = OpImageTexelPointer %texel_ptr_type %image_ptr %coord
// %sample
@@ -1035,6 +1038,7 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer(
def_use_mgr->AnalyzeInstUse(image_texel_pointer);
return SPV_SUCCESS;
#endif
}
opt::Instruction* GraphicsRobustAccessPass::InsertInst(

View File

@@ -33,6 +33,8 @@ const uint32_t kVariableStorageClassIndex = 0;
const uint32_t kTypeImageSampledIndex = 5;
// Constants for OpenCL.DebugInfo.100 extension instructions.
const uint32_t kExtInstSetIdInIdx = 0;
const uint32_t kExtInstInstructionInIdx = 1;
const uint32_t kDebugScopeNumWords = 7;
const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
const uint32_t kDebugNoScopeNumWords = 5;
@@ -180,10 +182,27 @@ void Instruction::ReplaceOperands(const OperandList& new_operands) {
bool Instruction::IsReadOnlyLoad() const {
if (IsLoad()) {
Instruction* address_def = GetBaseAddress();
if (!address_def || address_def->opcode() != SpvOpVariable) {
if (!address_def) {
return false;
}
return address_def->IsReadOnlyVariable();
if (address_def->opcode() == SpvOpVariable) {
if (address_def->IsReadOnlyVariable()) {
return true;
}
}
if (address_def->opcode() == SpvOpLoad) {
const analysis::Type* address_type =
context()->get_type_mgr()->GetType(address_def->type_id());
if (address_type->AsSampledImage() != nullptr) {
const auto* image_type =
address_type->AsSampledImage()->image_type()->AsImage();
if (image_type->sampled() == 1) {
return true;
}
}
}
}
return false;
}
@@ -510,6 +529,21 @@ bool Instruction::IsValidBasePointer() const {
return false;
}
OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
if (opcode() != SpvOpExtInst) return OpenCLDebugInfo100InstructionsMax;
if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo())
return OpenCLDebugInfo100InstructionsMax;
if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
return OpenCLDebugInfo100InstructionsMax;
}
return OpenCLDebugInfo100Instructions(
GetSingleWordInOperand(kExtInstInstructionInIdx));
}
bool Instruction::IsValidBaseImage() const {
uint32_t tid = type_id();
if (tid == 0) {
@@ -714,9 +748,6 @@ bool Instruction::IsScalarizable() const {
return true;
}
const uint32_t kExtInstSetIdInIdx = 0;
const uint32_t kExtInstInstructionInIdx = 1;
if (opcode() == SpvOpExtInst) {
uint32_t instSetId =
context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();

View File

@@ -22,14 +22,14 @@
#include <utility>
#include <vector>
#include "source/opcode.h"
#include "source/operand.h"
#include "source/util/ilist_node.h"
#include "source/util/small_vector.h"
#include "OpenCLDebugInfo100.h"
#include "source/latest_version_glsl_std_450_header.h"
#include "source/latest_version_spirv_header.h"
#include "source/opcode.h"
#include "source/operand.h"
#include "source/opt/reflect.h"
#include "source/util/ilist_node.h"
#include "source/util/small_vector.h"
#include "spirv-tools/libspirv.h"
const uint32_t kNoDebugScope = 0;
@@ -496,6 +496,11 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// rules for physical addressing.
bool IsValidBasePointer() const;
// Returns debug opcode of an OpenCL.100.DebugInfo instruction. If
// it is not an OpenCL.100.DebugInfo instruction, just returns
// OpenCLDebugInfo100InstructionsMax.
OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
// Dump this instruction on stderr. Useful when running interactive
// debuggers.
void Dump() const;

View File

@@ -16,6 +16,7 @@
#include <cstring>
#include "OpenCLDebugInfo100.h"
#include "source/latest_version_glsl_std_450_header.h"
#include "source/opt/log.h"
#include "source/opt/mem_pass.h"
@@ -29,6 +30,10 @@ static const int kSpvDecorateBuiltinInIdx = 2;
static const int kEntryPointInterfaceInIdx = 3;
static const int kEntryPointFunctionIdInIdx = 1;
// Constants for OpenCL.DebugInfo.100 extension instructions.
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
} // anonymous namespace
namespace spvtools {
@@ -153,6 +158,8 @@ Instruction* IRContext::KillInst(Instruction* inst) {
KillNamesAndDecorates(inst);
KillOperandFromDebugInstructions(inst);
if (AreAnalysesValid(kAnalysisDefUse)) {
get_def_use_mgr()->ClearInst(inst);
}
@@ -265,7 +272,7 @@ bool IRContext::ReplaceAllUsesWithPredicate(
bool IRContext::IsConsistent() {
#ifndef SPIRV_CHECK_CONTEXT
return true;
#endif
#else
if (AreAnalysesValid(kAnalysisDefUse)) {
analysis::DefUseManager new_def_use(module());
if (*get_def_use_mgr() != new_def_use) {
@@ -317,6 +324,7 @@ bool IRContext::IsConsistent() {
}
}
return true;
#endif
}
void IRContext::ForgetUses(Instruction* inst) {
@@ -365,6 +373,61 @@ 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();
// Kill id of OpFunction from DebugFunction.
if (opcode == SpvOpFunction) {
for (auto it = module()->ext_inst_debuginfo_begin();
it != module()->ext_inst_debuginfo_end(); ++it) {
if (it->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugFunction)
continue;
auto& operand = it->GetOperand(kDebugFunctionOperandFunctionIndex);
if (operand.words[0] == id) {
operand.words[0] = GetOpenCL100DebugInfoNone()->result_id();
}
}
}
// Kill id of OpVariable for global variable from DebugGlobalVariable.
if (opcode == SpvOpVariable || IsConstantInst(opcode)) {
for (auto it = module()->ext_inst_debuginfo_begin();
it != module()->ext_inst_debuginfo_end(); ++it) {
if (it->GetOpenCL100DebugOpcode() !=
OpenCLDebugInfo100DebugGlobalVariable)
continue;
auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex);
if (operand.words[0] == id) {
operand.words[0] = GetOpenCL100DebugInfoNone()->result_id();
}
}
}
// Notice that we do not need anythings to do for local variables.
// DebugLocalVariable does not have an OpVariable operand. Instead,
// DebugDeclare/DebugValue has an OpVariable operand for a local
// variable. The function inlining pass handles it properly.
}
void IRContext::AddCombinatorsForCapability(uint32_t capability) {
if (capability == SpvCapabilityShader) {
combinator_ops_[0].insert({SpvOpNop,

View File

@@ -102,7 +102,8 @@ class IRContext {
id_to_name_(nullptr),
max_id_bound_(kDefaultMaxIdBound),
preserve_bindings_(false),
preserve_spec_constants_(false) {
preserve_spec_constants_(false),
debug_info_none_inst_(nullptr) {
SetContextMessageConsumer(syntax_context_, consumer_);
module_->SetContext(this);
}
@@ -119,7 +120,8 @@ class IRContext {
id_to_name_(nullptr),
max_id_bound_(kDefaultMaxIdBound),
preserve_bindings_(false),
preserve_spec_constants_(false) {
preserve_spec_constants_(false),
debug_info_none_inst_(nullptr) {
SetContextMessageConsumer(syntax_context_, consumer_);
module_->SetContext(this);
InitializeCombinators();
@@ -426,6 +428,9 @@ class IRContext {
// Kill all name and decorate ops targeting the result id of |inst|.
void KillNamesAndDecorates(Instruction* inst);
// Change operands of debug instruction to DebugInfoNone.
void KillOperandFromDebugInstructions(Instruction* inst);
// Returns the next unique id for use by an instruction.
inline uint32_t TakeNextUniqueId() {
assert(unique_id_ != std::numeric_limits<uint32_t>::max());
@@ -705,6 +710,9 @@ 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_;
@@ -798,6 +806,10 @@ 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

@@ -498,7 +498,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
} else if (pass_name == "legalize-vector-shuffle") {
RegisterPass(CreateLegalizeVectorShufflePass());
} else if (pass_name == "split-invalid-unreachable") {
RegisterPass(CreateLegalizeVectorShufflePass());
RegisterPass(CreateSplitInvalidUnreachablePass());
} else if (pass_name == "decompose-initialized-variables") {
RegisterPass(CreateDecomposeInitializedVariablesPass());
} else if (pass_name == "graphics-robust-access") {

View File

@@ -85,9 +85,14 @@ void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) {
if (merge_inst->opcode() == SpvOpLoopMerge) {
new_state.cinfo.containing_loop = block->id();
new_state.cinfo.containing_switch = 0;
new_state.cinfo.in_continue = false;
new_state.continue_node =
merge_inst->GetSingleWordInOperand(kContinueNodeIndex);
if (block->id() == new_state.continue_node) {
new_state.cinfo.in_continue = true;
bb_to_construct_[block->id()].in_continue = true;
} else {
new_state.cinfo.in_continue = false;
}
} else {
new_state.cinfo.containing_loop = state.back().cinfo.containing_loop;
new_state.cinfo.in_continue = state.back().cinfo.in_continue;

View File

@@ -194,6 +194,13 @@ class TypeManager {
uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); }
Type* GetVoidType() {
Void void_type;
return GetRegisteredType(&void_type);
}
uint32_t GetVoidTypeId() { return GetTypeInstruction(GetVoidType()); }
private:
using TypeToIdMap = std::unordered_map<const Type*, uint32_t, HashTypePointer,
CompareTypePointers>;

View File

@@ -1090,8 +1090,9 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
return _.diag(SPV_ERROR_INVALID_CFG, inst)
<< "OpReturn can only be called from a function with void "
<< "return type.";
_.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
break;
}
// Fallthrough.
case SpvOpKill:
case SpvOpReturnValue:
case SpvOpUnreachable: