Skip to content

Commit 8f98634

Browse files
spirv-val: Validate zero product workgroup size (#5407)
* spirv-val: ValidateWorkgroupSizeAtDefinition as Universial * spirv-val: Validate ExecutionMode local size * spirv-val: Tests for zero product workgroup size
1 parent 1a0658f commit 8f98634

File tree

3 files changed

+265
-27
lines changed

3 files changed

+265
-27
lines changed

source/val/validate_builtins.cpp

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ class BuiltInsValidator {
269269
// specified. Seeds id_to_at_reference_checks_ with decorated ids if needed.
270270
spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration,
271271
const Instruction& inst);
272+
spv_result_t ValidateSingleBuiltInAtDefinitionVulkan(
273+
const Decoration& decoration, const Instruction& inst,
274+
const spv::BuiltIn label);
272275

273276
// The following section contains functions which are called when id defined
274277
// by |inst| is decorated with BuiltIn |decoration|.
@@ -3215,27 +3218,44 @@ spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition(
32153218

32163219
spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition(
32173220
const Decoration& decoration, const Instruction& inst) {
3218-
if (spvIsVulkanEnv(_.context()->target_env)) {
3219-
if (spvIsVulkanEnv(_.context()->target_env) &&
3220-
!spvOpcodeIsConstant(inst.opcode())) {
3221+
if (spv_result_t error = ValidateI32Vec(
3222+
decoration, inst, 3,
3223+
[this, &inst](const std::string& message) -> spv_result_t {
3224+
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3225+
<< _.VkErrorID(4427) << "According to the "
3226+
<< spvLogStringForEnv(_.context()->target_env)
3227+
<< " spec BuiltIn WorkgroupSize variable needs to be a "
3228+
"3-component 32-bit int vector. "
3229+
<< message;
3230+
})) {
3231+
return error;
3232+
}
3233+
3234+
if (!spvOpcodeIsConstant(inst.opcode())) {
3235+
if (spvIsVulkanEnv(_.context()->target_env)) {
32213236
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
32223237
<< _.VkErrorID(4426)
32233238
<< "Vulkan spec requires BuiltIn WorkgroupSize to be a "
32243239
"constant. "
32253240
<< GetIdDesc(inst) << " is not a constant.";
32263241
}
3227-
3228-
if (spv_result_t error = ValidateI32Vec(
3229-
decoration, inst, 3,
3230-
[this, &inst](const std::string& message) -> spv_result_t {
3231-
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3232-
<< _.VkErrorID(4427) << "According to the "
3233-
<< spvLogStringForEnv(_.context()->target_env)
3234-
<< " spec BuiltIn WorkgroupSize variable needs to be a "
3235-
"3-component 32-bit int vector. "
3236-
<< message;
3237-
})) {
3238-
return error;
3242+
} else if (inst.opcode() == spv::Op::OpConstantComposite) {
3243+
// can only validate product if static and not spec constant
3244+
if (_.FindDef(inst.word(3))->opcode() == spv::Op::OpConstant &&
3245+
_.FindDef(inst.word(4))->opcode() == spv::Op::OpConstant &&
3246+
_.FindDef(inst.word(5))->opcode() == spv::Op::OpConstant) {
3247+
uint64_t x_size, y_size, z_size;
3248+
// ValidateI32Vec above confirms there will be 3 words to read
3249+
bool static_x = _.EvalConstantValUint64(inst.word(3), &x_size);
3250+
bool static_y = _.EvalConstantValUint64(inst.word(4), &y_size);
3251+
bool static_z = _.EvalConstantValUint64(inst.word(5), &z_size);
3252+
if (static_x && static_y && static_z &&
3253+
((x_size * y_size * z_size) == 0)) {
3254+
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3255+
<< "WorkgroupSize decorations must not have a static "
3256+
"product of zero (X = "
3257+
<< x_size << ", Y = " << y_size << ", Z = " << z_size << ").";
3258+
}
32393259
}
32403260
}
32413261

@@ -4304,18 +4324,20 @@ spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtReference(
43044324
spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
43054325
const Decoration& decoration, const Instruction& inst) {
43064326
const spv::BuiltIn label = decoration.builtin();
4327+
// Universial checks
4328+
if (label == spv::BuiltIn::WorkgroupSize) {
4329+
return ValidateWorkgroupSizeAtDefinition(decoration, inst);
4330+
}
43074331

4308-
if (!spvIsVulkanEnv(_.context()->target_env)) {
4309-
// Early return. All currently implemented rules are based on Vulkan spec.
4310-
//
4311-
// TODO: If you are adding validation rules for environments other than
4312-
// Vulkan (or general rules which are not environment independent), then
4313-
// you need to modify or remove this condition. Consider also adding early
4314-
// returns into BuiltIn-specific rules, so that the system doesn't spawn new
4315-
// rules which don't do anything.
4316-
return SPV_SUCCESS;
4332+
if (spvIsVulkanEnv(_.context()->target_env)) {
4333+
return ValidateSingleBuiltInAtDefinitionVulkan(decoration, inst, label);
43174334
}
4335+
return SPV_SUCCESS;
4336+
}
43184337

4338+
spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinitionVulkan(
4339+
const Decoration& decoration, const Instruction& inst,
4340+
const spv::BuiltIn label) {
43194341
// If you are adding a new BuiltIn enum, please register it here.
43204342
// If the newly added enum has validation rules associated with it
43214343
// consider leaving a TODO and/or creating an issue.
@@ -4407,9 +4429,6 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
44074429
case spv::BuiltIn::VertexIndex: {
44084430
return ValidateVertexIndexAtDefinition(decoration, inst);
44094431
}
4410-
case spv::BuiltIn::WorkgroupSize: {
4411-
return ValidateWorkgroupSizeAtDefinition(decoration, inst);
4412-
}
44134432
case spv::BuiltIn::VertexId: {
44144433
return ValidateVertexIdAtDefinition(decoration, inst);
44154434
}

source/val/validate_mode_setting.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,42 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
323323
}
324324
}
325325

326+
if (_.EntryPointHasLocalSizeOrId(entry_point_id)) {
327+
const Instruction* local_size_inst =
328+
_.EntryPointLocalSizeOrId(entry_point_id);
329+
if (local_size_inst) {
330+
const auto mode = local_size_inst->GetOperandAs<spv::ExecutionMode>(1);
331+
const uint32_t operand_x = local_size_inst->GetOperandAs<uint32_t>(2);
332+
const uint32_t operand_y = local_size_inst->GetOperandAs<uint32_t>(3);
333+
const uint32_t operand_z = local_size_inst->GetOperandAs<uint32_t>(4);
334+
if (mode == spv::ExecutionMode::LocalSize) {
335+
if ((operand_x * operand_y * operand_z) == 0) {
336+
return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
337+
<< "Local Size execution mode must not have a product of zero "
338+
"(X "
339+
"= "
340+
<< operand_x << ", Y = " << operand_y << ", Z = " << operand_z
341+
<< ").";
342+
}
343+
} else if (mode == spv::ExecutionMode::LocalSizeId) {
344+
// can only validate product if static and not spec constant
345+
// (This is done for us in EvalConstantValUint64)
346+
uint64_t x_size, y_size, z_size;
347+
bool static_x = _.EvalConstantValUint64(operand_x, &x_size);
348+
bool static_y = _.EvalConstantValUint64(operand_y, &y_size);
349+
bool static_z = _.EvalConstantValUint64(operand_z, &z_size);
350+
if (static_x && static_y && static_z &&
351+
((x_size * y_size * z_size) == 0)) {
352+
return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
353+
<< "Local Size Id execution mode must not have a product of "
354+
"zero "
355+
"(X = "
356+
<< x_size << ", Y = " << y_size << ", Z = " << z_size << ").";
357+
}
358+
}
359+
}
360+
}
361+
326362
return SPV_SUCCESS;
327363
}
328364

test/val/val_modes_test.cpp

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,99 @@ OpDecorate %int3_1 BuiltIn WorkgroupSize
8888
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
8989
}
9090

91+
TEST_F(ValidateMode, GLComputeZeroWorkgroupSize) {
92+
const std::string spirv = R"(
93+
OpCapability Shader
94+
OpMemoryModel Logical GLSL450
95+
OpEntryPoint GLCompute %main "main"
96+
OpDecorate %int3_1 BuiltIn WorkgroupSize
97+
%int = OpTypeInt 32 0
98+
%int3 = OpTypeVector %int 3
99+
%int_0 = OpConstant %int 0
100+
%int_1 = OpConstant %int 1
101+
%int3_1 = OpConstantComposite %int3 %int_1 %int_0 %int_0
102+
)" + kVoidFunction;
103+
104+
CompileSuccessfully(spirv);
105+
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
106+
EXPECT_THAT(
107+
getDiagnosticString(),
108+
HasSubstr(
109+
"WorkgroupSize decorations must not have a static product of zero"));
110+
}
111+
112+
TEST_F(ValidateMode, GLComputeZeroSpecWorkgroupSize) {
113+
const std::string spirv = R"(
114+
OpCapability Shader
115+
OpMemoryModel Logical GLSL450
116+
OpEntryPoint GLCompute %main "main"
117+
OpDecorate %int3_1 BuiltIn WorkgroupSize
118+
%int = OpTypeInt 32 0
119+
%int3 = OpTypeVector %int 3
120+
%int_0 = OpSpecConstant %int 0
121+
%int_1 = OpConstant %int 1
122+
%int3_1 = OpConstantComposite %int3 %int_1 %int_0 %int_0
123+
)" + kVoidFunction;
124+
125+
CompileSuccessfully(spirv);
126+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
127+
}
128+
129+
TEST_F(ValidateMode, GLComputeZeroSpecCompositeWorkgroupSize) {
130+
const std::string spirv = R"(
131+
OpCapability Shader
132+
OpMemoryModel Logical GLSL450
133+
OpEntryPoint GLCompute %main "main"
134+
OpDecorate %int3_1 BuiltIn WorkgroupSize
135+
%int = OpTypeInt 32 0
136+
%int3 = OpTypeVector %int 3
137+
%int_0 = OpSpecConstant %int 0
138+
%int_1 = OpSpecConstant %int 1
139+
%int3_1 = OpSpecConstantComposite %int3 %int_1 %int_0 %int_0
140+
)" + kVoidFunction;
141+
142+
CompileSuccessfully(spirv);
143+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
144+
}
145+
146+
TEST_F(ValidateMode, KernelZeroWorkgroupSizeConstant) {
147+
const std::string spirv = R"(
148+
OpCapability Addresses
149+
OpCapability Linkage
150+
OpCapability Kernel
151+
OpMemoryModel Physical32 OpenCL
152+
OpEntryPoint Kernel %main "main"
153+
OpDecorate %int3_1 BuiltIn WorkgroupSize
154+
%int = OpTypeInt 32 0
155+
%int3 = OpTypeVector %int 3
156+
%int_0 = OpConstant %int 0
157+
%int_1 = OpConstant %int 1
158+
%int3_1 = OpConstantComposite %int3 %int_1 %int_0 %int_0
159+
)" + kVoidFunction;
160+
161+
CompileSuccessfully(spirv);
162+
EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions());
163+
EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
164+
}
165+
166+
TEST_F(ValidateMode, KernelZeroWorkgroupSizeVariable) {
167+
const std::string spirv = R"(
168+
OpCapability Addresses
169+
OpCapability Linkage
170+
OpCapability Kernel
171+
OpMemoryModel Physical32 OpenCL
172+
OpEntryPoint Kernel %main "main"
173+
OpDecorate %var BuiltIn WorkgroupSize
174+
%int = OpTypeInt 32 0
175+
%int3 = OpTypeVector %int 3
176+
%ptr = OpTypePointer Input %int3
177+
%var = OpVariable %ptr Input
178+
)" + kVoidFunction;
179+
180+
CompileSuccessfully(spirv);
181+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
182+
}
183+
91184
TEST_F(ValidateMode, GLComputeVulkanLocalSize) {
92185
const std::string spirv = R"(
93186
OpCapability Shader
@@ -101,6 +194,38 @@ OpExecutionMode %main LocalSize 1 1 1
101194
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
102195
}
103196

