Updated spirv-tools.
This commit is contained in:
parent
b9d9a387eb
commit
b9d37691cf
@ -1 +1 @@
|
||||
"v2020.7-dev", "SPIRV-Tools v2020.7-dev 93efa0c36bc823ed865bd45418c57390547cef74"
|
||||
"v2020.7-dev", "SPIRV-Tools v2020.7-dev 45234794d9cc274023fbed948636fd006b589aef"
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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|.
|
||||
//
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 "
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user