blob: 3f30ee9366968eb4d9bceac5bd83dacda35f40bd [file] [log] [blame]
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001/*
Hans-Kristian Arntzen47044822021-01-14 16:07:49 +01002 * Copyright 2015-2021 Arm Limited
Jon Leechf2a65542021-05-08 01:47:48 -07003 * SPDX-License-Identifier: Apache-2.0 OR MIT
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
Hans-Kristian Arntzencf1e9e02020-11-25 15:22:08 +010018/*
19 * At your option, you may choose to accept this material under either:
20 * 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
21 * 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
Hans-Kristian Arntzencf1e9e02020-11-25 15:22:08 +010022 */
23
Hans-Kristian Arntzen147e53a2016-04-04 09:36:04 +020024#include "spirv_cross.hpp"
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010025#include "GLSL.std.450.h"
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +010026#include "spirv_cfg.hpp"
Hans-Kristian Arntzene2c95bd2019-06-21 12:44:33 +020027#include "spirv_common.hpp"
Hans-Kristian Arntzenb4e01632019-06-21 16:02:22 +020028#include "spirv_parser.hpp"
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010029#include <algorithm>
Hans-Kristian Arntzen5ea59bd2016-05-23 13:30:02 +020030#include <cstring>
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010031#include <utility>
32
33using namespace std;
34using namespace spv;
Hans-Kristian Arntzen9b92e682019-03-29 10:29:44 +010035using namespace SPIRV_CROSS_NAMESPACE;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010036
Hans-Kristian Arntzen3fe57d32019-04-09 12:46:23 +020037Compiler::Compiler(vector<uint32_t> ir_)
Hans-Kristian Arntzenfd432f82017-03-18 10:52:41 +010038{
Daniel Thornburgh44c33332022-03-02 23:02:38 +000039 Parser parser(std::move(ir_));
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020040 parser.parse();
Daniel Thornburgh44c33332022-03-02 23:02:38 +000041 set_ir(std::move(parser.get_parsed_ir()));
Hans-Kristian Arntzenfd432f82017-03-18 10:52:41 +010042}
43
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020044Compiler::Compiler(const uint32_t *ir_, size_t word_count)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010045{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020046 Parser parser(ir_, word_count);
47 parser.parse();
Daniel Thornburgh44c33332022-03-02 23:02:38 +000048 set_ir(std::move(parser.get_parsed_ir()));
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010049}
50
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020051Compiler::Compiler(const ParsedIR &ir_)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010052{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020053 set_ir(ir_);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010054}
55
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020056Compiler::Compiler(ParsedIR &&ir_)
Yuriy O'Donnellae8de512017-04-01 12:31:34 +020057{
Daniel Thornburgh44c33332022-03-02 23:02:38 +000058 set_ir(std::move(ir_));
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020059}
60
61void Compiler::set_ir(ParsedIR &&ir_)
62{
Daniel Thornburgh44c33332022-03-02 23:02:38 +000063 ir = std::move(ir_);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020064 parse_fixup();
65}
66
67void Compiler::set_ir(const ParsedIR &ir_)
68{
69 ir = ir_;
70 parse_fixup();
Yuriy O'Donnellae8de512017-04-01 12:31:34 +020071}
72
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +020073string Compiler::compile()
74{
75 return "";
76}
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010077
78bool Compiler::variable_storage_is_aliased(const SPIRVariable &v)
79{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +020080 auto &type = get<SPIRType>(v.basetype);
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +020081 bool ssbo = v.storage == StorageClassStorageBuffer ||
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020082 ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +020083 bool image = type.basetype == SPIRType::Image;
84 bool counter = type.basetype == SPIRType::AtomicCounter;
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +020085 bool buffer_reference = type.storage == StorageClassPhysicalStorageBufferEXT;
Hans-Kristian Arntzen7eba2472018-05-11 10:14:20 +020086
87 bool is_restrict;
88 if (ssbo)
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +020089 is_restrict = ir.get_buffer_block_flags(v).get(DecorationRestrict);
Hans-Kristian Arntzen7eba2472018-05-11 10:14:20 +020090 else
91 is_restrict = has_decoration(v.self, DecorationRestrict);
92
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +020093 return !is_restrict && (ssbo || image || counter || buffer_reference);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +010094}
95
96bool Compiler::block_is_pure(const SPIRBlock &block)
97{
Hans-Kristian Arntzenbfa76ee2019-09-12 14:21:10 +020098 // This is a global side effect of the function.
Hans-Kristian Arntzen2097c302021-01-08 11:37:29 +010099 if (block.terminator == SPIRBlock::Kill ||
100 block.terminator == SPIRBlock::TerminateRay ||
Hans-Kristian Arntzen4c345162022-09-05 12:31:22 +0200101 block.terminator == SPIRBlock::IgnoreIntersection ||
102 block.terminator == SPIRBlock::EmitMeshTasks)
Hans-Kristian Arntzenbfa76ee2019-09-12 14:21:10 +0200103 return false;
104
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200105 for (auto &i : block.ops)
106 {
107 auto ops = stream(i);
108 auto op = static_cast<Op>(i.op);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100109
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200110 switch (op)
111 {
112 case OpFunctionCall:
113 {
114 uint32_t func = ops[2];
115 if (!function_is_pure(get<SPIRFunction>(func)))
116 return false;
117 break;
118 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100119
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +0100120 case OpCopyMemory:
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200121 case OpStore:
122 {
123 auto &type = expression_type(ops[0]);
124 if (type.storage != StorageClassFunction)
125 return false;
126 break;
127 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100128
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200129 case OpImageWrite:
130 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100131
Hans-Kristian Arntzen5af1a512016-05-05 09:51:42 +0200132 // Atomics are impure.
133 case OpAtomicLoad:
134 case OpAtomicStore:
135 case OpAtomicExchange:
136 case OpAtomicCompareExchange:
Bill Hollings8f6df772017-05-19 18:14:08 -0400137 case OpAtomicCompareExchangeWeak:
Hans-Kristian Arntzen5af1a512016-05-05 09:51:42 +0200138 case OpAtomicIIncrement:
139 case OpAtomicIDecrement:
140 case OpAtomicIAdd:
141 case OpAtomicISub:
142 case OpAtomicSMin:
143 case OpAtomicUMin:
144 case OpAtomicSMax:
145 case OpAtomicUMax:
146 case OpAtomicAnd:
147 case OpAtomicOr:
148 case OpAtomicXor:
149 return false;
150
151 // Geometry shader builtins modify global state.
152 case OpEndPrimitive:
153 case OpEmitStreamVertex:
154 case OpEndStreamPrimitive:
155 case OpEmitVertex:
156 return false;
157
Hans-Kristian Arntzen57626172022-09-02 16:31:04 +0200158 // Mesh shader functions modify global state.
Hans-Kristian Arntzen4c345162022-09-05 12:31:22 +0200159 // (EmitMeshTasks is a terminator).
Hans-Kristian Arntzen57626172022-09-02 16:31:04 +0200160 case OpSetMeshOutputsEXT:
161 return false;
162
Hans-Kristian Arntzen5af1a512016-05-05 09:51:42 +0200163 // Barriers disallow any reordering, so we should treat blocks with barrier as writing.
164 case OpControlBarrier:
165 case OpMemoryBarrier:
166 return false;
167
Patrick Mours90c91e42019-03-26 14:16:38 +0100168 // Ray tracing builtins are impure.
Hans-Kristian Arntzen2097c302021-01-08 11:37:29 +0100169 case OpReportIntersectionKHR:
Patrick Mours90c91e42019-03-26 14:16:38 +0100170 case OpIgnoreIntersectionNV:
171 case OpTerminateRayNV:
172 case OpTraceNV:
Hans-Kristian Arntzen2097c302021-01-08 11:37:29 +0100173 case OpTraceRayKHR:
Patrick Mours90c91e42019-03-26 14:16:38 +0100174 case OpExecuteCallableNV:
Hans-Kristian Arntzen2097c302021-01-08 11:37:29 +0100175 case OpExecuteCallableKHR:
Hans-Kristian Arntzen5b227cc2021-07-19 13:36:37 +0200176 case OpRayQueryInitializeKHR:
177 case OpRayQueryTerminateKHR:
178 case OpRayQueryGenerateIntersectionKHR:
179 case OpRayQueryConfirmIntersectionKHR:
180 case OpRayQueryProceedKHR:
181 // There are various getters in ray query, but they are considered pure.
Patrick Mours90c91e42019-03-26 14:16:38 +0100182 return false;
183
Hans-Kristian Arntzence18d4c2017-11-17 13:38:29 +0100184 // OpExtInst is potentially impure depending on extension, but GLSL builtins are at least pure.
Hans-Kristian Arntzen5af1a512016-05-05 09:51:42 +0200185
Hans-Kristian Arntzenbfa76ee2019-09-12 14:21:10 +0200186 case OpDemoteToHelperInvocationEXT:
187 // This is a global side effect of the function.
188 return false;
189
Hans-Kristian Arntzen4561ecd2021-11-07 11:27:20 +0100190 case OpExtInst:
191 {
192 uint32_t extension_set = ops[2];
193 if (get<SPIRExtension>(extension_set).ext == SPIRExtension::GLSL)
194 {
195 auto op_450 = static_cast<GLSLstd450>(ops[3]);
196 switch (op_450)
197 {
198 case GLSLstd450Modf:
199 case GLSLstd450Frexp:
200 {
201 auto &type = expression_type(ops[5]);
202 if (type.storage != StorageClassFunction)
203 return false;
204 break;
205 }
206
207 default:
208 break;
209 }
210 }
211 break;
212 }
213
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200214 default:
215 break;
216 }
217 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100218
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200219 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100220}
221
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100222string Compiler::to_name(uint32_t id, bool allow_alias) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100223{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100224 if (allow_alias && ir.ids[id].get_type() == TypeType)
Hans-Kristian Arntzenf05373b2016-05-23 10:57:22 +0200225 {
226 // If this type is a simple alias, emit the
227 // name of the original type instead.
228 // We don't want to override the meta alias
229 // as that can be overridden by the reflection APIs after parse.
230 auto &type = get<SPIRType>(id);
231 if (type.type_alias)
Hans-Kristian Arntzen294259e2018-03-05 16:27:04 +0100232 {
233 // If the alias master has been specially packed, we will have emitted a clean variant as well,
234 // so skip the name aliasing here.
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +0200235 if (!has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked))
Hans-Kristian Arntzen294259e2018-03-05 16:27:04 +0100236 return to_name(type.type_alias);
237 }
Hans-Kristian Arntzenf05373b2016-05-23 10:57:22 +0200238 }
239
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100240 auto &alias = ir.get_name(id);
241 if (alias.empty())
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200242 return join("_", id);
243 else
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100244 return alias;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100245}
246
247bool Compiler::function_is_pure(const SPIRFunction &func)
248{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200249 for (auto block : func.blocks)
250 {
251 if (!block_is_pure(get<SPIRBlock>(block)))
252 {
253 //fprintf(stderr, "Function %s is impure!\n", to_name(func.self).c_str());
254 return false;
255 }
256 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100257
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200258 //fprintf(stderr, "Function %s is pure!\n", to_name(func.self).c_str());
259 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100260}
261
262void Compiler::register_global_read_dependencies(const SPIRBlock &block, uint32_t id)
263{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200264 for (auto &i : block.ops)
265 {
266 auto ops = stream(i);
267 auto op = static_cast<Op>(i.op);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100268
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200269 switch (op)
270 {
271 case OpFunctionCall:
272 {
273 uint32_t func = ops[2];
274 register_global_read_dependencies(get<SPIRFunction>(func), id);
275 break;
276 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100277
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200278 case OpLoad:
279 case OpImageRead:
280 {
281 // If we're in a storage class which does not get invalidated, adding dependencies here is no big deal.
282 auto *var = maybe_get_backing_variable(ops[2]);
283 if (var && var->storage != StorageClassFunction)
284 {
285 auto &type = get<SPIRType>(var->basetype);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100286
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200287 // InputTargets are immutable.
288 if (type.basetype != SPIRType::Image && type.image.dim != DimSubpassData)
289 var->dependees.push_back(id);
290 }
291 break;
292 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100293
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200294 default:
295 break;
296 }
297 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100298}
299
300void Compiler::register_global_read_dependencies(const SPIRFunction &func, uint32_t id)
301{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200302 for (auto block : func.blocks)
303 register_global_read_dependencies(get<SPIRBlock>(block), id);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100304}
305
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200306SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100307{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200308 auto *var = maybe_get<SPIRVariable>(chain);
309 if (!var)
310 {
311 auto *cexpr = maybe_get<SPIRExpression>(chain);
312 if (cexpr)
313 var = maybe_get<SPIRVariable>(cexpr->loaded_from);
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +0200314
315 auto *access_chain = maybe_get<SPIRAccessChain>(chain);
316 if (access_chain)
317 var = maybe_get<SPIRVariable>(access_chain->loaded_from);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200318 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100319
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200320 return var;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100321}
322
323void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
324{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200325 auto &e = get<SPIRExpression>(expr);
326 auto *var = maybe_get_backing_variable(chain);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100327
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200328 if (var)
329 {
330 e.loaded_from = var->self;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100331
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200332 // If the backing variable is immutable, we do not need to depend on the variable.
333 if (forwarded && !is_immutable(var->self))
334 var->dependees.push_back(e.self);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100335
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200336 // If we load from a parameter, make sure we create "inout" if we also write to the parameter.
337 // The default is "in" however, so we never invalidate our compilation by reading.
338 if (var && var->parameter)
339 var->parameter->read_count++;
340 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100341}
342
343void Compiler::register_write(uint32_t chain)
344{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200345 auto *var = maybe_get<SPIRVariable>(chain);
346 if (!var)
347 {
348 // If we're storing through an access chain, invalidate the backing variable instead.
349 auto *expr = maybe_get<SPIRExpression>(chain);
350 if (expr && expr->loaded_from)
351 var = maybe_get<SPIRVariable>(expr->loaded_from);
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +0200352
353 auto *access_chain = maybe_get<SPIRAccessChain>(chain);
354 if (access_chain && access_chain->loaded_from)
355 var = maybe_get<SPIRVariable>(access_chain->loaded_from);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200356 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100357
Hans-Kristian Arntzencf725b42020-01-06 12:29:44 +0100358 auto &chain_type = expression_type(chain);
359
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200360 if (var)
361 {
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +0200362 bool check_argument_storage_qualifier = true;
363 auto &type = expression_type(chain);
364
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200365 // If our variable is in a storage class which can alias with other buffers,
Chip Davisd6aa9112019-01-08 15:16:17 -0600366 // invalidate all variables which depend on aliased variables. And if this is a
367 // variable pointer, then invalidate all variables regardless.
368 if (get_variable_data_type(*var).pointer)
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +0200369 {
Chip Davisd6aa9112019-01-08 15:16:17 -0600370 flush_all_active_variables();
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +0200371
372 if (type.pointer_depth == 1)
373 {
374 // We have a backing variable which is a pointer-to-pointer type.
375 // We are storing some data through a pointer acquired through that variable,
376 // but we are not writing to the value of the variable itself,
377 // i.e., we are not modifying the pointer directly.
378 // If we are storing a non-pointer type (pointer_depth == 1),
379 // we know that we are storing some unrelated data.
380 // A case here would be
381 // void foo(Foo * const *arg) {
382 // Foo *bar = *arg;
383 // bar->unrelated = 42;
384 // }
385 // arg, the argument is constant.
386 check_argument_storage_qualifier = false;
387 }
388 }
389
390 if (type.storage == StorageClassPhysicalStorageBufferEXT || variable_storage_is_aliased(*var))
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200391 flush_all_aliased_variables();
392 else if (var)
393 flush_dependees(*var);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100394
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200395 // We tried to write to a parameter which is not marked with out qualifier, force a recompile.
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +0200396 if (check_argument_storage_qualifier && var->parameter && var->parameter->write_count == 0)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200397 {
398 var->parameter->write_count++;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +0200399 force_recompile();
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200400 }
401 }
Hans-Kristian Arntzencf725b42020-01-06 12:29:44 +0100402 else if (chain_type.pointer)
Chip Davisd6aa9112019-01-08 15:16:17 -0600403 {
404 // If we stored through a variable pointer, then we don't know which
405 // variable we stored to. So *all* expressions after this point need to
406 // be invalidated.
407 // FIXME: If we can prove that the variable pointer will point to
408 // only certain variables, we can invalidate only those.
409 flush_all_active_variables();
410 }
Hans-Kristian Arntzencf725b42020-01-06 12:29:44 +0100411
412 // If chain_type.pointer is false, we're not writing to memory backed variables, but temporaries instead.
413 // This can happen in copy_logical_type where we unroll complex reads and writes to temporaries.
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100414}
415
416void Compiler::flush_dependees(SPIRVariable &var)
417{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200418 for (auto expr : var.dependees)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200419 invalid_expressions.insert(expr);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200420 var.dependees.clear();
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100421}
422
423void Compiler::flush_all_aliased_variables()
424{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200425 for (auto aliased : aliased_variables)
426 flush_dependees(get<SPIRVariable>(aliased));
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100427}
428
429void Compiler::flush_all_atomic_capable_variables()
430{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200431 for (auto global : global_variables)
432 flush_dependees(get<SPIRVariable>(global));
433 flush_all_aliased_variables();
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100434}
435
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +0100436void Compiler::flush_control_dependent_expressions(uint32_t block_id)
437{
438 auto &block = get<SPIRBlock>(block_id);
439 for (auto &expr : block.invalidate_expressions)
440 invalid_expressions.insert(expr);
441 block.invalidate_expressions.clear();
442}
443
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100444void Compiler::flush_all_active_variables()
445{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200446 // Invalidate all temporaries we read from variables in this block since they were forwarded.
447 // Invalidate all temporaries we read from globals.
448 for (auto &v : current_function->local_variables)
449 flush_dependees(get<SPIRVariable>(v));
450 for (auto &arg : current_function->arguments)
451 flush_dependees(get<SPIRVariable>(arg.id));
452 for (auto global : global_variables)
453 flush_dependees(get<SPIRVariable>(global));
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100454
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200455 flush_all_aliased_variables();
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100456}
457
Bill Hollings1e84a372017-08-12 00:21:13 -0400458uint32_t Compiler::expression_type_id(uint32_t id) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100459{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +0200460 switch (ir.ids[id].get_type())
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200461 {
462 case TypeVariable:
Bill Hollings1e84a372017-08-12 00:21:13 -0400463 return get<SPIRVariable>(id).basetype;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100464
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200465 case TypeExpression:
Bill Hollings1e84a372017-08-12 00:21:13 -0400466 return get<SPIRExpression>(id).expression_type;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100467
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200468 case TypeConstant:
Bill Hollings1e84a372017-08-12 00:21:13 -0400469 return get<SPIRConstant>(id).constant_type;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100470
Hans-Kristian Arntzen7e8afa82016-10-03 15:54:02 +0200471 case TypeConstantOp:
Bill Hollings1e84a372017-08-12 00:21:13 -0400472 return get<SPIRConstantOp>(id).basetype;
Hans-Kristian Arntzen7e8afa82016-10-03 15:54:02 +0200473
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200474 case TypeUndef:
Bill Hollings1e84a372017-08-12 00:21:13 -0400475 return get<SPIRUndef>(id).basetype;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100476
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +0200477 case TypeCombinedImageSampler:
Bill Hollings1e84a372017-08-12 00:21:13 -0400478 return get<SPIRCombinedImageSampler>(id).combined_type;
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +0200479
Hans-Kristian Arntzen3cbdbec2017-08-10 15:36:30 +0200480 case TypeAccessChain:
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +0200481 return get<SPIRAccessChain>(id).basetype;
Hans-Kristian Arntzen3cbdbec2017-08-10 15:36:30 +0200482
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200483 default:
Panagiotis Christopoulos Charitos946f7792016-12-12 22:33:22 +0100484 SPIRV_CROSS_THROW("Cannot resolve expression type.");
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200485 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100486}
487
Bill Hollings1e84a372017-08-12 00:21:13 -0400488const SPIRType &Compiler::expression_type(uint32_t id) const
489{
490 return get<SPIRType>(expression_type_id(id));
491}
492
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100493bool Compiler::expression_is_lvalue(uint32_t id) const
494{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200495 auto &type = expression_type(id);
496 switch (type.basetype)
497 {
498 case SPIRType::SampledImage:
499 case SPIRType::Image:
500 case SPIRType::Sampler:
501 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100502
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200503 default:
504 return true;
505 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100506}
507
508bool Compiler::is_immutable(uint32_t id) const
509{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +0200510 if (ir.ids[id].get_type() == TypeVariable)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200511 {
512 auto &var = get<SPIRVariable>(id);
Hans-Kristian Arntzen92134e42016-04-01 19:58:26 +0200513
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200514 // Anything we load from the UniformConstant address space is guaranteed to be immutable.
515 bool pointer_to_const = var.storage == StorageClassUniformConstant;
Hans-Kristian Arntzen36a0b632016-07-12 14:33:04 +0200516 return pointer_to_const || var.phi_variable || !expression_is_lvalue(id);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200517 }
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +0200518 else if (ir.ids[id].get_type() == TypeAccessChain)
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +0200519 return get<SPIRAccessChain>(id).immutable;
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +0200520 else if (ir.ids[id].get_type() == TypeExpression)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200521 return get<SPIRExpression>(id).immutable;
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +0200522 else if (ir.ids[id].get_type() == TypeConstant || ir.ids[id].get_type() == TypeConstantOp ||
523 ir.ids[id].get_type() == TypeUndef)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200524 return true;
525 else
526 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100527}
528
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200529static inline bool storage_class_is_interface(spv::StorageClass storage)
530{
531 switch (storage)
532 {
533 case StorageClassInput:
534 case StorageClassOutput:
535 case StorageClassUniform:
536 case StorageClassUniformConstant:
537 case StorageClassAtomicCounter:
538 case StorageClassPushConstant:
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +0200539 case StorageClassStorageBuffer:
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200540 return true;
541
542 default:
543 return false;
544 }
545}
546
547bool Compiler::is_hidden_variable(const SPIRVariable &var, bool include_builtins) const
548{
549 if ((is_builtin_variable(var) && !include_builtins) || var.remapped_variable)
550 return true;
551
Hans-Kristian Arntzen1b5ca8d2016-09-10 16:20:19 +0200552 // Combined image samplers are always considered active as they are "magic" variables.
553 if (find_if(begin(combined_image_samplers), end(combined_image_samplers), [&var](const CombinedImageSampler &samp) {
554 return samp.combined_id == var.self;
Hans-Kristian Arntzence18d4c2017-11-17 13:38:29 +0100555 }) != end(combined_image_samplers))
Hans-Kristian Arntzen1b5ca8d2016-09-10 16:20:19 +0200556 {
557 return false;
558 }
559
Hans-Kristian Arntzenea02a0c2021-01-22 13:48:16 +0100560 // In SPIR-V 1.4 and up we must also use the active variable interface to disable global variables
561 // which are not part of the entry point.
562 if (ir.get_spirv_version() >= 0x10400 && var.storage != spv::StorageClassGeneric &&
563 var.storage != spv::StorageClassFunction && !interface_variable_exists_in_entry_point(var.self))
564 {
565 return true;
566 }
567
568 return check_active_interface_variables && storage_class_is_interface(var.storage) &&
569 active_interface_variables.find(var.self) == end(active_interface_variables);
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200570}
571
Hans-Kristian Arntzend3100602018-09-13 14:42:05 +0200572bool Compiler::is_builtin_type(const SPIRType &type) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100573{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100574 auto *type_meta = ir.find_meta(type.self);
575
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200576 // We can have builtin structs as well. If one member of a struct is builtin, the struct must also be builtin.
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100577 if (type_meta)
578 for (auto &m : type_meta->members)
579 if (m.builtin)
580 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100581
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200582 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100583}
584
Hans-Kristian Arntzend3100602018-09-13 14:42:05 +0200585bool Compiler::is_builtin_variable(const SPIRVariable &var) const
586{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100587 auto *m = ir.find_meta(var.self);
588
589 if (var.compat_builtin || (m && m->decoration.builtin))
Hans-Kristian Arntzend3100602018-09-13 14:42:05 +0200590 return true;
591 else
592 return is_builtin_type(get<SPIRType>(var.basetype));
593}
594
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100595bool Compiler::is_member_builtin(const SPIRType &type, uint32_t index, BuiltIn *builtin) const
596{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100597 auto *type_meta = ir.find_meta(type.self);
598
599 if (type_meta)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200600 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100601 auto &memb = type_meta->members;
602 if (index < memb.size() && memb[index].builtin)
603 {
604 if (builtin)
605 *builtin = memb[index].builtin_type;
606 return true;
607 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200608 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100609
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200610 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100611}
612
Bill Hollings103aabf2016-04-06 17:42:27 -0400613bool Compiler::is_scalar(const SPIRType &type) const
614{
Hans-Kristian Arntzen64ca1ec2019-01-16 16:16:39 +0100615 return type.basetype != SPIRType::Struct && type.vecsize == 1 && type.columns == 1;
Bill Hollings103aabf2016-04-06 17:42:27 -0400616}
617
618bool Compiler::is_vector(const SPIRType &type) const
619{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200620 return type.vecsize > 1 && type.columns == 1;
Bill Hollings103aabf2016-04-06 17:42:27 -0400621}
622
623bool Compiler::is_matrix(const SPIRType &type) const
624{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200625 return type.vecsize > 1 && type.columns > 1;
Bill Hollings103aabf2016-04-06 17:42:27 -0400626}
627
Bill Hollingsf591bc02017-06-30 19:10:46 -0400628bool Compiler::is_array(const SPIRType &type) const
629{
630 return !type.array.empty();
631}
632
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100633ShaderResources Compiler::get_shader_resources() const
634{
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200635 return get_shader_resources(nullptr);
636}
637
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +0200638ShaderResources Compiler::get_shader_resources(const unordered_set<VariableID> &active_variables) const
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200639{
640 return get_shader_resources(&active_variables);
641}
642
643bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
644{
645 uint32_t variable = 0;
646 switch (opcode)
647 {
648 // Need this first, otherwise, GCC complains about unhandled switch statements.
649 default:
650 break;
651
652 case OpFunctionCall:
653 {
654 // Invalid SPIR-V.
655 if (length < 3)
656 return false;
657
658 uint32_t count = length - 3;
659 args += 3;
660 for (uint32_t i = 0; i < count; i++)
661 {
662 auto *var = compiler.maybe_get<SPIRVariable>(args[i]);
663 if (var && storage_class_is_interface(var->storage))
664 variables.insert(args[i]);
665 }
666 break;
667 }
668
Chip Davis3bfb2f92018-12-03 02:06:33 -0600669 case OpSelect:
670 {
671 // Invalid SPIR-V.
672 if (length < 5)
673 return false;
674
675 uint32_t count = length - 3;
676 args += 3;
677 for (uint32_t i = 0; i < count; i++)
678 {
679 auto *var = compiler.maybe_get<SPIRVariable>(args[i]);
680 if (var && storage_class_is_interface(var->storage))
681 variables.insert(args[i]);
682 }
683 break;
684 }
685
686 case OpPhi:
687 {
688 // Invalid SPIR-V.
689 if (length < 2)
690 return false;
691
692 uint32_t count = length - 2;
693 args += 2;
694 for (uint32_t i = 0; i < count; i += 2)
695 {
696 auto *var = compiler.maybe_get<SPIRVariable>(args[i]);
697 if (var && storage_class_is_interface(var->storage))
698 variables.insert(args[i]);
699 }
700 break;
701 }
702
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200703 case OpAtomicStore:
704 case OpStore:
705 // Invalid SPIR-V.
706 if (length < 1)
707 return false;
708 variable = args[0];
709 break;
710
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +0100711 case OpCopyMemory:
712 {
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +0100713 if (length < 2)
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +0100714 return false;
715
716 auto *var = compiler.maybe_get<SPIRVariable>(args[0]);
717 if (var && storage_class_is_interface(var->storage))
Chip Davis01c49162019-04-26 13:46:18 -0500718 variables.insert(args[0]);
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +0100719
720 var = compiler.maybe_get<SPIRVariable>(args[1]);
721 if (var && storage_class_is_interface(var->storage))
Chip Davis01c49162019-04-26 13:46:18 -0500722 variables.insert(args[1]);
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +0100723 break;
724 }
725
Hans-Kristian Arntzenbcdff2d2017-11-22 19:27:03 +0100726 case OpExtInst:
727 {
728 if (length < 5)
729 return false;
Chip Davisaca9b682020-11-02 20:56:46 -0600730 auto &extension_set = compiler.get<SPIRExtension>(args[2]);
731 switch (extension_set.ext)
732 {
733 case SPIRExtension::GLSL:
734 {
735 auto op = static_cast<GLSLstd450>(args[3]);
736
737 switch (op)
738 {
739 case GLSLstd450InterpolateAtCentroid:
740 case GLSLstd450InterpolateAtSample:
741 case GLSLstd450InterpolateAtOffset:
742 {
743 auto *var = compiler.maybe_get<SPIRVariable>(args[4]);
744 if (var && storage_class_is_interface(var->storage))
745 variables.insert(args[4]);
746 break;
747 }
748
Hans-Kristian Arntzen4561ecd2021-11-07 11:27:20 +0100749 case GLSLstd450Modf:
750 case GLSLstd450Fract:
751 {
752 auto *var = compiler.maybe_get<SPIRVariable>(args[5]);
753 if (var && storage_class_is_interface(var->storage))
754 variables.insert(args[5]);
755 break;
756 }
757
Chip Davisaca9b682020-11-02 20:56:46 -0600758 default:
759 break;
760 }
761 break;
762 }
763 case SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter:
Hans-Kristian Arntzenbcdff2d2017-11-22 19:27:03 +0100764 {
765 enum AMDShaderExplicitVertexParameter
766 {
767 InterpolateAtVertexAMD = 1
768 };
769
770 auto op = static_cast<AMDShaderExplicitVertexParameter>(args[3]);
771
772 switch (op)
773 {
774 case InterpolateAtVertexAMD:
775 {
776 auto *var = compiler.maybe_get<SPIRVariable>(args[4]);
777 if (var && storage_class_is_interface(var->storage))
778 variables.insert(args[4]);
779 break;
780 }
781
782 default:
783 break;
784 }
Chip Davisaca9b682020-11-02 20:56:46 -0600785 break;
786 }
787 default:
788 break;
Hans-Kristian Arntzenbcdff2d2017-11-22 19:27:03 +0100789 }
790 break;
791 }
792
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200793 case OpAccessChain:
794 case OpInBoundsAccessChain:
Chip Davis3bfb2f92018-12-03 02:06:33 -0600795 case OpPtrAccessChain:
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200796 case OpLoad:
Hans-Kristian Arntzen0c9683c2016-11-18 09:59:54 +0100797 case OpCopyObject:
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200798 case OpImageTexelPointer:
799 case OpAtomicLoad:
800 case OpAtomicExchange:
801 case OpAtomicCompareExchange:
Bill Hollings8f6df772017-05-19 18:14:08 -0400802 case OpAtomicCompareExchangeWeak:
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200803 case OpAtomicIIncrement:
804 case OpAtomicIDecrement:
805 case OpAtomicIAdd:
806 case OpAtomicISub:
807 case OpAtomicSMin:
808 case OpAtomicUMin:
809 case OpAtomicSMax:
810 case OpAtomicUMax:
811 case OpAtomicAnd:
812 case OpAtomicOr:
813 case OpAtomicXor:
Hans-Kristian Arntzen42e64592019-05-27 16:44:02 +0200814 case OpArrayLength:
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200815 // Invalid SPIR-V.
816 if (length < 3)
817 return false;
818 variable = args[2];
819 break;
820 }
821
822 if (variable)
823 {
824 auto *var = compiler.maybe_get<SPIRVariable>(variable);
825 if (var && storage_class_is_interface(var->storage))
826 variables.insert(variable);
827 }
828 return true;
829}
830
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +0200831unordered_set<VariableID> Compiler::get_active_interface_variables() const
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200832{
833 // Traverse the call graph and find all interface variables which are in use.
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +0200834 unordered_set<VariableID> variables;
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200835 InterfaceVariableAccessHandler handler(*this, variables);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +0200836 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Hans-Kristian Arntzen1a2e4de2018-02-21 13:43:16 +0100837
Hans-Kristian Arntzen3e098792019-01-30 10:29:08 +0100838 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
Hans-Kristian Arntzena4a9b532021-01-07 11:20:10 +0100839 if (var.storage != StorageClassOutput)
840 return;
841 if (!interface_variable_exists_in_entry_point(var.self))
842 return;
843
844 // An output variable which is just declared (but uninitialized) might be read by subsequent stages
845 // so we should force-enable these outputs,
846 // since compilation will fail if a subsequent stage attempts to read from the variable in question.
847 // Also, make sure we preserve output variables which are only initialized, but never accessed by any code.
848 if (var.initializer != ID(0) || get_execution_model() != ExecutionModelFragment)
Hans-Kristian Arntzen3e098792019-01-30 10:29:08 +0100849 variables.insert(var.self);
850 });
851
Hans-Kristian Arntzen1a2e4de2018-02-21 13:43:16 +0100852 // If we needed to create one, we'll need it.
853 if (dummy_sampler_id)
854 variables.insert(dummy_sampler_id);
855
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200856 return variables;
857}
858
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +0200859void Compiler::set_enabled_interface_variables(std::unordered_set<VariableID> active_variables)
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200860{
Daniel Thornburgh44c33332022-03-02 23:02:38 +0000861 active_interface_variables = std::move(active_variables);
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200862 check_active_interface_variables = true;
863}
864
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +0200865ShaderResources Compiler::get_shader_resources(const unordered_set<VariableID> *active_variables) const
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200866{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200867 ShaderResources res;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100868
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +0200869 bool ssbo_instance_name = reflection_ssbo_instance_name_is_significant();
870
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +0100871 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +0100872 auto &type = this->get<SPIRType>(var.basetype);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100873
Hans-Kristian Arntzend5dc5f32016-07-05 13:21:26 +0200874 // It is possible for uniform storage classes to be passed as function parameters, so detect
875 // that. To detect function parameters, check of StorageClass of variable is function scope.
Hans-Kristian Arntzenb4a380a2021-04-16 13:32:37 +0200876 if (var.storage == StorageClassFunction || !type.pointer)
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +0100877 return;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +0100878
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200879 if (active_variables && active_variables->find(var.self) == end(*active_variables))
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +0100880 return;
Hans-Kristian Arntzenf61a5d12016-08-26 12:58:50 +0200881
Hans-Kristian Arntzen852f2da2021-04-16 12:50:18 +0200882 // In SPIR-V 1.4 and up, every global must be present in the entry point interface list,
883 // not just IO variables.
884 bool active_in_entry_point = true;
885 if (ir.get_spirv_version() < 0x10400)
886 {
887 if (var.storage == StorageClassInput || var.storage == StorageClassOutput)
888 active_in_entry_point = interface_variable_exists_in_entry_point(var.self);
889 }
890 else
891 active_in_entry_point = interface_variable_exists_in_entry_point(var.self);
892
893 if (!active_in_entry_point)
894 return;
895
Hans-Kristian Arntzenb4a380a2021-04-16 13:32:37 +0200896 bool is_builtin = is_builtin_variable(var);
897
898 if (is_builtin)
899 {
900 if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
901 return;
902
903 auto &list = var.storage == StorageClassInput ? res.builtin_inputs : res.builtin_outputs;
904 BuiltInResource resource;
905
906 if (has_decoration(type.self, DecorationBlock))
907 {
908 resource.resource = { var.self, var.basetype, type.self,
909 get_remapped_declared_block_name(var.self, false) };
910
911 for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
912 {
913 resource.value_type_id = type.member_types[i];
914 resource.builtin = BuiltIn(get_member_decoration(type.self, i, DecorationBuiltIn));
915 list.push_back(resource);
916 }
917 }
918 else
919 {
920 bool strip_array =
921 !has_decoration(var.self, DecorationPatch) && (
922 get_execution_model() == ExecutionModelTessellationControl ||
923 (get_execution_model() == ExecutionModelTessellationEvaluation &&
924 var.storage == StorageClassInput));
925
926 resource.resource = { var.self, var.basetype, type.self, get_name(var.self) };
927
928 if (strip_array && !type.array.empty())
929 resource.value_type_id = get_variable_data_type(var).parent_type;
930 else
931 resource.value_type_id = get_variable_data_type_id(var);
932
933 assert(resource.value_type_id);
934
935 resource.builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
936 list.push_back(std::move(resource));
937 }
938 return;
939 }
940
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200941 // Input
Hans-Kristian Arntzen852f2da2021-04-16 12:50:18 +0200942 if (var.storage == StorageClassInput)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200943 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100944 if (has_decoration(type.self, DecorationBlock))
945 {
Hans-Kristian Arntzenaa2557c2017-12-05 09:58:12 +0100946 res.stage_inputs.push_back(
Hans-Kristian Arntzenb4a380a2021-04-16 13:32:37 +0200947 { var.self, var.basetype, type.self,
948 get_remapped_declared_block_name(var.self, false) });
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100949 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200950 else
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100951 res.stage_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200952 }
953 // Subpass inputs
954 else if (var.storage == StorageClassUniformConstant && type.image.dim == DimSubpassData)
955 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100956 res.subpass_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200957 }
958 // Outputs
Hans-Kristian Arntzen852f2da2021-04-16 12:50:18 +0200959 else if (var.storage == StorageClassOutput)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200960 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100961 if (has_decoration(type.self, DecorationBlock))
962 {
Hans-Kristian Arntzenaa2557c2017-12-05 09:58:12 +0100963 res.stage_outputs.push_back(
Hans-Kristian Arntzenb4a380a2021-04-16 13:32:37 +0200964 { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) });
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100965 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200966 else
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100967 res.stage_outputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200968 }
969 // UBOs
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100970 else if (type.storage == StorageClassUniform && has_decoration(type.self, DecorationBlock))
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200971 {
Hans-Kristian Arntzenaa2557c2017-12-05 09:58:12 +0100972 res.uniform_buffers.push_back(
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +0200973 { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200974 }
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +0200975 // Old way to declare SSBOs.
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100976 else if (type.storage == StorageClassUniform && has_decoration(type.self, DecorationBufferBlock))
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200977 {
Hans-Kristian Arntzenaa2557c2017-12-05 09:58:12 +0100978 res.storage_buffers.push_back(
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +0200979 { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, ssbo_instance_name) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200980 }
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +0200981 // Modern way to declare SSBOs.
982 else if (type.storage == StorageClassStorageBuffer)
983 {
Hans-Kristian Arntzenaa2557c2017-12-05 09:58:12 +0100984 res.storage_buffers.push_back(
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +0200985 { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, ssbo_instance_name) });
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +0200986 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200987 // Push constant blocks
988 else if (type.storage == StorageClassPushConstant)
989 {
990 // There can only be one push constant block, but keep the vector in case this restriction is lifted
991 // in the future.
Hans-Kristian Arntzen6e1c3cc2019-01-11 12:56:00 +0100992 res.push_constant_buffers.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200993 }
994 // Images
Hans-Kristian Arntzene9202082016-09-10 13:05:35 +0200995 else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Image &&
996 type.image.sampled == 2)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200997 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +0100998 res.storage_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +0200999 }
Hans-Kristian Arntzene9202082016-09-10 13:05:35 +02001000 // Separate images
1001 else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Image &&
1002 type.image.sampled == 1)
1003 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001004 res.separate_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzene9202082016-09-10 13:05:35 +02001005 }
1006 // Separate samplers
1007 else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Sampler)
1008 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001009 res.separate_samplers.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzene9202082016-09-10 13:05:35 +02001010 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001011 // Textures
1012 else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::SampledImage)
1013 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001014 res.sampled_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001015 }
1016 // Atomic counters
1017 else if (type.storage == StorageClassAtomicCounter)
1018 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001019 res.atomic_counters.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001020 }
Patrick Moursb2a66752019-03-26 15:02:00 +01001021 // Acceleration structures
Hans-Kristian Arntzen6b0e5582020-04-21 14:25:18 +02001022 else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::AccelerationStructure)
Patrick Moursb2a66752019-03-26 15:02:00 +01001023 {
1024 res.acceleration_structures.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
1025 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001026 });
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001027
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001028 return res;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001029}
1030
Hans-Kristian Arntzen294259e2018-03-05 16:27:04 +01001031bool Compiler::type_is_block_like(const SPIRType &type) const
1032{
1033 if (type.basetype != SPIRType::Struct)
1034 return false;
1035
Hans-Kristian Arntzenac0e93f2018-03-07 10:29:20 +01001036 if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))
Hans-Kristian Arntzen294259e2018-03-05 16:27:04 +01001037 {
1038 return true;
1039 }
1040
1041 // Block-like types may have Offset decorations.
1042 for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
1043 if (has_member_decoration(type.self, i, DecorationOffset))
1044 return true;
1045
1046 return false;
1047}
1048
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001049void Compiler::parse_fixup()
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001050{
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001051 // Figure out specialization constants for work group sizes.
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001052 for (auto id_ : ir.ids_for_constant_or_variable)
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001053 {
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001054 auto &id = ir.ids[id_];
1055
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001056 if (id.get_type() == TypeConstant)
1057 {
1058 auto &c = id.get<SPIRConstant>();
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01001059 if (has_decoration(c.self, DecorationBuiltIn) &&
1060 BuiltIn(get_decoration(c.self, DecorationBuiltIn)) == BuiltInWorkgroupSize)
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001061 {
1062 // In current SPIR-V, there can be just one constant like this.
1063 // All entry points will receive the constant value.
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01001064 // WorkgroupSize take precedence over LocalSizeId.
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001065 for (auto &entry : ir.entry_points)
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001066 {
1067 entry.second.workgroup_size.constant = c.self;
1068 entry.second.workgroup_size.x = c.scalar(0, 0);
1069 entry.second.workgroup_size.y = c.scalar(0, 1);
1070 entry.second.workgroup_size.z = c.scalar(0, 2);
1071 }
1072 }
1073 }
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001074 else if (id.get_type() == TypeVariable)
1075 {
1076 auto &var = id.get<SPIRVariable>();
1077 if (var.storage == StorageClassPrivate || var.storage == StorageClassWorkgroup ||
Hans-Kristian Arntzen57626172022-09-02 16:31:04 +02001078 var.storage == StorageClassTaskPayloadWorkgroupEXT ||
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001079 var.storage == StorageClassOutput)
Hans-Kristian Arntzen57626172022-09-02 16:31:04 +02001080 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001081 global_variables.push_back(var.self);
Hans-Kristian Arntzen57626172022-09-02 16:31:04 +02001082 }
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001083 if (variable_storage_is_aliased(var))
1084 aliased_variables.push_back(var.self);
1085 }
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001086 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001087}
1088
Hans-Kristian Arntzen5b876222019-01-07 10:01:28 +01001089void Compiler::update_name_cache(unordered_set<string> &cache_primary, const unordered_set<string> &cache_secondary,
1090 string &name)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001091{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001092 if (name.empty())
1093 return;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001094
Hans-Kristian Arntzen9728f9c2019-01-04 12:15:43 +01001095 const auto find_name = [&](const string &n) -> bool {
1096 if (cache_primary.find(n) != end(cache_primary))
1097 return true;
1098
1099 if (&cache_primary != &cache_secondary)
1100 if (cache_secondary.find(n) != end(cache_secondary))
1101 return true;
1102
1103 return false;
1104 };
1105
Hans-Kristian Arntzen5b876222019-01-07 10:01:28 +01001106 const auto insert_name = [&](const string &n) { cache_primary.insert(n); };
Hans-Kristian Arntzen9728f9c2019-01-04 12:15:43 +01001107
1108 if (!find_name(name))
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001109 {
Hans-Kristian Arntzen9728f9c2019-01-04 12:15:43 +01001110 insert_name(name);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001111 return;
1112 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001113
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001114 uint32_t counter = 0;
1115 auto tmpname = name;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001116
Hans-Kristian Arntzen6bcc8902018-06-04 10:13:57 +02001117 bool use_linked_underscore = true;
1118
1119 if (tmpname == "_")
1120 {
1121 // We cannot just append numbers, as we will end up creating internally reserved names.
1122 // Make it like _0_<counter> instead.
1123 tmpname += "0";
1124 }
1125 else if (tmpname.back() == '_')
1126 {
1127 // The last_character is an underscore, so we don't need to link in underscore.
1128 // This would violate double underscore rules.
1129 use_linked_underscore = false;
1130 }
1131
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001132 // If there is a collision (very rare),
1133 // keep tacking on extra identifier until it's unique.
1134 do
1135 {
1136 counter++;
Hans-Kristian Arntzen6bcc8902018-06-04 10:13:57 +02001137 name = tmpname + (use_linked_underscore ? "_" : "") + convert_to_string(counter);
Hans-Kristian Arntzen9728f9c2019-01-04 12:15:43 +01001138 } while (find_name(name));
1139 insert_name(name);
1140}
1141
1142void Compiler::update_name_cache(unordered_set<string> &cache, string &name)
1143{
1144 update_name_cache(cache, cache, name);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001145}
1146
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001147void Compiler::set_name(ID id, const std::string &name)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001148{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001149 ir.set_name(id, name);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001150}
1151
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001152const SPIRType &Compiler::get_type(TypeID id) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001153{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001154 return get<SPIRType>(id);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001155}
1156
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001157const SPIRType &Compiler::get_type_from_variable(VariableID id) const
Robert Konrad451bdee2016-09-24 22:17:01 +02001158{
1159 return get<SPIRType>(get<SPIRVariable>(id).basetype);
1160}
1161
Chip Davisfc02b3d2019-01-08 12:54:40 -06001162uint32_t Compiler::get_pointee_type_id(uint32_t type_id) const
Bill Hollingse0910312018-06-24 15:06:12 -04001163{
1164 auto *p_type = &get<SPIRType>(type_id);
Chip Davis3bfb2f92018-12-03 02:06:33 -06001165 if (p_type->pointer)
Bill Hollingse0910312018-06-24 15:06:12 -04001166 {
1167 assert(p_type->parent_type);
1168 type_id = p_type->parent_type;
Bill Hollingse0910312018-06-24 15:06:12 -04001169 }
1170 return type_id;
1171}
1172
Chip Davisfc02b3d2019-01-08 12:54:40 -06001173const SPIRType &Compiler::get_pointee_type(const SPIRType &type) const
Bill Hollingse0910312018-06-24 15:06:12 -04001174{
1175 auto *p_type = &type;
Chip Davis3bfb2f92018-12-03 02:06:33 -06001176 if (p_type->pointer)
Bill Hollingse0910312018-06-24 15:06:12 -04001177 {
1178 assert(p_type->parent_type);
1179 p_type = &get<SPIRType>(p_type->parent_type);
1180 }
1181 return *p_type;
1182}
1183
Chip Davisfc02b3d2019-01-08 12:54:40 -06001184const SPIRType &Compiler::get_pointee_type(uint32_t type_id) const
Bill Hollingse0910312018-06-24 15:06:12 -04001185{
Chip Davisfc02b3d2019-01-08 12:54:40 -06001186 return get_pointee_type(get<SPIRType>(type_id));
Bill Hollingse0910312018-06-24 15:06:12 -04001187}
1188
Chip Davis3bfb2f92018-12-03 02:06:33 -06001189uint32_t Compiler::get_variable_data_type_id(const SPIRVariable &var) const
1190{
1191 if (var.phi_variable)
1192 return var.basetype;
Chip Davisfc02b3d2019-01-08 12:54:40 -06001193 return get_pointee_type_id(var.basetype);
Chip Davis3bfb2f92018-12-03 02:06:33 -06001194}
1195
1196SPIRType &Compiler::get_variable_data_type(const SPIRVariable &var)
1197{
1198 return get<SPIRType>(get_variable_data_type_id(var));
1199}
1200
1201const SPIRType &Compiler::get_variable_data_type(const SPIRVariable &var) const
1202{
1203 return get<SPIRType>(get_variable_data_type_id(var));
1204}
1205
Chip Daviseb89c3a2019-02-03 23:58:46 -06001206SPIRType &Compiler::get_variable_element_type(const SPIRVariable &var)
1207{
1208 SPIRType *type = &get_variable_data_type(var);
1209 if (is_array(*type))
1210 type = &get<SPIRType>(type->parent_type);
1211 return *type;
1212}
1213
1214const SPIRType &Compiler::get_variable_element_type(const SPIRVariable &var) const
1215{
1216 const SPIRType *type = &get_variable_data_type(var);
1217 if (is_array(*type))
1218 type = &get<SPIRType>(type->parent_type);
1219 return *type;
1220}
1221
Chip Davis8855ea02018-09-24 12:24:58 -05001222bool Compiler::is_sampled_image_type(const SPIRType &type)
1223{
Hans-Kristian Arntzenc07c3032018-09-27 13:36:38 +02001224 return (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage) && type.image.sampled == 1 &&
1225 type.image.dim != DimBuffer;
Chip Davis8855ea02018-09-24 12:24:58 -05001226}
1227
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001228void Compiler::set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration,
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01001229 const std::string &argument)
1230{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001231 ir.set_member_decoration_string(id, index, decoration, argument);
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01001232}
1233
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001234void Compiler::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001235{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001236 ir.set_member_decoration(id, index, decoration, argument);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001237}
1238
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001239void Compiler::set_member_name(TypeID id, uint32_t index, const std::string &name)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001240{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001241 ir.set_member_name(id, index, name);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001242}
1243
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001244const std::string &Compiler::get_member_name(TypeID id, uint32_t index) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001245{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001246 return ir.get_member_name(id, index);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001247}
1248
Hans-Kristian Arntzene47a77d2019-03-14 10:29:34 +01001249void Compiler::set_qualified_name(uint32_t id, const string &name)
1250{
1251 ir.meta[id].decoration.qualified_alias = name;
1252}
1253
Bill Hollings1e84a372017-08-12 00:21:13 -04001254void Compiler::set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name)
Bill Hollingsc45e74f2016-07-08 12:39:22 -04001255{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001256 ir.meta[type_id].members.resize(max(ir.meta[type_id].members.size(), size_t(index) + 1));
1257 ir.meta[type_id].members[index].qualified_alias = name;
Bill Hollingsc1b81542017-05-22 21:41:19 -04001258}
1259
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001260const string &Compiler::get_member_qualified_name(TypeID type_id, uint32_t index) const
Bill Hollingsc1b81542017-05-22 21:41:19 -04001261{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001262 auto *m = ir.find_meta(type_id);
1263 if (m && index < m->members.size())
1264 return m->members[index].qualified_alias;
Bill Hollings1e84a372017-08-12 00:21:13 -04001265 else
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001266 return ir.get_empty_string();
Bill Hollingsc45e74f2016-07-08 12:39:22 -04001267}
1268
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001269uint32_t Compiler::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001270{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001271 return ir.get_member_decoration(id, index, decoration);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001272}
1273
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001274const Bitset &Compiler::get_member_decoration_bitset(TypeID id, uint32_t index) const
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01001275{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001276 return ir.get_member_decoration_bitset(id, index);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001277}
1278
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001279bool Compiler::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const
Bill Hollings484931d2017-02-28 21:44:36 -05001280{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001281 return ir.has_member_decoration(id, index, decoration);
Bill Hollings484931d2017-02-28 21:44:36 -05001282}
1283
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001284void Compiler::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001285{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001286 ir.unset_member_decoration(id, index, decoration);
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01001287}
1288
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001289void Compiler::set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument)
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01001290{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001291 ir.set_decoration_string(id, decoration, argument);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001292}
1293
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001294void Compiler::set_decoration(ID id, Decoration decoration, uint32_t argument)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001295{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001296 ir.set_decoration(id, decoration, argument);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001297}
1298
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001299void Compiler::set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value)
1300{
1301 auto &dec = ir.meta[id].decoration;
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001302 dec.extended.flags.set(decoration);
1303 dec.extended.values[decoration] = value;
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001304}
1305
Hans-Kristian Arntzen40e77232019-01-17 11:29:50 +01001306void Compiler::set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration,
1307 uint32_t value)
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001308{
1309 ir.meta[type].members.resize(max(ir.meta[type].members.size(), size_t(index) + 1));
1310 auto &dec = ir.meta[type].members[index];
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001311 dec.extended.flags.set(decoration);
1312 dec.extended.values[decoration] = value;
1313}
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001314
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001315static uint32_t get_default_extended_decoration(ExtendedDecorations decoration)
1316{
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001317 switch (decoration)
1318 {
Hans-Kristian Arntzene2c95bd2019-06-21 12:44:33 +02001319 case SPIRVCrossDecorationResourceIndexPrimary:
Hans-Kristian Arntzene2c95bd2019-06-21 12:44:33 +02001320 case SPIRVCrossDecorationResourceIndexSecondary:
Chip Davis39dce882019-08-02 15:11:19 -05001321 case SPIRVCrossDecorationResourceIndexTertiary:
1322 case SPIRVCrossDecorationResourceIndexQuaternary:
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001323 case SPIRVCrossDecorationInterfaceMemberIndex:
1324 return ~(0u);
1325
1326 default:
1327 return 0;
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001328 }
1329}
1330
1331uint32_t Compiler::get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const
1332{
1333 auto *m = ir.find_meta(id);
1334 if (!m)
1335 return 0;
1336
1337 auto &dec = m->decoration;
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001338
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001339 if (!dec.extended.flags.get(decoration))
1340 return get_default_extended_decoration(decoration);
Chip Daviseb89c3a2019-02-03 23:58:46 -06001341
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001342 return dec.extended.values[decoration];
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001343}
1344
1345uint32_t Compiler::get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const
1346{
1347 auto *m = ir.find_meta(type);
1348 if (!m)
1349 return 0;
1350
1351 if (index >= m->members.size())
1352 return 0;
1353
1354 auto &dec = m->members[index];
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001355 if (!dec.extended.flags.get(decoration))
1356 return get_default_extended_decoration(decoration);
1357 return dec.extended.values[decoration];
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001358}
1359
1360bool Compiler::has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const
1361{
1362 auto *m = ir.find_meta(id);
1363 if (!m)
1364 return false;
1365
1366 auto &dec = m->decoration;
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001367 return dec.extended.flags.get(decoration);
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001368}
1369
1370bool Compiler::has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const
1371{
1372 auto *m = ir.find_meta(type);
1373 if (!m)
1374 return false;
1375
1376 if (index >= m->members.size())
1377 return false;
1378
1379 auto &dec = m->members[index];
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001380 return dec.extended.flags.get(decoration);
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001381}
1382
1383void Compiler::unset_extended_decoration(uint32_t id, ExtendedDecorations decoration)
1384{
1385 auto &dec = ir.meta[id].decoration;
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001386 dec.extended.flags.clear(decoration);
1387 dec.extended.values[decoration] = 0;
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001388}
1389
1390void Compiler::unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration)
1391{
1392 ir.meta[type].members.resize(max(ir.meta[type].members.size(), size_t(index) + 1));
1393 auto &dec = ir.meta[type].members[index];
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02001394 dec.extended.flags.clear(decoration);
1395 dec.extended.values[decoration] = 0;
Hans-Kristian Arntzende7e5cc2019-01-17 11:22:24 +01001396}
1397
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001398StorageClass Compiler::get_storage_class(VariableID id) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001399{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001400 return get<SPIRVariable>(id).storage;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001401}
1402
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001403const std::string &Compiler::get_name(ID id) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001404{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001405 return ir.get_name(id);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001406}
1407
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001408const std::string Compiler::get_fallback_name(ID id) const
Hans-Kristian Arntzenaab31072017-09-29 12:16:53 +02001409{
1410 return join("_", id);
1411}
1412
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001413const std::string Compiler::get_block_fallback_name(VariableID id) const
Hans-Kristian Arntzenaab31072017-09-29 12:16:53 +02001414{
1415 auto &var = get<SPIRVariable>(id);
Hans-Kristian Arntzen2c90ea32017-12-01 14:20:51 +01001416 if (get_name(id).empty())
1417 return join("_", get<SPIRType>(var.basetype).self, "_", id);
1418 else
1419 return get_name(id);
Hans-Kristian Arntzenaab31072017-09-29 12:16:53 +02001420}
1421
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001422const Bitset &Compiler::get_decoration_bitset(ID id) const
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01001423{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001424 return ir.get_decoration_bitset(id);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001425}
1426
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001427bool Compiler::has_decoration(ID id, Decoration decoration) const
Bill Hollings484931d2017-02-28 21:44:36 -05001428{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001429 return ir.has_decoration(id, decoration);
Bill Hollings484931d2017-02-28 21:44:36 -05001430}
1431
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001432const string &Compiler::get_decoration_string(ID id, Decoration decoration) const
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01001433{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001434 return ir.get_decoration_string(id, decoration);
1435}
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01001436
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001437const string &Compiler::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001438{
1439 return ir.get_member_decoration_string(id, index, decoration);
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01001440}
1441
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001442uint32_t Compiler::get_decoration(ID id, Decoration decoration) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001443{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001444 return ir.get_decoration(id, decoration);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001445}
1446
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001447void Compiler::unset_decoration(ID id, Decoration decoration)
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001448{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02001449 ir.unset_decoration(id, decoration);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001450}
1451
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001452bool Compiler::get_binary_offset_for_decoration(VariableID id, spv::Decoration decoration, uint32_t &word_offset) const
Hans-Kristian Arntzen93fe19c2017-04-25 20:10:24 +02001453{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001454 auto *m = ir.find_meta(id);
1455 if (!m)
1456 return false;
1457
1458 auto &word_offsets = m->decoration_word_offset;
Hans-Kristian Arntzen93fe19c2017-04-25 20:10:24 +02001459 auto itr = word_offsets.find(decoration);
1460 if (itr == end(word_offsets))
1461 return false;
1462
1463 word_offset = itr->second;
1464 return true;
1465}
1466
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001467bool Compiler::block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const
1468{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001469 // Tried and failed.
1470 if (block.disable_block_optimization || block.complex_continue)
1471 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001472
Hans-Kristian Arntzen28cccc32018-03-08 17:51:55 +01001473 if (method == SPIRBlock::MergeToSelectForLoop || method == SPIRBlock::MergeToSelectContinueForLoop)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001474 {
1475 // Try to detect common for loop pattern
1476 // which the code backend can use to create cleaner code.
1477 // for(;;) { if (cond) { some_body; } else { break; } }
1478 // is the pattern we're looking for.
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001479 const auto *false_block = maybe_get<SPIRBlock>(block.false_block);
1480 const auto *true_block = maybe_get<SPIRBlock>(block.true_block);
1481 const auto *merge_block = maybe_get<SPIRBlock>(block.merge_block);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001482
Hans-Kristian Arntzen8bfb04d2019-03-06 12:20:13 +01001483 bool false_block_is_merge = block.false_block == block.merge_block ||
1484 (false_block && merge_block && execution_is_noop(*false_block, *merge_block));
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001485
Hans-Kristian Arntzen8bfb04d2019-03-06 12:20:13 +01001486 bool true_block_is_merge = block.true_block == block.merge_block ||
1487 (true_block && merge_block && execution_is_noop(*true_block, *merge_block));
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001488
Hans-Kristian Arntzen8bfb04d2019-03-06 12:20:13 +01001489 bool positive_candidate =
1490 block.true_block != block.merge_block && block.true_block != block.self && false_block_is_merge;
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001491
Hans-Kristian Arntzen8bfb04d2019-03-06 12:20:13 +01001492 bool negative_candidate =
1493 block.false_block != block.merge_block && block.false_block != block.self && true_block_is_merge;
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001494
1495 bool ret = block.terminator == SPIRBlock::Select && block.merge == SPIRBlock::MergeLoop &&
1496 (positive_candidate || negative_candidate);
1497
1498 if (ret && positive_candidate && method == SPIRBlock::MergeToSelectContinueForLoop)
Hans-Kristian Arntzen28cccc32018-03-08 17:51:55 +01001499 ret = block.true_block == block.continue_block;
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001500 else if (ret && negative_candidate && method == SPIRBlock::MergeToSelectContinueForLoop)
1501 ret = block.false_block == block.continue_block;
Hans-Kristian Arntzen28cccc32018-03-08 17:51:55 +01001502
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001503 // If we have OpPhi which depends on branches which came from our own block,
1504 // we need to flush phi variables in else block instead of a trivial break,
1505 // so we cannot assume this is a for loop candidate.
1506 if (ret)
1507 {
1508 for (auto &phi : block.phi_variables)
1509 if (phi.parent == block.self)
1510 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001511
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001512 auto *merge = maybe_get<SPIRBlock>(block.merge_block);
1513 if (merge)
1514 for (auto &phi : merge->phi_variables)
1515 if (phi.parent == block.self)
1516 return false;
1517 }
1518 return ret;
1519 }
1520 else if (method == SPIRBlock::MergeToDirectForLoop)
1521 {
1522 // Empty loop header that just sets up merge target
1523 // and branches to loop body.
1524 bool ret = block.terminator == SPIRBlock::Direct && block.merge == SPIRBlock::MergeLoop && block.ops.empty();
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001525
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001526 if (!ret)
1527 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001528
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001529 auto &child = get<SPIRBlock>(block.next_block);
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001530
1531 const auto *false_block = maybe_get<SPIRBlock>(child.false_block);
1532 const auto *true_block = maybe_get<SPIRBlock>(child.true_block);
1533 const auto *merge_block = maybe_get<SPIRBlock>(block.merge_block);
1534
Hans-Kristian Arntzen8bfb04d2019-03-06 12:20:13 +01001535 bool false_block_is_merge = child.false_block == block.merge_block ||
1536 (false_block && merge_block && execution_is_noop(*false_block, *merge_block));
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001537
Hans-Kristian Arntzen8bfb04d2019-03-06 12:20:13 +01001538 bool true_block_is_merge = child.true_block == block.merge_block ||
1539 (true_block && merge_block && execution_is_noop(*true_block, *merge_block));
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001540
Hans-Kristian Arntzen8bfb04d2019-03-06 12:20:13 +01001541 bool positive_candidate =
1542 child.true_block != block.merge_block && child.true_block != block.self && false_block_is_merge;
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001543
Hans-Kristian Arntzen8bfb04d2019-03-06 12:20:13 +01001544 bool negative_candidate =
1545 child.false_block != block.merge_block && child.false_block != block.self && true_block_is_merge;
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001546
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001547 ret = child.terminator == SPIRBlock::Select && child.merge == SPIRBlock::MergeNone &&
Hans-Kristian Arntzen70ff96b2019-03-06 11:24:43 +01001548 (positive_candidate || negative_candidate);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001549
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001550 // If we have OpPhi which depends on branches which came from our own block,
1551 // we need to flush phi variables in else block instead of a trivial break,
1552 // so we cannot assume this is a for loop candidate.
1553 if (ret)
1554 {
1555 for (auto &phi : block.phi_variables)
1556 if (phi.parent == block.self || phi.parent == child.self)
1557 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001558
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001559 for (auto &phi : child.phi_variables)
1560 if (phi.parent == block.self)
1561 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001562
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001563 auto *merge = maybe_get<SPIRBlock>(block.merge_block);
1564 if (merge)
1565 for (auto &phi : merge->phi_variables)
1566 if (phi.parent == block.self || phi.parent == child.false_block)
1567 return false;
1568 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001569
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001570 return ret;
1571 }
1572 else
1573 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001574}
1575
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001576bool Compiler::execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const
1577{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001578 if (!execution_is_branchless(from, to))
1579 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001580
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001581 auto *start = &from;
1582 for (;;)
1583 {
1584 if (start->self == to.self)
1585 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001586
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001587 if (!start->ops.empty())
1588 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001589
Hans-Kristian Arntzen40bb42f2018-08-06 13:28:01 +02001590 auto &next = get<SPIRBlock>(start->next_block);
1591 // Flushing phi variables does not count as noop.
1592 for (auto &phi : next.phi_variables)
1593 if (phi.parent == start->self)
1594 return false;
1595
1596 start = &next;
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001597 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001598}
1599
1600bool Compiler::execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const
1601{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001602 auto *start = &from;
1603 for (;;)
1604 {
1605 if (start->self == to.self)
1606 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001607
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001608 if (start->terminator == SPIRBlock::Direct && start->merge == SPIRBlock::MergeNone)
1609 start = &get<SPIRBlock>(start->next_block);
1610 else
1611 return false;
1612 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001613}
1614
Hans-Kristian Arntzenc365cc12019-06-21 11:16:51 +02001615bool Compiler::execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const
1616{
1617 return from.terminator == SPIRBlock::Direct && from.merge == SPIRBlock::MergeNone && from.next_block == to.self;
1618}
1619
Hans-Kristian Arntzen926916d2016-05-05 09:15:25 +02001620SPIRBlock::ContinueBlockType Compiler::continue_block_type(const SPIRBlock &block) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001621{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001622 // The block was deemed too complex during code emit, pick conservative fallback paths.
1623 if (block.complex_continue)
1624 return SPIRBlock::ComplexLoop;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001625
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001626 // In older glslang output continue block can be equal to the loop header.
1627 // In this case, execution is clearly branchless, so just assume a while loop header here.
1628 if (block.merge == SPIRBlock::MergeLoop)
1629 return SPIRBlock::WhileLoop;
Hans-Kristian Arntzen97f81ba2016-04-01 12:37:29 +02001630
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001631 if (block.loop_dominator == BlockID(SPIRBlock::NoDominator))
Hans-Kristian Arntzenbf56dc82019-06-06 12:17:46 +02001632 {
1633 // Continue block is never reached from CFG.
1634 return SPIRBlock::ComplexLoop;
1635 }
1636
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001637 auto &dominator = get<SPIRBlock>(block.loop_dominator);
Hans-Kristian Arntzen97f81ba2016-04-01 12:37:29 +02001638
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001639 if (execution_is_noop(block, dominator))
1640 return SPIRBlock::WhileLoop;
1641 else if (execution_is_branchless(block, dominator))
1642 return SPIRBlock::ForLoop;
1643 else
1644 {
Hans-Kristian Arntzenef243372019-03-06 12:17:38 +01001645 const auto *false_block = maybe_get<SPIRBlock>(block.false_block);
1646 const auto *true_block = maybe_get<SPIRBlock>(block.true_block);
1647 const auto *merge_block = maybe_get<SPIRBlock>(dominator.merge_block);
1648
Hans-Kristian Arntzene06efb72019-07-25 12:30:50 +02001649 // If we need to flush Phi in this block, we cannot have a DoWhile loop.
1650 bool flush_phi_to_false = false_block && flush_phi_required(block.self, block.false_block);
1651 bool flush_phi_to_true = true_block && flush_phi_required(block.self, block.true_block);
1652 if (flush_phi_to_false || flush_phi_to_true)
1653 return SPIRBlock::ComplexLoop;
1654
Hans-Kristian Arntzenef243372019-03-06 12:17:38 +01001655 bool positive_do_while = block.true_block == dominator.self &&
1656 (block.false_block == dominator.merge_block ||
1657 (false_block && merge_block && execution_is_noop(*false_block, *merge_block)));
1658
1659 bool negative_do_while = block.false_block == dominator.self &&
1660 (block.true_block == dominator.merge_block ||
1661 (true_block && merge_block && execution_is_noop(*true_block, *merge_block)));
1662
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001663 if (block.merge == SPIRBlock::MergeNone && block.terminator == SPIRBlock::Select &&
Hans-Kristian Arntzenef243372019-03-06 12:17:38 +01001664 (positive_do_while || negative_do_while))
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001665 {
1666 return SPIRBlock::DoWhileLoop;
1667 }
1668 else
1669 return SPIRBlock::ComplexLoop;
1670 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001671}
1672
Sebastián Aedo75e37522021-11-12 10:17:38 -03001673const SmallVector<SPIRBlock::Case> &Compiler::get_case_list(const SPIRBlock &block) const
Sebastián Aedo48046642021-11-08 15:18:13 -03001674{
Sebastián Aedo53450512021-11-13 14:13:30 -03001675 uint32_t width = 0;
1676
1677 // First we check if we can get the type directly from the block.condition
1678 // since it can be a SPIRConstant or a SPIRVariable.
1679 if (const auto *constant = maybe_get<SPIRConstant>(block.condition))
Sebastián Aedo48046642021-11-08 15:18:13 -03001680 {
Sebastián Aedo53450512021-11-13 14:13:30 -03001681 const auto &type = get<SPIRType>(constant->constant_type);
1682 width = type.width;
1683 }
1684 else if (const auto *var = maybe_get<SPIRVariable>(block.condition))
1685 {
1686 const auto &type = get<SPIRType>(var->basetype);
1687 width = type.width;
1688 }
Hans-Kristian Arntzen29cc1892022-02-16 11:49:24 +01001689 else if (const auto *undef = maybe_get<SPIRUndef>(block.condition))
1690 {
1691 const auto &type = get<SPIRType>(undef->basetype);
1692 width = type.width;
1693 }
Sebastián Aedo53450512021-11-13 14:13:30 -03001694 else
1695 {
1696 auto search = ir.load_type_width.find(block.condition);
1697 if (search == ir.load_type_width.end())
1698 {
1699 SPIRV_CROSS_THROW("Use of undeclared variable on a switch statement.");
1700 }
1701
1702 width = search->second;
Sebastián Aedof099d712021-11-02 17:17:13 -03001703 }
1704
Sebastián Aedo250a0292021-11-03 16:12:14 -03001705 if (width > 32)
Sebastián Aedo75e37522021-11-12 10:17:38 -03001706 return block.cases_64bit;
1707
1708 return block.cases_32bit;
Sebastián Aedof099d712021-11-02 17:17:13 -03001709}
1710
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001711bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const
1712{
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01001713 handler.set_current_block(block);
Hans-Kristian Arntzen36c433b2019-09-04 11:42:29 +02001714 handler.rearm_current_block(block);
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01001715
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001716 // Ideally, perhaps traverse the CFG instead of all blocks in order to eliminate dead blocks,
1717 // but this shouldn't be a problem in practice unless the SPIR-V is doing insane things like recursing
1718 // inside dead blocks ...
1719 for (auto &i : block.ops)
1720 {
1721 auto ops = stream(i);
1722 auto op = static_cast<Op>(i.op);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001723
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001724 if (!handler.handle(op, ops, i.length))
1725 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001726
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02001727 if (op == OpFunctionCall)
1728 {
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01001729 auto &func = get<SPIRFunction>(ops[2]);
1730 if (handler.follow_function_call(func))
1731 {
1732 if (!handler.begin_function_scope(ops, i.length))
1733 return false;
1734 if (!traverse_all_reachable_opcodes(get<SPIRFunction>(ops[2]), handler))
1735 return false;
1736 if (!handler.end_function_scope(ops, i.length))
1737 return false;
Hans-Kristian Arntzen36c433b2019-09-04 11:42:29 +02001738
1739 handler.rearm_current_block(block);
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01001740 }
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02001741 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001742 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001743
Hans-Kristian Arntzencb613eb2021-07-29 15:24:28 +02001744 if (!handler.handle_terminator(block))
1745 return false;
1746
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001747 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001748}
1749
1750bool Compiler::traverse_all_reachable_opcodes(const SPIRFunction &func, OpcodeHandler &handler) const
1751{
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001752 for (auto block : func.blocks)
1753 if (!traverse_all_reachable_opcodes(get<SPIRBlock>(block), handler))
1754 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001755
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001756 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001757}
1758
1759uint32_t Compiler::type_struct_member_offset(const SPIRType &type, uint32_t index) const
1760{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001761 auto *type_meta = ir.find_meta(type.self);
1762 if (type_meta)
1763 {
1764 // Decoration must be set in valid SPIR-V, otherwise throw.
1765 auto &dec = type_meta->members[index];
1766 if (dec.decoration_flags.get(DecorationOffset))
1767 return dec.offset;
1768 else
1769 SPIRV_CROSS_THROW("Struct member does not have Offset set.");
1770 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001771 else
Panagiotis Christopoulos Charitos946f7792016-12-12 22:33:22 +01001772 SPIRV_CROSS_THROW("Struct member does not have Offset set.");
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001773}
1774
1775uint32_t Compiler::type_struct_member_array_stride(const SPIRType &type, uint32_t index) const
1776{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001777 auto *type_meta = ir.find_meta(type.member_types[index]);
1778 if (type_meta)
1779 {
1780 // Decoration must be set in valid SPIR-V, otherwise throw.
1781 // ArrayStride is part of the array type not OpMemberDecorate.
1782 auto &dec = type_meta->decoration;
1783 if (dec.decoration_flags.get(DecorationArrayStride))
1784 return dec.array_stride;
1785 else
1786 SPIRV_CROSS_THROW("Struct member does not have ArrayStride set.");
1787 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02001788 else
Hans-Kristian Arntzenfc37c522019-04-02 19:18:13 +02001789 SPIRV_CROSS_THROW("Struct member does not have ArrayStride set.");
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001790}
1791
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01001792uint32_t Compiler::type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const
1793{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01001794 auto *type_meta = ir.find_meta(type.self);
1795 if (type_meta)
1796 {
1797 // Decoration must be set in valid SPIR-V, otherwise throw.
1798 // MatrixStride is part of OpMemberDecorate.
1799 auto &dec = type_meta->members[index];
1800 if (dec.decoration_flags.get(DecorationMatrixStride))
1801 return dec.matrix_stride;
1802 else
1803 SPIRV_CROSS_THROW("Struct member does not have MatrixStride set.");
1804 }
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01001805 else
1806 SPIRV_CROSS_THROW("Struct member does not have MatrixStride set.");
1807}
1808
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001809size_t Compiler::get_declared_struct_size(const SPIRType &type) const
1810{
Hans-Kristian Arntzen694b3142018-04-05 16:26:54 +02001811 if (type.member_types.empty())
1812 SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
1813
Hans-Kristian Arntzen05e8e5a2021-11-07 10:08:17 +01001814 // Offsets can be declared out of order, so we need to deduce the actual size
1815 // based on last member instead.
1816 uint32_t member_index = 0;
1817 size_t highest_offset = 0;
1818 for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
1819 {
1820 size_t offset = type_struct_member_offset(type, i);
1821 if (offset > highest_offset)
1822 {
1823 highest_offset = offset;
1824 member_index = i;
1825 }
1826 }
1827
1828 size_t size = get_declared_struct_member_size(type, member_index);
1829 return highest_offset + size;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01001830}
1831
Hans-Kristian Arntzene86018f2018-09-10 11:08:47 +02001832size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, size_t array_size) const
1833{
1834 if (type.member_types.empty())
1835 SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
1836
1837 size_t size = get_declared_struct_size(type);
1838 auto &last_type = get<SPIRType>(type.member_types.back());
1839 if (!last_type.array.empty() && last_type.array_size_literal[0] && last_type.array[0] == 0) // Runtime array
1840 size += array_size * type_struct_member_array_stride(type, uint32_t(type.member_types.size() - 1));
1841
1842 return size;
1843}
1844
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02001845uint32_t Compiler::evaluate_spec_constant_u32(const SPIRConstantOp &spec) const
1846{
1847 auto &result_type = get<SPIRType>(spec.basetype);
Hans-Kristian Arntzen6fc2a052020-11-08 13:59:52 +01001848 if (result_type.basetype != SPIRType::UInt && result_type.basetype != SPIRType::Int &&
1849 result_type.basetype != SPIRType::Boolean)
1850 {
1851 SPIRV_CROSS_THROW(
1852 "Only 32-bit integers and booleans are currently supported when evaluating specialization constants.\n");
1853 }
1854
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02001855 if (!is_scalar(result_type))
1856 SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n");
1857
1858 uint32_t value = 0;
1859
1860 const auto eval_u32 = [&](uint32_t id) -> uint32_t {
1861 auto &type = expression_type(id);
1862 if (type.basetype != SPIRType::UInt && type.basetype != SPIRType::Int && type.basetype != SPIRType::Boolean)
Hans-Kristian Arntzen6fc2a052020-11-08 13:59:52 +01001863 {
1864 SPIRV_CROSS_THROW("Only 32-bit integers and booleans are currently supported when evaluating "
1865 "specialization constants.\n");
1866 }
1867
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02001868 if (!is_scalar(type))
1869 SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n");
1870 if (const auto *c = this->maybe_get<SPIRConstant>(id))
1871 return c->scalar();
1872 else
1873 return evaluate_spec_constant_u32(this->get<SPIRConstantOp>(id));
1874 };
1875
Hans-Kristian Arntzen6fc2a052020-11-08 13:59:52 +01001876#define binary_spec_op(op, binary_op) \
1877 case Op##op: \
1878 value = eval_u32(spec.arguments[0]) binary_op eval_u32(spec.arguments[1]); \
1879 break
1880#define binary_spec_op_cast(op, binary_op, type) \
1881 case Op##op: \
1882 value = uint32_t(type(eval_u32(spec.arguments[0])) binary_op type(eval_u32(spec.arguments[1]))); \
1883 break
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02001884
1885 // Support the basic opcodes which are typically used when computing array sizes.
1886 switch (spec.opcode)
1887 {
Hans-Kristian Arntzen6fc2a052020-11-08 13:59:52 +01001888 binary_spec_op(IAdd, +);
1889 binary_spec_op(ISub, -);
1890 binary_spec_op(IMul, *);
1891 binary_spec_op(BitwiseAnd, &);
1892 binary_spec_op(BitwiseOr, |);
1893 binary_spec_op(BitwiseXor, ^);
1894 binary_spec_op(LogicalAnd, &);
1895 binary_spec_op(LogicalOr, |);
1896 binary_spec_op(ShiftLeftLogical, <<);
1897 binary_spec_op(ShiftRightLogical, >>);
1898 binary_spec_op_cast(ShiftRightArithmetic, >>, int32_t);
1899 binary_spec_op(LogicalEqual, ==);
1900 binary_spec_op(LogicalNotEqual, !=);
1901 binary_spec_op(IEqual, ==);
1902 binary_spec_op(INotEqual, !=);
1903 binary_spec_op(ULessThan, <);
1904 binary_spec_op(ULessThanEqual, <=);
1905 binary_spec_op(UGreaterThan, >);
1906 binary_spec_op(UGreaterThanEqual, >=);
1907 binary_spec_op_cast(SLessThan, <, int32_t);
1908 binary_spec_op_cast(SLessThanEqual, <=, int32_t);
1909 binary_spec_op_cast(SGreaterThan, >, int32_t);
1910 binary_spec_op_cast(SGreaterThanEqual, >=, int32_t);
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02001911#undef binary_spec_op
1912#undef binary_spec_op_cast
1913
1914 case OpLogicalNot:
1915 value = uint32_t(!eval_u32(spec.arguments[0]));
1916 break;
1917
1918 case OpNot:
1919 value = ~eval_u32(spec.arguments[0]);
1920 break;
1921
1922 case OpSNegate:
Hans-Kristian Arntzen34a6a452020-09-28 14:12:54 +02001923 value = uint32_t(-int32_t(eval_u32(spec.arguments[0])));
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02001924 break;
1925
1926 case OpSelect:
1927 value = eval_u32(spec.arguments[0]) ? eval_u32(spec.arguments[1]) : eval_u32(spec.arguments[2]);
1928 break;
1929
1930 case OpUMod:
1931 {
1932 uint32_t a = eval_u32(spec.arguments[0]);
1933 uint32_t b = eval_u32(spec.arguments[1]);
1934 if (b == 0)
1935 SPIRV_CROSS_THROW("Undefined behavior in UMod, b == 0.\n");
1936 value = a % b;
1937 break;
1938 }
1939
1940 case OpSRem:
1941 {
1942 auto a = int32_t(eval_u32(spec.arguments[0]));
1943 auto b = int32_t(eval_u32(spec.arguments[1]));
1944 if (b == 0)
1945 SPIRV_CROSS_THROW("Undefined behavior in SRem, b == 0.\n");
1946 value = a % b;
1947 break;
1948 }
1949
1950 case OpSMod:
1951 {
1952 auto a = int32_t(eval_u32(spec.arguments[0]));
1953 auto b = int32_t(eval_u32(spec.arguments[1]));
1954 if (b == 0)
1955 SPIRV_CROSS_THROW("Undefined behavior in SMod, b == 0.\n");
1956 auto v = a % b;
1957
1958 // Makes sure we match the sign of b, not a.
1959 if ((b < 0 && v > 0) || (b > 0 && v < 0))
1960 v += b;
1961 value = v;
1962 break;
1963 }
1964
1965 case OpUDiv:
1966 {
1967 uint32_t a = eval_u32(spec.arguments[0]);
1968 uint32_t b = eval_u32(spec.arguments[1]);
1969 if (b == 0)
1970 SPIRV_CROSS_THROW("Undefined behavior in UDiv, b == 0.\n");
1971 value = a / b;
1972 break;
1973 }
1974
1975 case OpSDiv:
1976 {
1977 auto a = int32_t(eval_u32(spec.arguments[0]));
1978 auto b = int32_t(eval_u32(spec.arguments[1]));
1979 if (b == 0)
1980 SPIRV_CROSS_THROW("Undefined behavior in SDiv, b == 0.\n");
1981 value = a / b;
1982 break;
1983 }
1984
1985 default:
1986 SPIRV_CROSS_THROW("Unsupported spec constant opcode for evaluation.\n");
1987 }
1988
1989 return value;
1990}
1991
1992uint32_t Compiler::evaluate_constant_u32(uint32_t id) const
1993{
1994 if (const auto *c = maybe_get<SPIRConstant>(id))
1995 return c->scalar();
1996 else
1997 return evaluate_spec_constant_u32(get<SPIRConstantOp>(id));
1998}
1999
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002000size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const
2001{
Hans-Kristian Arntzen694b3142018-04-05 16:26:54 +02002002 if (struct_type.member_types.empty())
2003 SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
2004
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002005 auto &flags = get_member_decoration_bitset(struct_type.self, index);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002006 auto &type = get<SPIRType>(struct_type.member_types[index]);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002007
Arseny Kapoulkinef63e7c52017-01-17 01:52:12 -08002008 switch (type.basetype)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002009 {
Arseny Kapoulkinef63e7c52017-01-17 01:52:12 -08002010 case SPIRType::Unknown:
2011 case SPIRType::Void:
2012 case SPIRType::Boolean: // Bools are purely logical, and cannot be used for externally visible types.
2013 case SPIRType::AtomicCounter:
2014 case SPIRType::Image:
2015 case SPIRType::SampledImage:
2016 case SPIRType::Sampler:
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01002017 SPIRV_CROSS_THROW("Querying size for object with opaque size.");
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002018
Arseny Kapoulkinef63e7c52017-01-17 01:52:12 -08002019 default:
2020 break;
2021 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002022
Hans-Kristian Arntzen58dad822020-05-25 11:05:42 +02002023 if (type.pointer && type.storage == StorageClassPhysicalStorageBuffer)
2024 {
2025 // Check if this is a top-level pointer type, and not an array of pointers.
2026 if (type.pointer_depth > get<SPIRType>(type.parent_type).pointer_depth)
2027 return 8;
2028 }
2029
Arseny Kapoulkinef63e7c52017-01-17 01:52:12 -08002030 if (!type.array.empty())
2031 {
2032 // For arrays, we can use ArrayStride to get an easy check.
Hans-Kristian Arntzendd603ea2018-02-23 15:09:28 +01002033 bool array_size_literal = type.array_size_literal.back();
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02002034 uint32_t array_size = array_size_literal ? type.array.back() : evaluate_constant_u32(type.array.back());
Hans-Kristian Arntzendd603ea2018-02-23 15:09:28 +01002035 return type_struct_member_array_stride(struct_type, index) * array_size;
Arseny Kapoulkinef63e7c52017-01-17 01:52:12 -08002036 }
2037 else if (type.basetype == SPIRType::Struct)
2038 {
2039 return get_declared_struct_size(type);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002040 }
2041 else
2042 {
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002043 unsigned vecsize = type.vecsize;
2044 unsigned columns = type.columns;
Hans-Kristian Arntzen2d79d362016-11-28 15:01:36 +01002045
Arseny Kapoulkinef63e7c52017-01-17 01:52:12 -08002046 // Vectors.
2047 if (columns == 1)
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01002048 {
2049 size_t component_size = type.width / 8;
Arseny Kapoulkinef63e7c52017-01-17 01:52:12 -08002050 return vecsize * component_size;
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01002051 }
Hans-Kristian Arntzen2d79d362016-11-28 15:01:36 +01002052 else
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002053 {
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01002054 uint32_t matrix_stride = type_struct_member_matrix_stride(struct_type, index);
Hans-Kristian Arntzen2d79d362016-11-28 15:01:36 +01002055
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01002056 // Per SPIR-V spec, matrices must be tightly packed and aligned up for vec3 accesses.
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002057 if (flags.get(DecorationRowMajor))
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01002058 return matrix_stride * vecsize;
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002059 else if (flags.get(DecorationColMajor))
Hans-Kristian Arntzend3cad992017-01-21 11:30:33 +01002060 return matrix_stride * columns;
2061 else
2062 SPIRV_CROSS_THROW("Either row-major or column-major must be declared for matrices.");
Arseny Kapoulkinef63e7c52017-01-17 01:52:12 -08002063 }
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002064 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002065}
2066
2067bool Compiler::BufferAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
2068{
Chip Davis3bfb2f92018-12-03 02:06:33 -06002069 if (opcode != OpAccessChain && opcode != OpInBoundsAccessChain && opcode != OpPtrAccessChain)
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002070 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002071
Chip Davis3bfb2f92018-12-03 02:06:33 -06002072 bool ptr_chain = (opcode == OpPtrAccessChain);
2073
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002074 // Invalid SPIR-V.
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01002075 if (length < (ptr_chain ? 5u : 4u))
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002076 return false;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002077
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002078 if (args[2] != id)
2079 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002080
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002081 // Don't bother traversing the entire access chain tree yet.
2082 // If we access a struct member, assume we access the entire member.
Chip Davis3bfb2f92018-12-03 02:06:33 -06002083 uint32_t index = compiler.get<SPIRConstant>(args[ptr_chain ? 4 : 3]).scalar();
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002084
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002085 // Seen this index already.
2086 if (seen.find(index) != end(seen))
2087 return true;
2088 seen.insert(index);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002089
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002090 auto &type = compiler.expression_type(id);
2091 uint32_t offset = compiler.type_struct_member_offset(type, index);
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002092
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002093 size_t range;
2094 // If we have another member in the struct, deduce the range by looking at the next member.
2095 // This is okay since structs in SPIR-V can have padding, but Offset decoration must be
2096 // monotonically increasing.
2097 // Of course, this doesn't take into account if the SPIR-V for some reason decided to add
2098 // very large amounts of padding, but that's not really a big deal.
2099 if (index + 1 < type.member_types.size())
2100 {
2101 range = compiler.type_struct_member_offset(type, index + 1) - offset;
2102 }
2103 else
2104 {
2105 // No padding, so just deduce it from the size of the member directly.
2106 range = compiler.get_declared_struct_member_size(type, index);
2107 }
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002108
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002109 ranges.push_back({ index, offset, range });
2110 return true;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002111}
2112
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002113SmallVector<BufferRange> Compiler::get_active_buffer_ranges(VariableID id) const
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002114{
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02002115 SmallVector<BufferRange> ranges;
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002116 BufferAccessHandler handler(*this, ranges, id);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002117 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Hans-Kristian Arntzen4b8ed532016-05-05 09:33:18 +02002118 return ranges;
Hans-Kristian Arntzen75471fb2016-03-02 18:09:16 +01002119}
2120
Hans-Kristian Arntzenf05373b2016-05-23 10:57:22 +02002121bool Compiler::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const
2122{
2123 if (a.basetype != b.basetype)
2124 return false;
2125 if (a.width != b.width)
2126 return false;
2127 if (a.vecsize != b.vecsize)
2128 return false;
2129 if (a.columns != b.columns)
2130 return false;
2131 if (a.array.size() != b.array.size())
2132 return false;
2133
Hans-Kristian Arntzen46892ab2016-05-23 13:45:20 +02002134 size_t array_count = a.array.size();
Hans-Kristian Arntzenf05373b2016-05-23 10:57:22 +02002135 if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0)
2136 return false;
2137
2138 if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage)
2139 {
2140 if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0)
2141 return false;
2142 }
2143
2144 if (a.member_types.size() != b.member_types.size())
2145 return false;
2146
Hans-Kristian Arntzen46892ab2016-05-23 13:45:20 +02002147 size_t member_types = a.member_types.size();
2148 for (size_t i = 0; i < member_types; i++)
Hans-Kristian Arntzenf05373b2016-05-23 10:57:22 +02002149 {
2150 if (!types_are_logically_equivalent(get<SPIRType>(a.member_types[i]), get<SPIRType>(b.member_types[i])))
2151 return false;
2152 }
2153
2154 return true;
2155}
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002156
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002157const Bitset &Compiler::get_execution_mode_bitset() const
2158{
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002159 return get_entry_point().flags;
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002160}
2161
2162void Compiler::set_execution_mode(ExecutionMode mode, uint32_t arg0, uint32_t arg1, uint32_t arg2)
2163{
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002164 auto &execution = get_entry_point();
2165
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002166 execution.flags.set(mode);
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002167 switch (mode)
2168 {
2169 case ExecutionModeLocalSize:
2170 execution.workgroup_size.x = arg0;
2171 execution.workgroup_size.y = arg1;
2172 execution.workgroup_size.z = arg2;
2173 break;
2174
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01002175 case ExecutionModeLocalSizeId:
2176 execution.workgroup_size.id_x = arg0;
2177 execution.workgroup_size.id_y = arg1;
2178 execution.workgroup_size.id_z = arg2;
2179 break;
2180
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002181 case ExecutionModeInvocations:
2182 execution.invocations = arg0;
2183 break;
2184
2185 case ExecutionModeOutputVertices:
2186 execution.output_vertices = arg0;
2187 break;
2188
Hans-Kristian Arntzen57626172022-09-02 16:31:04 +02002189 case ExecutionModeOutputPrimitivesEXT:
2190 execution.output_primitives = arg0;
2191 break;
2192
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002193 default:
2194 break;
2195 }
2196}
2197
2198void Compiler::unset_execution_mode(ExecutionMode mode)
2199{
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002200 auto &execution = get_entry_point();
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002201 execution.flags.clear(mode);
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002202}
2203
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002204uint32_t Compiler::get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y,
2205 SpecializationConstant &z) const
2206{
2207 auto &execution = get_entry_point();
Hans-Kristian Arntzenca69b612017-11-06 09:49:52 +01002208 x = { 0, 0 };
2209 y = { 0, 0 };
2210 z = { 0, 0 };
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002211
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01002212 // WorkgroupSize builtin takes precedence over LocalSize / LocalSizeId.
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002213 if (execution.workgroup_size.constant != 0)
2214 {
2215 auto &c = get<SPIRConstant>(execution.workgroup_size.constant);
2216
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002217 if (c.m.c[0].id[0] != ID(0))
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002218 {
2219 x.id = c.m.c[0].id[0];
2220 x.constant_id = get_decoration(c.m.c[0].id[0], DecorationSpecId);
2221 }
2222
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002223 if (c.m.c[0].id[1] != ID(0))
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002224 {
2225 y.id = c.m.c[0].id[1];
2226 y.constant_id = get_decoration(c.m.c[0].id[1], DecorationSpecId);
2227 }
2228
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002229 if (c.m.c[0].id[2] != ID(0))
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002230 {
2231 z.id = c.m.c[0].id[2];
2232 z.constant_id = get_decoration(c.m.c[0].id[2], DecorationSpecId);
2233 }
2234 }
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01002235 else if (execution.flags.get(ExecutionModeLocalSizeId))
2236 {
2237 auto &cx = get<SPIRConstant>(execution.workgroup_size.id_x);
2238 if (cx.specialization)
2239 {
2240 x.id = execution.workgroup_size.id_x;
2241 x.constant_id = get_decoration(execution.workgroup_size.id_x, DecorationSpecId);
2242 }
2243
2244 auto &cy = get<SPIRConstant>(execution.workgroup_size.id_y);
2245 if (cy.specialization)
2246 {
2247 y.id = execution.workgroup_size.id_y;
2248 y.constant_id = get_decoration(execution.workgroup_size.id_y, DecorationSpecId);
2249 }
2250
2251 auto &cz = get<SPIRConstant>(execution.workgroup_size.id_z);
2252 if (cz.specialization)
2253 {
2254 z.id = execution.workgroup_size.id_z;
2255 z.constant_id = get_decoration(execution.workgroup_size.id_z, DecorationSpecId);
2256 }
2257 }
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002258
2259 return execution.workgroup_size.constant;
2260}
2261
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002262uint32_t Compiler::get_execution_mode_argument(spv::ExecutionMode mode, uint32_t index) const
2263{
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002264 auto &execution = get_entry_point();
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002265 switch (mode)
2266 {
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01002267 case ExecutionModeLocalSizeId:
2268 if (execution.flags.get(ExecutionModeLocalSizeId))
2269 {
2270 switch (index)
2271 {
2272 case 0:
2273 return execution.workgroup_size.id_x;
2274 case 1:
2275 return execution.workgroup_size.id_y;
2276 case 2:
2277 return execution.workgroup_size.id_z;
2278 default:
2279 return 0;
2280 }
2281 }
2282 else
2283 return 0;
2284
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002285 case ExecutionModeLocalSize:
2286 switch (index)
2287 {
2288 case 0:
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01002289 if (execution.flags.get(ExecutionModeLocalSizeId) && execution.workgroup_size.id_x != 0)
2290 return get<SPIRConstant>(execution.workgroup_size.id_x).scalar();
2291 else
2292 return execution.workgroup_size.x;
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002293 case 1:
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01002294 if (execution.flags.get(ExecutionModeLocalSizeId) && execution.workgroup_size.id_y != 0)
2295 return get<SPIRConstant>(execution.workgroup_size.id_y).scalar();
2296 else
2297 return execution.workgroup_size.y;
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002298 case 2:
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01002299 if (execution.flags.get(ExecutionModeLocalSizeId) && execution.workgroup_size.id_z != 0)
2300 return get<SPIRConstant>(execution.workgroup_size.id_z).scalar();
2301 else
2302 return execution.workgroup_size.z;
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002303 default:
2304 return 0;
2305 }
2306
2307 case ExecutionModeInvocations:
2308 return execution.invocations;
2309
2310 case ExecutionModeOutputVertices:
2311 return execution.output_vertices;
2312
Hans-Kristian Arntzen57626172022-09-02 16:31:04 +02002313 case ExecutionModeOutputPrimitivesEXT:
2314 return execution.output_primitives;
2315
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002316 default:
2317 return 0;
2318 }
2319}
2320
2321ExecutionModel Compiler::get_execution_model() const
2322{
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002323 auto &execution = get_entry_point();
Hans-Kristian Arntzen3c285a12016-07-04 13:30:05 +02002324 return execution.model;
2325}
Hans-Kristian Arntzen8e63c772016-07-06 09:58:01 +02002326
Chip Davise75add42019-02-05 18:13:26 -06002327bool Compiler::is_tessellation_shader(ExecutionModel model)
2328{
2329 return model == ExecutionModelTessellationControl || model == ExecutionModelTessellationEvaluation;
2330}
2331
Hans-Kristian Arntzen5ea576e2020-09-28 14:09:39 +02002332bool Compiler::is_vertex_like_shader() const
2333{
2334 auto model = get_execution_model();
2335 return model == ExecutionModelVertex || model == ExecutionModelGeometry ||
2336 model == ExecutionModelTessellationControl || model == ExecutionModelTessellationEvaluation;
2337}
2338
Chip Davise75add42019-02-05 18:13:26 -06002339bool Compiler::is_tessellation_shader() const
2340{
2341 return is_tessellation_shader(get_execution_model());
2342}
2343
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002344void Compiler::set_remapped_variable_state(VariableID id, bool remap_enable)
Hans-Kristian Arntzen8e63c772016-07-06 09:58:01 +02002345{
2346 get<SPIRVariable>(id).remapped_variable = remap_enable;
2347}
2348
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002349bool Compiler::get_remapped_variable_state(VariableID id) const
Hans-Kristian Arntzen8e63c772016-07-06 09:58:01 +02002350{
2351 return get<SPIRVariable>(id).remapped_variable;
2352}
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +02002353
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002354void Compiler::set_subpass_input_remapped_components(VariableID id, uint32_t components)
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +02002355{
2356 get<SPIRVariable>(id).remapped_components = components;
2357}
2358
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002359uint32_t Compiler::get_subpass_input_remapped_components(VariableID id) const
Hans-Kristian Arntzen078eec52016-07-06 11:04:06 +02002360{
2361 return get<SPIRVariable>(id).remapped_components;
2362}
Hans-Kristian Arntzen36a0b632016-07-12 14:33:04 +02002363
Hans-Kristian Arntzenacae6072019-01-04 13:19:50 +01002364void Compiler::add_implied_read_expression(SPIRExpression &e, uint32_t source)
2365{
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002366 auto itr = find(begin(e.implied_read_expressions), end(e.implied_read_expressions), ID(source));
Hans-Kristian Arntzenacae6072019-01-04 13:19:50 +01002367 if (itr == end(e.implied_read_expressions))
2368 e.implied_read_expressions.push_back(source);
2369}
2370
2371void Compiler::add_implied_read_expression(SPIRAccessChain &e, uint32_t source)
2372{
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002373 auto itr = find(begin(e.implied_read_expressions), end(e.implied_read_expressions), ID(source));
Hans-Kristian Arntzenacae6072019-01-04 13:19:50 +01002374 if (itr == end(e.implied_read_expressions))
2375 e.implied_read_expressions.push_back(source);
2376}
2377
Bill Hollings5493b302022-09-14 15:19:15 -04002378void Compiler::add_active_interface_variable(uint32_t var_id)
2379{
2380 active_interface_variables.insert(var_id);
2381
2382 // In SPIR-V 1.4 and up we must also track the interface variable in the entry point.
2383 if (ir.get_spirv_version() >= 0x10400)
2384 {
2385 auto &vars = get_entry_point().interface_variables;
2386 if (find(begin(vars), end(vars), VariableID(var_id)) == end(vars))
2387 vars.push_back(var_id);
2388 }
2389}
2390
Hans-Kristian Arntzen36a0b632016-07-12 14:33:04 +02002391void Compiler::inherit_expression_dependencies(uint32_t dst, uint32_t source_expression)
2392{
Hans-Kristian Arntzen75391f92017-03-20 22:38:05 +01002393 // Don't inherit any expression dependencies if the expression in dst
2394 // is not a forwarded temporary.
2395 if (forwarded_temporaries.find(dst) == end(forwarded_temporaries) ||
2396 forced_temporaries.find(dst) != end(forced_temporaries))
2397 {
2398 return;
2399 }
2400
Hans-Kristian Arntzen36a0b632016-07-12 14:33:04 +02002401 auto &e = get<SPIRExpression>(dst);
Hans-Kristian Arntzene0efa732018-03-09 13:21:38 +01002402 auto *phi = maybe_get<SPIRVariable>(source_expression);
2403 if (phi && phi->phi_variable)
2404 {
2405 // We have used a phi variable, which can change at the end of the block,
2406 // so make sure we take a dependency on this phi variable.
2407 phi->dependees.push_back(dst);
2408 }
2409
Hans-Kristian Arntzen36a0b632016-07-12 14:33:04 +02002410 auto *s = maybe_get<SPIRExpression>(source_expression);
2411 if (!s)
2412 return;
2413
2414 auto &e_deps = e.expression_dependencies;
2415 auto &s_deps = s->expression_dependencies;
2416
2417 // If we depend on a expression, we also depend on all sub-dependencies from source.
2418 e_deps.push_back(source_expression);
2419 e_deps.insert(end(e_deps), begin(s_deps), end(s_deps));
2420
2421 // Eliminate duplicated dependencies.
Hans-Kristian Arntzene0efa732018-03-09 13:21:38 +01002422 sort(begin(e_deps), end(e_deps));
Hans-Kristian Arntzen36a0b632016-07-12 14:33:04 +02002423 e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps));
2424}
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002425
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02002426SmallVector<EntryPoint> Compiler::get_entry_points_and_stages() const
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002427{
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02002428 SmallVector<EntryPoint> entries;
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002429 for (auto &entry : ir.entry_points)
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002430 entries.push_back({ entry.second.orig_name, entry.second.model });
2431 return entries;
2432}
2433
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002434void Compiler::rename_entry_point(const std::string &old_name, const std::string &new_name, spv::ExecutionModel model)
2435{
2436 auto &entry = get_entry_point(old_name, model);
Hans-Kristian Arntzen4427cb92017-11-13 13:49:11 +01002437 entry.orig_name = new_name;
2438 entry.name = new_name;
2439}
2440
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002441void Compiler::set_entry_point(const std::string &name, spv::ExecutionModel model)
2442{
2443 auto &entry = get_entry_point(name, model);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002444 ir.default_entry_point = entry.self;
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002445}
2446
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002447SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name)
2448{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002449 auto itr = find_if(
2450 begin(ir.entry_points), end(ir.entry_points),
2451 [&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool { return entry.second.orig_name == name; });
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002452
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002453 if (itr == end(ir.entry_points))
Panagiotis Christopoulos Charitos946f7792016-12-12 22:33:22 +01002454 SPIRV_CROSS_THROW("Entry point does not exist.");
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002455
2456 return itr->second;
2457}
2458
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002459const SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name) const
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002460{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002461 auto itr = find_if(
2462 begin(ir.entry_points), end(ir.entry_points),
2463 [&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool { return entry.second.orig_name == name; });
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002464
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002465 if (itr == end(ir.entry_points))
Panagiotis Christopoulos Charitos946f7792016-12-12 22:33:22 +01002466 SPIRV_CROSS_THROW("Entry point does not exist.");
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002467
2468 return itr->second;
2469}
2470
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002471SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model)
2472{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002473 auto itr = find_if(begin(ir.entry_points), end(ir.entry_points),
2474 [&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool {
2475 return entry.second.orig_name == name && entry.second.model == model;
2476 });
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002477
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002478 if (itr == end(ir.entry_points))
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002479 SPIRV_CROSS_THROW("Entry point does not exist.");
2480
2481 return itr->second;
2482}
2483
2484const SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model) const
2485{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002486 auto itr = find_if(begin(ir.entry_points), end(ir.entry_points),
2487 [&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool {
2488 return entry.second.orig_name == name && entry.second.model == model;
2489 });
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002490
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002491 if (itr == end(ir.entry_points))
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002492 SPIRV_CROSS_THROW("Entry point does not exist.");
2493
2494 return itr->second;
2495}
2496
Hans-Kristian Arntzeneecbeaa2018-03-01 14:00:04 +01002497const string &Compiler::get_cleansed_entry_point_name(const std::string &name, ExecutionModel model) const
2498{
2499 return get_entry_point(name, model).name;
Bill Hollings10148472017-11-10 16:40:33 -05002500}
2501
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002502const SPIREntryPoint &Compiler::get_entry_point() const
2503{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002504 return ir.entry_points.find(ir.default_entry_point)->second;
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002505}
2506
2507SPIREntryPoint &Compiler::get_entry_point()
2508{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002509 return ir.entry_points.find(ir.default_entry_point)->second;
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002510}
2511
2512bool Compiler::interface_variable_exists_in_entry_point(uint32_t id) const
2513{
2514 auto &var = get<SPIRVariable>(id);
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002515
Hans-Kristian Arntzen2097c302021-01-08 11:37:29 +01002516 if (ir.get_spirv_version() < 0x10400)
2517 {
2518 if (var.storage != StorageClassInput && var.storage != StorageClassOutput &&
2519 var.storage != StorageClassUniformConstant)
2520 SPIRV_CROSS_THROW("Only Input, Output variables and Uniform constants are part of a shader linking interface.");
2521
2522 // This is to avoid potential problems with very old glslang versions which did
2523 // not emit input/output interfaces properly.
2524 // We can assume they only had a single entry point, and single entry point
2525 // shaders could easily be assumed to use every interface variable anyways.
2526 if (ir.entry_points.size() <= 1)
2527 return true;
2528 }
2529
2530 // In SPIR-V 1.4 and later, all global resource variables must be present.
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002531
2532 auto &execution = get_entry_point();
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002533 return find(begin(execution.interface_variables), end(execution.interface_variables), VariableID(id)) !=
Hans-Kristian Arntzen042475e2016-07-28 11:16:02 +02002534 end(execution.interface_variables);
2535}
Hans-Kristian Arntzenbcb55602016-09-10 13:56:36 +02002536
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002537void Compiler::CombinedImageSamplerHandler::push_remap_parameters(const SPIRFunction &func, const uint32_t *args,
2538 uint32_t length)
2539{
2540 // If possible, pipe through a remapping table so that parameters know
2541 // which variables they actually bind to in this scope.
2542 unordered_map<uint32_t, uint32_t> remapping;
2543 for (uint32_t i = 0; i < length; i++)
2544 remapping[func.arguments[i].id] = remap_parameter(args[i]);
Daniel Thornburgh44c33332022-03-02 23:02:38 +00002545 parameter_remapping.push(std::move(remapping));
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002546}
2547
2548void Compiler::CombinedImageSamplerHandler::pop_remap_parameters()
2549{
2550 parameter_remapping.pop();
2551}
2552
2553uint32_t Compiler::CombinedImageSamplerHandler::remap_parameter(uint32_t id)
2554{
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002555 auto *var = compiler.maybe_get_backing_variable(id);
2556 if (var)
2557 id = var->self;
2558
Hans-Kristian Arntzen901b45e2016-09-10 22:21:57 +02002559 if (parameter_remapping.empty())
2560 return id;
2561
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002562 auto &remapping = parameter_remapping.top();
2563 auto itr = remapping.find(id);
2564 if (itr != end(remapping))
2565 return itr->second;
2566 else
2567 return id;
2568}
2569
2570bool Compiler::CombinedImageSamplerHandler::begin_function_scope(const uint32_t *args, uint32_t length)
2571{
2572 if (length < 3)
2573 return false;
2574
2575 auto &callee = compiler.get<SPIRFunction>(args[2]);
2576 args += 3;
2577 length -= 3;
2578 push_remap_parameters(callee, args, length);
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002579 functions.push(&callee);
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002580 return true;
2581}
2582
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002583bool Compiler::CombinedImageSamplerHandler::end_function_scope(const uint32_t *args, uint32_t length)
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002584{
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002585 if (length < 3)
2586 return false;
2587
2588 auto &callee = compiler.get<SPIRFunction>(args[2]);
2589 args += 3;
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002590
2591 // There are two types of cases we have to handle,
2592 // a callee might call sampler2D(texture2D, sampler) directly where
2593 // one or more parameters originate from parameters.
2594 // Alternatively, we need to provide combined image samplers to our callees,
2595 // and in this case we need to add those as well.
2596
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002597 pop_remap_parameters();
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002598
2599 // Our callee has now been processed at least once.
2600 // No point in doing it again.
2601 callee.do_combined_parameters = false;
2602
2603 auto &params = functions.top()->combined_parameters;
2604 functions.pop();
2605 if (functions.empty())
2606 return true;
2607
2608 auto &caller = *functions.top();
2609 if (caller.do_combined_parameters)
2610 {
2611 for (auto &param : params)
2612 {
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002613 VariableID image_id = param.global_image ? param.image_id : VariableID(args[param.image_id]);
2614 VariableID sampler_id = param.global_sampler ? param.sampler_id : VariableID(args[param.sampler_id]);
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002615
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002616 auto *i = compiler.maybe_get_backing_variable(image_id);
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002617 auto *s = compiler.maybe_get_backing_variable(sampler_id);
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002618 if (i)
2619 image_id = i->self;
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002620 if (s)
2621 sampler_id = s->self;
2622
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002623 register_combined_image_sampler(caller, 0, image_id, sampler_id, param.depth);
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002624 }
2625 }
2626
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002627 return true;
2628}
2629
Hans-Kristian Arntzen3ccfbce2019-08-28 14:25:26 +02002630void Compiler::CombinedImageSamplerHandler::register_combined_image_sampler(SPIRFunction &caller,
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002631 VariableID combined_module_id,
2632 VariableID image_id, VariableID sampler_id,
Hans-Kristian Arntzen3ccfbce2019-08-28 14:25:26 +02002633 bool depth)
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002634{
2635 // We now have a texture ID and a sampler ID which will either be found as a global
2636 // or a parameter in our own function. If both are global, they will not need a parameter,
2637 // otherwise, add it to our list.
2638 SPIRFunction::CombinedImageSamplerParameter param = {
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02002639 0u, image_id, sampler_id, true, true, depth,
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002640 };
2641
2642 auto texture_itr = find_if(begin(caller.arguments), end(caller.arguments),
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002643 [image_id](const SPIRFunction::Parameter &p) { return p.id == image_id; });
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002644 auto sampler_itr = find_if(begin(caller.arguments), end(caller.arguments),
2645 [sampler_id](const SPIRFunction::Parameter &p) { return p.id == sampler_id; });
2646
2647 if (texture_itr != end(caller.arguments))
2648 {
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002649 param.global_image = false;
Hans-Kristian Arntzen7630d3c2016-11-21 12:14:02 +01002650 param.image_id = uint32_t(texture_itr - begin(caller.arguments));
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002651 }
2652
2653 if (sampler_itr != end(caller.arguments))
2654 {
2655 param.global_sampler = false;
Hans-Kristian Arntzen7630d3c2016-11-21 12:14:02 +01002656 param.sampler_id = uint32_t(sampler_itr - begin(caller.arguments));
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002657 }
2658
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002659 if (param.global_image && param.global_sampler)
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002660 return;
2661
2662 auto itr = find_if(begin(caller.combined_parameters), end(caller.combined_parameters),
2663 [&param](const SPIRFunction::CombinedImageSamplerParameter &p) {
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002664 return param.image_id == p.image_id && param.sampler_id == p.sampler_id &&
2665 param.global_image == p.global_image && param.global_sampler == p.global_sampler;
Hans-Kristian Arntzence18d4c2017-11-17 13:38:29 +01002666 });
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002667
2668 if (itr == end(caller.combined_parameters))
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002669 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002670 uint32_t id = compiler.ir.increase_bound_by(3);
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002671 auto type_id = id + 0;
Hans-Kristian Arntzen313cb5f2016-09-11 12:54:08 +02002672 auto ptr_type_id = id + 1;
2673 auto combined_id = id + 2;
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002674 auto &base = compiler.expression_type(image_id);
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002675 auto &type = compiler.set<SPIRType>(type_id);
Hans-Kristian Arntzen313cb5f2016-09-11 12:54:08 +02002676 auto &ptr_type = compiler.set<SPIRType>(ptr_type_id);
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002677
2678 type = base;
Hans-Kristian Arntzen313cb5f2016-09-11 12:54:08 +02002679 type.self = type_id;
2680 type.basetype = SPIRType::SampledImage;
2681 type.pointer = false;
2682 type.storage = StorageClassGeneric;
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02002683 type.image.depth = depth;
Hans-Kristian Arntzen313cb5f2016-09-11 12:54:08 +02002684
2685 ptr_type = type;
2686 ptr_type.pointer = true;
2687 ptr_type.storage = StorageClassUniformConstant;
Chip Davis3bfb2f92018-12-03 02:06:33 -06002688 ptr_type.parent_type = type_id;
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002689
2690 // Build new variable.
Hans-Kristian Arntzen313cb5f2016-09-11 12:54:08 +02002691 compiler.set<SPIRVariable>(combined_id, ptr_type_id, StorageClassFunction, 0);
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002692
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002693 // Inherit RelaxedPrecision.
2694 // If any of OpSampledImage, underlying image or sampler are marked, inherit the decoration.
2695 bool relaxed_precision =
Hans-Kristian Arntzen3ccfbce2019-08-28 14:25:26 +02002696 compiler.has_decoration(sampler_id, DecorationRelaxedPrecision) ||
2697 compiler.has_decoration(image_id, DecorationRelaxedPrecision) ||
2698 (combined_module_id && compiler.has_decoration(combined_module_id, DecorationRelaxedPrecision));
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002699
2700 if (relaxed_precision)
2701 compiler.set_decoration(combined_id, DecorationRelaxedPrecision);
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002702
2703 param.id = combined_id;
2704
2705 compiler.set_name(combined_id,
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002706 join("SPIRV_Cross_Combined", compiler.to_name(image_id), compiler.to_name(sampler_id)));
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002707
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002708 caller.combined_parameters.push_back(param);
Hans-Kristian Arntzen9cb86162017-02-05 10:50:14 +01002709 caller.shadow_arguments.push_back({ ptr_type_id, combined_id, 0u, 0u, true });
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002710 }
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02002711}
2712
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002713bool Compiler::DummySamplerForCombinedImageHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
2714{
2715 if (need_dummy_sampler)
2716 {
2717 // No need to traverse further, we know the result.
2718 return false;
2719 }
2720
2721 switch (opcode)
2722 {
2723 case OpLoad:
2724 {
2725 if (length < 3)
2726 return false;
2727
2728 uint32_t result_type = args[0];
2729
2730 auto &type = compiler.get<SPIRType>(result_type);
Hans-Kristian Arntzen47b37422018-02-21 13:46:16 +01002731 bool separate_image =
2732 type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer;
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002733
2734 // If not separate image, don't bother.
2735 if (!separate_image)
2736 return true;
2737
2738 uint32_t id = args[1];
2739 uint32_t ptr = args[2];
2740 compiler.set<SPIRExpression>(id, "", result_type, true);
2741 compiler.register_read(id, ptr, true);
2742 break;
2743 }
2744
2745 case OpImageFetch:
Hans-Kristian Arntzen40bbf6b2018-04-30 11:18:18 +02002746 case OpImageQuerySizeLod:
2747 case OpImageQuerySize:
2748 case OpImageQueryLevels:
2749 case OpImageQuerySamples:
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002750 {
Hans-Kristian Arntzen40bbf6b2018-04-30 11:18:18 +02002751 // If we are fetching or querying LOD from a plain OpTypeImage, we must pre-combine with our dummy sampler.
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002752 auto *var = compiler.maybe_get_backing_variable(args[2]);
2753 if (var)
2754 {
2755 auto &type = compiler.get<SPIRType>(var->basetype);
2756 if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer)
2757 need_dummy_sampler = true;
2758 }
2759
2760 break;
2761 }
2762
2763 case OpInBoundsAccessChain:
2764 case OpAccessChain:
Chip Davis3bfb2f92018-12-03 02:06:33 -06002765 case OpPtrAccessChain:
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002766 {
2767 if (length < 3)
2768 return false;
2769
Hans-Kristian Arntzena39eb482018-04-23 11:52:05 +02002770 uint32_t result_type = args[0];
2771 auto &type = compiler.get<SPIRType>(result_type);
Hans-Kristian Arntzen47b37422018-02-21 13:46:16 +01002772 bool separate_image =
2773 type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer;
Hans-Kristian Arntzena39eb482018-04-23 11:52:05 +02002774 if (!separate_image)
2775 return true;
2776
2777 uint32_t id = args[1];
2778 uint32_t ptr = args[2];
2779 compiler.set<SPIRExpression>(id, "", result_type, true);
2780 compiler.register_read(id, ptr, true);
Hans-Kristian Arntzen7eba2472018-05-11 10:14:20 +02002781
2782 // Other backends might use SPIRAccessChain for this later.
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002783 compiler.ir.ids[id].set_allow_type_rewrite();
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002784 break;
2785 }
2786
2787 default:
2788 break;
2789 }
2790
2791 return true;
2792}
2793
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002794bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
2795{
2796 // We need to figure out where samplers and images are loaded from, so do only the bare bones compilation we need.
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002797 bool is_fetch = false;
2798
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002799 switch (opcode)
2800 {
2801 case OpLoad:
2802 {
2803 if (length < 3)
2804 return false;
2805
2806 uint32_t result_type = args[0];
2807
2808 auto &type = compiler.get<SPIRType>(result_type);
2809 bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
2810 bool separate_sampler = type.basetype == SPIRType::Sampler;
2811
Hans-Kristian Arntzen378fbe82016-09-11 13:47:06 +02002812 // If not separate image or sampler, don't bother.
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002813 if (!separate_image && !separate_sampler)
2814 return true;
2815
2816 uint32_t id = args[1];
2817 uint32_t ptr = args[2];
2818 compiler.set<SPIRExpression>(id, "", result_type, true);
2819 compiler.register_read(id, ptr, true);
2820 return true;
2821 }
2822
2823 case OpInBoundsAccessChain:
2824 case OpAccessChain:
Chip Davis3bfb2f92018-12-03 02:06:33 -06002825 case OpPtrAccessChain:
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002826 {
2827 if (length < 3)
2828 return false;
2829
2830 // Technically, it is possible to have arrays of textures and arrays of samplers and combine them, but this becomes essentially
2831 // impossible to implement, since we don't know which concrete sampler we are accessing.
2832 // One potential way is to create a combinatorial explosion where N textures and M samplers are combined into N * M sampler2Ds,
2833 // but this seems ridiculously complicated for a problem which is easy to work around.
2834 // Checking access chains like this assumes we don't have samplers or textures inside uniform structs, but this makes no sense.
2835
Hans-Kristian Arntzena39eb482018-04-23 11:52:05 +02002836 uint32_t result_type = args[0];
2837
2838 auto &type = compiler.get<SPIRType>(result_type);
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002839 bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
2840 bool separate_sampler = type.basetype == SPIRType::Sampler;
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002841 if (separate_sampler)
Hans-Kristian Arntzen36e1c472017-05-06 13:59:00 +02002842 SPIRV_CROSS_THROW(
2843 "Attempting to use arrays or structs of separate samplers. This is not possible to statically "
2844 "remap to plain GLSL.");
Hans-Kristian Arntzena39eb482018-04-23 11:52:05 +02002845
2846 if (separate_image)
2847 {
2848 uint32_t id = args[1];
2849 uint32_t ptr = args[2];
2850 compiler.set<SPIRExpression>(id, "", result_type, true);
2851 compiler.register_read(id, ptr, true);
2852 }
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002853 return true;
2854 }
2855
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002856 case OpImageFetch:
Hans-Kristian Arntzen40bbf6b2018-04-30 11:18:18 +02002857 case OpImageQuerySizeLod:
2858 case OpImageQuerySize:
2859 case OpImageQueryLevels:
2860 case OpImageQuerySamples:
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002861 {
Hans-Kristian Arntzen40bbf6b2018-04-30 11:18:18 +02002862 // If we are fetching from a plain OpTypeImage or querying LOD, we must pre-combine with our dummy sampler.
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002863 auto *var = compiler.maybe_get_backing_variable(args[2]);
2864 if (!var)
2865 return true;
2866
2867 auto &type = compiler.get<SPIRType>(var->basetype);
2868 if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer)
2869 {
2870 if (compiler.dummy_sampler_id == 0)
Hans-Kristian Arntzen47b37422018-02-21 13:46:16 +01002871 SPIRV_CROSS_THROW("texelFetch without sampler was found, but no dummy sampler has been created with "
2872 "build_dummy_sampler_for_combined_images().");
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002873
2874 // Do it outside.
2875 is_fetch = true;
2876 break;
2877 }
2878
2879 return true;
2880 }
2881
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002882 case OpSampledImage:
2883 // Do it outside.
2884 break;
2885
2886 default:
2887 return true;
2888 }
2889
Hans-Kristian Arntzendfb65972016-09-11 12:05:20 +02002890 // Registers sampler2D calls used in case they are parameters so
2891 // that their callees know which combined image samplers to propagate down the call stack.
2892 if (!functions.empty())
2893 {
2894 auto &callee = *functions.top();
2895 if (callee.do_combined_parameters)
2896 {
2897 uint32_t image_id = args[2];
2898
2899 auto *image = compiler.maybe_get_backing_variable(image_id);
2900 if (image)
2901 image_id = image->self;
2902
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002903 uint32_t sampler_id = is_fetch ? compiler.dummy_sampler_id : args[3];
Hans-Kristian Arntzendfb65972016-09-11 12:05:20 +02002904 auto *sampler = compiler.maybe_get_backing_variable(sampler_id);
2905 if (sampler)
2906 sampler_id = sampler->self;
2907
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002908 uint32_t combined_id = args[1];
2909
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02002910 auto &combined_type = compiler.get<SPIRType>(args[0]);
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002911 register_combined_image_sampler(callee, combined_id, image_id, sampler_id, combined_type.image.depth);
Hans-Kristian Arntzendfb65972016-09-11 12:05:20 +02002912 }
2913 }
2914
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002915 // For function calls, we need to remap IDs which are function parameters into global variables.
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002916 // This information is statically known from the current place in the call stack.
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002917 // Function parameters are not necessarily pointers, so if we don't have a backing variable, remapping will know
2918 // which backing variable the image/sample came from.
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002919 VariableID image_id = remap_parameter(args[2]);
2920 VariableID sampler_id = is_fetch ? compiler.dummy_sampler_id : remap_parameter(args[3]);
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002921
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002922 auto itr = find_if(begin(compiler.combined_image_samplers), end(compiler.combined_image_samplers),
2923 [image_id, sampler_id](const CombinedImageSampler &combined) {
2924 return combined.image_id == image_id && combined.sampler_id == sampler_id;
Hans-Kristian Arntzence18d4c2017-11-17 13:38:29 +01002925 });
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002926
2927 if (itr == end(compiler.combined_image_samplers))
2928 {
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002929 uint32_t sampled_type;
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002930 uint32_t combined_module_id;
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002931 if (is_fetch)
2932 {
2933 // Have to invent the sampled image type.
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002934 sampled_type = compiler.ir.increase_bound_by(1);
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002935 auto &type = compiler.set<SPIRType>(sampled_type);
2936 type = compiler.expression_type(args[2]);
2937 type.self = sampled_type;
2938 type.basetype = SPIRType::SampledImage;
Hans-Kristian Arntzen40bbf6b2018-04-30 11:18:18 +02002939 type.image.depth = false;
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002940 combined_module_id = 0;
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002941 }
2942 else
2943 {
2944 sampled_type = args[0];
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002945 combined_module_id = args[1];
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002946 }
2947
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002948 auto id = compiler.ir.increase_bound_by(2);
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002949 auto type_id = id + 0;
2950 auto combined_id = id + 1;
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002951
2952 // Make a new type, pointer to OpTypeSampledImage, so we can make a variable of this type.
2953 // We will probably have this type lying around, but it doesn't hurt to make duplicates for internal purposes.
2954 auto &type = compiler.set<SPIRType>(type_id);
2955 auto &base = compiler.get<SPIRType>(sampled_type);
2956 type = base;
2957 type.pointer = true;
2958 type.storage = StorageClassUniformConstant;
Chip Davis3bfb2f92018-12-03 02:06:33 -06002959 type.parent_type = type_id;
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002960
2961 // Build new variable.
Hans-Kristian Arntzen948930b2016-09-11 12:36:12 +02002962 compiler.set<SPIRVariable>(combined_id, type_id, StorageClassUniformConstant, 0);
Hans-Kristian Arntzen14bc1ff2016-09-10 18:07:52 +02002963
2964 // Inherit RelaxedPrecision (and potentially other useful flags if deemed relevant).
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002965 // If any of OpSampledImage, underlying image or sampler are marked, inherit the decoration.
2966 bool relaxed_precision =
Hans-Kristian Arntzen3ccfbce2019-08-28 14:25:26 +02002967 (sampler_id && compiler.has_decoration(sampler_id, DecorationRelaxedPrecision)) ||
2968 (image_id && compiler.has_decoration(image_id, DecorationRelaxedPrecision)) ||
2969 (combined_module_id && compiler.has_decoration(combined_module_id, DecorationRelaxedPrecision));
Hans-Kristian Arntzend5a65b42019-08-27 17:15:19 +02002970
2971 if (relaxed_precision)
2972 compiler.set_decoration(combined_id, DecorationRelaxedPrecision);
Hans-Kristian Arntzen14bc1ff2016-09-10 18:07:52 +02002973
Hans-Kristian Arntzena39eb482018-04-23 11:52:05 +02002974 // Propagate the array type for the original image as well.
2975 auto *var = compiler.maybe_get_backing_variable(image_id);
2976 if (var)
2977 {
2978 auto &parent_type = compiler.get<SPIRType>(var->basetype);
2979 type.array = parent_type.array;
2980 type.array_size_literal = parent_type.array_size_literal;
2981 }
2982
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002983 compiler.combined_image_samplers.push_back({ combined_id, image_id, sampler_id });
2984 }
Hans-Kristian Arntzendd1513b2016-09-10 21:52:22 +02002985
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02002986 return true;
2987}
2988
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002989VariableID Compiler::build_dummy_sampler_for_combined_images()
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002990{
2991 DummySamplerForCombinedImageHandler handler(*this);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002992 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002993 if (handler.need_dummy_sampler)
2994 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002995 uint32_t offset = ir.increase_bound_by(3);
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01002996 auto type_id = offset + 0;
2997 auto ptr_type_id = offset + 1;
2998 auto var_id = offset + 2;
2999
3000 SPIRType sampler_type;
3001 auto &sampler = set<SPIRType>(type_id);
3002 sampler.basetype = SPIRType::Sampler;
3003
3004 auto &ptr_sampler = set<SPIRType>(ptr_type_id);
3005 ptr_sampler = sampler;
3006 ptr_sampler.self = type_id;
3007 ptr_sampler.storage = StorageClassUniformConstant;
Hans-Kristian Arntzen1a2e4de2018-02-21 13:43:16 +01003008 ptr_sampler.pointer = true;
Chip Davis3bfb2f92018-12-03 02:06:33 -06003009 ptr_sampler.parent_type = type_id;
Hans-Kristian Arntzen4db70612018-02-21 13:08:30 +01003010
3011 set<SPIRVariable>(var_id, ptr_type_id, StorageClassUniformConstant, 0);
3012 set_name(var_id, "SPIRV_Cross_DummySampler");
3013 dummy_sampler_id = var_id;
3014 return var_id;
3015 }
3016 else
3017 return 0;
3018}
3019
Hans-Kristian Arntzenbcb55602016-09-10 13:56:36 +02003020void Compiler::build_combined_image_samplers()
3021{
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01003022 ir.for_each_typed_id<SPIRFunction>([&](uint32_t, SPIRFunction &func) {
3023 func.combined_parameters.clear();
3024 func.shadow_arguments.clear();
3025 func.do_combined_parameters = true;
3026 });
Hans-Kristian Arntzened98a8e2016-09-11 11:39:20 +02003027
Hans-Kristian Arntzen71bacc42016-09-10 17:48:52 +02003028 combined_image_samplers.clear();
3029 CombinedImageSamplerHandler handler(*this);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02003030 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Hans-Kristian Arntzenbcb55602016-09-10 13:56:36 +02003031}
Hans-Kristian Arntzen6bd545b2016-09-17 15:16:07 +02003032
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02003033SmallVector<SpecializationConstant> Compiler::get_specialization_constants() const
Hans-Kristian Arntzen6bd545b2016-09-17 15:16:07 +02003034{
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02003035 SmallVector<SpecializationConstant> spec_consts;
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01003036 ir.for_each_typed_id<SPIRConstant>([&](uint32_t, const SPIRConstant &c) {
3037 if (c.specialization && has_decoration(c.self, DecorationSpecId))
3038 spec_consts.push_back({ c.self, get_decoration(c.self, DecorationSpecId) });
3039 });
Hans-Kristian Arntzen6bd545b2016-09-17 15:16:07 +02003040 return spec_consts;
3041}
3042
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003043SPIRConstant &Compiler::get_constant(ConstantID id)
Hans-Kristian Arntzen6bd545b2016-09-17 15:16:07 +02003044{
3045 return get<SPIRConstant>(id);
3046}
3047
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003048const SPIRConstant &Compiler::get_constant(ConstantID id) const
Hans-Kristian Arntzen6bd545b2016-09-17 15:16:07 +02003049{
3050 return get<SPIRConstant>(id);
3051}
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003052
Hans-Kristian Arntzen4c866e42021-01-14 15:58:19 +01003053static bool exists_unaccessed_path_to_return(const CFG &cfg, uint32_t block, const unordered_set<uint32_t> &blocks,
3054 unordered_set<uint32_t> &visit_cache)
Hans-Kristian Arntzenb2c2e642017-03-25 16:25:30 +01003055{
3056 // This block accesses the variable.
3057 if (blocks.find(block) != end(blocks))
3058 return false;
3059
3060 // We are at the end of the CFG.
3061 if (cfg.get_succeeding_edges(block).empty())
3062 return true;
3063
3064 // If any of our successors have a path to the end, there exists a path from block.
3065 for (auto &succ : cfg.get_succeeding_edges(block))
Hans-Kristian Arntzen4c866e42021-01-14 15:58:19 +01003066 {
3067 if (visit_cache.count(succ) == 0)
3068 {
3069 if (exists_unaccessed_path_to_return(cfg, succ, blocks, visit_cache))
3070 return true;
3071 visit_cache.insert(succ);
3072 }
3073 }
Hans-Kristian Arntzenb2c2e642017-03-25 16:25:30 +01003074
3075 return false;
3076}
3077
Hans-Kristian Arntzenbf5c0752017-03-25 16:28:44 +01003078void Compiler::analyze_parameter_preservation(
Hans-Kristian Arntzen744d0402017-08-09 17:05:51 +02003079 SPIRFunction &entry, const CFG &cfg, const unordered_map<uint32_t, unordered_set<uint32_t>> &variable_to_blocks,
3080 const unordered_map<uint32_t, unordered_set<uint32_t>> &complete_write_blocks)
Hans-Kristian Arntzenb2c2e642017-03-25 16:25:30 +01003081{
3082 for (auto &arg : entry.arguments)
3083 {
3084 // Non-pointers are always inputs.
3085 auto &type = get<SPIRType>(arg.type);
3086 if (!type.pointer)
3087 continue;
3088
3089 // Opaque argument types are always in
3090 bool potential_preserve;
3091 switch (type.basetype)
3092 {
3093 case SPIRType::Sampler:
3094 case SPIRType::Image:
3095 case SPIRType::SampledImage:
3096 case SPIRType::AtomicCounter:
3097 potential_preserve = false;
3098 break;
3099
3100 default:
3101 potential_preserve = true;
3102 break;
3103 }
3104
3105 if (!potential_preserve)
3106 continue;
3107
3108 auto itr = variable_to_blocks.find(arg.id);
3109 if (itr == end(variable_to_blocks))
3110 {
3111 // Variable is never accessed.
3112 continue;
3113 }
3114
Hans-Kristian Arntzen744d0402017-08-09 17:05:51 +02003115 // We have accessed a variable, but there was no complete writes to that variable.
3116 // We deduce that we must preserve the argument.
3117 itr = complete_write_blocks.find(arg.id);
3118 if (itr == end(complete_write_blocks))
3119 {
3120 arg.read_count++;
3121 continue;
3122 }
3123
3124 // If there is a path through the CFG where no block completely writes to the variable, the variable will be in an undefined state
Hans-Kristian Arntzenb2c2e642017-03-25 16:25:30 +01003125 // when the function returns. We therefore need to implicitly preserve the variable in case there are writers in the function.
3126 // Major case here is if a function is
3127 // void foo(int &var) { if (cond) var = 10; }
3128 // Using read/write counts, we will think it's just an out variable, but it really needs to be inout,
3129 // because if we don't write anything whatever we put into the function must return back to the caller.
Hans-Kristian Arntzen4c866e42021-01-14 15:58:19 +01003130 unordered_set<uint32_t> visit_cache;
3131 if (exists_unaccessed_path_to_return(cfg, entry.entry_block, itr->second, visit_cache))
Hans-Kristian Arntzenb2c2e642017-03-25 16:25:30 +01003132 arg.read_count++;
3133 }
3134}
3135
Hans-Kristian Arntzenb5ed7062018-07-05 10:42:05 +02003136Compiler::AnalyzeVariableScopeAccessHandler::AnalyzeVariableScopeAccessHandler(Compiler &compiler_,
3137 SPIRFunction &entry_)
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003138 : compiler(compiler_)
3139 , entry(entry_)
3140{
3141}
3142
Hans-Kristian Arntzenb5ed7062018-07-05 10:42:05 +02003143bool Compiler::AnalyzeVariableScopeAccessHandler::follow_function_call(const SPIRFunction &)
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003144{
3145 // Only analyze within this function.
3146 return false;
3147}
3148
Hans-Kristian Arntzenb5ed7062018-07-05 10:42:05 +02003149void Compiler::AnalyzeVariableScopeAccessHandler::set_current_block(const SPIRBlock &block)
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003150{
3151 current_block = &block;
3152
3153 // If we're branching to a block which uses OpPhi, in GLSL
3154 // this will be a variable write when we branch,
3155 // so we need to track access to these variables as well to
3156 // have a complete picture.
3157 const auto test_phi = [this, &block](uint32_t to) {
3158 auto &next = compiler.get<SPIRBlock>(to);
3159 for (auto &phi : next.phi_variables)
3160 {
3161 if (phi.parent == block.self)
3162 {
3163 accessed_variables_to_block[phi.function_variable].insert(block.self);
3164 // Phi variables are also accessed in our target branch block.
3165 accessed_variables_to_block[phi.function_variable].insert(next.self);
3166
3167 notify_variable_access(phi.local_variable, block.self);
3168 }
3169 }
3170 };
3171
3172 switch (block.terminator)
3173 {
3174 case SPIRBlock::Direct:
3175 notify_variable_access(block.condition, block.self);
3176 test_phi(block.next_block);
3177 break;
3178
3179 case SPIRBlock::Select:
3180 notify_variable_access(block.condition, block.self);
3181 test_phi(block.true_block);
3182 test_phi(block.false_block);
3183 break;
3184
3185 case SPIRBlock::MultiSelect:
Sebastián Aedo75e37522021-11-12 10:17:38 -03003186 {
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003187 notify_variable_access(block.condition, block.self);
Sebastián Aedo75e37522021-11-12 10:17:38 -03003188 auto &cases = compiler.get_case_list(block);
3189 for (auto &target : cases)
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003190 test_phi(target.block);
3191 if (block.default_block)
3192 test_phi(block.default_block);
3193 break;
Sebastián Aedo75e37522021-11-12 10:17:38 -03003194 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003195
3196 default:
3197 break;
3198 }
3199}
3200
3201void Compiler::AnalyzeVariableScopeAccessHandler::notify_variable_access(uint32_t id, uint32_t block)
3202{
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003203 if (id == 0)
3204 return;
Lukas Hermanns7ad0a842019-09-23 18:05:04 -04003205
Lukas Hermanns50ac6862019-09-18 14:03:54 -04003206 // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers.
Hans-Kristian Arntzen830e24c2019-10-24 16:13:34 +02003207 auto itr = access_chain_children.find(id);
3208 if (itr != end(access_chain_children))
3209 for (auto child_id : itr->second)
3210 notify_variable_access(child_id, block);
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003211
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003212 if (id_is_phi_variable(id))
3213 accessed_variables_to_block[id].insert(block);
3214 else if (id_is_potential_temporary(id))
3215 accessed_temporaries_to_block[id].insert(block);
3216}
3217
3218bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_phi_variable(uint32_t id) const
3219{
3220 if (id >= compiler.get_current_id_bound())
3221 return false;
3222 auto *var = compiler.maybe_get<SPIRVariable>(id);
3223 return var && var->phi_variable;
3224}
3225
3226bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_potential_temporary(uint32_t id) const
3227{
3228 if (id >= compiler.get_current_id_bound())
3229 return false;
3230
3231 // Temporaries are not created before we start emitting code.
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02003232 return compiler.ir.ids[id].empty() || (compiler.ir.ids[id].get_type() == TypeExpression);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003233}
3234
Hans-Kristian Arntzencb613eb2021-07-29 15:24:28 +02003235bool Compiler::AnalyzeVariableScopeAccessHandler::handle_terminator(const SPIRBlock &block)
3236{
3237 switch (block.terminator)
3238 {
3239 case SPIRBlock::Return:
3240 if (block.return_value)
3241 notify_variable_access(block.return_value, block.self);
3242 break;
3243
3244 case SPIRBlock::Select:
3245 case SPIRBlock::MultiSelect:
3246 notify_variable_access(block.condition, block.self);
3247 break;
3248
3249 default:
3250 break;
3251 }
3252
3253 return true;
3254}
3255
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003256bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length)
3257{
3258 // Keep track of the types of temporaries, so we can hoist them out as necessary.
3259 uint32_t result_type, result_id;
3260 if (compiler.instruction_to_result_type(result_type, result_id, op, args, length))
Hans-Kristian Arntzene08e0cf2022-06-17 13:05:08 +02003261 {
3262 // For some opcodes, we will need to override the result id.
3263 // If we need to hoist the temporary, the temporary type is the input, not the result.
3264 // FIXME: This will likely break with OpCopyObject + hoisting, but we'll have to
3265 // solve it if we ever get there ...
3266 if (op == OpConvertUToAccelerationStructureKHR)
3267 {
3268 auto itr = result_id_to_type.find(args[2]);
3269 if (itr != result_id_to_type.end())
3270 result_type = itr->second;
3271 }
3272
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003273 result_id_to_type[result_id] = result_type;
Hans-Kristian Arntzene08e0cf2022-06-17 13:05:08 +02003274 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003275
3276 switch (op)
3277 {
3278 case OpStore:
3279 {
3280 if (length < 2)
3281 return false;
3282
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003283 ID ptr = args[0];
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003284 auto *var = compiler.maybe_get_backing_variable(ptr);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003285
3286 // If we store through an access chain, we have a partial write.
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003287 if (var)
3288 {
3289 accessed_variables_to_block[var->self].insert(current_block->self);
3290 if (var->self == ptr)
3291 complete_write_variables_to_block[var->self].insert(current_block->self);
3292 else
3293 partial_write_variables_to_block[var->self].insert(current_block->self);
3294 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003295
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003296 // args[0] might be an access chain we have to track use of.
3297 notify_variable_access(args[0], current_block->self);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003298 // Might try to store a Phi variable here.
3299 notify_variable_access(args[1], current_block->self);
3300 break;
3301 }
3302
3303 case OpAccessChain:
3304 case OpInBoundsAccessChain:
Chip Davis3bfb2f92018-12-03 02:06:33 -06003305 case OpPtrAccessChain:
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003306 {
3307 if (length < 3)
3308 return false;
3309
Lukas Hermanns50ac6862019-09-18 14:03:54 -04003310 // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers.
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003311 uint32_t ptr = args[2];
3312 auto *var = compiler.maybe_get<SPIRVariable>(ptr);
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003313 if (var)
Mark Satterthwaite869d6282019-08-14 11:06:21 -04003314 {
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003315 accessed_variables_to_block[var->self].insert(current_block->self);
Mark Satterthwaite869d6282019-08-14 11:06:21 -04003316 access_chain_children[args[1]].insert(var->self);
3317 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003318
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003319 // args[2] might be another access chain we have to track use of.
3320 for (uint32_t i = 2; i < length; i++)
Mark Satterthwaite869d6282019-08-14 11:06:21 -04003321 {
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003322 notify_variable_access(args[i], current_block->self);
Mark Satterthwaite869d6282019-08-14 11:06:21 -04003323 access_chain_children[args[1]].insert(args[i]);
3324 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003325
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003326 // Also keep track of the access chain pointer itself.
3327 // In exceptionally rare cases, we can end up with a case where
3328 // the access chain is generated in the loop body, but is consumed in continue block.
3329 // This means we need complex loop workarounds, and we must detect this via CFG analysis.
3330 notify_variable_access(args[1], current_block->self);
3331
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003332 // The result of an access chain is a fixed expression and is not really considered a temporary.
3333 auto &e = compiler.set<SPIRExpression>(args[1], "", args[0], true);
3334 auto *backing_variable = compiler.maybe_get_backing_variable(ptr);
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003335 e.loaded_from = backing_variable ? VariableID(backing_variable->self) : VariableID(0);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003336
3337 // Other backends might use SPIRAccessChain for this later.
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02003338 compiler.ir.ids[args[1]].set_allow_type_rewrite();
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003339 access_chain_expressions.insert(args[1]);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003340 break;
3341 }
3342
3343 case OpCopyMemory:
3344 {
3345 if (length < 2)
3346 return false;
3347
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003348 ID lhs = args[0];
3349 ID rhs = args[1];
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003350 auto *var = compiler.maybe_get_backing_variable(lhs);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003351
3352 // If we store through an access chain, we have a partial write.
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003353 if (var)
3354 {
3355 accessed_variables_to_block[var->self].insert(current_block->self);
3356 if (var->self == lhs)
3357 complete_write_variables_to_block[var->self].insert(current_block->self);
3358 else
3359 partial_write_variables_to_block[var->self].insert(current_block->self);
3360 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003361
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003362 // args[0:1] might be access chains we have to track use of.
3363 for (uint32_t i = 0; i < 2; i++)
3364 notify_variable_access(args[i], current_block->self);
3365
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003366 var = compiler.maybe_get_backing_variable(rhs);
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003367 if (var)
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003368 accessed_variables_to_block[var->self].insert(current_block->self);
3369 break;
3370 }
3371
3372 case OpCopyObject:
3373 {
3374 if (length < 3)
3375 return false;
3376
3377 auto *var = compiler.maybe_get_backing_variable(args[2]);
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003378 if (var)
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003379 accessed_variables_to_block[var->self].insert(current_block->self);
3380
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003381 // Might be an access chain which we have to keep track of.
3382 notify_variable_access(args[1], current_block->self);
3383 if (access_chain_expressions.count(args[2]))
3384 access_chain_expressions.insert(args[1]);
3385
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003386 // Might try to copy a Phi variable here.
3387 notify_variable_access(args[2], current_block->self);
3388 break;
3389 }
3390
3391 case OpLoad:
3392 {
3393 if (length < 3)
3394 return false;
3395 uint32_t ptr = args[2];
3396 auto *var = compiler.maybe_get_backing_variable(ptr);
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003397 if (var)
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003398 accessed_variables_to_block[var->self].insert(current_block->self);
3399
3400 // Loaded value is a temporary.
3401 notify_variable_access(args[1], current_block->self);
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003402
3403 // Might be an access chain we have to track use of.
3404 notify_variable_access(args[2], current_block->self);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003405 break;
3406 }
3407
3408 case OpFunctionCall:
3409 {
3410 if (length < 3)
3411 return false;
3412
Hans-Kristian Arntzene2155052019-10-29 11:11:12 +01003413 // Return value may be a temporary.
3414 if (compiler.get_type(args[0]).basetype != SPIRType::Void)
3415 notify_variable_access(args[1], current_block->self);
3416
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003417 length -= 3;
3418 args += 3;
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003419
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003420 for (uint32_t i = 0; i < length; i++)
3421 {
3422 auto *var = compiler.maybe_get_backing_variable(args[i]);
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003423 if (var)
3424 {
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003425 accessed_variables_to_block[var->self].insert(current_block->self);
Hans-Kristian Arntzenb5ed7062018-07-05 10:42:05 +02003426 // Assume we can get partial writes to this variable.
3427 partial_write_variables_to_block[var->self].insert(current_block->self);
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003428 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003429
3430 // Cannot easily prove if argument we pass to a function is completely written.
3431 // Usually, functions write to a dummy variable,
3432 // which is then copied to in full to the real argument.
3433
3434 // Might try to copy a Phi variable here.
3435 notify_variable_access(args[i], current_block->self);
3436 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003437 break;
3438 }
3439
Hans-Kristian Arntzen90c70e62021-04-20 13:19:04 +02003440 case OpSelect:
3441 {
3442 // In case of variable pointers, we might access a variable here.
3443 // We cannot prove anything about these accesses however.
3444 for (uint32_t i = 1; i < length; i++)
3445 {
3446 if (i >= 3)
3447 {
3448 auto *var = compiler.maybe_get_backing_variable(args[i]);
3449 if (var)
3450 {
3451 accessed_variables_to_block[var->self].insert(current_block->self);
3452 // Assume we can get partial writes to this variable.
3453 partial_write_variables_to_block[var->self].insert(current_block->self);
3454 }
3455 }
3456
3457 // Might try to copy a Phi variable here.
3458 notify_variable_access(args[i], current_block->self);
3459 }
3460 break;
3461 }
3462
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003463 case OpExtInst:
3464 {
3465 for (uint32_t i = 4; i < length; i++)
3466 notify_variable_access(args[i], current_block->self);
3467 notify_variable_access(args[1], current_block->self);
Hans-Kristian Arntzen4561ecd2021-11-07 11:27:20 +01003468
3469 uint32_t extension_set = args[2];
3470 if (compiler.get<SPIRExtension>(extension_set).ext == SPIRExtension::GLSL)
3471 {
3472 auto op_450 = static_cast<GLSLstd450>(args[3]);
3473 switch (op_450)
3474 {
3475 case GLSLstd450Modf:
3476 case GLSLstd450Frexp:
3477 {
3478 uint32_t ptr = args[5];
3479 auto *var = compiler.maybe_get_backing_variable(ptr);
3480 if (var)
3481 {
3482 accessed_variables_to_block[var->self].insert(current_block->self);
3483 if (var->self == ptr)
3484 complete_write_variables_to_block[var->self].insert(current_block->self);
3485 else
3486 partial_write_variables_to_block[var->self].insert(current_block->self);
3487 }
3488 break;
3489 }
3490
3491 default:
3492 break;
3493 }
3494 }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003495 break;
3496 }
3497
3498 case OpArrayLength:
Hans-Kristian Arntzen5b0cafb2021-08-23 12:43:41 +02003499 // Only result is a temporary.
3500 notify_variable_access(args[1], current_block->self);
3501 break;
3502
Hans-Kristian Arntzen65af09d2019-05-28 13:41:46 +02003503 case OpLine:
Lifeng Pan5ca87792019-07-04 16:03:06 +08003504 case OpNoLine:
Hans-Kristian Arntzend2cc43e2019-02-19 17:00:49 +01003505 // Uses literals, but cannot be a phi variable or temporary, so ignore.
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003506 break;
3507
3508 // Atomics shouldn't be able to access function-local variables.
3509 // Some GLSL builtins access a pointer.
3510
3511 case OpCompositeInsert:
3512 case OpVectorShuffle:
3513 // Specialize for opcode which contains literals.
3514 for (uint32_t i = 1; i < 4; i++)
3515 notify_variable_access(args[i], current_block->self);
3516 break;
3517
3518 case OpCompositeExtract:
3519 // Specialize for opcode which contains literals.
3520 for (uint32_t i = 1; i < 3; i++)
3521 notify_variable_access(args[i], current_block->self);
3522 break;
3523
Hans-Kristian Arntzend2cc43e2019-02-19 17:00:49 +01003524 case OpImageWrite:
3525 for (uint32_t i = 0; i < length; i++)
3526 {
3527 // Argument 3 is a literal.
3528 if (i != 3)
3529 notify_variable_access(args[i], current_block->self);
3530 }
3531 break;
3532
3533 case OpImageSampleImplicitLod:
3534 case OpImageSampleExplicitLod:
3535 case OpImageSparseSampleImplicitLod:
3536 case OpImageSparseSampleExplicitLod:
3537 case OpImageSampleProjImplicitLod:
3538 case OpImageSampleProjExplicitLod:
3539 case OpImageSparseSampleProjImplicitLod:
3540 case OpImageSparseSampleProjExplicitLod:
3541 case OpImageFetch:
3542 case OpImageSparseFetch:
3543 case OpImageRead:
3544 case OpImageSparseRead:
3545 for (uint32_t i = 1; i < length; i++)
3546 {
3547 // Argument 4 is a literal.
3548 if (i != 4)
3549 notify_variable_access(args[i], current_block->self);
3550 }
3551 break;
3552
3553 case OpImageSampleDrefImplicitLod:
3554 case OpImageSampleDrefExplicitLod:
3555 case OpImageSparseSampleDrefImplicitLod:
3556 case OpImageSparseSampleDrefExplicitLod:
3557 case OpImageSampleProjDrefImplicitLod:
3558 case OpImageSampleProjDrefExplicitLod:
3559 case OpImageSparseSampleProjDrefImplicitLod:
3560 case OpImageSparseSampleProjDrefExplicitLod:
3561 case OpImageGather:
3562 case OpImageSparseGather:
3563 case OpImageDrefGather:
3564 case OpImageSparseDrefGather:
3565 for (uint32_t i = 1; i < length; i++)
3566 {
3567 // Argument 5 is a literal.
3568 if (i != 5)
3569 notify_variable_access(args[i], current_block->self);
3570 }
3571 break;
3572
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003573 default:
3574 {
3575 // Rather dirty way of figuring out where Phi variables are used.
3576 // As long as only IDs are used, we can scan through instructions and try to find any evidence that
3577 // the ID of a variable has been used.
3578 // There are potential false positives here where a literal is used in-place of an ID,
3579 // but worst case, it does not affect the correctness of the compile.
3580 // Exhaustive analysis would be better here, but it's not worth it for now.
3581 for (uint32_t i = 0; i < length; i++)
3582 notify_variable_access(args[i], current_block->self);
3583 break;
3584 }
3585 }
3586 return true;
3587}
3588
Hans-Kristian Arntzen8c314112018-07-05 14:18:34 +02003589Compiler::StaticExpressionAccessHandler::StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_)
3590 : compiler(compiler_)
3591 , variable_id(variable_id_)
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003592{
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02003593}
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003594
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02003595bool Compiler::StaticExpressionAccessHandler::follow_function_call(const SPIRFunction &)
3596{
3597 return false;
3598}
3599
3600bool Compiler::StaticExpressionAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length)
3601{
3602 switch (op)
3603 {
3604 case OpStore:
3605 if (length < 2)
3606 return false;
3607 if (args[0] == variable_id)
3608 {
3609 static_expression = args[1];
3610 write_count++;
3611 }
3612 break;
3613
3614 case OpLoad:
3615 if (length < 3)
3616 return false;
3617 if (args[2] == variable_id && static_expression == 0) // Tried to read from variable before it was initialized.
3618 return false;
3619 break;
3620
3621 case OpAccessChain:
3622 case OpInBoundsAccessChain:
Chip Davis3bfb2f92018-12-03 02:06:33 -06003623 case OpPtrAccessChain:
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02003624 if (length < 3)
3625 return false;
3626 if (args[2] == variable_id) // If we try to access chain our candidate variable before we store to it, bail.
3627 return false;
3628 break;
3629
3630 default:
3631 break;
3632 }
3633
3634 return true;
3635}
3636
Hans-Kristian Arntzen3e584f22019-02-06 10:38:18 +01003637void Compiler::find_function_local_luts(SPIRFunction &entry, const AnalyzeVariableScopeAccessHandler &handler,
3638 bool single_function)
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02003639{
3640 auto &cfg = *function_cfgs.find(entry.self)->second;
3641
3642 // For each variable which is statically accessed.
3643 for (auto &accessed_var : handler.accessed_variables_to_block)
3644 {
3645 auto &blocks = accessed_var.second;
3646 auto &var = get<SPIRVariable>(accessed_var.first);
3647 auto &type = expression_type(accessed_var.first);
3648
3649 // Only consider function local variables here.
Hans-Kristian Arntzen3e584f22019-02-06 10:38:18 +01003650 // If we only have a single function in our CFG, private storage is also fine,
3651 // since it behaves like a function local variable.
3652 bool allow_lut = var.storage == StorageClassFunction || (single_function && var.storage == StorageClassPrivate);
3653 if (!allow_lut)
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02003654 continue;
3655
3656 // We cannot be a phi variable.
3657 if (var.phi_variable)
3658 continue;
3659
3660 // Only consider arrays here.
3661 if (type.array.empty())
3662 continue;
3663
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02003664 // If the variable has an initializer, make sure it is a constant expression.
3665 uint32_t static_constant_expression = 0;
3666 if (var.initializer)
3667 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02003668 if (ir.ids[var.initializer].get_type() != TypeConstant)
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02003669 continue;
3670 static_constant_expression = var.initializer;
3671
3672 // There can be no stores to this variable, we have now proved we have a LUT.
3673 if (handler.complete_write_variables_to_block.count(var.self) != 0 ||
3674 handler.partial_write_variables_to_block.count(var.self) != 0)
3675 continue;
3676 }
3677 else
3678 {
3679 // We can have one, and only one write to the variable, and that write needs to be a constant.
3680
3681 // No partial writes allowed.
3682 if (handler.partial_write_variables_to_block.count(var.self) != 0)
3683 continue;
3684
3685 auto itr = handler.complete_write_variables_to_block.find(var.self);
3686
3687 // No writes?
3688 if (itr == end(handler.complete_write_variables_to_block))
3689 continue;
3690
3691 // We write to the variable in more than one block.
3692 auto &write_blocks = itr->second;
3693 if (write_blocks.size() != 1)
3694 continue;
3695
3696 // The write needs to happen in the dominating block.
3697 DominatorBuilder builder(cfg);
3698 for (auto &block : blocks)
3699 builder.add_block(block);
3700 uint32_t dominator = builder.get_dominator();
3701
3702 // The complete write happened in a branch or similar, cannot deduce static expression.
3703 if (write_blocks.count(dominator) == 0)
3704 continue;
3705
3706 // Find the static expression for this variable.
3707 StaticExpressionAccessHandler static_expression_handler(*this, var.self);
3708 traverse_all_reachable_opcodes(get<SPIRBlock>(dominator), static_expression_handler);
3709
3710 // We want one, and exactly one write
3711 if (static_expression_handler.write_count != 1 || static_expression_handler.static_expression == 0)
3712 continue;
3713
3714 // Is it a constant expression?
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02003715 if (ir.ids[static_expression_handler.static_expression].get_type() != TypeConstant)
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02003716 continue;
3717
3718 // We found a LUT!
3719 static_constant_expression = static_expression_handler.static_expression;
3720 }
3721
3722 get<SPIRConstant>(static_constant_expression).is_used_as_lut = true;
3723 var.static_expression = static_constant_expression;
3724 var.statically_assigned = true;
3725 var.remapped_variable = true;
3726 }
3727}
3728
3729void Compiler::analyze_variable_scope(SPIRFunction &entry, AnalyzeVariableScopeAccessHandler &handler)
3730{
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003731 // First, we map out all variable access within a function.
3732 // Essentially a map of block -> { variables accessed in the basic block }
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003733 traverse_all_reachable_opcodes(entry, handler);
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003734
Hans-Kristian Arntzenc26c41b2018-07-04 17:26:53 +02003735 auto &cfg = *function_cfgs.find(entry.self)->second;
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003736
Hans-Kristian Arntzenb2c2e642017-03-25 16:25:30 +01003737 // Analyze if there are parameters which need to be implicitly preserved with an "in" qualifier.
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003738 analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block,
3739 handler.complete_write_variables_to_block);
Hans-Kristian Arntzenb2c2e642017-03-25 16:25:30 +01003740
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003741 unordered_map<uint32_t, uint32_t> potential_loop_variables;
3742
Hans-Kristian Arntzenbf56dc82019-06-06 12:17:46 +02003743 // Find the loop dominator block for each block.
3744 for (auto &block_id : entry.blocks)
3745 {
3746 auto &block = get<SPIRBlock>(block_id);
3747
3748 auto itr = ir.continue_block_to_loop_header.find(block_id);
3749 if (itr != end(ir.continue_block_to_loop_header) && itr->second != block_id)
3750 {
3751 // Continue block might be unreachable in the CFG, but we still like to know the loop dominator.
3752 // Edge case is when continue block is also the loop header, don't set the dominator in this case.
3753 block.loop_dominator = itr->second;
3754 }
3755 else
3756 {
3757 uint32_t loop_dominator = cfg.find_loop_dominator(block_id);
3758 if (loop_dominator != block_id)
3759 block.loop_dominator = loop_dominator;
3760 else
3761 block.loop_dominator = SPIRBlock::NoDominator;
3762 }
3763 }
3764
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003765 // For each variable which is statically accessed.
3766 for (auto &var : handler.accessed_variables_to_block)
3767 {
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003768 // Only deal with variables which are considered local variables in this function.
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003769 if (find(begin(entry.local_variables), end(entry.local_variables), VariableID(var.first)) ==
3770 end(entry.local_variables))
Hans-Kristian Arntzen6fdadb92018-07-04 16:46:25 +02003771 continue;
3772
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003773 DominatorBuilder builder(cfg);
3774 auto &blocks = var.second;
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003775 auto &type = expression_type(var.first);
Hans-Kristian Arntzenf7e98c32022-07-22 13:48:28 +02003776 BlockID potential_continue_block = 0;
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003777
3778 // Figure out which block is dominating all accesses of those variables.
3779 for (auto &block : blocks)
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003780 {
Hans-Kristian Arntzena714d422016-12-16 12:43:12 +01003781 // If we're accessing a variable inside a continue block, this variable might be a loop variable.
3782 // We can only use loop variables with scalars, as we cannot track static expressions for vectors.
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003783 if (is_continue(block))
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003784 {
Hans-Kristian Arntzenb737d2b2017-12-05 17:40:23 +01003785 // Potentially awkward case to check for.
3786 // We might have a variable inside a loop, which is touched by the continue block,
3787 // but is not actually a loop variable.
3788 // The continue block is dominated by the inner part of the loop, which does not make sense in high-level
3789 // language output because it will be declared before the body,
3790 // so we will have to lift the dominator up to the relevant loop header instead.
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02003791 builder.add_block(ir.continue_block_to_loop_header[block]);
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003792
Hans-Kristian Arntzen922420e2018-03-07 14:54:11 +01003793 // Arrays or structs cannot be loop variables.
3794 if (type.vecsize == 1 && type.columns == 1 && type.basetype != SPIRType::Struct && type.array.empty())
Hans-Kristian Arntzenb737d2b2017-12-05 17:40:23 +01003795 {
3796 // The variable is used in multiple continue blocks, this is not a loop
3797 // candidate, signal that by setting block to -1u.
Hans-Kristian Arntzenf7e98c32022-07-22 13:48:28 +02003798 if (potential_continue_block == 0)
3799 potential_continue_block = block;
Hans-Kristian Arntzenb737d2b2017-12-05 17:40:23 +01003800 else
Hans-Kristian Arntzenf7e98c32022-07-22 13:48:28 +02003801 potential_continue_block = ~(0u);
Hans-Kristian Arntzenb737d2b2017-12-05 17:40:23 +01003802 }
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003803 }
Hans-Kristian Arntzenf7e98c32022-07-22 13:48:28 +02003804
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003805 builder.add_block(block);
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003806 }
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003807
Hans-Kristian Arntzen5ff11cc2016-11-18 16:45:11 +01003808 builder.lift_continue_block_dominator();
3809
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003810 // Add it to a per-block list of variables.
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003811 BlockID dominating_block = builder.get_dominator();
Hans-Kristian Arntzenb737d2b2017-12-05 17:40:23 +01003812
Hans-Kristian Arntzenf7e98c32022-07-22 13:48:28 +02003813 if (dominating_block && potential_continue_block != 0 && potential_continue_block != ~0u)
3814 {
3815 auto &inner_block = get<SPIRBlock>(dominating_block);
3816
3817 BlockID merge_candidate = 0;
3818
3819 // Analyze the dominator. If it lives in a different loop scope than the candidate continue
3820 // block, reject the loop variable candidate.
3821 if (inner_block.merge == SPIRBlock::MergeLoop)
3822 merge_candidate = inner_block.merge_block;
3823 else if (inner_block.loop_dominator != SPIRBlock::NoDominator)
3824 merge_candidate = get<SPIRBlock>(inner_block.loop_dominator).merge_block;
3825
3826 if (merge_candidate != 0 && cfg.is_reachable(merge_candidate))
3827 {
3828 // If the merge block has a higher post-visit order, we know that continue candidate
3829 // cannot reach the merge block, and we have two separate scopes.
3830 if (!cfg.is_reachable(potential_continue_block) ||
3831 cfg.get_visit_order(merge_candidate) > cfg.get_visit_order(potential_continue_block))
3832 {
3833 potential_continue_block = 0;
3834 }
3835 }
3836 }
3837
3838 if (potential_continue_block != 0 && potential_continue_block != ~0u)
3839 potential_loop_variables[var.first] = potential_continue_block;
3840
Hans-Kristian Arntzen03d93ab2019-06-06 11:10:39 +02003841 // For variables whose dominating block is inside a loop, there is a risk that these variables
3842 // actually need to be preserved across loop iterations. We can express this by adding
3843 // a "read" access to the loop header.
3844 // In the dominating block, we must see an OpStore or equivalent as the first access of an OpVariable.
Hans-Kristian Arntzen6b52b0f2019-06-06 14:37:02 +02003845 // Should that fail, we look for the outermost loop header and tack on an access there.
Hans-Kristian Arntzen03d93ab2019-06-06 11:10:39 +02003846 // Phi nodes cannot have this problem.
3847 if (dominating_block)
3848 {
3849 auto &variable = get<SPIRVariable>(var.first);
3850 if (!variable.phi_variable)
3851 {
Hans-Kristian Arntzen6b52b0f2019-06-06 14:37:02 +02003852 auto *block = &get<SPIRBlock>(dominating_block);
3853 bool preserve = may_read_undefined_variable_in_block(*block, var.first);
Hans-Kristian Arntzen03d93ab2019-06-06 11:10:39 +02003854 if (preserve)
3855 {
Hans-Kristian Arntzen6b52b0f2019-06-06 14:37:02 +02003856 // Find the outermost loop scope.
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003857 while (block->loop_dominator != BlockID(SPIRBlock::NoDominator))
Hans-Kristian Arntzen6b52b0f2019-06-06 14:37:02 +02003858 block = &get<SPIRBlock>(block->loop_dominator);
3859
3860 if (block->self != dominating_block)
Hans-Kristian Arntzen03d93ab2019-06-06 11:10:39 +02003861 {
Hans-Kristian Arntzen6b52b0f2019-06-06 14:37:02 +02003862 builder.add_block(block->self);
Hans-Kristian Arntzen03d93ab2019-06-06 11:10:39 +02003863 dominating_block = builder.get_dominator();
3864 }
3865 }
3866 }
3867 }
3868
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +01003869 // If all blocks here are dead code, this will be 0, so the variable in question
3870 // will be completely eliminated.
3871 if (dominating_block)
3872 {
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003873 auto &block = get<SPIRBlock>(dominating_block);
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +01003874 block.dominated_variables.push_back(var.first);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003875 get<SPIRVariable>(var.first).dominator = dominating_block;
Hans-Kristian Arntzenedbe8672016-11-17 22:15:07 +01003876 }
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01003877 }
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003878
Hans-Kristian Arntzend4e470b2018-01-12 10:42:39 +01003879 for (auto &var : handler.accessed_temporaries_to_block)
3880 {
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01003881 auto itr = handler.result_id_to_type.find(var.first);
3882
3883 if (itr == end(handler.result_id_to_type))
3884 {
3885 // We found a false positive ID being used, ignore.
3886 // This should probably be an assert.
3887 continue;
3888 }
3889
Hans-Kristian Arntzend2cc43e2019-02-19 17:00:49 +01003890 // There is no point in doing domination analysis for opaque types.
3891 auto &type = get<SPIRType>(itr->second);
3892 if (type_is_opaque_value(type))
3893 continue;
3894
Hans-Kristian Arntzend4e470b2018-01-12 10:42:39 +01003895 DominatorBuilder builder(cfg);
Hans-Kristian Arntzen9fbd8b72018-03-12 14:58:40 +01003896 bool force_temporary = false;
Hans-Kristian Arntzen648dfa52019-10-28 10:57:51 +01003897 bool used_in_header_hoisted_continue_block = false;
Hans-Kristian Arntzend4e470b2018-01-12 10:42:39 +01003898
3899 // Figure out which block is dominating all accesses of those temporaries.
3900 auto &blocks = var.second;
3901 for (auto &block : blocks)
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01003902 {
Hans-Kristian Arntzend4e470b2018-01-12 10:42:39 +01003903 builder.add_block(block);
3904
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003905 if (blocks.size() != 1 && is_continue(block))
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003906 {
Hans-Kristian Arntzend620f1d2019-07-26 10:39:05 +02003907 // The risk here is that inner loop can dominate the continue block.
3908 // Any temporary we access in the continue block must be declared before the loop.
3909 // This is moot for complex loops however.
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003910 auto &loop_header_block = get<SPIRBlock>(ir.continue_block_to_loop_header[block]);
3911 assert(loop_header_block.merge == SPIRBlock::MergeLoop);
Hans-Kristian Arntzen648dfa52019-10-28 10:57:51 +01003912 builder.add_block(loop_header_block.self);
3913 used_in_header_hoisted_continue_block = true;
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003914 }
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01003915 }
Hans-Kristian Arntzend4e470b2018-01-12 10:42:39 +01003916
3917 uint32_t dominating_block = builder.get_dominator();
Hans-Kristian Arntzend620f1d2019-07-26 10:39:05 +02003918
3919 if (blocks.size() != 1 && is_single_block_loop(dominating_block))
3920 {
3921 // Awkward case, because the loop header is also the continue block,
3922 // so hoisting to loop header does not help.
3923 force_temporary = true;
3924 }
3925
Hans-Kristian Arntzend4e470b2018-01-12 10:42:39 +01003926 if (dominating_block)
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01003927 {
3928 // If we touch a variable in the dominating block, this is the expected setup.
3929 // SPIR-V normally mandates this, but we have extra cases for temporary use inside loops.
3930 bool first_use_is_dominator = blocks.count(dominating_block) != 0;
3931
Hans-Kristian Arntzen9fbd8b72018-03-12 14:58:40 +01003932 if (!first_use_is_dominator || force_temporary)
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01003933 {
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003934 if (handler.access_chain_expressions.count(var.first))
3935 {
3936 // Exceptionally rare case.
3937 // We cannot declare temporaries of access chains (except on MSL perhaps with pointers).
Hans-Kristian Arntzen648dfa52019-10-28 10:57:51 +01003938 // Rather than do that, we force the indexing expressions to be declared in the right scope by
3939 // tracking their usage to that end. There is no temporary to hoist.
3940 // However, we still need to observe declaration order of the access chain.
3941
3942 if (used_in_header_hoisted_continue_block)
3943 {
3944 // For this scenario, we used an access chain inside a continue block where we also registered an access to header block.
3945 // This is a problem as we need to declare an access chain properly first with full definition.
3946 // We cannot use temporaries for these expressions,
3947 // so we must make sure the access chain is declared ahead of time.
3948 // Force a complex for loop to deal with this.
3949 // TODO: Out-of-order declaring for loops where continue blocks are emitted last might be another option.
3950 auto &loop_header_block = get<SPIRBlock>(dominating_block);
3951 assert(loop_header_block.merge == SPIRBlock::MergeLoop);
3952 loop_header_block.complex_continue = true;
3953 }
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003954 }
3955 else
3956 {
3957 // This should be very rare, but if we try to declare a temporary inside a loop,
3958 // and that temporary is used outside the loop as well (spirv-opt inliner likes this)
3959 // we should actually emit the temporary outside the loop.
3960 hoisted_temporaries.insert(var.first);
3961 forced_temporaries.insert(var.first);
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01003962
Hans-Kristian Arntzene23c9ea2019-04-10 15:46:48 +02003963 auto &block_temporaries = get<SPIRBlock>(dominating_block).declare_temporary;
3964 block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first);
3965 }
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01003966 }
Hans-Kristian Arntzenc1947aa2018-03-24 04:16:18 +01003967 else if (blocks.size() > 1)
3968 {
3969 // Keep track of the temporary as we might have to declare this temporary.
3970 // This can happen if the loop header dominates a temporary, but we have a complex fallback loop.
3971 // In this case, the header is actually inside the for (;;) {} block, and we have problems.
3972 // What we need to do is hoist the temporaries outside the for (;;) {} block in case the header block
3973 // declares the temporary.
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003974 auto &block_temporaries = get<SPIRBlock>(dominating_block).potential_declare_temporary;
Hans-Kristian Arntzenc1947aa2018-03-24 04:16:18 +01003975 block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first);
3976 }
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01003977 }
Hans-Kristian Arntzend4e470b2018-01-12 10:42:39 +01003978 }
3979
Hans-Kristian Arntzenf0c50a62017-07-25 18:22:15 +02003980 unordered_set<uint32_t> seen_blocks;
3981
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003982 // Now, try to analyze whether or not these variables are actually loop variables.
3983 for (auto &loop_variable : potential_loop_variables)
3984 {
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02003985 auto &var = get<SPIRVariable>(loop_variable.first);
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003986 auto dominator = var.dominator;
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003987 BlockID block = loop_variable.second;
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003988
3989 // The variable was accessed in multiple continue blocks, ignore.
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003990 if (block == BlockID(~(0u)) || block == BlockID(0))
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003991 continue;
3992
3993 // Dead code.
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003994 if (dominator == ID(0))
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003995 continue;
3996
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02003997 BlockID header = 0;
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01003998
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02003999 // Find the loop header for this block if we are a continue block.
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004000 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004001 auto itr = ir.continue_block_to_loop_header.find(block);
4002 if (itr != end(ir.continue_block_to_loop_header))
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004003 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004004 header = itr->second;
4005 }
4006 else if (get<SPIRBlock>(block).continue_block == block)
4007 {
4008 // Also check for self-referential continue block.
4009 header = block;
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004010 }
4011 }
4012
4013 assert(header);
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02004014 auto &header_block = get<SPIRBlock>(header);
Hans-Kristian Arntzen4a7a3722018-01-23 20:27:43 +01004015 auto &blocks = handler.accessed_variables_to_block[loop_variable.first];
4016
4017 // If a loop variable is not used before the loop, it's probably not a loop variable.
4018 bool has_accessed_variable = blocks.count(header) != 0;
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004019
4020 // Now, there are two conditions we need to meet for the variable to be a loop variable.
4021 // 1. The dominating block must have a branch-free path to the loop header,
4022 // this way we statically know which expression should be part of the loop variable initializer.
4023
4024 // Walk from the dominator, if there is one straight edge connecting
4025 // dominator and loop header, we statically know the loop initializer.
4026 bool static_loop_init = true;
4027 while (dominator != header)
4028 {
Hans-Kristian Arntzen4a7a3722018-01-23 20:27:43 +01004029 if (blocks.count(dominator) != 0)
4030 has_accessed_variable = true;
4031
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004032 auto &succ = cfg.get_succeeding_edges(dominator);
4033 if (succ.size() != 1)
4034 {
4035 static_loop_init = false;
4036 break;
4037 }
4038
4039 auto &pred = cfg.get_preceding_edges(succ.front());
4040 if (pred.size() != 1 || pred.front() != dominator)
4041 {
4042 static_loop_init = false;
4043 break;
4044 }
4045
4046 dominator = succ.front();
4047 }
4048
Hans-Kristian Arntzen4a7a3722018-01-23 20:27:43 +01004049 if (!static_loop_init || !has_accessed_variable)
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004050 continue;
4051
4052 // The second condition we need to meet is that no access after the loop
4053 // merge can occur. Walk the CFG to see if we find anything.
Hans-Kristian Arntzenf0c50a62017-07-25 18:22:15 +02004054
4055 seen_blocks.clear();
Hans-Kristian Arntzen5d97dae2019-08-26 15:36:23 +02004056 cfg.walk_from(seen_blocks, header_block.merge_block, [&](uint32_t walk_block) -> bool {
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004057 // We found a block which accesses the variable outside the loop.
4058 if (blocks.find(walk_block) != end(blocks))
4059 static_loop_init = false;
Hans-Kristian Arntzen5d97dae2019-08-26 15:36:23 +02004060 return true;
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004061 });
4062
4063 if (!static_loop_init)
4064 continue;
4065
4066 // We have a loop variable.
4067 header_block.loop_variables.push_back(loop_variable.first);
Hans-Kristian Arntzen44b32162016-12-16 14:01:09 +01004068 // Need to sort here as variables come from an unordered container, and pushing stuff in wrong order
4069 // will break reproducability in regression runs.
4070 sort(begin(header_block.loop_variables), end(header_block.loop_variables));
Hans-Kristian Arntzen72161292018-07-04 16:20:06 +02004071 get<SPIRVariable>(loop_variable.first).loop_variable = true;
Hans-Kristian Arntzen4f07a322016-12-15 17:14:47 +01004072 }
Hans-Kristian Arntzendad4a342016-11-11 18:04:14 +01004073}
Hans-Kristian Arntzen016b1d82017-01-21 10:07:38 +01004074
Hans-Kristian Arntzen03d93ab2019-06-06 11:10:39 +02004075bool Compiler::may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var)
4076{
4077 for (auto &op : block.ops)
4078 {
4079 auto *ops = stream(op);
4080 switch (op.op)
4081 {
4082 case OpStore:
4083 case OpCopyMemory:
4084 if (ops[0] == var)
4085 return false;
4086 break;
4087
4088 case OpAccessChain:
4089 case OpInBoundsAccessChain:
4090 case OpPtrAccessChain:
4091 // Access chains are generally used to partially read and write. It's too hard to analyze
4092 // if all constituents are written fully before continuing, so just assume it's preserved.
4093 // This is the same as the parameter preservation analysis.
4094 if (ops[2] == var)
4095 return true;
4096 break;
4097
4098 case OpSelect:
4099 // Variable pointers.
4100 // We might read before writing.
4101 if (ops[3] == var || ops[4] == var)
4102 return true;
4103 break;
4104
4105 case OpPhi:
4106 {
4107 // Variable pointers.
4108 // We might read before writing.
4109 if (op.length < 2)
4110 break;
4111
4112 uint32_t count = op.length - 2;
4113 for (uint32_t i = 0; i < count; i += 2)
4114 if (ops[i + 2] == var)
4115 return true;
4116 break;
4117 }
4118
4119 case OpCopyObject:
4120 case OpLoad:
4121 if (ops[2] == var)
4122 return true;
4123 break;
4124
4125 case OpFunctionCall:
4126 {
4127 if (op.length < 3)
4128 break;
4129
4130 // May read before writing.
4131 uint32_t count = op.length - 3;
4132 for (uint32_t i = 0; i < count; i++)
4133 if (ops[i + 3] == var)
4134 return true;
4135 break;
4136 }
4137
4138 default:
4139 break;
4140 }
4141 }
4142
4143 // Not accessed somehow, at least not in a usual fashion.
4144 // It's likely accessed in a branch, so assume we must preserve.
4145 return true;
4146}
4147
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02004148Bitset Compiler::get_buffer_block_flags(VariableID id) const
Hans-Kristian Arntzen3a9b0452018-06-03 12:00:22 +02004149{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004150 return ir.get_buffer_block_flags(get<SPIRVariable>(id));
Hans-Kristian Arntzen016b1d82017-01-21 10:07:38 +01004151}
Hans-Kristian Arntzen95409792017-01-21 12:29:20 +01004152
4153bool Compiler::get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type)
4154{
4155 if (type.basetype == SPIRType::Struct)
4156 {
4157 base_type = SPIRType::Unknown;
4158 for (auto &member_type : type.member_types)
4159 {
4160 SPIRType::BaseType member_base;
4161 if (!get_common_basic_type(get<SPIRType>(member_type), member_base))
4162 return false;
4163
4164 if (base_type == SPIRType::Unknown)
4165 base_type = member_base;
4166 else if (base_type != member_base)
4167 return false;
4168 }
4169 return true;
4170 }
4171 else
4172 {
4173 base_type = type.basetype;
4174 return true;
4175 }
4176}
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004177
Hans-Kristian Arntzen719cf9d2018-03-13 14:05:33 +01004178void Compiler::ActiveBuiltinHandler::handle_builtin(const SPIRType &type, BuiltIn builtin,
4179 const Bitset &decoration_flags)
Hans-Kristian Arntzenfb3f92a2018-02-22 14:36:50 +01004180{
4181 // If used, we will need to explicitly declare a new array size for these builtins.
4182
4183 if (builtin == BuiltInClipDistance)
4184 {
4185 if (!type.array_size_literal[0])
4186 SPIRV_CROSS_THROW("Array size for ClipDistance must be a literal.");
4187 uint32_t array_size = type.array[0];
4188 if (array_size == 0)
4189 SPIRV_CROSS_THROW("Array size for ClipDistance must not be unsized.");
4190 compiler.clip_distance_count = array_size;
4191 }
4192 else if (builtin == BuiltInCullDistance)
4193 {
4194 if (!type.array_size_literal[0])
4195 SPIRV_CROSS_THROW("Array size for CullDistance must be a literal.");
4196 uint32_t array_size = type.array[0];
4197 if (array_size == 0)
4198 SPIRV_CROSS_THROW("Array size for CullDistance must not be unsized.");
4199 compiler.cull_distance_count = array_size;
4200 }
Hans-Kristian Arntzen3c1b1472018-03-01 12:30:55 +01004201 else if (builtin == BuiltInPosition)
4202 {
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01004203 if (decoration_flags.get(DecorationInvariant))
Hans-Kristian Arntzen3c1b1472018-03-01 12:30:55 +01004204 compiler.position_invariant = true;
4205 }
Hans-Kristian Arntzenfb3f92a2018-02-22 14:36:50 +01004206}
4207
Hans-Kristian Arntzen014b3bc2021-01-07 12:00:21 +01004208void Compiler::ActiveBuiltinHandler::add_if_builtin(uint32_t id, bool allow_blocks)
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004209{
Hans-Kristian Arntzen014b3bc2021-01-07 12:00:21 +01004210 // Only handle plain variables here.
4211 // Builtins which are part of a block are handled in AccessChain.
4212 // If allow_blocks is used however, this is to handle initializers of blocks,
4213 // which implies that all members are written to.
4214
4215 auto *var = compiler.maybe_get<SPIRVariable>(id);
4216 auto *m = compiler.ir.find_meta(id);
4217 if (var && m)
4218 {
4219 auto &type = compiler.get<SPIRType>(var->basetype);
4220 auto &decorations = m->decoration;
4221 auto &flags = type.storage == StorageClassInput ?
4222 compiler.active_input_builtins : compiler.active_output_builtins;
4223 if (decorations.builtin)
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01004224 {
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01004225 flags.set(decorations.builtin_type);
Hans-Kristian Arntzen3c1b1472018-03-01 12:30:55 +01004226 handle_builtin(type, decorations.builtin_type, decorations.decoration_flags);
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01004227 }
Hans-Kristian Arntzen014b3bc2021-01-07 12:00:21 +01004228 else if (allow_blocks && compiler.has_decoration(type.self, DecorationBlock))
4229 {
4230 uint32_t member_count = uint32_t(type.member_types.size());
4231 for (uint32_t i = 0; i < member_count; i++)
4232 {
4233 if (compiler.has_member_decoration(type.self, i, DecorationBuiltIn))
4234 {
4235 auto &member_type = compiler.get<SPIRType>(type.member_types[i]);
4236 BuiltIn builtin = BuiltIn(compiler.get_member_decoration(type.self, i, DecorationBuiltIn));
4237 flags.set(builtin);
4238 handle_builtin(member_type, builtin, compiler.get_member_decoration_bitset(type.self, i));
4239 }
4240 }
4241 }
4242 }
4243}
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004244
Hans-Kristian Arntzen014b3bc2021-01-07 12:00:21 +01004245void Compiler::ActiveBuiltinHandler::add_if_builtin(uint32_t id)
4246{
4247 add_if_builtin(id, false);
4248}
4249
4250void Compiler::ActiveBuiltinHandler::add_if_builtin_or_block(uint32_t id)
4251{
4252 add_if_builtin(id, true);
4253}
4254
4255bool Compiler::ActiveBuiltinHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t length)
4256{
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004257 switch (opcode)
4258 {
4259 case OpStore:
4260 if (length < 1)
4261 return false;
4262
4263 add_if_builtin(args[0]);
4264 break;
4265
4266 case OpCopyMemory:
4267 if (length < 2)
4268 return false;
4269
4270 add_if_builtin(args[0]);
4271 add_if_builtin(args[1]);
4272 break;
4273
4274 case OpCopyObject:
4275 case OpLoad:
4276 if (length < 3)
4277 return false;
4278
4279 add_if_builtin(args[2]);
4280 break;
4281
Chip Davis3bfb2f92018-12-03 02:06:33 -06004282 case OpSelect:
4283 if (length < 5)
4284 return false;
4285
4286 add_if_builtin(args[3]);
4287 add_if_builtin(args[4]);
4288 break;
4289
4290 case OpPhi:
4291 {
4292 if (length < 2)
4293 return false;
4294
4295 uint32_t count = length - 2;
4296 args += 2;
4297 for (uint32_t i = 0; i < count; i += 2)
4298 add_if_builtin(args[i]);
4299 break;
4300 }
4301
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004302 case OpFunctionCall:
4303 {
4304 if (length < 3)
4305 return false;
4306
4307 uint32_t count = length - 3;
4308 args += 3;
4309 for (uint32_t i = 0; i < count; i++)
4310 add_if_builtin(args[i]);
4311 break;
4312 }
4313
4314 case OpAccessChain:
4315 case OpInBoundsAccessChain:
Chip Davis3bfb2f92018-12-03 02:06:33 -06004316 case OpPtrAccessChain:
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004317 {
4318 if (length < 4)
4319 return false;
4320
4321 // Only consider global variables, cannot consider variables in functions yet, or other
4322 // access chains as they have not been created yet.
4323 auto *var = compiler.maybe_get<SPIRVariable>(args[2]);
4324 if (!var)
4325 break;
4326
Hans-Kristian Arntzen945425e2017-08-15 10:23:04 +02004327 // Required if we access chain into builtins like gl_GlobalInvocationID.
4328 add_if_builtin(args[2]);
4329
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004330 // Start traversing type hierarchy at the proper non-pointer types.
Chip Davis3bfb2f92018-12-03 02:06:33 -06004331 auto *type = &compiler.get_variable_data_type(*var);
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004332
Hans-Kristian Arntzen2ebe1a82017-03-06 16:50:46 +01004333 auto &flags =
Chip Daviseb89c3a2019-02-03 23:58:46 -06004334 var->storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins;
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01004335
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004336 uint32_t count = length - 3;
4337 args += 3;
4338 for (uint32_t i = 0; i < count; i++)
4339 {
Chip Davis3bfb2f92018-12-03 02:06:33 -06004340 // Pointers
4341 if (opcode == OpPtrAccessChain && i == 0)
4342 {
4343 type = &compiler.get<SPIRType>(type->parent_type);
4344 continue;
4345 }
4346
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004347 // Arrays
4348 if (!type->array.empty())
4349 {
4350 type = &compiler.get<SPIRType>(type->parent_type);
4351 }
4352 // Structs
4353 else if (type->basetype == SPIRType::Struct)
4354 {
4355 uint32_t index = compiler.get<SPIRConstant>(args[i]).scalar();
4356
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004357 if (index < uint32_t(compiler.ir.meta[type->self].members.size()))
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004358 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004359 auto &decorations = compiler.ir.meta[type->self].members[index];
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004360 if (decorations.builtin)
Hans-Kristian Arntzenfb3f92a2018-02-22 14:36:50 +01004361 {
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01004362 flags.set(decorations.builtin_type);
Hans-Kristian Arntzen3c1b1472018-03-01 12:30:55 +01004363 handle_builtin(compiler.get<SPIRType>(type->member_types[index]), decorations.builtin_type,
4364 decorations.decoration_flags);
Hans-Kristian Arntzenfb3f92a2018-02-22 14:36:50 +01004365 }
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004366 }
4367
4368 type = &compiler.get<SPIRType>(type->member_types[index]);
4369 }
4370 else
4371 {
4372 // No point in traversing further. We won't find any extra builtins.
4373 break;
4374 }
4375 }
4376 break;
4377 }
4378
4379 default:
4380 break;
4381 }
4382
4383 return true;
4384}
4385
4386void Compiler::update_active_builtins()
4387{
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01004388 active_input_builtins.reset();
4389 active_output_builtins.reset();
Hans-Kristian Arntzenfb3f92a2018-02-22 14:36:50 +01004390 cull_distance_count = 0;
4391 clip_distance_count = 0;
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004392 ActiveBuiltinHandler handler(*this);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004393 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Hans-Kristian Arntzen014b3bc2021-01-07 12:00:21 +01004394
4395 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
4396 if (var.storage != StorageClassOutput)
4397 return;
4398 if (!interface_variable_exists_in_entry_point(var.self))
4399 return;
4400
4401 // Also, make sure we preserve output variables which are only initialized, but never accessed by any code.
4402 if (var.initializer != ID(0))
4403 handler.add_if_builtin_or_block(var.self);
4404 });
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01004405}
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004406
Bill Hollings192bdc92017-05-24 09:31:38 -04004407// Returns whether this shader uses a builtin of the storage class
Hans-Kristian Arntzenb4a380a2021-04-16 13:32:37 +02004408bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage) const
Bill Hollings192bdc92017-05-24 09:31:38 -04004409{
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01004410 const Bitset *flags;
Bill Hollings192bdc92017-05-24 09:31:38 -04004411 switch (storage)
4412 {
4413 case StorageClassInput:
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01004414 flags = &active_input_builtins;
Bill Hollings192bdc92017-05-24 09:31:38 -04004415 break;
4416 case StorageClassOutput:
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01004417 flags = &active_output_builtins;
Bill Hollings192bdc92017-05-24 09:31:38 -04004418 break;
4419
4420 default:
4421 return false;
4422 }
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01004423 return flags->get(builtin);
Bill Hollings192bdc92017-05-24 09:31:38 -04004424}
4425
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01004426void Compiler::analyze_image_and_sampler_usage()
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004427{
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004428 CombinedImageSamplerDrefHandler dref_handler(*this);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004429 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), dref_handler);
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004430
4431 CombinedImageSamplerUsageHandler handler(*this, dref_handler.dref_combined_samplers);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004432 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Hans-Kristian Arntzen8066d132019-10-24 16:34:51 +02004433
4434 // Need to run this traversal twice. First time, we propagate any comparison sampler usage from leaf functions
4435 // down to main().
4436 // In the second pass, we can propagate up forced depth state coming from main() up into leaf functions.
4437 handler.dependency_hierarchy.clear();
4438 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
4439
Daniel Thornburgh44c33332022-03-02 23:02:38 +00004440 comparison_ids = std::move(handler.comparison_ids);
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01004441 need_subpass_input = handler.need_subpass_input;
Yuwen Wu1b9296e2022-09-13 18:50:57 +08004442 need_subpass_input_ms = handler.need_subpass_input_ms;
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004443
4444 // Forward information from separate images and samplers into combined image samplers.
4445 for (auto &combined : combined_image_samplers)
4446 if (comparison_ids.count(combined.sampler_id))
4447 comparison_ids.insert(combined.combined_id);
4448}
4449
4450bool Compiler::CombinedImageSamplerDrefHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t)
4451{
4452 // Mark all sampled images which are used with Dref.
4453 switch (opcode)
4454 {
4455 case OpImageSampleDrefExplicitLod:
4456 case OpImageSampleDrefImplicitLod:
4457 case OpImageSampleProjDrefExplicitLod:
4458 case OpImageSampleProjDrefImplicitLod:
4459 case OpImageSparseSampleProjDrefImplicitLod:
4460 case OpImageSparseSampleDrefImplicitLod:
4461 case OpImageSparseSampleProjDrefExplicitLod:
4462 case OpImageSparseSampleDrefExplicitLod:
4463 case OpImageDrefGather:
4464 case OpImageSparseDrefGather:
4465 dref_combined_samplers.insert(args[2]);
4466 return true;
4467
4468 default:
4469 break;
4470 }
4471
4472 return true;
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004473}
4474
Hans-Kristian Arntzen5d97dae2019-08-26 15:36:23 +02004475const CFG &Compiler::get_cfg_for_current_function() const
4476{
4477 assert(current_function);
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02004478 return get_cfg_for_function(current_function->self);
4479}
4480
4481const CFG &Compiler::get_cfg_for_function(uint32_t id) const
4482{
4483 auto cfg_itr = function_cfgs.find(id);
Hans-Kristian Arntzen5d97dae2019-08-26 15:36:23 +02004484 assert(cfg_itr != end(function_cfgs));
4485 assert(cfg_itr->second);
4486 return *cfg_itr->second;
4487}
4488
Hans-Kristian Arntzenb5ed7062018-07-05 10:42:05 +02004489void Compiler::build_function_control_flow_graphs_and_analyze()
Hans-Kristian Arntzenc26c41b2018-07-04 17:26:53 +02004490{
4491 CFGBuilder handler(*this);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004492 handler.function_cfgs[ir.default_entry_point].reset(new CFG(*this, get<SPIRFunction>(ir.default_entry_point)));
4493 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Daniel Thornburgh44c33332022-03-02 23:02:38 +00004494 function_cfgs = std::move(handler.function_cfgs);
Hans-Kristian Arntzen3e584f22019-02-06 10:38:18 +01004495 bool single_function = function_cfgs.size() <= 1;
Hans-Kristian Arntzenb5ed7062018-07-05 10:42:05 +02004496
4497 for (auto &f : function_cfgs)
4498 {
4499 auto &func = get<SPIRFunction>(f.first);
Hans-Kristian Arntzend29f48e2018-07-05 13:25:57 +02004500 AnalyzeVariableScopeAccessHandler scope_handler(*this, func);
4501 analyze_variable_scope(func, scope_handler);
Hans-Kristian Arntzen3e584f22019-02-06 10:38:18 +01004502 find_function_local_luts(func, scope_handler, single_function);
Hans-Kristian Arntzenb5ed7062018-07-05 10:42:05 +02004503
4504 // Check if we can actually use the loop variables we found in analyze_variable_scope.
4505 // To use multiple initializers, we need the same type and qualifiers.
4506 for (auto block : func.blocks)
4507 {
4508 auto &b = get<SPIRBlock>(block);
4509 if (b.loop_variables.size() < 2)
4510 continue;
4511
4512 auto &flags = get_decoration_bitset(b.loop_variables.front());
4513 uint32_t type = get<SPIRVariable>(b.loop_variables.front()).basetype;
4514 bool invalid_initializers = false;
4515 for (auto loop_variable : b.loop_variables)
4516 {
4517 if (flags != get_decoration_bitset(loop_variable) ||
4518 type != get<SPIRVariable>(b.loop_variables.front()).basetype)
4519 {
4520 invalid_initializers = true;
4521 break;
4522 }
4523 }
4524
4525 if (invalid_initializers)
4526 {
4527 for (auto loop_variable : b.loop_variables)
4528 get<SPIRVariable>(loop_variable).loop_variable = false;
4529 b.loop_variables.clear();
4530 }
4531 }
4532 }
Hans-Kristian Arntzenc26c41b2018-07-04 17:26:53 +02004533}
4534
Hans-Kristian Arntzen9b92e682019-03-29 10:29:44 +01004535Compiler::CFGBuilder::CFGBuilder(Compiler &compiler_)
Hans-Kristian Arntzen8c314112018-07-05 14:18:34 +02004536 : compiler(compiler_)
Hans-Kristian Arntzenc26c41b2018-07-04 17:26:53 +02004537{
4538}
4539
4540bool Compiler::CFGBuilder::handle(spv::Op, const uint32_t *, uint32_t)
4541{
4542 return true;
4543}
4544
4545bool Compiler::CFGBuilder::follow_function_call(const SPIRFunction &func)
4546{
4547 if (function_cfgs.find(func.self) == end(function_cfgs))
4548 {
4549 function_cfgs[func.self].reset(new CFG(compiler, func));
4550 return true;
4551 }
4552 else
4553 return false;
4554}
4555
Hans-Kristian Arntzen8066d132019-10-24 16:34:51 +02004556void Compiler::CombinedImageSamplerUsageHandler::add_dependency(uint32_t dst, uint32_t src)
4557{
4558 dependency_hierarchy[dst].insert(src);
4559 // Propagate up any comparison state if we're loading from one such variable.
4560 if (comparison_ids.count(src))
4561 comparison_ids.insert(dst);
4562}
4563
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004564bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length)
4565{
4566 if (length < 3)
4567 return false;
4568
4569 auto &func = compiler.get<SPIRFunction>(args[2]);
4570 const auto *arg = &args[3];
4571 length -= 3;
4572
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004573 for (uint32_t i = 0; i < length; i++)
4574 {
4575 auto &argument = func.arguments[i];
Hans-Kristian Arntzen8066d132019-10-24 16:34:51 +02004576 add_dependency(argument.id, arg[i]);
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004577 }
4578
4579 return true;
4580}
4581
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004582void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_ids(uint32_t id)
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01004583{
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004584 // Traverse the variable dependency hierarchy and tag everything in its path with comparison ids.
4585 comparison_ids.insert(id);
Lukas Hermanns7ad0a842019-09-23 18:05:04 -04004586
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004587 for (auto &dep_id : dependency_hierarchy[id])
4588 add_hierarchy_to_comparison_ids(dep_id);
Hans-Kristian Arntzen07ee7d02017-05-06 13:53:06 +02004589}
4590
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004591bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
4592{
4593 switch (opcode)
4594 {
Hans-Kristian Arntzen07ee7d02017-05-06 13:53:06 +02004595 case OpAccessChain:
4596 case OpInBoundsAccessChain:
Chip Davis3bfb2f92018-12-03 02:06:33 -06004597 case OpPtrAccessChain:
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004598 case OpLoad:
4599 {
4600 if (length < 3)
4601 return false;
Hans-Kristian Arntzen8066d132019-10-24 16:34:51 +02004602
4603 add_dependency(args[1], args[2]);
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01004604
4605 // Ideally defer this to OpImageRead, but then we'd need to track loaded IDs.
4606 // If we load an image, we're going to use it and there is little harm in declaring an unused gl_FragCoord.
4607 auto &type = compiler.get<SPIRType>(args[0]);
4608 if (type.image.dim == DimSubpassData)
Yuwen Wu1b9296e2022-09-13 18:50:57 +08004609 {
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01004610 need_subpass_input = true;
Yuwen Wu1b9296e2022-09-13 18:50:57 +08004611 if (type.image.ms)
4612 need_subpass_input_ms = true;
4613 }
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004614
4615 // If we load a SampledImage and it will be used with Dref, propagate the state up.
4616 if (dref_combined_samplers.count(args[1]) != 0)
4617 add_hierarchy_to_comparison_ids(args[1]);
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004618 break;
4619 }
4620
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004621 case OpSampledImage:
4622 {
4623 if (length < 4)
4624 return false;
4625
Lukas Hermanns50ac6862019-09-18 14:03:54 -04004626 // If the underlying resource has been used for comparison then duplicate loads of that resource must be too.
Mark Satterthwaite32557e92019-08-14 10:46:23 -04004627 // This image must be a depth image.
Bill Hollingsfd252b22021-11-08 15:59:45 -05004628 uint32_t result_id = args[1];
Mark Satterthwaite32557e92019-08-14 10:46:23 -04004629 uint32_t image = args[2];
4630 uint32_t sampler = args[3];
4631
Bill Hollingsfd252b22021-11-08 15:59:45 -05004632 if (dref_combined_samplers.count(result_id) != 0)
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004633 {
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004634 add_hierarchy_to_comparison_ids(image);
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01004635
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004636 // This sampler must be a SamplerComparisonState, and not a regular SamplerState.
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004637 add_hierarchy_to_comparison_ids(sampler);
4638
4639 // Mark the OpSampledImage itself as being comparison state.
4640 comparison_ids.insert(result_id);
Hans-Kristian Arntzenf4d72682017-05-06 13:21:35 +02004641 }
4642 return true;
4643 }
4644
4645 default:
4646 break;
4647 }
4648
4649 return true;
4650}
Hans-Kristian Arntzenec45c9e2017-04-19 17:33:14 +02004651
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02004652bool Compiler::buffer_is_hlsl_counter_buffer(VariableID id) const
Hans-Kristian Arntzenec45c9e2017-04-19 17:33:14 +02004653{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004654 auto *m = ir.find_meta(id);
4655 return m && m->hlsl_is_magic_counter_buffer;
Hans-Kristian Arntzenec45c9e2017-04-19 17:33:14 +02004656}
4657
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02004658bool Compiler::buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const
Hans-Kristian Arntzenec45c9e2017-04-19 17:33:14 +02004659{
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004660 auto *m = ir.find_meta(id);
4661
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01004662 // First, check for the proper decoration.
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004663 if (m && m->hlsl_magic_counter_buffer != 0)
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01004664 {
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004665 counter_id = m->hlsl_magic_counter_buffer;
Hans-Kristian Arntzen215d3ca2018-03-20 20:04:12 +01004666 return true;
4667 }
Hans-Kristian Arntzen9aa623a2018-11-22 10:23:58 +01004668 else
4669 return false;
Hans-Kristian Arntzenec45c9e2017-04-19 17:33:14 +02004670}
Hans-Kristian Arntzen48ccde32017-08-03 14:32:07 +02004671
4672void Compiler::make_constant_null(uint32_t id, uint32_t type)
4673{
4674 auto &constant_type = get<SPIRType>(type);
4675
Chip Davis3bfb2f92018-12-03 02:06:33 -06004676 if (constant_type.pointer)
4677 {
4678 auto &constant = set<SPIRConstant>(id, type);
4679 constant.make_null(constant_type);
4680 }
4681 else if (!constant_type.array.empty())
Hans-Kristian Arntzen48ccde32017-08-03 14:32:07 +02004682 {
4683 assert(constant_type.parent_type);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004684 uint32_t parent_id = ir.increase_bound_by(1);
Hans-Kristian Arntzen48ccde32017-08-03 14:32:07 +02004685 make_constant_null(parent_id, constant_type.parent_type);
4686
4687 if (!constant_type.array_size_literal.back())
4688 SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
4689
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02004690 SmallVector<uint32_t> elements(constant_type.array.back());
Hans-Kristian Arntzen48ccde32017-08-03 14:32:07 +02004691 for (uint32_t i = 0; i < constant_type.array.back(); i++)
4692 elements[i] = parent_id;
Hans-Kristian Arntzen5e1d6fb2017-09-27 15:16:33 +02004693 set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
Hans-Kristian Arntzen48ccde32017-08-03 14:32:07 +02004694 }
4695 else if (!constant_type.member_types.empty())
4696 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004697 uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size()));
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02004698 SmallVector<uint32_t> elements(constant_type.member_types.size());
Hans-Kristian Arntzen48ccde32017-08-03 14:32:07 +02004699 for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
4700 {
4701 make_constant_null(member_ids + i, constant_type.member_types[i]);
4702 elements[i] = member_ids + i;
4703 }
Hans-Kristian Arntzen5e1d6fb2017-09-27 15:16:33 +02004704 set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
Hans-Kristian Arntzen48ccde32017-08-03 14:32:07 +02004705 }
4706 else
4707 {
4708 auto &constant = set<SPIRConstant>(id, type);
4709 constant.make_null(constant_type);
4710 }
4711}
Hans-Kristian Arntzen8d7a9092017-08-15 15:27:53 +02004712
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02004713const SmallVector<spv::Capability> &Compiler::get_declared_capabilities() const
Hans-Kristian Arntzen8d7a9092017-08-15 15:27:53 +02004714{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004715 return ir.declared_capabilities;
Hans-Kristian Arntzen8d7a9092017-08-15 15:27:53 +02004716}
4717
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02004718const SmallVector<std::string> &Compiler::get_declared_extensions() const
Hans-Kristian Arntzen8d7a9092017-08-15 15:27:53 +02004719{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02004720 return ir.declared_extensions;
Hans-Kristian Arntzen8d7a9092017-08-15 15:27:53 +02004721}
Hans-Kristian Arntzen2c90ea32017-12-01 14:20:51 +01004722
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02004723std::string Compiler::get_remapped_declared_block_name(VariableID id) const
Hans-Kristian Arntzen2c90ea32017-12-01 14:20:51 +01004724{
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +02004725 return get_remapped_declared_block_name(id, false);
4726}
4727
4728std::string Compiler::get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const
4729{
Hans-Kristian Arntzen2c90ea32017-12-01 14:20:51 +01004730 auto itr = declared_block_names.find(id);
4731 if (itr != end(declared_block_names))
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +02004732 {
Hans-Kristian Arntzen2c90ea32017-12-01 14:20:51 +01004733 return itr->second;
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +02004734 }
Hans-Kristian Arntzen2c90ea32017-12-01 14:20:51 +01004735 else
4736 {
4737 auto &var = get<SPIRVariable>(id);
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004738
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +02004739 if (fallback_prefer_instance_name)
4740 {
4741 return to_name(var.self);
4742 }
4743 else
4744 {
4745 auto &type = get<SPIRType>(var.basetype);
4746 auto *type_meta = ir.find_meta(type.self);
4747 auto *block_name = type_meta ? &type_meta->decoration.alias : nullptr;
4748 return (!block_name || block_name->empty()) ? get_block_fallback_name(id) : *block_name;
4749 }
Hans-Kristian Arntzen2c90ea32017-12-01 14:20:51 +01004750 }
4751}
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01004752
Hans-Kristian Arntzen457eba32019-06-10 10:50:53 +02004753bool Compiler::reflection_ssbo_instance_name_is_significant() const
4754{
4755 if (ir.source.known)
4756 {
4757 // UAVs from HLSL source tend to be declared in a way where the type is reused
4758 // but the instance name is significant, and that's the name we should report.
4759 // For GLSL, SSBOs each have their own block type as that's how GLSL is written.
4760 return ir.source.hlsl;
4761 }
4762
4763 unordered_set<uint32_t> ssbo_type_ids;
4764 bool aliased_ssbo_types = false;
4765
4766 // If we don't have any OpSource information, we need to perform some shaky heuristics.
4767 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
4768 auto &type = this->get<SPIRType>(var.basetype);
4769 if (!type.pointer || var.storage == StorageClassFunction)
4770 return;
4771
4772 bool ssbo = var.storage == StorageClassStorageBuffer ||
4773 (var.storage == StorageClassUniform && has_decoration(type.self, DecorationBufferBlock));
4774
4775 if (ssbo)
4776 {
4777 if (ssbo_type_ids.count(type.self))
4778 aliased_ssbo_types = true;
4779 else
4780 ssbo_type_ids.insert(type.self);
4781 }
4782 });
4783
4784 // If the block name is aliased, assume we have HLSL-style UAV declarations.
4785 return aliased_ssbo_types;
4786}
4787
Hans-Kristian Arntzen7eb5ced2022-05-02 15:27:09 +02004788bool Compiler::instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op,
4789 const uint32_t *args, uint32_t length)
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01004790{
Hans-Kristian Arntzen7eb5ced2022-05-02 15:27:09 +02004791 if (length < 2)
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01004792 return false;
4793
Hans-Kristian Arntzen7eb5ced2022-05-02 15:27:09 +02004794 bool has_result_id = false, has_result_type = false;
4795 HasResultAndType(op, &has_result_id, &has_result_type);
4796 if (has_result_id && has_result_type)
4797 {
4798 result_type = args[0];
4799 result_id = args[1];
4800 return true;
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01004801 }
Hans-Kristian Arntzen7eb5ced2022-05-02 15:27:09 +02004802 else
4803 return false;
Hans-Kristian Arntzen7d223b82018-01-18 12:07:10 +01004804}
Brad Davis6c88b002018-06-18 09:30:16 -07004805
4806Bitset Compiler::combined_decoration_for_member(const SPIRType &type, uint32_t index) const
4807{
4808 Bitset flags;
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004809 auto *type_meta = ir.find_meta(type.self);
Brad Davis6c88b002018-06-18 09:30:16 -07004810
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004811 if (type_meta)
4812 {
Alexis Payen de la Garanderie4edfe962020-04-27 15:54:16 +09004813 auto &members = type_meta->members;
4814 if (index >= members.size())
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004815 return flags;
Alexis Payen de la Garanderie4edfe962020-04-27 15:54:16 +09004816 auto &dec = members[index];
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004817
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004818 flags.merge_or(dec.decoration_flags);
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02004819
Alexis Payen de la Garanderie4edfe962020-04-27 15:54:16 +09004820 auto &member_type = get<SPIRType>(type.member_types[index]);
4821
4822 // If our member type is a struct, traverse all the child members as well recursively.
4823 auto &member_childs = member_type.member_types;
4824 for (uint32_t i = 0; i < member_childs.size(); i++)
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02004825 {
Alexis Payen de la Garanderie4edfe962020-04-27 15:54:16 +09004826 auto &child_member_type = get<SPIRType>(member_childs[i]);
4827 if (!child_member_type.pointer)
4828 flags.merge_or(combined_decoration_for_member(member_type, i));
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02004829 }
Hans-Kristian Arntzenb6298782019-01-10 14:04:01 +01004830 }
Brad Davis6c88b002018-06-18 09:30:16 -07004831
4832 return flags;
4833}
4834
Brad Davis76204002018-06-20 10:25:38 -07004835bool Compiler::is_desktop_only_format(spv::ImageFormat format)
Brad Davis6c88b002018-06-18 09:30:16 -07004836{
4837 switch (format)
4838 {
Brad Davis76204002018-06-20 10:25:38 -07004839 // Desktop-only formats
Brad Davis6c88b002018-06-18 09:30:16 -07004840 case ImageFormatR11fG11fB10f:
Brad Davis6c88b002018-06-18 09:30:16 -07004841 case ImageFormatR16f:
Brad Davis6c88b002018-06-18 09:30:16 -07004842 case ImageFormatRgb10A2:
Brad Davis6c88b002018-06-18 09:30:16 -07004843 case ImageFormatR8:
Brad Davis6c88b002018-06-18 09:30:16 -07004844 case ImageFormatRg8:
Brad Davis6c88b002018-06-18 09:30:16 -07004845 case ImageFormatR16:
Brad Davis6c88b002018-06-18 09:30:16 -07004846 case ImageFormatRg16:
Brad Davis6c88b002018-06-18 09:30:16 -07004847 case ImageFormatRgba16:
Brad Davis6c88b002018-06-18 09:30:16 -07004848 case ImageFormatR16Snorm:
Brad Davis6c88b002018-06-18 09:30:16 -07004849 case ImageFormatRg16Snorm:
Brad Davis6c88b002018-06-18 09:30:16 -07004850 case ImageFormatRgba16Snorm:
Brad Davis6c88b002018-06-18 09:30:16 -07004851 case ImageFormatR8Snorm:
Brad Davis6c88b002018-06-18 09:30:16 -07004852 case ImageFormatRg8Snorm:
Brad Davis6c88b002018-06-18 09:30:16 -07004853 case ImageFormatR8ui:
Brad Davis6c88b002018-06-18 09:30:16 -07004854 case ImageFormatRg8ui:
Brad Davis6c88b002018-06-18 09:30:16 -07004855 case ImageFormatR16ui:
Brad Davis6c88b002018-06-18 09:30:16 -07004856 case ImageFormatRgb10a2ui:
Brad Davis6c88b002018-06-18 09:30:16 -07004857 case ImageFormatR8i:
Brad Davis6c88b002018-06-18 09:30:16 -07004858 case ImageFormatRg8i:
Brad Davis6c88b002018-06-18 09:30:16 -07004859 case ImageFormatR16i:
Brad Davis76204002018-06-20 10:25:38 -07004860 return true;
Brad Davis6c88b002018-06-18 09:30:16 -07004861 default:
Brad Davis76204002018-06-20 10:25:38 -07004862 break;
Brad Davis6c88b002018-06-18 09:30:16 -07004863 }
Brad Davis76204002018-06-20 10:25:38 -07004864
4865 return false;
Brad Davis6c88b002018-06-18 09:30:16 -07004866}
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004867
Bill Hollingsfd252b22021-11-08 15:59:45 -05004868// An image is determined to be a depth image if it is marked as a depth image and is not also
4869// explicitly marked with a color format, or if there are any sample/gather compare operations on it.
4870bool Compiler::is_depth_image(const SPIRType &type, uint32_t id) const
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004871{
Bill Hollingsfd252b22021-11-08 15:59:45 -05004872 return (type.image.depth && type.image.format == ImageFormatUnknown) || comparison_ids.count(id);
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02004873}
Hans-Kristian Arntzend2cc43e2019-02-19 17:00:49 +01004874
Hans-Kristian Arntzen9b92e682019-03-29 10:29:44 +01004875bool Compiler::type_is_opaque_value(const SPIRType &type) const
Hans-Kristian Arntzend2cc43e2019-02-19 17:00:49 +01004876{
4877 return !type.pointer && (type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::Image ||
4878 type.basetype == SPIRType::Sampler);
4879}
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02004880
4881// Make these member functions so we can easily break on any force_recompile events.
4882void Compiler::force_recompile()
4883{
4884 is_force_recompile = true;
4885}
4886
Hans-Kristian Arntzen1d13a3e2022-01-17 14:12:01 +01004887void Compiler::force_recompile_guarantee_forward_progress()
4888{
4889 force_recompile();
4890 is_force_recompile_forward_progress = true;
4891}
4892
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02004893bool Compiler::is_forcing_recompilation() const
4894{
4895 return is_force_recompile;
4896}
4897
4898void Compiler::clear_force_recompile()
4899{
4900 is_force_recompile = false;
Hans-Kristian Arntzen1d13a3e2022-01-17 14:12:01 +01004901 is_force_recompile_forward_progress = false;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02004902}
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02004903
4904Compiler::PhysicalStorageBufferPointerHandler::PhysicalStorageBufferPointerHandler(Compiler &compiler_)
4905 : compiler(compiler_)
4906{
4907}
4908
Hans-Kristian Arntzenf1b411c2021-11-07 15:43:57 +01004909Compiler::PhysicalBlockMeta *Compiler::PhysicalStorageBufferPointerHandler::find_block_meta(uint32_t id) const
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02004910{
Hans-Kristian Arntzenf1b411c2021-11-07 15:43:57 +01004911 auto chain_itr = access_chain_to_physical_block.find(id);
4912 if (chain_itr != access_chain_to_physical_block.end())
4913 return chain_itr->second;
4914 else
4915 return nullptr;
4916}
4917
4918void Compiler::PhysicalStorageBufferPointerHandler::mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length)
4919{
4920 uint32_t mask = *args;
4921 args++;
4922 length--;
4923 if (length && (mask & MemoryAccessVolatileMask) != 0)
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02004924 {
Hans-Kristian Arntzenf1b411c2021-11-07 15:43:57 +01004925 args++;
4926 length--;
4927 }
4928
4929 if (length && (mask & MemoryAccessAlignedMask) != 0)
4930 {
4931 uint32_t alignment = *args;
4932 auto *meta = find_block_meta(id);
4933
4934 // This makes the assumption that the application does not rely on insane edge cases like:
4935 // Bind buffer with ADDR = 8, use block offset of 8 bytes, load/store with 16 byte alignment.
4936 // If we emit the buffer with alignment = 16 here, the first element at offset = 0 should
4937 // actually have alignment of 8 bytes, but this is too theoretical and awkward to support.
4938 // We could potentially keep track of any offset in the access chain, but it's
4939 // practically impossible for high level compilers to emit code like that,
4940 // so deducing overall alignment requirement based on maximum observed Alignment value is probably fine.
4941 if (meta && alignment > meta->alignment)
4942 meta->alignment = alignment;
4943 }
4944}
4945
4946bool Compiler::PhysicalStorageBufferPointerHandler::type_is_bda_block_entry(uint32_t type_id) const
4947{
4948 auto &type = compiler.get<SPIRType>(type_id);
4949 return type.storage == StorageClassPhysicalStorageBufferEXT && type.pointer &&
4950 type.pointer_depth == 1 && !compiler.type_is_array_of_pointers(type);
4951}
4952
4953uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_minimum_scalar_alignment(const SPIRType &type) const
4954{
4955 if (type.storage == spv::StorageClassPhysicalStorageBufferEXT)
4956 return 8;
4957 else if (type.basetype == SPIRType::Struct)
4958 {
4959 uint32_t alignment = 0;
4960 for (auto &member_type : type.member_types)
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02004961 {
Hans-Kristian Arntzenf1b411c2021-11-07 15:43:57 +01004962 uint32_t member_align = get_minimum_scalar_alignment(compiler.get<SPIRType>(member_type));
4963 if (member_align > alignment)
4964 alignment = member_align;
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02004965 }
Hans-Kristian Arntzenf1b411c2021-11-07 15:43:57 +01004966 return alignment;
4967 }
4968 else
4969 return type.width / 8;
4970}
4971
4972void Compiler::PhysicalStorageBufferPointerHandler::setup_meta_chain(uint32_t type_id, uint32_t var_id)
4973{
4974 if (type_is_bda_block_entry(type_id))
4975 {
4976 auto &meta = physical_block_type_meta[type_id];
4977 access_chain_to_physical_block[var_id] = &meta;
4978
4979 auto &type = compiler.get<SPIRType>(type_id);
4980 if (type.basetype != SPIRType::Struct)
4981 non_block_types.insert(type_id);
4982
4983 if (meta.alignment == 0)
4984 meta.alignment = get_minimum_scalar_alignment(compiler.get_pointee_type(type));
4985 }
4986}
4987
4988bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t length)
4989{
4990 // When a BDA pointer comes to life, we need to keep a mapping of SSA ID -> type ID for the pointer type.
4991 // For every load and store, we'll need to be able to look up the type ID being accessed and mark any alignment
4992 // requirements.
4993 switch (op)
4994 {
4995 case OpConvertUToPtr:
4996 case OpBitcast:
4997 case OpCompositeExtract:
4998 // Extract can begin a new chain if we had a struct or array of pointers as input.
4999 // We don't begin chains before we have a pure scalar pointer.
5000 setup_meta_chain(args[0], args[1]);
5001 break;
5002
5003 case OpAccessChain:
5004 case OpInBoundsAccessChain:
5005 case OpPtrAccessChain:
5006 case OpCopyObject:
5007 {
5008 auto itr = access_chain_to_physical_block.find(args[2]);
5009 if (itr != access_chain_to_physical_block.end())
5010 access_chain_to_physical_block[args[1]] = itr->second;
5011 break;
5012 }
5013
5014 case OpLoad:
5015 {
5016 setup_meta_chain(args[0], args[1]);
5017 if (length >= 4)
5018 mark_aligned_access(args[2], args + 3, length - 3);
5019 break;
5020 }
5021
5022 case OpStore:
5023 {
5024 if (length >= 3)
5025 mark_aligned_access(args[0], args + 2, length - 2);
5026 break;
5027 }
5028
5029 default:
5030 break;
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02005031 }
5032
5033 return true;
5034}
5035
Hans-Kristian Arntzenf1b411c2021-11-07 15:43:57 +01005036uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_base_non_block_type_id(uint32_t type_id) const
5037{
5038 auto *type = &compiler.get<SPIRType>(type_id);
5039 while (type->pointer &&
5040 type->storage == StorageClassPhysicalStorageBufferEXT &&
5041 !type_is_bda_block_entry(type_id))
5042 {
5043 type_id = type->parent_type;
5044 type = &compiler.get<SPIRType>(type_id);
5045 }
5046
5047 assert(type_is_bda_block_entry(type_id));
5048 return type_id;
5049}
5050
5051void Compiler::PhysicalStorageBufferPointerHandler::analyze_non_block_types_from_block(const SPIRType &type)
5052{
5053 for (auto &member : type.member_types)
5054 {
5055 auto &subtype = compiler.get<SPIRType>(member);
5056 if (subtype.basetype != SPIRType::Struct && subtype.pointer &&
5057 subtype.storage == spv::StorageClassPhysicalStorageBufferEXT)
5058 {
5059 non_block_types.insert(get_base_non_block_type_id(member));
5060 }
5061 else if (subtype.basetype == SPIRType::Struct && !subtype.pointer)
5062 analyze_non_block_types_from_block(subtype);
5063 }
5064}
5065
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02005066void Compiler::analyze_non_block_pointer_types()
5067{
5068 PhysicalStorageBufferPointerHandler handler(*this);
5069 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Hans-Kristian Arntzenf1b411c2021-11-07 15:43:57 +01005070
5071 // Analyze any block declaration we have to make. It might contain
5072 // physical pointers to POD types which we never used, and thus never added to the list.
5073 // We'll need to add those pointer types to the set of types we declare.
5074 ir.for_each_typed_id<SPIRType>([&](uint32_t, SPIRType &type) {
5075 if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))
5076 handler.analyze_non_block_types_from_block(type);
5077 });
5078
5079 physical_storage_non_block_pointer_types.reserve(handler.non_block_types.size());
5080 for (auto type : handler.non_block_types)
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02005081 physical_storage_non_block_pointer_types.push_back(type);
5082 sort(begin(physical_storage_non_block_pointer_types), end(physical_storage_non_block_pointer_types));
Daniel Thornburgh44c33332022-03-02 23:02:38 +00005083 physical_storage_type_to_alignment = std::move(handler.physical_block_type_meta);
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02005084}
5085
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005086bool Compiler::InterlockedResourceAccessPrepassHandler::handle(Op op, const uint32_t *, uint32_t)
5087{
5088 if (op == OpBeginInvocationInterlockEXT || op == OpEndInvocationInterlockEXT)
5089 {
5090 if (interlock_function_id != 0 && interlock_function_id != call_stack.back())
5091 {
5092 // Most complex case, we have no sensible way of dealing with this
5093 // other than taking the 100% conservative approach, exit early.
5094 split_function_case = true;
5095 return false;
5096 }
5097 else
5098 {
5099 interlock_function_id = call_stack.back();
5100 // If this call is performed inside control flow we have a problem.
5101 auto &cfg = compiler.get_cfg_for_function(interlock_function_id);
5102
5103 uint32_t from_block_id = compiler.get<SPIRFunction>(interlock_function_id).entry_block;
5104 bool outside_control_flow = cfg.node_terminates_control_flow_in_sub_graph(from_block_id, current_block_id);
5105 if (!outside_control_flow)
5106 control_flow_interlock = true;
5107 }
5108 }
5109 return true;
5110}
5111
Hans-Kristian Arntzen36c433b2019-09-04 11:42:29 +02005112void Compiler::InterlockedResourceAccessPrepassHandler::rearm_current_block(const SPIRBlock &block)
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005113{
5114 current_block_id = block.self;
5115}
5116
5117bool Compiler::InterlockedResourceAccessPrepassHandler::begin_function_scope(const uint32_t *args, uint32_t length)
5118{
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005119 if (length < 3)
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005120 return false;
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005121 call_stack.push_back(args[2]);
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005122 return true;
5123}
5124
5125bool Compiler::InterlockedResourceAccessPrepassHandler::end_function_scope(const uint32_t *, uint32_t)
5126{
5127 call_stack.pop_back();
5128 return true;
5129}
5130
5131bool Compiler::InterlockedResourceAccessHandler::begin_function_scope(const uint32_t *args, uint32_t length)
5132{
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005133 if (length < 3)
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005134 return false;
Hans-Kristian Arntzen36c433b2019-09-04 11:42:29 +02005135
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005136 if (args[2] == interlock_function_id)
Hans-Kristian Arntzen36c433b2019-09-04 11:42:29 +02005137 call_stack_is_interlocked = true;
5138
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005139 call_stack.push_back(args[2]);
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005140 return true;
5141}
5142
5143bool Compiler::InterlockedResourceAccessHandler::end_function_scope(const uint32_t *, uint32_t)
5144{
Hans-Kristian Arntzen36c433b2019-09-04 11:42:29 +02005145 if (call_stack.back() == interlock_function_id)
5146 call_stack_is_interlocked = false;
5147
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005148 call_stack.pop_back();
5149 return true;
5150}
5151
5152void Compiler::InterlockedResourceAccessHandler::access_potential_resource(uint32_t id)
5153{
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02005154 if ((use_critical_section && in_crit_sec) || (control_flow_interlock && call_stack_is_interlocked) ||
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005155 split_function_case)
5156 {
5157 compiler.interlocked_resources.insert(id);
5158 }
5159}
5160
Chip Davis2eff4202019-08-04 00:07:20 -05005161bool Compiler::InterlockedResourceAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
5162{
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005163 // Only care about critical section analysis if we have simple case.
5164 if (use_critical_section)
Chip Davis2eff4202019-08-04 00:07:20 -05005165 {
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005166 if (opcode == OpBeginInvocationInterlockEXT)
5167 {
5168 in_crit_sec = true;
5169 return true;
5170 }
Chip Davis2eff4202019-08-04 00:07:20 -05005171
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005172 if (opcode == OpEndInvocationInterlockEXT)
5173 {
5174 // End critical section--nothing more to do.
5175 return false;
5176 }
Chip Davis2eff4202019-08-04 00:07:20 -05005177 }
5178
5179 // We need to figure out where images and buffers are loaded from, so do only the bare bones compilation we need.
5180 switch (opcode)
5181 {
5182 case OpLoad:
5183 {
5184 if (length < 3)
5185 return false;
5186
5187 uint32_t ptr = args[2];
5188 auto *var = compiler.maybe_get_backing_variable(ptr);
5189
5190 // We're only concerned with buffer and image memory here.
5191 if (!var)
5192 break;
5193
5194 switch (var->storage)
5195 {
5196 default:
5197 break;
5198
5199 case StorageClassUniformConstant:
5200 {
5201 uint32_t result_type = args[0];
5202 uint32_t id = args[1];
5203 compiler.set<SPIRExpression>(id, "", result_type, true);
5204 compiler.register_read(id, ptr, true);
5205 break;
5206 }
5207
5208 case StorageClassUniform:
5209 // Must have BufferBlock; we only care about SSBOs.
5210 if (!compiler.has_decoration(compiler.get<SPIRType>(var->basetype).self, DecorationBufferBlock))
5211 break;
5212 // fallthrough
5213 case StorageClassStorageBuffer:
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005214 access_potential_resource(var->self);
Chip Davis2eff4202019-08-04 00:07:20 -05005215 break;
5216 }
5217 break;
5218 }
5219
5220 case OpInBoundsAccessChain:
5221 case OpAccessChain:
5222 case OpPtrAccessChain:
5223 {
5224 if (length < 3)
5225 return false;
5226
5227 uint32_t result_type = args[0];
5228
5229 auto &type = compiler.get<SPIRType>(result_type);
5230 if (type.storage == StorageClassUniform || type.storage == StorageClassUniformConstant ||
5231 type.storage == StorageClassStorageBuffer)
5232 {
5233 uint32_t id = args[1];
5234 uint32_t ptr = args[2];
5235 compiler.set<SPIRExpression>(id, "", result_type, true);
5236 compiler.register_read(id, ptr, true);
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005237 compiler.ir.ids[id].set_allow_type_rewrite();
Chip Davis2eff4202019-08-04 00:07:20 -05005238 }
5239 break;
5240 }
5241
5242 case OpImageTexelPointer:
5243 {
5244 if (length < 3)
5245 return false;
5246
5247 uint32_t result_type = args[0];
5248 uint32_t id = args[1];
5249 uint32_t ptr = args[2];
5250 auto &e = compiler.set<SPIRExpression>(id, "", result_type, true);
5251 auto *var = compiler.maybe_get_backing_variable(ptr);
5252 if (var)
5253 e.loaded_from = var->self;
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005254 break;
Chip Davis2eff4202019-08-04 00:07:20 -05005255 }
5256
5257 case OpStore:
5258 case OpImageWrite:
5259 case OpAtomicStore:
5260 {
5261 if (length < 1)
5262 return false;
5263
Chip Davis2eff4202019-08-04 00:07:20 -05005264 uint32_t ptr = args[0];
5265 auto *var = compiler.maybe_get_backing_variable(ptr);
5266 if (var && (var->storage == StorageClassUniform || var->storage == StorageClassUniformConstant ||
5267 var->storage == StorageClassStorageBuffer))
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005268 {
5269 access_potential_resource(var->self);
5270 }
Chip Davis2eff4202019-08-04 00:07:20 -05005271
5272 break;
5273 }
5274
5275 case OpCopyMemory:
5276 {
5277 if (length < 2)
5278 return false;
5279
Chip Davis2eff4202019-08-04 00:07:20 -05005280 uint32_t dst = args[0];
5281 uint32_t src = args[1];
5282 auto *dst_var = compiler.maybe_get_backing_variable(dst);
5283 auto *src_var = compiler.maybe_get_backing_variable(src);
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005284
Chip Davis2eff4202019-08-04 00:07:20 -05005285 if (dst_var && (dst_var->storage == StorageClassUniform || dst_var->storage == StorageClassStorageBuffer))
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005286 access_potential_resource(dst_var->self);
5287
Chip Davis2eff4202019-08-04 00:07:20 -05005288 if (src_var)
5289 {
5290 if (src_var->storage != StorageClassUniform && src_var->storage != StorageClassStorageBuffer)
5291 break;
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005292
Chip Davis2eff4202019-08-04 00:07:20 -05005293 if (src_var->storage == StorageClassUniform &&
5294 !compiler.has_decoration(compiler.get<SPIRType>(src_var->basetype).self, DecorationBufferBlock))
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005295 {
Chip Davis2eff4202019-08-04 00:07:20 -05005296 break;
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005297 }
5298
5299 access_potential_resource(src_var->self);
Chip Davis2eff4202019-08-04 00:07:20 -05005300 }
5301
5302 break;
5303 }
5304
5305 case OpImageRead:
5306 case OpAtomicLoad:
5307 {
5308 if (length < 3)
5309 return false;
5310
Chip Davis2eff4202019-08-04 00:07:20 -05005311 uint32_t ptr = args[2];
5312 auto *var = compiler.maybe_get_backing_variable(ptr);
5313
5314 // We're only concerned with buffer and image memory here.
5315 if (!var)
5316 break;
5317
5318 switch (var->storage)
5319 {
5320 default:
5321 break;
5322
5323 case StorageClassUniform:
5324 // Must have BufferBlock; we only care about SSBOs.
5325 if (!compiler.has_decoration(compiler.get<SPIRType>(var->basetype).self, DecorationBufferBlock))
5326 break;
5327 // fallthrough
5328 case StorageClassUniformConstant:
5329 case StorageClassStorageBuffer:
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005330 access_potential_resource(var->self);
Chip Davis2eff4202019-08-04 00:07:20 -05005331 break;
5332 }
5333 break;
5334 }
5335
5336 case OpAtomicExchange:
5337 case OpAtomicCompareExchange:
5338 case OpAtomicIIncrement:
5339 case OpAtomicIDecrement:
5340 case OpAtomicIAdd:
5341 case OpAtomicISub:
5342 case OpAtomicSMin:
5343 case OpAtomicUMin:
5344 case OpAtomicSMax:
5345 case OpAtomicUMax:
5346 case OpAtomicAnd:
5347 case OpAtomicOr:
5348 case OpAtomicXor:
5349 {
5350 if (length < 3)
5351 return false;
5352
Chip Davis2eff4202019-08-04 00:07:20 -05005353 uint32_t ptr = args[2];
5354 auto *var = compiler.maybe_get_backing_variable(ptr);
5355 if (var && (var->storage == StorageClassUniform || var->storage == StorageClassUniformConstant ||
5356 var->storage == StorageClassStorageBuffer))
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005357 {
5358 access_potential_resource(var->self);
5359 }
Chip Davis2eff4202019-08-04 00:07:20 -05005360
5361 break;
5362 }
5363
5364 default:
5365 break;
5366 }
5367
5368 return true;
5369}
5370
5371void Compiler::analyze_interlocked_resource_usage()
5372{
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005373 if (get_execution_model() == ExecutionModelFragment &&
5374 (get_entry_point().flags.get(ExecutionModePixelInterlockOrderedEXT) ||
5375 get_entry_point().flags.get(ExecutionModePixelInterlockUnorderedEXT) ||
5376 get_entry_point().flags.get(ExecutionModeSampleInterlockOrderedEXT) ||
5377 get_entry_point().flags.get(ExecutionModeSampleInterlockUnorderedEXT)))
5378 {
5379 InterlockedResourceAccessPrepassHandler prepass_handler(*this, ir.default_entry_point);
5380 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), prepass_handler);
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005381
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005382 InterlockedResourceAccessHandler handler(*this, ir.default_entry_point);
5383 handler.interlock_function_id = prepass_handler.interlock_function_id;
5384 handler.split_function_case = prepass_handler.split_function_case;
5385 handler.control_flow_interlock = prepass_handler.control_flow_interlock;
5386 handler.use_critical_section = !handler.split_function_case && !handler.control_flow_interlock;
Hans-Kristian Arntzen3f2ce372019-09-04 11:20:25 +02005387
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005388 traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
Hans-Kristian Arntzen36c433b2019-09-04 11:42:29 +02005389
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005390 // For GLSL. If we hit any of these cases, we have to fall back to conservative approach.
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02005391 interlocked_is_complex =
5392 !handler.use_critical_section || handler.interlock_function_id != ir.default_entry_point;
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005393 }
Chip Davis2eff4202019-08-04 00:07:20 -05005394}
5395
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02005396bool Compiler::type_is_array_of_pointers(const SPIRType &type) const
5397{
5398 if (!type.pointer)
5399 return false;
5400
5401 // If parent type has same pointer depth, we must have an array of pointers.
5402 return type.pointer_depth == get<SPIRType>(type.parent_type).pointer_depth;
5403}
Hans-Kristian Arntzene06efb72019-07-25 12:30:50 +02005404
Hans-Kristian Arntzen58dad822020-05-25 11:05:42 +02005405bool Compiler::type_is_top_level_physical_pointer(const SPIRType &type) const
5406{
5407 return type.pointer && type.storage == StorageClassPhysicalStorageBuffer &&
5408 type.pointer_depth > get<SPIRType>(type.parent_type).pointer_depth;
5409}
5410
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02005411bool Compiler::flush_phi_required(BlockID from, BlockID to) const
Hans-Kristian Arntzene06efb72019-07-25 12:30:50 +02005412{
5413 auto &child = get<SPIRBlock>(to);
5414 for (auto &phi : child.phi_variables)
5415 if (phi.parent == from)
5416 return true;
5417 return false;
5418}
Hans-Kristian Arntzen3afbfdb2020-06-29 12:20:35 +02005419
5420void Compiler::add_loop_level()
5421{
5422 current_loop_level++;
5423}