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