197+
TEST_F(ValidateMode, GLComputeZeroLocalSize) {
198+
const std::string spirv = R"(
199+
OpCapability Shader
200+
OpMemoryModel Logical GLSL450
201+
OpEntryPoint GLCompute %main "main"
202+
OpExecutionMode %main LocalSize 1 1 0
203+
)" + kVoidFunction;
204+
205+
CompileSuccessfully(spirv);
206+
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
207+
EXPECT_THAT(
208+
getDiagnosticString(),
209+
HasSubstr("Local Size execution mode must not have a product of zero"));
210+
}
211+
212+
TEST_F(ValidateMode, KernelZeroLocalSize) {
213+
const std::string spirv = R"(
214+
OpCapability Addresses
215+
OpCapability Linkage
216+
OpCapability Kernel
217+
OpMemoryModel Physical32 OpenCL
218+
OpEntryPoint Kernel %main "main"
219+
OpExecutionMode %main LocalSize 1 1 0
220+
)" + kVoidFunction;
221+
222+
CompileSuccessfully(spirv);
223+
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
224+
EXPECT_THAT(
225+
getDiagnosticString(),
226+
HasSubstr("Local Size execution mode must not have a product of zero"));
227+
}
228+
104229
TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) {
105230
const std::string spirv = R"(
106231
OpCapability Shader
@@ -135,6 +260,64 @@ OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
135260
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
136261
}
137262

