Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2021-02-05 19:58:13 -08:00
parent b9d9a387eb
commit b9d37691cf
21 changed files with 267 additions and 74 deletions

View File

@ -1 +1 @@
"v2020.7-dev", "SPIRV-Tools v2020.7-dev 93efa0c36bc823ed865bd45418c57390547cef74"
"v2020.7-dev", "SPIRV-Tools v2020.7-dev 45234794d9cc274023fbed948636fd006b589aef"

View File

@ -626,6 +626,12 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout(
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetScalarBlockLayout(
spv_validator_options options, bool val);
// Records whether the validator should use "scalar" block layout
// rules (as defined above) for Workgroup blocks. See Vulkan
// extension VK_KHR_workgroup_memory_explicit_layout.
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetWorkgroupScalarBlockLayout(
spv_validator_options options, bool val);
// Records whether or not the validator should skip validating standard
// uniform/storage block layout.
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout(

View File

@ -104,6 +104,12 @@ class ValidatorOptions {
spvValidatorOptionsSetScalarBlockLayout(options_, val);
}
// Enables scalar layout when validating Workgroup blocks. See
// VK_KHR_workgroup_memory_explicit_layout.
void SetWorkgroupScalarBlockLayout(bool val) {
spvValidatorOptionsSetWorkgroupScalarBlockLayout(options_, val);
}
// Skips validating standard uniform/storage buffer/push-constant layout.
void SetSkipBlockLayout(bool val) {
spvValidatorOptionsSetSkipBlockLayout(options_, val);

View File

@ -147,8 +147,8 @@ bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) {
return true;
}
void ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) {
context()->get_decoration_mgr()->RemoveDecorationsFrom(
bool ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) {
return context()->get_decoration_mgr()->RemoveDecorationsFrom(
id, [](const Instruction& dec) {
if (dec.opcode() == SpvOpDecorate &&
dec.GetSingleWordInOperand(1u) == SpvDecorationRelaxedPrecision)
@ -344,10 +344,14 @@ Pass::Status ConvertToHalfPass::ProcessImpl() {
// If modified, make sure module has Float16 capability
if (modified) context()->AddCapability(SpvCapabilityFloat16);
// Remove all RelaxedPrecision decorations from instructions and globals
for (auto c_id : relaxed_ids_set_) RemoveRelaxedDecoration(c_id);
for (auto c_id : relaxed_ids_set_) {
modified |= RemoveRelaxedDecoration(c_id);
}
for (auto& val : get_module()->types_values()) {
uint32_t v_id = val.result_id();
if (v_id != 0) RemoveRelaxedDecoration(v_id);
if (v_id != 0) {
modified |= RemoveRelaxedDecoration(v_id);
}
}
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}

View File

@ -74,7 +74,7 @@ class ConvertToHalfPass : public Pass {
void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst);
// Remove RelaxedPrecision decoration of |id|.
void RemoveRelaxedDecoration(uint32_t id);
bool RemoveRelaxedDecoration(uint32_t id);
// Add |inst| to relaxed instruction set if warranted. Specifically, if
// it is float32 and either decorated relaxed or a composite or phi

View File

@ -500,14 +500,15 @@ bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
insert_before->opcode() == SpvOpVariable) {
insert_before = insert_before->NextNode();
}
modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id,
insert_before) != nullptr;
modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, insert_before,
scope_and_line) != nullptr;
}
return modified;
}
Instruction* DebugInfoManager::AddDebugValueForDecl(
Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before) {
Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before,
Instruction* scope_and_line) {
if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return nullptr;
std::unique_ptr<Instruction> dbg_val(dbg_decl->Clone(context()));
@ -517,6 +518,7 @@ Instruction* DebugInfoManager::AddDebugValueForDecl(
dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id});
dbg_val->SetOperand(kDebugValueOperandExpressionIndex,
{GetEmptyDebugExpression()->result_id()});
dbg_val->UpdateDebugInfoFrom(scope_and_line);
auto* added_dbg_val = insert_before->InsertBefore(std::move(dbg_val));
AnalyzeDebugInst(added_dbg_val);

View File

