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