Support SPIR-V 1.4 (#2550)
* SPIR-V 1.4 headers, add SPV_ENV_UNIVERSAL_1_4 * Support --target-env spv1.4 in help for command line tools * Support asm/dis of UniformId decoration * Validate UniformId decoration * Fix version check on instructions and operands Also register decorations used with OpDecorateId * Extension lists can differ between enums that match Example: SubgroupMaskEq vs SubgroupMaskEqKHR * Validate scope value for Uniform decoration, for SPIR-V 1.4 * More unioning of exts * Preserve grammar order within an enum value * 1.4: Validate OpSelect over composites * Tools default to 1.4 * Add asm/dis test for OpCopyLogical * 1.4: asm/dis tests for PtrEqual, PtrNotEqual, PtrDiff * Basic asm/Dis test for OpCopyMemory * Test asm/dis OpCopyMemory with 2-memory access Add asm/dis tests for OpCopyMemorySized Requires grammar update to add second optional memory access operand to OpCopyMemory and OpCopyMemorySized * Validate one or two memory accesses on OpCopyMemory* * Check av/vis on CopyMemory source and target memory access This is a proposed rule. See https://gitlab.khronos.org/spirv/SPIR-V/issues/413 * Validate operation for OpSpecConstantOp * Validate NonWritable decoration Also permit NonWritable on members of UBO and SSBO. * SPIR-V 1.4: NonWrtiable can decorate Function and Private vars * Update optimizer CLI tests for SPIR-V 1.4 * Testing tools: Give expected SPIR-V version in message * SPIR-V 1.4 validation for entry point interfaces * Allow only unique interfaces * Allow all global variables * Check that all statically used global variables are listed * new tests * Add validation fixture CompileFailure * Add 1.4 validation for pointer comparisons * New tests * Validate with image operands SignExtend, ZeroExtend Since we don't actually know the image texel format, we can't fully validate. We need more context. But we can make sure we allow the new image operands in known-good cases. * Validate OpCopyLogical * Recursively checks subtypes * new tests * Add SPIR-V 1.4 tests for NoSignedWrap, NoUnsignedWrap * Allow scalar conditions in 1.4 with OpSelect * Allows scalar conditions with vector operands * new tests * Validate uniform id scope as an execution scope * Validate the values of memory and execution scopes are valid scope values * new test * Remove SPIR-V 1.4 Vulkan 1.0 environment * SPIR-V 1.4 requires Vulkan 1.1 * FIX: include string for spvLog * FIX: validate nonwritable * FIX: test case suite for member decorate string * FIX: test case for hlsl functionality1 * Validation test fixture: ease debugging * Use binary version for SPIR-V 1.4 specific features * Switch checks based on the SPIR-V version from the target environment to instead use the version from the binary * Moved header parsing into the ValidationState_t constructor (where version based features are set) * Added new versions of tests that assemble a 1.3 binary and validate a 1.4 environment * Fix test for update to SPIR-V 1.4 headers * Fix formatting * Ext inst lookup: Add Vulkan 1.1 env with SPIR-V 1.4 * Update spirv-val help * Operand version checks should use module version Use the module version instead of the target environment version. * Fix comment about two-access form of OpCopyMemory
This commit is contained in:
Родитель
106c98d0fa
Коммит
63f57d95d6
|
@ -427,6 +427,8 @@ typedef enum {
|
|||
SPV_ENV_UNIVERSAL_1_3, // SPIR-V 1.3 latest revision, no other restrictions.
|
||||
SPV_ENV_VULKAN_1_1, // Vulkan 1.1 latest revision.
|
||||
SPV_ENV_WEBGPU_0, // Work in progress WebGPU 1.0.
|
||||
SPV_ENV_UNIVERSAL_1_4, // SPIR-V 1.4 latest revision, no other restrictions.
|
||||
SPV_ENV_VULKAN_1_1_SPIRV_1_4, // Vulkan 1.1 with SPIR-V 1.4 binary.
|
||||
} spv_target_env;
|
||||
|
||||
// SPIR-V Validator can be parameterized with the following Universal Limits.
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "glsl.std.450.insts.inc"
|
||||
#include "opencl.std.insts.inc"
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "spv-amd-gcn-shader.insts.inc"
|
||||
#include "spv-amd-shader-ballot.insts.inc"
|
||||
#include "spv-amd-shader-explicit-vertex-parameter.insts.inc"
|
||||
|
@ -80,7 +81,9 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
|
|||
case SPV_ENV_OPENGL_4_5:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
*pExtInstTable = &kTable_1_0;
|
||||
return SPV_SUCCESS;
|
||||
default:
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "source/spirv_target_env.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "source/spirv_constant.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
@ -61,6 +62,10 @@ const char* spvTargetEnvDescription(spv_target_env env) {
|
|||
return "SPIR-V 1.3 (under Vulkan 1.1 semantics)";
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return "SPIR-V 1.3 (under WIP WebGPU semantics)";
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
return "SPIR-V 1.4";
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return "SPIR-V 1.4 (under Vulkan 1.1 semantics)";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -91,6 +96,9 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
|
|||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return SPV_SPIRV_VERSION_WORD(1, 3);
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return SPV_SPIRV_VERSION_WORD(1, 4);
|
||||
}
|
||||
return SPV_SPIRV_VERSION_WORD(0, 0);
|
||||
}
|
||||
|
@ -99,7 +107,10 @@ bool spvParseTargetEnv(const char* s, spv_target_env* env) {
|
|||
auto match = [s](const char* b) {
|
||||
return s && (0 == strncmp(s, b, strlen(b)));
|
||||
};
|
||||
if (match("vulkan1.0")) {
|
||||
if (match("vulkan1.1spv1.4")) {
|
||||
if (env) *env = SPV_ENV_VULKAN_1_1_SPIRV_1_4;
|
||||
return true;
|
||||
} else if (match("vulkan1.0")) {
|
||||
if (env) *env = SPV_ENV_VULKAN_1_0;
|
||||
return true;
|
||||
} else if (match("vulkan1.1")) {
|
||||
|
@ -117,6 +128,9 @@ bool spvParseTargetEnv(const char* s, spv_target_env* env) {
|
|||
} else if (match("spv1.3")) {
|
||||
if (env) *env = SPV_ENV_UNIVERSAL_1_3;
|
||||
return true;
|
||||
} else if (match("spv1.4")) {
|
||||
if (env) *env = SPV_ENV_UNIVERSAL_1_4;
|
||||
return true;
|
||||
} else if (match("opencl1.2embedded")) {
|
||||
if (env) *env = SPV_ENV_OPENCL_EMBEDDED_1_2;
|
||||
return true;
|
||||
|
@ -185,9 +199,11 @@ bool spvIsVulkanEnv(spv_target_env env) {
|
|||
case SPV_ENV_OPENCL_EMBEDDED_2_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
return false;
|
||||
case SPV_ENV_VULKAN_1_0:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -207,6 +223,8 @@ bool spvIsOpenCLEnv(spv_target_env env) {
|
|||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return false;
|
||||
case SPV_ENV_OPENCL_1_2:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_1_2:
|
||||
|
@ -242,6 +260,8 @@ bool spvIsWebGPUEnv(spv_target_env env) {
|
|||
case SPV_ENV_OPENCL_EMBEDDED_2_2:
|
||||
case SPV_ENV_OPENCL_2_1:
|
||||
case SPV_ENV_OPENCL_2_2:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return false;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return true;
|
||||
|
@ -273,7 +293,8 @@ std::string spvLogStringForEnv(spv_target_env env) {
|
|||
return "OpenGL";
|
||||
}
|
||||
case SPV_ENV_VULKAN_1_0:
|
||||
case SPV_ENV_VULKAN_1_1: {
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4: {
|
||||
return "Vulkan";
|
||||
}
|
||||
case SPV_ENV_WEBGPU_0: {
|
||||
|
@ -282,7 +303,8 @@ std::string spvLogStringForEnv(spv_target_env env) {
|
|||
case SPV_ENV_UNIVERSAL_1_0:
|
||||
case SPV_ENV_UNIVERSAL_1_1:
|
||||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3: {
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_UNIVERSAL_1_4: {
|
||||
return "Universal";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@ spv_context spvContextCreate(spv_target_env env) {
|
|||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
|
|
|
@ -14,10 +14,9 @@
|
|||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
@ -52,21 +51,6 @@ namespace spvtools {
|
|||
namespace val {
|
||||
namespace {
|
||||
|
||||
// TODO(umar): Validate header
|
||||
// TODO(umar): The binary parser validates the magic word, and the length of the
|
||||
// header, but nothing else.
|
||||
spv_result_t setHeader(void* user_data, spv_endianness_t, uint32_t,
|
||||
uint32_t version, uint32_t generator, uint32_t id_bound,
|
||||
uint32_t) {
|
||||
// Record the ID bound so that the validator can ensure no ID is out of bound.
|
||||
ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
|
||||
_.setIdBound(id_bound);
|
||||
_.setGenerator(generator);
|
||||
_.setVersion(version);
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Parses OpExtension instruction and registers extension.
|
||||
void RegisterExtension(ValidationState_t& _,
|
||||
const spv_parsed_instruction_t* inst) {
|
||||
|
@ -282,7 +266,8 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
|||
|
||||
// Parse the module and perform inline validation checks. These checks do
|
||||
// not require the the knowledge of the whole module.
|
||||
if (auto error = spvBinaryParse(&context, vstate, words, num_words, setHeader,
|
||||
if (auto error = spvBinaryParse(&context, vstate, words, num_words,
|
||||
/*parsed_header =*/nullptr,
|
||||
ProcessInstruction, pDiagnostic)) {
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -185,6 +185,21 @@ std::string LogStringForDecoration(uint32_t decoration) {
|
|||
return "Unknown";
|
||||
}
|
||||
|
||||
// Returns true if the decoration takes ID parameters.
|
||||
// TODO(dneto): This can be generated from the grammar.
|
||||
bool DecorationTakesIdParameters(uint32_t type) {
|
||||
switch (static_cast<SpvDecoration>(type)) {
|
||||
case SpvDecorationUniformId:
|
||||
case SpvDecorationAlignmentId:
|
||||
case SpvDecorationMaxByteOffsetId:
|
||||
case SpvDecorationHlslCounterBufferGOOGLE:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto decoration = inst->GetOperandAs<uint32_t>(1);
|
||||
if (decoration == SpvDecorationSpecId) {
|
||||
|
@ -205,10 +220,27 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
|
|||
<< "' is not valid for the WebGPU execution environment.";
|
||||
}
|
||||
|
||||
if (DecorationTakesIdParameters(decoration)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Decorations taking ID parameters may not be used with "
|
||||
"OpDecorateId";
|
||||
}
|
||||
// TODO: Add validations for all decorations.
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto decoration = inst->GetOperandAs<uint32_t>(1);
|
||||
if (!DecorationTakesIdParameters(decoration)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Decorations that don't take ID parameters may not be used with "
|
||||
"OpDecorateId";
|
||||
}
|
||||
// TODO: Add validations for these decorations.
|
||||
// UniformId is covered elsewhere.
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateMemberDecorate(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto struct_type_id = inst->GetOperandAs<uint32_t>(0);
|
||||
|
@ -340,7 +372,8 @@ spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _,
|
|||
spv_result_t RegisterDecorations(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpDecorate: {
|
||||
case SpvOpDecorate:
|
||||
case SpvOpDecorateId: {
|
||||
const uint32_t target_id = inst->word(1);
|
||||
const SpvDecoration dec_type = static_cast<SpvDecoration>(inst->word(2));
|
||||
std::vector<uint32_t> dec_params;
|
||||
|
@ -415,6 +448,11 @@ spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) {
|
|||
case SpvOpDecorate:
|
||||
if (auto error = ValidateDecorate(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpDecorateId:
|
||||
if (auto error = ValidateDecorateId(_, inst)) return error;
|
||||
break;
|
||||
// TODO(dneto): SpvOpDecorateStringGOOGLE
|
||||
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253
|
||||
case SpvOpMemberDecorate:
|
||||
if (auto error = ValidateMemberDecorate(_, inst)) return error;
|
||||
break;
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
|
||||
// Validates correctness of barrier SPIR-V instructions.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
|
@ -24,6 +22,7 @@
|
|||
#include "source/spirv_target_env.h"
|
||||
#include "source/util/bitutils.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validate_memory_semantics.h"
|
||||
#include "source/val/validate_scopes.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
@ -38,8 +37,7 @@ spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) {
|
|||
|
||||
switch (opcode) {
|
||||
case SpvOpControlBarrier: {
|
||||
if (spvVersionForTargetEnv(_.context()->target_env) <
|
||||
SPV_SPIRV_VERSION_WORD(1, 3)) {
|
||||
if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) {
|
||||
_.function(inst->function()->id())
|
||||
->RegisterExecutionModelLimitation(
|
||||
[](SpvExecutionModel model, std::string* message) {
|
||||
|
|
|
@ -513,6 +513,92 @@ spv_result_t ValidateVectorShuffle(ValidationState_t& _,
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns true if |lhs| and |rhs| logically match.
|
||||
// 1. Must both be either OpTypeArray or OpTypeStruct
|
||||
// 2. If OpTypeArray, then
|
||||
// * Length must be the same
|
||||
// * Element type must match or logically match
|
||||
// 3. If OpTypeStruct, then
|
||||
// * Both have same number of elements
|
||||
// * Element N for both structs must match or logically match
|
||||
bool LogicallyMatch(ValidationState_t& _, const Instruction* lhs,
|
||||
const Instruction* rhs) {
|
||||
if (lhs->opcode() != rhs->opcode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs->opcode() == SpvOpTypeArray) {
|
||||
// Size operands must match.
|
||||
if (lhs->GetOperandAs<uint32_t>(2u) != rhs->GetOperandAs<uint32_t>(2u)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Elements must match or logically match.
|
||||
const auto lhs_ele_id = lhs->GetOperandAs<uint32_t>(1u);
|
||||
const auto rhs_ele_id = rhs->GetOperandAs<uint32_t>(1u);
|
||||
if (lhs_ele_id == rhs_ele_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto lhs_ele = _.FindDef(lhs_ele_id);
|
||||
const auto rhs_ele = _.FindDef(rhs_ele_id);
|
||||
if (!lhs_ele || !rhs_ele) {
|
||||
return false;
|
||||
}
|
||||
return LogicallyMatch(_, lhs_ele, rhs_ele);
|
||||
} else if (lhs->opcode() == SpvOpTypeStruct) {
|
||||
// Number of elements must match.
|
||||
if (lhs->operands().size() != rhs->operands().size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 1u; i < lhs->operands().size(); ++i) {
|
||||
const auto lhs_ele_id = lhs->GetOperandAs<uint32_t>(i);
|
||||
const auto rhs_ele_id = rhs->GetOperandAs<uint32_t>(i);
|
||||
// Elements must match or logically match.
|
||||
if (lhs_ele_id == rhs_ele_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto lhs_ele = _.FindDef(lhs_ele_id);
|
||||
const auto rhs_ele = _.FindDef(rhs_ele_id);
|
||||
if (!lhs_ele || !rhs_ele) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LogicallyMatch(_, lhs_ele, rhs_ele)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All checks passed.
|
||||
return true;
|
||||
}
|
||||
|
||||
// No other opcodes are acceptable at this point. Arrays and structs are
|
||||
// caught above and if they're elements are not arrays or structs they are
|
||||
// required to match exactly.
|
||||
return false;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCopyLogical(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto result_type = _.FindDef(inst->type_id());
|
||||
const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
|
||||
const auto source_type = _.FindDef(source->type_id());
|
||||
if (!source_type || !result_type || source_type == result_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result Type must not equal the Operand type";
|
||||
}
|
||||
|
||||
if (!LogicallyMatch(_, source_type, result_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result Type does not logically match the Operand type";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// Validates correctness of composite instructions.
|
||||
|
@ -534,6 +620,8 @@ spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst) {
|
|||
return ValidateCopyObject(_, inst);
|
||||
case SpvOpTranspose:
|
||||
return ValidateTranspose(_, inst);
|
||||
case SpvOpCopyLogical:
|
||||
return ValidateCopyLogical(_, inst);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -364,7 +364,8 @@ spv_result_t ValidateSpecConstantOp(ValidationState_t& _,
|
|||
if (!_.features().uconvert_spec_constant_op &&
|
||||
!_.HasCapability(SpvCapabilityKernel)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "UConvert requires Kernel capability or extension "
|
||||
<< "Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability or extension "
|
||||
"SPV_AMD_gpu_shader_int16";
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
@ -25,8 +23,10 @@
|
|||
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/opcode.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/spirv_validator_options.h"
|
||||
#include "source/val/validate_scopes.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
|
@ -683,6 +683,7 @@ 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;
|
||||
std::unordered_set<Instruction*> seen_vars;
|
||||
for (const auto& desc : descs) {
|
||||
for (auto interface : desc.interfaces) {
|
||||
Instruction* var_instr = vstate.FindDef(interface);
|
||||
|
@ -694,14 +695,30 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
|||
}
|
||||
const SpvStorageClass storage_class =
|
||||
var_instr->GetOperandAs<SpvStorageClass>(2);
|
||||
if (storage_class != SpvStorageClassInput &&
|
||||
storage_class != SpvStorageClassOutput) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "OpEntryPoint interfaces must be OpVariables with "
|
||||
"Storage Class of Input(1) or Output(3). Found Storage "
|
||||
"Class "
|
||||
<< storage_class << " for Entry Point id " << entry_point
|
||||
<< ".";
|
||||
if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Starting in 1.4, OpEntryPoint must list all global variables
|
||||
// it statically uses and those interfaces must be unique.
|
||||
if (storage_class == SpvStorageClassFunction) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "OpEntryPoint interfaces should only list global "
|
||||
"variables";
|
||||
}
|
||||
|
||||
if (!seen_vars.insert(var_instr).second) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "Non-unique OpEntryPoint interface "
|
||||
<< vstate.getIdName(interface) << " is disallowed";
|
||||
}
|
||||
} else {
|
||||
if (storage_class != SpvStorageClassInput &&
|
||||
storage_class != SpvStorageClassOutput) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "OpEntryPoint interfaces must be OpVariables with "
|
||||
"Storage Class of Input(1) or Output(3). Found Storage "
|
||||
"Class "
|
||||
<< storage_class << " for Entry Point id " << entry_point
|
||||
<< ".";
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t ptr_id = var_instr->word(1);
|
||||
|
@ -964,10 +981,12 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
|||
vstate.RegisterPointerToUniformBlock(ptrInst->id());
|
||||
vstate.RegisterStructForUniformBlock(id);
|
||||
}
|
||||
if ((uniform && bufferDeco) || (storage_buffer && blockDeco)) {
|
||||
if ((uniform && bufferDeco) ||
|
||||
((storage_buffer || phys_storage_buffer) && blockDeco)) {
|
||||
vstate.RegisterPointerToStorageBuffer(ptrInst->id());
|
||||
vstate.RegisterStructForStorageBuffer(id);
|
||||
}
|
||||
|
||||
if (blockRules || bufferRules) {
|
||||
const char* deco_str = blockDeco ? "Block" : "BufferBlock";
|
||||
spv_result_t recursive_status = SPV_SUCCESS;
|
||||
|
@ -1257,34 +1276,51 @@ spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate,
|
|||
if (decoration.struct_member_index() == Decoration::kInvalidMember) {
|
||||
// The target must be a memory object declaration.
|
||||
// First, it must be a variable or function parameter.
|
||||
if (inst.opcode() != SpvOpVariable &&
|
||||
inst.opcode() != SpvOpFunctionParameter) {
|
||||
const auto opcode = inst.opcode();
|
||||
const auto type_id = inst.type_id();
|
||||
if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Target of NonWritable decoration must be a memory object "
|
||||
"declaration (a variable or a function parameter)";
|
||||
}
|
||||
// Second, it must point to a UBO, SSBO, or storage image.
|
||||
const auto type_id = inst.type_id();
|
||||
if (!vstate.IsPointerToUniformBlock(type_id) &&
|
||||
!vstate.IsPointerToStorageBuffer(type_id) &&
|
||||
!vstate.IsPointerToStorageImage(type_id)) {
|
||||
const auto var_storage_class = opcode == SpvOpVariable
|
||||
? inst.GetOperandAs<SpvStorageClass>(2)
|
||||
: SpvStorageClassMax;
|
||||
if ((var_storage_class == SpvStorageClassFunction ||
|
||||
var_storage_class == SpvStorageClassPrivate) &&
|
||||
vstate.features().nonwritable_var_in_function_or_private) {
|
||||
// New permitted feature in SPIR-V 1.4.
|
||||
} else if (
|
||||
// It may point to a UBO, SSBO, or storage image.
|
||||
vstate.IsPointerToUniformBlock(type_id) ||
|
||||
vstate.IsPointerToStorageBuffer(type_id) ||
|
||||
vstate.IsPointerToStorageImage(type_id)) {
|
||||
} else {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Target of NonWritable decoration is invalid: must point to a "
|
||||
"storage image, uniform block, or storage buffer";
|
||||
"storage image, uniform block, "
|
||||
<< (vstate.features().nonwritable_var_in_function_or_private
|
||||
? "storage buffer, or variable in Private or Function "
|
||||
"storage class"
|
||||
: "or storage buffer");
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns SPV_SUCCESS if validation rules are satisfied for Uniform
|
||||
// decorations. Otherwise emits a diagnostic and returns something other than
|
||||
// SPV_SUCCESS. Assumes each decoration on a group has been propagated down to
|
||||
// the group members.
|
||||
// Returns SPV_SUCCESS if validation rules are satisfied for Uniform or
|
||||
// UniformId decorations. Otherwise emits a diagnostic and returns something
|
||||
// other than SPV_SUCCESS. Assumes each decoration on a group has been
|
||||
// propagated down to the group members. The |inst| parameter is the object
|
||||
// being decorated.
|
||||
spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
|
||||
const Instruction& inst,
|
||||
const Decoration&) {
|
||||
// Uniform must decorate an "object"
|
||||
const Decoration& decoration) {
|
||||
const char* const dec_name =
|
||||
decoration.dec_type() == SpvDecorationUniform ? "Uniform" : "UniformId";
|
||||
|
||||
// Uniform or UniformId must decorate an "object"
|
||||
// - has a result ID
|
||||
// - is an instantiation of a non-void type. So it has a type ID, and that
|
||||
// type is not void.
|
||||
|
@ -1293,19 +1329,33 @@ spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
|
|||
|
||||
if (inst.type_id() == 0) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Uniform decoration applied to a non-object";
|
||||
<< dec_name << " decoration applied to a non-object";
|
||||
}
|
||||
if (Instruction* type_inst = vstate.FindDef(inst.type_id())) {
|
||||
if (type_inst->opcode() == SpvOpTypeVoid) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Uniform decoration applied to a value with void type";
|
||||
<< dec_name << " decoration applied to a value with void type";
|
||||
}
|
||||
} else {
|
||||
// We might never get here because this would have been rejected earlier in
|
||||
// the flow.
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Uniform decoration applied to an object with invalid type";
|
||||
<< dec_name << " decoration applied to an object with invalid type";
|
||||
}
|
||||
|
||||
// Use of Uniform with OpDecorate is checked elsewhere.
|
||||
// Use of UniformId with OpDecorateId is checked elsewhere.
|
||||
|
||||
if (decoration.dec_type() == SpvDecorationUniformId) {
|
||||
assert(decoration.params().size() == 1 &&
|
||||
"Grammar ensures UniformId has one parameter");
|
||||
|
||||
// The scope id is an execution scope.
|
||||
if (auto error =
|
||||
ValidateExecutionScope(vstate, &inst, decoration.params()[0]))
|
||||
return error;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1365,7 +1415,6 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
|
|||
// been propagated down to the group members.
|
||||
if (inst->opcode() == SpvOpDecorationGroup) continue;
|
||||
|
||||
// Validates FPRoundingMode decoration
|
||||
for (const auto& decoration : decorations) {
|
||||
switch (decoration.dec_type()) {
|
||||
case SpvDecorationFPRoundingMode:
|
||||
|
@ -1376,6 +1425,7 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
|
|||
PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration));
|
||||
break;
|
||||
case SpvDecorationUniform:
|
||||
case SpvDecorationUniformId:
|
||||
PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration));
|
||||
break;
|
||||
case SpvDecorationNoSignedWrap:
|
||||
|
|
|
@ -63,6 +63,8 @@ bool CheckAllImageOperandsHandled() {
|
|||
case SpvImageOperandsMakeTexelVisibleKHRMask:
|
||||
case SpvImageOperandsNonPrivateTexelKHRMask:
|
||||
case SpvImageOperandsVolatileTexelKHRMask:
|
||||
case SpvImageOperandsSignExtendMask:
|
||||
case SpvImageOperandsZeroExtendMask:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -218,10 +220,12 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
|
|||
const SpvOp opcode = inst->opcode();
|
||||
const size_t num_words = inst->words().size();
|
||||
|
||||
// NonPrivate and Volatile take no operand words.
|
||||
// NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words.
|
||||
const uint32_t mask_bits_having_operands =
|
||||
mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask |
|
||||
SpvImageOperandsVolatileTexelKHRMask);
|
||||
SpvImageOperandsVolatileTexelKHRMask |
|
||||
SpvImageOperandsSignExtendMask |
|
||||
SpvImageOperandsZeroExtendMask);
|
||||
size_t expected_num_image_operand_words =
|
||||
spvtools::utils::CountSetBits(mask_bits_having_operands);
|
||||
if (mask & SpvImageOperandsGradMask) {
|
||||
|
@ -541,6 +545,32 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
|
|||
if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
|
||||
}
|
||||
|
||||
if (mask & SpvImageOperandsSignExtendMask) {
|
||||
// Checked elsewhere: SPIR-V 1.4 version or later.
|
||||
|
||||
// "The texel value is converted to the target value via sign extension.
|
||||
// Only valid when the texel type is a scalar or vector of integer type."
|
||||
//
|
||||
// We don't have enough information to know what the texel type is.
|
||||
// In OpenCL, knowledge is deferred until runtime: the image SampledType is
|
||||
// void, and the Format is Unknown.
|
||||
// In Vulkan, the texel type is only known in all cases by the pipeline
|
||||
// setup.
|
||||
}
|
||||
|
||||
if (mask & SpvImageOperandsZeroExtendMask) {
|
||||
// Checked elsewhere: SPIR-V 1.4 version or later.
|
||||
|
||||
// "The texel value is converted to the target value via zero extension.
|
||||
// Only valid when the texel type is a scalar or vector of integer type."
|
||||
//
|
||||
// We don't have enough information to know what the texel type is.
|
||||
// In OpenCL, knowledge is deferred until runtime: the image SampledType is
|
||||
// void, and the Format is Unknown.
|
||||
// In Vulkan, the texel type is only known in all cases by the pipeline
|
||||
// setup.
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,9 @@
|
|||
|
||||
// Performs validation on instructions that appear inside of a SPIR-V block.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -35,6 +34,7 @@
|
|||
#include "source/spirv_validator_options.h"
|
||||
#include "source/util/string_utils.h"
|
||||
#include "source/val/function.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
|
@ -98,21 +98,59 @@ CapabilitySet EnablingCapabilitiesForOp(const ValidationState_t& state,
|
|||
return CapabilitySet();
|
||||
}
|
||||
|
||||
// Returns SPV_SUCCESS if, for the given operand, the target environment
|
||||
// satsifies minimum version requirements, or if the module declares an
|
||||
// enabling extension for the operand. Otherwise emit a diagnostic and
|
||||
// return an error code.
|
||||
spv_result_t OperandVersionExtensionCheck(
|
||||
ValidationState_t& _, const Instruction* inst, size_t which_operand,
|
||||
const spv_operand_desc_t& operand_desc, uint32_t word) {
|
||||
const uint32_t module_version = _.version();
|
||||
const uint32_t operand_min_version = operand_desc.minVersion;
|
||||
const bool reserved = operand_min_version == 0xffffffffu;
|
||||
const bool version_satisfied =
|
||||
!reserved && (operand_min_version <= module_version);
|
||||
|
||||
if (version_satisfied) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
if (!reserved && operand_desc.numExtensions == 0) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvtools::utils::CardinalToOrdinal(which_operand)
|
||||
<< " operand of " << spvOpcodeString(inst->opcode()) << ": operand "
|
||||
<< operand_desc.name << "(" << word << ") requires SPIR-V version "
|
||||
<< SPV_SPIRV_VERSION_MAJOR_PART(operand_min_version) << "."
|
||||
<< SPV_SPIRV_VERSION_MINOR_PART(operand_min_version) << " or later";
|
||||
} else {
|
||||
ExtensionSet required_extensions(operand_desc.numExtensions,
|
||||
operand_desc.extensions);
|
||||
if (!_.HasAnyOfExtensions(required_extensions)) {
|
||||
return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
|
||||
<< spvtools::utils::CardinalToOrdinal(which_operand)
|
||||
<< " operand of " << spvOpcodeString(inst->opcode())
|
||||
<< ": operand " << operand_desc.name << "(" << word
|
||||
<< ") requires one of these extensions: "
|
||||
<< ExtensionSetToString(required_extensions);
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns SPV_SUCCESS if the given operand is enabled by capabilities declared
|
||||
// in the module. Otherwise issues an error message and returns
|
||||
// SPV_ERROR_INVALID_CAPABILITY.
|
||||
spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
|
||||
const Instruction* inst,
|
||||
size_t which_operand,
|
||||
spv_operand_type_t type,
|
||||
uint32_t operand) {
|
||||
const spv_parsed_operand_t& operand,
|
||||
uint32_t word) {
|
||||
// Mere mention of PointSize, ClipDistance, or CullDistance in a Builtin
|
||||
// decoration does not require the associated capability. The use of such
|
||||
// a variable value should trigger the capability requirement, but that's
|
||||
// not implemented yet. This rule is independent of target environment.
|
||||
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/365
|
||||
if (type == SPV_OPERAND_TYPE_BUILT_IN) {
|
||||
switch (operand) {
|
||||
if (operand.type == SPV_OPERAND_TYPE_BUILT_IN) {
|
||||
switch (word) {
|
||||
case SpvBuiltInPointSize:
|
||||
case SpvBuiltInClipDistance:
|
||||
case SpvBuiltInCullDistance:
|
||||
|
@ -120,14 +158,14 @@ spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
} else if (type == SPV_OPERAND_TYPE_FP_ROUNDING_MODE) {
|
||||
} else if (operand.type == SPV_OPERAND_TYPE_FP_ROUNDING_MODE) {
|
||||
// Allow all FP rounding modes if requested
|
||||
if (state.features().free_fp_rounding_mode) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
} else if (type == SPV_OPERAND_TYPE_GROUP_OPERATION &&
|
||||
} else if (operand.type == SPV_OPERAND_TYPE_GROUP_OPERATION &&
|
||||
state.features().group_ops_reduce_and_scans &&
|
||||
(operand <= uint32_t(SpvGroupOperationExclusiveScan))) {
|
||||
(word <= uint32_t(SpvGroupOperationExclusiveScan))) {
|
||||
// Allow certain group operations if requested.
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
@ -135,10 +173,10 @@ spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
|
|||
CapabilitySet enabling_capabilities;
|
||||
spv_operand_desc operand_desc = nullptr;
|
||||
const auto lookup_result =
|
||||
state.grammar().lookupOperand(type, operand, &operand_desc);
|
||||
state.grammar().lookupOperand(operand.type, word, &operand_desc);
|
||||
if (lookup_result == SPV_SUCCESS) {
|
||||
// Allow FPRoundingMode decoration if requested.
|
||||
if (type == SPV_OPERAND_TYPE_DECORATION &&
|
||||
if (operand.type == SPV_OPERAND_TYPE_DECORATION &&
|
||||
operand_desc->value == SpvDecorationFPRoundingMode) {
|
||||
if (state.features().free_fp_rounding_mode) return SPV_SUCCESS;
|
||||
|
||||
|
@ -161,29 +199,13 @@ spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
|
|||
<< " requires one of these capabilities: "
|
||||
<< ToString(enabling_capabilities, state.grammar());
|
||||
}
|
||||
return OperandVersionExtensionCheck(state, inst, which_operand,
|
||||
*operand_desc, word);
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns operand's required extensions.
|
||||
ExtensionSet RequiredExtensions(const ValidationState_t& state,
|
||||
spv_operand_type_t type, uint32_t operand) {
|
||||
spv_operand_desc operand_desc;
|
||||
if (state.grammar().lookupOperand(type, operand, &operand_desc) ==
|
||||
SPV_SUCCESS) {
|
||||
assert(operand_desc);
|
||||
// If this operand is incorporated into core SPIR-V before or in the current
|
||||
// target environment, we don't require extensions anymore.
|
||||
if (spvVersionForTargetEnv(state.grammar().target_env()) >=
|
||||
operand_desc->minVersion)
|
||||
return {};
|
||||
return {operand_desc->numExtensions, operand_desc->extensions};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction
|
||||
// is explicitly reserved in the SPIR-V core spec. Otherwise return
|
||||
// SPV_SUCCESS.
|
||||
|
@ -244,7 +266,7 @@ spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) {
|
|||
for (uint32_t mask_bit = 0x80000000; mask_bit; mask_bit >>= 1) {
|
||||
if (word & mask_bit) {
|
||||
spv_result_t status =
|
||||
CheckRequiredCapabilities(_, inst, i + 1, operand.type, mask_bit);
|
||||
CheckRequiredCapabilities(_, inst, i + 1, operand, mask_bit);
|
||||
if (status != SPV_SUCCESS) return status;
|
||||
}
|
||||
}
|
||||
|
@ -255,34 +277,13 @@ spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) {
|
|||
} else {
|
||||
// Check the operand word as a whole.
|
||||
spv_result_t status =
|
||||
CheckRequiredCapabilities(_, inst, i + 1, operand.type, word);
|
||||
CheckRequiredCapabilities(_, inst, i + 1, operand, word);
|
||||
if (status != SPV_SUCCESS) return status;
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Checks that all extensions required by the given instruction's operands were
|
||||
// declared in the module.
|
||||
spv_result_t ExtensionCheck(ValidationState_t& _, const Instruction* inst) {
|
||||
const SpvOp opcode = inst->opcode();
|
||||
for (size_t operand_index = 0; operand_index < inst->operands().size();
|
||||
++operand_index) {
|
||||
const auto& operand = inst->operand(operand_index);
|
||||
const uint32_t word = inst->word(operand.offset);
|
||||
const ExtensionSet required_extensions =
|
||||
RequiredExtensions(_, operand.type, word);
|
||||
if (!_.HasAnyOfExtensions(required_extensions)) {
|
||||
return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
|
||||
<< spvtools::utils::CardinalToOrdinal(operand_index + 1)
|
||||
<< " operand of " << spvOpcodeString(opcode) << ": operand "
|
||||
<< word << " requires one of these extensions: "
|
||||
<< ExtensionSetToString(required_extensions);
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Checks that the instruction can be used in this target environment's base
|
||||
// version. Assumes that CapabilityCheck has checked direct capability
|
||||
// dependencies for the opcode.
|
||||
|
@ -301,24 +302,27 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
const auto module_version = _.version();
|
||||
|
||||
ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions);
|
||||
if (exts.IsEmpty()) {
|
||||
// If no extensions can enable this instruction, then emit error messages
|
||||
// only concerning core SPIR-V versions if errors happen.
|
||||
// If no extensions can enable this instruction, then emit error
|
||||
// messages only concerning core SPIR-V versions if errors happen.
|
||||
if (min_version == ~0u) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvOpcodeString(opcode) << " is reserved for future use.";
|
||||
}
|
||||
|
||||
if (spvVersionForTargetEnv(_.grammar().target_env()) < min_version) {
|
||||
if (module_version < min_version) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvOpcodeString(opcode) << " requires "
|
||||
<< spvTargetEnvDescription(
|
||||
static_cast<spv_target_env>(min_version))
|
||||
<< " at minimum.";
|
||||
}
|
||||
// Otherwise, we only error out when no enabling extensions are registered.
|
||||
} else if (!_.HasAnyOfExtensions(exts)) {
|
||||
// Otherwise, we only error out when no enabling extensions are
|
||||
// registered.
|
||||
if (min_version == ~0u) {
|
||||
return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
|
@ -326,11 +330,11 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
|
|||
<< ExtensionSetToString(exts);
|
||||
}
|
||||
|
||||
if (static_cast<uint32_t>(_.grammar().target_env()) < min_version) {
|
||||
if (module_version < min_version) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvOpcodeString(opcode) << " requires "
|
||||
<< spvTargetEnvDescription(
|
||||
static_cast<spv_target_env>(min_version))
|
||||
<< spvOpcodeString(opcode) << " requires SPIR-V version "
|
||||
<< SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "."
|
||||
<< SPV_SPIRV_VERSION_MINOR_PART(min_version)
|
||||
<< " at minimum or one of the following extensions: "
|
||||
<< ExtensionSetToString(exts);
|
||||
}
|
||||
|
@ -367,11 +371,10 @@ spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) {
|
|||
|
||||
// Section 2.17 of SPIRV Spec specifies that the "Structure Nesting Depth"
|
||||
// must be less than or equal to 255.
|
||||
// This is interpreted as structures including other structures as members.
|
||||
// The code does not follow pointers or look into arrays to see if we reach a
|
||||
// structure downstream.
|
||||
// The nesting depth of a struct is 1+(largest depth of any member).
|
||||
// Scalars are at depth 0.
|
||||
// This is interpreted as structures including other structures as
|
||||
// members. The code does not follow pointers or look into arrays to see
|
||||
// if we reach a structure downstream. The nesting depth of a struct is
|
||||
// 1+(largest depth of any member). Scalars are at depth 0.
|
||||
uint32_t max_member_depth = 0;
|
||||
// Struct members start at word 2 of OpTypeStruct instruction.
|
||||
for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) {
|
||||
|
@ -394,8 +397,8 @@ spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) {
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Checks that the number of (literal, label) pairs in OpSwitch is within the
|
||||
// limit.
|
||||
// Checks that the number of (literal, label) pairs in OpSwitch is within
|
||||
// the limit.
|
||||
spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) {
|
||||
if (SpvOpSwitch == inst->opcode()) {
|
||||
// The instruction syntax is as follows:
|
||||
|
@ -414,7 +417,8 @@ spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) {
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Ensure the number of variables of the given class does not exceed the limit.
|
||||
// Ensure the number of variables of the given class does not exceed the
|
||||
// limit.
|
||||
spv_result_t LimitCheckNumVars(ValidationState_t& _, const uint32_t var_id,
|
||||
const SpvStorageClass storage_class) {
|
||||
if (SpvStorageClassFunction == storage_class) {
|
||||
|
@ -472,7 +476,8 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
|
|||
if (_.memory_model() != SpvMemoryModelVulkanKHR &&
|
||||
_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "VulkanMemoryModelKHR capability must only be specified if the "
|
||||
<< "VulkanMemoryModelKHR capability must only be specified if "
|
||||
"the "
|
||||
"VulkanKHR memory model is used.";
|
||||
}
|
||||
|
||||
|
@ -548,7 +553,6 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
|
|||
"capability is used.";
|
||||
}
|
||||
|
||||
if (auto error = ExtensionCheck(_, inst)) return error;
|
||||
if (auto error = ReservedCheck(_, inst)) return error;
|
||||
if (auto error = EnvironmentCheck(_, inst)) return error;
|
||||
if (auto error = CapabilityCheck(_, inst)) return error;
|
||||
|
|
|
@ -12,14 +12,15 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/val/function.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
|
@ -27,10 +28,16 @@ namespace val {
|
|||
namespace {
|
||||
|
||||
// Returns true if \c inst is an input or output variable.
|
||||
bool is_interface_variable(const Instruction* inst) {
|
||||
return inst->opcode() == SpvOpVariable &&
|
||||
(inst->word(3u) == SpvStorageClassInput ||
|
||||
inst->word(3u) == SpvStorageClassOutput);
|
||||
bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) {
|
||||
if (is_spv_1_4) {
|
||||
// Starting in SPIR-V 1.4, all global variables are interface variables.
|
||||
return inst->opcode() == SpvOpVariable &&
|
||||
inst->word(3u) != SpvStorageClassFunction;
|
||||
} else {
|
||||
return inst->opcode() == SpvOpVariable &&
|
||||
(inst->word(3u) == SpvStorageClassInput ||
|
||||
inst->word(3u) == SpvStorageClassOutput);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that \c var is listed as an interface in all the entry points that use
|
||||
|
@ -85,9 +92,8 @@ spv_result_t check_interface_variable(ValidationState_t& _,
|
|||
}
|
||||
if (!found) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, var)
|
||||
<< (var->word(3u) == SpvStorageClassInput ? "Input" : "Output")
|
||||
<< " variable id <" << var->id() << "> is used by entry point '"
|
||||
<< desc.name << "' id <" << id
|
||||
<< "Interface variable id <" << var->id()
|
||||
<< "> is used by entry point '" << desc.name << "' id <" << id
|
||||
<< ">, but is not listed as an interface";
|
||||
}
|
||||
}
|
||||
|
@ -99,8 +105,9 @@ spv_result_t check_interface_variable(ValidationState_t& _,
|
|||
} // namespace
|
||||
|
||||
spv_result_t ValidateInterfaces(ValidationState_t& _) {
|
||||
bool is_spv_1_4 = _.version() >= SPV_SPIRV_VERSION_WORD(1, 4);
|
||||
for (auto& inst : _.ordered_instructions()) {
|
||||
if (is_interface_variable(&inst)) {
|
||||
if (is_interface_variable(&inst, is_spv_1_4)) {
|
||||
if (auto error = check_interface_variable(_, &inst)) {
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -151,6 +151,14 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
|
|||
const Instruction* type_inst = _.FindDef(result_type);
|
||||
assert(type_inst);
|
||||
|
||||
const auto composites = _.features().select_between_composites;
|
||||
auto fail = [&_, composites, inst, opcode]() -> spv_result_t {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected scalar or "
|
||||
<< (composites ? "composite" : "vector")
|
||||
<< " type as Result Type: " << spvOpcodeString(opcode);
|
||||
};
|
||||
|
||||
const SpvOp type_opcode = type_inst->opcode();
|
||||
switch (type_opcode) {
|
||||
case SpvOpTypePointer: {
|
||||
|
@ -174,35 +182,48 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
|
|||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// Not RuntimeArray because of other rules.
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeStruct: {
|
||||
if (!composites) return fail();
|
||||
break;
|
||||
};
|
||||
|
||||
default:
|
||||
return fail();
|
||||
}
|
||||
|
||||
const uint32_t condition_type = _.GetOperandTypeId(inst, 2);
|
||||
const uint32_t left_type = _.GetOperandTypeId(inst, 3);
|
||||
const uint32_t right_type = _.GetOperandTypeId(inst, 4);
|
||||
|
||||
if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
|
||||
!_.IsBoolVectorType(condition_type)))
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected bool scalar or vector type as condition: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
if (_.GetDimension(condition_type) != dimension) {
|
||||
// If the condition is a vector type, then the result must also be a
|
||||
// vector with matching dimensions. In SPIR-V 1.4, a scalar condition
|
||||
// can be used to select between vector types. |composites| is a
|
||||
// proxy for SPIR-V 1.4 functionality.
|
||||
if (!composites || _.IsBoolVectorType(condition_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected scalar or vector type as Result Type: "
|
||||
<< "Expected vector sizes of Result Type and the condition "
|
||||
"to be equal: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
if (result_type != left_type || result_type != right_type)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected both objects to be of Result Type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t condition_type = _.GetOperandTypeId(inst, 2);
|
||||
const uint32_t left_type = _.GetOperandTypeId(inst, 3);
|
||||
const uint32_t right_type = _.GetOperandTypeId(inst, 4);
|
||||
|
||||
if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
|
||||
!_.IsBoolVectorType(condition_type)))
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected bool scalar or vector type as condition: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
if (_.GetDimension(condition_type) != dimension)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected vector sizes of Result Type and the condition to be"
|
||||
<< " equal: " << spvOpcodeString(opcode);
|
||||
|
||||
if (result_type != left_type || result_type != right_type)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected both objects to be of Result Type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SpvOpIEqual:
|
||||
|
|
|
@ -263,59 +263,40 @@ std::pair<SpvStorageClass, SpvStorageClass> GetStorageClass(
|
|||
return std::make_pair(dst_sc, src_sc);
|
||||
}
|
||||
|
||||
// Returns the number of instruction words taken up by a memory access
|
||||
// argument and its implied operands.
|
||||
int MemoryAccessNumWords(uint32_t mask) {
|
||||
int result = 1; // Count the mask
|
||||
if (mask & SpvMemoryAccessAlignedMask) ++result;
|
||||
if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++result;
|
||||
if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) ++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the scope ID operand for MakeAvailable memory access with mask
|
||||
// at the given operand index.
|
||||
// This function is only called for OpLoad, OpStore, OpCopyMemory and
|
||||
// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and
|
||||
// OpCooperativeMatrixStoreNV.
|
||||
uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask) {
|
||||
uint32_t offset = 1;
|
||||
if (mask & SpvMemoryAccessAlignedMask) ++offset;
|
||||
|
||||
uint32_t scope_id = 0;
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpLoad:
|
||||
case SpvOpCopyMemorySized:
|
||||
return inst->GetOperandAs<uint32_t>(3 + offset);
|
||||
case SpvOpStore:
|
||||
case SpvOpCopyMemory:
|
||||
return inst->GetOperandAs<uint32_t>(2 + offset);
|
||||
case SpvOpCooperativeMatrixLoadNV:
|
||||
return inst->GetOperandAs<uint32_t>(5 + offset);
|
||||
case SpvOpCooperativeMatrixStoreNV:
|
||||
return inst->GetOperandAs<uint32_t>(4 + offset);
|
||||
default:
|
||||
assert(false && "unexpected opcode");
|
||||
break;
|
||||
}
|
||||
|
||||
return scope_id;
|
||||
uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask,
|
||||
uint32_t mask_index) {
|
||||
assert(mask & SpvMemoryAccessMakePointerAvailableKHRMask);
|
||||
uint32_t this_bit = uint32_t(SpvMemoryAccessMakePointerAvailableKHRMask);
|
||||
uint32_t index =
|
||||
mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1)));
|
||||
return inst->GetOperandAs<uint32_t>(index);
|
||||
}
|
||||
|
||||
// This function is only called for OpLoad, OpStore, OpCopyMemory,
|
||||
// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and
|
||||
// OpCooperativeMatrixStoreNV.
|
||||
uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask) {
|
||||
uint32_t offset = 1;
|
||||
if (mask & SpvMemoryAccessAlignedMask) ++offset;
|
||||
if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++offset;
|
||||
|
||||
uint32_t scope_id = 0;
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpLoad:
|
||||
case SpvOpCopyMemorySized:
|
||||
return inst->GetOperandAs<uint32_t>(3 + offset);
|
||||
case SpvOpStore:
|
||||
case SpvOpCopyMemory:
|
||||
return inst->GetOperandAs<uint32_t>(2 + offset);
|
||||
case SpvOpCooperativeMatrixLoadNV:
|
||||
return inst->GetOperandAs<uint32_t>(5 + offset);
|
||||
case SpvOpCooperativeMatrixStoreNV:
|
||||
return inst->GetOperandAs<uint32_t>(4 + offset);
|
||||
default:
|
||||
assert(false && "unexpected opcode");
|
||||
break;
|
||||
}
|
||||
|
||||
return scope_id;
|
||||
uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask,
|
||||
uint32_t mask_index) {
|
||||
assert(mask & SpvMemoryAccessMakePointerVisibleKHRMask);
|
||||
uint32_t this_bit = uint32_t(SpvMemoryAccessMakePointerVisibleKHRMask);
|
||||
uint32_t index =
|
||||
mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1)));
|
||||
return inst->GetOperandAs<uint32_t>(index);
|
||||
}
|
||||
|
||||
bool DoesStructContainRTA(const ValidationState_t& _, const Instruction* inst) {
|
||||
|
@ -342,7 +323,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t mask = inst->GetOperandAs<uint32_t>(index);
|
||||
const uint32_t mask = inst->GetOperandAs<uint32_t>(index);
|
||||
if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) {
|
||||
if (inst->opcode() == SpvOpLoad ||
|
||||
inst->opcode() == SpvOpCooperativeMatrixLoadNV) {
|
||||
|
@ -357,7 +338,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
|||
}
|
||||
|
||||
// Check the associated scope for MakeAvailableKHR.
|
||||
const auto available_scope = GetMakeAvailableScope(inst, mask);
|
||||
const auto available_scope = GetMakeAvailableScope(inst, mask, index);
|
||||
if (auto error = ValidateMemoryScope(_, inst, available_scope))
|
||||
return error;
|
||||
}
|
||||
|
@ -376,7 +357,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
|||
}
|
||||
|
||||
// Check the associated scope for MakeVisibleKHR.
|
||||
const auto visible_scope = GetMakeVisibleScope(inst, mask);
|
||||
const auto visible_scope = GetMakeVisibleScope(inst, mask, index);
|
||||
if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
|
||||
}
|
||||
|
||||
|
@ -863,6 +844,51 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCopyMemoryMemoryAccess(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
assert(inst->opcode() == SpvOpCopyMemory ||
|
||||
inst->opcode() == SpvOpCopyMemorySized);
|
||||
const uint32_t first_access_index = inst->opcode() == SpvOpCopyMemory ? 2 : 3;
|
||||
if (inst->operands().size() > first_access_index) {
|
||||
if (auto error = CheckMemoryAccess(_, inst, first_access_index))
|
||||
return error;
|
||||
|
||||
const auto first_access = inst->GetOperandAs<uint32_t>(first_access_index);
|
||||
const uint32_t second_access_index =
|
||||
first_access_index + MemoryAccessNumWords(first_access);
|
||||
if (inst->operands().size() > second_access_index) {
|
||||
if (_.features().copy_memory_permits_two_memory_accesses) {
|
||||
if (auto error = CheckMemoryAccess(_, inst, second_access_index))
|
||||
return error;
|
||||
|
||||
// In the two-access form in SPIR-V 1.4 and later:
|
||||
// - the first is the target (write) access and it can't have
|
||||
// make-visible.
|
||||
// - the second is the source (read) access and it can't have
|
||||
// make-available.
|
||||
if (first_access & SpvMemoryAccessMakePointerVisibleKHRMask) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Target memory access must not include "
|
||||
"MakePointerVisibleKHR";
|
||||
}
|
||||
const auto second_access =
|
||||
inst->GetOperandAs<uint32_t>(second_access_index);
|
||||
if (second_access & SpvMemoryAccessMakePointerAvailableKHRMask) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Source memory access must not include "
|
||||
"MakePointerAvailableKHR";
|
||||
}
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(static_cast<SpvOp>(inst->opcode()))
|
||||
<< " with two memory access operands requires SPIR-V 1.4 or "
|
||||
"later";
|
||||
}
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto target_index = 0;
|
||||
const auto target_id = inst->GetOperandAs<uint32_t>(target_index);
|
||||
|
@ -968,7 +994,7 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
|
|||
|
||||
if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
return ValidateCopyMemoryMemoryAccess(_, inst);
|
||||
}
|
||||
|
||||
spv_result_t ValidateAccessChain(ValidationState_t& _,
|
||||
|
@ -1326,6 +1352,57 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _,
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidatePtrComparison(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (_.addressing_model() == SpvAddressingModelLogical &&
|
||||
!_.features().variable_pointers_storage_buffer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Instruction cannot be used without a variable pointers "
|
||||
"capability";
|
||||
}
|
||||
|
||||
const auto result_type = _.FindDef(inst->type_id());
|
||||
if (inst->opcode() == SpvOpPtrDiff) {
|
||||
if (!result_type || result_type->opcode() != SpvOpTypeInt) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result Type must be an integer scalar";
|
||||
}
|
||||
} else {
|
||||
if (!result_type || result_type->opcode() != SpvOpTypeBool) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result Type must be OpTypeBool";
|
||||
}
|
||||
}
|
||||
|
||||
const auto op1 = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
|
||||
const auto op2 = _.FindDef(inst->GetOperandAs<uint32_t>(3u));
|
||||
if (!op1 || !op2 || op1->type_id() != op2->type_id()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The types of Operand 1 and Operand 2 must match";
|
||||
}
|
||||
const auto op1_type = _.FindDef(op1->type_id());
|
||||
if (!op1_type || op1_type->opcode() != SpvOpTypePointer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Operand type must be a pointer";
|
||||
}
|
||||
|
||||
if (_.addressing_model() == SpvAddressingModelLogical) {
|
||||
SpvStorageClass sc = op1_type->GetOperandAs<SpvStorageClass>(1u);
|
||||
if (sc != SpvStorageClassWorkgroup && sc != SpvStorageClassStorageBuffer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Invalid pointer storage class";
|
||||
}
|
||||
|
||||
if (sc == SpvStorageClassWorkgroup && !_.features().variable_pointers) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Workgroup storage class pointer requires VariablePointers "
|
||||
"capability to be specified";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
|
||||
|
@ -1362,6 +1439,11 @@ spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
|
|||
case SpvOpCooperativeMatrixLengthNV:
|
||||
if (auto error = ValidateCooperativeMatrixLengthNV(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpPtrEqual:
|
||||
case SpvOpPtrNotEqual:
|
||||
case SpvOpPtrDiff:
|
||||
if (auto error = ValidatePtrComparison(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpImageTexelPointer:
|
||||
case SpvOpGenericPtrMemSemantics:
|
||||
default:
|
||||
|
|
|
@ -22,6 +22,23 @@
|
|||
namespace spvtools {
|
||||
namespace val {
|
||||
|
||||
bool IsValidScope(uint32_t scope) {
|
||||
// Deliberately avoid a default case so we have to update the list when the
|
||||
// scopes list changes.
|
||||
switch (static_cast<SpvScope>(scope)) {
|
||||
case SpvScopeCrossDevice:
|
||||
case SpvScopeDevice:
|
||||
case SpvScopeWorkgroup:
|
||||
case SpvScopeSubgroup:
|
||||
case SpvScopeInvocation:
|
||||
case SpvScopeQueueFamilyKHR:
|
||||
return true;
|
||||
case SpvScopeMax:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
spv_result_t ValidateExecutionScope(ValidationState_t& _,
|
||||
const Instruction* inst, uint32_t scope) {
|
||||
SpvOp opcode = inst->opcode();
|
||||
|
@ -52,6 +69,11 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _,
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (is_const_int32 && !IsValidScope(value)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
|
||||
}
|
||||
|
||||
// Vulkan specific rules
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
// Vulkan 1.1 specific rules
|
||||
|
@ -154,6 +176,11 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (is_const_int32 && !IsValidScope(value)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
|
||||
}
|
||||
|
||||
if (value == SpvScopeQueueFamilyKHR) {
|
||||
if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
|
||||
return SPV_SUCCESS;
|
||||
|
|
|
@ -319,6 +319,7 @@ spv_result_t ValidateTypePointer(ValidationState_t& _,
|
|||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
spv_result_t ValidateTypeFunction(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
|
@ -437,8 +438,6 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _,
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (!spvOpcodeGeneratesType(inst->opcode()) &&
|
||||
inst->opcode() != SpvOpTypeForwardPointer) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "source/opcode.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/val/basic_block.h"
|
||||
#include "source/val/construct.h"
|
||||
|
@ -146,6 +147,30 @@ spv_result_t CountInstructions(void* user_data,
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t setHeader(void* user_data, spv_endianness_t, uint32_t,
|
||||
uint32_t version, uint32_t generator, uint32_t id_bound,
|
||||
uint32_t) {
|
||||
ValidationState_t& vstate =
|
||||
*(reinterpret_cast<ValidationState_t*>(user_data));
|
||||
vstate.setIdBound(id_bound);
|
||||
vstate.setGenerator(generator);
|
||||
vstate.setVersion(version);
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Add features based on SPIR-V core version number.
|
||||
void UpdateFeaturesBasedOnSpirvVersion(ValidationState_t::Feature* features,
|
||||
uint32_t version) {
|
||||
assert(features);
|
||||
if (version >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
features->select_between_composites = true;
|
||||
features->copy_memory_permits_two_memory_accesses = true;
|
||||
features->uconvert_spec_constant_op = true;
|
||||
features->nonwritable_var_in_function_or_private = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ValidationState_t::ValidationState_t(const spv_const_context ctx,
|
||||
|
@ -205,11 +230,12 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx,
|
|||
spv_context_t hijacked_context = *ctx;
|
||||
hijacked_context.consumer = [](spv_message_level_t, const char*,
|
||||
const spv_position_t&, const char*) {};
|
||||
spvBinaryParse(&hijacked_context, this, words, num_words,
|
||||
/* parsed_header = */ nullptr, CountInstructions,
|
||||
spvBinaryParse(&hijacked_context, this, words, num_words, setHeader,
|
||||
CountInstructions,
|
||||
/* diagnostic = */ nullptr);
|
||||
preallocateStorage();
|
||||
}
|
||||
UpdateFeaturesBasedOnSpirvVersion(&features_, version_);
|
||||
|
||||
friendly_mapper_ = spvtools::MakeUnique<spvtools::FriendlyNameMapper>(
|
||||
context_, words_, num_words_);
|
||||
|
@ -447,7 +473,7 @@ void ValidationState_t::set_addressing_model(SpvAddressingModel am) {
|
|||
pointer_size_and_alignment_ = 4;
|
||||
break;
|
||||
default:
|
||||
// fall through
|
||||
// fall through
|
||||
case SpvAddressingModelPhysical64:
|
||||
case SpvAddressingModelPhysicalStorageBuffer64EXT:
|
||||
pointer_size_and_alignment_ = 8;
|
||||
|
|
|
@ -106,9 +106,20 @@ class ValidationState_t {
|
|||
// Members need not be listed in offset order
|
||||
bool scalar_block_layout = false;
|
||||
|
||||
// Permit UConvert as an OpSpecConstantOp operation.
|
||||
// SPIR-V 1.4 allows us to select between any two composite values
|
||||
// of the same type.
|
||||
bool select_between_composites = false;
|
||||
|
||||
// SPIR-V 1.4 allows two memory access operands for OpCopyMemory and
|
||||
// OpCopyMemorySized.
|
||||
bool copy_memory_permits_two_memory_accesses = false;
|
||||
|
||||
// SPIR-V 1.4 allows UConvert as a spec constant op in any environment.
|
||||
// The Kernel capability already enables it, separately from this flag.
|
||||
bool uconvert_spec_constant_op = false;
|
||||
|
||||
// SPIR-V 1.4 allows Function and Private variables to be NonWritable
|
||||
bool nonwritable_var_in_function_or_private = false;
|
||||
};
|
||||
|
||||
ValidationState_t(const spv_const_context context,
|
||||
|
|
|
@ -129,6 +129,7 @@ set(TEST_SOURCES
|
|||
text_start_new_inst_test.cpp
|
||||
text_to_binary.annotation_test.cpp
|
||||
text_to_binary.barrier_test.cpp
|
||||
text_to_binary.composite_test.cpp
|
||||
text_to_binary.constant_test.cpp
|
||||
text_to_binary.control_flow_test.cpp
|
||||
text_to_binary_test.cpp
|
||||
|
|
|
@ -51,7 +51,7 @@ TEST_F(BinaryHeaderGet, Default) {
|
|||
ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header));
|
||||
|
||||
ASSERT_EQ(static_cast<uint32_t>(SpvMagicNumber), header.magic);
|
||||
ASSERT_EQ(0x00010300u, header.version);
|
||||
ASSERT_EQ(0x00010400u, header.version);
|
||||
ASSERT_EQ(static_cast<uint32_t>(SPV_GENERATOR_CODEPLAY), header.generator);
|
||||
ASSERT_EQ(1u, header.bound);
|
||||
ASSERT_EQ(0u, header.schema);
|
||||
|
|
|
@ -709,8 +709,8 @@ OpDecorate %1 Aliased
|
|||
EXPECT_THAT(GetErrorMessage(), "");
|
||||
|
||||
std::string expected_decorations =
|
||||
R"(OpDecorateStringGOOGLE %5 HlslSemanticGOOGLE "blah"
|
||||
OpDecorateId %5 HlslCounterBufferGOOGLE %2
|
||||
R"(OpDecorateString %5 UserSemantic "blah"
|
||||
OpDecorateId %5 CounterBuffer %2
|
||||
OpDecorate %5 Aliased
|
||||
)";
|
||||
EXPECT_THAT(ToText(decorations), expected_decorations);
|
||||
|
@ -720,11 +720,11 @@ OpCapability Linkage
|
|||
OpExtension "SPV_GOOGLE_hlsl_functionality1"
|
||||
OpExtension "SPV_GOOGLE_decorate_string"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "blah"
|
||||
OpDecorateId %1 HlslCounterBufferGOOGLE %2
|
||||
OpDecorateString %1 UserSemantic "blah"
|
||||
OpDecorateId %1 CounterBuffer %2
|
||||
OpDecorate %1 Aliased
|
||||
OpDecorateStringGOOGLE %5 HlslSemanticGOOGLE "blah"
|
||||
OpDecorateId %5 HlslCounterBufferGOOGLE %2
|
||||
OpDecorateString %5 UserSemantic "blah"
|
||||
OpDecorateId %5 CounterBuffer %2
|
||||
OpDecorate %5 Aliased
|
||||
%3 = OpTypeInt 32 0
|
||||
%4 = OpTypePointer Uniform %3
|
||||
|
|
|
@ -60,6 +60,29 @@ TEST_P(OpDecorateSimpleTest, AnySimpleDecoration) {
|
|||
Eq(input.str()));
|
||||
}
|
||||
|
||||
// Like above, but parameters to the decoration are IDs.
|
||||
using OpDecorateSimpleIdTest =
|
||||
spvtest::TextToBinaryTestBase<::testing::TestWithParam<
|
||||
std::tuple<spv_target_env, EnumCase<SpvDecoration>>>>;
|
||||
|
||||
TEST_P(OpDecorateSimpleIdTest, AnySimpleDecoration) {
|
||||
// This string should assemble, but should not validate.
|
||||
std::stringstream input;
|
||||
input << "OpDecorateId %1 " << std::get<1>(GetParam()).name();
|
||||
for (auto operand : std::get<1>(GetParam()).operands())
|
||||
input << " %" << operand;
|
||||
input << std::endl;
|
||||
EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())),
|
||||
Eq(MakeInstruction(SpvOpDecorateId,
|
||||
{1, uint32_t(std::get<1>(GetParam()).value())},
|
||||
std::get<1>(GetParam()).operands())));
|
||||
// Also check disassembly.
|
||||
EXPECT_THAT(
|
||||
EncodeAndDecodeSuccessfully(input.str(), SPV_BINARY_TO_TEXT_OPTION_NONE,
|
||||
std::get<0>(GetParam())),
|
||||
Eq(input.str()));
|
||||
}
|
||||
|
||||
#define CASE(NAME) SpvDecoration##NAME, #NAME
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TextToBinaryDecorateSimple, OpDecorateSimpleTest,
|
||||
|
@ -112,6 +135,21 @@ INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV11, OpDecorateSimpleTest,
|
|||
Combine(Values(SPV_ENV_UNIVERSAL_1_1),
|
||||
Values(EnumCase<SpvDecoration>{
|
||||
CASE(MaxByteOffset), {128}})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV14, OpDecorateSimpleTest,
|
||||
Combine(Values(SPV_ENV_UNIVERSAL_1_4),
|
||||
ValuesIn(std::vector<EnumCase<SpvDecoration>>{
|
||||
{CASE(Uniform), {}},
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleIdV14,
|
||||
OpDecorateSimpleIdTest,
|
||||
Combine(Values(SPV_ENV_UNIVERSAL_1_4),
|
||||
ValuesIn(std::vector<EnumCase<SpvDecoration>>{
|
||||
// In 1.4, UniformId decoration takes a
|
||||
// scope Id.
|
||||
{CASE(UniformId), {1}},
|
||||
})));
|
||||
#undef CASE
|
||||
|
||||
TEST_F(OpDecorateSimpleTest, WrongDecoration) {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2018 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Assembler tests for instructions in the "Group Instrucions" section of the
|
||||
// SPIR-V spec.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "test/test_fixture.h"
|
||||
#include "test/unit_spirv.h"
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
namespace spvtools {
|
||||
namespace {
|
||||
|
||||
using spvtest::Concatenate;
|
||||
|
||||
using CompositeRoundTripTest = RoundTripTest;
|
||||
|
||||
TEST_F(CompositeRoundTripTest, Good) {
|
||||
std::string spirv = "%2 = OpCopyLogical %1 %3\n";
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(CompositeRoundTripTest, V13Bad) {
|
||||
std::string spirv = "%2 = OpCopyLogical %1 %3\n";
|
||||
std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpCopyLogical'"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace spvtools
|
|
@ -276,11 +276,11 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
{"OpCapability StorageBuffer16BitAccess\n",
|
||||
MakeInstruction(SpvOpCapability,
|
||||
{SpvCapabilityStorageBuffer16BitAccess})},
|
||||
{"OpCapability StorageUniform16\n",
|
||||
{"OpCapability UniformAndStorageBuffer16BitAccess\n",
|
||||
MakeInstruction(
|
||||
SpvOpCapability,
|
||||
{SpvCapabilityUniformAndStorageBuffer16BitAccess})},
|
||||
{"OpCapability StorageUniform16\n",
|
||||
{"OpCapability UniformAndStorageBuffer16BitAccess\n",
|
||||
MakeInstruction(SpvOpCapability,
|
||||
{SpvCapabilityStorageUniform16})},
|
||||
{"OpCapability StoragePushConstant16\n",
|
||||
|
@ -656,8 +656,38 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
|
||||
// SPV_GOOGLE_decorate_string
|
||||
|
||||
// Now that OpDecorateString is the preferred spelling for
|
||||
// OpDecorateStringGOOGLE use that name in round trip tests, and the GOOGLE
|
||||
// name in an assembly-only test.
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_GOOGLE_decorate_string, ExtensionRoundTripTest,
|
||||
Combine(
|
||||
// We'll get coverage over operand tables by trying the universal
|
||||
// environments, and at least one specific environment.
|
||||
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
|
||||
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0),
|
||||
ValuesIn(std::vector<AssemblyCase>{
|
||||
{"OpDecorateString %1 UserSemantic \"ABC\"\n",
|
||||
MakeInstruction(SpvOpDecorateStringGOOGLE,
|
||||
{1, SpvDecorationHlslSemanticGOOGLE},
|
||||
MakeVector("ABC"))},
|
||||
{"OpDecorateString %1 UserSemantic \"ABC\"\n",
|
||||
MakeInstruction(SpvOpDecorateString,
|
||||
{1, SpvDecorationUserSemantic},
|
||||
MakeVector("ABC"))},
|
||||
{"OpMemberDecorateString %1 3 UserSemantic \"DEF\"\n",
|
||||
MakeInstruction(SpvOpMemberDecorateStringGOOGLE,
|
||||
{1, 3, SpvDecorationUserSemantic},
|
||||
MakeVector("DEF"))},
|
||||
{"OpMemberDecorateString %1 3 UserSemantic \"DEF\"\n",
|
||||
MakeInstruction(SpvOpMemberDecorateString,
|
||||
{1, 3, SpvDecorationUserSemantic},
|
||||
MakeVector("DEF"))},
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_GOOGLE_decorate_string, ExtensionAssemblyTest,
|
||||
Combine(
|
||||
// We'll get coverage over operand tables by trying the universal
|
||||
// environments, and at least one specific environment.
|
||||
|
@ -676,8 +706,29 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
|
||||
// SPV_GOOGLE_hlsl_functionality1
|
||||
|
||||
// Now that CounterBuffer is the preferred spelling for HlslCounterBufferGOOGLE,
|
||||
// use that name in round trip tests, and the GOOGLE name in an assembly-only
|
||||
// test.
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_GOOGLE_hlsl_functionality1, ExtensionRoundTripTest,
|
||||
Combine(
|
||||
// We'll get coverage over operand tables by trying the universal
|
||||
// environments, and at least one specific environment.
|
||||
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
|
||||
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0),
|
||||
// HlslSemanticGOOGLE is tested in SPV_GOOGLE_decorate_string, since
|
||||
// they are coupled together.
|
||||
ValuesIn(std::vector<AssemblyCase>{
|
||||
{"OpDecorateId %1 CounterBuffer %2\n",
|
||||
MakeInstruction(SpvOpDecorateId,
|
||||
{1, SpvDecorationHlslCounterBufferGOOGLE, 2})},
|
||||
{"OpDecorateId %1 CounterBuffer %2\n",
|
||||
MakeInstruction(SpvOpDecorateId,
|
||||
{1, SpvDecorationCounterBuffer, 2})},
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_GOOGLE_hlsl_functionality1, ExtensionAssemblyTest,
|
||||
Combine(
|
||||
// We'll get coverage over operand tables by trying the universal
|
||||
// environments, and at least one specific environment.
|
||||
|
|
|
@ -30,6 +30,7 @@ using spvtest::EnumCase;
|
|||
using spvtest::MakeInstruction;
|
||||
using spvtest::TextToBinaryTest;
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
// Test assembly of Memory Access masks
|
||||
|
||||
|
@ -95,12 +96,326 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
#undef CASE
|
||||
// clang-format on
|
||||
|
||||
using MemoryRoundTripTest = RoundTripTest;
|
||||
|
||||
// OpPtrEqual appeared in SPIR-V 1.4
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrEqualGood) {
|
||||
std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpPtrEqual, {1, 2, 3, 4})));
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrEqualV13Bad) {
|
||||
std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n";
|
||||
std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrEqual'"));
|
||||
}
|
||||
|
||||
// OpPtrNotEqual appeared in SPIR-V 1.4
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrNotEqualGood) {
|
||||
std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpPtrNotEqual, {1, 2, 3, 4})));
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrNotEqualV13Bad) {
|
||||
std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n";
|
||||
std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrNotEqual'"));
|
||||
}
|
||||
|
||||
// OpPtrDiff appeared in SPIR-V 1.4
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrDiffGood) {
|
||||
std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpPtrDiff, {1, 2, 3, 4})));
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrDiffV13Good) {
|
||||
// OpPtrDiff is enabled by a capability as well, so we can assemble
|
||||
// it even in older SPIR-V environments. We do that so we can
|
||||
// write tests.
|
||||
std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n";
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
}
|
||||
|
||||
// OpCopyMemory
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) {
|
||||
std::string spirv = "OpCopyMemory %1\n";
|
||||
std::string err = CompileFailure(spirv);
|
||||
EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream"));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 %3\n";
|
||||
std::string err = CompileFailure(spirv);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%3'"));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNoneGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 None\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 0})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVolatileGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Volatile\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAligned8Good) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Aligned 8\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 2, 8})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNontemporalGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Nontemporal\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAvGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailableKHR %3\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 8, 3})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVisGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 MakePointerVisibleKHR %3\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 16, 3})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNonPrivateGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointerKHR\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 32})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessMixedGood) {
|
||||
std::string spirv =
|
||||
"OpCopyMemory %1 %2 "
|
||||
"Volatile|Aligned|Nontemporal|MakePointerAvailableKHR|"
|
||||
"MakePointerVisibleKHR|NonPrivatePointerKHR 16 %3 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 63, 16, 3, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV13Good) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n";
|
||||
// Note: This will assemble but should not validate for SPIR-V 1.3
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV14Good) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessMixedV14Good) {
|
||||
std::string spirv =
|
||||
"OpCopyMemory %1 %2 Volatile|Nontemporal|"
|
||||
"MakePointerVisibleKHR %3 "
|
||||
"Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 16 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 21, 3, 42, 16, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
// OpCopyMemorySized
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2\n";
|
||||
std::string err = CompileFailure(spirv);
|
||||
EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream"));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 %4\n";
|
||||
std::string err = CompileFailure(spirv);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%4'"));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNoneGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 None\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 0})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVolatileGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAligned8Good) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Aligned 8\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 2, 8})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNontemporalGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Nontemporal\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAvGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailableKHR %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 8, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVisGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisibleKHR %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 16, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNonPrivateGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointerKHR\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 32})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessMixedGood) {
|
||||
std::string spirv =
|
||||
"OpCopyMemorySized %1 %2 %3 "
|
||||
"Volatile|Aligned|Nontemporal|MakePointerAvailableKHR|"
|
||||
"MakePointerVisibleKHR|NonPrivatePointerKHR 16 %4 %5\n";
|
||||
EXPECT_THAT(
|
||||
CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV13Good) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n";
|
||||
// Note: This will assemble but should not validate for SPIR-V 1.3
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV14Good) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessMixedV14Good) {
|
||||
std::string spirv =
|
||||
"OpCopyMemorySized %1 %2 %3 Volatile|Nontemporal|"
|
||||
"MakePointerVisibleKHR %4 "
|
||||
"Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 16 %5\n";
|
||||
EXPECT_THAT(
|
||||
CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 21, 4, 42, 16, 5})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
// TODO(dneto): OpVariable with initializers
|
||||
// TODO(dneto): OpImageTexelPointer
|
||||
// TODO(dneto): OpLoad
|
||||
// TODO(dneto): OpStore
|
||||
// TODO(dneto): OpCopyMemory
|
||||
// TODO(dneto): OpCopyMemorySized
|
||||
// TODO(dneto): OpAccessChain
|
||||
// TODO(dneto): OpInBoundsAccessChain
|
||||
// TODO(dneto): OpPtrAccessChain
|
||||
|
|
|
@ -26,6 +26,8 @@ import traceback
|
|||
from spirv_test_framework import SpirvTest
|
||||
from builtins import bytes
|
||||
|
||||
DEFAULT_SPIRV_VERSION = 0x010000
|
||||
|
||||
def convert_to_unix_line_endings(source):
|
||||
"""Converts all line endings in source to be unix line endings."""
|
||||
result = source.replace('\r\n', '\n').replace('\r', '\n')
|
||||
|
@ -172,7 +174,7 @@ class CorrectBinaryLengthAndPreamble(SpirvTest):
|
|||
# profile
|
||||
|
||||
if version != spv_version and version != 0:
|
||||
return False, 'Incorrect SPV binary: wrong version number'
|
||||
return False, 'Incorrect SPV binary: wrong version number: ' + hex(version) + ' expected ' + hex(spv_version)
|
||||
# Shaderc-over-Glslang (0x000d....) or
|
||||
# SPIRV-Tools (0x0007....) generator number
|
||||
if read_word(preamble, 2, little_endian) != 0x000d0007 and \
|
||||
|
@ -188,7 +190,9 @@ class CorrectBinaryLengthAndPreamble(SpirvTest):
|
|||
class CorrectObjectFilePreamble(CorrectBinaryLengthAndPreamble):
|
||||
"""Provides methods for verifying preamble for a SPV object file."""
|
||||
|
||||
def verify_object_file_preamble(self, filename, spv_version=0x10000):
|
||||
def verify_object_file_preamble(self,
|
||||
filename,
|
||||
spv_version=DEFAULT_SPIRV_VERSION):
|
||||
"""Checks that the given SPIR-V binary file has correct preamble."""
|
||||
|
||||
success, message = verify_file_non_empty(filename)
|
||||
|
@ -257,6 +261,21 @@ class ValidObjectFile1_3(ReturnCodeIsZero, CorrectObjectFilePreamble):
|
|||
return True, ''
|
||||
|
||||
|
||||
class ValidObjectFile1_4(ReturnCodeIsZero, CorrectObjectFilePreamble):
|
||||
"""Mixin class for checking that every input file generates a valid SPIR-V 1.4
|
||||
object file following the object file naming rule, and there is no output on
|
||||
stdout/stderr."""
|
||||
|
||||
def check_object_file_preamble(self, status):
|
||||
for input_filename in status.input_filenames:
|
||||
object_filename = get_object_filename(input_filename)
|
||||
success, message = self.verify_object_file_preamble(
|
||||
os.path.join(status.directory, object_filename), 0x10400)
|
||||
if not success:
|
||||
return False, message
|
||||
return True, ''
|
||||
|
||||
|
||||
class ValidObjectFileWithAssemblySubstr(SuccessfulReturn,
|
||||
CorrectObjectFilePreamble):
|
||||
"""Mixin class for checking that every input file generates a valid object
|
||||
|
|
|
@ -34,7 +34,7 @@ def empty_main_assembly():
|
|||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptBase')
|
||||
class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_3):
|
||||
class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_4):
|
||||
"""Tests that spirv-opt accepts a SPIR-V object file."""
|
||||
|
||||
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
|
||||
|
@ -52,7 +52,7 @@ class TestHelpFlag(expect.ReturnCodeIsZero, expect.StdoutMatch):
|
|||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestValidPassFlags(expect.ValidObjectFile1_3,
|
||||
class TestValidPassFlags(expect.ValidObjectFile1_4,
|
||||
expect.ExecutedListOfPasses):
|
||||
"""Tests that spirv-opt accepts all valid optimization flags."""
|
||||
|
||||
|
@ -129,7 +129,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_3,
|
|||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_3,
|
||||
class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_4,
|
||||
expect.ExecutedListOfPasses):
|
||||
"""Tests that spirv-opt schedules all the passes triggered by -O."""
|
||||
|
||||
|
@ -176,7 +176,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_3,
|
|||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestSizeOptimizationPasses(expect.ValidObjectFile1_3,
|
||||
class TestSizeOptimizationPasses(expect.ValidObjectFile1_4,
|
||||
expect.ExecutedListOfPasses):
|
||||
"""Tests that spirv-opt schedules all the passes triggered by -Os."""
|
||||
|
||||
|
@ -215,7 +215,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_3,
|
|||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestLegalizationPasses(expect.ValidObjectFile1_3,
|
||||
class TestLegalizationPasses(expect.ValidObjectFile1_4,
|
||||
expect.ExecutedListOfPasses):
|
||||
"""Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl.
|
||||
"""
|
||||
|
|
|
@ -954,8 +954,8 @@ TEST_F(ValidateBarriers, TypeAsMemoryScope) {
|
|||
OpMemoryBarrier %u32 %u32_0
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 5[%uint] cannot be a "
|
||||
"type"));
|
||||
}
|
||||
|
|
|
@ -2265,8 +2265,8 @@ OpMemoryModel Logical Simple
|
|||
EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("operand 5255 requires one of these extensions: "
|
||||
"SPV_NV_viewport_array2"));
|
||||
HasSubstr("operand ShaderViewportMaskNV(5255) requires one of "
|
||||
"these extensions: SPV_NV_viewport_array2"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCapability,
|
||||
|
@ -2347,8 +2347,8 @@ OpFunctionEnd
|
|||
EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("operand 5568 requires one of these extensions: "
|
||||
"SPV_INTEL_subgroups"));
|
||||
HasSubstr("operand SubgroupShuffleINTEL(5568) requires one of "
|
||||
"these extensions: SPV_INTEL_subgroups"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCapability,
|
||||
|
@ -2407,6 +2407,33 @@ OpMemoryModel Logical GLSL450
|
|||
"specified if the VulkanKHR memory model is used"));
|
||||
}
|
||||
|
||||
// In the grammar, SubgroupEqMask and SubgroupMaskKHR have different enabling
|
||||
// lists of extensions.
|
||||
TEST_F(ValidateCapability, SubgroupEqMaskEnabledByExtension) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability SubgroupBallotKHR
|
||||
OpExtension "SPV_KHR_shader_ballot"
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpDecorate %var BuiltIn SubgroupEqMask
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%ptr_uint = OpTypePointer Private %uint
|
||||
%var = OpVariable %ptr_uint Private
|
||||
%fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %fn
|
||||
%entry = OpLabel
|
||||
%val = OpLoad %uint %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_0))
|
||||
<< getDiagnosticString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -1568,6 +1568,209 @@ OpFunctionEnd
|
|||
HasSubstr("Expected Index to be int scalar"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalSameType) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%struct = OpTypeStruct
|
||||
%const_struct = OpConstantComposite %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Result Type must not equal the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalSameStructDifferentId) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%struct1 = OpTypeStruct
|
||||
%struct2 = OpTypeStruct
|
||||
%const_struct = OpConstantComposite %struct1
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct2 %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalArrayDifferentLength) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_4 = OpConstant %int 4
|
||||
%int_5 = OpConstant %int 5
|
||||
%array1 = OpTypeArray %int %int_4
|
||||
%array2 = OpTypeArray %int %int_5
|
||||
%const_array = OpConstantComposite %array1 %int_4 %int_4 %int_4 %int_4
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %array2 %const_array
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Result Type does not logically match the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalArrayDifferentElement) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
%int = OpTypeInt 32 0
|
||||
%int_4 = OpConstant %int 4
|
||||
%array1 = OpTypeArray %int %int_4
|
||||
%array2 = OpTypeArray %float %int_4
|
||||
%const_array = OpConstantComposite %array1 %int_4 %int_4 %int_4 %int_4
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %array2 %const_array
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Result Type does not logically match the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalArrayLogicallyMatchedElement) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
%int = OpTypeInt 32 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%inner1 = OpTypeArray %int %int_1
|
||||
%inner2 = OpTypeArray %int %int_1
|
||||
%array1 = OpTypeArray %inner1 %int_1
|
||||
%array2 = OpTypeArray %inner2 %int_1
|
||||
%const_inner = OpConstantComposite %inner1 %int_1
|
||||
%const_array = OpConstantComposite %array1 %const_inner
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %array2 %const_array
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalStructDifferentNumberElements) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct1 = OpTypeStruct
|
||||
%struct2 = OpTypeStruct %int
|
||||
%const_struct = OpConstantComposite %struct1
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct2 %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Result Type does not logically match the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalStructDifferentElement) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%struct1 = OpTypeStruct %int %uint
|
||||
%struct2 = OpTypeStruct %int %int
|
||||
%const_struct = OpConstantComposite %struct1 %int_0 %uint_0
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct2 %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Result Type does not logically match the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalStructLogicallyMatch) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%array1 = OpTypeArray %int %int_1
|
||||
%array2 = OpTypeArray %int %int_1
|
||||
%struct1 = OpTypeStruct %int %array1
|
||||
%struct2 = OpTypeStruct %int %array2
|
||||
%const_array = OpConstantComposite %array1 %int_1
|
||||
%const_struct = OpConstantComposite %struct1 %int_1 %const_array
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct2 %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -198,6 +198,60 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
"%v = OpSpecConstantOp %bool SGreaterThanEqual %uint_0 %uint_0"),
|
||||
}));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UConvert, ValidateConstantOp,
|
||||
ValuesIn(std::vector<ConstantOpCase>{
|
||||
// TODO(dneto): Conversions must change width.
|
||||
{SPV_ENV_UNIVERSAL_1_0,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_1,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_3,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_3,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_4,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_0,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
false,
|
||||
"Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability"},
|
||||
{SPV_ENV_UNIVERSAL_1_1,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
false,
|
||||
"Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability"},
|
||||
{SPV_ENV_UNIVERSAL_1_3,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
false,
|
||||
"Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability"},
|
||||
{SPV_ENV_UNIVERSAL_1_3,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
false,
|
||||
"Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability"},
|
||||
{SPV_ENV_UNIVERSAL_1_4,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
}));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
KernelInKernel, ValidateConstantOp,
|
||||
ValuesIn(std::vector<ConstantOpCase>{
|
||||
|
@ -294,6 +348,25 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
true, ""},
|
||||
}));
|
||||
|
||||
TEST_F(ValidateConstant, SpecConstantUConvert1p3Binary1p4EnvBad) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%int = OpTypeInt 32 0
|
||||
%int0 = OpConstant %int 0
|
||||
%const = OpSpecConstantOp %int UConvert %int0
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Prior to SPIR-V 1.4, specialization constant operation UConvert "
|
||||
"requires Kernel capability or extension SPV_AMD_gpu_shader_int16"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -778,9 +778,10 @@ TEST_F(ValidateData, vulkan_disallow_free_fp_rounding_mode) {
|
|||
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Operand 2 of Decorate requires one of these capabilities: "
|
||||
"StorageBuffer16BitAccess StorageUniform16 "
|
||||
"StoragePushConstant16 StorageInputOutput16"));
|
||||
HasSubstr(
|
||||
"Operand 2 of Decorate requires one of these capabilities: "
|
||||
"StorageBuffer16BitAccess UniformAndStorageBuffer16BitAccess "
|
||||
"StoragePushConstant16 StorageInputOutput16"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "gmock/gmock.h"
|
||||
#include "source/val/decoration.h"
|
||||
#include "test/test_fixture.h"
|
||||
#include "test/unit_spirv.h"
|
||||
#include "test/val/val_code_generator.h"
|
||||
#include "test/val/val_fixtures.h"
|
||||
|
@ -4708,7 +4709,7 @@ OpFunctionEnd
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
// Uniform decoration
|
||||
// Uniform and UniformId decorations
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationGood) {
|
||||
const std::string spirv = R"(
|
||||
|
@ -4737,49 +4738,96 @@ OpFunctionEnd
|
|||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationTargetsTypeBad) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpDecorate %fn Uniform
|
||||
%void = OpTypeVoid
|
||||
%fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Uniform decoration applied to a non-object"));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("%2 = OpTypeFunction %void"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationTargetsVoidValueBad) {
|
||||
const std::string spirv = R"(
|
||||
// Returns SPIR-V assembly for a shader that uses a given decoration
|
||||
// instruction.
|
||||
std::string ShaderWithUniformLikeDecoration(const std::string& inst) {
|
||||
return std::string(R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %subgroupscope "subgroupscope"
|
||||
OpName %call "call"
|
||||
OpName %myfunc "myfunc"
|
||||
OpDecorate %call Uniform
|
||||
OpName %int0 "int0"
|
||||
OpName %float0 "float0"
|
||||
OpName %fn "fn"
|
||||
)") + inst +
|
||||
R"(
|
||||
%void = OpTypeVoid
|
||||
%fnty = OpTypeFunction %void
|
||||
%myfunc = OpFunction %void None %fnty
|
||||
%float = OpTypeFloat 32
|
||||
%int = OpTypeInt 32 1
|
||||
%int0 = OpConstantNull %int
|
||||
%int_99 = OpConstant %int 99
|
||||
%subgroupscope = OpConstant %int 3
|
||||
%float0 = OpConstantNull %float
|
||||
%fn = OpTypeFunction %void
|
||||
%myfunc = OpFunction %void None %fn
|
||||
%myfuncentry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %fnty
|
||||
%main = OpFunction %void None %fn
|
||||
%entry = OpLabel
|
||||
%call = OpFunctionCall %void %myfunc
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13Bad) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %int0 UniformId %subgroupscope");
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires SPIR-V version 1.4 or later\n"
|
||||
" OpDecorateId %int0 UniformId %subgroupscope"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13BadTargetV14) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %int0 UniformId %subgroupscope");
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires SPIR-V version 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV14Good) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %int0 UniformId %subgroupscope");
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationTargetsTypeBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorate %fn Uniform");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Uniform decoration applied to a non-object"));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("%fn = OpTypeFunction %void"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationTargetsTypeBad) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %fn UniformId %subgroupscope");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("UniformId decoration applied to a non-object"));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("%fn = OpTypeFunction %void"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationTargetsVoidValueBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorate %call Uniform");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
|
@ -4788,6 +4836,82 @@ OpFunctionEnd
|
|||
" %call = OpFunctionCall %void %myfunc"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationTargetsVoidValueBad) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %call UniformId %subgroupscope");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4))
|
||||
<< spirv;
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("UniformId decoration applied to a value with void type\n"
|
||||
" %call = OpFunctionCall %void %myfunc"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations,
|
||||
UniformDecorationWithScopeIdV14IdIsFloatValueIsBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %float0");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("ConstantNull: expected Execution Scope to be a 32-bit int"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations,
|
||||
UniformDecorationWithScopeIdV14IdIsInvalidIntValueBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int_99");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Invalid scope value:\n %int_99 = OpConstant %int 99\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationWithScopeIdV14VulkanEnv) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int0");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1_SPIRV_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr(": in Vulkan environment Execution Scope is limited to "
|
||||
"Workgroup and Subgroup"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationWithWrongInstructionBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorateId %int0 Uniform");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_2);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Decorations that don't take ID parameters may not be "
|
||||
"used with OpDecorateId\n"
|
||||
" OpDecorateId %int0 Uniform"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationWithWrongInstructionBad) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorate %int0 UniformId %subgroupscope");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Decorations taking ID parameters may not be used with OpDecorateId\n"
|
||||
" OpDecorate %int0 UniformId %subgroupscope"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, MultipleOffsetDecorationsOnSameID) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -5070,6 +5194,26 @@ TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionBad) {
|
|||
"SPV_KHR_no_integer_wrap_decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionV13Bad) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero", "");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires one of these extensions: "
|
||||
"SPV_KHR_no_integer_wrap_decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoSignedWrapOkInSPV14Good) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero", "");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoSignedWrapIAddGood) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero");
|
||||
|
@ -5192,6 +5336,26 @@ TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionBad) {
|
|||
"SPV_KHR_no_integer_wrap_decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionV13Bad) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero", "");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires one of these extensions: "
|
||||
"SPV_KHR_no_integer_wrap_decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoUnsignedWrapOkInSPV14Good) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero", "");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoUnsignedWrapIAddGood) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero");
|
||||
|
@ -5987,6 +6151,18 @@ TEST_F(ValidateDecorations, NonWritableVarWorkgroupBad) {
|
|||
"buffer\n %var_wg"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarWorkgroupV14Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_wg");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, storage "
|
||||
"buffer, or variable in Private or Function storage "
|
||||
"class\n %var_wg"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarPrivateBad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_priv");
|
||||
|
||||
|
@ -5998,6 +6174,36 @@ TEST_F(ValidateDecorations, NonWritableVarPrivateBad) {
|
|||
"buffer\n %var_priv"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarPrivateV13Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_priv");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, or storage "
|
||||
"buffer\n %var_priv"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarPrivateV14Good) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_priv");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarPrivateV13TargetV14Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_priv");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, or storage "
|
||||
"buffer\n %var_priv"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarFunctionBad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_func");
|
||||
|
||||
|
@ -6084,6 +6290,36 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
SPV_ERROR_INVALID_ID,
|
||||
"is not valid for the WebGPU execution environment."))));
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarFunctionV13Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_func");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, or storage "
|
||||
"buffer\n %var_func"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarFunctionV14Good) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_func");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarFunctionV13TargetV14Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_func");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, or storage "
|
||||
"buffer\n %var_func"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -288,9 +288,12 @@ TEST_P(ValidateExtIntoCore, DoNotAskForExtensionInLaterVersion) {
|
|||
|
||||
CompileSuccessfully(code.c_str(), GetParam().env);
|
||||
if (GetParam().success) {
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(GetParam().env));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(GetParam().env))
|
||||
<< getDiagnosticString();
|
||||
} else {
|
||||
ASSERT_NE(SPV_SUCCESS, ValidateInstructions(GetParam().env));
|
||||
ASSERT_NE(SPV_SUCCESS, ValidateInstructions(GetParam().env))
|
||||
<< " in " << spvTargetEnvDescription(GetParam().env) << ":\n"
|
||||
<< code;
|
||||
const std::string message = getDiagnosticString();
|
||||
if (spvIsVulkanEnv(GetParam().env)) {
|
||||
EXPECT_THAT(message, HasSubstr(std::string(GetParam().cap) +
|
||||
|
|
|
@ -37,6 +37,11 @@ class ValidateBase : public ::testing::Test,
|
|||
// Returns the a spv_const_binary struct
|
||||
spv_const_binary get_const_binary();
|
||||
|
||||
// Assembles the given SPIR-V text, checks that it fails to assemble,
|
||||
// and returns resulting diagnostic. No internal state is updated.
|
||||
std::string CompileFailure(std::string code,
|
||||
spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
|
||||
|
||||
// Checks that 'code' is valid SPIR-V text representation and stores the
|
||||
// binary version for further method calls.
|
||||
void CompileSuccessfully(std::string code,
|
||||
|
@ -100,17 +105,30 @@ void ValidateBase<T>::TearDown() {
|
|||
spvValidatorOptionsDestroy(options_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string ValidateBase<T>::CompileFailure(std::string code,
|
||||
spv_target_env env) {
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
EXPECT_NE(SPV_SUCCESS,
|
||||
spvTextToBinary(ScopedContext(env).context, code.c_str(),
|
||||
code.size(), &binary_, &diagnostic));
|
||||
std::string result(diagnostic->error);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ValidateBase<T>::CompileSuccessfully(std::string code,
|
||||
spv_target_env env) {
|
||||
DestroyBinary();
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
ASSERT_EQ(SPV_SUCCESS,
|
||||
spvTextToBinary(ScopedContext(env).context, code.c_str(),
|
||||
code.size(), &binary_, &diagnostic))
|
||||
ScopedContext context(env);
|
||||
auto status = spvTextToBinary(context.context, code.c_str(), code.size(),
|
||||
&binary_, &diagnostic);
|
||||
EXPECT_EQ(SPV_SUCCESS, status)
|
||||
<< "ERROR: " << diagnostic->error
|
||||
<< "\nSPIR-V could not be compiled into binary:\n"
|
||||
<< code;
|
||||
ASSERT_EQ(SPV_SUCCESS, status);
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace spvtools {
|
|||
namespace val {
|
||||
namespace {
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Not;
|
||||
|
||||
|
@ -54,9 +55,33 @@ OpCapability ImageBuffer
|
|||
ss << "OpCapability SampledRect\n";
|
||||
}
|
||||
|
||||
// In 1.4, the entry point must list all module-scope variables used. Just
|
||||
// list all of them.
|
||||
std::string interface_vars = (env != SPV_ENV_UNIVERSAL_1_4) ? "" :
|
||||
R"(
|
||||
%uniform_image_f32_1d_0001
|
||||
%uniform_image_f32_1d_0002_rgba32f
|
||||
%uniform_image_f32_2d_0001
|
||||
%uniform_image_f32_2d_0010
|
||||
%uniform_image_u32_2d_0001
|
||||
%uniform_image_u32_2d_0000
|
||||
%uniform_image_s32_3d_0001
|
||||
%uniform_image_f32_2d_0002
|
||||
%uniform_image_s32_2d_0002
|
||||
%uniform_image_f32_spd_0002
|
||||
%uniform_image_f32_3d_0111
|
||||
%uniform_image_f32_cube_0101
|
||||
%uniform_image_f32_cube_0102_rgba32f
|
||||
%uniform_sampler
|
||||
%private_image_u32_buffer_0002_r32ui
|
||||
%private_image_u32_spd_0002
|
||||
%private_image_f32_buffer_0002_r32ui
|
||||
)";
|
||||
|
||||
ss << capabilities_and_extensions;
|
||||
ss << "OpMemoryModel Logical " << memory_model << "\n";
|
||||
ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
|
||||
ss << "OpEntryPoint " << execution_model
|
||||
<< " %main \"main\" " + interface_vars + "\n";
|
||||
if (execution_model == "Fragment") {
|
||||
ss << "OpExecutionMode %main OriginUpperLeft\n";
|
||||
}
|
||||
|
@ -79,6 +104,8 @@ OpDecorate %uniform_image_s32_3d_0001 DescriptorSet 1
|
|||
OpDecorate %uniform_image_s32_3d_0001 Binding 2
|
||||
OpDecorate %uniform_image_f32_2d_0002 DescriptorSet 1
|
||||
OpDecorate %uniform_image_f32_2d_0002 Binding 3
|
||||
OpDecorate %uniform_image_s32_2d_0002 DescriptorSet 1
|
||||
OpDecorate %uniform_image_s32_2d_0002 Binding 4
|
||||
OpDecorate %uniform_image_f32_spd_0002 DescriptorSet 2
|
||||
OpDecorate %uniform_image_f32_spd_0002 Binding 0
|
||||
OpDecorate %uniform_image_f32_3d_0111 DescriptorSet 2
|
||||
|
@ -222,6 +249,11 @@ OpDecorate %uniform_sampler Binding 0
|
|||
%uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant
|
||||
%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002
|
||||
|
||||
%type_image_s32_2d_0002 = OpTypeImage %s32 2D 0 0 0 2 Unknown
|
||||
%ptr_image_s32_2d_0002 = OpTypePointer UniformConstant %type_image_s32_2d_0002
|
||||
%uniform_image_s32_2d_0002 = OpVariable %ptr_image_s32_2d_0002 UniformConstant
|
||||
%type_sampled_image_s32_2d_0002 = OpTypeSampledImage %type_image_s32_2d_0002
|
||||
|
||||
%type_image_f32_spd_0002 = OpTypeImage %f32 SubpassData 0 0 0 2 Unknown
|
||||
%ptr_image_f32_spd_0002 = OpTypePointer UniformConstant %type_image_f32_spd_0002
|
||||
%uniform_image_f32_spd_0002 = OpVariable %ptr_image_f32_spd_0002 UniformConstant
|
||||
|
@ -703,7 +735,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) {
|
|||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 136[%136] cannot be a "
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 140[%140] cannot be a "
|
||||
"type"));
|
||||
}
|
||||
|
||||
|
@ -4571,6 +4603,150 @@ TEST_F(ValidateImage, Issue2463NoSegFault) {
|
|||
"for OpReturnValue"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendV13Bad) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
|
||||
EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment",
|
||||
SPV_ENV_UNIVERSAL_1_3)),
|
||||
HasSubstr("Invalid image operand 'SignExtend'"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendV13Bad) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
|
||||
EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment",
|
||||
SPV_ENV_UNIVERSAL_1_3)),
|
||||
HasSubstr("Invalid image operand 'ZeroExtend'"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendScalarUIntTexelV14Good) {
|
||||
// Unsigned int sampled type
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendScalarSIntTexelV14Good) {
|
||||
// Signed int sampled type
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
|
||||
%res1 = OpImageRead %s32 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendScalarVectorUIntTexelV14Good) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendVectorSIntTexelV14Good) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
|
||||
%res1 = OpImageRead %s32vec4 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
// No negative tests for SignExtend since we don't truly know the
|
||||
// texel format.
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendScalarUIntTexelV14Good) {
|
||||
// Unsigned int sampled type
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendScalarSIntTexelV14Good) {
|
||||
// Zeroed int sampled type
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
|
||||
%res1 = OpImageRead %s32 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendScalarVectorUIntTexelV14Good) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14Good) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
|
||||
%res1 = OpImageRead %s32vec4 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
// No negative tests for ZeroExtend since we don't truly know the
|
||||
// texel format.
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -48,8 +48,9 @@ OpFunctionEnd
|
|||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Input variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, EntryPointMissingOutput) {
|
||||
|
@ -74,8 +75,9 @@ OpFunctionEnd
|
|||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Output variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, InterfaceMissingUseInSubfunction) {
|
||||
|
@ -105,8 +107,9 @@ OpFunctionEnd
|
|||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Input variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, TwoEntryPointsOneFunction) {
|
||||
|
@ -132,8 +135,9 @@ OpFunctionEnd
|
|||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Input variable id <2> is used by entry point 'func2' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <2> is used by entry point 'func2' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, MissingInterfaceThroughInitializer) {
|
||||
|
@ -160,8 +164,215 @@ OpFunctionEnd
|
|||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Input variable id <6> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <6> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, NonUniqueInterfacesSPV1p3) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer Input %struct
|
||||
%var = OpVariable %ptr_struct Input
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, NonUniqueInterfacesSPV1p4) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %main "main"
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer Input %struct
|
||||
%var = OpVariable %ptr_struct Input
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Non-unique OpEntryPoint interface 2[%var] is disallowed"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p3) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer StorageBuffer %struct
|
||||
%var = OpVariable %ptr_struct StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %struct %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p4) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer StorageBuffer %struct
|
||||
%var = OpVariable %ptr_struct StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %struct %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Interface variable id <2> is used by entry point "
|
||||
"'main' id <1>, but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, FunctionInterfaceVarSPV1p3) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer Function %struct
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%var = OpVariable %ptr_struct Function
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpEntryPoint interfaces must be OpVariables with "
|
||||
"Storage Class of Input(1) or Output(3). Found Storage "
|
||||
"Class 7 for Entry Point id 1."));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, FunctionInterfaceVarSPV1p4) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer Function %struct
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%var = OpVariable %ptr_struct Function
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpEntryPoint interfaces should only list global variables"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, ModuleSPV1p3ValidateSPV1p4_NotAllUsedGlobals) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer StorageBuffer %struct
|
||||
%var = OpVariable %ptr_struct StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %struct %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, ModuleSPV1p3ValidateSPV1p4_DuplicateInterface) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %gid %gid
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpDecorate %gid BuiltIn GlobalInvocationId
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int3 = OpTypeVector %int 3
|
||||
%ptr_input_int3 = OpTypePointer Input %int3
|
||||
%gid = OpVariable %ptr_input_int3 Input
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace spvtools {
|
|||
namespace val {
|
||||
namespace {
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Not;
|
||||
|
||||
|
@ -144,6 +145,18 @@ OpExecutionMode %main OriginUpperLeft
|
|||
%boolvec3_tft = OpConstantComposite %boolvec3 %true %false %true
|
||||
%boolvec4_tftf = OpConstantComposite %boolvec4 %true %false %true %false
|
||||
|
||||
%arr_u32_2 = OpTypeArray %u32 %u32_2
|
||||
%st_u32_u32 = OpTypeStruct %u32 %u32
|
||||
%mat_f32_2_2 = OpTypeMatrix %f32vec2 2
|
||||
|
||||
%nul_arr_u32_2 = OpConstantNull %arr_u32_2
|
||||
%nul_st_u32_u32 = OpConstantNull %st_u32_u32
|
||||
%nul_mat_f32_2_2 = OpConstantNull %mat_f32_2_2
|
||||
|
||||
%arr_u32_2_1_2 = OpConstantComposite %arr_u32_2 %u32_1 %u32_2
|
||||
%st_u32_u32_1_2 = OpConstantComposite %st_u32_u32 %u32_1 %u32_2
|
||||
%mat_f32_2_2_01_12 = OpConstantComposite %mat_f32_2_2 %f32vec2_01 %f32vec2_12
|
||||
|
||||
%f32vec4ptr = OpTypePointer Function %f32vec4
|
||||
|
||||
%main = OpFunction %void None %func
|
||||
|
@ -585,6 +598,20 @@ TEST_F(ValidateLogicals, OpSelectWrongTypeId) {
|
|||
HasSubstr("Expected scalar or vector type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectWrongTypeIdV14) {
|
||||
// In 1.4, the message changes to allow composites.
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %void %true %u32_0 %u32_1
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or composite type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectPointerNoCapability) {
|
||||
const std::string body = R"(
|
||||
%x = OpVariable %f32vec4ptr Function
|
||||
|
@ -687,6 +714,111 @@ TEST_F(ValidateLogicals, OpSelectWrongRightObject) {
|
|||
HasSubstr("Expected both objects to be of Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectArrayV13Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectArrayV13TargetV14Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectArrayV14Good) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectStructV13Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectStructV13TargetV14Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectStructV14Good) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectMatrixV13Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectMatrixV13TargetV14Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectMatrixV14Good) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpIEqualSuccess) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpIEqual %bool %u32_0 %s32_1
|
||||
|
@ -949,6 +1081,84 @@ OpFunctionEnd
|
|||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, SelectVectorsScalarCondition) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%int4 = OpTypeVector %int 4
|
||||
%int4_0 = OpConstantNull %int4
|
||||
%true = OpConstantTrue %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%select = OpSelect %int4 %true %int4_0 %int4_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected vector sizes of Result Type and the "
|
||||
"condition to be equal: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, SelectVectorsScalarCondition1p4) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%int4 = OpTypeVector %int 4
|
||||
%int4_0 = OpConstantNull %int4
|
||||
%true = OpConstantTrue %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%select = OpSelect %int4 %true %int4_0 %int4_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, SelectVectorsVectorConditionMismatchedDimensions1p4) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%bool3 = OpTypeVector %bool 3
|
||||
%int = OpTypeInt 32 0
|
||||
%int4 = OpTypeVector %int 4
|
||||
%int4_0 = OpConstantNull %int4
|
||||
%bool3_null = OpConstantNull %bool3
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%select = OpSelect %int4 %bool3_null %int4_0 %int4_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected vector sizes of Result Type and the "
|
||||
"condition to be equal: Select"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace {
|
|||
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Values;
|
||||
|
||||
using ValidateMemory = spvtest::ValidateBase<bool>;
|
||||
|
||||
|
@ -1342,32 +1343,6 @@ OpFunctionEnd
|
|||
"VulkanMemoryModelDeviceScopeKHR capability"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryGood1) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2 MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryGood2) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -1422,6 +1397,138 @@ OpFunctionEnd
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessAvVisBadBinaryV13) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
MakePointerVisibleKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"with two memory access operands requires SPIR-V 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessAvVisGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
MakePointerVisibleKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessFirstWithAvBad) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Source memory access must not include MakePointerAvailableKHR\n"
|
||||
" OpCopyMemory %5 %6 MakePointerAvailableKHR|NonPrivatePointerKHR"
|
||||
" %uint_1 MakePointerAvailableKHR|NonPrivatePointerKHR %uint_1"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessSecondWithVisBad) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
MakePointerVisibleKHR|NonPrivatePointerKHR %device
|
||||
MakePointerVisibleKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Target memory access must not include MakePointerVisibleKHR\n"
|
||||
" OpCopyMemory %5 %6 MakePointerVisibleKHR|NonPrivatePointerKHR"
|
||||
" %uint_1 MakePointerVisibleKHR|NonPrivatePointerKHR %uint_1"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedBad1) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -1767,8 +1874,8 @@ OpReturn
|
|||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(body.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
CompileSuccessfully(body);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("PhysicalStorageBufferEXT must not be used with OpVariable"));
|
||||
|
@ -2934,6 +3041,508 @@ OpFunctionEnd
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemoryNoAccessGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySimpleMixedAccessGood) {
|
||||
// Test one memory access operand using features that don't require the
|
||||
// Vulkan memory model.
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2 Volatile|Aligned|Nontemporal 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySimpleTwoMixedAccessV13Bad) {
|
||||
// Two memory access operands is invalid up to SPIR-V 1.3
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2 Volatile Volatile
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("CopyMemory with two memory access operands requires "
|
||||
"SPIR-V 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySimpleTwoMixedAccessV14Good) {
|
||||
// Two memory access operands is valid in SPIR-V 1.4
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2 Volatile Volatile
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySizedNoAccessGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Addresses
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_16 = OpConstant %int 16
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemorySized %var1 %var2 %int_16
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySizedSimpleMixedAccessGood) {
|
||||
// Test one memory access operand using features that don't require the
|
||||
// Vulkan memory model.
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Addresses
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_16 = OpConstant %int 16
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemorySized %var1 %var2 %int_16 Volatile|Aligned|Nontemporal 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySizedSimpleTwoMixedAccessV13Bad) {
|
||||
// Two memory access operands is invalid up to SPIR-V 1.3
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Addresses
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_16 = OpConstant %int 16
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemorySized %var1 %var2 %int_16 Volatile Volatile
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("CopyMemorySized with two memory access operands requires "
|
||||
"SPIR-V 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySizedSimpleTwoMixedAccessV14Good) {
|
||||
// Two memory access operands is valid in SPIR-V 1.4
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Addresses
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_16 = OpConstant %int 16
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemorySized %var1 %var2 %int_16 Volatile Volatile
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
using ValidatePointerComparisons = spvtest::ValidateBase<std::string>;
|
||||
|
||||
TEST_P(ValidatePointerComparisons, Good) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer StorageBuffer %int
|
||||
%var = OpVariable %ptr_int StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, GoodWorkgroup) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointers
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer Workgroup %int
|
||||
%var = OpVariable %ptr_int Workgroup
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadResultType) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer StorageBuffer %int
|
||||
%var = OpVariable %ptr_int StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %bool ";
|
||||
} else {
|
||||
spirv += " %int ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
if (operation == "OpPtrDiff") {
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Result Type must be an integer scalar"));
|
||||
} else {
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Result Type must be OpTypeBool"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadCapabilities) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer StorageBuffer %int
|
||||
%var = OpVariable %ptr_int StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
if (operation == "OpPtrDiff") {
|
||||
// Gets caught by the grammar.
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
} else {
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Instruction cannot be used without a variable "
|
||||
"pointers capability"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadOperandType) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer StorageBuffer %int
|
||||
%var = OpVariable %ptr_int StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %int %var
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%ld %ld
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Operand type must be a pointer"));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadStorageClassWorkgroup) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer Workgroup %int
|
||||
%var = OpVariable %ptr_int Workgroup
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Workgroup storage class pointer requires "
|
||||
"VariablePointers capability to be specified"));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadStorageClass) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer Private %int
|
||||
%var = OpVariable %ptr_int Private
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Invalid pointer storage class"));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadDiffOperandTypes) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer Private %int
|
||||
%var = OpVariable %ptr_int Private
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %int %var
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %ld
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("The types of Operand 1 and Operand 2 must match"));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(PointerComparisons, ValidatePointerComparisons,
|
||||
Values("OpPtrEqual", "OpPtrNotEqual", "OpPtrDiff"));
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -86,6 +86,9 @@ std::string version(spv_target_env env) {
|
|||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return "1.3";
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return "1.4";
|
||||
default:
|
||||
return "0";
|
||||
}
|
||||
|
@ -179,6 +182,7 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion,
|
|||
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true),
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true),
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true),
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true),
|
||||
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
|
||||
|
@ -190,7 +194,8 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion,
|
|||
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false)
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -41,14 +41,14 @@ Options:
|
|||
Numeric IDs in the binary will have the same values as in the
|
||||
source. Non-numeric IDs are allocated by filling in the gaps,
|
||||
starting with 1 and going up.
|
||||
--target-env {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3}
|
||||
--target-env {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3|spv1.4}
|
||||
Use Vulkan 1.0, Vulkan 1.1, SPIR-V 1.0, SPIR-V 1.1,
|
||||
SPIR-V 1.2, or SPIR-V 1.3
|
||||
SPIR-V 1.2, SPIR-V 1.3, or SPIR-V 1.4
|
||||
)",
|
||||
argv0, argv0);
|
||||
}
|
||||
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_3;
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* inFile = nullptr;
|
||||
|
|
|
@ -44,7 +44,7 @@ Options:
|
|||
argv0, argv0);
|
||||
}
|
||||
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_2;
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* inFile = nullptr;
|
||||
|
|
|
@ -60,7 +60,7 @@ Options:
|
|||
argv0, argv0);
|
||||
}
|
||||
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_3;
|
||||
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* inFile = nullptr;
|
||||
|
|
|
@ -39,8 +39,9 @@ Options:
|
|||
--allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved.
|
||||
--verify-ids Verify that IDs in the resulting modules are truly unique.
|
||||
--version Display linker version information
|
||||
--target-env {vulkan1.0|spv1.0|spv1.1|spv1.2|opencl2.1|opencl2.2}
|
||||
Use Vulkan1.0/SPIR-V1.0/SPIR-V1.1/SPIR-V1.2/OpenCL-2.1/OpenCL2.2 validation rules.
|
||||
--target-env {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3|spv1.4|opencl2.1|opencl2.2}
|
||||
Use Vulkan 1.0, Vulkan 1.1, SPIR-V 1.0, SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3,
|
||||
SPIR-V1.4, OpenCL 2.1, OpenCL 2.2 validation rules.
|
||||
)",
|
||||
argv0, argv0);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
|
||||
|
||||
std::string GetLegalizationPasses() {
|
||||
spvtools::Optimizer optimizer(kDefaultEnvironment);
|
||||
|
|
|
@ -216,7 +216,7 @@ void DumpShader(spvtools::opt::IRContext* context, const char* filename) {
|
|||
DumpShader(binary, filename);
|
||||
}
|
||||
|
||||
const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
const char* in_file = nullptr;
|
||||
|
|
|
@ -61,16 +61,18 @@ Options:
|
|||
different type with compatible layout and
|
||||
members.
|
||||
--version Display validator version information.
|
||||
--target-env {vulkan1.0|vulkan1.1|opencl2.2|spv1.0|spv1.1|spv1.2|spv1.3|webgpu0}
|
||||
Use Vulkan 1.0, Vulkan 1.1, OpenCL 2.2, SPIR-V 1.0,
|
||||
SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3 or WIP WebGPU validation rules.
|
||||
--target-env {vulkan1.0|vulkan1.1|vulkan1.1spv1.4|opencl2.2|spv1.0|spv1.1|
|
||||
spv1.2|spv1.3|spv1.4|webgpu0}
|
||||
Use Vulkan 1.0, Vulkan 1.1, Vulkan 1.1 with SPIR-V 1.4,
|
||||
OpenCL 2.2, SPIR-V 1.0, SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3,
|
||||
SPIR-V 1.4, or WIP WebGPU validation rules.
|
||||
)",
|
||||
argv0, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* inFile = nullptr;
|
||||
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_3;
|
||||
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_4;
|
||||
spvtools::ValidatorOptions options;
|
||||
bool continue_processing = true;
|
||||
int return_code = 0;
|
||||
|
@ -102,15 +104,19 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
} else if (0 == strcmp(cur_arg, "--version")) {
|
||||
printf("%s\n", spvSoftwareVersionDetailsString());
|
||||
printf("Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n",
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3),
|
||||
spvTargetEnvDescription(SPV_ENV_OPENCL_2_2),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_WEBGPU_0));
|
||||
printf(
|
||||
"Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n "
|
||||
"%s\n",
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_4),
|
||||
spvTargetEnvDescription(SPV_ENV_OPENCL_2_2),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4),
|
||||
spvTargetEnvDescription(SPV_ENV_WEBGPU_0));
|
||||
continue_processing = false;
|
||||
return_code = 0;
|
||||
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
||||
|
|
|
@ -405,11 +405,12 @@ class EnumerantInitializer(object):
|
|||
min_version=self.version)
|
||||
|
||||
|
||||
def generate_enum_operand_kind_entry(entry):
|
||||
def generate_enum_operand_kind_entry(entry, extension_map):
|
||||
"""Returns the C initializer for the given operand enum entry.
|
||||
|
||||
Arguments:
|
||||
- entry: a dict containing information about an enum entry
|
||||
- extension_map: a dict mapping enum value to list of extensions
|
||||
|
||||
Returns:
|
||||
a string containing the C initializer for spv_operand_desc_t
|
||||
|
@ -417,7 +418,10 @@ def generate_enum_operand_kind_entry(entry):
|
|||
enumerant = entry.get('enumerant')
|
||||
value = entry.get('value')
|
||||
caps = entry.get('capabilities', [])
|
||||
exts = entry.get('extensions', [])
|
||||
if value in extension_map:
|
||||
exts = extension_map[value]
|
||||
else:
|
||||
exts = []
|
||||
params = entry.get('parameters', [])
|
||||
params = [p.get('kind') for p in params]
|
||||
params = zip(params, [''] * len(params))
|
||||
|
@ -430,22 +434,41 @@ def generate_enum_operand_kind_entry(entry):
|
|||
enumerant, value, caps, exts, params, version))
|
||||
|
||||
|
||||
def generate_enum_operand_kind(enum):
|
||||
"""Returns the C definition for the given operand kind."""
|
||||
def generate_enum_operand_kind(enum, synthetic_exts_list):
|
||||
"""Returns the C definition for the given operand kind.
|
||||
Also appends to |synthetic_exts_list| a list of extension
|
||||
lists used."""
|
||||
kind = enum.get('kind')
|
||||
assert kind is not None
|
||||
|
||||
# Sort all enumerants first according to their values and then
|
||||
# their names so that the symbols with the same values are
|
||||
# grouped together.
|
||||
# Sort all enumerants according to their values, but otherwise
|
||||
# preserve their order so the first name listed in the grammar
|
||||
# as the preferred name for disassembly.
|
||||
if enum.get('category') == 'ValueEnum':
|
||||
functor = lambda k: (k['value'], k['enumerant'])
|
||||
functor = lambda k: (k['value'])
|
||||
else:
|
||||
functor = lambda k: (int(k['value'], 16), k['enumerant'])
|
||||
functor = lambda k: (int(k['value'], 16))
|
||||
entries = sorted(enum.get('enumerants', []), key=functor)
|
||||
|
||||
# SubgroupEqMask and SubgroupEqMaskKHR are the same number with
|
||||
# same semantics, but one has no extension list while the other
|
||||
# does. Both should have the extension list.
|
||||
# So create a mapping from enum value to the union of the extensions
|
||||
# across all those grammar entries. Preserve order.
|
||||
extension_map = { }
|
||||
for e in entries:
|
||||
value = e.get('value')
|
||||
extension_map[value] = []
|
||||
for e in entries:
|
||||
value = e.get('value')
|
||||
exts = e.get('extensions', [])
|
||||
for ext in exts:
|
||||
if ext not in extension_map[value]:
|
||||
extension_map[value].append(ext)
|
||||
synthetic_exts_list.extend(extension_map.values())
|
||||
|
||||
name = '{}_{}Entries'.format(PYGEN_VARIABLE_PREFIX, kind)
|
||||
entries = [' {}'.format(generate_enum_operand_kind_entry(e))
|
||||
entries = [' {}'.format(generate_enum_operand_kind_entry(e, extension_map))
|
||||
for e in entries]
|
||||
|
||||
template = ['static const spv_operand_desc_t {name}[] = {{',
|
||||
|
@ -470,9 +493,9 @@ def generate_operand_kind_table(enums):
|
|||
exts = [entry.get('extensions', [])
|
||||
for enum in enums
|
||||
for entry in enum.get('enumerants', [])]
|
||||
enums = [generate_enum_operand_kind(e, exts) for e in enums]
|
||||
exts_arrays = generate_extension_arrays(exts)
|
||||
|
||||
enums = [generate_enum_operand_kind(e) for e in enums]
|
||||
# We have three operand kinds that requires their optional counterpart to
|
||||
# exist in the operand info table.
|
||||
three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess']
|
||||
|
@ -608,6 +631,12 @@ def generate_all_string_enum_mappings(extensions, operand_kinds):
|
|||
return '\n\n'.join(tables)
|
||||
|
||||
|
||||
def precondition_operand_kinds(operand_kinds):
|
||||
"""For operand kinds that have the same number, make sure they all have
|
||||
the same extension list"""
|
||||
return operand_kinds
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description='Generate SPIR-V info tables')
|
||||
|
@ -701,6 +730,7 @@ def main():
|
|||
operand_kinds.extend(core_grammar['operand_kinds'])
|
||||
operand_kinds.extend(debuginfo_grammar['operand_kinds'])
|
||||
extensions = get_extension_list(instructions, operand_kinds)
|
||||
operand_kinds = precondition_operand_kinds(operand_kinds)
|
||||
if args.core_insts_output is not None:
|
||||
make_path_to_file(args.core_insts_output)
|
||||
make_path_to_file(args.operand_kinds_output)
|
||||
|
|
Загрузка…
Ссылка в новой задаче