263+
TEST_F(ValidateMode, GLComputeZeroLocalSizeId) {
264+
const std::string spirv = R"(
265+
OpCapability Shader
266+
OpMemoryModel Logical GLSL450
267+
OpEntryPoint GLCompute %main "main"
268+
OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1
269+
%int = OpTypeInt 32 0
270+
%int_1 = OpConstant %int 1
271+
%int_0 = OpConstant %int 0
272+
)" + kVoidFunction;
273+
274+
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
275+
CompileSuccessfully(spirv, env);
276+
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
277+
EXPECT_THAT(
278+
getDiagnosticString(),
279+
HasSubstr(
280+
"Local Size Id execution mode must not have a product of zero"));
281+
}
282+
283+
TEST_F(ValidateMode, GLComputeZeroSpecLocalSizeId) {
284+
const std::string spirv = R"(
285+
OpCapability Shader
286+
OpMemoryModel Logical GLSL450
287+
OpEntryPoint GLCompute %main "main"
288+
OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1
289+
%int = OpTypeInt 32 0
290+
%int_1 = OpConstant %int 1
291+
%int_0 = OpSpecConstant %int 0
292+
)" + kVoidFunction;
293+
294+
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
295+
CompileSuccessfully(spirv, env);
296+
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(env));
297+
}
298+
299+
TEST_F(ValidateMode, KernelZeroLocalSizeId) {
300+
const std::string spirv = R"(
301+
OpCapability Addresses
302+
OpCapability Linkage
303+
OpCapability Kernel
304+
OpMemoryModel Physical32 OpenCL
305+
OpEntryPoint Kernel %main "main"
306+
OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1
307+
%int = OpTypeInt 32 0
308+
%int_1 = OpConstant %int 1
309+
%int_0 = OpConstant %int 0
310+
)" + kVoidFunction;
311+
312+
spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
313+
CompileSuccessfully(spirv, env);
314+
EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
315+
EXPECT_THAT(
316+
getDiagnosticString(),
317+
HasSubstr(
318+
"Local Size Id execution mode must not have a product of zero"));
319+
}
320+
138321
TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) {
139322
const std::string spirv = R"(
140323
OpCapability Shader

0 commit comments

Comments
 (0)