Home | History | Annotate | Download | only in val
      1 // Copyright (c) 2017 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // Tests for OpExtension validator rules.
     16 
     17 #include <string>
     18 #include <vector>
     19 
     20 #include "gmock/gmock.h"
     21 #include "source/enum_string_mapping.h"
     22 #include "source/extensions.h"
     23 #include "source/spirv_target_env.h"
     24 #include "test/test_fixture.h"
     25 #include "test/unit_spirv.h"
     26 #include "test/val/val_fixtures.h"
     27 
     28 namespace spvtools {
     29 namespace val {
     30 namespace {
     31 
     32 using ::testing::HasSubstr;
     33 using ::testing::Not;
     34 using ::testing::Values;
     35 using ::testing::ValuesIn;
     36 
     37 using ValidateKnownExtensions = spvtest::ValidateBase<std::string>;
     38 using ValidateUnknownExtensions = spvtest::ValidateBase<std::string>;
     39 using ValidateExtensionCapabilities = spvtest::ValidateBase<bool>;
     40 
     41 // Returns expected error string if |extension| is not recognized.
     42 std::string GetErrorString(const std::string& extension) {
     43   return "Found unrecognized extension " + extension;
     44 }
     45 
     46 INSTANTIATE_TEST_CASE_P(
     47     ExpectSuccess, ValidateKnownExtensions,
     48     Values(
     49         // Match the order as published on the SPIR-V Registry.
     50         "SPV_AMD_shader_explicit_vertex_parameter",
     51         "SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
     52         "SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
     53         "SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
     54         "SPV_KHR_subgroup_vote", "SPV_KHR_16bit_storage",
     55         "SPV_KHR_device_group", "SPV_KHR_multiview",
     56         "SPV_NVX_multiview_per_view_attributes", "SPV_NV_viewport_array2",
     57         "SPV_NV_stereo_view_rendering", "SPV_NV_sample_mask_override_coverage",
     58         "SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
     59         "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_variable_pointers",
     60         "SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
     61         "SPV_KHR_shader_atomic_counter_ops", "SPV_EXT_shader_stencil_export",
     62         "SPV_EXT_shader_viewport_index_layer",
     63         "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask",
     64         "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1",
     65         "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing"));
     66 
     67 INSTANTIATE_TEST_CASE_P(FailSilently, ValidateUnknownExtensions,
     68                         Values("ERROR_unknown_extension", "SPV_KHR_",
     69                                "SPV_KHR_shader_ballot_ERROR"));
     70 
     71 TEST_P(ValidateKnownExtensions, ExpectSuccess) {
     72   const std::string extension = GetParam();
     73   const std::string str =
     74       "OpCapability Shader\nOpCapability Linkage\nOpExtension \"" + extension +
     75       "\"\nOpMemoryModel Logical GLSL450";
     76   CompileSuccessfully(str.c_str());
     77   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
     78   EXPECT_THAT(getDiagnosticString(), Not(HasSubstr(GetErrorString(extension))));
     79 }
     80 
     81 TEST_P(ValidateUnknownExtensions, FailSilently) {
     82   const std::string extension = GetParam();
     83   const std::string str =
     84       "OpCapability Shader\nOpCapability Linkage\nOpExtension \"" + extension +
     85       "\"\nOpMemoryModel Logical GLSL450";
     86   CompileSuccessfully(str.c_str());
     87   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
     88   EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(extension)));
     89 }
     90 
     91 TEST_F(ValidateUnknownExtensions, HitMaxNumOfWarnings) {
     92   const std::string str =
     93       std::string("OpCapability Shader\n") + "OpCapability Linkage\n" +
     94       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
     95       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
     96       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
     97       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
     98       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
     99       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
    100       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
    101       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
    102       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
    103       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
    104       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
    105       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
    106       "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" +
    107       "OpMemoryModel Logical GLSL450";
    108   CompileSuccessfully(str.c_str());
    109   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
    110   EXPECT_THAT(getDiagnosticString(),
    111               HasSubstr("Other warnings have been suppressed."));
    112 }
    113 
    114 TEST_F(ValidateExtensionCapabilities, DeclCapabilitySuccess) {
    115   const std::string str =
    116       "OpCapability Shader\nOpCapability Linkage\nOpCapability DeviceGroup\n"
    117       "OpExtension \"SPV_KHR_device_group\""
    118       "\nOpMemoryModel Logical GLSL450";
    119   CompileSuccessfully(str.c_str());
    120   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
    121 }
    122 
    123 TEST_F(ValidateExtensionCapabilities, DeclCapabilityFailure) {
    124   const std::string str =
    125       "OpCapability Shader\nOpCapability Linkage\nOpCapability DeviceGroup\n"
    126       "\nOpMemoryModel Logical GLSL450";
    127   CompileSuccessfully(str.c_str());
    128   ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions());
    129   EXPECT_THAT(getDiagnosticString(), HasSubstr("1st operand of Capability"));
    130   EXPECT_THAT(getDiagnosticString(),
    131               HasSubstr("requires one of these extensions"));
    132   EXPECT_THAT(getDiagnosticString(), HasSubstr("SPV_KHR_device_group"));
    133 }
    134 
    135 using ValidateAMDShaderBallotCapabilities = spvtest::ValidateBase<std::string>;
    136 
    137 // Returns a vector of strings for the prefix of a SPIR-V assembly shader
    138 // that can use the group instructions introduced by SPV_AMD_shader_ballot.
    139 std::vector<std::string> ShaderPartsForAMDShaderBallot() {
    140   return std::vector<std::string>{R"(
    141   OpCapability Shader
    142   OpCapability Linkage
    143   )",
    144                                   R"(
    145   OpMemoryModel Logical GLSL450
    146   %float = OpTypeFloat 32
    147   %uint = OpTypeInt 32 0
    148   %int = OpTypeInt 32 1
    149   %scope = OpConstant %uint 3
    150   %uint_const = OpConstant %uint 42
    151   %int_const = OpConstant %uint 45
    152   %float_const = OpConstant %float 3.5
    153 
    154   %void = OpTypeVoid
    155   %fn_ty = OpTypeFunction %void
    156   %fn = OpFunction %void None %fn_ty
    157   %entry = OpLabel
    158   )"};
    159 }
    160 
    161 // Returns a list of SPIR-V assembly strings, where each uses only types
    162 // and IDs that can fit with a shader made from parts from the result
    163 // of ShaderPartsForAMDShaderBallot.
    164 std::vector<std::string> AMDShaderBallotGroupInstructions() {
    165   return std::vector<std::string>{
    166       "%iadd_reduce = OpGroupIAddNonUniformAMD %uint %scope Reduce %uint_const",
    167       "%iadd_iscan = OpGroupIAddNonUniformAMD %uint %scope InclusiveScan "
    168       "%uint_const",
    169       "%iadd_escan = OpGroupIAddNonUniformAMD %uint %scope ExclusiveScan "
    170       "%uint_const",
    171 
    172       "%fadd_reduce = OpGroupFAddNonUniformAMD %float %scope Reduce "
    173       "%float_const",
    174       "%fadd_iscan = OpGroupFAddNonUniformAMD %float %scope InclusiveScan "
    175       "%float_const",
    176       "%fadd_escan = OpGroupFAddNonUniformAMD %float %scope ExclusiveScan "
    177       "%float_const",
    178 
    179       "%fmin_reduce = OpGroupFMinNonUniformAMD %float %scope Reduce "
    180       "%float_const",
    181       "%fmin_iscan = OpGroupFMinNonUniformAMD %float %scope InclusiveScan "
    182       "%float_const",
    183       "%fmin_escan = OpGroupFMinNonUniformAMD %float %scope ExclusiveScan "
    184       "%float_const",
    185 
    186       "%umin_reduce = OpGroupUMinNonUniformAMD %uint %scope Reduce %uint_const",
    187       "%umin_iscan = OpGroupUMinNonUniformAMD %uint %scope InclusiveScan "
    188       "%uint_const",
    189       "%umin_escan = OpGroupUMinNonUniformAMD %uint %scope ExclusiveScan "
    190       "%uint_const",
    191 
    192       "%smin_reduce = OpGroupUMinNonUniformAMD %int %scope Reduce %int_const",
    193       "%smin_iscan = OpGroupUMinNonUniformAMD %int %scope InclusiveScan "
    194       "%int_const",
    195       "%smin_escan = OpGroupUMinNonUniformAMD %int %scope ExclusiveScan "
    196       "%int_const",
    197 
    198       "%fmax_reduce = OpGroupFMaxNonUniformAMD %float %scope Reduce "
    199       "%float_const",
    200       "%fmax_iscan = OpGroupFMaxNonUniformAMD %float %scope InclusiveScan "
    201       "%float_const",
    202       "%fmax_escan = OpGroupFMaxNonUniformAMD %float %scope ExclusiveScan "
    203       "%float_const",
    204 
    205       "%umax_reduce = OpGroupUMaxNonUniformAMD %uint %scope Reduce %uint_const",
    206       "%umax_iscan = OpGroupUMaxNonUniformAMD %uint %scope InclusiveScan "
    207       "%uint_const",
    208       "%umax_escan = OpGroupUMaxNonUniformAMD %uint %scope ExclusiveScan "
    209       "%uint_const",
    210 
    211       "%smax_reduce = OpGroupUMaxNonUniformAMD %int %scope Reduce %int_const",
    212       "%smax_iscan = OpGroupUMaxNonUniformAMD %int %scope InclusiveScan "
    213       "%int_const",
    214       "%smax_escan = OpGroupUMaxNonUniformAMD %int %scope ExclusiveScan "
    215       "%int_const"};
    216 }
    217 
    218 TEST_P(ValidateAMDShaderBallotCapabilities, ExpectSuccess) {
    219   // Succeed because the module specifies the SPV_AMD_shader_ballot extension.
    220   auto parts = ShaderPartsForAMDShaderBallot();
    221 
    222   const std::string assembly =
    223       parts[0] + "OpExtension \"SPV_AMD_shader_ballot\"\n" + parts[1] +
    224       GetParam() + "\nOpReturn OpFunctionEnd";
    225 
    226   CompileSuccessfully(assembly.c_str());
    227   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
    228 }
    229 
    230 INSTANTIATE_TEST_CASE_P(ExpectSuccess, ValidateAMDShaderBallotCapabilities,
    231                         ValuesIn(AMDShaderBallotGroupInstructions()));
    232 
    233 TEST_P(ValidateAMDShaderBallotCapabilities, ExpectFailure) {
    234   // Fail because the module does not specify the SPV_AMD_shader_ballot
    235   // extension.
    236   auto parts = ShaderPartsForAMDShaderBallot();
    237 
    238   const std::string assembly =
    239       parts[0] + parts[1] + GetParam() + "\nOpReturn OpFunctionEnd";
    240 
    241   CompileSuccessfully(assembly.c_str());
    242   EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
    243 
    244   // Make sure we get an appropriate error message.
    245   // Find just the opcode name, skipping over the "Op" part.
    246   auto prefix_with_opcode = GetParam().substr(GetParam().find("Group"));
    247   auto opcode = prefix_with_opcode.substr(0, prefix_with_opcode.find(' '));
    248   EXPECT_THAT(
    249       getDiagnosticString(),
    250       HasSubstr(std::string("Opcode " + opcode +
    251                             " requires one of these capabilities: Groups")));
    252 }
    253 
    254 INSTANTIATE_TEST_CASE_P(ExpectFailure, ValidateAMDShaderBallotCapabilities,
    255                         ValuesIn(AMDShaderBallotGroupInstructions()));
    256 
    257 struct ExtIntoCoreCase {
    258   const char* ext;
    259   const char* cap;
    260   const char* builtin;
    261   spv_target_env env;
    262   bool success;
    263 };
    264 
    265 using ValidateExtIntoCore = spvtest::ValidateBase<ExtIntoCoreCase>;
    266 
    267 // Make sure that we don't panic about missing extensions for using
    268 // functionalities that introduced in extensions but became core SPIR-V later.
    269 
    270 TEST_P(ValidateExtIntoCore, DoNotAskForExtensionInLaterVersion) {
    271   const std::string code = std::string(R"(
    272                OpCapability Shader
    273                OpCapability )") +
    274                            GetParam().cap + R"(
    275                OpMemoryModel Logical GLSL450
    276                OpEntryPoint Vertex %main "main" %builtin
    277                OpDecorate %builtin BuiltIn )" + GetParam().builtin + R"(
    278        %void = OpTypeVoid
    279           %3 = OpTypeFunction %void
    280         %int = OpTypeInt 32 1
    281 %_ptr_Input_int = OpTypePointer Input %int
    282     %builtin = OpVariable %_ptr_Input_int Input
    283        %main = OpFunction %void None %3
    284           %5 = OpLabel
    285          %18 = OpLoad %int %builtin
    286                OpReturn
    287                OpFunctionEnd)";
    288 
    289   CompileSuccessfully(code.c_str(), GetParam().env);
    290   if (GetParam().success) {
    291     ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(GetParam().env));
    292   } else {
    293     ASSERT_NE(SPV_SUCCESS, ValidateInstructions(GetParam().env));
    294     const std::string message = getDiagnosticString();
    295     if (spvIsVulkanEnv(GetParam().env)) {
    296       EXPECT_THAT(message, HasSubstr(std::string(GetParam().cap) +
    297                                      " is not allowed by Vulkan"));
    298       EXPECT_THAT(message, HasSubstr(std::string("or requires extension")));
    299     } else {
    300       EXPECT_THAT(message,
    301                   HasSubstr(std::string("requires one of these extensions: ") +
    302                             GetParam().ext));
    303     }
    304   }
    305 }
    306 
    307 // clang-format off
    308 INSTANTIATE_TEST_CASE_P(
    309     KHR_extensions, ValidateExtIntoCore,
    310     ValuesIn(std::vector<ExtIntoCoreCase>{
    311         // SPV_KHR_shader_draw_parameters became core SPIR-V 1.3
    312         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_UNIVERSAL_1_3, true},
    313         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_UNIVERSAL_1_2, false},
    314         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_UNIVERSAL_1_1, false},
    315         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_UNIVERSAL_1_0, false},
    316         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_VULKAN_1_1, true},
    317         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_VULKAN_1_0, false},
    318 
    319         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseInstance", SPV_ENV_UNIVERSAL_1_3, true},
    320         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseInstance", SPV_ENV_VULKAN_1_0, false},
    321 
    322         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "DrawIndex", SPV_ENV_UNIVERSAL_1_3, true},
    323         {"SPV_KHR_shader_draw_parameters", "DrawParameters", "DrawIndex", SPV_ENV_UNIVERSAL_1_1, false},
    324 
    325         // SPV_KHR_multiview became core SPIR-V 1.3
    326         {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_UNIVERSAL_1_3, true},
    327         {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_UNIVERSAL_1_2, false},
    328         {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_UNIVERSAL_1_1, false},
    329         {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_UNIVERSAL_1_0, false},
    330         {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_VULKAN_1_1, true},
    331         {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_VULKAN_1_0, false},
    332 
    333         // SPV_KHR_device_group became core SPIR-V 1.3
    334         {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_UNIVERSAL_1_3, true},
    335         {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_UNIVERSAL_1_2, false},
    336         {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_UNIVERSAL_1_1, false},
    337         {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_UNIVERSAL_1_0, false},
    338         {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_VULKAN_1_1, true},
    339         {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_VULKAN_1_0, false},
    340     }));
    341 // clang-format on
    342 
    343 }  // namespace
    344 }  // namespace val
    345 }  // namespace spvtools
    346