1 // Copyright (c) 2018 Google LLC.
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 #include "source/val/validate_scopes.h"
16
17 #include "source/diagnostic.h"
18 #include "source/spirv_target_env.h"
19 #include "source/val/instruction.h"
20 #include "source/val/validation_state.h"
21
22 namespace spvtools {
23 namespace val {
24
ValidateExecutionScope(ValidationState_t & _,const Instruction * inst,uint32_t scope)25 spv_result_t ValidateExecutionScope(ValidationState_t& _,
26 const Instruction* inst, uint32_t scope) {
27 SpvOp opcode = inst->opcode();
28 bool is_int32 = false, is_const_int32 = false;
29 uint32_t value = 0;
30 std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
31
32 if (!is_int32) {
33 return _.diag(SPV_ERROR_INVALID_DATA, inst)
34 << spvOpcodeString(opcode)
35 << ": expected Execution Scope to be a 32-bit int";
36 }
37
38 if (!is_const_int32) {
39 if (_.HasCapability(SpvCapabilityShader)) {
40 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Scope ids must be "
41 "OpConstant when Shader "
42 "capability is present";
43 }
44 return SPV_SUCCESS;
45 }
46
47 // Vulkan specific rules
48 if (spvIsVulkanEnv(_.context()->target_env)) {
49 // Vulkan 1.1 specific rules
50 if (_.context()->target_env != SPV_ENV_VULKAN_1_0) {
51 // Scope for Non Uniform Group Operations must be limited to Subgroup
52 if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
53 value != SpvScopeSubgroup) {
54 return _.diag(SPV_ERROR_INVALID_DATA, inst)
55 << spvOpcodeString(opcode)
56 << ": in Vulkan environment Execution scope is limited to "
57 "Subgroup";
58 }
59 }
60
61 // If OpControlBarrier is used in fragment, vertex, tessellation evaluation,
62 // or geometry stages, the execution Scope must be Subgroup.
63 if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) {
64 _.function(inst->function()->id())
65 ->RegisterExecutionModelLimitation([](SpvExecutionModel model,
66 std::string* message) {
67 if (model == SpvExecutionModelFragment ||
68 model == SpvExecutionModelVertex ||
69 model == SpvExecutionModelGeometry ||
70 model == SpvExecutionModelTessellationEvaluation) {
71 if (message) {
72 *message =
73 "in Vulkan evironment, OpControlBarrier execution scope "
74 "must be Subgroup for Fragment, Vertex, Geometry and "
75 "TessellationEvaluation execution models";
76 }
77 return false;
78 }
79 return true;
80 });
81 }
82
83 // Vulkan generic rules
84 // Scope for execution must be limited to Workgroup or Subgroup
85 if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
86 return _.diag(SPV_ERROR_INVALID_DATA, inst)
87 << spvOpcodeString(opcode)
88 << ": in Vulkan environment Execution Scope is limited to "
89 "Workgroup and Subgroup";
90 }
91 }
92
93 // WebGPU Specific rules
94 if (spvIsWebGPUEnv(_.context()->target_env)) {
95 // Scope for execution must be limited to Workgroup or Subgroup
96 if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
97 return _.diag(SPV_ERROR_INVALID_DATA, inst)
98 << spvOpcodeString(opcode)
99 << ": in WebGPU environment Execution Scope is limited to "
100 "Workgroup and Subgroup";
101 }
102 }
103
104 // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
105
106 // General SPIRV rules
107 // Scope for execution must be limited to Workgroup or Subgroup for
108 // non-uniform operations
109 if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
110 value != SpvScopeSubgroup && value != SpvScopeWorkgroup) {
111 return _.diag(SPV_ERROR_INVALID_DATA, inst)
112 << spvOpcodeString(opcode)
113 << ": Execution scope is limited to Subgroup or Workgroup";
114 }
115
116 return SPV_SUCCESS;
117 }
118
ValidateMemoryScope(ValidationState_t & _,const Instruction * inst,uint32_t scope)119 spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
120 uint32_t scope) {
121 const SpvOp opcode = inst->opcode();
122 bool is_int32 = false, is_const_int32 = false;
123 uint32_t value = 0;
124 std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
125
126 if (!is_int32) {
127 return _.diag(SPV_ERROR_INVALID_DATA, inst)
128 << spvOpcodeString(opcode)
129 << ": expected Memory Scope to be a 32-bit int";
130 }
131
132 if (!is_const_int32) {
133 if (_.HasCapability(SpvCapabilityShader)) {
134 return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Scope ids must be "
135 "OpConstant when Shader "
136 "capability is present";
137 }
138 return SPV_SUCCESS;
139 }
140
141 if (value == SpvScopeQueueFamilyKHR) {
142 if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
143 return SPV_SUCCESS;
144 } else {
145 return _.diag(SPV_ERROR_INVALID_DATA, inst)
146 << spvOpcodeString(opcode)
147 << ": Memory Scope QueueFamilyKHR requires capability "
148 "VulkanMemoryModelKHR";
149 }
150 }
151
152 if (value == SpvScopeDevice &&
153 _.HasCapability(SpvCapabilityVulkanMemoryModelKHR) &&
154 !_.HasCapability(SpvCapabilityVulkanMemoryModelDeviceScopeKHR)) {
155 return _.diag(SPV_ERROR_INVALID_DATA, inst)
156 << "Use of device scope with VulkanKHR memory model requires the "
157 "VulkanMemoryModelDeviceScopeKHR capability";
158 }
159
160 // Vulkan Specific rules
161 if (spvIsVulkanEnv(_.context()->target_env)) {
162 if (value == SpvScopeCrossDevice) {
163 return _.diag(SPV_ERROR_INVALID_DATA, inst)
164 << spvOpcodeString(opcode)
165 << ": in Vulkan environment, Memory Scope cannot be CrossDevice";
166 }
167 // Vulkan 1.0 specifc rules
168 if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
169 value != SpvScopeDevice && value != SpvScopeWorkgroup &&
170 value != SpvScopeInvocation) {
171 return _.diag(SPV_ERROR_INVALID_DATA, inst)
172 << spvOpcodeString(opcode)
173 << ": in Vulkan 1.0 environment Memory Scope is limited to "
174 "Device, "
175 "Workgroup and Invocation";
176 }
177 // Vulkan 1.1 specifc rules
178 if (_.context()->target_env == SPV_ENV_VULKAN_1_1 &&
179 value != SpvScopeDevice && value != SpvScopeWorkgroup &&
180 value != SpvScopeSubgroup && value != SpvScopeInvocation) {
181 return _.diag(SPV_ERROR_INVALID_DATA, inst)
182 << spvOpcodeString(opcode)
183 << ": in Vulkan 1.1 environment Memory Scope is limited to "
184 "Device, "
185 "Workgroup and Invocation";
186 }
187 }
188
189 // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
190
191 return SPV_SUCCESS;
192 }
193
194 } // namespace val
195 } // namespace spvtools
196