1 | // Copyright (c) 2018 Google LLC. |
2 | // Copyright (c) 2019 NVIDIA Corporation |
3 | // |
4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
5 | // you may not use this file except in compliance with the License. |
6 | // You may obtain a copy of the License at |
7 | // |
8 | // http://www.apache.org/licenses/LICENSE-2.0 |
9 | // |
10 | // Unless required by applicable law or agreed to in writing, software |
11 | // distributed under the License is distributed on an "AS IS" BASIS, |
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | // See the License for the specific language governing permissions and |
14 | // limitations under the License. |
15 | |
16 | #include "source/val/validate.h" |
17 | |
18 | #include "source/opcode.h" |
19 | #include "source/spirv_target_env.h" |
20 | #include "source/val/instruction.h" |
21 | #include "source/val/validate_scopes.h" |
22 | #include "source/val/validation_state.h" |
23 | |
24 | namespace spvtools { |
25 | namespace val { |
26 | namespace { |
27 | |
28 | spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) { |
29 | if (_.HasCapability(SpvCapabilityShader) && |
30 | _.ContainsLimitedUseIntOrFloatType(inst->type_id()) && |
31 | !_.IsPointerType(inst->type_id())) { |
32 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
33 | << "Cannot create undefined values with 8- or 16-bit types" ; |
34 | } |
35 | |
36 | if (spvIsWebGPUEnv(_.context()->target_env)) { |
37 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) << "OpUndef is disallowed" ; |
38 | } |
39 | |
40 | return SPV_SUCCESS; |
41 | } |
42 | |
43 | spv_result_t ValidateShaderClock(ValidationState_t& _, |
44 | const Instruction* inst) { |
45 | const uint32_t scope = inst->GetOperandAs<uint32_t>(2); |
46 | if (auto error = ValidateScope(_, inst, scope)) { |
47 | return error; |
48 | } |
49 | |
50 | bool is_int32 = false, is_const_int32 = false; |
51 | uint32_t value = 0; |
52 | std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope); |
53 | if (is_const_int32 && value != SpvScopeSubgroup && value != SpvScopeDevice) { |
54 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
55 | << "Scope must be Subgroup or Device" ; |
56 | } |
57 | |
58 | // Result Type must be a 64 - bit unsigned integer type or |
59 | // a vector of two - components of 32 - |
60 | // bit unsigned integer type |
61 | const uint32_t result_type = inst->type_id(); |
62 | if (!(_.IsUnsignedIntScalarType(result_type) && |
63 | _.GetBitWidth(result_type) == 64) && |
64 | !(_.IsUnsignedIntVectorType(result_type) && |
65 | _.GetDimension(result_type) == 2 && _.GetBitWidth(result_type) == 32)) { |
66 | return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a " |
67 | "vector of two components" |
68 | " of unsigned integer" |
69 | " or 64bit unsigned integer" ; |
70 | } |
71 | |
72 | return SPV_SUCCESS; |
73 | } |
74 | |
75 | } // namespace |
76 | |
77 | spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) { |
78 | switch (inst->opcode()) { |
79 | case SpvOpUndef: |
80 | if (auto error = ValidateUndef(_, inst)) return error; |
81 | break; |
82 | default: |
83 | break; |
84 | } |
85 | switch (inst->opcode()) { |
86 | case SpvOpBeginInvocationInterlockEXT: |
87 | case SpvOpEndInvocationInterlockEXT: |
88 | _.function(inst->function()->id()) |
89 | ->RegisterExecutionModelLimitation( |
90 | SpvExecutionModelFragment, |
91 | "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT " |
92 | "require Fragment execution model" ); |
93 | |
94 | _.function(inst->function()->id()) |
95 | ->RegisterLimitation([](const ValidationState_t& state, |
96 | const Function* entry_point, |
97 | std::string* message) { |
98 | const auto* execution_modes = |
99 | state.GetExecutionModes(entry_point->id()); |
100 | |
101 | auto find_interlock = [](const SpvExecutionMode& mode) { |
102 | switch (mode) { |
103 | case SpvExecutionModePixelInterlockOrderedEXT: |
104 | case SpvExecutionModePixelInterlockUnorderedEXT: |
105 | case SpvExecutionModeSampleInterlockOrderedEXT: |
106 | case SpvExecutionModeSampleInterlockUnorderedEXT: |
107 | case SpvExecutionModeShadingRateInterlockOrderedEXT: |
108 | case SpvExecutionModeShadingRateInterlockUnorderedEXT: |
109 | return true; |
110 | default: |
111 | return false; |
112 | } |
113 | }; |
114 | |
115 | bool found = false; |
116 | if (execution_modes) { |
117 | auto i = std::find_if(execution_modes->begin(), |
118 | execution_modes->end(), find_interlock); |
119 | found = (i != execution_modes->end()); |
120 | } |
121 | |
122 | if (!found) { |
123 | *message = |
124 | "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT " |
125 | "require a fragment shader interlock execution mode." ; |
126 | return false; |
127 | } |
128 | return true; |
129 | }); |
130 | break; |
131 | case SpvOpDemoteToHelperInvocationEXT: |
132 | _.function(inst->function()->id()) |
133 | ->RegisterExecutionModelLimitation( |
134 | SpvExecutionModelFragment, |
135 | "OpDemoteToHelperInvocationEXT requires Fragment execution " |
136 | "model" ); |
137 | break; |
138 | case SpvOpIsHelperInvocationEXT: { |
139 | const uint32_t result_type = inst->type_id(); |
140 | _.function(inst->function()->id()) |
141 | ->RegisterExecutionModelLimitation( |
142 | SpvExecutionModelFragment, |
143 | "OpIsHelperInvocationEXT requires Fragment execution model" ); |
144 | if (!_.IsBoolScalarType(result_type)) |
145 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
146 | << "Expected bool scalar type as Result Type: " |
147 | << spvOpcodeString(inst->opcode()); |
148 | break; |
149 | } |
150 | case SpvOpReadClockKHR: |
151 | if (auto error = ValidateShaderClock(_, inst)) { |
152 | return error; |
153 | } |
154 | break; |
155 | default: |
156 | break; |
157 | } |
158 | |
159 | return SPV_SUCCESS; |
160 | } |
161 | |
162 | } // namespace val |
163 | } // namespace spvtools |
164 | |