SPIRV-Tools/source/val/ValidationState.cpp
Lei Zhang 755f97f534 Add a callback mechanism for communicating messages to callers.
Every time an event happens in the library that the user should be
aware of, the callback will be invoked.

The existing diagnostic mechanism is hijacked internally by a
callback that creates an diagnostic object each time an event
happens.
2016-09-15 12:35:48 -04:00

390 lines
12 KiB
C++

// Copyright (c) 2015-2016 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "val/ValidationState.h"
#include <cassert>
#include "val/BasicBlock.h"
#include "val/Construct.h"
#include "val/Function.h"
using std::deque;
using std::make_pair;
using std::pair;
using std::string;
using std::unordered_map;
using std::vector;
namespace libspirv {
namespace {
bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
// See Section 2.4
bool out = false;
// clang-format off
switch (layout) {
case kLayoutCapabilities: out = op == SpvOpCapability; break;
case kLayoutExtensions: out = op == SpvOpExtension; break;
case kLayoutExtInstImport: out = op == SpvOpExtInstImport; break;
case kLayoutMemoryModel: out = op == SpvOpMemoryModel; break;
case kLayoutEntryPoint: out = op == SpvOpEntryPoint; break;
case kLayoutExecutionMode: out = op == SpvOpExecutionMode; break;
case kLayoutDebug1:
switch (op) {
case SpvOpSourceContinued:
case SpvOpSource:
case SpvOpSourceExtension:
case SpvOpString:
out = true;
break;
default: break;
}
break;
case kLayoutDebug2:
switch (op) {
case SpvOpName:
case SpvOpMemberName:
out = true;
break;
default: break;
}
break;
case kLayoutAnnotations:
switch (op) {
case SpvOpDecorate:
case SpvOpMemberDecorate:
case SpvOpGroupDecorate:
case SpvOpGroupMemberDecorate:
case SpvOpDecorationGroup:
out = true;
break;
default: break;
}
break;
case kLayoutTypes:
switch (op) {
case SpvOpTypeVoid:
case SpvOpTypeBool:
case SpvOpTypeInt:
case SpvOpTypeFloat:
case SpvOpTypeVector:
case SpvOpTypeMatrix:
case SpvOpTypeImage:
case SpvOpTypeSampler:
case SpvOpTypeSampledImage:
case SpvOpTypeArray:
case SpvOpTypeRuntimeArray:
case SpvOpTypeStruct:
case SpvOpTypeOpaque:
case SpvOpTypePointer:
case SpvOpTypeFunction:
case SpvOpTypeEvent:
case SpvOpTypeDeviceEvent:
case SpvOpTypeReserveId:
case SpvOpTypeQueue:
case SpvOpTypePipe:
case SpvOpTypeForwardPointer:
case SpvOpConstantTrue:
case SpvOpConstantFalse:
case SpvOpConstant:
case SpvOpConstantComposite:
case SpvOpConstantSampler:
case SpvOpConstantNull:
case SpvOpSpecConstantTrue:
case SpvOpSpecConstantFalse:
case SpvOpSpecConstant:
case SpvOpSpecConstantComposite:
case SpvOpSpecConstantOp:
case SpvOpVariable:
case SpvOpLine:
case SpvOpNoLine:
case SpvOpUndef:
out = true;
break;
default: break;
}
break;
case kLayoutFunctionDeclarations:
case kLayoutFunctionDefinitions:
// NOTE: These instructions should NOT be in these layout sections
switch (op) {
case SpvOpCapability:
case SpvOpExtension:
case SpvOpExtInstImport:
case SpvOpMemoryModel:
case SpvOpEntryPoint:
case SpvOpExecutionMode:
case SpvOpSourceContinued:
case SpvOpSource:
case SpvOpSourceExtension:
case SpvOpString:
case SpvOpName:
case SpvOpMemberName:
case SpvOpDecorate:
case SpvOpMemberDecorate:
case SpvOpGroupDecorate:
case SpvOpGroupMemberDecorate:
case SpvOpDecorationGroup:
case SpvOpTypeVoid:
case SpvOpTypeBool:
case SpvOpTypeInt:
case SpvOpTypeFloat:
case SpvOpTypeVector:
case SpvOpTypeMatrix:
case SpvOpTypeImage:
case SpvOpTypeSampler:
case SpvOpTypeSampledImage:
case SpvOpTypeArray:
case SpvOpTypeRuntimeArray:
case SpvOpTypeStruct:
case SpvOpTypeOpaque:
case SpvOpTypePointer:
case SpvOpTypeFunction:
case SpvOpTypeEvent:
case SpvOpTypeDeviceEvent:
case SpvOpTypeReserveId:
case SpvOpTypeQueue:
case SpvOpTypePipe:
case SpvOpTypeForwardPointer:
case SpvOpConstantTrue:
case SpvOpConstantFalse:
case SpvOpConstant:
case SpvOpConstantComposite:
case SpvOpConstantSampler:
case SpvOpConstantNull:
case SpvOpSpecConstantTrue:
case SpvOpSpecConstantFalse:
case SpvOpSpecConstant:
case SpvOpSpecConstantComposite:
case SpvOpSpecConstantOp:
out = false;
break;
default:
out = true;
break;
}
}
// clang-format on
return out;
}
} // anonymous namespace
ValidationState_t::ValidationState_t(const spv_const_context ctx)
: context_(ctx),
instruction_counter_(0),
unresolved_forward_ids_{},
operand_names_{},
current_layout_section_(kLayoutCapabilities),
module_functions_(),
module_capabilities_(),
ordered_instructions_(),
all_definitions_(),
grammar_(ctx),
addressing_model_(SpvAddressingModelLogical),
memory_model_(SpvMemoryModelSimple),
in_function_(false) {}
spv_result_t ValidationState_t::ForwardDeclareId(uint32_t id) {
unresolved_forward_ids_.insert(id);
return SPV_SUCCESS;
}
spv_result_t ValidationState_t::RemoveIfForwardDeclared(uint32_t id) {
unresolved_forward_ids_.erase(id);
return SPV_SUCCESS;
}
void ValidationState_t::AssignNameToId(uint32_t id, string name) {
operand_names_[id] = name;
}
string ValidationState_t::getIdName(uint32_t id) const {
std::stringstream out;
out << id;
if (operand_names_.find(id) != end(operand_names_)) {
out << "[" << operand_names_.at(id) << "]";
}
return out.str();
}
string ValidationState_t::getIdOrName(uint32_t id) const {
std::stringstream out;
if (operand_names_.find(id) != end(operand_names_)) {
out << operand_names_.at(id);
} else {
out << id;
}
return out.str();
}
size_t ValidationState_t::unresolved_forward_id_count() const {
return unresolved_forward_ids_.size();
}
vector<uint32_t> ValidationState_t::UnresolvedForwardIds() const {
vector<uint32_t> out(begin(unresolved_forward_ids_),
end(unresolved_forward_ids_));
return out;
}
bool ValidationState_t::IsDefinedId(uint32_t id) const {
return all_definitions_.find(id) != end(all_definitions_);
}
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);
}
}
Instruction* ValidationState_t::FindDef(uint32_t id) {
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);
}
}
// Increments the instruction count. Used for diagnostic
int ValidationState_t::increment_instruction_count() {
return instruction_counter_++;
}
ModuleLayoutSection ValidationState_t::current_layout_section() const {
return current_layout_section_;
}
void ValidationState_t::ProgressToNextLayoutSectionOrder() {
// Guard against going past the last element(kLayoutFunctionDefinitions)
if (current_layout_section_ <= kLayoutFunctionDefinitions) {
current_layout_section_ =
static_cast<ModuleLayoutSection>(current_layout_section_ + 1);
}
}
bool ValidationState_t::IsOpcodeInCurrentLayoutSection(SpvOp op) {
return IsInstructionInLayoutSection(current_layout_section_, op);
}
DiagnosticStream ValidationState_t::diag(spv_result_t error_code) const {
return libspirv::DiagnosticStream(
{0, 0, static_cast<size_t>(instruction_counter_)}, context_->consumer,
error_code);
}
deque<Function>& ValidationState_t::functions() { return module_functions_; }
Function& ValidationState_t::current_function() {
assert(in_function_body());
return module_functions_.back();
}
bool ValidationState_t::in_function_body() const { return in_function_; }
bool ValidationState_t::in_block() const {
return module_functions_.empty() == false &&
module_functions_.back().current_block() != nullptr;
}
void ValidationState_t::RegisterCapability(SpvCapability cap) {
// Avoid redundant work. Otherwise the recursion could induce work
// quadrdatic in the capability dependency depth. (Ok, not much, but
// it's something.)
if (module_capabilities_.Contains(cap)) return;
module_capabilities_.Add(cap);
spv_operand_desc desc;
if (SPV_SUCCESS ==
grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) {
desc->capabilities.ForEach(
[this](SpvCapability c) { RegisterCapability(c); });
}
}
bool ValidationState_t::HasAnyOf(const CapabilitySet& capabilities) const {
bool found = false;
bool any_queried = false;
capabilities.ForEach([&found, &any_queried, this](SpvCapability c) {
any_queried = true;
found = found || this->module_capabilities_.Contains(c);
});
return !any_queried || found;
}
void ValidationState_t::set_addressing_model(SpvAddressingModel am) {
addressing_model_ = am;
}
SpvAddressingModel ValidationState_t::addressing_model() const {
return addressing_model_;
}
void ValidationState_t::set_memory_model(SpvMemoryModel mm) {
memory_model_ = mm;
}
SpvMemoryModel ValidationState_t::memory_model() const { return memory_model_; }
spv_result_t ValidationState_t::RegisterFunction(
uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control,
uint32_t function_type_id) {
assert(in_function_body() == false &&
"RegisterFunction can only be called when parsing the binary outside "
"of another function");
in_function_ = true;
module_functions_.emplace_back(id, ret_type_id, function_control,
function_type_id);
// TODO(umar): validate function type and type_id
return SPV_SUCCESS;
}
spv_result_t ValidationState_t::RegisterFunctionEnd() {
assert(in_function_body() == true &&
"RegisterFunctionEnd can only be called when parsing the binary "
"inside of another function");
assert(in_block() == false &&
"RegisterFunctionParameter can only be called when parsing the binary "
"ouside of a block");
current_function().RegisterFunctionEnd();
in_function_ = false;
return SPV_SUCCESS;
}
void ValidationState_t::RegisterInstruction(
const spv_parsed_instruction_t& inst) {
if (in_function_body()) {
ordered_instructions_.emplace_back(&inst, &current_function(),
current_function().current_block());
} else {
ordered_instructions_.emplace_back(&inst, nullptr, nullptr);
}
uint32_t id = ordered_instructions_.back().id();
if (id) {
all_definitions_.insert(make_pair(id, &ordered_instructions_.back()));
}
}
} /// namespace libspirv