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
24namespace spvtools {
25namespace val {
26namespace {
27
28spv_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
43spv_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
77spv_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