| 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 | |
| 23 | const 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 | |
| 77 | uint32_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 | |
| 113 | static 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 | |
| 140 | bool 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 | |
| 159 | struct 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. |
| 167 | static 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 | |
| 173 | bool 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 | |
| 184 | bool 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 | |
| 216 | bool 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 | |
| 248 | bool 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 | |
| 280 | bool 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 | |
| 312 | bool spvIsVulkanOrWebGPUEnv(spv_target_env env) { |
| 313 | return spvIsVulkanEnv(env) || spvIsWebGPUEnv(env); |
| 314 | } |
| 315 | |
| 316 | std::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 | |
| 356 | std::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 | |