@ -152,11 +152,14 @@ class DebugInfoManager {
std::unordered_set<Instruction*>* invisible_decls);
// Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before
// |insert_before|. The new DebugValue has the same line, scope, and
// operands with DebugDeclare but it uses |value_id| for value. Returns
// the added DebugValue, or nullptr if it does not add a DebugValue.
// |insert_before|. The new DebugValue has the same line and scope as
// |scope_and_line|, or no scope and line information if |scope_and_line|
// is nullptr. The new DebugValue has the same operands as DebugDeclare
// but it uses |value_id| for the value. Returns the created DebugValue,
// or nullptr if fails to create one.
Instruction* AddDebugValueForDecl(Instruction* dbg_decl, uint32_t value_id,
Instruction* insert_before);
Instruction* insert_before,
Instruction* scope_and_line);
// Erases |instr| from data structures of this class.
void ClearDebugInfo(Instruction* instr);

View File

@ -53,11 +53,12 @@ namespace spvtools {
namespace opt {
namespace analysis {
void DecorationManager::RemoveDecorationsFrom(
bool DecorationManager::RemoveDecorationsFrom(
uint32_t id, std::function<bool(const Instruction&)> pred) {
bool was_modified = false;
const auto ids_iter = id_to_decoration_insts_.find(id);
if (ids_iter == id_to_decoration_insts_.end()) {
return;
return was_modified;
}
TargetData& decorations_info = ids_iter->second;
@ -99,7 +100,6 @@ void DecorationManager::RemoveDecorationsFrom(
// Otherwise, remove |id| from the targets of |group_id|
const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u;
bool was_modified = false;
for (uint32_t i = 1u; i < inst->NumInOperands();) {
if (inst->GetSingleWordInOperand(i) != id) {
i += stride;
@ -155,6 +155,7 @@ void DecorationManager::RemoveDecorationsFrom(
}),
indirect_decorations.end());
was_modified |= !insts_to_kill.empty();
for (Instruction* inst : insts_to_kill) context->KillInst(inst);
insts_to_kill.clear();
@ -165,6 +166,7 @@ void DecorationManager::RemoveDecorationsFrom(
for (Instruction* inst : decorations_info.decorate_insts)
insts_to_kill.push_back(inst);
}
was_modified |= !insts_to_kill.empty();
for (Instruction* inst : insts_to_kill) context->KillInst(inst);
if (decorations_info.direct_decorations.empty() &&
@ -172,6 +174,7 @@ void DecorationManager::RemoveDecorationsFrom(
decorations_info.decorate_insts.empty()) {
id_to_decoration_insts_.erase(ids_iter);
}
return was_modified;
}
std::vector<Instruction*> DecorationManager::GetDecorationsFor(

View File

@ -36,8 +36,9 @@ class DecorationManager {
}
DecorationManager() = delete;
// Changes all of the decorations (direct and through groups) where |pred| is
// true and that apply to |id| so that they no longer apply to |id|.
// Removes all decorations (direct and through groups) where |pred| is
// true and that apply to |id| so that they no longer apply to |id|. Returns
// true if something changed.
//
// If |id| is part of a group, it will be removed from the group if it
// does not use all of the group's decorations, or, if there are no
@ -52,9 +53,9 @@ class DecorationManager {
// removed, then the |OpGroupDecorate| and
// |OpGroupMemberDecorate| for the group will be killed, but not the defining
// |OpDecorationGroup| instruction.
void RemoveDecorationsFrom(uint32_t id,
std::function<bool(const Instruction&)> pred =
[](const Instruction&) { return true; });
bool RemoveDecorationsFrom(
uint32_t id, std::function<bool(const Instruction&)> pred =
[](const Instruction&) { return true; });
// Removes all decorations from the result id of |inst|.
//

View File

@ -175,7 +175,7 @@ bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst,
for (auto* decl : invisible_decls) {
if (dominator_analysis->Dominates(store_inst, decl)) {
context()->get_debug_info_mgr()->AddDebugValueForDecl(decl, value_id,
decl);
decl, store_inst);
modified = true;
}
}

View File

@ -175,7 +175,8 @@ bool ScalarReplacementPass::ReplaceWholeDebugDeclare(
Instruction* added_dbg_value =
context()->get_debug_info_mgr()->AddDebugValueForDecl(
dbg_decl, /*value_id=*/var->result_id(),
/*insert_before=*/var->NextNode());
/*insert_before=*/var->NextNode(), /*scope_and_line=*/dbg_decl);
if (added_dbg_value == nullptr) return false;
added_dbg_value->AddOperand(
{SPV_OPERAND_TYPE_ID,

View File

@ -658,8 +658,8 @@ Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) {
// a = 3;
// foo(3);
// After inlining:
// a = 3; // we want to specify "DebugValue: %x = %int_3"
// foo and x disappeared!
// a = 3;
// foo and x disappeared but we want to specify "DebugValue: %x = %int_3".
//
// We want to specify the value for the variable using |defs_at_block_[bb]|,
// where |bb| is the basic block contains the decl.
@ -681,16 +681,17 @@ Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) {
if (value && (pass_->context()->get_instr_block(value) == nullptr ||
dom_tree->Dominates(value, decl))) {
if (pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl(
decl, value->result_id(), decl) == nullptr) {
decl, value->result_id(), decl, value) == nullptr) {
return Pass::Status::Failure;
}
} else {
// If |value| in the same basic block does not dominate |decl|, we can
// assign the value in the immediate dominator.
value_id = GetValueAtBlock(var_id, dom_tree->ImmediateDominator(bb));
if (value_id) value = pass_->get_def_use_mgr()->GetDef(value_id);
if (value_id &&
pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl(
decl, value_id, decl) == nullptr) {
decl, value_id, decl, value) == nullptr) {
return Pass::Status::Failure;
}
}

View File

@ -111,6 +111,11 @@ void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options,
options->scalar_block_layout = val;
}
void spvValidatorOptionsSetWorkgroupScalarBlockLayout(spv_validator_options options,
bool val) {
options->workgroup_scalar_block_layout = val;
}
void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options,
bool val) {
options->skip_block_layout = val;

View File

@ -45,6 +45,7 @@ struct spv_validator_options_t {
relax_block_layout(false),
uniform_buffer_standard_layout(false),
scalar_block_layout(false),
workgroup_scalar_block_layout(false),
skip_block_layout(false),
before_hlsl_legalization(false) {}
@ -54,6 +55,7 @@ struct spv_validator_options_t {
bool relax_block_layout;
bool uniform_buffer_standard_layout;
bool scalar_block_layout;
bool workgroup_scalar_block_layout;
bool skip_block_layout;
bool before_hlsl_legalization;
};

View File

@ -14,12 +14,12 @@
// Validates correctness of conversion instructions.
#include "source/val/validate.h"
#include "source/diagnostic.h"
#include "source/opcode.h"
#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
#include "source/val/validate.h"
#include "source/val/validation_state.h"
namespace spvtools {
@ -263,16 +263,25 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
<< "Logical addressing not supported: "
<< spvOpcodeString(opcode);
if (_.addressing_model() ==
SpvAddressingModelPhysicalStorageBuffer64EXT) {
if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) {
uint32_t input_storage_class = 0;
uint32_t input_data_type = 0;
_.GetPointerTypeInfo(input_type, &input_data_type,
&input_storage_class);
if (input_storage_class != SpvStorageClassPhysicalStorageBufferEXT)
if (input_storage_class != SpvStorageClassPhysicalStorageBuffer)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Pointer storage class must be PhysicalStorageBufferEXT: "
<< "Pointer storage class must be PhysicalStorageBuffer: "
<< spvOpcodeString(opcode);
if (spvIsVulkanEnv(_.context()->target_env)) {
if (_.GetBitWidth(result_type) != 64) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4710)
<< "PhysicalStorageBuffer64 addressing mode requires the "
"result integer type to have a 64-bit width for Vulkan "
"environment.";
}
}
}
break;
}
@ -314,16 +323,25 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
<< "Logical addressing not supported: "
<< spvOpcodeString(opcode);
if (_.addressing_model() ==
SpvAddressingModelPhysicalStorageBuffer64EXT) {
if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) {
uint32_t result_storage_class = 0;
uint32_t result_data_type = 0;
_.GetPointerTypeInfo(result_type, &result_data_type,
&result_storage_class);
if (result_storage_class != SpvStorageClassPhysicalStorageBufferEXT)
if (result_storage_class != SpvStorageClassPhysicalStorageBuffer)
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Pointer storage class must be PhysicalStorageBufferEXT: "
<< "Pointer storage class must be PhysicalStorageBuffer: "
<< spvOpcodeString(opcode);
if (spvIsVulkanEnv(_.context()->target_env)) {
if (_.GetBitWidth(input_type) != 64) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4710)
<< "PhysicalStorageBuffer64 addressing mode requires the "
"input integer to have a 64-bit width for Vulkan "
"environment.";
}
}
}
break;
}

View File

@ -379,6 +379,7 @@ bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
// or row major-ness.
spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
const char* decoration_str, bool blockRules,
bool scalar_block_layout,
uint32_t incoming_offset,
MemberConstraints& constraints,
ValidationState_t& vstate) {
@ -392,7 +393,6 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
// For example, relaxed layout is implied by Vulkan 1.1. But scalar layout
// is more permissive than relaxed layout.
const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
const bool scalar_block_layout = vstate.options()->scalar_block_layout;
auto fail = [&vstate, struct_id, storage_class_str, decoration_str,
blockRules, relaxed_block_layout,
@ -501,6 +501,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
if (SpvOpTypeStruct == opcode &&
SPV_SUCCESS != (recursive_status = checkLayout(
id, storage_class_str, decoration_str, blockRules,
scalar_block_layout,
offset, constraints, vstate)))
return recursive_status;
// Check matrix stride.
@ -553,7 +554,8 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
if (SpvOpTypeStruct == element_inst->opcode() &&
SPV_SUCCESS != (recursive_status = checkLayout(
typeId, storage_class_str, decoration_str,
blockRules, next_offset, constraints, vstate)))
blockRules, scalar_block_layout,
next_offset, constraints, vstate)))
return recursive_status;
// If offsets accumulate up to a 16-byte multiple stop checking since
// it will just repeat.
@ -698,6 +700,9 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
const auto& descs = vstate.entry_point_descriptions(entry_point);
int num_builtin_inputs = 0;
int num_builtin_outputs = 0;
int num_workgroup_variables = 0;
int num_workgroup_variables_with_block = 0;
int num_workgroup_variables_with_aliased = 0;
for (const auto& desc : descs) {
std::unordered_set<Instruction*> seen_vars;
for (auto interface : desc.interfaces) {
@ -754,6 +759,16 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
if (auto error = CheckBuiltInVariable(interface, vstate))
return error;
}
if (storage_class == SpvStorageClassWorkgroup) {
++num_workgroup_variables;
if (type_instr && SpvOpTypeStruct == type_instr->opcode()) {
if (hasDecoration(type_id, SpvDecorationBlock, vstate))
++num_workgroup_variables_with_block;
if (hasDecoration(var_instr->id(), SpvDecorationAliased, vstate))
++num_workgroup_variables_with_aliased;
}
}
}
if (num_builtin_inputs > 1 || num_builtin_outputs > 1) {
return vstate.diag(SPV_ERROR_INVALID_BINARY,
@ -777,6 +792,30 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
<< " because it is targeted by an OpEntryPoint instruction.";
}
}
if (vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR) &&
num_workgroup_variables > 0 &&
num_workgroup_variables_with_block > 0) {
if (num_workgroup_variables != num_workgroup_variables_with_block) {
return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point))
<< "When declaring WorkgroupMemoryExplicitLayoutKHR, "
"either all or none of the Workgroup Storage Class variables "
"in the entry point interface must point to struct types "
"decorated with Block. Entry point id "
<< entry_point << " does not meet this requirement.";
}
if (num_workgroup_variables_with_block > 1 &&
num_workgroup_variables_with_block !=
num_workgroup_variables_with_aliased) {
return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point))
<< "When declaring WorkgroupMemoryExplicitLayoutKHR, "
"if more than one Workgroup Storage Class variable in "
"the entry point interface point to a type decorated "
"with Block, all of them must be decorated with Aliased. "
"Entry point id "
<< entry_point << " does not meet this requirement.";
}
}
}
}
return SPV_SUCCESS;
@ -942,14 +981,16 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
const bool phys_storage_buffer =
storageClass == SpvStorageClassPhysicalStorageBufferEXT;
if (uniform || push_constant || storage_buffer || phys_storage_buffer) {
const bool workgroup = storageClass == SpvStorageClassWorkgroup;
if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
workgroup) {
const auto ptrInst = vstate.FindDef(words[1]);
assert(SpvOpTypePointer == ptrInst->opcode());
auto id = ptrInst->words()[3];
auto id_inst = vstate.FindDef(id);
// Jump through one level of arraying.
if (id_inst->opcode() == SpvOpTypeArray ||
id_inst->opcode() == SpvOpTypeRuntimeArray) {
if (!workgroup && (id_inst->opcode() == SpvOpTypeArray ||
id_inst->opcode() == SpvOpTypeRuntimeArray)) {
id = id_inst->GetOperandAs<uint32_t>(1u);
id_inst = vstate.FindDef(id);
}
@ -961,7 +1002,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
// Prepare for messages
const char* sc_str =
uniform ? "Uniform"
: (push_constant ? "PushConstant" : "StorageBuffer");
: (push_constant ? "PushConstant"
: (workgroup ? "Workgroup"
: "StorageBuffer"));
if (spvIsVulkanEnv(vstate.context()->target_env)) {
const bool block = hasDecoration(id, SpvDecorationBlock, vstate);
@ -1029,8 +1072,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type();
const bool blockRules = uniform && blockDeco;
const bool bufferRules =
(uniform && bufferDeco) || (push_constant && blockDeco) ||
((storage_buffer || phys_storage_buffer) && blockDeco);
(uniform && bufferDeco) ||
((push_constant || storage_buffer ||
phys_storage_buffer || workgroup) && blockDeco);
if (uniform && blockDeco) {
vstate.RegisterPointerToUniformBlock(ptrInst->id());
vstate.RegisterStructForUniformBlock(id);
@ -1044,6 +1088,10 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
if (blockRules || bufferRules) {
const char* deco_str = blockDeco ? "Block" : "BufferBlock";
spv_result_t recursive_status = SPV_SUCCESS;
const bool scalar_block_layout = workgroup ?
vstate.options()->workgroup_scalar_block_layout :
vstate.options()->scalar_block_layout;
if (isMissingOffsetInStruct(id, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
@ -1072,12 +1120,14 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
"decorations.";
} else if (blockRules &&
(SPV_SUCCESS != (recursive_status = checkLayout(
id, sc_str, deco_str, true, 0,
id, sc_str, deco_str, true,
scalar_block_layout, 0,
constraints, vstate)))) {
return recursive_status;
} else if (bufferRules &&
(SPV_SUCCESS != (recursive_status = checkLayout(
id, sc_str, deco_str, false, 0,
id, sc_str, deco_str, false,
scalar_block_layout, 0,
constraints, vstate)))) {
return recursive_status;
}

View File

@ -27,6 +27,7 @@
#include "source/latest_version_glsl_std_450_header.h"
#include "source/latest_version_opencl_std_header.h"
#include "source/opcode.h"
#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
#include "source/val/validate.h"
@ -685,6 +686,20 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _,
} // anonymous namespace
spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) {
std::string extension = GetExtensionString(&(inst->c_inst()));
if (extension ==
ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) {
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
<< "SPV_KHR_workgroup_memory_explicit_layout extension "
"requires SPIR-V version 1.4 or later.";
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateExtInstImport(ValidationState_t& _,
const Instruction* inst) {
const auto name_id = 1;
@ -3124,6 +3139,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst) {
const SpvOp opcode = inst->opcode();
if (opcode == SpvOpExtension) return ValidateExtension(_, inst);
if (opcode == SpvOpExtInstImport) return ValidateExtInstImport(_, inst);
if (opcode == SpvOpExtInst) return ValidateExtInst(_, inst);

View File

@ -441,6 +441,17 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
<< "Expected Image Operand Offset to have " << plane_size
<< " components, but given " << offset_size;
}
if (spvIsVulkanEnv(_.context()->target_env)) {
if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather &&
opcode != SpvOpImageSparseGather &&
opcode != SpvOpImageSparseDrefGather) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4663)
<< "Image Operand Offset can only be used with "
"OpImage*Gather operations";
}
}
}
if (mask & SpvImageOperandsConstOffsetsMask) {
@ -888,6 +899,7 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _,
// Vulkan uses the Sampled=1 case.
if ((info.sampled != 0) && (info.sampled != 1)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4657)
<< "Sampled image type requires an image type with \"Sampled\" "
"operand set to 0 or 1";
}
@ -1445,12 +1457,21 @@ spv_result_t ValidateImageGather(ValidationState_t& _,
}
if (opcode == SpvOpImageGather || opcode == SpvOpImageSparseGather) {
const uint32_t component_index_type = _.GetOperandTypeId(inst, 4);
const uint32_t component = inst->GetOperandAs<uint32_t>(4);
const uint32_t component_index_type = _.GetTypeId(component);
if (!_.IsIntScalarType(component_index_type) ||
_.GetBitWidth(component_index_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Expected Component to be 32-bit int scalar";
}
if (spvIsVulkanEnv(_.context()->target_env)) {
if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< _.VkErrorID(4664)
<< "Expected Component Operand to be a const object for Vulkan "
"environment";
}
}
} else {
assert(opcode == SpvOpImageDrefGather ||
opcode == SpvOpImageSparseDrefGather);

View File

@ -407,6 +407,10 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
<< "' is not a pointer type.";
}
const auto type_index = 2;
const auto value_id = result_type->GetOperandAs<uint32_t>(type_index);
auto value_type = _.FindDef(value_id);
const auto initializer_index = 3;
const auto storage_class_index = 2;
if (initializer_index < inst->operands().size()) {
@ -423,7 +427,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
<< "OpVariable Initializer <id> '" << _.getIdName(initializer_id)
<< "' is not a constant or module-scope variable.";
}
if (initializer->type_id() != result_type->GetOperandAs<uint32_t>(2u)) {
if (initializer->type_id() != value_id) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Initializer type must match the type pointed to by the Result "
"Type";
@ -440,9 +444,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
storage_class != SpvStorageClassHitAttributeNV &&
storage_class != SpvStorageClassCallableDataNV &&
storage_class != SpvStorageClassIncomingCallableDataNV) {
const auto storage_index = 2;
const auto storage_id = result_type->GetOperandAs<uint32_t>(storage_index);
const auto storage = _.FindDef(storage_id);
bool storage_input_or_output = storage_class == SpvStorageClassInput ||
storage_class == SpvStorageClassOutput;
bool builtin = false;
@ -455,7 +456,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
}
if (!(storage_input_or_output && builtin) &&
ContainsInvalidBool(_, storage, storage_input_or_output)) {
ContainsInvalidBool(_, value_type, storage_input_or_output)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "If OpTypeBool is stored in conjunction with OpVariable, it "
<< "can only be used with non-externally visible shader Storage "
@ -535,18 +536,14 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
if (!IsAllowedTypeOrArrayOfSame(
_, pointee,
{SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage,
SpvOpTypeAccelerationStructureNV,
SpvOpTypeAccelerationStructureKHR, SpvOpTypeRayQueryKHR})) {
SpvOpTypeAccelerationStructureKHR})) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "UniformConstant OpVariable <id> '" << _.getIdName(inst->id())
<< "' has illegal type.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< _.VkErrorID(4655) << "UniformConstant OpVariable <id> '"
<< _.getIdName(inst->id()) << "' has illegal type.\n"
<< "Variables identified with the UniformConstant storage class "
<< "are used only as handles to refer to opaque resources. Such "
<< "variables must be typed as OpTypeImage, OpTypeSampler, "
<< "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
"OpTypeAccelerationStructureKHR, "
"OpTypeRayQueryKHR, "
<< "OpTypeSampledImage, OpTypeAccelerationStructureKHR, "
<< "or an array of one of these types.";
}
}
@ -576,6 +573,28 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
"of this type";
}
}
// Check for invalid use of Invariant
if (storage_class != SpvStorageClassInput &&
storage_class != SpvStorageClassOutput) {
if (_.HasDecoration(inst->id(), SpvDecorationInvariant)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< _.VkErrorID(4677)
<< "Variable decorated with Invariant must only be identified "
"with the Input or Output storage class in Vulkan "
"environment.";
}
// Need to check if only the members in a struct are decorated
if (value_type && value_type->opcode() == SpvOpTypeStruct) {
if (_.HasDecoration(value_id, SpvDecorationInvariant)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< _.VkErrorID(4677)
<< "Variable struct member decorated with Invariant must only "
"be identified with the Input or Output storage class in "
"Vulkan environment.";
}
}
}
}
// Vulkan Appendix A: Check that if contains initializer, then
@ -584,16 +603,26 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
storage_class != SpvStorageClassPrivate &&
storage_class != SpvStorageClassFunction) {
if (spvIsVulkanEnv(_.context()->target_env)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< _.VkErrorID(4651) << "OpVariable, <id> '"
<< _.getIdName(inst->id())
<< "', has a disallowed initializer & storage class "
<< "combination.\n"
<< "From " << spvLogStringForEnv(_.context()->target_env)
<< " spec:\n"
<< "Variable declarations that include initializers must have "
<< "one of the following storage classes: Output, Private, or "
<< "Function";
if (storage_class == SpvStorageClassWorkgroup) {
auto init_id = inst->GetOperandAs<uint32_t>(3);
auto init = _.FindDef(init_id);
if (init->opcode() != SpvOpConstantNull) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Variable initializers in Workgroup storage class are "
"limited to OpConstantNull";
}
} else {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< _.VkErrorID(4651) << "OpVariable, <id> '"
<< _.getIdName(inst->id())
<< "', has a disallowed initializer & storage class "
<< "combination.\n"
<< "From " << spvLogStringForEnv(_.context()->target_env)
<< " spec:\n"
<< "Variable declarations that include initializers must have "
<< "one of the following storage classes: Output, Private, "
<< "Function or Workgroup";
}
}
}
@ -630,9 +659,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
}
// Vulkan specific validation rules for OpTypeRuntimeArray
const auto type_index = 2;
const auto value_id = result_type->GetOperandAs<uint32_t>(type_index);
auto value_type = _.FindDef(value_id);
if (spvIsVulkanEnv(_.context()->target_env)) {
// OpTypeRuntimeArray should only ever be in a container like OpTypeStruct,
// so should never appear as a bare variable.
@ -749,6 +775,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
storage_class_ok = false;
}
break;
case SpvStorageClassWorkgroup:
if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR)) {
storage_class_ok = false;
}
break;
default:
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Cannot allocate a variable containing a 16-bit type in "
@ -800,6 +831,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
storage_class_ok = false;
}
break;
case SpvStorageClassWorkgroup:
if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR)) {
storage_class_ok = false;
}
break;
default:
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Cannot allocate a variable containing a 8-bit type in "

