1/*
2 * Copyright © 2017 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 *
24 */
25
26#include "nir_spirv.h"
27
28#include "vtn_private.h"
29#include "spirv_info.h"
30
31static bool
32vtn_validate_preamble_instruction(struct vtn_builder *b, SpvOp opcode,
33                                  const uint32_t *w, unsigned count)
34{
35   switch (opcode) {
36   case SpvOpSource:
37   case SpvOpSourceExtension:
38   case SpvOpSourceContinued:
39   case SpvOpExtension:
40   case SpvOpCapability:
41   case SpvOpExtInstImport:
42   case SpvOpMemoryModel:
43   case SpvOpString:
44   case SpvOpName:
45   case SpvOpMemberName:
46   case SpvOpExecutionMode:
47   case SpvOpDecorationGroup:
48   case SpvOpMemberDecorate:
49   case SpvOpGroupDecorate:
50   case SpvOpGroupMemberDecorate:
51      break;
52
53   case SpvOpEntryPoint:
54      vtn_handle_entry_point(b, w, count);
55      break;
56
57   case SpvOpDecorate:
58      vtn_handle_decoration(b, opcode, w, count);
59      break;
60
61   default:
62      return false; /* End of preamble */
63   }
64
65   return true;
66}
67
68static void
69spec_constant_decoration_cb(struct vtn_builder *b, struct vtn_value *v,
70                            int member, const struct vtn_decoration *dec,
71                            void *data)
72{
73   vtn_assert(member == -1);
74   if (dec->decoration != SpvDecorationSpecId)
75      return;
76
77   for (unsigned i = 0; i < b->num_specializations; i++) {
78      if (b->specializations[i].id == dec->operands[0]) {
79         b->specializations[i].defined_on_module = true;
80         return;
81      }
82   }
83}
84
85static void
86vtn_validate_handle_constant(struct vtn_builder *b, SpvOp opcode,
87                             const uint32_t *w, unsigned count)
88{
89   struct vtn_value *val = vtn_push_value(b, w[2], vtn_value_type_constant);
90
91   switch (opcode) {
92   case SpvOpConstant:
93   case SpvOpConstantNull:
94   case SpvOpSpecConstantComposite:
95   case SpvOpConstantComposite:
96      /* Nothing to do here for gl_spirv needs */
97      break;
98
99   case SpvOpConstantTrue:
100   case SpvOpConstantFalse:
101   case SpvOpSpecConstantTrue:
102   case SpvOpSpecConstantFalse:
103   case SpvOpSpecConstant:
104   case SpvOpSpecConstantOp:
105      vtn_foreach_decoration(b, val, spec_constant_decoration_cb, NULL);
106      break;
107
108   case SpvOpConstantSampler:
109      vtn_fail("OpConstantSampler requires Kernel Capability");
110      break;
111
112   default:
113      vtn_fail("Unhandled opcode");
114   }
115}
116
117static bool
118vtn_validate_handle_constant_instruction(struct vtn_builder *b, SpvOp opcode,
119                                         const uint32_t *w, unsigned count)
120{
121   switch (opcode) {
122   case SpvOpSource:
123   case SpvOpSourceContinued:
124   case SpvOpSourceExtension:
125   case SpvOpExtension:
126   case SpvOpCapability:
127   case SpvOpExtInstImport:
128   case SpvOpMemoryModel:
129   case SpvOpEntryPoint:
130   case SpvOpExecutionMode:
131   case SpvOpString:
132   case SpvOpName:
133   case SpvOpMemberName:
134   case SpvOpDecorationGroup:
135   case SpvOpDecorate:
136   case SpvOpMemberDecorate:
137   case SpvOpGroupDecorate:
138   case SpvOpGroupMemberDecorate:
139      vtn_fail("Invalid opcode types and variables section");
140      break;
141
142   case SpvOpTypeVoid:
143   case SpvOpTypeBool:
144   case SpvOpTypeInt:
145   case SpvOpTypeFloat:
146   case SpvOpTypeVector:
147   case SpvOpTypeMatrix:
148   case SpvOpTypeImage:
149   case SpvOpTypeSampler:
150   case SpvOpTypeSampledImage:
151   case SpvOpTypeArray:
152   case SpvOpTypeRuntimeArray:
153   case SpvOpTypeStruct:
154   case SpvOpTypeOpaque:
155   case SpvOpTypePointer:
156   case SpvOpTypeFunction:
157   case SpvOpTypeEvent:
158   case SpvOpTypeDeviceEvent:
159   case SpvOpTypeReserveId:
160   case SpvOpTypeQueue:
161   case SpvOpTypePipe:
162      /* We don't need to handle types */
163      break;
164
165   case SpvOpConstantTrue:
166   case SpvOpConstantFalse:
167   case SpvOpConstant:
168   case SpvOpConstantComposite:
169   case SpvOpConstantSampler:
170   case SpvOpConstantNull:
171   case SpvOpSpecConstantTrue:
172   case SpvOpSpecConstantFalse:
173   case SpvOpSpecConstant:
174   case SpvOpSpecConstantComposite:
175   case SpvOpSpecConstantOp:
176      vtn_validate_handle_constant(b, opcode, w, count);
177      break;
178
179   case SpvOpUndef:
180   case SpvOpVariable:
181      /* We don't need to handle them */
182      break;
183
184   default:
185      return false; /* End of preamble */
186   }
187
188   return true;
189}
190
191/*
192 * Since OpenGL 4.6 you can use SPIR-V modules directly on OpenGL. One of the
193 * new methods, glSpecializeShader include some possible errors when trying to
194 * use it.
195 *
196 * From OpenGL 4.6, Section 7.2.1, "Shader Specialization":
197 *
198 * "void SpecializeShaderARB(uint shader,
199 *                           const char* pEntryPoint,
200 *                           uint numSpecializationConstants,
201 *                           const uint* pConstantIndex,
202 *                           const uint* pConstantValue);
203 * <skip>
204 *
205 * INVALID_VALUE is generated if <pEntryPoint> does not name a valid
206 * entry point for <shader>.
207 *
208 * An INVALID_VALUE error is generated if any element of pConstantIndex refers
209 * to a specialization constant that does not exist in the shader module
210 * contained in shader."
211 *
212 * We could do those checks on spirv_to_nir, but we are only interested on the
213 * full translation later, during linking. This method is a simplified version
214 * of spirv_to_nir, looking for only the checks needed by SpecializeShader.
215 *
216 * This method returns NULL if no entry point was found, and fill the
217 * nir_spirv_specialization field "defined_on_module" accordingly. Caller
218 * would need to trigger the specific errors.
219 *
220 */
221bool
222gl_spirv_validation(const uint32_t *words, size_t word_count,
223                    struct nir_spirv_specialization *spec, unsigned num_spec,
224                    gl_shader_stage stage, const char *entry_point_name)
225{
226   /* vtn_warn/vtn_log uses debug.func. Setting a null to prevent crash. Not
227    * need to print the warnings now, would be done later, on the real
228    * spirv_to_nir
229    */
230   const struct spirv_to_nir_options options = { .debug.func = NULL};
231   const uint32_t *word_end = words + word_count;
232
233   struct vtn_builder *b = vtn_create_builder(words, word_count,
234                                              stage, entry_point_name,
235                                              &options);
236
237   if (b == NULL)
238      return false;
239
240   /* See also _vtn_fail() */
241   if (vtn_setjmp(b->fail_jump)) {
242      ralloc_free(b);
243      return false;
244   }
245
246   /* Skip the SPIR-V header, handled at vtn_create_builder */
247   words+= 5;
248
249   /* Search entry point from preamble */
250   words = vtn_foreach_instruction(b, words, word_end,
251                                   vtn_validate_preamble_instruction);
252
253   if (b->entry_point == NULL) {
254      ralloc_free(b);
255      return false;
256   }
257
258   b->specializations = spec;
259   b->num_specializations = num_spec;
260
261   /* Handle constant instructions (we don't need to handle
262    * variables or types for gl_spirv)
263    */
264   words = vtn_foreach_instruction(b, words, word_end,
265                                   vtn_validate_handle_constant_instruction);
266
267   ralloc_free(b);
268
269   return true;
270}
271
272