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