mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 19:20:05 +00:00
Refactor Id -> Instruction
This commit is contained in:
parent
d49928f0cc
commit
816f29805b
@ -170,7 +170,7 @@ set(SPIRV_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/BasicBlock.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/Construct.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/Function.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/Id.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/Instruction.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/ValidationState.cpp)
|
||||
|
||||
# The software_version.cpp file includes build-version.inc.
|
||||
|
135
source/val/Id.h
135
source/val/Id.h
@ -1,135 +0,0 @@
|
||||
// Copyright (c) 2015-2016 The Khronos Group Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and/or associated documentation files (the
|
||||
// "Materials"), to deal in the Materials without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
// permit persons to whom the Materials are furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Materials.
|
||||
//
|
||||
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
|
||||
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
|
||||
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
|
||||
// https://www.khronos.org/registry/
|
||||
//
|
||||
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
|
||||
#ifndef LIBSPIRV_VAL_ID_H_
|
||||
#define LIBSPIRV_VAL_ID_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <functional>
|
||||
#include <set>
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "val/Function.h"
|
||||
|
||||
namespace libspirv {
|
||||
|
||||
class BasicBlock;
|
||||
|
||||
/// Represents a definition of any ID
|
||||
///
|
||||
/// This class represents the a definition of an Id in a module. This object can
|
||||
/// be formed as a complete, or incomplete Id. A complete Id allows you to
|
||||
/// reference all of the properties of this class and forms a fully defined
|
||||
/// object in the module. The incomplete Id is defined only by its integer value
|
||||
/// in a module and can only be used to search in a data structure.
|
||||
class Id {
|
||||
public:
|
||||
/// This constructor creates an incomplete Id. This constructor can be used to
|
||||
/// create Id that are used to find other Ids
|
||||
explicit Id(const uint32_t result_id = 0);
|
||||
|
||||
/// This constructor creates a complete Id.
|
||||
explicit Id(const spv_parsed_instruction_t* inst,
|
||||
Function* function = nullptr, BasicBlock* block = nullptr);
|
||||
|
||||
/// Registers a use of the Id
|
||||
void RegisterUse(const BasicBlock* block = nullptr);
|
||||
|
||||
/// returns the id of the Id
|
||||
operator uint32_t() const { return id_; }
|
||||
|
||||
uint32_t id() const { return id_; }
|
||||
uint32_t type_id() const { return type_id_; }
|
||||
SpvOp opcode() const { return opcode_; }
|
||||
|
||||
/// returns the Function where the id was defined. nullptr if it was defined
|
||||
/// outside of a Function
|
||||
const Function* defining_function() const { return defining_function_; }
|
||||
|
||||
/// returns the BasicBlock where the id was defined. nullptr if it was defined
|
||||
/// outside of a BasicBlock
|
||||
const BasicBlock* defining_block() const { return defining_block_; }
|
||||
|
||||
/// Returns the set of blocks where this Id was used
|
||||
const std::set<const BasicBlock*>& uses() const { return uses_; }
|
||||
|
||||
/// The words used to define the Id
|
||||
const std::vector<uint32_t>& words() const { return words_; }
|
||||
|
||||
private:
|
||||
/// The integer that identifies the Id
|
||||
uint32_t id_;
|
||||
|
||||
/// The type of the Id
|
||||
uint32_t type_id_;
|
||||
|
||||
/// The opcode used to define the Id
|
||||
SpvOp opcode_;
|
||||
|
||||
/// The function in which the Id was defined
|
||||
Function* defining_function_;
|
||||
|
||||
/// The block in which the Id was defined
|
||||
BasicBlock* defining_block_;
|
||||
|
||||
/// The blocks in which the Id was used
|
||||
std::set<const BasicBlock*> uses_;
|
||||
|
||||
/// The words of the instuction that defined the Id
|
||||
std::vector<uint32_t> words_;
|
||||
|
||||
#define OPERATOR(OP) \
|
||||
friend bool operator OP(const Id& lhs, const Id& rhs); \
|
||||
friend bool operator OP(const Id& lhs, uint32_t rhs)
|
||||
OPERATOR(<);
|
||||
OPERATOR(==);
|
||||
#undef OPERATOR
|
||||
};
|
||||
|
||||
#define OPERATOR(OP) \
|
||||
bool operator OP(const Id& lhs, const Id& rhs); \
|
||||
bool operator OP(const Id& lhs, uint32_t rhs)
|
||||
|
||||
OPERATOR(<);
|
||||
OPERATOR(==);
|
||||
#undef OPERATOR
|
||||
|
||||
} // namespace libspirv
|
||||
|
||||
// custom specialization of std::hash for Id
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<libspirv::Id> {
|
||||
typedef libspirv::Id argument_type;
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(const argument_type& id) const {
|
||||
return hash<uint32_t>()(id);
|
||||
}
|
||||
};
|
||||
} /// namespace std
|
||||
|
||||
#endif // LIBSPIRV_VAL_ID_H_
|
@ -24,39 +24,38 @@
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
|
||||
#include "val/Id.h"
|
||||
#include "val/Instruction.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
using std::make_pair;
|
||||
|
||||
namespace libspirv {
|
||||
#define OPERATOR(OP) \
|
||||
bool operator OP(const Id& lhs, const Id& rhs) { \
|
||||
return lhs.id_ OP rhs.id_; \
|
||||
bool operator OP(const Instruction& lhs, const Instruction& rhs) { \
|
||||
return lhs.id() OP rhs.id(); \
|
||||
} \
|
||||
bool operator OP(const Id& lhs, uint32_t rhs) { return lhs.id_ OP rhs; }
|
||||
bool operator OP(const Instruction& lhs, uint32_t rhs) { \
|
||||
return lhs.id() OP rhs; \
|
||||
}
|
||||
|
||||
OPERATOR(<)
|
||||
OPERATOR(==)
|
||||
#undef OPERATOR
|
||||
|
||||
Id::Id(const uint32_t result_id)
|
||||
: id_(result_id),
|
||||
type_id_(0),
|
||||
opcode_(SpvOpNop),
|
||||
defining_function_(nullptr),
|
||||
defining_block_(nullptr),
|
||||
uses_(),
|
||||
words_(0) {}
|
||||
Instruction::Instruction(const spv_parsed_instruction_t* inst,
|
||||
Function* defining_function,
|
||||
BasicBlock* defining_block)
|
||||
: words_(inst->words, inst->words + inst->num_words),
|
||||
operands_(inst->operands, inst->operands + inst->num_operands),
|
||||
inst_({words_.data(), inst->num_words, inst->opcode, inst->ext_inst_type,
|
||||
inst->type_id, inst->result_id, operands_.data(),
|
||||
inst->num_operands}),
|
||||
function_(defining_function),
|
||||
block_(defining_block),
|
||||
uses_() {}
|
||||
|
||||
Id::Id(const spv_parsed_instruction_t* inst, Function* function,
|
||||
BasicBlock* block)
|
||||
: id_(inst->result_id),
|
||||
type_id_(inst->type_id),
|
||||
opcode_(static_cast<SpvOp>(inst->opcode)),
|
||||
defining_function_(function),
|
||||
defining_block_(block),
|
||||
uses_(),
|
||||
words_(inst->words, inst->words + inst->num_words) {}
|
||||
|
||||
void Id::RegisterUse(const BasicBlock* block) {
|
||||
if (block) { uses_.insert(block); }
|
||||
void Instruction::RegisterUse(const Instruction* inst, uint32_t index) {
|
||||
uses_.push_back(make_pair(inst, index));
|
||||
}
|
||||
} // namespace libspirv
|
131
source/val/Instruction.h
Normal file
131
source/val/Instruction.h
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2015-2016 The Khronos Group Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and/or associated documentation files (the
|
||||
// "Materials"), to deal in the Materials without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
// permit persons to whom the Materials are furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Materials.
|
||||
//
|
||||
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
|
||||
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
|
||||
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
|
||||
// https://www.khronos.org/registry/
|
||||
//
|
||||
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
|
||||
#ifndef LIBSPIRV_VAL_INSTRUCTION_H_
|
||||
#define LIBSPIRV_VAL_INSTRUCTION_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "table.h"
|
||||
|
||||
namespace libspirv {
|
||||
|
||||
class BasicBlock;
|
||||
class Function;
|
||||
|
||||
/// Wraps the spv_parsed_instruction struct along with use and definition of the
|
||||
/// instruction's result id
|
||||
class Instruction {
|
||||
public:
|
||||
explicit Instruction(const spv_parsed_instruction_t* inst,
|
||||
Function* defining_function = nullptr,
|
||||
BasicBlock* defining_block = nullptr);
|
||||
|
||||
/// Registers the use of the Instruction in instruction \p inst at \p index
|
||||
void RegisterUse(const Instruction* inst, uint32_t index);
|
||||
|
||||
uint32_t id() const { return inst_.result_id; }
|
||||
uint32_t type_id() const { return inst_.type_id; }
|
||||
SpvOp opcode() const { return static_cast<SpvOp>(inst_.opcode); }
|
||||
|
||||
/// Returns the Function where the instruction was defined. nullptr if it was
|
||||
/// defined outside of a Function
|
||||
const Function* function() const { return function_; }
|
||||
|
||||
/// Returns the BasicBlock where the instruction was defined. nullptr if it
|
||||
/// was defined outside of a BasicBlock
|
||||
const BasicBlock* block() const { return block_; }
|
||||
|
||||
/// Returns a vector of pairs of all references to this instruction's result
|
||||
/// id. The first element is the instruction in which this result id was
|
||||
/// referenced and the second is the index of the word in that instruction
|
||||
/// where this result id appeared
|
||||
const std::vector<std::pair<const Instruction*, uint32_t>>& uses() const {
|
||||
return uses_;
|
||||
}
|
||||
|
||||
/// The word used to define the Instruction
|
||||
uint32_t words(size_t index) const { return words_[index]; }
|
||||
|
||||
/// The words used to define the Instruction
|
||||
const std::vector<uint32_t>& words() const { return words_; }
|
||||
|
||||
/// The operand of the Instruction at \p index
|
||||
const spv_parsed_operand_t& operands(size_t index) const {
|
||||
return operands_[index];
|
||||
}
|
||||
|
||||
/// The operands of the Instruction
|
||||
const std::vector<spv_parsed_operand_t>& operands() const {
|
||||
return operands_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<uint32_t> words_;
|
||||
const std::vector<spv_parsed_operand_t> operands_;
|
||||
spv_parsed_instruction_t inst_;
|
||||
|
||||
/// The function in which this instruction was declared
|
||||
Function* function_;
|
||||
|
||||
/// The basic block in which this instruction was declared
|
||||
BasicBlock* block_;
|
||||
|
||||
/// This is a vector of pairs of all references to this instruction's result
|
||||
/// id. The first element is the instruction in which this result id was
|
||||
/// referenced and the second is the index of the word in the referencing
|
||||
/// instruction where this instruction appeared
|
||||
std::vector<std::pair<const Instruction*, uint32_t>> uses_;
|
||||
};
|
||||
|
||||
#define OPERATOR(OP) \
|
||||
bool operator OP(const Instruction& lhs, const Instruction& rhs); \
|
||||
bool operator OP(const Instruction& lhs, uint32_t rhs)
|
||||
|
||||
OPERATOR(<);
|
||||
OPERATOR(==);
|
||||
#undef OPERATOR
|
||||
|
||||
} // namespace libspirv
|
||||
|
||||
// custom specialization of std::hash for Instruction
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<libspirv::Instruction> {
|
||||
typedef libspirv::Instruction argument_type;
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(const argument_type& inst) const {
|
||||
return hash<uint32_t>()(inst.id());
|
||||
}
|
||||
};
|
||||
} /// namespace std
|
||||
|
||||
#endif // LIBSPIRV_VAL_INSTRUCTION_H_
|
@ -202,6 +202,8 @@ ValidationState_t::ValidationState_t(spv_diagnostic* diagnostic,
|
||||
current_layout_section_(kLayoutCapabilities),
|
||||
module_functions_(),
|
||||
module_capabilities_(0u),
|
||||
ordered_instructions_(),
|
||||
all_definitions_(),
|
||||
grammar_(context),
|
||||
addressing_model_(SpvAddressingModelLogical),
|
||||
memory_model_(SpvMemoryModelSimple),
|
||||
@ -251,20 +253,25 @@ vector<uint32_t> ValidationState_t::UnresolvedForwardIds() const {
|
||||
}
|
||||
|
||||
bool ValidationState_t::IsDefinedId(uint32_t id) const {
|
||||
return all_definitions_.find(Id{id}) != end(all_definitions_);
|
||||
return all_definitions_.find(id) != end(all_definitions_);
|
||||
}
|
||||
|
||||
const Id* ValidationState_t::FindDef(uint32_t id) const {
|
||||
if (all_definitions_.count(Id{id}) == 0) {
|
||||
const Instruction* ValidationState_t::FindDef(uint32_t id) const {
|
||||
if (all_definitions_.count(id) == 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
/// We are in a const function, so we cannot use defs.operator[]().
|
||||
/// Luckily we know the key exists, so defs_.at() won't throw an
|
||||
/// exception.
|
||||
return &all_definitions_.at(id);
|
||||
return all_definitions_.at(id);
|
||||
}
|
||||
}
|
||||
|
||||
Instruction* ValidationState_t::FindDef(uint32_t id) {
|
||||
return const_cast<Instruction*>(
|
||||
const_cast<const ValidationState_t*>(this)->FindDef(id));
|
||||
}
|
||||
|
||||
// Increments the instruction count. Used for diagnostic
|
||||
int ValidationState_t::increment_instruction_count() {
|
||||
return instruction_counter_++;
|
||||
@ -370,26 +377,17 @@ spv_result_t ValidationState_t::RegisterFunctionEnd() {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
void ValidationState_t::AddId(const spv_parsed_instruction_t& inst) {
|
||||
void ValidationState_t::RegisterInstruction(
|
||||
const spv_parsed_instruction_t& inst) {
|
||||
if (in_function_body()) {
|
||||
if (in_block()) {
|
||||
all_definitions_[inst.result_id] =
|
||||
Id{&inst, ¤t_function(), current_function().current_block()};
|
||||
ordered_instructions_.emplace_back(
|
||||
&inst, ¤t_function(), current_function().current_block());
|
||||
} else {
|
||||
all_definitions_[inst.result_id] = Id{&inst, ¤t_function()};
|
||||
ordered_instructions_.emplace_back(&inst, nullptr, nullptr);
|
||||
}
|
||||
} else {
|
||||
all_definitions_[inst.result_id] = Id{&inst};
|
||||
}
|
||||
}
|
||||
|
||||
void ValidationState_t::RegisterUseId(uint32_t used_id) {
|
||||
auto used = all_definitions_.find(used_id);
|
||||
if (used != end(all_definitions_)) {
|
||||
if (in_function_body())
|
||||
used->second.RegisterUse(current_function().current_block());
|
||||
else
|
||||
used->second.RegisterUse(nullptr);
|
||||
uint32_t id = ordered_instructions_.back().id();
|
||||
if (id) {
|
||||
all_definitions_.insert(make_pair(id, &ordered_instructions_.back()));
|
||||
}
|
||||
}
|
||||
} /// namespace libspirv
|
||||
|
@ -28,7 +28,6 @@
|
||||
#define LIBSPIRV_VAL_VALIDATIONSTATE_H_
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@ -40,7 +39,7 @@
|
||||
#include "spirv/1.1/spirv.h"
|
||||
#include "spirv_definition.h"
|
||||
#include "val/Function.h"
|
||||
#include "val/Id.h"
|
||||
#include "val/Instruction.h"
|
||||
|
||||
namespace libspirv {
|
||||
|
||||
@ -160,17 +159,24 @@ class ValidationState_t {
|
||||
|
||||
AssemblyGrammar& grammar() { return grammar_; }
|
||||
|
||||
/// Adds an id to the module
|
||||
void AddId(const spv_parsed_instruction_t& inst);
|
||||
|
||||
/// Register Id use
|
||||
void RegisterUseId(uint32_t used_id);
|
||||
/// Registers the instruction
|
||||
void RegisterInstruction(const spv_parsed_instruction_t& inst);
|
||||
|
||||
/// Finds id's def, if it exists. If found, returns the definition otherwise
|
||||
/// nullptr
|
||||
const Id* FindDef(uint32_t id) const;
|
||||
const Instruction* FindDef(uint32_t id) const;
|
||||
|
||||
const std::unordered_map<uint32_t, Id>& all_definitions() const {
|
||||
/// Finds id's def, if it exists. If found, returns the definition otherwise
|
||||
/// nullptr
|
||||
Instruction* FindDef(uint32_t id);
|
||||
|
||||
/// Returns a vector of instructions in the order they appear in the binary
|
||||
const std::list<Instruction>& ordered_instructions() {
|
||||
return ordered_instructions_;
|
||||
}
|
||||
|
||||
/// Returns a map of instructions mapped by their result id
|
||||
const std::unordered_map<uint32_t, Instruction*>& all_definitions() const {
|
||||
return all_definitions_;
|
||||
}
|
||||
|
||||
@ -185,7 +191,7 @@ class ValidationState_t {
|
||||
std::unordered_set<uint32_t> unresolved_forward_ids_;
|
||||
|
||||
/// A map of operand IDs and their names defined by the OpName instruction
|
||||
std::map<uint32_t, std::string> operand_names_;
|
||||
std::unordered_map<uint32_t, std::string> operand_names_;
|
||||
|
||||
/// The section of the code being processed
|
||||
ModuleLayoutSection current_layout_section_;
|
||||
@ -197,7 +203,11 @@ class ValidationState_t {
|
||||
spv_capability_mask_t
|
||||
module_capabilities_; /// Module's declared capabilities.
|
||||
|
||||
std::unordered_map<uint32_t, Id> all_definitions_;
|
||||
/// List of all instructions in the order they appear in the binary
|
||||
std::list<Instruction> ordered_instructions_;
|
||||
|
||||
/// Instructions that can be referenced by Ids
|
||||
std::unordered_map<uint32_t, Instruction*> all_definitions_;
|
||||
|
||||
/// IDs that are entry points, ie, arguments to OpEntryPoint.
|
||||
std::vector<uint32_t> entry_points_;
|
||||
|
@ -229,6 +229,7 @@ spv_result_t spvValidate(const spv_const_context context,
|
||||
// CFG checks are performed after the binary has been parsed
|
||||
// and the CFGPass has collected information about the control flow
|
||||
spvCheckReturn(PerformCfgChecks(vstate));
|
||||
spvCheckReturn(UpdateIdUse(vstate));
|
||||
spvCheckReturn(CheckIdDefinitionDominateUse(vstate));
|
||||
|
||||
// NOTE: Copy each instruction for easier processing
|
||||
|
@ -101,6 +101,16 @@ std::vector<std::pair<BasicBlock*, BasicBlock*>> CalculateDominators(
|
||||
/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_CFG otherwise
|
||||
spv_result_t PerformCfgChecks(ValidationState_t& _);
|
||||
|
||||
/// @brief Updates the use vectors of all instructions that can be referenced
|
||||
///
|
||||
/// This function will update the vector which define where an instruction was
|
||||
/// referenced in the binary.
|
||||
///
|
||||
/// @param[in] _ the validation state of the module
|
||||
///
|
||||
/// @return SPV_SUCCESS if no errors are found.
|
||||
spv_result_t UpdateIdUse(ValidationState_t& _);
|
||||
|
||||
/// @brief This function checks all ID definitions dominate their use in the
|
||||
/// CFG.
|
||||
///
|
||||
|
@ -51,7 +51,6 @@ using std::make_pair;
|
||||
using std::make_tuple;
|
||||
using std::numeric_limits;
|
||||
using std::pair;
|
||||
using std::set;
|
||||
using std::string;
|
||||
using std::tie;
|
||||
using std::transform;
|
||||
|
@ -30,13 +30,15 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "diagnostic.h"
|
||||
#include "instruction.h"
|
||||
#include "opcode.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "val/Function.h"
|
||||
#include "val/ValidationState.h"
|
||||
|
||||
#define spvCheck(condition, action) \
|
||||
@ -47,6 +49,10 @@
|
||||
using libspirv::ValidationState_t;
|
||||
using std::function;
|
||||
using std::ignore;
|
||||
using std::make_pair;
|
||||
using std::pair;
|
||||
using std::unordered_set;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
|
||||
@ -58,9 +64,8 @@ class idUsage {
|
||||
const spv_instruction_t* pInsts, const uint64_t instCountArg,
|
||||
const SpvMemoryModel memoryModelArg,
|
||||
const SpvAddressingModel addressingModelArg,
|
||||
const ValidationState_t& module,
|
||||
const std::vector<uint32_t>& entry_points, spv_position positionArg,
|
||||
spv_diagnostic* pDiagnosticArg)
|
||||
const ValidationState_t& module, const vector<uint32_t>& entry_points,
|
||||
spv_position positionArg, spv_diagnostic* pDiagnosticArg)
|
||||
: opcodeTable(opcodeTableArg),
|
||||
operandTable(operandTableArg),
|
||||
extInstTable(extInstTableArg),
|
||||
@ -89,7 +94,7 @@ class idUsage {
|
||||
spv_position position;
|
||||
spv_diagnostic* pDiagnostic;
|
||||
const ValidationState_t& module_;
|
||||
std::vector<uint32_t> entry_points_;
|
||||
vector<uint32_t> entry_points_;
|
||||
};
|
||||
|
||||
#define DIAG(INDEX) \
|
||||
@ -278,8 +283,8 @@ bool idUsage::isValid<SpvOpTypeSampler>(const spv_instruction_t*,
|
||||
// constant-defining instruction (either OpConstant or
|
||||
// OpSpecConstant). typeWords are the words of the constant's-type-defining
|
||||
// OpTypeInt.
|
||||
bool aboveZero(const std::vector<uint32_t>& constWords,
|
||||
const std::vector<uint32_t>& typeWords) {
|
||||
bool aboveZero(const vector<uint32_t>& constWords,
|
||||
const vector<uint32_t>& typeWords) {
|
||||
const uint32_t width = typeWords[2];
|
||||
const bool is_signed = typeWords[3] > 0;
|
||||
const uint32_t loWord = constWords[3];
|
||||
@ -630,7 +635,7 @@ bool idUsage::isValid<SpvOpConstantSampler>(const spv_instruction_t* inst,
|
||||
// True if instruction defines a type that can have a null value, as defined by
|
||||
// the SPIR-V spec. Tracks composite-type components through module to check
|
||||
// nullability transitively.
|
||||
bool IsTypeNullable(const std::vector<uint32_t>& instruction,
|
||||
bool IsTypeNullable(const vector<uint32_t>& instruction,
|
||||
const ValidationState_t& module) {
|
||||
uint16_t opcode;
|
||||
uint16_t word_count;
|
||||
@ -2377,6 +2382,29 @@ function<bool(unsigned)> getCanBeForwardDeclaredFunction(SpvOp opcode) {
|
||||
|
||||
namespace libspirv {
|
||||
|
||||
spv_result_t UpdateIdUse(ValidationState_t& _) {
|
||||
for (const auto& inst : _.ordered_instructions()) {
|
||||
for (auto& operand : inst.operands()) {
|
||||
const spv_operand_type_t& type = operand.type;
|
||||
const uint32_t operand_id = inst.words()[operand.offset];
|
||||
|
||||
switch (type) {
|
||||
case SPV_OPERAND_TYPE_VARIABLE_ID:
|
||||
case SPV_OPERAND_TYPE_ID:
|
||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
|
||||
case SPV_OPERAND_TYPE_SCOPE_ID:
|
||||
if (auto def = _.FindDef(operand_id))
|
||||
def->RegisterUse(&inst, operand.offset);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
/// This function checks all ID definitions dominate their use in the CFG.
|
||||
///
|
||||
/// This function will iterate over all ID definitions that are defined in the
|
||||
@ -2387,32 +2415,38 @@ namespace libspirv {
|
||||
/// checked during the initial binary parse in the IdPass below
|
||||
spv_result_t CheckIdDefinitionDominateUse(const ValidationState_t& _) {
|
||||
for (const auto& definition : _.all_definitions()) {
|
||||
// Check only those blocks defined in a function
|
||||
if (const Function* func = definition.second.defining_function()) {
|
||||
if (const BasicBlock* block = definition.second.defining_block()) {
|
||||
// Check only those definitions defined in a function
|
||||
if (const Function* func = definition.second->function()) {
|
||||
if (const BasicBlock* block = definition.second->block()) {
|
||||
if (!block->reachable()) continue;
|
||||
// If the Id is defined within a block then make sure all references to
|
||||
// that Id appear in a blocks that are dominated by the defining block
|
||||
for (auto use : definition.second.uses()) {
|
||||
if (!use->reachable()) continue;
|
||||
if (use->dom_end() == find(use->dom_begin(), use->dom_end(), block)) {
|
||||
for (auto& use_index_pair : definition.second->uses()) {
|
||||
const Instruction* use = use_index_pair.first;
|
||||
if (const BasicBlock* use_block = use->block()) {
|
||||
if (!use_block->reachable()) continue;
|
||||
if (use->opcode() != SpvOpPhi &&
|
||||
use_block->dom_end() ==
|
||||
find(use_block->dom_begin(), use_block->dom_end(), block)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID)
|
||||
<< "ID " << _.getIdName(definition.first)
|
||||
<< " defined in block " << _.getIdName(block->id())
|
||||
<< " does not dominate its use in block "
|
||||
<< _.getIdName(use->id());
|
||||
<< _.getIdName(use_block->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the Ids defined within a function but not in a block(i.e. function
|
||||
// parameters, block ids), then make sure all references to that Id
|
||||
// appear within the same function
|
||||
bool found = false;
|
||||
for (auto use : definition.second.uses()) {
|
||||
tie(ignore, found) = func->GetBlock(use->id());
|
||||
if (!found) {
|
||||
for (auto use : definition.second->uses()) {
|
||||
const Instruction* inst = use.first;
|
||||
if (inst->function() && inst->function() != func) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID)
|
||||
<< "ID " << _.getIdName(definition.first)
|
||||
<< " used in block " << _.getIdName(use->id())
|
||||
<< " used in function "
|
||||
<< _.getIdName(inst->function()->id())
|
||||
<< " is used outside of it's defining function "
|
||||
<< _.getIdName(func->id());
|
||||
}
|
||||
@ -2455,17 +2489,6 @@ spv_result_t IdPass(ValidationState_t& _,
|
||||
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
|
||||
case SPV_OPERAND_TYPE_SCOPE_ID:
|
||||
if (_.IsDefinedId(*operand_ptr)) {
|
||||
if (inst->opcode == SpvOpPhi && i > 1) {
|
||||
// For now, ignore uses of IDs as arguments to OpPhi, since
|
||||
// the job of an OpPhi is to allow a block to use an ID from a
|
||||
// block that doesn't dominate the use.
|
||||
// We only track usage by a particular block, rather than
|
||||
// which instruction and operand number is using the value, so
|
||||
// we have to just bluntly avod tracking the use here.
|
||||
// Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/286
|
||||
} else {
|
||||
_.RegisterUseId(*operand_ptr);
|
||||
}
|
||||
ret = SPV_SUCCESS;
|
||||
} else if (can_have_forward_declared_ids(i)) {
|
||||
ret = _.ForwardDeclareId(*operand_ptr);
|
||||
@ -2483,9 +2506,7 @@ spv_result_t IdPass(ValidationState_t& _,
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (inst->result_id) {
|
||||
_.AddId(*inst);
|
||||
}
|
||||
_.RegisterInstruction(*inst);
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
} // namespace libspirv
|
||||
|
@ -1234,9 +1234,9 @@ TEST_F(ValidateSSA, DISABLED_PhiVariableDefMustComeFromBlockDominatingThePredece
|
||||
// TODO(dneto): Check for a good error message
|
||||
}
|
||||
|
||||
TEST_F(ValidateSSA, DominanceCheckIgnoresUsesInUnreachableBlocksDefInBlockGood) {
|
||||
string str = kHeader
|
||||
+ kBasicTypes +
|
||||
TEST_F(ValidateSSA,
|
||||
DominanceCheckIgnoresUsesInUnreachableBlocksDefInBlockGood) {
|
||||
string str = kHeader + kBasicTypes +
|
||||
R"(
|
||||
%func = OpFunction %voidt None %vfunct
|
||||
%entry = OpLabel
|
||||
@ -1253,7 +1253,8 @@ TEST_F(ValidateSSA, DominanceCheckIgnoresUsesInUnreachableBlocksDefInBlockGood)
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
|
||||
}
|
||||
|
||||
TEST_F(ValidateSSA, DominanceCheckIgnoresUsesInUnreachableBlocksDefIsParamGood) {
|
||||
TEST_F(ValidateSSA,
|
||||
DominanceCheckIgnoresUsesInUnreachableBlocksDefIsParamGood) {
|
||||
string str = kHeader + kBasicTypes +
|
||||
R"(
|
||||
%void_fn_int = OpTypeFunction %voidt %intt
|
||||
@ -1275,9 +1276,8 @@ TEST_F(ValidateSSA, DominanceCheckIgnoresUsesInUnreachableBlocksDefIsParamGood)
|
||||
TEST_F(ValidateSSA, UseFunctionParameterFromOtherFunctionBad) {
|
||||
string str = kHeader +
|
||||
"OpName %first \"first\"\n"
|
||||
"OpName %entry2 \"entry2\"\n"
|
||||
"OpName %func \"func\"\n" +
|
||||
kBasicTypes +
|
||||
"OpName %func2 \"func2\"\n" + kBasicTypes +
|
||||
R"(
|
||||
%viifunct = OpTypeFunction %voidt %intt %intt
|
||||
%func = OpFunction %voidt None %viifunct
|
||||
@ -1297,7 +1297,7 @@ TEST_F(ValidateSSA, UseFunctionParameterFromOtherFunctionBad) {
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
MatchesRegex("ID .\\[first\\] used in block .\\[entry2\\] is used "
|
||||
MatchesRegex("ID .\\[first\\] used in function .\\[func2\\] is used "
|
||||
"outside of it's defining function .\\[func\\]"));
|
||||
}
|
||||
// TODO(umar): OpGroupMemberDecorate
|
||||
|
Loading…
Reference in New Issue
Block a user