1 | // Copyright (c) 2015-2016 The Khronos Group Inc. |
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/spirv_target_env.h" |
16 | |
17 | #include <cassert> |
18 | #include <cstring> |
19 | #include <string> |
20 | |
21 | #include "source/spirv_constant.h" |
22 | #include "spirv-tools/libspirv.h" |
23 | |
24 | const char* spvTargetEnvDescription(spv_target_env env) { |
25 | switch (env) { |
26 | case SPV_ENV_UNIVERSAL_1_0: |
27 | return "SPIR-V 1.0" ; |
28 | case SPV_ENV_VULKAN_1_0: |
29 | return "SPIR-V 1.0 (under Vulkan 1.0 semantics)" ; |
30 | case SPV_ENV_UNIVERSAL_1_1: |
31 | return "SPIR-V 1.1" ; |
32 | case SPV_ENV_OPENCL_1_2: |
33 | return "SPIR-V 1.0 (under OpenCL 1.2 Full Profile semantics)" ; |
34 | case SPV_ENV_OPENCL_EMBEDDED_1_2: |
35 | return "SPIR-V 1.0 (under OpenCL 1.2 Embedded Profile semantics)" ; |
36 | case SPV_ENV_OPENCL_2_0: |
37 | return "SPIR-V 1.0 (under OpenCL 2.0 Full Profile semantics)" ; |
38 | case SPV_ENV_OPENCL_EMBEDDED_2_0: |
39 | return "SPIR-V 1.0 (under OpenCL 2.0 Embedded Profile semantics)" ; |
40 | case SPV_ENV_OPENCL_2_1: |
41 | return "SPIR-V 1.0 (under OpenCL 2.1 Full Profile semantics)" ; |
42 | case SPV_ENV_OPENCL_EMBEDDED_2_1: |
43 | return "SPIR-V 1.0 (under OpenCL 2.1 Embedded Profile semantics)" ; |
44 | case SPV_ENV_OPENCL_2_2: |
45 | return "SPIR-V 1.2 (under OpenCL 2.2 Full Profile semantics)" ; |
46 | case SPV_ENV_OPENCL_EMBEDDED_2_2: |
47 | return "SPIR-V 1.2 (under OpenCL 2.2 Embedded Profile semantics)" ; |
48 | case SPV_ENV_OPENGL_4_0: |
49 | return "SPIR-V 1.0 (under OpenGL 4.0 semantics)" ; |
50 | case SPV_ENV_OPENGL_4_1: |
51 | return "SPIR-V 1.0 (under OpenGL 4.1 semantics)" ; |
52 | case SPV_ENV_OPENGL_4_2: |
53 | return "SPIR-V 1.0 (under OpenGL 4.2 semantics)" ; |
54 | case SPV_ENV_OPENGL_4_3: |
55 | return "SPIR-V 1.0 (under OpenGL 4.3 semantics)" ; |
56 | case SPV_ENV_OPENGL_4_5: |
57 | return "SPIR-V 1.0 (under OpenGL 4.5 semantics)" ; |
58 | case SPV_ENV_UNIVERSAL_1_2: |
59 | return "SPIR-V 1.2" ; |
60 | case SPV_ENV_UNIVERSAL_1_3: |
61 | return "SPIR-V 1.3" ; |
62 | case SPV_ENV_VULKAN_1_1: |
63 | return "SPIR-V 1.3 (under Vulkan 1.1 semantics)" ; |
64 | case SPV_ENV_WEBGPU_0: |
65 | assert(false && "Deprecated target environment value." ); |
66 | break; |
67 | case SPV_ENV_UNIVERSAL_1_4: |
68 | return "SPIR-V 1.4" ; |
69 | case SPV_ENV_VULKAN_1_1_SPIRV_1_4: |
70 | return "SPIR-V 1.4 (under Vulkan 1.1 semantics)" ; |
71 | case SPV_ENV_UNIVERSAL_1_5: |
72 | return "SPIR-V 1.5" ; |
73 | case SPV_ENV_VULKAN_1_2: |
74 | return "SPIR-V 1.5 (under Vulkan 1.2 semantics)" ; |
75 | case SPV_ENV_UNIVERSAL_1_6: |
76 | return "SPIR-V 1.6" ; |
77 | case SPV_ENV_VULKAN_1_3: |
78 | return "SPIR-V 1.6 (under Vulkan 1.3 semantics)" ; |
79 | case SPV_ENV_MAX: |
80 | assert(false && "Invalid target environment value." ); |
81 | break; |
82 | } |
83 | return "" ; |
84 | } |
85 | |
86 | uint32_t spvVersionForTargetEnv(spv_target_env env) { |
87 | switch (env) { |
88 | case SPV_ENV_UNIVERSAL_1_0: |
89 | case SPV_ENV_VULKAN_1_0: |
90 | case SPV_ENV_OPENCL_1_2: |
91 | case SPV_ENV_OPENCL_EMBEDDED_1_2: |
92 | case SPV_ENV_OPENCL_2_0: |
93 | case SPV_ENV_OPENCL_EMBEDDED_2_0: |
94 | case SPV_ENV_OPENCL_2_1: |
95 | case SPV_ENV_OPENCL_EMBEDDED_2_1: |
96 | case SPV_ENV_OPENGL_4_0: |
97 | case SPV_ENV_OPENGL_4_1: |
98 | case SPV_ENV_OPENGL_4_2: |
99 | case SPV_ENV_OPENGL_4_3: |
100 | case SPV_ENV_OPENGL_4_5: |
101 | return SPV_SPIRV_VERSION_WORD(1, 0); |
102 | case SPV_ENV_UNIVERSAL_1_1: |
103 | return SPV_SPIRV_VERSION_WORD(1, 1); |
104 | case SPV_ENV_UNIVERSAL_1_2: |
105 | case SPV_ENV_OPENCL_2_2: |
106 | case SPV_ENV_OPENCL_EMBEDDED_2_2: |
107 | return SPV_SPIRV_VERSION_WORD(1, 2); |
108 | case SPV_ENV_UNIVERSAL_1_3: |
109 | case SPV_ENV_VULKAN_1_1: |
110 | return SPV_SPIRV_VERSION_WORD(1, 3); |
111 | case SPV_ENV_WEBGPU_0: |
112 | assert(false && "Deprecated target environment value." ); |
113 | break; |
114 | case SPV_ENV_UNIVERSAL_1_4: |
115 | case SPV_ENV_VULKAN_1_1_SPIRV_1_4: |
116 | return SPV_SPIRV_VERSION_WORD(1, 4); |
117 | case SPV_ENV_UNIVERSAL_1_5: |
118 | case SPV_ENV_VULKAN_1_2: |
119 | return SPV_SPIRV_VERSION_WORD(1, 5); |
120 | case SPV_ENV_UNIVERSAL_1_6: |
121 | case SPV_ENV_VULKAN_1_3: |
122 | return SPV_SPIRV_VERSION_WORD(1, 6); |
123 | case SPV_ENV_MAX: |
124 | assert(false && "Invalid target environment value." ); |
125 | break; |
126 | } |
127 | return SPV_SPIRV_VERSION_WORD(0, 0); |
128 | } |
129 | |
130 | static const std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = { |
131 | {"vulkan1.1spv1.4" , SPV_ENV_VULKAN_1_1_SPIRV_1_4}, |
132 | {"vulkan1.0" , SPV_ENV_VULKAN_1_0}, |
133 | {"vulkan1.1" , SPV_ENV_VULKAN_1_1}, |
134 | {"vulkan1.2" , SPV_ENV_VULKAN_1_2}, |
135 | {"vulkan1.3" , SPV_ENV_VULKAN_1_3}, |
136 | {"spv1.0" , SPV_ENV_UNIVERSAL_1_0}, |
137 | {"spv1.1" , SPV_ENV_UNIVERSAL_1_1}, |
138 | {"spv1.2" , SPV_ENV_UNIVERSAL_1_2}, |
139 | {"spv1.3" , SPV_ENV_UNIVERSAL_1_3}, |
140 | {"spv1.4" , SPV_ENV_UNIVERSAL_1_4}, |
141 | {"spv1.5" , SPV_ENV_UNIVERSAL_1_5}, |
142 | {"spv1.6" , SPV_ENV_UNIVERSAL_1_6}, |
143 | {"opencl1.2embedded" , SPV_ENV_OPENCL_EMBEDDED_1_2}, |
144 | {"opencl1.2" , SPV_ENV_OPENCL_1_2}, |
145 | {"opencl2.0embedded" , SPV_ENV_OPENCL_EMBEDDED_2_0}, |
146 | {"opencl2.0" , SPV_ENV_OPENCL_2_0}, |
147 | {"opencl2.1embedded" , SPV_ENV_OPENCL_EMBEDDED_2_1}, |
148 | {"opencl2.1" , SPV_ENV_OPENCL_2_1}, |
149 | {"opencl2.2embedded" , SPV_ENV_OPENCL_EMBEDDED_2_2}, |
150 | {"opencl2.2" , SPV_ENV_OPENCL_2_2}, |
151 | {"opengl4.0" , SPV_ENV_OPENGL_4_0}, |
152 | {"opengl4.1" , SPV_ENV_OPENGL_4_1}, |
153 | {"opengl4.2" , SPV_ENV_OPENGL_4_2}, |
154 | {"opengl4.3" , SPV_ENV_OPENGL_4_3}, |
155 | {"opengl4.5" , SPV_ENV_OPENGL_4_5}, |
156 | }; |
157 | |
158 | bool spvParseTargetEnv(const char* s, spv_target_env* env) { |
159 | auto match = [s](const char* b) { |
160 | return s && (0 == strncmp(s, b, strlen(b))); |
161 | }; |
162 | for (auto& name_env : spvTargetEnvNameMap) { |
163 | if (match(name_env.first)) { |
164 | if (env) { |
165 | *env = name_env.second; |
166 | } |
167 | return true; |
168 | } |
169 | } |
170 | if (env) *env = SPV_ENV_UNIVERSAL_1_0; |
171 | return false; |
172 | } |
173 | |
174 | #define VULKAN_VER(MAJOR, MINOR) ((MAJOR << 22) | (MINOR << 12)) |
175 | #define SPIRV_VER(MAJOR, MINOR) ((MAJOR << 16) | (MINOR << 8)) |
176 | |
177 | struct VulkanEnv { |
178 | spv_target_env vulkan_env; |
179 | uint32_t vulkan_ver; |
180 | uint32_t spirv_ver; |
181 | }; |
182 | // Maps each Vulkan target environment enum to the Vulkan version, and the |
183 | // maximum supported SPIR-V version for that Vulkan environment. |
184 | // Keep this ordered from least capable to most capable. |
185 | static const VulkanEnv ordered_vulkan_envs[] = { |
186 | {SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)}, |
187 | {SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)}, |
188 | {SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)}, |
189 | {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}, |
190 | {SPV_ENV_VULKAN_1_3, VULKAN_VER(1, 3), SPIRV_VER(1, 6)}}; |
191 | |
192 | bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver, |
193 | spv_target_env* env) { |
194 | for (auto triple : ordered_vulkan_envs) { |
195 | if (triple.vulkan_ver >= vulkan_ver && triple.spirv_ver >= spirv_ver) { |
196 | *env = triple.vulkan_env; |
197 | return true; |
198 | } |
199 | } |
200 | return false; |
201 | } |
202 | |
203 | bool spvIsVulkanEnv(spv_target_env env) { |
204 | switch (env) { |
205 | case SPV_ENV_UNIVERSAL_1_0: |
206 | case SPV_ENV_OPENCL_1_2: |
207 | case SPV_ENV_OPENCL_EMBEDDED_1_2: |
208 | case SPV_ENV_OPENCL_2_0: |
209 | case SPV_ENV_OPENCL_EMBEDDED_2_0: |
210 | case SPV_ENV_OPENCL_2_1: |
211 | case SPV_ENV_OPENCL_EMBEDDED_2_1: |
212 | case SPV_ENV_OPENGL_4_0: |
213 | case SPV_ENV_OPENGL_4_1: |
214 | case SPV_ENV_OPENGL_4_2: |
215 | case SPV_ENV_OPENGL_4_3: |
216 | case SPV_ENV_OPENGL_4_5: |
217 | case SPV_ENV_UNIVERSAL_1_1: |
218 | case SPV_ENV_UNIVERSAL_1_2: |
219 | case SPV_ENV_OPENCL_2_2: |
220 | case SPV_ENV_OPENCL_EMBEDDED_2_2: |
221 | case SPV_ENV_UNIVERSAL_1_3: |
222 | case SPV_ENV_UNIVERSAL_1_4: |
223 | case SPV_ENV_UNIVERSAL_1_5: |
224 | case SPV_ENV_UNIVERSAL_1_6: |
225 | return false; |
226 | case SPV_ENV_VULKAN_1_0: |
227 | case SPV_ENV_VULKAN_1_1: |
228 | case SPV_ENV_VULKAN_1_1_SPIRV_1_4: |
229 | case SPV_ENV_VULKAN_1_2: |
230 | case SPV_ENV_VULKAN_1_3: |
231 | return true; |
232 | case SPV_ENV_WEBGPU_0: |
233 | assert(false && "Deprecated target environment value." ); |
234 | break; |
235 | case SPV_ENV_MAX: |
236 | assert(false && "Invalid target environment value." ); |
237 | break; |
238 | } |
239 | return false; |
240 | } |
241 | |
242 | bool spvIsOpenCLEnv(spv_target_env env) { |
243 | switch (env) { |
244 | case SPV_ENV_UNIVERSAL_1_0: |
245 | case SPV_ENV_VULKAN_1_0: |
246 | case SPV_ENV_UNIVERSAL_1_1: |
247 | case SPV_ENV_OPENGL_4_0: |
248 | case SPV_ENV_OPENGL_4_1: |
249 | case SPV_ENV_OPENGL_4_2: |
250 | case SPV_ENV_OPENGL_4_3: |
251 | case SPV_ENV_OPENGL_4_5: |
252 | case SPV_ENV_UNIVERSAL_1_2: |
253 | case SPV_ENV_UNIVERSAL_1_3: |
254 | case SPV_ENV_VULKAN_1_1: |
255 | case SPV_ENV_UNIVERSAL_1_4: |
256 | case SPV_ENV_VULKAN_1_1_SPIRV_1_4: |
257 | case SPV_ENV_UNIVERSAL_1_5: |
258 | case SPV_ENV_VULKAN_1_2: |
259 | case SPV_ENV_UNIVERSAL_1_6: |
260 | case SPV_ENV_VULKAN_1_3: |
261 | return false; |
262 | case SPV_ENV_OPENCL_1_2: |
263 | case SPV_ENV_OPENCL_EMBEDDED_1_2: |
264 | case SPV_ENV_OPENCL_2_0: |
265 | case SPV_ENV_OPENCL_EMBEDDED_2_0: |
266 | case SPV_ENV_OPENCL_EMBEDDED_2_1: |
267 | case SPV_ENV_OPENCL_EMBEDDED_2_2: |
268 | case SPV_ENV_OPENCL_2_1: |
269 | case SPV_ENV_OPENCL_2_2: |
270 | return true; |
271 | case SPV_ENV_WEBGPU_0: |
272 | assert(false && "Deprecated target environment value." ); |
273 | break; |
274 | case SPV_ENV_MAX: |
275 | assert(false && "Invalid target environment value." ); |
276 | break; |
277 | } |
278 | return false; |
279 | } |
280 | |
281 | bool spvIsOpenGLEnv(spv_target_env env) { |
282 | switch (env) { |
283 | case SPV_ENV_UNIVERSAL_1_0: |
284 | case SPV_ENV_VULKAN_1_0: |
285 | case SPV_ENV_UNIVERSAL_1_1: |
286 | case SPV_ENV_UNIVERSAL_1_2: |
287 | case SPV_ENV_UNIVERSAL_1_3: |
288 | case SPV_ENV_VULKAN_1_1: |
289 | case SPV_ENV_OPENCL_1_2: |
290 | case SPV_ENV_OPENCL_EMBEDDED_1_2: |
291 | case SPV_ENV_OPENCL_2_0: |
292 | case SPV_ENV_OPENCL_EMBEDDED_2_0: |
293 | case SPV_ENV_OPENCL_EMBEDDED_2_1: |
294 | case SPV_ENV_OPENCL_EMBEDDED_2_2: |
295 | case SPV_ENV_OPENCL_2_1: |
296 | case SPV_ENV_OPENCL_2_2: |
297 | case SPV_ENV_UNIVERSAL_1_4: |
298 | case SPV_ENV_VULKAN_1_1_SPIRV_1_4: |
299 | case SPV_ENV_UNIVERSAL_1_5: |
300 | case SPV_ENV_VULKAN_1_2: |
301 | case SPV_ENV_UNIVERSAL_1_6: |
302 | case SPV_ENV_VULKAN_1_3: |
303 | return false; |
304 | case SPV_ENV_OPENGL_4_0: |
305 | case SPV_ENV_OPENGL_4_1: |
306 | case SPV_ENV_OPENGL_4_2: |
307 | case SPV_ENV_OPENGL_4_3: |
308 | case SPV_ENV_OPENGL_4_5: |
309 | return true; |
310 | case SPV_ENV_WEBGPU_0: |
311 | assert(false && "Deprecated target environment value." ); |
312 | break; |
313 | case SPV_ENV_MAX: |
314 | assert(false && "Invalid target environment value." ); |
315 | break; |
316 | } |
317 | return false; |
318 | } |
319 | |
320 | bool spvIsValidEnv(spv_target_env env) { |
321 | switch (env) { |
322 | case SPV_ENV_UNIVERSAL_1_0: |
323 | case SPV_ENV_VULKAN_1_0: |
324 | case SPV_ENV_UNIVERSAL_1_1: |
325 | case SPV_ENV_UNIVERSAL_1_2: |
326 | case SPV_ENV_UNIVERSAL_1_3: |
327 | case SPV_ENV_VULKAN_1_1: |
328 | case SPV_ENV_OPENCL_1_2: |
329 | case SPV_ENV_OPENCL_EMBEDDED_1_2: |
330 | case SPV_ENV_OPENCL_2_0: |
331 | case SPV_ENV_OPENCL_EMBEDDED_2_0: |
332 | case SPV_ENV_OPENCL_EMBEDDED_2_1: |
333 | case SPV_ENV_OPENCL_EMBEDDED_2_2: |
334 | case SPV_ENV_OPENCL_2_1: |
335 | case SPV_ENV_OPENCL_2_2: |
336 | case SPV_ENV_UNIVERSAL_1_4: |
337 | case SPV_ENV_VULKAN_1_1_SPIRV_1_4: |
338 | case SPV_ENV_UNIVERSAL_1_5: |
339 | case SPV_ENV_VULKAN_1_2: |
340 | case SPV_ENV_UNIVERSAL_1_6: |
341 | case SPV_ENV_VULKAN_1_3: |
342 | case SPV_ENV_OPENGL_4_0: |
343 | case SPV_ENV_OPENGL_4_1: |
344 | case SPV_ENV_OPENGL_4_2: |
345 | case SPV_ENV_OPENGL_4_3: |
346 | case SPV_ENV_OPENGL_4_5: |
347 | return true; |
348 | case SPV_ENV_WEBGPU_0: |
349 | case SPV_ENV_MAX: |
350 | break; |
351 | } |
352 | return false; |
353 | } |
354 | |
355 | std::string spvLogStringForEnv(spv_target_env env) { |
356 | switch (env) { |
357 | case SPV_ENV_OPENCL_1_2: |
358 | case SPV_ENV_OPENCL_2_0: |
359 | case SPV_ENV_OPENCL_2_1: |
360 | case SPV_ENV_OPENCL_2_2: |
361 | case SPV_ENV_OPENCL_EMBEDDED_1_2: |
362 | case SPV_ENV_OPENCL_EMBEDDED_2_0: |
363 | case SPV_ENV_OPENCL_EMBEDDED_2_1: |
364 | case SPV_ENV_OPENCL_EMBEDDED_2_2: { |
365 | return "OpenCL" ; |
366 | } |
367 | case SPV_ENV_OPENGL_4_0: |
368 | case SPV_ENV_OPENGL_4_1: |
369 | case SPV_ENV_OPENGL_4_2: |
370 | case SPV_ENV_OPENGL_4_3: |
371 | case SPV_ENV_OPENGL_4_5: { |
372 | return "OpenGL" ; |
373 | } |
374 | case SPV_ENV_VULKAN_1_0: |
375 | case SPV_ENV_VULKAN_1_1: |
376 | case SPV_ENV_VULKAN_1_1_SPIRV_1_4: |
377 | case SPV_ENV_VULKAN_1_2: |
378 | case SPV_ENV_VULKAN_1_3: { |
379 | return "Vulkan" ; |
380 | } |
381 | case SPV_ENV_UNIVERSAL_1_0: |
382 | case SPV_ENV_UNIVERSAL_1_1: |
383 | case SPV_ENV_UNIVERSAL_1_2: |
384 | case SPV_ENV_UNIVERSAL_1_3: |
385 | case SPV_ENV_UNIVERSAL_1_4: |
386 | case SPV_ENV_UNIVERSAL_1_5: |
387 | case SPV_ENV_UNIVERSAL_1_6: { |
388 | return "Universal" ; |
389 | } |
390 | case SPV_ENV_WEBGPU_0: |
391 | assert(false && "Deprecated target environment value." ); |
392 | break; |
393 | case SPV_ENV_MAX: |
394 | assert(false && "Invalid target environment value." ); |
395 | break; |
396 | } |
397 | return "Unknown" ; |
398 | } |
399 | |
400 | std::string spvTargetEnvList(const int pad, const int wrap) { |
401 | std::string ret; |
402 | size_t max_line_len = wrap - pad; // The first line isn't padded |
403 | std::string line; |
404 | std::string sep = "" ; |
405 | |
406 | for (auto& name_env : spvTargetEnvNameMap) { |
407 | std::string word = sep + name_env.first; |
408 | if (line.length() + word.length() > max_line_len) { |
409 | // Adding one word wouldn't fit, commit the line in progress and |
410 | // start a new one. |
411 | ret += line + "\n" ; |
412 | line.assign(pad, ' '); |
413 | // The first line is done. The max length now comprises the |
414 | // padding. |
415 | max_line_len = wrap; |
416 | } |
417 | line += word; |
418 | sep = "|" ; |
419 | } |
420 | |
421 | ret += line; |
422 | |
423 | return ret; |
424 | } |
425 | |