diff --git a/source/opcode.cpp b/source/opcode.cpp index 9e1791ac7..f03da6cf1 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -108,7 +108,7 @@ spv_result_t spvOpcodeTableNameLookup(spv_target_env env, // is indeed requested in the SPIR-V code; checking that should be // validator's work. if ((spvVersionForTargetEnv(env) >= entry.minVersion || - entry.numExtensions > 0u) && + entry.numExtensions > 0u || entry.numCapabilities > 0u) && nameLength == strlen(entry.name) && !strncmp(name, entry.name, nameLength)) { // NOTE: Found out Opcode! @@ -153,7 +153,7 @@ spv_result_t spvOpcodeTableValueLookup(spv_target_env env, // is indeed requested in the SPIR-V code; checking that should be // validator's work. if (spvVersionForTargetEnv(env) >= it->minVersion || - it->numExtensions > 0u) { + it->numExtensions > 0u || it->numCapabilities > 0u) { *pEntry = it; return SPV_SUCCESS; } diff --git a/source/validate_instruction.cpp b/source/validate_instruction.cpp index 92cb8cc15..4da82dd98 100644 --- a/source/validate_instruction.cpp +++ b/source/validate_instruction.cpp @@ -187,6 +187,30 @@ ExtensionSet RequiredExtensions(const ValidationState_t& state, namespace libspirv { +// Return SPV_ERROR_INVALID_BINARY and emit a diagnostic if the instruction +// is explicitly reserved in the SPIR-V core spec. Otherwise return +// SPV_SUCCESS. +spv_result_t ReservedCheck(ValidationState_t& _, + const spv_parsed_instruction_t* inst) { + const SpvOp opcode = static_cast(inst->opcode); + switch (opcode) { + // These instructions are enabled by a capability, but should never + // be used anyway. + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: { + spv_opcode_desc inst_desc; + _.grammar().lookupOpcode(opcode, &inst_desc); + return _.diag(SPV_ERROR_INVALID_BINARY) + << "Invalid Opcode name 'Op" << inst_desc->name << "'"; + } + default: + break; + } + return SPV_SUCCESS; +} + spv_result_t CapabilityCheck(ValidationState_t& _, const spv_parsed_instruction_t* inst) { const SpvOp opcode = static_cast(inst->opcode); @@ -246,6 +270,8 @@ spv_result_t ExtensionCheck(ValidationState_t& _, } // Checks that the instruction can be used in this target environment. +// Assumes that CapabilityCheck has checked direct capability dependencies +// for the opcode. spv_result_t VersionCheck(ValidationState_t& _, const spv_parsed_instruction_t* inst) { const auto opcode = static_cast(inst->opcode); @@ -256,6 +282,12 @@ spv_result_t VersionCheck(ValidationState_t& _, const auto min_version = inst_desc->minVersion; + if (inst_desc->numCapabilities > 0u) { + // We already checked that the direct capability dependency has been + // satisfied. We don't need to check any further. + return SPV_SUCCESS; + } + ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions); if (exts.IsEmpty()) { // If no extensions can enable this instruction, then emit error messages @@ -549,6 +581,7 @@ spv_result_t InstructionPass(ValidationState_t& _, RegisterDecorations(_, inst); if (auto error = ExtensionCheck(_, inst)) return error; + if (auto error = ReservedCheck(_, inst)) return error; if (auto error = CapabilityCheck(_, inst)) return error; if (auto error = LimitCheckIdBound(_, inst)) return error; if (auto error = LimitCheckStruct(_, inst)) return error; diff --git a/test/cpp_interface_test.cpp b/test/cpp_interface_test.cpp index 2bab430b2..a035e377d 100644 --- a/test/cpp_interface_test.cpp +++ b/test/cpp_interface_test.cpp @@ -87,28 +87,6 @@ TEST(CppInterface, AssembleOverloads) { } } -TEST(CppInterface, AssembleWithWrongTargetEnv) { - const std::string input_text = "%r = OpSizeOf %type %pointer"; - SpirvTools t(SPV_ENV_UNIVERSAL_1_0); - int invocation_count = 0; - t.SetMessageConsumer( - [&invocation_count](spv_message_level_t level, const char* source, - const spv_position_t& position, const char* message) { - ++invocation_count; - EXPECT_EQ(SPV_MSG_ERROR, level); - EXPECT_STREQ("input", source); - EXPECT_EQ(0u, position.line); - EXPECT_EQ(5u, position.column); - EXPECT_EQ(5u, position.index); - EXPECT_STREQ("Invalid Opcode name 'OpSizeOf'", message); - }); - - std::vector binary = {42, 42}; - EXPECT_FALSE(t.Assemble(input_text, &binary)); - EXPECT_THAT(binary, ContainerEq(std::vector{42, 42})); - EXPECT_EQ(1, invocation_count); -} - TEST(CppInterface, DisassembleEmptyModule) { std::string text(10, 'x'); SpirvTools t(SPV_ENV_UNIVERSAL_1_1); @@ -148,31 +126,6 @@ TEST(CppInterface, DisassembleOverloads) { } } -TEST(CppInterface, DisassembleWithWrongTargetEnv) { - const std::string input_text = "%r = OpSizeOf %type %pointer"; - SpirvTools t11(SPV_ENV_UNIVERSAL_1_1); - SpirvTools t10(SPV_ENV_UNIVERSAL_1_0); - int invocation_count = 0; - t10.SetMessageConsumer( - [&invocation_count](spv_message_level_t level, const char* source, - const spv_position_t& position, const char* message) { - ++invocation_count; - EXPECT_EQ(SPV_MSG_ERROR, level); - EXPECT_STREQ("input", source); - EXPECT_EQ(0u, position.line); - EXPECT_EQ(0u, position.column); - EXPECT_EQ(5u, position.index); - EXPECT_STREQ("Invalid opcode: 321", message); - }); - - std::vector binary; - EXPECT_TRUE(t11.Assemble(input_text, &binary)); - - std::string output_text(10, 'x'); - EXPECT_FALSE(t10.Disassemble(binary, &output_text)); - EXPECT_EQ("xxxxxxxxxx", output_text); // The original string is unmodified. -} - TEST(CppInterface, SuccessfulValidation) { const std::string input_text = R"( OpCapability Shader diff --git a/test/text_to_binary.barrier_test.cpp b/test/text_to_binary.barrier_test.cpp index 743104298..9caf929c2 100644 --- a/test/text_to_binary.barrier_test.cpp +++ b/test/text_to_binary.barrier_test.cpp @@ -78,10 +78,14 @@ TEST_F(OpMemoryBarrier, BadInvalidMemorySemanticsId) { using NamedMemoryBarrierTest = spvtest::TextToBinaryTest; -TEST_F(NamedMemoryBarrierTest, OpcodeUnrecognizedInV10) { - EXPECT_THAT(CompileFailure("OpMemoryNamedBarrier %bar %scope %semantics", - SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpMemoryNamedBarrier'")); +// OpMemoryNamedBarrier is not in 1.0, but it is enabled by a capability. +// We should be able to assemble it. Validation checks are in another test +// file. +TEST_F(NamedMemoryBarrierTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics", + SPV_ENV_UNIVERSAL_1_0), + ElementsAre(spvOpcodeMake(4, SpvOpMemoryNamedBarrier), _, _, _)); } TEST_F(NamedMemoryBarrierTest, ArgumentCount) { @@ -114,9 +118,10 @@ TEST_F(NamedMemoryBarrierTest, ArgumentTypes) { using TypeNamedBarrierTest = spvtest::TextToBinaryTest; -TEST_F(TypeNamedBarrierTest, OpcodeUnrecognizedInV10) { - EXPECT_THAT(CompileFailure("%t = OpTypeNamedBarrier", SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpTypeNamedBarrier'")); +TEST_F(TypeNamedBarrierTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%t = OpTypeNamedBarrier", SPV_ENV_UNIVERSAL_1_0), + ElementsAre(spvOpcodeMake(2, SpvOpTypeNamedBarrier), _)); } TEST_F(TypeNamedBarrierTest, ArgumentCount) { @@ -134,10 +139,11 @@ TEST_F(TypeNamedBarrierTest, ArgumentCount) { using NamedBarrierInitializeTest = spvtest::TextToBinaryTest; -TEST_F(NamedBarrierInitializeTest, OpcodeUnrecognizedInV10) { - EXPECT_THAT(CompileFailure("%bar = OpNamedBarrierInitialize %type %count", - SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpNamedBarrierInitialize'")); +TEST_F(NamedBarrierInitializeTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count", + SPV_ENV_UNIVERSAL_1_0), + ElementsAre(spvOpcodeMake(4, SpvOpNamedBarrierInitialize), _, _, _)); } TEST_F(NamedBarrierInitializeTest, ArgumentCount) { diff --git a/test/text_to_binary.pipe_storage_test.cpp b/test/text_to_binary.pipe_storage_test.cpp index 97dc286f5..29e6689be 100644 --- a/test/text_to_binary.pipe_storage_test.cpp +++ b/test/text_to_binary.pipe_storage_test.cpp @@ -22,9 +22,12 @@ using ::testing::Eq; using OpTypePipeStorageTest = spvtest::TextToBinaryTest; -TEST_F(OpTypePipeStorageTest, OpcodeUnrecognizedInV10) { - EXPECT_THAT(CompileFailure("%res = OpTypePipeStorage", SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpTypePipeStorage'")); +// It can assemble, but should not validate. Validation checks for version +// and capability are in another test file. +TEST_F(OpTypePipeStorageTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%res = OpTypePipeStorage", SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpTypePipeStorage, {1}))); } TEST_F(OpTypePipeStorageTest, ArgumentCount) { @@ -42,10 +45,10 @@ TEST_F(OpTypePipeStorageTest, ArgumentCount) { using OpConstantPipeStorageTest = spvtest::TextToBinaryTest; -TEST_F(OpConstantPipeStorageTest, OpcodeUnrecognizedInV10) { - EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4 5", - SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpConstantPipeStorage'")); +TEST_F(OpConstantPipeStorageTest, OpcodeAssemblesInV10) { + EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5", + SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5}))); } TEST_F(OpConstantPipeStorageTest, ArgumentCount) { @@ -84,10 +87,10 @@ TEST_F(OpConstantPipeStorageTest, ArgumentTypes) { using OpCreatePipeFromPipeStorageTest = spvtest::TextToBinaryTest; -TEST_F(OpCreatePipeFromPipeStorageTest, OpcodeUnrecognizedInV10) { - EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 %3", - SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpCreatePipeFromPipeStorage'")); +TEST_F(OpCreatePipeFromPipeStorageTest, OpcodeAssemblesInV10) { + EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3", + SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3}))); } TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentCount) { diff --git a/test/text_to_binary.reserved_sampling_test.cpp b/test/text_to_binary.reserved_sampling_test.cpp index db9a53dc6..5e0160673 100644 --- a/test/text_to_binary.reserved_sampling_test.cpp +++ b/test/text_to_binary.reserved_sampling_test.cpp @@ -21,37 +21,40 @@ namespace { +using ::spvtest::MakeInstruction; using ::testing::Eq; -using ReservedSamplingInstTest = spvtest::TextToBinaryTest; +using ReservedSamplingInstTest = RoundTripTest; TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjImplicitLod) { - const std::string input = "OpImageSparseSampleProjImplicitLod %1 %2 %3\n"; - EXPECT_THAT(CompileFailure(input), - Eq("Invalid Opcode name 'OpImageSparseSampleProjImplicitLod'")); + std::string input = "%2 = OpImageSparseSampleProjImplicitLod %1 %3 %4\n"; + EXPECT_THAT( + CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpImageSparseSampleProjImplicitLod, {1, 2, 3, 4}))); } TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjExplicitLod) { - const std::string input = - "OpImageSparseSampleProjExplicitLod %1 %2 %3 Lod %4\n"; - EXPECT_THAT(CompileFailure(input), - Eq("Invalid Opcode name 'OpImageSparseSampleProjExplicitLod'")); + std::string input = + "%2 = OpImageSparseSampleProjExplicitLod %1 %3 %4 Lod %5\n"; + EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpImageSparseSampleProjExplicitLod, + {1, 2, 3, 4, SpvImageOperandsLodMask, 5}))); } TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefImplicitLod) { - const std::string input = - "OpImageSparseSampleProjDrefImplicitLod %1 %2 %3 %4\n"; - EXPECT_THAT( - CompileFailure(input), - Eq("Invalid Opcode name 'OpImageSparseSampleProjDrefImplicitLod'")); + std::string input = + "%2 = OpImageSparseSampleProjDrefImplicitLod %1 %3 %4 %5\n"; + EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpImageSparseSampleProjDrefImplicitLod, + {1, 2, 3, 4, 5}))); } TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefExplicitLod) { - const std::string input = - "OpImageSparseSampleProjDrefExplicitLod %1 %2 %3 %4 Lod %5\n"; - EXPECT_THAT( - CompileFailure(input), - Eq("Invalid Opcode name 'OpImageSparseSampleProjDrefExplicitLod'")); + std::string input = + "%2 = OpImageSparseSampleProjDrefExplicitLod %1 %3 %4 %5 Lod %6\n"; + EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpImageSparseSampleProjDrefExplicitLod, + {1, 2, 3, 4, 5, SpvImageOperandsLodMask, 6}))); } } // namespace diff --git a/test/text_to_binary.subgroup_dispatch_test.cpp b/test/text_to_binary.subgroup_dispatch_test.cpp index 8cc8896ff..385509d5a 100644 --- a/test/text_to_binary.subgroup_dispatch_test.cpp +++ b/test/text_to_binary.subgroup_dispatch_test.cpp @@ -28,12 +28,15 @@ using ::testing::Eq; using OpGetKernelLocalSizeForSubgroupCountTest = spvtest::TextToBinaryTest; -TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, OpcodeUnrecognizedInV10) { +// We should be able to assemble it. Validation checks are in another test +// file. +TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, OpcodeAssemblesInV10) { EXPECT_THAT( - CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount %type " - "%sgcount %invoke %param %param_size %param_align", - SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpGetKernelLocalSizeForSubgroupCount'")); + CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type " + "%sgcount %invoke %param %param_size %param_align", + SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpGetKernelLocalSizeForSubgroupCount, + {1, 2, 3, 4, 5, 6, 7}))); } TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, ArgumentCount) { @@ -75,11 +78,12 @@ TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, ArgumentTypes) { using OpGetKernelMaxNumSubgroupsTest = spvtest::TextToBinaryTest; -TEST_F(OpGetKernelMaxNumSubgroupsTest, OpcodeUnrecognizedInV10) { - EXPECT_THAT(CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount " - "%type %invoke %param %param_size %param_align", - SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpGetKernelLocalSizeForSubgroupCount'")); +TEST_F(OpGetKernelMaxNumSubgroupsTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type " + "%invoke %param %param_size %param_align", + SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpGetKernelMaxNumSubgroups, {1, 2, 3, 4, 5, 6}))); } TEST_F(OpGetKernelMaxNumSubgroupsTest, ArgumentCount) { diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp index be8d6916e..d5bdea2f6 100644 --- a/test/text_to_binary.type_declaration_test.cpp +++ b/test/text_to_binary.type_declaration_test.cpp @@ -235,9 +235,12 @@ TEST_F(OpTypeForwardPointerTest, WrongClass) { using OpSizeOfTest = spvtest::TextToBinaryTest; -TEST_F(OpSizeOfTest, OpcodeUnrecognizedInV10) { - EXPECT_THAT(CompileFailure("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_0), - Eq("Invalid Opcode name 'OpSizeOf'")); +// We should be able to assemble it. Validation checks are in another test +// file. +TEST_F(OpSizeOfTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3}))); } TEST_F(OpSizeOfTest, ArgumentCount) { diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index 1ff5edf10..f6297b94a 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt @@ -33,6 +33,7 @@ add_spvtools_unittest(TARGET val_abcde val_data_test.cpp val_decoration_test.cpp val_derivatives_test.cpp + val_explicit_reserved_test.cpp val_extensions_test.cpp val_ext_inst_test.cpp ${VAL_TEST_COMMON_SRCS} diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index 17a79c54a..53d685dbe 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -1965,6 +1965,10 @@ OpMemoryModel Physical64 OpenCL "Embedded Profile")); } +// Three tests to check enablement of an enum (a decoration) which is not +// in core, and is directly enabled by a capability, but not directly enabled +// by an extension. See https://github.com/KhronosGroup/SPIRV-Tools/issues/1596 + TEST_F(ValidateCapability, DecorationFromExtensionMissingEnabledByCapability) { // Decoration ViewportRelativeNV is enabled by ShaderViewportMaskNV, which in // turn is enabled by SPV_NV_viewport_array2. @@ -2017,4 +2021,92 @@ OpDecorate %void ViewportRelativeNV << getDiagnosticString(); } +// Three tests to check enablement of an instruction which is not in core, and +// is directly enabled by a capability, but not directly enabled by an +// extension. See https://github.com/KhronosGroup/SPIRV-Tools/issues/1624 +// Instruction OpSubgroupShuffleINTEL is enabled by SubgroupShuffleINTEL, which +// in turn is enabled by SPV_INTEL_subgroups. + +TEST_F(ValidateCapability, InstructionFromExtensionMissingEnabledByCapability) { + // Decoration ViewportRelativeNV is enabled by ShaderViewportMaskNV, which in + // turn is enabled by SPV_NV_viewport_array2. + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +; OpCapability SubgroupShuffleINTEL +OpExtension "SPV_INTEL_subgroups" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%voidfn = OpTypeFunction %void +%zero = OpConstant %uint 0 +%main = OpFunction %void None %voidfn +%entry = OpLabel +%foo = OpSubgroupShuffleINTEL %uint %zero %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode SubgroupShuffleINTEL requires one of these " + "capabilities: SubgroupShuffleINTEL")); +} + +TEST_F(ValidateCapability, + InstructionEnablingCapabilityEnabledByMissingExtension) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability SubgroupShuffleINTEL +; OpExtension "SPV_INTEL_subgroups" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%voidfn = OpTypeFunction %void +%zero = OpConstant %uint 0 +%main = OpFunction %void None %voidfn +%entry = OpLabel +%foo = OpSubgroupShuffleINTEL %uint %zero %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + 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")); +} + +TEST_F(ValidateCapability, + InstructionEnabledByCapabilityEnabledByPresentExtension) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability SubgroupShuffleINTEL +OpExtension "SPV_INTEL_subgroups" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%voidfn = OpTypeFunction %void +%zero = OpConstant %uint 0 +%main = OpFunction %void None %voidfn +%entry = OpLabel +%foo = OpSubgroupShuffleINTEL %uint %zero %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)) + << getDiagnosticString(); +} + } // namespace diff --git a/test/val/val_explicit_reserved_test.cpp b/test/val/val_explicit_reserved_test.cpp new file mode 100644 index 000000000..674335896 --- /dev/null +++ b/test/val/val_explicit_reserved_test.cpp @@ -0,0 +1,117 @@ +// 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. + +// Validation tests for illegal instructions + +#include "unit_spirv.h" + +#include "gmock/gmock.h" +#include "val_fixtures.h" + +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; + +using ReservedSamplingInstTest = spvtest::ValidateBase; + +// Generate a shader for use with validation tests for sparse sampling +// instructions. +std::string ShaderAssembly(const std::string& instruction_under_test) { + std::ostringstream os; + os << R"( OpCapability Shader + OpCapability SparseResidency + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %float_0 = OpConstant %float 0 + %8 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %9 = OpTypeImage %float 2D 0 0 0 1 Unknown + %10 = OpTypeSampledImage %9 +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %2 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %v2float = OpTypeVector %float 2 + %13 = OpConstantComposite %v2float %float_0 %float_0 + %int = OpTypeInt 32 1 + %_struct_15 = OpTypeStruct %int %v4float + %1 = OpFunction %void None %4 + %16 = OpLabel + %17 = OpLoad %10 %2 +)" << instruction_under_test + << R"( + OpReturn + OpFunctionEnd +)"; + + return os.str(); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjImplicitLod) { + const std::string input = ShaderAssembly( + "%result = OpImageSparseSampleProjImplicitLod %_struct_15 %17 %13"); + CompileSuccessfully(input); + + EXPECT_THAT(ValidateInstructions(), Eq(SPV_ERROR_INVALID_BINARY)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid Opcode name 'OpImageSparseSampleProjImplicitLod'")); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjExplicitLod) { + const std::string input = ShaderAssembly( + "%result = OpImageSparseSampleProjExplicitLod %_struct_15 %17 %13 Lod " + "%float_0\n"); + CompileSuccessfully(input); + + EXPECT_THAT(ValidateInstructions(), Eq(SPV_ERROR_INVALID_BINARY)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid Opcode name 'OpImageSparseSampleProjExplicitLod'")); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefImplicitLod) { + const std::string input = ShaderAssembly( + "%result = OpImageSparseSampleProjDrefImplicitLod %_struct_15 %17 %13 " + "%float_0\n"); + CompileSuccessfully(input); + + EXPECT_THAT(ValidateInstructions(), Eq(SPV_ERROR_INVALID_BINARY)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Invalid Opcode name 'OpImageSparseSampleProjDrefImplicitLod'")); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefExplicitLod) { + const std::string input = ShaderAssembly( + "%result = OpImageSparseSampleProjDrefExplicitLod %_struct_15 %17 %13 " + "%float_0 Lod " + "%float_0\n"); + CompileSuccessfully(input); + + EXPECT_THAT(ValidateInstructions(), Eq(SPV_ERROR_INVALID_BINARY)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Invalid Opcode name 'OpImageSparseSampleProjDrefExplicitLod'")); +} + +} // namespace