View File

@ -359,6 +359,7 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) {
case SpvCapabilityStorageBuffer8BitAccess:
case SpvCapabilityUniformAndStorageBuffer8BitAccess:
case SpvCapabilityStoragePushConstant8:
case SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR:
features_.declare_int8_type = true;
break;
case SpvCapabilityInt16:
@ -372,6 +373,7 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) {
case SpvCapabilityStorageUniform16:
case SpvCapabilityStoragePushConstant16:
case SpvCapabilityStorageInputOutput16:
case SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR:
features_.declare_int16_type = true;
features_.declare_float16_type = true;
features_.free_fp_rounding_mode = true;
@ -1692,24 +1694,36 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-OriginLowerLeft-04653);
case 4654:
return VUID_WRAP(VUID-StandaloneSpirv-PixelCenterInteger-04654);
case 4655:
return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-04655);
case 4656:
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04656);
case 4657:
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04657);
case 4658:
return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658);
case 4659:
return VUID_WRAP(VUID-StandaloneSpirv-OpImageQuerySizeLod-04659);
case 4662:
return VUID_WRAP(VUID-StandaloneSpirv-Offset-04662);
case 4663:
return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663);
case 4664:
return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664);
case 4669:
return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669);
case 4675:
return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675);
case 4677:
return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
case 4683:
return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683);
case 4685:
return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
case 4686:
return VUID_WRAP(VUID-StandaloneSpirv-None-04686);
case 4710:
return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710);
case 4711:
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeForwardPointer-04711);
case 4730:

View File

@ -103,6 +103,10 @@ class ValidationState_t {
// Members need not be listed in offset order
bool scalar_block_layout = false;
// Use scalar block layout (as defined above) for Workgroup block
// variables. See VK_KHR_workgroup_memory_explicit_layout.
bool workgroup_scalar_block_layout = false;
// SPIR-V 1.4 allows us to select between any two composite values
// of the same type.
bool select_between_composites = false;