/src/glslang/glslang/MachineIndependent/Intermediate.cpp
Line | Count | Source |
1 | | // |
2 | | // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. |
3 | | // Copyright (C) 2012-2015 LunarG, Inc. |
4 | | // Copyright (C) 2015-2020 Google, Inc. |
5 | | // Copyright (C) 2017 ARM Limited. |
6 | | // |
7 | | // All rights reserved. |
8 | | // |
9 | | // Redistribution and use in source and binary forms, with or without |
10 | | // modification, are permitted provided that the following conditions |
11 | | // are met: |
12 | | // |
13 | | // Redistributions of source code must retain the above copyright |
14 | | // notice, this list of conditions and the following disclaimer. |
15 | | // |
16 | | // Redistributions in binary form must reproduce the above |
17 | | // copyright notice, this list of conditions and the following |
18 | | // disclaimer in the documentation and/or other materials provided |
19 | | // with the distribution. |
20 | | // |
21 | | // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
22 | | // contributors may be used to endorse or promote products derived |
23 | | // from this software without specific prior written permission. |
24 | | // |
25 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
26 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
27 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
28 | | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
29 | | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
30 | | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
31 | | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
32 | | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
33 | | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
34 | | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
35 | | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
36 | | // POSSIBILITY OF SUCH DAMAGE. |
37 | | // |
38 | | |
39 | | // |
40 | | // Build the intermediate representation. |
41 | | // |
42 | | |
43 | | #include "localintermediate.h" |
44 | | #include "RemoveTree.h" |
45 | | #include "SymbolTable.h" |
46 | | #include "propagateNoContraction.h" |
47 | | |
48 | | #include <cfloat> |
49 | | #include <limits> |
50 | | #include <utility> |
51 | | #include <tuple> |
52 | | |
53 | | namespace glslang { |
54 | | |
55 | | //////////////////////////////////////////////////////////////////////////// |
56 | | // |
57 | | // First set of functions are to help build the intermediate representation. |
58 | | // These functions are not member functions of the nodes. |
59 | | // They are called from parser productions. |
60 | | // |
61 | | ///////////////////////////////////////////////////////////////////////////// |
62 | | |
63 | | // |
64 | | // Add a terminal node for an identifier in an expression. |
65 | | // |
66 | | // Returns the added node. |
67 | | // |
68 | | |
69 | | TIntermSymbol* TIntermediate::addSymbol(long long id, const TString& name, const TString& mangledName, const TType& type, const TConstUnionArray& constArray, |
70 | | TIntermTyped* constSubtree, const TSourceLoc& loc) |
71 | 213 | { |
72 | 213 | TIntermSymbol* node = new TIntermSymbol(id, name, getStage(), type, &mangledName); |
73 | 213 | node->setLoc(loc); |
74 | 213 | node->setConstArray(constArray); |
75 | 213 | node->setConstSubtree(constSubtree); |
76 | | |
77 | 213 | return node; |
78 | 213 | } |
79 | | |
80 | | TIntermSymbol* TIntermediate::addSymbol(const TIntermSymbol& intermSymbol) |
81 | 0 | { |
82 | 0 | return addSymbol(intermSymbol.getId(), |
83 | 0 | intermSymbol.getName(), |
84 | 0 | intermSymbol.getMangledName(), |
85 | 0 | intermSymbol.getType(), |
86 | 0 | intermSymbol.getConstArray(), |
87 | 0 | intermSymbol.getConstSubtree(), |
88 | 0 | intermSymbol.getLoc()); |
89 | 0 | } |
90 | | |
91 | | TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable) |
92 | 14 | { |
93 | 14 | glslang::TSourceLoc loc; // just a null location |
94 | 14 | loc.init(); |
95 | | |
96 | 14 | return addSymbol(variable, loc); |
97 | 14 | } |
98 | | |
99 | | TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc) |
100 | 213 | { |
101 | 213 | return addSymbol(variable.getUniqueId(), variable.getName(), variable.getMangledName(), variable.getType(), variable.getConstArray(), variable.getConstSubtree(), loc); |
102 | 213 | } |
103 | | |
104 | | TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc) |
105 | 0 | { |
106 | 0 | TConstUnionArray unionArray; // just a null constant |
107 | |
|
108 | 0 | return addSymbol(0, "", "", type, unionArray, nullptr, loc); |
109 | 0 | } |
110 | | |
111 | | // |
112 | | // Connect two nodes with a new parent that does a binary operation on the nodes. |
113 | | // |
114 | | // Returns the added node. |
115 | | // |
116 | | // Returns nullptr if the working conversions and promotions could not be found. |
117 | | // |
118 | | TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) |
119 | 10 | { |
120 | | // No operations work on blocks |
121 | 10 | if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock || |
122 | 10 | left->getType().getBasicType() == EbtString || right->getType().getBasicType() == EbtString) |
123 | 0 | return nullptr; |
124 | | |
125 | | // Convert "reference +/- int" and "reference - reference" to integer math |
126 | 10 | if (op == EOpAdd || op == EOpSub) { |
127 | | |
128 | | // No addressing math on struct with unsized array. |
129 | 7 | if ((left->isReference() && left->getType().getReferentType()->containsUnsizedArray()) || |
130 | 7 | (right->isReference() && right->getType().getReferentType()->containsUnsizedArray())) { |
131 | 0 | return nullptr; |
132 | 0 | } |
133 | | |
134 | 7 | if (left->isReference() && isTypeInt(right->getBasicType())) { |
135 | 0 | const TType& referenceType = left->getType(); |
136 | 0 | TIntermConstantUnion* size = addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(left->getType()), loc, true); |
137 | 0 | left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64)); |
138 | |
|
139 | 0 | right = createConversion(EbtInt64, right); |
140 | 0 | right = addBinaryMath(EOpMul, right, size, loc); |
141 | |
|
142 | 0 | TIntermTyped *node = addBinaryMath(op, left, right, loc); |
143 | 0 | node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType); |
144 | 0 | return node; |
145 | 0 | } |
146 | 7 | } |
147 | | |
148 | 10 | if (op == EOpAdd && right->isReference() && isTypeInt(left->getBasicType())) { |
149 | 0 | const TType& referenceType = right->getType(); |
150 | 0 | TIntermConstantUnion* size = |
151 | 0 | addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(right->getType()), loc, true); |
152 | 0 | right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64)); |
153 | |
|
154 | 0 | left = createConversion(EbtInt64, left); |
155 | 0 | left = addBinaryMath(EOpMul, left, size, loc); |
156 | |
|
157 | 0 | TIntermTyped *node = addBinaryMath(op, left, right, loc); |
158 | 0 | node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType); |
159 | 0 | return node; |
160 | 0 | } |
161 | | |
162 | 10 | if (op == EOpSub && left->isReference() && right->isReference()) { |
163 | 0 | TIntermConstantUnion* size = |
164 | 0 | addConstantUnion((long long)computeBufferReferenceTypeSize(left->getType()), loc, true); |
165 | |
|
166 | 0 | left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64)); |
167 | 0 | right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64)); |
168 | |
|
169 | 0 | left = addBuiltInFunctionCall(loc, EOpConvNumeric, true, left, TType(EbtInt64)); |
170 | 0 | right = addBuiltInFunctionCall(loc, EOpConvNumeric, true, right, TType(EbtInt64)); |
171 | |
|
172 | 0 | left = addBinaryMath(EOpSub, left, right, loc); |
173 | |
|
174 | 0 | TIntermTyped *node = addBinaryMath(EOpDiv, left, size, loc); |
175 | 0 | return node; |
176 | 0 | } |
177 | | |
178 | | // No other math operators supported on references |
179 | 10 | if (left->isReference() || right->isReference()) |
180 | 0 | return nullptr; |
181 | | |
182 | | // Try converting the children's base types to compatible types. |
183 | 10 | auto children = addPairConversion(op, left, right); |
184 | 10 | left = std::get<0>(children); |
185 | 10 | right = std::get<1>(children); |
186 | | |
187 | 10 | if (left == nullptr || right == nullptr) |
188 | 0 | return nullptr; |
189 | | |
190 | | // Convert the children's type shape to be compatible. |
191 | 10 | addBiShapeConversion(op, left, right); |
192 | 10 | if (left == nullptr || right == nullptr) |
193 | 0 | return nullptr; |
194 | | |
195 | | // |
196 | | // Need a new node holding things together. Make |
197 | | // one and promote it to the right type. |
198 | | // |
199 | 10 | TIntermBinary* node = addBinaryNode(op, left, right, loc); |
200 | 10 | if (! promote(node)) |
201 | 0 | return nullptr; |
202 | | |
203 | 10 | node->updatePrecision(); |
204 | | |
205 | | // |
206 | | // If they are both (non-specialization) constants, they must be folded. |
207 | | // (Unless it's the sequence (comma) operator, but that's handled in addComma().) |
208 | | // |
209 | 10 | TIntermConstantUnion *leftTempConstant = node->getLeft()->getAsConstantUnion(); |
210 | 10 | TIntermConstantUnion *rightTempConstant = node->getRight()->getAsConstantUnion(); |
211 | 10 | if (leftTempConstant && rightTempConstant) { |
212 | 10 | TIntermTyped* folded = leftTempConstant->fold(node->getOp(), rightTempConstant); |
213 | 10 | if (folded) |
214 | 10 | return folded; |
215 | 10 | } |
216 | | |
217 | | // If can propagate spec-constantness and if the operation is an allowed |
218 | | // specialization-constant operation, make a spec-constant. |
219 | 0 | if (specConstantPropagates(*node->getLeft(), *node->getRight()) && isSpecializationOperation(*node)) |
220 | 0 | node->getWritableType().getQualifier().makeSpecConstant(); |
221 | | |
222 | | // If must propagate nonuniform, make a nonuniform. |
223 | 0 | if ((node->getLeft()->getQualifier().isNonUniform() || node->getRight()->getQualifier().isNonUniform()) && |
224 | 0 | isNonuniformPropagating(node->getOp())) |
225 | 0 | node->getWritableType().getQualifier().nonUniform = true; |
226 | |
|
227 | 0 | return node; |
228 | 10 | } |
229 | | |
230 | | // |
231 | | // Low level: add binary node (no promotions or other argument modifications) |
232 | | // |
233 | | TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, |
234 | | const TSourceLoc& loc) const |
235 | 13 | { |
236 | | // build the node |
237 | 13 | TIntermBinary* node = new TIntermBinary(op); |
238 | 13 | node->setLoc(loc.line != 0 ? loc : left->getLoc()); |
239 | 13 | node->setLeft(left); |
240 | 13 | node->setRight(right); |
241 | | |
242 | 13 | return node; |
243 | 13 | } |
244 | | |
245 | | // |
246 | | // like non-type form, but sets node's type. |
247 | | // |
248 | | TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, |
249 | | const TSourceLoc& loc, const TType& type) const |
250 | 0 | { |
251 | 0 | TIntermBinary* node = addBinaryNode(op, left, right, loc); |
252 | 0 | node->setType(type); |
253 | 0 | return node; |
254 | 0 | } |
255 | | |
256 | | // |
257 | | // Low level: add unary node (no promotions or other argument modifications) |
258 | | // |
259 | | TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc) const |
260 | 35 | { |
261 | 35 | TIntermUnary* node = new TIntermUnary(op); |
262 | 35 | node->setLoc(loc.line != 0 ? loc : child->getLoc()); |
263 | 35 | node->setOperand(child); |
264 | | |
265 | 35 | return node; |
266 | 35 | } |
267 | | |
268 | | // |
269 | | // like non-type form, but sets node's type. |
270 | | // |
271 | | TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc, const TType& type) |
272 | | const |
273 | 3 | { |
274 | 3 | TIntermUnary* node = addUnaryNode(op, child, loc); |
275 | 3 | node->setType(type); |
276 | 3 | return node; |
277 | 3 | } |
278 | | |
279 | | // |
280 | | // Connect two nodes through an assignment. |
281 | | // |
282 | | // Returns the added node. |
283 | | // |
284 | | // Returns nullptr if the 'right' type could not be converted to match the 'left' type, |
285 | | // or the resulting operation cannot be properly promoted. |
286 | | // |
287 | | TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, |
288 | | const TSourceLoc& loc) |
289 | 3 | { |
290 | | // No block assignment |
291 | 3 | if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock) |
292 | 0 | return nullptr; |
293 | | |
294 | | // Convert "reference += int" to "reference = reference + int". We need this because the |
295 | | // "reference + int" calculation involves a cast back to the original type, which makes it |
296 | | // not an lvalue. |
297 | 3 | if ((op == EOpAddAssign || op == EOpSubAssign) && left->isReference()) { |
298 | 0 | if (!(right->getType().isScalar() && right->getType().isIntegerDomain())) |
299 | 0 | return nullptr; |
300 | | |
301 | 0 | TIntermTyped* node = addBinaryMath(op == EOpAddAssign ? EOpAdd : EOpSub, left, right, loc); |
302 | 0 | if (!node) |
303 | 0 | return nullptr; |
304 | | |
305 | 0 | TIntermSymbol* symbol = left->getAsSymbolNode(); |
306 | 0 | left = addSymbol(*symbol); |
307 | |
|
308 | 0 | node = addAssign(EOpAssign, left, node, loc); |
309 | 0 | return node; |
310 | 0 | } |
311 | | |
312 | | // |
313 | | // Like adding binary math, except the conversion can only go |
314 | | // from right to left. |
315 | | // |
316 | | |
317 | | // convert base types, nullptr return means not possible |
318 | 3 | right = addConversion(op, left->getType(), right); |
319 | 3 | if (right == nullptr) |
320 | 0 | return nullptr; |
321 | | |
322 | | // convert shape |
323 | 3 | right = addUniShapeConversion(op, left->getType(), right); |
324 | | |
325 | | // build the node |
326 | 3 | TIntermBinary* node = addBinaryNode(op, left, right, loc); |
327 | | |
328 | 3 | if (! promote(node)) |
329 | 0 | return nullptr; |
330 | | |
331 | 3 | node->updatePrecision(); |
332 | | |
333 | 3 | return node; |
334 | 3 | } |
335 | | |
336 | | // |
337 | | // Connect two nodes through an index operator, where the left node is the base |
338 | | // of an array or struct, and the right node is a direct or indirect offset. |
339 | | // |
340 | | // Returns the added node. |
341 | | // The caller should set the type of the returned node. |
342 | | // |
343 | | TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, |
344 | | const TSourceLoc& loc) |
345 | 0 | { |
346 | | // caller should set the type |
347 | 0 | return addBinaryNode(op, base, index, loc); |
348 | 0 | } |
349 | | |
350 | | // |
351 | | // Add one node as the parent of another that it operates on. |
352 | | // |
353 | | // Returns the added node. |
354 | | // |
355 | | TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, |
356 | | const TSourceLoc& loc) |
357 | 288 | { |
358 | 288 | if (child == nullptr) |
359 | 2 | return nullptr; |
360 | | |
361 | 286 | if (child->getType().getBasicType() == EbtBlock) |
362 | 0 | return nullptr; |
363 | | |
364 | 286 | switch (op) { |
365 | 20 | case EOpLogicalNot: |
366 | 20 | if (getSource() == EShSourceHlsl) { |
367 | 20 | break; // HLSL can promote logical not |
368 | 20 | } |
369 | | |
370 | 0 | if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) { |
371 | 0 | return nullptr; |
372 | 0 | } |
373 | 0 | break; |
374 | | |
375 | 1 | case EOpPostIncrement: |
376 | 2 | case EOpPreIncrement: |
377 | 2 | case EOpPostDecrement: |
378 | 2 | case EOpPreDecrement: |
379 | 9 | case EOpNegative: |
380 | 9 | if (child->getType().getBasicType() == EbtStruct || child->getType().isArray()) |
381 | 0 | return nullptr; |
382 | 9 | break; |
383 | 257 | default: break; // some compilers want this |
384 | 286 | } |
385 | | |
386 | | // |
387 | | // Do we need to promote the operand? |
388 | | // |
389 | 286 | TBasicType newType = EbtVoid; |
390 | 286 | switch (op) { |
391 | 0 | case EOpConstructBool: newType = EbtBool; break; |
392 | 0 | case EOpConstructFloat: newType = EbtFloat; break; |
393 | 92 | case EOpConstructInt: newType = EbtInt; break; |
394 | 162 | case EOpConstructUint: newType = EbtUint; break; |
395 | 0 | case EOpConstructInt8: newType = EbtInt8; break; |
396 | 0 | case EOpConstructUint8: newType = EbtUint8; break; |
397 | 0 | case EOpConstructInt16: newType = EbtInt16; break; |
398 | 0 | case EOpConstructUint16: newType = EbtUint16; break; |
399 | 0 | case EOpConstructInt64: newType = EbtInt64; break; |
400 | 0 | case EOpConstructUint64: newType = EbtUint64; break; |
401 | 0 | case EOpConstructDouble: newType = EbtDouble; break; |
402 | 0 | case EOpConstructFloat16: newType = EbtFloat16; break; |
403 | 0 | case EOpConstructBFloat16: newType = EbtBFloat16; break; |
404 | 0 | case EOpConstructFloatE4M3: newType = EbtFloatE4M3; break; |
405 | 0 | case EOpConstructFloatE5M2: newType = EbtFloatE5M2; break; |
406 | 32 | default: break; // some compilers want this |
407 | 286 | } |
408 | | |
409 | 286 | if (newType != EbtVoid) { |
410 | 254 | TType newTType(newType, EvqTemporary, child->getVectorSize(), child->getMatrixCols(), child->getMatrixRows(), child->isVector()); |
411 | 254 | if (child->getType().isLongVector()) { |
412 | 0 | newTType.shallowCopy(child->getType()); |
413 | 0 | newTType.setBasicType(newType); |
414 | 0 | newTType.makeTemporary(); |
415 | 0 | newTType.getQualifier().clear(); |
416 | 0 | } |
417 | | |
418 | 254 | child = addConversion(op, newTType, child); |
419 | 254 | if (child == nullptr) |
420 | 0 | return nullptr; |
421 | 254 | } |
422 | | |
423 | | // |
424 | | // For constructors, we are now done, it was all in the conversion. |
425 | | // TODO: but, did this bypass constant folding? |
426 | | // |
427 | 286 | switch (op) { |
428 | 0 | case EOpConstructInt8: |
429 | 0 | case EOpConstructUint8: |
430 | 0 | case EOpConstructInt16: |
431 | 0 | case EOpConstructUint16: |
432 | 92 | case EOpConstructInt: |
433 | 254 | case EOpConstructUint: |
434 | 254 | case EOpConstructInt64: |
435 | 254 | case EOpConstructUint64: |
436 | 254 | case EOpConstructBool: |
437 | 254 | case EOpConstructFloat: |
438 | 254 | case EOpConstructDouble: |
439 | 254 | case EOpConstructFloat16: |
440 | 254 | case EOpConstructBFloat16: |
441 | 254 | case EOpConstructFloatE5M2: |
442 | 254 | case EOpConstructFloatE4M3: { |
443 | 254 | TIntermUnary* unary_node = child->getAsUnaryNode(); |
444 | 254 | if (unary_node != nullptr) |
445 | 3 | unary_node->updatePrecision(); |
446 | 254 | return child; |
447 | 254 | } |
448 | 32 | default: break; // some compilers want this |
449 | 286 | } |
450 | | |
451 | | // |
452 | | // Make a new node for the operator. |
453 | | // |
454 | 32 | TIntermUnary* node = addUnaryNode(op, child, loc); |
455 | | |
456 | 32 | if (! promote(node)) |
457 | 1 | return nullptr; |
458 | | |
459 | 31 | node->updatePrecision(); |
460 | | |
461 | | // If it's a (non-specialization) constant, it must be folded. |
462 | 31 | if (node->getOperand()->getAsConstantUnion()) |
463 | 31 | return node->getOperand()->getAsConstantUnion()->fold(op, node->getType()); |
464 | | |
465 | | // If it's a specialization constant, the result is too, |
466 | | // if the operation is allowed for specialization constants. |
467 | 0 | if (node->getOperand()->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*node)) |
468 | 0 | node->getWritableType().getQualifier().makeSpecConstant(); |
469 | | |
470 | | // If must propagate nonuniform, make a nonuniform. |
471 | 0 | if (node->getOperand()->getQualifier().isNonUniform() && isNonuniformPropagating(node->getOp())) |
472 | 0 | node->getWritableType().getQualifier().nonUniform = true; |
473 | |
|
474 | 0 | return node; |
475 | 31 | } |
476 | | |
477 | | TIntermTyped* TIntermediate::addBuiltInFunctionCall(const TSourceLoc& loc, TOperator op, bool unary, |
478 | | TIntermNode* childNode, const TType& returnType) |
479 | 0 | { |
480 | 0 | if (unary) { |
481 | | // |
482 | | // Treat it like a unary operator. |
483 | | // addUnaryMath() should get the type correct on its own; |
484 | | // including constness (which would differ from the prototype). |
485 | | // |
486 | 0 | TIntermTyped* child = childNode->getAsTyped(); |
487 | 0 | if (child == nullptr) |
488 | 0 | return nullptr; |
489 | | |
490 | 0 | if (child->getAsConstantUnion()) { |
491 | 0 | TIntermTyped* folded = child->getAsConstantUnion()->fold(op, returnType); |
492 | 0 | if (folded) |
493 | 0 | return folded; |
494 | 0 | } |
495 | | |
496 | 0 | return addUnaryNode(op, child, child->getLoc(), returnType); |
497 | 0 | } else { |
498 | | // setAggregateOperater() calls fold() for constant folding |
499 | 0 | TIntermTyped* node = setAggregateOperator(childNode, op, returnType, loc); |
500 | |
|
501 | 0 | return node; |
502 | 0 | } |
503 | 0 | } |
504 | | |
505 | | // |
506 | | // This is the safe way to change the operator on an aggregate, as it |
507 | | // does lots of error checking and fixing. Especially for establishing |
508 | | // a function call's operation on its set of parameters. Sequences |
509 | | // of instructions are also aggregates, but they just directly set |
510 | | // their operator to EOpSequence. |
511 | | // |
512 | | // Returns an aggregate node, which could be the one passed in if |
513 | | // it was already an aggregate. |
514 | | // |
515 | | TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator op, const TType& type, |
516 | | const TSourceLoc& loc) |
517 | 108 | { |
518 | 108 | TIntermAggregate* aggNode; |
519 | | |
520 | | // |
521 | | // Make sure we have an aggregate. If not turn it into one. |
522 | | // |
523 | 108 | if (node != nullptr) { |
524 | 108 | aggNode = node->getAsAggregate(); |
525 | 108 | if (aggNode == nullptr || aggNode->getOp() != EOpNull) { |
526 | | // |
527 | | // Make an aggregate containing this node. |
528 | | // |
529 | 8 | aggNode = new TIntermAggregate(); |
530 | 8 | aggNode->getSequence().push_back(node); |
531 | 8 | } |
532 | 108 | } else |
533 | 0 | aggNode = new TIntermAggregate(); |
534 | | |
535 | | // |
536 | | // Set the operator. |
537 | | // |
538 | 108 | aggNode->setOperator(op); |
539 | 108 | if (loc.line != 0 || node != nullptr) |
540 | 108 | aggNode->setLoc(loc.line != 0 ? loc : node->getLoc()); |
541 | | |
542 | 108 | aggNode->setType(type); |
543 | | |
544 | 108 | return fold(aggNode); |
545 | 108 | } |
546 | | |
547 | | bool TIntermediate::isConversionAllowed(TOperator op, TIntermTyped* node) const |
548 | 3.02k | { |
549 | | // |
550 | | // Does the base type even allow the operation? |
551 | | // |
552 | 3.02k | switch (node->getBasicType()) { |
553 | 1 | case EbtVoid: |
554 | 1 | return false; |
555 | 0 | case EbtAtomicUint: |
556 | 0 | case EbtSampler: |
557 | 0 | case EbtAccStruct: |
558 | | // opaque types can be passed to functions |
559 | 0 | if (op == EOpFunction) |
560 | 0 | break; |
561 | | |
562 | | // HLSL can assign samplers directly (no constructor) |
563 | 0 | if (getSource() == EShSourceHlsl && node->getBasicType() == EbtSampler) |
564 | 0 | break; |
565 | | |
566 | | // samplers can get assigned via a sampler constructor |
567 | | // (well, not yet, but code in the rest of this function is ready for it) |
568 | 0 | if (node->getBasicType() == EbtSampler && op == EOpAssign && |
569 | 0 | node->getAsOperator() != nullptr && node->getAsOperator()->getOp() == EOpConstructTextureSampler) |
570 | 0 | break; |
571 | | |
572 | | // otherwise, opaque types can't even be operated on, let alone converted |
573 | 0 | return false; |
574 | 3.02k | default: |
575 | 3.02k | break; |
576 | 3.02k | } |
577 | | |
578 | 3.02k | return true; |
579 | 3.02k | } |
580 | | |
581 | | bool TIntermediate::buildConvertOp(TBasicType dst, TBasicType src, TOperator& newOp) const |
582 | 3 | { |
583 | | // (bfloat16_t,fp8) <-> bool not supported |
584 | 3 | if (((src == EbtBFloat16 || src == EbtFloatE5M2 || src == EbtFloatE4M3) && dst == EbtBool) || |
585 | 3 | ((dst == EbtBFloat16 || dst == EbtFloatE5M2 || dst == EbtFloatE4M3) && src == EbtBool)) { |
586 | 0 | return false; |
587 | 0 | } |
588 | | |
589 | 3 | if ((isTypeInt(dst) || isTypeFloat(dst) || dst == EbtBool) && |
590 | 3 | (isTypeInt(src) || isTypeFloat(src) || src == EbtBool)) { |
591 | 3 | newOp = EOpConvNumeric; |
592 | 3 | return true; |
593 | 3 | } |
594 | 0 | return false; |
595 | 3 | } |
596 | | |
597 | | // This is 'mechanism' here, it does any conversion told. |
598 | | // It is about basic type, not about shape. |
599 | | // The policy comes from the shader or the calling code. |
600 | | TIntermTyped* TIntermediate::createConversion(TBasicType convertTo, TIntermTyped* node) const |
601 | 3 | { |
602 | | // |
603 | | // Add a new newNode for the conversion. |
604 | | // |
605 | | |
606 | 3 | bool convertToIntTypes = (convertTo == EbtInt8 || convertTo == EbtUint8 || |
607 | 3 | convertTo == EbtInt16 || convertTo == EbtUint16 || |
608 | 3 | convertTo == EbtInt || convertTo == EbtUint || |
609 | 0 | convertTo == EbtInt64 || convertTo == EbtUint64); |
610 | | |
611 | 3 | bool convertFromIntTypes = (node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8 || |
612 | 3 | node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16 || |
613 | 3 | node->getBasicType() == EbtInt || node->getBasicType() == EbtUint || |
614 | 0 | node->getBasicType() == EbtInt64 || node->getBasicType() == EbtUint64); |
615 | | |
616 | 3 | bool convertToFloatTypes = (convertTo == EbtFloat16 || convertTo == EbtBFloat16 || convertTo == EbtFloat || convertTo == EbtDouble || |
617 | 3 | convertTo == EbtFloatE5M2 || convertTo == EbtFloatE4M3); |
618 | | |
619 | 3 | bool convertFromFloatTypes = (node->getBasicType() == EbtFloat16 || |
620 | 3 | node->getBasicType() == EbtBFloat16 || |
621 | 3 | node->getBasicType() == EbtFloat || |
622 | 3 | node->getBasicType() == EbtDouble || |
623 | 3 | node->getBasicType() == EbtFloatE5M2 || |
624 | 3 | node->getBasicType() == EbtFloatE4M3); |
625 | | |
626 | 3 | if (((convertTo == EbtInt8 || convertTo == EbtUint8) && ! convertFromIntTypes) || |
627 | 3 | ((node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8) && ! convertToIntTypes)) { |
628 | 0 | if (! getArithemeticInt8Enabled()) { |
629 | 0 | return nullptr; |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | 3 | if (((convertTo == EbtInt16 || convertTo == EbtUint16) && ! convertFromIntTypes) || |
634 | 3 | ((node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16) && ! convertToIntTypes)) { |
635 | 0 | if (! getArithemeticInt16Enabled()) { |
636 | 0 | return nullptr; |
637 | 0 | } |
638 | 0 | } |
639 | | |
640 | 3 | if ((convertTo == EbtFloat16 && ! convertFromFloatTypes) || |
641 | 3 | (node->getBasicType() == EbtFloat16 && ! convertToFloatTypes)) { |
642 | 0 | if (! getArithemeticFloat16Enabled()) { |
643 | 0 | return nullptr; |
644 | 0 | } |
645 | 0 | } |
646 | | |
647 | 3 | TIntermUnary* newNode = nullptr; |
648 | 3 | TOperator newOp = EOpNull; |
649 | 3 | if (!buildConvertOp(convertTo, node->getBasicType(), newOp)) { |
650 | 0 | return nullptr; |
651 | 0 | } |
652 | | |
653 | 3 | TType newType(convertTo, EvqTemporary, node->getVectorSize(), node->getMatrixCols(), node->getMatrixRows()); |
654 | 3 | if (node->getType().isLongVector()) { |
655 | 0 | newType.shallowCopy(node->getType()); |
656 | 0 | newType.setBasicType(convertTo); |
657 | 0 | newType.makeTemporary(); |
658 | 0 | newType.getQualifier().clear(); |
659 | 0 | } |
660 | 3 | newNode = addUnaryNode(newOp, node, node->getLoc(), newType); |
661 | | |
662 | 3 | if (node->getAsConstantUnion()) { |
663 | | // 8/16-bit storage extensions don't support 8/16-bit constants, so don't fold conversions |
664 | | // to those types |
665 | 0 | if ((getArithemeticInt8Enabled() || !(convertTo == EbtInt8 || convertTo == EbtUint8)) && |
666 | 0 | (getArithemeticInt16Enabled() || !(convertTo == EbtInt16 || convertTo == EbtUint16)) && |
667 | 0 | (getArithemeticFloat16Enabled() || !(convertTo == EbtFloat16))) |
668 | 0 | { |
669 | 0 | TIntermTyped* folded = node->getAsConstantUnion()->fold(newOp, newType); |
670 | 0 | if (folded) |
671 | 0 | return folded; |
672 | 0 | } |
673 | 0 | } |
674 | | |
675 | | // Propagate specialization-constant-ness, if allowed |
676 | 3 | if (node->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*newNode)) |
677 | 0 | newNode->getWritableType().getQualifier().makeSpecConstant(); |
678 | | |
679 | 3 | return newNode; |
680 | 3 | } |
681 | | |
682 | | TIntermTyped* TIntermediate::addConversion(TBasicType convertTo, TIntermTyped* node) const |
683 | 0 | { |
684 | 0 | return createConversion(convertTo, node); |
685 | 0 | } |
686 | | |
687 | | // For converting a pair of operands to a binary operation to compatible |
688 | | // types with each other, relative to the operation in 'op'. |
689 | | // This does not cover assignment operations, which is asymmetric in that the |
690 | | // left type is not changeable. |
691 | | // See addConversion(op, type, node) for assignments and unary operation |
692 | | // conversions. |
693 | | // |
694 | | // Generally, this is focused on basic type conversion, not shape conversion. |
695 | | // See addShapeConversion() for shape conversions. |
696 | | // |
697 | | // Returns the converted pair of nodes. |
698 | | // Returns <nullptr, nullptr> when there is no conversion. |
699 | | std::tuple<TIntermTyped*, TIntermTyped*> |
700 | | TIntermediate::addPairConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1) |
701 | 26 | { |
702 | 26 | if (!isConversionAllowed(op, node0) || !isConversionAllowed(op, node1)) |
703 | 0 | return std::make_tuple(nullptr, nullptr); |
704 | | |
705 | 26 | if (node0->getType() != node1->getType()) { |
706 | | // If differing structure, then no conversions. |
707 | 23 | if (node0->isStruct() || node1->isStruct()) |
708 | 0 | return std::make_tuple(nullptr, nullptr); |
709 | | |
710 | | // If differing arrays, then no conversions. |
711 | 23 | if (node0->getType().isArray() || node1->getType().isArray()) |
712 | 0 | return std::make_tuple(nullptr, nullptr); |
713 | | |
714 | | // No implicit conversions for operations involving cooperative matrices |
715 | 23 | if (node0->getType().isCoopMat() || node1->getType().isCoopMat()) |
716 | 0 | return std::make_tuple(node0, node1); |
717 | 23 | } |
718 | | |
719 | 26 | auto promoteTo = std::make_tuple(EbtNumTypes, EbtNumTypes); |
720 | | |
721 | 26 | switch (op) { |
722 | | // |
723 | | // List all the binary ops that can implicitly convert one operand to the other's type; |
724 | | // This implements the 'policy' for implicit type conversion. |
725 | | // |
726 | 1 | case EOpLessThan: |
727 | 1 | case EOpGreaterThan: |
728 | 1 | case EOpLessThanEqual: |
729 | 1 | case EOpGreaterThanEqual: |
730 | 1 | case EOpEqual: |
731 | 1 | case EOpNotEqual: |
732 | | |
733 | 5 | case EOpAdd: |
734 | 8 | case EOpSub: |
735 | 8 | case EOpMul: |
736 | 10 | case EOpDiv: |
737 | 10 | case EOpMod: |
738 | | |
739 | 10 | case EOpVectorTimesScalar: |
740 | 10 | case EOpVectorTimesMatrix: |
741 | 10 | case EOpMatrixTimesVector: |
742 | 10 | case EOpMatrixTimesScalar: |
743 | | |
744 | 10 | case EOpAnd: |
745 | 10 | case EOpInclusiveOr: |
746 | 10 | case EOpExclusiveOr: |
747 | | |
748 | 26 | case EOpSequence: // used by ?: |
749 | | |
750 | 26 | if (node0->getBasicType() == node1->getBasicType()) |
751 | 5 | return std::make_tuple(node0, node1); |
752 | | |
753 | 21 | promoteTo = getConversionDestinationType(node0->getBasicType(), node1->getBasicType(), op); |
754 | 21 | if (std::get<0>(promoteTo) == EbtNumTypes || std::get<1>(promoteTo) == EbtNumTypes) |
755 | 0 | return std::make_tuple(nullptr, nullptr); |
756 | | |
757 | 21 | break; |
758 | | |
759 | 21 | case EOpLogicalAnd: |
760 | 0 | case EOpLogicalOr: |
761 | 0 | case EOpLogicalXor: |
762 | 0 | if (getSource() == EShSourceHlsl) |
763 | 0 | promoteTo = std::make_tuple(EbtBool, EbtBool); |
764 | 0 | else |
765 | 0 | return std::make_tuple(node0, node1); |
766 | 0 | break; |
767 | | |
768 | | // There are no conversions needed for GLSL; the shift amount just needs to be an |
769 | | // integer type, as does the base. |
770 | | // HLSL can promote bools to ints to make this work. |
771 | 0 | case EOpLeftShift: |
772 | 0 | case EOpRightShift: |
773 | 0 | if (getSource() == EShSourceHlsl) { |
774 | 0 | TBasicType node0BasicType = node0->getBasicType(); |
775 | 0 | if (node0BasicType == EbtBool) |
776 | 0 | node0BasicType = EbtInt; |
777 | 0 | if (node1->getBasicType() == EbtBool) |
778 | 0 | promoteTo = std::make_tuple(node0BasicType, EbtInt); |
779 | 0 | else |
780 | 0 | promoteTo = std::make_tuple(node0BasicType, node1->getBasicType()); |
781 | 0 | } else { |
782 | 0 | if (isTypeInt(node0->getBasicType()) && isTypeInt(node1->getBasicType())) |
783 | 0 | return std::make_tuple(node0, node1); |
784 | 0 | else |
785 | 0 | return std::make_tuple(nullptr, nullptr); |
786 | 0 | } |
787 | 0 | break; |
788 | | |
789 | 0 | default: |
790 | 0 | if (node0->getType() == node1->getType()) |
791 | 0 | return std::make_tuple(node0, node1); |
792 | | |
793 | 0 | return std::make_tuple(nullptr, nullptr); |
794 | 26 | } |
795 | | |
796 | 21 | TIntermTyped* newNode0; |
797 | 21 | TIntermTyped* newNode1; |
798 | | |
799 | 21 | if (std::get<0>(promoteTo) != node0->getType().getBasicType()) { |
800 | 20 | if (node0->getAsConstantUnion()) |
801 | 20 | newNode0 = promoteConstantUnion(std::get<0>(promoteTo), node0->getAsConstantUnion()); |
802 | 0 | else |
803 | 0 | newNode0 = createConversion(std::get<0>(promoteTo), node0); |
804 | 20 | } else |
805 | 1 | newNode0 = node0; |
806 | | |
807 | 21 | if (std::get<1>(promoteTo) != node1->getType().getBasicType()) { |
808 | 1 | if (node1->getAsConstantUnion()) |
809 | 1 | newNode1 = promoteConstantUnion(std::get<1>(promoteTo), node1->getAsConstantUnion()); |
810 | 0 | else |
811 | 0 | newNode1 = createConversion(std::get<1>(promoteTo), node1); |
812 | 1 | } else |
813 | 20 | newNode1 = node1; |
814 | | |
815 | 21 | return std::make_tuple(newNode0, newNode1); |
816 | 26 | } |
817 | | |
818 | | // |
819 | | // Convert the node's type to the given type, as allowed by the operation involved: 'op'. |
820 | | // For implicit conversions, 'op' is not the requested conversion, it is the explicit |
821 | | // operation requiring the implicit conversion. |
822 | | // |
823 | | // Binary operation conversions should be handled by addConversion(op, node, node), not here. |
824 | | // |
825 | | // Returns a node representing the conversion, which could be the same |
826 | | // node passed in if no conversion was needed. |
827 | | // |
828 | | // Generally, this is focused on basic type conversion, not shape conversion. |
829 | | // See addShapeConversion() for shape conversions. |
830 | | // |
831 | | // Return nullptr if a conversion can't be done. |
832 | | // |
833 | | TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) |
834 | 2.97k | { |
835 | 2.97k | if (!isConversionAllowed(op, node)) |
836 | 1 | return nullptr; |
837 | | |
838 | | // Otherwise, if types are identical, no problem |
839 | 2.97k | if (type == node->getType()) |
840 | 2.78k | return node; |
841 | | |
842 | | // If one's a structure, then no conversions. |
843 | 190 | if (type.isStruct() || node->isStruct()) |
844 | 0 | return nullptr; |
845 | | |
846 | | // If one's an array, then no conversions. |
847 | 190 | if (type.isArray() || node->getType().isArray()) |
848 | 0 | return nullptr; |
849 | | |
850 | | // Reject implicit conversions to cooperative matrix types |
851 | 190 | if (node->getType().isCoopMat() && |
852 | 0 | op != EOpConstructCooperativeMatrixNV && |
853 | 0 | op != EOpConstructCooperativeMatrixKHR && |
854 | 0 | op != glslang::EOpCompositeConstructCoopMatQCOM) |
855 | 0 | return nullptr; |
856 | | |
857 | 190 | if (node->getType().isTensorLayoutNV() || |
858 | 190 | node->getType().isTensorViewNV()) |
859 | 0 | return nullptr; |
860 | | |
861 | | // Reject implicit conversions to cooperative vector types |
862 | 190 | if (node->getType().isCoopVecNV() && |
863 | 0 | op != EOpConstructCooperativeVectorNV) |
864 | 0 | return nullptr; |
865 | | |
866 | | // Note: callers are responsible for other aspects of shape, |
867 | | // like vector and matrix sizes. |
868 | | |
869 | 190 | switch (op) { |
870 | | // |
871 | | // Explicit conversions (unary operations) |
872 | | // |
873 | 8 | case EOpConstructBool: |
874 | 8 | case EOpConstructFloat: |
875 | 11 | case EOpConstructInt: |
876 | 174 | case EOpConstructUint: |
877 | 174 | case EOpConstructDouble: |
878 | 174 | case EOpConstructFloat16: |
879 | 174 | case EOpConstructBFloat16: |
880 | 174 | case EOpConstructFloatE5M2: |
881 | 174 | case EOpConstructFloatE4M3: |
882 | 174 | case EOpConstructInt8: |
883 | 174 | case EOpConstructUint8: |
884 | 174 | case EOpConstructInt16: |
885 | 174 | case EOpConstructUint16: |
886 | 174 | case EOpConstructInt64: |
887 | 174 | case EOpConstructUint64: |
888 | 174 | case EOpConstructSaturated: |
889 | 174 | break; |
890 | | |
891 | | // |
892 | | // Implicit conversions |
893 | | // |
894 | 16 | case EOpLogicalNot: |
895 | | |
896 | 16 | case EOpFunctionCall: |
897 | | |
898 | 16 | case EOpReturn: |
899 | 16 | case EOpAssign: |
900 | 16 | case EOpAddAssign: |
901 | 16 | case EOpSubAssign: |
902 | 16 | case EOpMulAssign: |
903 | 16 | case EOpVectorTimesScalarAssign: |
904 | 16 | case EOpMatrixTimesScalarAssign: |
905 | 16 | case EOpDivAssign: |
906 | 16 | case EOpModAssign: |
907 | 16 | case EOpAndAssign: |
908 | 16 | case EOpInclusiveOrAssign: |
909 | 16 | case EOpExclusiveOrAssign: |
910 | | |
911 | 16 | case EOpAtan: |
912 | 16 | case EOpClamp: |
913 | 16 | case EOpCross: |
914 | 16 | case EOpDistance: |
915 | 16 | case EOpDot: |
916 | 16 | case EOpDst: |
917 | 16 | case EOpFaceForward: |
918 | 16 | case EOpFma: |
919 | 16 | case EOpFrexp: |
920 | 16 | case EOpLdexp: |
921 | 16 | case EOpMix: |
922 | 16 | case EOpLit: |
923 | 16 | case EOpMax: |
924 | 16 | case EOpMin: |
925 | 16 | case EOpMod: |
926 | 16 | case EOpModf: |
927 | 16 | case EOpPow: |
928 | 16 | case EOpReflect: |
929 | 16 | case EOpRefract: |
930 | 16 | case EOpSmoothStep: |
931 | 16 | case EOpStep: |
932 | | |
933 | 16 | case EOpSequence: |
934 | 16 | case EOpConstructStruct: |
935 | 16 | case EOpConstructCooperativeMatrixNV: |
936 | 16 | case EOpConstructCooperativeMatrixKHR: |
937 | 16 | case EOpConstructCooperativeVectorNV: |
938 | | |
939 | 16 | if (type.isReference() || node->getType().isReference()) { |
940 | | // types must match to assign a reference |
941 | 0 | if (type == node->getType()) |
942 | 0 | return node; |
943 | 0 | else |
944 | 0 | return nullptr; |
945 | 0 | } |
946 | | |
947 | 16 | if (type.getBasicType() == node->getType().getBasicType()) |
948 | 0 | return node; |
949 | | |
950 | 16 | if (! canImplicitlyPromote(node->getBasicType(), type.getBasicType(), op)) |
951 | 0 | return nullptr; |
952 | 16 | break; |
953 | | |
954 | | // For GLSL, there are no conversions needed; the shift amount just needs to be an |
955 | | // integer type, as do the base/result. |
956 | | // HLSL can convert the shift from a bool to an int. |
957 | 16 | case EOpLeftShiftAssign: |
958 | 0 | case EOpRightShiftAssign: |
959 | 0 | { |
960 | 0 | if (!(getSource() == EShSourceHlsl && node->getType().getBasicType() == EbtBool)) { |
961 | 0 | if (isTypeInt(type.getBasicType()) && isTypeInt(node->getBasicType())) |
962 | 0 | return node; |
963 | 0 | else |
964 | 0 | return nullptr; |
965 | 0 | } |
966 | 0 | break; |
967 | 0 | } |
968 | | |
969 | 0 | default: |
970 | | // default is to require a match; all exceptions should have case statements above |
971 | |
|
972 | 0 | if (type.getBasicType() == node->getType().getBasicType()) |
973 | 0 | return node; |
974 | 0 | else |
975 | 0 | return nullptr; |
976 | 190 | } |
977 | | |
978 | 190 | bool canPromoteConstant = true; |
979 | | // GL_EXT_shader_16bit_storage can't do OpConstantComposite with |
980 | | // 16-bit types, so disable promotion for those types. |
981 | | // Many issues with this, from JohnK: |
982 | | // - this isn't really right to discuss SPIR-V here |
983 | | // - this could easily be entirely about scalars, so is overstepping |
984 | | // - we should be looking at what the shader asked for, and saying whether or |
985 | | // not it can be done, in the parser, by calling requireExtensions(), not |
986 | | // changing language sementics on the fly by asking what extensions are in use |
987 | | // - at the time of this writing (14-Aug-2020), no test results are changed by this. |
988 | 190 | switch (op) { |
989 | 0 | case EOpConstructBFloat16: |
990 | 0 | case EOpConstructFloatE5M2: |
991 | 0 | case EOpConstructFloatE4M3: |
992 | 0 | canPromoteConstant = true; |
993 | 0 | break; |
994 | 0 | case EOpConstructFloat16: |
995 | 0 | canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || |
996 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16); |
997 | 0 | break; |
998 | 0 | case EOpConstructInt8: |
999 | 0 | case EOpConstructUint8: |
1000 | 0 | canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || |
1001 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8); |
1002 | 0 | break; |
1003 | 0 | case EOpConstructInt16: |
1004 | 0 | case EOpConstructUint16: |
1005 | 0 | canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || |
1006 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16); |
1007 | 0 | break; |
1008 | 190 | default: |
1009 | 190 | break; |
1010 | 190 | } |
1011 | | |
1012 | 190 | if (canPromoteConstant && node->getAsConstantUnion()) |
1013 | 187 | return promoteConstantUnion(type.getBasicType(), node->getAsConstantUnion()); |
1014 | | |
1015 | | // |
1016 | | // Add a new newNode for the conversion. |
1017 | | // |
1018 | 3 | TIntermTyped* newNode = createConversion(type.getBasicType(), node); |
1019 | | |
1020 | 3 | return newNode; |
1021 | 190 | } |
1022 | | |
1023 | | // Convert the node's shape of type for the given type, as allowed by the |
1024 | | // operation involved: 'op'. This is for situations where there is only one |
1025 | | // direction to consider doing the shape conversion. |
1026 | | // |
1027 | | // This implements policy, it call addShapeConversion() for the mechanism. |
1028 | | // |
1029 | | // Generally, the AST represents allowed GLSL shapes, so this isn't needed |
1030 | | // for GLSL. Bad shapes are caught in conversion or promotion. |
1031 | | // |
1032 | | // Return 'node' if no conversion was done. Promotion handles final shape |
1033 | | // checking. |
1034 | | // |
1035 | | TIntermTyped* TIntermediate::addUniShapeConversion(TOperator op, const TType& type, TIntermTyped* node) |
1036 | 11 | { |
1037 | | // some source languages don't do this |
1038 | 11 | switch (getSource()) { |
1039 | 11 | case EShSourceHlsl: |
1040 | 11 | break; |
1041 | 0 | case EShSourceGlsl: |
1042 | 0 | default: |
1043 | 0 | return node; |
1044 | 11 | } |
1045 | | |
1046 | | // some operations don't do this |
1047 | 11 | switch (op) { |
1048 | 0 | case EOpFunctionCall: |
1049 | 0 | case EOpReturn: |
1050 | 0 | break; |
1051 | | |
1052 | 0 | case EOpMulAssign: |
1053 | | // want to support vector *= scalar native ops in AST and lower, not smear, similarly for |
1054 | | // matrix *= scalar, etc. |
1055 | |
|
1056 | 0 | case EOpAddAssign: |
1057 | 0 | case EOpSubAssign: |
1058 | 0 | case EOpDivAssign: |
1059 | 0 | case EOpAndAssign: |
1060 | 0 | case EOpInclusiveOrAssign: |
1061 | 0 | case EOpExclusiveOrAssign: |
1062 | 0 | case EOpRightShiftAssign: |
1063 | 0 | case EOpLeftShiftAssign: |
1064 | 0 | if (node->getVectorSize() == 1) |
1065 | 0 | return node; |
1066 | 0 | break; |
1067 | | |
1068 | 3 | case EOpAssign: |
1069 | 3 | break; |
1070 | | |
1071 | 8 | case EOpMix: |
1072 | 8 | break; |
1073 | | |
1074 | 0 | default: |
1075 | 0 | return node; |
1076 | 11 | } |
1077 | | |
1078 | 11 | return addShapeConversion(type, node); |
1079 | 11 | } |
1080 | | |
1081 | | // Convert the nodes' shapes to be compatible for the operation 'op'. |
1082 | | // |
1083 | | // This implements policy, it call addShapeConversion() for the mechanism. |
1084 | | // |
1085 | | // Generally, the AST represents allowed GLSL shapes, so this isn't needed |
1086 | | // for GLSL. Bad shapes are caught in conversion or promotion. |
1087 | | // |
1088 | | void TIntermediate::addBiShapeConversion(TOperator op, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode) |
1089 | 22 | { |
1090 | | // some source languages don't do this |
1091 | 22 | switch (getSource()) { |
1092 | 22 | case EShSourceHlsl: |
1093 | 22 | break; |
1094 | 0 | case EShSourceGlsl: |
1095 | 0 | default: |
1096 | 0 | return; |
1097 | 22 | } |
1098 | | |
1099 | | // some operations don't do this |
1100 | | // 'break' will mean attempt bidirectional conversion |
1101 | 22 | switch (op) { |
1102 | 0 | case EOpMulAssign: |
1103 | 0 | case EOpAssign: |
1104 | 0 | case EOpAddAssign: |
1105 | 0 | case EOpSubAssign: |
1106 | 0 | case EOpDivAssign: |
1107 | 0 | case EOpAndAssign: |
1108 | 0 | case EOpInclusiveOrAssign: |
1109 | 0 | case EOpExclusiveOrAssign: |
1110 | 0 | case EOpRightShiftAssign: |
1111 | 0 | case EOpLeftShiftAssign: |
1112 | | // switch to unidirectional conversion (the lhs can't change) |
1113 | 0 | rhsNode = addUniShapeConversion(op, lhsNode->getType(), rhsNode); |
1114 | 0 | return; |
1115 | | |
1116 | 0 | case EOpMul: |
1117 | | // matrix multiply does not change shapes |
1118 | 0 | if (lhsNode->isMatrix() && rhsNode->isMatrix()) |
1119 | 0 | return; |
1120 | 0 | [[fallthrough]]; |
1121 | 4 | case EOpAdd: |
1122 | 7 | case EOpSub: |
1123 | 9 | case EOpDiv: |
1124 | | // want to support vector * scalar native ops in AST and lower, not smear, similarly for |
1125 | | // matrix * vector, etc. |
1126 | 9 | if (lhsNode->getVectorSize() == 1 || rhsNode->getVectorSize() == 1) |
1127 | 9 | return; |
1128 | 0 | break; |
1129 | | |
1130 | 0 | case EOpRightShift: |
1131 | 0 | case EOpLeftShift: |
1132 | | // can natively support the right operand being a scalar and the left a vector, |
1133 | | // but not the reverse |
1134 | 0 | if (rhsNode->getVectorSize() == 1) |
1135 | 0 | return; |
1136 | 0 | break; |
1137 | | |
1138 | 1 | case EOpLessThan: |
1139 | 1 | case EOpGreaterThan: |
1140 | 1 | case EOpLessThanEqual: |
1141 | 1 | case EOpGreaterThanEqual: |
1142 | | |
1143 | 1 | case EOpEqual: |
1144 | 1 | case EOpNotEqual: |
1145 | | |
1146 | 1 | case EOpLogicalAnd: |
1147 | 1 | case EOpLogicalOr: |
1148 | 1 | case EOpLogicalXor: |
1149 | | |
1150 | 1 | case EOpAnd: |
1151 | 1 | case EOpInclusiveOr: |
1152 | 1 | case EOpExclusiveOr: |
1153 | | |
1154 | 13 | case EOpMix: |
1155 | 13 | break; |
1156 | | |
1157 | 0 | default: |
1158 | 0 | return; |
1159 | 22 | } |
1160 | | |
1161 | | // Do bidirectional conversions |
1162 | 13 | if (lhsNode->getType().isScalarOrVec1() || rhsNode->getType().isScalarOrVec1()) { |
1163 | 13 | if (lhsNode->getType().isScalarOrVec1()) |
1164 | 13 | lhsNode = addShapeConversion(rhsNode->getType(), lhsNode); |
1165 | 0 | else |
1166 | 0 | rhsNode = addShapeConversion(lhsNode->getType(), rhsNode); |
1167 | 13 | } |
1168 | 13 | lhsNode = addShapeConversion(rhsNode->getType(), lhsNode); |
1169 | 13 | rhsNode = addShapeConversion(lhsNode->getType(), rhsNode); |
1170 | 13 | } |
1171 | | |
1172 | | // Convert the node's shape of type for the given type, as allowed by the |
1173 | | // operation involved: 'op'. |
1174 | | // |
1175 | | // Generally, the AST represents allowed GLSL shapes, so this isn't needed |
1176 | | // for GLSL. Bad shapes are caught in conversion or promotion. |
1177 | | // |
1178 | | // Return 'node' if no conversion was done. Promotion handles final shape |
1179 | | // checking. |
1180 | | // |
1181 | | TIntermTyped* TIntermediate::addShapeConversion(const TType& type, TIntermTyped* node) |
1182 | 50 | { |
1183 | | // no conversion needed |
1184 | 50 | if (node->getType() == type) |
1185 | 34 | return node; |
1186 | | |
1187 | | // structures and arrays don't change shape, either to or from |
1188 | 16 | if (node->getType().isStruct() || node->getType().isArray() || |
1189 | 16 | type.isStruct() || type.isArray()) |
1190 | 0 | return node; |
1191 | | |
1192 | | // The new node that handles the conversion |
1193 | 16 | TOperator constructorOp = mapTypeToConstructorOp(type); |
1194 | | |
1195 | 16 | if (getSource() == EShSourceHlsl) { |
1196 | | // HLSL rules for scalar, vector and matrix conversions: |
1197 | | // 1) scalar can become anything, initializing every component with its value |
1198 | | // 2) vector and matrix can become scalar, first element is used (warning: truncation) |
1199 | | // 3) matrix can become matrix with less rows and/or columns (warning: truncation) |
1200 | | // 4) vector can become vector with less rows size (warning: truncation) |
1201 | | // 5a) vector 4 can become 2x2 matrix (special case) (same packing layout, its a reinterpret) |
1202 | | // 5b) 2x2 matrix can become vector 4 (special case) (same packing layout, its a reinterpret) |
1203 | | |
1204 | 16 | const TType &sourceType = node->getType(); |
1205 | | |
1206 | | // rule 1 for scalar to matrix is special |
1207 | 16 | if (sourceType.isScalarOrVec1() && type.isMatrix()) { |
1208 | | |
1209 | | // HLSL semantics: the scalar (or vec1) is replicated to every component of the matrix. Left to its |
1210 | | // own devices, the constructor from a scalar would populate the diagonal. This forces replication |
1211 | | // to every matrix element. |
1212 | | |
1213 | | // Note that if the node is complex (e.g, a function call), we don't want to duplicate it here |
1214 | | // repeatedly, so we copy it to a temp, then use the temp. |
1215 | 0 | const int matSize = type.computeNumComponents(); |
1216 | 0 | TIntermAggregate* rhsAggregate = new TIntermAggregate(); |
1217 | |
|
1218 | 0 | const bool isSimple = (node->getAsSymbolNode() != nullptr) || (node->getAsConstantUnion() != nullptr); |
1219 | |
|
1220 | 0 | if (!isSimple) { |
1221 | 0 | assert(0); // TODO: use node replicator service when available. |
1222 | 0 | } |
1223 | |
|
1224 | 0 | for (int x = 0; x < matSize; ++x) |
1225 | 0 | rhsAggregate->getSequence().push_back(node); |
1226 | |
|
1227 | 0 | return setAggregateOperator(rhsAggregate, constructorOp, type, node->getLoc()); |
1228 | 0 | } |
1229 | | |
1230 | | // rule 1 and 2 |
1231 | 16 | if ((sourceType.isScalar() && !type.isScalar()) || (!sourceType.isScalar() && type.isScalar())) |
1232 | 16 | return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); |
1233 | | |
1234 | | // rule 3 and 5b |
1235 | 0 | if (sourceType.isMatrix()) { |
1236 | | // rule 3 |
1237 | 0 | if (type.isMatrix()) { |
1238 | 0 | if ((sourceType.getMatrixCols() != type.getMatrixCols() || sourceType.getMatrixRows() != type.getMatrixRows()) && |
1239 | 0 | sourceType.getMatrixCols() >= type.getMatrixCols() && sourceType.getMatrixRows() >= type.getMatrixRows()) |
1240 | 0 | return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); |
1241 | | // rule 5b |
1242 | 0 | } else if (type.isVector()) { |
1243 | 0 | if (type.getVectorSize() == 4 && sourceType.getMatrixCols() == 2 && sourceType.getMatrixRows() == 2) |
1244 | 0 | return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); |
1245 | 0 | } |
1246 | 0 | } |
1247 | | |
1248 | | // rule 4 and 5a |
1249 | 0 | if (sourceType.isVector()) { |
1250 | | // rule 4 |
1251 | 0 | if (type.isVector()) |
1252 | 0 | { |
1253 | 0 | if (sourceType.getVectorSize() > type.getVectorSize()) |
1254 | 0 | return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); |
1255 | | // rule 5a |
1256 | 0 | } else if (type.isMatrix()) { |
1257 | 0 | if (sourceType.getVectorSize() == 4 && type.getMatrixCols() == 2 && type.getMatrixRows() == 2) |
1258 | 0 | return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); |
1259 | 0 | } |
1260 | 0 | } |
1261 | 0 | } |
1262 | | |
1263 | | // scalar -> vector or vec1 -> vector or |
1264 | | // vector -> scalar or |
1265 | | // bigger vector -> smaller vector |
1266 | 0 | if ((node->getType().isScalarOrVec1() && type.isVector()) || |
1267 | 0 | (node->getType().isVector() && type.isScalar()) || |
1268 | 0 | (node->isVector() && type.isVector() && node->getVectorSize() > type.getVectorSize())) |
1269 | 0 | return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); |
1270 | | |
1271 | 0 | return node; |
1272 | 0 | } |
1273 | | |
1274 | | bool TIntermediate::isIntegralPromotion(TBasicType from, TBasicType to) const |
1275 | 0 | { |
1276 | | // integral promotions |
1277 | 0 | if (to == EbtInt) { |
1278 | 0 | switch(from) { |
1279 | 0 | case EbtInt8: |
1280 | 0 | case EbtInt16: |
1281 | 0 | case EbtUint8: |
1282 | 0 | case EbtUint16: |
1283 | 0 | return true; |
1284 | 0 | default: |
1285 | 0 | break; |
1286 | 0 | } |
1287 | 0 | } |
1288 | 0 | return false; |
1289 | 0 | } |
1290 | | |
1291 | | bool TIntermediate::isFPPromotion(TBasicType from, TBasicType to) const |
1292 | 0 | { |
1293 | | // floating-point promotions |
1294 | 0 | if (to == EbtDouble) { |
1295 | 0 | switch(from) { |
1296 | 0 | case EbtBFloat16: |
1297 | 0 | case EbtFloatE5M2: |
1298 | 0 | case EbtFloatE4M3: |
1299 | 0 | case EbtFloat16: |
1300 | 0 | case EbtFloat: |
1301 | 0 | return true; |
1302 | 0 | default: |
1303 | 0 | break; |
1304 | 0 | } |
1305 | 0 | } |
1306 | 0 | return false; |
1307 | 0 | } |
1308 | | |
1309 | | bool TIntermediate::isIntegralConversion(TBasicType from, TBasicType to) const |
1310 | 0 | { |
1311 | 0 | switch (from) { |
1312 | 0 | case EbtInt: |
1313 | 0 | switch(to) { |
1314 | 0 | case EbtUint: |
1315 | 0 | return version >= 400 || getSource() == EShSourceHlsl; |
1316 | 0 | case EbtInt64: |
1317 | 0 | case EbtUint64: |
1318 | 0 | return true; |
1319 | 0 | default: |
1320 | 0 | break; |
1321 | 0 | } |
1322 | 0 | break; |
1323 | 0 | case EbtUint: |
1324 | 0 | switch(to) { |
1325 | 0 | case EbtInt64: |
1326 | 0 | case EbtUint64: |
1327 | 0 | return true; |
1328 | 0 | default: |
1329 | 0 | break; |
1330 | 0 | } |
1331 | 0 | break; |
1332 | 0 | case EbtInt8: |
1333 | 0 | switch (to) { |
1334 | 0 | case EbtUint8: |
1335 | 0 | case EbtInt16: |
1336 | 0 | case EbtUint16: |
1337 | 0 | case EbtUint: |
1338 | 0 | case EbtInt64: |
1339 | 0 | case EbtUint64: |
1340 | 0 | return true; |
1341 | 0 | default: |
1342 | 0 | break; |
1343 | 0 | } |
1344 | 0 | break; |
1345 | 0 | case EbtUint8: |
1346 | 0 | switch (to) { |
1347 | 0 | case EbtInt16: |
1348 | 0 | case EbtUint16: |
1349 | 0 | case EbtUint: |
1350 | 0 | case EbtInt64: |
1351 | 0 | case EbtUint64: |
1352 | 0 | return true; |
1353 | 0 | default: |
1354 | 0 | break; |
1355 | 0 | } |
1356 | 0 | break; |
1357 | 0 | case EbtInt16: |
1358 | 0 | switch(to) { |
1359 | 0 | case EbtUint16: |
1360 | 0 | case EbtUint: |
1361 | 0 | case EbtInt64: |
1362 | 0 | case EbtUint64: |
1363 | 0 | return true; |
1364 | 0 | default: |
1365 | 0 | break; |
1366 | 0 | } |
1367 | 0 | break; |
1368 | 0 | case EbtUint16: |
1369 | 0 | switch(to) { |
1370 | 0 | case EbtUint: |
1371 | 0 | case EbtInt64: |
1372 | 0 | case EbtUint64: |
1373 | 0 | return true; |
1374 | 0 | default: |
1375 | 0 | break; |
1376 | 0 | } |
1377 | 0 | break; |
1378 | 0 | case EbtInt64: |
1379 | 0 | if (to == EbtUint64) { |
1380 | 0 | return true; |
1381 | 0 | } |
1382 | 0 | break; |
1383 | 0 | default: |
1384 | 0 | break; |
1385 | 0 | } |
1386 | 0 | return false; |
1387 | 0 | } |
1388 | | |
1389 | | bool TIntermediate::isFPConversion(TBasicType from, TBasicType to) const |
1390 | 0 | { |
1391 | 0 | if (to == EbtFloat && (from == EbtFloat16 || from == EbtBFloat16 || from == EbtFloatE5M2 || from == EbtFloatE4M3)) { |
1392 | 0 | return true; |
1393 | 0 | } else { |
1394 | 0 | return false; |
1395 | 0 | } |
1396 | 0 | } |
1397 | | |
1398 | | bool TIntermediate::isFPIntegralConversion(TBasicType from, TBasicType to) const |
1399 | 0 | { |
1400 | 0 | switch (from) { |
1401 | 0 | case EbtInt: |
1402 | 0 | case EbtUint: |
1403 | 0 | switch(to) { |
1404 | 0 | case EbtFloat: |
1405 | 0 | case EbtDouble: |
1406 | 0 | return true; |
1407 | 0 | default: |
1408 | 0 | break; |
1409 | 0 | } |
1410 | 0 | break; |
1411 | 0 | case EbtInt8: |
1412 | 0 | case EbtUint8: |
1413 | 0 | case EbtInt16: |
1414 | 0 | case EbtUint16: |
1415 | 0 | switch (to) { |
1416 | 0 | case EbtFloat16: |
1417 | 0 | case EbtFloat: |
1418 | 0 | case EbtDouble: |
1419 | 0 | return true; |
1420 | 0 | default: |
1421 | 0 | break; |
1422 | 0 | } |
1423 | 0 | break; |
1424 | 0 | case EbtInt64: |
1425 | 0 | case EbtUint64: |
1426 | 0 | if (to == EbtDouble) { |
1427 | 0 | return true; |
1428 | 0 | } |
1429 | 0 | break; |
1430 | 0 | default: |
1431 | 0 | break; |
1432 | 0 | } |
1433 | 0 | return false; |
1434 | 0 | } |
1435 | | |
1436 | | // |
1437 | | // See if the 'from' type is allowed to be implicitly converted to the |
1438 | | // 'to' type. This is not about vector/array/struct, only about basic type. |
1439 | | // |
1440 | | bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op) const |
1441 | 57 | { |
1442 | 57 | if ((isEsProfile() && version < 310 ) || version == 110) |
1443 | 0 | return false; |
1444 | | |
1445 | 57 | if (from == to) |
1446 | 0 | return true; |
1447 | | |
1448 | | // TODO: Move more policies into language-specific handlers. |
1449 | | // Some languages allow more general (or potentially, more specific) conversions under some conditions. |
1450 | 57 | if (getSource() == EShSourceHlsl) { |
1451 | 57 | const bool fromConvertable = (from == EbtFloat || from == EbtDouble || from == EbtInt || from == EbtUint || from == EbtBool); |
1452 | 57 | const bool toConvertable = (to == EbtFloat || to == EbtDouble || to == EbtInt || to == EbtUint || to == EbtBool); |
1453 | | |
1454 | 57 | if (fromConvertable && toConvertable) { |
1455 | 57 | switch (op) { |
1456 | 0 | case EOpAndAssign: // assignments can perform arbitrary conversions |
1457 | 0 | case EOpInclusiveOrAssign: // ... |
1458 | 0 | case EOpExclusiveOrAssign: // ... |
1459 | 0 | case EOpAssign: // ... |
1460 | 0 | case EOpAddAssign: // ... |
1461 | 0 | case EOpSubAssign: // ... |
1462 | 0 | case EOpMulAssign: // ... |
1463 | 0 | case EOpVectorTimesScalarAssign: // ... |
1464 | 0 | case EOpMatrixTimesScalarAssign: // ... |
1465 | 0 | case EOpDivAssign: // ... |
1466 | 0 | case EOpModAssign: // ... |
1467 | 0 | case EOpReturn: // function returns can also perform arbitrary conversions |
1468 | 0 | case EOpFunctionCall: // conversion of a calling parameter |
1469 | 16 | case EOpLogicalNot: |
1470 | 16 | case EOpLogicalAnd: |
1471 | 16 | case EOpLogicalOr: |
1472 | 16 | case EOpLogicalXor: |
1473 | 16 | case EOpConstructStruct: |
1474 | 16 | return true; |
1475 | 41 | default: |
1476 | 41 | break; |
1477 | 57 | } |
1478 | 57 | } |
1479 | 57 | } |
1480 | | |
1481 | 41 | if (getSource() == EShSourceHlsl) { |
1482 | | // HLSL |
1483 | 41 | if (from == EbtBool && (to == EbtInt || to == EbtUint || to == EbtFloat)) |
1484 | 9 | return true; |
1485 | 41 | } else { |
1486 | | // GLSL |
1487 | 0 | if (isIntegralPromotion(from, to) || |
1488 | 0 | isFPPromotion(from, to) || |
1489 | 0 | isIntegralConversion(from, to) || |
1490 | 0 | isFPConversion(from, to) || |
1491 | 0 | isFPIntegralConversion(from, to)) { |
1492 | |
|
1493 | 0 | if (numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || |
1494 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8) || |
1495 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16) || |
1496 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int32) || |
1497 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int64) || |
1498 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16) || |
1499 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float32) || |
1500 | 0 | numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float64)) { |
1501 | 0 | return true; |
1502 | 0 | } |
1503 | 0 | } |
1504 | 0 | } |
1505 | | |
1506 | 32 | if (isEsProfile()) { |
1507 | 0 | switch (to) { |
1508 | 0 | case EbtFloat: |
1509 | 0 | switch (from) { |
1510 | 0 | case EbtInt: |
1511 | 0 | case EbtUint: |
1512 | 0 | return numericFeatures.contains(TNumericFeatures::shader_implicit_conversions); |
1513 | 0 | default: |
1514 | 0 | return false; |
1515 | 0 | } |
1516 | 0 | case EbtUint: |
1517 | 0 | switch (from) { |
1518 | 0 | case EbtInt: |
1519 | 0 | return numericFeatures.contains(TNumericFeatures::shader_implicit_conversions); |
1520 | 0 | default: |
1521 | 0 | return false; |
1522 | 0 | } |
1523 | 0 | default: |
1524 | 0 | return false; |
1525 | 0 | } |
1526 | 32 | } else { |
1527 | 32 | switch (to) { |
1528 | 0 | case EbtDouble: |
1529 | 0 | switch (from) { |
1530 | 0 | case EbtInt: |
1531 | 0 | case EbtUint: |
1532 | 0 | case EbtInt64: |
1533 | 0 | case EbtUint64: |
1534 | 0 | case EbtFloat: |
1535 | 0 | return version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64); |
1536 | 0 | case EbtInt16: |
1537 | 0 | case EbtUint16: |
1538 | 0 | return (version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64)) && |
1539 | 0 | (numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types) || |
1540 | 0 | numericFeatures.contains(TNumericFeatures::gpu_shader_int16)); |
1541 | 0 | case EbtFloat16: |
1542 | 0 | return (version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64)) && |
1543 | 0 | (numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types) || |
1544 | 0 | numericFeatures.contains(TNumericFeatures::gpu_shader_half_float)); |
1545 | 0 | case EbtBFloat16: |
1546 | 0 | case EbtFloatE5M2: |
1547 | 0 | case EbtFloatE4M3: |
1548 | 0 | return true; |
1549 | 0 | case EbtInt8: |
1550 | 0 | case EbtUint8: |
1551 | 0 | return numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1552 | 0 | default: |
1553 | 0 | return false; |
1554 | 0 | } |
1555 | 0 | case EbtFloat: |
1556 | 0 | switch (from) { |
1557 | 0 | case EbtInt: |
1558 | 0 | case EbtUint: |
1559 | 0 | return true; |
1560 | 0 | case EbtBool: |
1561 | 0 | return getSource() == EShSourceHlsl; |
1562 | 0 | case EbtInt16: |
1563 | 0 | case EbtUint16: |
1564 | 0 | return numericFeatures.contains(TNumericFeatures::gpu_shader_int16) || |
1565 | 0 | numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1566 | 0 | case EbtFloat16: |
1567 | 0 | return numericFeatures.contains(TNumericFeatures::gpu_shader_half_float) || |
1568 | 0 | numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types) || |
1569 | 0 | getSource() == EShSourceHlsl; |
1570 | 0 | case EbtBFloat16: |
1571 | 0 | case EbtFloatE5M2: |
1572 | 0 | case EbtFloatE4M3: |
1573 | 0 | return true; |
1574 | 0 | case EbtInt8: |
1575 | 0 | case EbtUint8: |
1576 | 0 | return numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1577 | 0 | default: |
1578 | 0 | return false; |
1579 | 0 | } |
1580 | 12 | case EbtUint: |
1581 | 12 | switch (from) { |
1582 | 12 | case EbtInt: |
1583 | 12 | return version >= 400 || getSource() == EShSourceHlsl || |
1584 | 0 | IsRequestedExtension(E_GL_ARB_gpu_shader5) || |
1585 | 0 | numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1586 | 0 | case EbtBool: |
1587 | 0 | return getSource() == EShSourceHlsl; |
1588 | 0 | case EbtInt16: |
1589 | 0 | case EbtUint16: |
1590 | 0 | return numericFeatures.contains(TNumericFeatures::gpu_shader_int16) || |
1591 | 0 | numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1592 | 0 | case EbtInt8: |
1593 | 0 | case EbtUint8: |
1594 | 0 | return numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1595 | 0 | default: |
1596 | 0 | return false; |
1597 | 12 | } |
1598 | 12 | case EbtInt: |
1599 | 12 | switch (from) { |
1600 | 0 | case EbtBool: |
1601 | 0 | return getSource() == EShSourceHlsl; |
1602 | 0 | case EbtInt16: |
1603 | 0 | return numericFeatures.contains(TNumericFeatures::gpu_shader_int16) || |
1604 | 0 | numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1605 | 0 | case EbtInt8: |
1606 | 0 | return numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1607 | 12 | default: |
1608 | 12 | return false; |
1609 | 12 | } |
1610 | 0 | case EbtUint64: |
1611 | 0 | switch (from) { |
1612 | 0 | case EbtInt: |
1613 | 0 | case EbtUint: |
1614 | 0 | case EbtInt64: |
1615 | 0 | return true; |
1616 | 0 | case EbtInt16: |
1617 | 0 | case EbtUint16: |
1618 | 0 | return numericFeatures.contains(TNumericFeatures::gpu_shader_int16) || |
1619 | 0 | numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1620 | 0 | case EbtInt8: |
1621 | 0 | case EbtUint8: |
1622 | 0 | return numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1623 | 0 | default: |
1624 | 0 | return false; |
1625 | 0 | } |
1626 | 0 | case EbtInt64: |
1627 | 0 | switch (from) { |
1628 | 0 | case EbtInt: |
1629 | 0 | return true; |
1630 | 0 | case EbtInt8: |
1631 | 0 | return numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1632 | 0 | case EbtInt16: |
1633 | 0 | return numericFeatures.contains(TNumericFeatures::gpu_shader_int16) || |
1634 | 0 | numericFeatures.contains(TNumericFeatures::nv_gpu_shader5_types); |
1635 | 0 | default: |
1636 | 0 | return false; |
1637 | 0 | } |
1638 | 0 | case EbtFloat16: |
1639 | 0 | switch (from) { |
1640 | 0 | case EbtInt16: |
1641 | 0 | case EbtUint16: |
1642 | 0 | return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); |
1643 | 0 | case EbtFloatE5M2: |
1644 | 0 | case EbtFloatE4M3: |
1645 | 0 | return true; |
1646 | 0 | default: |
1647 | 0 | break; |
1648 | 0 | } |
1649 | 0 | return false; |
1650 | 0 | case EbtBFloat16: |
1651 | 0 | switch (from) { |
1652 | 0 | case EbtFloatE5M2: |
1653 | 0 | case EbtFloatE4M3: |
1654 | 0 | return true; |
1655 | 0 | default: |
1656 | 0 | break; |
1657 | 0 | } |
1658 | 0 | return false; |
1659 | 0 | case EbtUint16: |
1660 | 0 | switch (from) { |
1661 | 0 | case EbtInt16: |
1662 | 0 | return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); |
1663 | 0 | default: |
1664 | 0 | break; |
1665 | 0 | } |
1666 | 0 | return false; |
1667 | 8 | default: |
1668 | 8 | return false; |
1669 | 32 | } |
1670 | 32 | } |
1671 | | |
1672 | 0 | return false; |
1673 | 32 | } |
1674 | | |
1675 | | static bool canSignedIntTypeRepresentAllUnsignedValues(TBasicType sintType, TBasicType uintType) |
1676 | 0 | { |
1677 | 0 | switch(sintType) { |
1678 | 0 | case EbtInt8: |
1679 | 0 | switch(uintType) { |
1680 | 0 | case EbtUint8: |
1681 | 0 | case EbtUint16: |
1682 | 0 | case EbtUint: |
1683 | 0 | case EbtUint64: |
1684 | 0 | return false; |
1685 | 0 | default: |
1686 | 0 | assert(false); |
1687 | 0 | return false; |
1688 | 0 | } |
1689 | 0 | break; |
1690 | 0 | case EbtInt16: |
1691 | 0 | switch(uintType) { |
1692 | 0 | case EbtUint8: |
1693 | 0 | return true; |
1694 | 0 | case EbtUint16: |
1695 | 0 | case EbtUint: |
1696 | 0 | case EbtUint64: |
1697 | 0 | return false; |
1698 | 0 | default: |
1699 | 0 | assert(false); |
1700 | 0 | return false; |
1701 | 0 | } |
1702 | 0 | break; |
1703 | 0 | case EbtInt: |
1704 | 0 | switch(uintType) { |
1705 | 0 | case EbtUint8: |
1706 | 0 | case EbtUint16: |
1707 | 0 | return true; |
1708 | 0 | case EbtUint: |
1709 | 0 | return false; |
1710 | 0 | default: |
1711 | 0 | assert(false); |
1712 | 0 | return false; |
1713 | 0 | } |
1714 | 0 | break; |
1715 | 0 | case EbtInt64: |
1716 | 0 | switch(uintType) { |
1717 | 0 | case EbtUint8: |
1718 | 0 | case EbtUint16: |
1719 | 0 | case EbtUint: |
1720 | 0 | return true; |
1721 | 0 | case EbtUint64: |
1722 | 0 | return false; |
1723 | 0 | default: |
1724 | 0 | assert(false); |
1725 | 0 | return false; |
1726 | 0 | } |
1727 | 0 | break; |
1728 | 0 | default: |
1729 | 0 | assert(false); |
1730 | 0 | return false; |
1731 | 0 | } |
1732 | 0 | } |
1733 | | |
1734 | | |
1735 | | static TBasicType getCorrespondingUnsignedType(TBasicType type) |
1736 | 0 | { |
1737 | 0 | switch(type) { |
1738 | 0 | case EbtInt8: |
1739 | 0 | return EbtUint8; |
1740 | 0 | case EbtInt16: |
1741 | 0 | return EbtUint16; |
1742 | 0 | case EbtInt: |
1743 | 0 | return EbtUint; |
1744 | 0 | case EbtInt64: |
1745 | 0 | return EbtUint64; |
1746 | 0 | default: |
1747 | 0 | assert(false); |
1748 | 0 | return EbtNumTypes; |
1749 | 0 | } |
1750 | 0 | } |
1751 | | |
1752 | | // Implements the following rules |
1753 | | // - If either operand has type float64_t or derived from float64_t, |
1754 | | // the other shall be converted to float64_t or derived type. |
1755 | | // - Otherwise, if either operand has type float32_t or derived from |
1756 | | // float32_t, the other shall be converted to float32_t or derived type. |
1757 | | // - Otherwise, if either operand has type float16_t or derived from |
1758 | | // float16_t, the other shall be converted to float16_t or derived type. |
1759 | | // - Otherwise, if both operands have integer types the following rules |
1760 | | // shall be applied to the operands: |
1761 | | // - If both operands have the same type, no further conversion |
1762 | | // is needed. |
1763 | | // - Otherwise, if both operands have signed integer types or both |
1764 | | // have unsigned integer types, the operand with the type of lesser |
1765 | | // integer conversion rank shall be converted to the type of the |
1766 | | // operand with greater rank. |
1767 | | // - Otherwise, if the operand that has unsigned integer type has rank |
1768 | | // greater than or equal to the rank of the type of the other |
1769 | | // operand, the operand with signed integer type shall be converted |
1770 | | // to the type of the operand with unsigned integer type. |
1771 | | // - Otherwise, if the type of the operand with signed integer type can |
1772 | | // represent all of the values of the type of the operand with |
1773 | | // unsigned integer type, the operand with unsigned integer type |
1774 | | // shall be converted to the type of the operand with signed |
1775 | | // integer type. |
1776 | | // - Otherwise, both operands shall be converted to the unsigned |
1777 | | // integer type corresponding to the type of the operand with signed |
1778 | | // integer type. |
1779 | | |
1780 | | std::tuple<TBasicType, TBasicType> TIntermediate::getConversionDestinationType(TBasicType type0, TBasicType type1, TOperator op) const |
1781 | 21 | { |
1782 | 21 | TBasicType res0 = EbtNumTypes; |
1783 | 21 | TBasicType res1 = EbtNumTypes; |
1784 | | |
1785 | 21 | if ((isEsProfile() && |
1786 | 0 | (version < 310 || !numericFeatures.contains(TNumericFeatures::shader_implicit_conversions))) || |
1787 | 21 | version == 110) |
1788 | 0 | return std::make_tuple(res0, res1); |
1789 | | |
1790 | 21 | if (getSource() == EShSourceHlsl) { |
1791 | 21 | if (canImplicitlyPromote(type1, type0, op)) { |
1792 | 1 | res0 = type0; |
1793 | 1 | res1 = type0; |
1794 | 20 | } else if (canImplicitlyPromote(type0, type1, op)) { |
1795 | 20 | res0 = type1; |
1796 | 20 | res1 = type1; |
1797 | 20 | } |
1798 | 21 | return std::make_tuple(res0, res1); |
1799 | 21 | } |
1800 | | |
1801 | 0 | if ((type0 == EbtDouble && canImplicitlyPromote(type1, EbtDouble, op)) || |
1802 | 0 | (type1 == EbtDouble && canImplicitlyPromote(type0, EbtDouble, op)) ) { |
1803 | 0 | res0 = EbtDouble; |
1804 | 0 | res1 = EbtDouble; |
1805 | 0 | } else if ((type0 == EbtFloat && canImplicitlyPromote(type1, EbtFloat, op)) || |
1806 | 0 | (type1 == EbtFloat && canImplicitlyPromote(type0, EbtFloat, op)) ) { |
1807 | 0 | res0 = EbtFloat; |
1808 | 0 | res1 = EbtFloat; |
1809 | 0 | } else if ((type0 == EbtFloat16 && canImplicitlyPromote(type1, EbtFloat16, op)) || |
1810 | 0 | (type1 == EbtFloat16 && canImplicitlyPromote(type0, EbtFloat16, op)) ) { |
1811 | 0 | res0 = EbtFloat16; |
1812 | 0 | res1 = EbtFloat16; |
1813 | 0 | } else if ((type0 == EbtBFloat16 && canImplicitlyPromote(type1, EbtBFloat16, op)) || |
1814 | 0 | (type1 == EbtBFloat16 && canImplicitlyPromote(type0, EbtBFloat16, op)) ) { |
1815 | 0 | res0 = EbtBFloat16; |
1816 | 0 | res1 = EbtBFloat16; |
1817 | 0 | } else if (isTypeInt(type0) && isTypeInt(type1) && |
1818 | 0 | (canImplicitlyPromote(type0, type1, op) || canImplicitlyPromote(type1, type0, op))) { |
1819 | 0 | if ((isTypeSignedInt(type0) && isTypeSignedInt(type1)) || |
1820 | 0 | (isTypeUnsignedInt(type0) && isTypeUnsignedInt(type1))) { |
1821 | 0 | if (getTypeRank(type0) < getTypeRank(type1)) { |
1822 | 0 | res0 = type1; |
1823 | 0 | res1 = type1; |
1824 | 0 | } else { |
1825 | 0 | res0 = type0; |
1826 | 0 | res1 = type0; |
1827 | 0 | } |
1828 | 0 | } else if (isTypeUnsignedInt(type0) && (getTypeRank(type0) > getTypeRank(type1))) { |
1829 | 0 | res0 = type0; |
1830 | 0 | res1 = type0; |
1831 | 0 | } else if (isTypeUnsignedInt(type1) && (getTypeRank(type1) > getTypeRank(type0))) { |
1832 | 0 | res0 = type1; |
1833 | 0 | res1 = type1; |
1834 | 0 | } else if (isTypeSignedInt(type0)) { |
1835 | 0 | if (canSignedIntTypeRepresentAllUnsignedValues(type0, type1)) { |
1836 | 0 | res0 = type0; |
1837 | 0 | res1 = type0; |
1838 | 0 | } else { |
1839 | 0 | res0 = getCorrespondingUnsignedType(type0); |
1840 | 0 | res1 = getCorrespondingUnsignedType(type0); |
1841 | 0 | } |
1842 | 0 | } else if (isTypeSignedInt(type1)) { |
1843 | 0 | if (canSignedIntTypeRepresentAllUnsignedValues(type1, type0)) { |
1844 | 0 | res0 = type1; |
1845 | 0 | res1 = type1; |
1846 | 0 | } else { |
1847 | 0 | res0 = getCorrespondingUnsignedType(type1); |
1848 | 0 | res1 = getCorrespondingUnsignedType(type1); |
1849 | 0 | } |
1850 | 0 | } |
1851 | 0 | } |
1852 | |
|
1853 | 0 | return std::make_tuple(res0, res1); |
1854 | 21 | } |
1855 | | |
1856 | | // |
1857 | | // Given a type, find what operation would fully construct it. |
1858 | | // |
1859 | | TOperator TIntermediate::mapTypeToConstructorOp(const TType& type) const |
1860 | 519 | { |
1861 | 519 | TOperator op = EOpNull; |
1862 | | |
1863 | 519 | if (type.getQualifier().isNonUniform()) |
1864 | 0 | return EOpConstructNonuniform; |
1865 | | |
1866 | 519 | if (type.isCoopMatNV()) |
1867 | 0 | return EOpConstructCooperativeMatrixNV; |
1868 | | |
1869 | 519 | if (type.isCoopMatKHR()) |
1870 | 0 | return EOpConstructCooperativeMatrixKHR; |
1871 | | |
1872 | 519 | if (type.isCoopVecOrLongVector()) |
1873 | 0 | return EOpConstructCooperativeVectorNV; |
1874 | | |
1875 | 519 | switch (type.getBasicType()) { |
1876 | 0 | case EbtStruct: |
1877 | 0 | op = EOpConstructStruct; |
1878 | 0 | break; |
1879 | 0 | case EbtSampler: |
1880 | 0 | if (type.getSampler().isCombined()) |
1881 | 0 | op = EOpConstructTextureSampler; |
1882 | 0 | break; |
1883 | 0 | case EbtFloat: |
1884 | 0 | if (type.isMatrix()) { |
1885 | 0 | switch (type.getMatrixCols()) { |
1886 | 0 | case 2: |
1887 | 0 | switch (type.getMatrixRows()) { |
1888 | 0 | case 2: op = EOpConstructMat2x2; break; |
1889 | 0 | case 3: op = EOpConstructMat2x3; break; |
1890 | 0 | case 4: op = EOpConstructMat2x4; break; |
1891 | 0 | default: break; // some compilers want this |
1892 | 0 | } |
1893 | 0 | break; |
1894 | 0 | case 3: |
1895 | 0 | switch (type.getMatrixRows()) { |
1896 | 0 | case 2: op = EOpConstructMat3x2; break; |
1897 | 0 | case 3: op = EOpConstructMat3x3; break; |
1898 | 0 | case 4: op = EOpConstructMat3x4; break; |
1899 | 0 | default: break; // some compilers want this |
1900 | 0 | } |
1901 | 0 | break; |
1902 | 0 | case 4: |
1903 | 0 | switch (type.getMatrixRows()) { |
1904 | 0 | case 2: op = EOpConstructMat4x2; break; |
1905 | 0 | case 3: op = EOpConstructMat4x3; break; |
1906 | 0 | case 4: op = EOpConstructMat4x4; break; |
1907 | 0 | default: break; // some compilers want this |
1908 | 0 | } |
1909 | 0 | break; |
1910 | 0 | default: break; // some compilers want this |
1911 | 0 | } |
1912 | 0 | } else { |
1913 | 0 | switch(type.getVectorSize()) { |
1914 | 0 | case 1: op = EOpConstructFloat; break; |
1915 | 0 | case 2: op = EOpConstructVec2; break; |
1916 | 0 | case 3: op = EOpConstructVec3; break; |
1917 | 0 | case 4: op = EOpConstructVec4; break; |
1918 | 0 | default: break; // some compilers want this |
1919 | 0 | } |
1920 | 0 | } |
1921 | 0 | break; |
1922 | 394 | case EbtInt: |
1923 | 394 | if (type.getMatrixCols()) { |
1924 | 0 | switch (type.getMatrixCols()) { |
1925 | 0 | case 2: |
1926 | 0 | switch (type.getMatrixRows()) { |
1927 | 0 | case 2: op = EOpConstructIMat2x2; break; |
1928 | 0 | case 3: op = EOpConstructIMat2x3; break; |
1929 | 0 | case 4: op = EOpConstructIMat2x4; break; |
1930 | 0 | default: break; // some compilers want this |
1931 | 0 | } |
1932 | 0 | break; |
1933 | 0 | case 3: |
1934 | 0 | switch (type.getMatrixRows()) { |
1935 | 0 | case 2: op = EOpConstructIMat3x2; break; |
1936 | 0 | case 3: op = EOpConstructIMat3x3; break; |
1937 | 0 | case 4: op = EOpConstructIMat3x4; break; |
1938 | 0 | default: break; // some compilers want this |
1939 | 0 | } |
1940 | 0 | break; |
1941 | 0 | case 4: |
1942 | 0 | switch (type.getMatrixRows()) { |
1943 | 0 | case 2: op = EOpConstructIMat4x2; break; |
1944 | 0 | case 3: op = EOpConstructIMat4x3; break; |
1945 | 0 | case 4: op = EOpConstructIMat4x4; break; |
1946 | 0 | default: break; // some compilers want this |
1947 | 0 | } |
1948 | 0 | break; |
1949 | 0 | } |
1950 | 394 | } else { |
1951 | 394 | switch(type.getVectorSize()) { |
1952 | 330 | case 1: op = EOpConstructInt; break; |
1953 | 8 | case 2: op = EOpConstructIVec2; break; |
1954 | 56 | case 3: op = EOpConstructIVec3; break; |
1955 | 0 | case 4: op = EOpConstructIVec4; break; |
1956 | 0 | default: break; // some compilers want this |
1957 | 394 | } |
1958 | 394 | } |
1959 | 394 | break; |
1960 | 394 | case EbtUint: |
1961 | 120 | if (type.getMatrixCols()) { |
1962 | 0 | switch (type.getMatrixCols()) { |
1963 | 0 | case 2: |
1964 | 0 | switch (type.getMatrixRows()) { |
1965 | 0 | case 2: op = EOpConstructUMat2x2; break; |
1966 | 0 | case 3: op = EOpConstructUMat2x3; break; |
1967 | 0 | case 4: op = EOpConstructUMat2x4; break; |
1968 | 0 | default: break; // some compilers want this |
1969 | 0 | } |
1970 | 0 | break; |
1971 | 0 | case 3: |
1972 | 0 | switch (type.getMatrixRows()) { |
1973 | 0 | case 2: op = EOpConstructUMat3x2; break; |
1974 | 0 | case 3: op = EOpConstructUMat3x3; break; |
1975 | 0 | case 4: op = EOpConstructUMat3x4; break; |
1976 | 0 | default: break; // some compilers want this |
1977 | 0 | } |
1978 | 0 | break; |
1979 | 0 | case 4: |
1980 | 0 | switch (type.getMatrixRows()) { |
1981 | 0 | case 2: op = EOpConstructUMat4x2; break; |
1982 | 0 | case 3: op = EOpConstructUMat4x3; break; |
1983 | 0 | case 4: op = EOpConstructUMat4x4; break; |
1984 | 0 | default: break; // some compilers want this |
1985 | 0 | } |
1986 | 0 | break; |
1987 | 0 | } |
1988 | 120 | } else { |
1989 | 120 | switch(type.getVectorSize()) { |
1990 | 0 | case 1: op = EOpConstructUint; break; |
1991 | 12 | case 2: op = EOpConstructUVec2; break; |
1992 | 108 | case 3: op = EOpConstructUVec3; break; |
1993 | 0 | case 4: op = EOpConstructUVec4; break; |
1994 | 0 | default: break; // some compilers want this |
1995 | 120 | } |
1996 | 120 | } |
1997 | 120 | break; |
1998 | 120 | case EbtBool: |
1999 | 1 | if (type.getMatrixCols()) { |
2000 | 0 | switch (type.getMatrixCols()) { |
2001 | 0 | case 2: |
2002 | 0 | switch (type.getMatrixRows()) { |
2003 | 0 | case 2: op = EOpConstructBMat2x2; break; |
2004 | 0 | case 3: op = EOpConstructBMat2x3; break; |
2005 | 0 | case 4: op = EOpConstructBMat2x4; break; |
2006 | 0 | default: break; // some compilers want this |
2007 | 0 | } |
2008 | 0 | break; |
2009 | 0 | case 3: |
2010 | 0 | switch (type.getMatrixRows()) { |
2011 | 0 | case 2: op = EOpConstructBMat3x2; break; |
2012 | 0 | case 3: op = EOpConstructBMat3x3; break; |
2013 | 0 | case 4: op = EOpConstructBMat3x4; break; |
2014 | 0 | default: break; // some compilers want this |
2015 | 0 | } |
2016 | 0 | break; |
2017 | 0 | case 4: |
2018 | 0 | switch (type.getMatrixRows()) { |
2019 | 0 | case 2: op = EOpConstructBMat4x2; break; |
2020 | 0 | case 3: op = EOpConstructBMat4x3; break; |
2021 | 0 | case 4: op = EOpConstructBMat4x4; break; |
2022 | 0 | default: break; // some compilers want this |
2023 | 0 | } |
2024 | 0 | break; |
2025 | 0 | } |
2026 | 1 | } else { |
2027 | 1 | switch(type.getVectorSize()) { |
2028 | 1 | case 1: op = EOpConstructBool; break; |
2029 | 0 | case 2: op = EOpConstructBVec2; break; |
2030 | 0 | case 3: op = EOpConstructBVec3; break; |
2031 | 0 | case 4: op = EOpConstructBVec4; break; |
2032 | 0 | default: break; // some compilers want this |
2033 | 1 | } |
2034 | 1 | } |
2035 | 1 | break; |
2036 | 1 | case EbtDouble: |
2037 | 0 | if (type.getMatrixCols()) { |
2038 | 0 | switch (type.getMatrixCols()) { |
2039 | 0 | case 2: |
2040 | 0 | switch (type.getMatrixRows()) { |
2041 | 0 | case 2: op = EOpConstructDMat2x2; break; |
2042 | 0 | case 3: op = EOpConstructDMat2x3; break; |
2043 | 0 | case 4: op = EOpConstructDMat2x4; break; |
2044 | 0 | default: break; // some compilers want this |
2045 | 0 | } |
2046 | 0 | break; |
2047 | 0 | case 3: |
2048 | 0 | switch (type.getMatrixRows()) { |
2049 | 0 | case 2: op = EOpConstructDMat3x2; break; |
2050 | 0 | case 3: op = EOpConstructDMat3x3; break; |
2051 | 0 | case 4: op = EOpConstructDMat3x4; break; |
2052 | 0 | default: break; // some compilers want this |
2053 | 0 | } |
2054 | 0 | break; |
2055 | 0 | case 4: |
2056 | 0 | switch (type.getMatrixRows()) { |
2057 | 0 | case 2: op = EOpConstructDMat4x2; break; |
2058 | 0 | case 3: op = EOpConstructDMat4x3; break; |
2059 | 0 | case 4: op = EOpConstructDMat4x4; break; |
2060 | 0 | default: break; // some compilers want this |
2061 | 0 | } |
2062 | 0 | break; |
2063 | 0 | } |
2064 | 0 | } else { |
2065 | 0 | switch(type.getVectorSize()) { |
2066 | 0 | case 1: op = EOpConstructDouble; break; |
2067 | 0 | case 2: op = EOpConstructDVec2; break; |
2068 | 0 | case 3: op = EOpConstructDVec3; break; |
2069 | 0 | case 4: op = EOpConstructDVec4; break; |
2070 | 0 | default: break; // some compilers want this |
2071 | 0 | } |
2072 | 0 | } |
2073 | 0 | break; |
2074 | 0 | case EbtFloat16: |
2075 | 0 | if (type.getMatrixCols()) { |
2076 | 0 | switch (type.getMatrixCols()) { |
2077 | 0 | case 2: |
2078 | 0 | switch (type.getMatrixRows()) { |
2079 | 0 | case 2: op = EOpConstructF16Mat2x2; break; |
2080 | 0 | case 3: op = EOpConstructF16Mat2x3; break; |
2081 | 0 | case 4: op = EOpConstructF16Mat2x4; break; |
2082 | 0 | default: break; // some compilers want this |
2083 | 0 | } |
2084 | 0 | break; |
2085 | 0 | case 3: |
2086 | 0 | switch (type.getMatrixRows()) { |
2087 | 0 | case 2: op = EOpConstructF16Mat3x2; break; |
2088 | 0 | case 3: op = EOpConstructF16Mat3x3; break; |
2089 | 0 | case 4: op = EOpConstructF16Mat3x4; break; |
2090 | 0 | default: break; // some compilers want this |
2091 | 0 | } |
2092 | 0 | break; |
2093 | 0 | case 4: |
2094 | 0 | switch (type.getMatrixRows()) { |
2095 | 0 | case 2: op = EOpConstructF16Mat4x2; break; |
2096 | 0 | case 3: op = EOpConstructF16Mat4x3; break; |
2097 | 0 | case 4: op = EOpConstructF16Mat4x4; break; |
2098 | 0 | default: break; // some compilers want this |
2099 | 0 | } |
2100 | 0 | break; |
2101 | 0 | } |
2102 | 0 | } |
2103 | 0 | else { |
2104 | 0 | switch (type.getVectorSize()) { |
2105 | 0 | case 1: op = EOpConstructFloat16; break; |
2106 | 0 | case 2: op = EOpConstructF16Vec2; break; |
2107 | 0 | case 3: op = EOpConstructF16Vec3; break; |
2108 | 0 | case 4: op = EOpConstructF16Vec4; break; |
2109 | 0 | default: break; // some compilers want this |
2110 | 0 | } |
2111 | 0 | } |
2112 | 0 | break; |
2113 | 0 | case EbtBFloat16: |
2114 | 0 | switch (type.getVectorSize()) { |
2115 | 0 | case 1: op = EOpConstructBFloat16; break; |
2116 | 0 | case 2: op = EOpConstructBF16Vec2; break; |
2117 | 0 | case 3: op = EOpConstructBF16Vec3; break; |
2118 | 0 | case 4: op = EOpConstructBF16Vec4; break; |
2119 | 0 | default: break; // some compilers want this |
2120 | 0 | } |
2121 | 0 | break; |
2122 | 0 | case EbtFloatE5M2: |
2123 | 0 | switch (type.getVectorSize()) { |
2124 | 0 | case 1: op = EOpConstructFloatE5M2; break; |
2125 | 0 | case 2: op = EOpConstructFloatE5M2Vec2; break; |
2126 | 0 | case 3: op = EOpConstructFloatE5M2Vec3; break; |
2127 | 0 | case 4: op = EOpConstructFloatE5M2Vec4; break; |
2128 | 0 | default: break; // some compilers want this |
2129 | 0 | } |
2130 | 0 | break; |
2131 | 0 | case EbtFloatE4M3: |
2132 | 0 | switch (type.getVectorSize()) { |
2133 | 0 | case 1: op = EOpConstructFloatE4M3; break; |
2134 | 0 | case 2: op = EOpConstructFloatE4M3Vec2; break; |
2135 | 0 | case 3: op = EOpConstructFloatE4M3Vec3; break; |
2136 | 0 | case 4: op = EOpConstructFloatE4M3Vec4; break; |
2137 | 0 | default: break; // some compilers want this |
2138 | 0 | } |
2139 | 0 | break; |
2140 | 0 | case EbtInt8: |
2141 | 0 | switch(type.getVectorSize()) { |
2142 | 0 | case 1: op = EOpConstructInt8; break; |
2143 | 0 | case 2: op = EOpConstructI8Vec2; break; |
2144 | 0 | case 3: op = EOpConstructI8Vec3; break; |
2145 | 0 | case 4: op = EOpConstructI8Vec4; break; |
2146 | 0 | default: break; // some compilers want this |
2147 | 0 | } |
2148 | 0 | break; |
2149 | 0 | case EbtUint8: |
2150 | 0 | switch(type.getVectorSize()) { |
2151 | 0 | case 1: op = EOpConstructUint8; break; |
2152 | 0 | case 2: op = EOpConstructU8Vec2; break; |
2153 | 0 | case 3: op = EOpConstructU8Vec3; break; |
2154 | 0 | case 4: op = EOpConstructU8Vec4; break; |
2155 | 0 | default: break; // some compilers want this |
2156 | 0 | } |
2157 | 0 | break; |
2158 | 0 | case EbtInt16: |
2159 | 0 | switch(type.getVectorSize()) { |
2160 | 0 | case 1: op = EOpConstructInt16; break; |
2161 | 0 | case 2: op = EOpConstructI16Vec2; break; |
2162 | 0 | case 3: op = EOpConstructI16Vec3; break; |
2163 | 0 | case 4: op = EOpConstructI16Vec4; break; |
2164 | 0 | default: break; // some compilers want this |
2165 | 0 | } |
2166 | 0 | break; |
2167 | 0 | case EbtUint16: |
2168 | 0 | switch(type.getVectorSize()) { |
2169 | 0 | case 1: op = EOpConstructUint16; break; |
2170 | 0 | case 2: op = EOpConstructU16Vec2; break; |
2171 | 0 | case 3: op = EOpConstructU16Vec3; break; |
2172 | 0 | case 4: op = EOpConstructU16Vec4; break; |
2173 | 0 | default: break; // some compilers want this |
2174 | 0 | } |
2175 | 0 | break; |
2176 | 0 | case EbtInt64: |
2177 | 0 | switch(type.getVectorSize()) { |
2178 | 0 | case 1: op = EOpConstructInt64; break; |
2179 | 0 | case 2: op = EOpConstructI64Vec2; break; |
2180 | 0 | case 3: op = EOpConstructI64Vec3; break; |
2181 | 0 | case 4: op = EOpConstructI64Vec4; break; |
2182 | 0 | default: break; // some compilers want this |
2183 | 0 | } |
2184 | 0 | break; |
2185 | 4 | case EbtUint64: |
2186 | 4 | switch(type.getVectorSize()) { |
2187 | 4 | case 1: op = EOpConstructUint64; break; |
2188 | 0 | case 2: op = EOpConstructU64Vec2; break; |
2189 | 0 | case 3: op = EOpConstructU64Vec3; break; |
2190 | 0 | case 4: op = EOpConstructU64Vec4; break; |
2191 | 0 | default: break; // some compilers want this |
2192 | 4 | } |
2193 | 4 | break; |
2194 | 4 | case EbtReference: |
2195 | 0 | op = EOpConstructReference; |
2196 | 0 | break; |
2197 | | |
2198 | 0 | case EbtAccStruct: |
2199 | 0 | op = EOpConstructAccStruct; |
2200 | 0 | break; |
2201 | 0 | default: |
2202 | 0 | break; |
2203 | 519 | } |
2204 | | |
2205 | 519 | return op; |
2206 | 519 | } |
2207 | | |
2208 | | // |
2209 | | // Safe way to combine two nodes into an aggregate. Works with null pointers, |
2210 | | // a node that's not a aggregate yet, etc. |
2211 | | // |
2212 | | // Returns the resulting aggregate, unless nullptr was passed in for |
2213 | | // both existing nodes. |
2214 | | // |
2215 | | TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right) |
2216 | 10.6k | { |
2217 | 10.6k | if (left == nullptr && right == nullptr) |
2218 | 10.3k | return nullptr; |
2219 | | |
2220 | 332 | TIntermAggregate* aggNode = nullptr; |
2221 | 332 | if (left != nullptr) |
2222 | 199 | aggNode = left->getAsAggregate(); |
2223 | 332 | if (aggNode == nullptr || aggNode->getOp() != EOpNull) { |
2224 | 220 | aggNode = new TIntermAggregate; |
2225 | 220 | if (left != nullptr) |
2226 | 87 | aggNode->getSequence().push_back(left); |
2227 | 220 | } |
2228 | | |
2229 | 332 | if (right != nullptr) |
2230 | 330 | aggNode->getSequence().push_back(right); |
2231 | | |
2232 | 332 | return aggNode; |
2233 | 10.6k | } |
2234 | | |
2235 | | TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc) |
2236 | 10.4k | { |
2237 | 10.4k | TIntermAggregate* aggNode = growAggregate(left, right); |
2238 | 10.4k | if (aggNode) |
2239 | 174 | aggNode->setLoc(loc); |
2240 | | |
2241 | 10.4k | return aggNode; |
2242 | 10.4k | } |
2243 | | |
2244 | | TIntermAggregate* TIntermediate::mergeAggregate(TIntermNode* left, TIntermNode* right) |
2245 | 0 | { |
2246 | 0 | if (left == nullptr && right == nullptr) |
2247 | 0 | return nullptr; |
2248 | | |
2249 | 0 | TIntermAggregate* aggNode = nullptr; |
2250 | 0 | if (left != nullptr) |
2251 | 0 | aggNode = left->getAsAggregate(); |
2252 | 0 | if (aggNode == nullptr || aggNode->getOp() != EOpNull) { |
2253 | 0 | aggNode = new TIntermAggregate; |
2254 | 0 | if (left != nullptr) |
2255 | 0 | aggNode->getSequence().push_back(left); |
2256 | 0 | } |
2257 | |
|
2258 | 0 | TIntermAggregate* rhsagg = right->getAsAggregate(); |
2259 | 0 | if (rhsagg == nullptr || rhsagg->getOp() != EOpNull) |
2260 | 0 | aggNode->getSequence().push_back(right); |
2261 | 0 | else |
2262 | 0 | aggNode->getSequence().insert(aggNode->getSequence().end(), |
2263 | 0 | rhsagg->getSequence().begin(), |
2264 | 0 | rhsagg->getSequence().end()); |
2265 | |
|
2266 | 0 | return aggNode; |
2267 | 0 | } |
2268 | | |
2269 | | TIntermAggregate* TIntermediate::mergeAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc) |
2270 | 0 | { |
2271 | 0 | TIntermAggregate* aggNode = mergeAggregate(left, right); |
2272 | 0 | if (aggNode) |
2273 | 0 | aggNode->setLoc(loc); |
2274 | |
|
2275 | 0 | return aggNode; |
2276 | 0 | } |
2277 | | |
2278 | | // |
2279 | | // Turn an existing node into an aggregate. |
2280 | | // |
2281 | | // Returns an aggregate, unless nullptr was passed in for the existing node. |
2282 | | // |
2283 | | TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node) |
2284 | 16 | { |
2285 | 16 | if (node == nullptr) |
2286 | 0 | return nullptr; |
2287 | | |
2288 | 16 | TIntermAggregate* aggNode = new TIntermAggregate; |
2289 | 16 | aggNode->getSequence().push_back(node); |
2290 | 16 | aggNode->setLoc(node->getLoc()); |
2291 | | |
2292 | 16 | return aggNode; |
2293 | 16 | } |
2294 | | |
2295 | | TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, const TSourceLoc& loc) |
2296 | 0 | { |
2297 | 0 | if (node == nullptr) |
2298 | 0 | return nullptr; |
2299 | | |
2300 | 0 | TIntermAggregate* aggNode = new TIntermAggregate; |
2301 | 0 | aggNode->getSequence().push_back(node); |
2302 | 0 | aggNode->setLoc(loc); |
2303 | |
|
2304 | 0 | return aggNode; |
2305 | 0 | } |
2306 | | |
2307 | | // |
2308 | | // Make an aggregate with an empty sequence. |
2309 | | // |
2310 | | TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc) |
2311 | 4 | { |
2312 | 4 | TIntermAggregate* aggNode = new TIntermAggregate; |
2313 | 4 | aggNode->setLoc(loc); |
2314 | | |
2315 | 4 | return aggNode; |
2316 | 4 | } |
2317 | | |
2318 | | // |
2319 | | // For "if" test nodes. There are three children; a condition, |
2320 | | // a true path, and a false path. The two paths are in the |
2321 | | // nodePair. |
2322 | | // |
2323 | | // Returns the selection node created. |
2324 | | // |
2325 | | TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc) |
2326 | 0 | { |
2327 | | // |
2328 | | // Don't prune the false path for compile-time constants; it's needed |
2329 | | // for static access analysis. |
2330 | | // |
2331 | |
|
2332 | 0 | TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2); |
2333 | 0 | node->setLoc(loc); |
2334 | |
|
2335 | 0 | return node; |
2336 | 0 | } |
2337 | | |
2338 | | TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) |
2339 | 5 | { |
2340 | | // However, the lowest precedence operators of the sequence operator ( , ) and the assignment operators |
2341 | | // ... are not included in the operators that can create a constant expression. |
2342 | | // |
2343 | | // if (left->getType().getQualifier().storage == EvqConst && |
2344 | | // right->getType().getQualifier().storage == EvqConst) { |
2345 | | |
2346 | | // return right; |
2347 | | //} |
2348 | | |
2349 | 5 | TIntermTyped *commaAggregate = growAggregate(left, right, loc); |
2350 | 5 | commaAggregate->getAsAggregate()->setOperator(EOpComma); |
2351 | 5 | commaAggregate->setType(right->getType()); |
2352 | 5 | commaAggregate->getWritableType().getQualifier().makeTemporary(); |
2353 | | |
2354 | 5 | return commaAggregate; |
2355 | 5 | } |
2356 | | |
2357 | | TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type, const TString* name, const TSourceLoc& loc) |
2358 | 0 | { |
2359 | 0 | TIntermMethod* method = new TIntermMethod(object, type, *name); |
2360 | 0 | method->setLoc(loc); |
2361 | |
|
2362 | 0 | return method; |
2363 | 0 | } |
2364 | | |
2365 | | // |
2366 | | // For "?:" test nodes. There are three children; a condition, |
2367 | | // a true path, and a false path. The two paths are specified |
2368 | | // as separate parameters. For vector 'cond', the true and false |
2369 | | // are not paths, but vectors to mix. |
2370 | | // |
2371 | | // Specialization constant operations include |
2372 | | // - The ternary operator ( ? : ) |
2373 | | // |
2374 | | // Returns the selection node created, or nullptr if one could not be. |
2375 | | // |
2376 | | TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, |
2377 | | const TSourceLoc& loc) |
2378 | 16 | { |
2379 | | // If it's void, go to the if-then-else selection() |
2380 | 16 | if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) { |
2381 | 0 | TIntermNodePair pair = { trueBlock, falseBlock }; |
2382 | 0 | TIntermSelection* selection = addSelection(cond, pair, loc); |
2383 | 0 | if (getSource() == EShSourceHlsl) |
2384 | 0 | selection->setNoShortCircuit(); |
2385 | |
|
2386 | 0 | return selection; |
2387 | 0 | } |
2388 | | |
2389 | | // |
2390 | | // Get compatible types. |
2391 | | // |
2392 | 16 | auto children = addPairConversion(EOpSequence, trueBlock, falseBlock); |
2393 | 16 | trueBlock = std::get<0>(children); |
2394 | 16 | falseBlock = std::get<1>(children); |
2395 | | |
2396 | 16 | if (trueBlock == nullptr || falseBlock == nullptr || |
2397 | 16 | trueBlock->getBasicType() == EbtString || falseBlock->getBasicType() == EbtString) |
2398 | 0 | return nullptr; |
2399 | | |
2400 | | // Handle a vector condition as a mix |
2401 | 16 | if (!cond->getType().isScalarOrVec1()) { |
2402 | 4 | TType targetVectorType(trueBlock->getType().getBasicType(), EvqTemporary, |
2403 | 4 | cond->getType().getVectorSize()); |
2404 | | // smear true/false operands as needed |
2405 | 4 | trueBlock = addUniShapeConversion(EOpMix, targetVectorType, trueBlock); |
2406 | 4 | falseBlock = addUniShapeConversion(EOpMix, targetVectorType, falseBlock); |
2407 | | |
2408 | | // After conversion, types have to match. |
2409 | 4 | if (falseBlock->getType() != trueBlock->getType()) |
2410 | 0 | return nullptr; |
2411 | | |
2412 | | // make the mix operation |
2413 | 4 | TIntermAggregate* mix = makeAggregate(loc); |
2414 | 4 | mix = growAggregate(mix, falseBlock); |
2415 | 4 | mix = growAggregate(mix, trueBlock); |
2416 | 4 | mix = growAggregate(mix, cond); |
2417 | 4 | mix->setType(targetVectorType); |
2418 | 4 | mix->setOp(EOpMix); |
2419 | | |
2420 | 4 | return mix; |
2421 | 4 | } |
2422 | | |
2423 | | // Now have a scalar condition... |
2424 | | |
2425 | | // Convert true and false expressions to matching types |
2426 | 12 | addBiShapeConversion(EOpMix, trueBlock, falseBlock); |
2427 | | |
2428 | | // After conversion, types have to match. |
2429 | 12 | if (falseBlock->getType() != trueBlock->getType()) |
2430 | 0 | return nullptr; |
2431 | | |
2432 | | // Eliminate the selection when the condition is a scalar and all operands are constant. |
2433 | 12 | if (cond->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion()) { |
2434 | 4 | if (cond->getAsConstantUnion()->getConstArray()[0].getBConst()) |
2435 | 4 | return trueBlock; |
2436 | 0 | else |
2437 | 0 | return falseBlock; |
2438 | 4 | } |
2439 | | |
2440 | | // |
2441 | | // Make a selection node. |
2442 | | // |
2443 | 8 | TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); |
2444 | 8 | node->setLoc(loc); |
2445 | 8 | node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision); |
2446 | | |
2447 | 8 | if ((cond->getQualifier().isConstant() && specConstantPropagates(*trueBlock, *falseBlock)) || |
2448 | 8 | (cond->getQualifier().isSpecConstant() && trueBlock->getQualifier().isConstant() && |
2449 | 0 | falseBlock->getQualifier().isConstant())) |
2450 | 0 | node->getQualifier().makeSpecConstant(); |
2451 | 8 | else |
2452 | 8 | node->getQualifier().makeTemporary(); |
2453 | | |
2454 | 8 | if (getSource() == EShSourceHlsl) |
2455 | 8 | node->setNoShortCircuit(); |
2456 | | |
2457 | 8 | return node; |
2458 | 12 | } |
2459 | | |
2460 | | // |
2461 | | // Constant terminal nodes. Has a union that contains bool, float or int constants |
2462 | | // |
2463 | | // Returns the constant union node created. |
2464 | | // |
2465 | | |
2466 | | TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, const TSourceLoc& loc, bool literal) const |
2467 | 6.12k | { |
2468 | 6.12k | TIntermConstantUnion* node = new TIntermConstantUnion(unionArray, t); |
2469 | 6.12k | node->getQualifier().storage = EvqConst; |
2470 | 6.12k | node->setLoc(loc); |
2471 | 6.12k | if (literal) |
2472 | 5.81k | node->setLiteral(); |
2473 | | |
2474 | 6.12k | return node; |
2475 | 6.12k | } |
2476 | | TIntermConstantUnion* TIntermediate::addConstantUnion(signed char i8, const TSourceLoc& loc, bool literal) const |
2477 | 0 | { |
2478 | 0 | TConstUnionArray unionArray(1); |
2479 | 0 | unionArray[0].setI8Const(i8); |
2480 | |
|
2481 | 0 | return addConstantUnion(unionArray, TType(EbtInt8, EvqConst), loc, literal); |
2482 | 0 | } |
2483 | | |
2484 | | TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned char u8, const TSourceLoc& loc, bool literal) const |
2485 | 0 | { |
2486 | 0 | TConstUnionArray unionArray(1); |
2487 | 0 | unionArray[0].setUConst(u8); |
2488 | |
|
2489 | 0 | return addConstantUnion(unionArray, TType(EbtUint8, EvqConst), loc, literal); |
2490 | 0 | } |
2491 | | |
2492 | | TIntermConstantUnion* TIntermediate::addConstantUnion(signed short i16, const TSourceLoc& loc, bool literal) const |
2493 | 0 | { |
2494 | 0 | TConstUnionArray unionArray(1); |
2495 | 0 | unionArray[0].setI16Const(i16); |
2496 | |
|
2497 | 0 | return addConstantUnion(unionArray, TType(EbtInt16, EvqConst), loc, literal); |
2498 | 0 | } |
2499 | | |
2500 | | TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned short u16, const TSourceLoc& loc, bool literal) const |
2501 | 0 | { |
2502 | 0 | TConstUnionArray unionArray(1); |
2503 | 0 | unionArray[0].setU16Const(u16); |
2504 | |
|
2505 | 0 | return addConstantUnion(unionArray, TType(EbtUint16, EvqConst), loc, literal); |
2506 | 0 | } |
2507 | | |
2508 | | TIntermConstantUnion* TIntermediate::addConstantUnion(int i, const TSourceLoc& loc, bool literal) const |
2509 | 5.02k | { |
2510 | 5.02k | TConstUnionArray unionArray(1); |
2511 | 5.02k | unionArray[0].setIConst(i); |
2512 | | |
2513 | 5.02k | return addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc, literal); |
2514 | 5.02k | } |
2515 | | |
2516 | | TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned int u, const TSourceLoc& loc, bool literal) const |
2517 | 787 | { |
2518 | 787 | TConstUnionArray unionArray(1); |
2519 | 787 | unionArray[0].setUConst(u); |
2520 | | |
2521 | 787 | return addConstantUnion(unionArray, TType(EbtUint, EvqConst), loc, literal); |
2522 | 787 | } |
2523 | | |
2524 | | TIntermConstantUnion* TIntermediate::addConstantUnion(long long i64, const TSourceLoc& loc, bool literal) const |
2525 | 0 | { |
2526 | 0 | TConstUnionArray unionArray(1); |
2527 | 0 | unionArray[0].setI64Const(i64); |
2528 | |
|
2529 | 0 | return addConstantUnion(unionArray, TType(EbtInt64, EvqConst), loc, literal); |
2530 | 0 | } |
2531 | | |
2532 | | TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned long long u64, const TSourceLoc& loc, bool literal) const |
2533 | 0 | { |
2534 | 0 | TConstUnionArray unionArray(1); |
2535 | 0 | unionArray[0].setU64Const(u64); |
2536 | |
|
2537 | 0 | return addConstantUnion(unionArray, TType(EbtUint64, EvqConst), loc, literal); |
2538 | 0 | } |
2539 | | |
2540 | | TIntermConstantUnion* TIntermediate::addConstantUnion(bool b, const TSourceLoc& loc, bool literal) const |
2541 | 0 | { |
2542 | 0 | TConstUnionArray unionArray(1); |
2543 | 0 | unionArray[0].setBConst(b); |
2544 | |
|
2545 | 0 | return addConstantUnion(unionArray, TType(EbtBool, EvqConst), loc, literal); |
2546 | 0 | } |
2547 | | |
2548 | | TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseType, const TSourceLoc& loc, bool literal) const |
2549 | 3 | { |
2550 | 3 | assert(baseType == EbtFloat || baseType == EbtDouble || baseType == EbtFloat16 || baseType == EbtBFloat16 || baseType == EbtFloatE5M2 || baseType == EbtFloatE4M3); |
2551 | | |
2552 | 3 | if (isEsProfile() && (baseType == EbtFloat || baseType == EbtFloat16)) { |
2553 | 0 | int exponent = 0; |
2554 | 0 | frexp(d, &exponent); |
2555 | 0 | int minExp = baseType == EbtFloat ? -126 : -14; |
2556 | 0 | int maxExp = baseType == EbtFloat ? 127 : 15; |
2557 | 0 | if (exponent > maxExp) { //overflow, d = inf |
2558 | 0 | d = std::numeric_limits<double>::infinity(); |
2559 | 0 | } else if (exponent < minExp) { //underflow, d = 0.0; |
2560 | 0 | d = 0.0; |
2561 | 0 | } |
2562 | 0 | } |
2563 | | |
2564 | 3 | TConstUnionArray unionArray(1); |
2565 | 3 | unionArray[0].setDConst(d); |
2566 | | |
2567 | 3 | return addConstantUnion(unionArray, TType(baseType, EvqConst), loc, literal); |
2568 | 3 | } |
2569 | | |
2570 | | TIntermConstantUnion* TIntermediate::addConstantUnion(const TString* s, const TSourceLoc& loc, bool literal) const |
2571 | 0 | { |
2572 | 0 | TConstUnionArray unionArray(1); |
2573 | 0 | unionArray[0].setSConst(s); |
2574 | |
|
2575 | 0 | return addConstantUnion(unionArray, TType(EbtString, EvqConst), loc, literal); |
2576 | 0 | } |
2577 | | |
2578 | | // Put vector swizzle selectors onto the given sequence |
2579 | | void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc) |
2580 | 0 | { |
2581 | 0 | TIntermConstantUnion* constIntNode = addConstantUnion(selector, loc); |
2582 | 0 | sequence.push_back(constIntNode); |
2583 | 0 | } |
2584 | | |
2585 | | // Put matrix swizzle selectors onto the given sequence |
2586 | | void TIntermediate::pushSelector(TIntermSequence& sequence, const TMatrixSelector& selector, const TSourceLoc& loc) |
2587 | 0 | { |
2588 | 0 | TIntermConstantUnion* constIntNode = addConstantUnion(selector.coord1, loc); |
2589 | 0 | sequence.push_back(constIntNode); |
2590 | 0 | constIntNode = addConstantUnion(selector.coord2, loc); |
2591 | 0 | sequence.push_back(constIntNode); |
2592 | 0 | } |
2593 | | |
2594 | | // Make an aggregate node that has a sequence of all selectors. |
2595 | | template TIntermTyped* TIntermediate::addSwizzle<TVectorSelector>(TSwizzleSelectors<TVectorSelector>& selector, const TSourceLoc& loc); |
2596 | | template TIntermTyped* TIntermediate::addSwizzle<TMatrixSelector>(TSwizzleSelectors<TMatrixSelector>& selector, const TSourceLoc& loc); |
2597 | | template<typename selectorType> |
2598 | | TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors<selectorType>& selector, const TSourceLoc& loc) |
2599 | 0 | { |
2600 | 0 | TIntermAggregate* node = new TIntermAggregate(EOpSequence); |
2601 | |
|
2602 | 0 | node->setLoc(loc); |
2603 | 0 | TIntermSequence &sequenceVector = node->getSequence(); |
2604 | |
|
2605 | 0 | for (int i = 0; i < selector.size(); i++) |
2606 | 0 | pushSelector(sequenceVector, selector[i], loc); |
2607 | |
|
2608 | 0 | return node; |
2609 | 0 | } Unexecuted instantiation: glslang::TIntermTyped* glslang::TIntermediate::addSwizzle<int>(glslang::TSwizzleSelectors<int>&, glslang::TSourceLoc const&) Unexecuted instantiation: glslang::TIntermTyped* glslang::TIntermediate::addSwizzle<glslang::TMatrixSelector>(glslang::TSwizzleSelectors<glslang::TMatrixSelector>&, glslang::TSourceLoc const&) |
2610 | | |
2611 | | // |
2612 | | // Follow the left branches down to the root of an l-value |
2613 | | // expression (just "." and []). |
2614 | | // |
2615 | | // Return the base of the l-value (where following indexing quits working). |
2616 | | // Return nullptr if a chain following dereferences cannot be followed. |
2617 | | // |
2618 | | // 'swizzleOkay' says whether or not it is okay to consider a swizzle |
2619 | | // a valid part of the dereference chain. |
2620 | | // |
2621 | | // 'bufferReferenceOk' says if type is buffer_reference, the routine will stop to find the most left node. |
2622 | | // |
2623 | | // 'proc' is an optional function to run on each node that is processed during the traversal. 'proc' must |
2624 | | // return true to continue the traversal, or false to end the traversal early. |
2625 | | // |
2626 | | |
2627 | | const TIntermTyped* TIntermediate::traverseLValueBase(const TIntermTyped* node, bool swizzleOkay, |
2628 | | bool bufferReferenceOk, |
2629 | | std::function<bool(const TIntermNode&)> proc) |
2630 | 0 | { |
2631 | 0 | do { |
2632 | 0 | const TIntermBinary* binary = node->getAsBinaryNode(); |
2633 | 0 | if (binary == nullptr) { |
2634 | 0 | if (proc) { |
2635 | 0 | proc(*node); |
2636 | 0 | } |
2637 | 0 | return node; |
2638 | 0 | } |
2639 | 0 | TOperator op = binary->getOp(); |
2640 | 0 | if (op != EOpIndexDirect && op != EOpIndexIndirect && op != EOpIndexDirectStruct && op != EOpVectorSwizzle && |
2641 | 0 | op != EOpMatrixSwizzle) |
2642 | 0 | return nullptr; |
2643 | 0 | if (!swizzleOkay) { |
2644 | 0 | if (op == EOpVectorSwizzle || op == EOpMatrixSwizzle) |
2645 | 0 | return nullptr; |
2646 | 0 | if ((op == EOpIndexDirect || op == EOpIndexIndirect) && |
2647 | 0 | (binary->getLeft()->getType().isVector() || binary->getLeft()->getType().isScalar()) && |
2648 | 0 | !binary->getLeft()->getType().isArray()) |
2649 | 0 | return nullptr; |
2650 | 0 | } |
2651 | 0 | if (proc) { |
2652 | 0 | if (!proc(*node)) { |
2653 | 0 | return node; |
2654 | 0 | } |
2655 | 0 | } |
2656 | 0 | node = binary->getLeft(); |
2657 | 0 | if (bufferReferenceOk && node->isReference()) |
2658 | 0 | return node; |
2659 | 0 | } while (true); |
2660 | 0 | } |
2661 | | |
2662 | | // |
2663 | | // Create while and do-while loop nodes. |
2664 | | // |
2665 | | TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermNode* test, TIntermTyped* terminal, bool testFirst, |
2666 | | const TSourceLoc& loc) |
2667 | 0 | { |
2668 | 0 | TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst); |
2669 | 0 | node->setLoc(loc); |
2670 | |
|
2671 | 0 | return node; |
2672 | 0 | } |
2673 | | |
2674 | | // |
2675 | | // Create a for-loop sequence. |
2676 | | // |
2677 | | TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermNode* test, |
2678 | | TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node) |
2679 | 0 | { |
2680 | 0 | node = new TIntermLoop(body, test, terminal, testFirst); |
2681 | 0 | node->setLoc(loc); |
2682 | | |
2683 | | // make a sequence of the initializer and statement, but try to reuse the |
2684 | | // aggregate already created for whatever is in the initializer, if there is one |
2685 | 0 | TIntermAggregate* loopSequence = (initializer == nullptr || |
2686 | 0 | initializer->getAsAggregate() == nullptr) ? makeAggregate(initializer, loc) |
2687 | 0 | : initializer->getAsAggregate(); |
2688 | 0 | if (loopSequence != nullptr && (loopSequence->getOp() == EOpSequence || loopSequence->getOp() == EOpScope)) |
2689 | 0 | loopSequence->setOp(EOpNull); |
2690 | 0 | loopSequence = growAggregate(loopSequence, node); |
2691 | 0 | loopSequence->setOperator(getDebugInfo() ? EOpScope : EOpSequence); |
2692 | |
|
2693 | 0 | return loopSequence; |
2694 | 0 | } |
2695 | | |
2696 | | // |
2697 | | // Add branches. |
2698 | | // |
2699 | | TIntermBranch* TIntermediate::addBranch(TOperator branchOp, const TSourceLoc& loc) |
2700 | 0 | { |
2701 | 0 | return addBranch(branchOp, nullptr, loc); |
2702 | 0 | } |
2703 | | |
2704 | | TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TIntermTyped* expression, const TSourceLoc& loc) |
2705 | 0 | { |
2706 | 0 | TIntermBranch* node = new TIntermBranch(branchOp, expression); |
2707 | 0 | node->setLoc(loc); |
2708 | |
|
2709 | 0 | return node; |
2710 | 0 | } |
2711 | | |
2712 | | // Propagate precision from formal function return type to actual return type, |
2713 | | // and on to its subtree. |
2714 | | void TIntermBranch::updatePrecision(TPrecisionQualifier parentPrecision) |
2715 | 0 | { |
2716 | 0 | TIntermTyped* exp = getExpression(); |
2717 | 0 | if (exp == nullptr) |
2718 | 0 | return; |
2719 | | |
2720 | 0 | if (exp->getBasicType() == EbtInt || exp->getBasicType() == EbtUint || |
2721 | 0 | exp->getBasicType() == EbtFloat) { |
2722 | 0 | if (parentPrecision != EpqNone && exp->getQualifier().precision == EpqNone) { |
2723 | 0 | exp->propagatePrecision(parentPrecision); |
2724 | 0 | } |
2725 | 0 | } |
2726 | 0 | } |
2727 | | |
2728 | | // |
2729 | | // This is to be executed after the final root is put on top by the parsing |
2730 | | // process. |
2731 | | // |
2732 | | bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/) |
2733 | 1 | { |
2734 | 1 | if (root == nullptr) |
2735 | 0 | return true; |
2736 | | |
2737 | | // Finish off the top-level sequence |
2738 | 1 | TIntermAggregate* aggRoot = root->getAsAggregate(); |
2739 | 1 | if (aggRoot && aggRoot->getOp() == EOpNull) |
2740 | 1 | aggRoot->setOperator(EOpSequence); |
2741 | | |
2742 | | // Propagate 'noContraction' label in backward from 'precise' variables. |
2743 | 1 | glslang::PropagateNoContraction(*this); |
2744 | | |
2745 | 1 | switch (textureSamplerTransformMode) { |
2746 | 1 | case EShTexSampTransKeep: |
2747 | 1 | break; |
2748 | 0 | case EShTexSampTransUpgradeTextureRemoveSampler: |
2749 | 0 | performTextureUpgradeAndSamplerRemovalTransformation(root); |
2750 | 0 | break; |
2751 | 0 | case EShTexSampTransCount: |
2752 | 0 | assert(0); |
2753 | 0 | break; |
2754 | 1 | } |
2755 | | |
2756 | 1 | return true; |
2757 | 1 | } |
2758 | | |
2759 | | void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable) |
2760 | 131 | { |
2761 | | // Add top-level nodes for declarations that must be checked cross |
2762 | | // compilation unit by a linker, yet might not have been referenced |
2763 | | // by the AST. |
2764 | | // |
2765 | | // Almost entirely, translation of symbols is driven by what's present |
2766 | | // in the AST traversal, not by translating the symbol table. |
2767 | | // |
2768 | | // However, there are some special cases: |
2769 | | // - From the specification: "Special built-in inputs gl_VertexID and |
2770 | | // gl_InstanceID are also considered active vertex attributes." |
2771 | | // - Linker-based type mismatch error reporting needs to see all |
2772 | | // uniforms/ins/outs variables and blocks. |
2773 | | // - ftransform() can make gl_Vertex and gl_ModelViewProjectionMatrix active. |
2774 | | // |
2775 | | |
2776 | | // if (ftransformUsed) { |
2777 | | // TODO: 1.1 lowering functionality: track ftransform() usage |
2778 | | // addSymbolLinkageNode(root, symbolTable, "gl_Vertex"); |
2779 | | // addSymbolLinkageNode(root, symbolTable, "gl_ModelViewProjectionMatrix"); |
2780 | | //} |
2781 | | |
2782 | 131 | if (language == EShLangVertex) { |
2783 | 131 | addSymbolLinkageNode(linkage, symbolTable, "gl_VertexID"); |
2784 | 131 | if ((version < 140 && requestedExtensions.find(E_GL_EXT_draw_instanced) != requestedExtensions.end()) || version >= 140) |
2785 | 102 | addSymbolLinkageNode(linkage, symbolTable, "gl_InstanceID"); |
2786 | 131 | } |
2787 | | |
2788 | | // Add a child to the root node for the linker objects |
2789 | 131 | linkage->setOperator(EOpLinkerObjects); |
2790 | 131 | treeRoot = growAggregate(treeRoot, linkage); |
2791 | 131 | } |
2792 | | |
2793 | | // |
2794 | | // Add the given name or symbol to the list of nodes at the end of the tree used |
2795 | | // for link-time checking and external linkage. |
2796 | | // |
2797 | | |
2798 | | void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable& symbolTable, const TString& name) |
2799 | 233 | { |
2800 | 233 | TSymbol* symbol = symbolTable.find(name); |
2801 | 233 | if (symbol) |
2802 | 14 | addSymbolLinkageNode(linkage, *symbol->getAsVariable()); |
2803 | 233 | } |
2804 | | |
2805 | | void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol& symbol) |
2806 | 14 | { |
2807 | 14 | const TVariable* variable = symbol.getAsVariable(); |
2808 | 14 | if (! variable) { |
2809 | | // This must be a member of an anonymous block, and we need to add the whole block |
2810 | 0 | const TAnonMember* anon = symbol.getAsAnonMember(); |
2811 | 0 | variable = &anon->getAnonContainer(); |
2812 | 0 | } |
2813 | 14 | TIntermSymbol* node = addSymbol(*variable); |
2814 | 14 | linkage = growAggregate(linkage, node); |
2815 | 14 | } |
2816 | | |
2817 | | // |
2818 | | // Add a caller->callee relationship to the call graph. |
2819 | | // Assumes the strings are unique per signature. |
2820 | | // |
2821 | | void TIntermediate::addToCallGraph(TInfoSink& /*infoSink*/, const TString& caller, const TString& callee) |
2822 | 0 | { |
2823 | | // Duplicates are okay, but faster to not keep them, and they come grouped by caller, |
2824 | | // as long as new ones are push on the same end we check on for duplicates |
2825 | 0 | for (TGraph::const_iterator call = callGraph.begin(); call != callGraph.end(); ++call) { |
2826 | 0 | if (call->caller != caller) |
2827 | 0 | break; |
2828 | 0 | if (call->callee == callee) |
2829 | 0 | return; |
2830 | 0 | } |
2831 | | |
2832 | 0 | callGraph.emplace_front(caller, callee); |
2833 | 0 | } |
2834 | | |
2835 | | // |
2836 | | // This deletes the tree. |
2837 | | // |
2838 | | void TIntermediate::removeTree() |
2839 | 0 | { |
2840 | 0 | if (treeRoot) |
2841 | 0 | RemoveAllTreeNodes(treeRoot); |
2842 | 0 | } |
2843 | | |
2844 | | // |
2845 | | // Implement the part of KHR_vulkan_glsl that lists the set of operations |
2846 | | // that can result in a specialization constant operation. |
2847 | | // |
2848 | | // "5.x Specialization Constant Operations" |
2849 | | // |
2850 | | // Only some operations discussed in this section may be applied to a |
2851 | | // specialization constant and still yield a result that is as |
2852 | | // specialization constant. The operations allowed are listed below. |
2853 | | // When a specialization constant is operated on with one of these |
2854 | | // operators and with another constant or specialization constant, the |
2855 | | // result is implicitly a specialization constant. |
2856 | | // |
2857 | | // - int(), uint(), and bool() constructors for type conversions |
2858 | | // from any of the following types to any of the following types: |
2859 | | // * int |
2860 | | // * uint |
2861 | | // * bool |
2862 | | // - vector versions of the above conversion constructors |
2863 | | // - allowed implicit conversions of the above |
2864 | | // - swizzles (e.g., foo.yx) |
2865 | | // - The following when applied to integer or unsigned integer types: |
2866 | | // * unary negative ( - ) |
2867 | | // * binary operations ( + , - , * , / , % ) |
2868 | | // * shift ( <<, >> ) |
2869 | | // * bitwise operations ( & , | , ^ ) |
2870 | | // - The following when applied to integer or unsigned integer scalar types: |
2871 | | // * comparison ( == , != , > , >= , < , <= ) |
2872 | | // - The following when applied to the Boolean scalar type: |
2873 | | // * not ( ! ) |
2874 | | // * logical operations ( && , || , ^^ ) |
2875 | | // * comparison ( == , != )" |
2876 | | // |
2877 | | // This function just handles binary and unary nodes. Construction |
2878 | | // rules are handled in construction paths that are not covered by the unary |
2879 | | // and binary paths, while required conversions will still show up here |
2880 | | // as unary converters in the from a construction operator. |
2881 | | // |
2882 | | bool TIntermediate::isSpecializationOperation(const TIntermOperator& node) const |
2883 | 0 | { |
2884 | | // The operations resulting in floating point are quite limited |
2885 | | // (However, some floating-point operations result in bool, like ">", |
2886 | | // so are handled later.) |
2887 | 0 | if (node.getType().isFloatingDomain()) { |
2888 | 0 | if (IsOpNumericConv(node.getOp()) && |
2889 | 0 | isTypeFloat(node.getType().getBasicType()) && |
2890 | 0 | isTypeFloat(node.getAsUnaryNode()->getOperand()->getAsTyped()->getType().getBasicType())) { |
2891 | 0 | return true; |
2892 | 0 | } |
2893 | 0 | switch (node.getOp()) { |
2894 | 0 | case EOpIndexDirect: |
2895 | 0 | case EOpIndexIndirect: |
2896 | 0 | case EOpIndexDirectStruct: |
2897 | 0 | case EOpVectorSwizzle: |
2898 | 0 | return true; |
2899 | 0 | default: |
2900 | 0 | return false; |
2901 | 0 | } |
2902 | 0 | } |
2903 | | |
2904 | | // Check for floating-point arguments |
2905 | 0 | if (const TIntermBinary* bin = node.getAsBinaryNode()) |
2906 | 0 | if (bin->getLeft() ->getType().isFloatingDomain() || |
2907 | 0 | bin->getRight()->getType().isFloatingDomain()) |
2908 | 0 | return false; |
2909 | | |
2910 | | // So, for now, we can assume everything left is non-floating-point... |
2911 | | |
2912 | 0 | if (IsOpNumericConv(node.getOp())) { |
2913 | 0 | TBasicType srcType = node.getAsUnaryNode()->getOperand()->getAsTyped()->getType().getBasicType(); |
2914 | 0 | TBasicType dstType = node.getType().getBasicType(); |
2915 | 0 | if ((isTypeInt(srcType) || srcType == EbtBool) && |
2916 | 0 | (isTypeInt(dstType) || dstType == EbtBool)) { |
2917 | 0 | return true; |
2918 | 0 | } |
2919 | 0 | } |
2920 | | |
2921 | | // Now check for integer/bool-based operations |
2922 | 0 | switch (node.getOp()) { |
2923 | | |
2924 | | // dereference/swizzle |
2925 | 0 | case EOpIndexDirect: |
2926 | 0 | case EOpIndexIndirect: |
2927 | 0 | case EOpIndexDirectStruct: |
2928 | 0 | case EOpVectorSwizzle: |
2929 | | |
2930 | | // unary operations |
2931 | 0 | case EOpNegative: |
2932 | 0 | case EOpLogicalNot: |
2933 | 0 | case EOpBitwiseNot: |
2934 | | |
2935 | | // binary operations |
2936 | 0 | case EOpAdd: |
2937 | 0 | case EOpSub: |
2938 | 0 | case EOpMul: |
2939 | 0 | case EOpVectorTimesScalar: |
2940 | 0 | case EOpDiv: |
2941 | 0 | case EOpMod: |
2942 | 0 | case EOpRightShift: |
2943 | 0 | case EOpLeftShift: |
2944 | 0 | case EOpAnd: |
2945 | 0 | case EOpInclusiveOr: |
2946 | 0 | case EOpExclusiveOr: |
2947 | 0 | case EOpLogicalOr: |
2948 | 0 | case EOpLogicalXor: |
2949 | 0 | case EOpLogicalAnd: |
2950 | 0 | case EOpEqual: |
2951 | 0 | case EOpNotEqual: |
2952 | 0 | case EOpLessThan: |
2953 | 0 | case EOpGreaterThan: |
2954 | 0 | case EOpLessThanEqual: |
2955 | 0 | case EOpGreaterThanEqual: |
2956 | 0 | return true; |
2957 | 0 | default: |
2958 | 0 | return false; |
2959 | 0 | } |
2960 | 0 | } |
2961 | | |
2962 | | // Is the operation one that must propagate nonuniform? |
2963 | | bool TIntermediate::isNonuniformPropagating(TOperator op) const |
2964 | 0 | { |
2965 | | // "* All Operators in Section 5.1 (Operators), except for assignment, |
2966 | | // arithmetic assignment, and sequence |
2967 | | // * Component selection in Section 5.5 |
2968 | | // * Matrix components in Section 5.6 |
2969 | | // * Structure and Array Operations in Section 5.7, except for the length |
2970 | | // method." |
2971 | 0 | switch (op) { |
2972 | 0 | case EOpPostIncrement: |
2973 | 0 | case EOpPostDecrement: |
2974 | 0 | case EOpPreIncrement: |
2975 | 0 | case EOpPreDecrement: |
2976 | |
|
2977 | 0 | case EOpNegative: |
2978 | 0 | case EOpLogicalNot: |
2979 | 0 | case EOpVectorLogicalNot: |
2980 | 0 | case EOpBitwiseNot: |
2981 | |
|
2982 | 0 | case EOpAdd: |
2983 | 0 | case EOpSub: |
2984 | 0 | case EOpMul: |
2985 | 0 | case EOpDiv: |
2986 | 0 | case EOpMod: |
2987 | 0 | case EOpRightShift: |
2988 | 0 | case EOpLeftShift: |
2989 | 0 | case EOpAnd: |
2990 | 0 | case EOpInclusiveOr: |
2991 | 0 | case EOpExclusiveOr: |
2992 | 0 | case EOpEqual: |
2993 | 0 | case EOpNotEqual: |
2994 | 0 | case EOpLessThan: |
2995 | 0 | case EOpGreaterThan: |
2996 | 0 | case EOpLessThanEqual: |
2997 | 0 | case EOpGreaterThanEqual: |
2998 | 0 | case EOpVectorTimesScalar: |
2999 | 0 | case EOpVectorTimesMatrix: |
3000 | 0 | case EOpMatrixTimesVector: |
3001 | 0 | case EOpMatrixTimesScalar: |
3002 | |
|
3003 | 0 | case EOpLogicalOr: |
3004 | 0 | case EOpLogicalXor: |
3005 | 0 | case EOpLogicalAnd: |
3006 | |
|
3007 | 0 | case EOpIndexDirect: |
3008 | 0 | case EOpIndexIndirect: |
3009 | 0 | case EOpIndexDirectStruct: |
3010 | 0 | case EOpVectorSwizzle: |
3011 | 0 | return true; |
3012 | | |
3013 | 0 | default: |
3014 | 0 | break; |
3015 | 0 | } |
3016 | | |
3017 | 0 | return false; |
3018 | 0 | } |
3019 | | |
3020 | | //////////////////////////////////////////////////////////////// |
3021 | | // |
3022 | | // Member functions of the nodes used for building the tree. |
3023 | | // |
3024 | | //////////////////////////////////////////////////////////////// |
3025 | | |
3026 | | // |
3027 | | // Say whether or not an operation node changes the value of a variable. |
3028 | | // |
3029 | | // Returns true if state is modified. |
3030 | | // |
3031 | | bool TIntermOperator::modifiesState() const |
3032 | 0 | { |
3033 | 0 | switch (op) { |
3034 | 0 | case EOpPostIncrement: |
3035 | 0 | case EOpPostDecrement: |
3036 | 0 | case EOpPreIncrement: |
3037 | 0 | case EOpPreDecrement: |
3038 | 0 | case EOpAssign: |
3039 | 0 | case EOpAddAssign: |
3040 | 0 | case EOpSubAssign: |
3041 | 0 | case EOpMulAssign: |
3042 | 0 | case EOpVectorTimesMatrixAssign: |
3043 | 0 | case EOpVectorTimesScalarAssign: |
3044 | 0 | case EOpMatrixTimesScalarAssign: |
3045 | 0 | case EOpMatrixTimesMatrixAssign: |
3046 | 0 | case EOpDivAssign: |
3047 | 0 | case EOpModAssign: |
3048 | 0 | case EOpAndAssign: |
3049 | 0 | case EOpInclusiveOrAssign: |
3050 | 0 | case EOpExclusiveOrAssign: |
3051 | 0 | case EOpLeftShiftAssign: |
3052 | 0 | case EOpRightShiftAssign: |
3053 | 0 | return true; |
3054 | 0 | default: |
3055 | 0 | return false; |
3056 | 0 | } |
3057 | 0 | } |
3058 | | |
3059 | | // |
3060 | | // returns true if the operator is for one of the constructors |
3061 | | // |
3062 | | bool TIntermOperator::isConstructor() const |
3063 | 206 | { |
3064 | 206 | return op > EOpConstructGuardStart && op < EOpConstructGuardEnd; |
3065 | 206 | } |
3066 | | |
3067 | | // |
3068 | | // Make sure the type of an operator is appropriate for its |
3069 | | // combination of operation and operand type. This will invoke |
3070 | | // promoteUnary, promoteBinary, etc as needed. |
3071 | | // |
3072 | | // Returns false if nothing makes sense. |
3073 | | // |
3074 | | bool TIntermediate::promote(TIntermOperator* node) |
3075 | 45 | { |
3076 | 45 | if (node == nullptr) |
3077 | 0 | return false; |
3078 | | |
3079 | 45 | if (node->getAsUnaryNode()) |
3080 | 32 | return promoteUnary(*node->getAsUnaryNode()); |
3081 | | |
3082 | 13 | if (node->getAsBinaryNode()) |
3083 | 13 | return promoteBinary(*node->getAsBinaryNode()); |
3084 | | |
3085 | 0 | if (node->getAsAggregate()) |
3086 | 0 | return promoteAggregate(*node->getAsAggregate()); |
3087 | | |
3088 | 0 | return false; |
3089 | 0 | } |
3090 | | |
3091 | | // |
3092 | | // See TIntermediate::promote |
3093 | | // |
3094 | | bool TIntermediate::promoteUnary(TIntermUnary& node) |
3095 | 32 | { |
3096 | 32 | const TOperator op = node.getOp(); |
3097 | 32 | TIntermTyped* operand = node.getOperand(); |
3098 | | |
3099 | 32 | switch (op) { |
3100 | 20 | case EOpLogicalNot: |
3101 | | // Convert operand to a boolean type |
3102 | 20 | if (operand->getBasicType() != EbtBool) { |
3103 | | // Add constructor to boolean type. If that fails, we can't do it, so return false. |
3104 | 16 | TIntermTyped* converted = addConversion(op, TType(EbtBool), operand); |
3105 | 16 | if (converted == nullptr) |
3106 | 0 | return false; |
3107 | | |
3108 | | // Use the result of converting the node to a bool. |
3109 | 16 | node.setOperand(operand = converted); // also updates stack variable |
3110 | 16 | } |
3111 | 20 | break; |
3112 | 20 | case EOpBitwiseNot: |
3113 | 3 | if (!isTypeInt(operand->getBasicType())) |
3114 | 0 | return false; |
3115 | 3 | break; |
3116 | 7 | case EOpNegative: |
3117 | 8 | case EOpPostIncrement: |
3118 | 8 | case EOpPostDecrement: |
3119 | 9 | case EOpPreIncrement: |
3120 | 9 | case EOpPreDecrement: |
3121 | 9 | if (!isTypeInt(operand->getBasicType()) && |
3122 | 1 | operand->getBasicType() != EbtFloat && |
3123 | 1 | operand->getBasicType() != EbtFloat16 && |
3124 | 1 | operand->getBasicType() != EbtDouble) |
3125 | | |
3126 | 1 | return false; |
3127 | 8 | break; |
3128 | 8 | default: |
3129 | | // HLSL uses this path for initial function signature finding for built-ins |
3130 | | // taking a single argument, which generally don't participate in |
3131 | | // operator-based type promotion (type conversion will occur later). |
3132 | | // For now, scalar argument cases are relying on the setType() call below. |
3133 | 0 | if (getSource() == EShSourceHlsl) |
3134 | 0 | break; |
3135 | | |
3136 | | // GLSL only allows integer arguments for the cases identified above in the |
3137 | | // case statements. |
3138 | 0 | if (operand->getBasicType() != EbtFloat) |
3139 | 0 | return false; |
3140 | 32 | } |
3141 | | |
3142 | 31 | node.setType(operand->getType()); |
3143 | 31 | node.getWritableType().getQualifier().makeTemporary(); |
3144 | | |
3145 | 31 | return true; |
3146 | 32 | } |
3147 | | |
3148 | | // Propagate precision qualifiers *up* from children to parent. |
3149 | | void TIntermUnary::updatePrecision() |
3150 | 34 | { |
3151 | 34 | if (getBasicType() == EbtInt || getBasicType() == EbtUint || |
3152 | 20 | getBasicType() == EbtFloat) { |
3153 | 14 | if (operand->getQualifier().precision > getQualifier().precision) |
3154 | 0 | getQualifier().precision = operand->getQualifier().precision; |
3155 | 14 | } |
3156 | 34 | } |
3157 | | |
3158 | | // |
3159 | | // See TIntermediate::promote |
3160 | | // |
3161 | | bool TIntermediate::promoteBinary(TIntermBinary& node) |
3162 | 13 | { |
3163 | 13 | TOperator op = node.getOp(); |
3164 | 13 | TIntermTyped* left = node.getLeft(); |
3165 | 13 | TIntermTyped* right = node.getRight(); |
3166 | | |
3167 | | // Arrays and structures have to be exact matches. |
3168 | 13 | if ((left->isArray() || right->isArray() || left->getBasicType() == EbtStruct || right->getBasicType() == EbtStruct) |
3169 | 0 | && left->getType() != right->getType()) |
3170 | 0 | return false; |
3171 | | |
3172 | | // Base assumption: just make the type the same as the left |
3173 | | // operand. Only deviations from this will be coded. |
3174 | 13 | node.setType(left->getType()); |
3175 | 13 | node.getWritableType().getQualifier().clear(); |
3176 | | |
3177 | | // Composite and opaque types don't having pending operator changes, e.g., |
3178 | | // array, structure, and samplers. Just establish final type and correctness. |
3179 | 13 | if (left->isArray() || left->getBasicType() == EbtStruct || left->getBasicType() == EbtSampler) { |
3180 | 0 | switch (op) { |
3181 | 0 | case EOpEqual: |
3182 | 0 | case EOpNotEqual: |
3183 | 0 | if (left->getBasicType() == EbtSampler) { |
3184 | | // can't compare samplers |
3185 | 0 | return false; |
3186 | 0 | } else { |
3187 | | // Promote to conditional |
3188 | 0 | node.setType(TType(EbtBool)); |
3189 | 0 | } |
3190 | | |
3191 | 0 | return true; |
3192 | | |
3193 | 0 | case EOpAssign: |
3194 | | // Keep type from above |
3195 | |
|
3196 | 0 | return true; |
3197 | | |
3198 | 0 | default: |
3199 | 0 | return false; |
3200 | 0 | } |
3201 | 0 | } |
3202 | | |
3203 | | // |
3204 | | // We now have only scalars, vectors, and matrices to worry about. |
3205 | | // |
3206 | | |
3207 | | // HLSL implicitly promotes bool -> int for numeric operations. |
3208 | | // (Implicit conversions to make the operands match each other's types were already done.) |
3209 | 13 | if (getSource() == EShSourceHlsl && |
3210 | 13 | (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)) { |
3211 | 0 | switch (op) { |
3212 | 0 | case EOpLessThan: |
3213 | 0 | case EOpGreaterThan: |
3214 | 0 | case EOpLessThanEqual: |
3215 | 0 | case EOpGreaterThanEqual: |
3216 | |
|
3217 | 0 | case EOpRightShift: |
3218 | 0 | case EOpLeftShift: |
3219 | |
|
3220 | 0 | case EOpMod: |
3221 | |
|
3222 | 0 | case EOpAnd: |
3223 | 0 | case EOpInclusiveOr: |
3224 | 0 | case EOpExclusiveOr: |
3225 | |
|
3226 | 0 | case EOpAdd: |
3227 | 0 | case EOpSub: |
3228 | 0 | case EOpDiv: |
3229 | 0 | case EOpMul: |
3230 | 0 | if (left->getBasicType() == EbtBool) |
3231 | 0 | left = createConversion(EbtInt, left); |
3232 | 0 | if (right->getBasicType() == EbtBool) |
3233 | 0 | right = createConversion(EbtInt, right); |
3234 | 0 | if (left == nullptr || right == nullptr) |
3235 | 0 | return false; |
3236 | 0 | node.setLeft(left); |
3237 | 0 | node.setRight(right); |
3238 | | |
3239 | | // Update the original base assumption on result type.. |
3240 | 0 | node.setType(left->getType()); |
3241 | 0 | node.getWritableType().getQualifier().clear(); |
3242 | |
|
3243 | 0 | break; |
3244 | | |
3245 | 0 | default: |
3246 | 0 | break; |
3247 | 0 | } |
3248 | 0 | } |
3249 | | |
3250 | | // Do general type checks against individual operands (comparing left and right is coming up, checking mixed shapes after that) |
3251 | 13 | switch (op) { |
3252 | 1 | case EOpLessThan: |
3253 | 1 | case EOpGreaterThan: |
3254 | 1 | case EOpLessThanEqual: |
3255 | 1 | case EOpGreaterThanEqual: |
3256 | | // Relational comparisons need numeric types and will promote to scalar Boolean. |
3257 | 1 | if (left->getBasicType() == EbtBool) |
3258 | 0 | return false; |
3259 | | |
3260 | 1 | node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize())); |
3261 | 1 | break; |
3262 | | |
3263 | 0 | case EOpEqual: |
3264 | 0 | case EOpNotEqual: |
3265 | 0 | if (getSource() == EShSourceHlsl) { |
3266 | 0 | const int resultWidth = std::max(left->getVectorSize(), right->getVectorSize()); |
3267 | | |
3268 | | // In HLSL, == or != on vectors means component-wise comparison. |
3269 | 0 | if (resultWidth > 1) { |
3270 | 0 | op = (op == EOpEqual) ? EOpVectorEqual : EOpVectorNotEqual; |
3271 | 0 | node.setOp(op); |
3272 | 0 | } |
3273 | |
|
3274 | 0 | node.setType(TType(EbtBool, EvqTemporary, resultWidth)); |
3275 | 0 | } else { |
3276 | | // All the above comparisons result in a bool (but not the vector compares) |
3277 | 0 | node.setType(TType(EbtBool)); |
3278 | 0 | } |
3279 | 0 | break; |
3280 | | |
3281 | 0 | case EOpLogicalAnd: |
3282 | 0 | case EOpLogicalOr: |
3283 | 0 | case EOpLogicalXor: |
3284 | | // logical ops operate only on Booleans or vectors of Booleans. |
3285 | 0 | if (left->getBasicType() != EbtBool || left->isMatrix()) |
3286 | 0 | return false; |
3287 | | |
3288 | 0 | if (getSource() == EShSourceGlsl) { |
3289 | | // logical ops operate only on scalar Booleans and will promote to scalar Boolean. |
3290 | 0 | if (left->isVector()) |
3291 | 0 | return false; |
3292 | 0 | } |
3293 | | |
3294 | 0 | node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize())); |
3295 | 0 | break; |
3296 | | |
3297 | 0 | case EOpRightShift: |
3298 | 0 | case EOpLeftShift: |
3299 | 0 | case EOpRightShiftAssign: |
3300 | 0 | case EOpLeftShiftAssign: |
3301 | |
|
3302 | 0 | case EOpMod: |
3303 | 0 | case EOpModAssign: |
3304 | |
|
3305 | 0 | case EOpAnd: |
3306 | 0 | case EOpInclusiveOr: |
3307 | 0 | case EOpExclusiveOr: |
3308 | 0 | case EOpAndAssign: |
3309 | 0 | case EOpInclusiveOrAssign: |
3310 | 0 | case EOpExclusiveOrAssign: |
3311 | 0 | if (getSource() == EShSourceHlsl) |
3312 | 0 | break; |
3313 | | |
3314 | | // Check for integer-only operands. |
3315 | 0 | if (!isTypeInt(left->getBasicType()) && !isTypeInt(right->getBasicType())) |
3316 | 0 | return false; |
3317 | 0 | if (left->isMatrix() || right->isMatrix()) |
3318 | 0 | return false; |
3319 | | |
3320 | 0 | break; |
3321 | | |
3322 | 4 | case EOpAdd: |
3323 | 7 | case EOpSub: |
3324 | 9 | case EOpDiv: |
3325 | 9 | case EOpMul: |
3326 | 9 | case EOpAddAssign: |
3327 | 9 | case EOpSubAssign: |
3328 | 9 | case EOpMulAssign: |
3329 | 9 | case EOpDivAssign: |
3330 | | // check for non-Boolean operands |
3331 | 9 | if (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool) |
3332 | 0 | return false; |
3333 | 9 | break; |
3334 | | |
3335 | 9 | default: |
3336 | 3 | break; |
3337 | 13 | } |
3338 | | |
3339 | | // Compare left and right, and finish with the cases where the operand types must match |
3340 | 13 | switch (op) { |
3341 | 1 | case EOpLessThan: |
3342 | 1 | case EOpGreaterThan: |
3343 | 1 | case EOpLessThanEqual: |
3344 | 1 | case EOpGreaterThanEqual: |
3345 | | |
3346 | 1 | case EOpEqual: |
3347 | 1 | case EOpNotEqual: |
3348 | 1 | case EOpVectorEqual: |
3349 | 1 | case EOpVectorNotEqual: |
3350 | | |
3351 | 1 | case EOpLogicalAnd: |
3352 | 1 | case EOpLogicalOr: |
3353 | 1 | case EOpLogicalXor: |
3354 | 1 | return left->getType() == right->getType(); |
3355 | | |
3356 | 0 | case EOpMod: |
3357 | 0 | case EOpModAssign: |
3358 | |
|
3359 | 0 | case EOpAnd: |
3360 | 0 | case EOpInclusiveOr: |
3361 | 0 | case EOpExclusiveOr: |
3362 | 0 | case EOpAndAssign: |
3363 | 0 | case EOpInclusiveOrAssign: |
3364 | 0 | case EOpExclusiveOrAssign: |
3365 | |
|
3366 | 4 | case EOpAdd: |
3367 | 7 | case EOpSub: |
3368 | 9 | case EOpDiv: |
3369 | | |
3370 | 9 | case EOpAddAssign: |
3371 | 9 | case EOpSubAssign: |
3372 | 9 | case EOpDivAssign: |
3373 | | // Quick out in case the types do match |
3374 | 9 | if (left->getType() == right->getType()) |
3375 | 9 | return true; |
3376 | | |
3377 | 0 | [[fallthrough]]; |
3378 | |
|
3379 | 0 | case EOpMul: |
3380 | 0 | case EOpMulAssign: |
3381 | | // At least the basic type has to match |
3382 | 0 | if (left->getBasicType() != right->getBasicType()) |
3383 | 0 | return false; |
3384 | 0 | break; |
3385 | | |
3386 | 3 | default: |
3387 | 3 | break; |
3388 | 13 | } |
3389 | | |
3390 | 3 | if (left->getType().isCoopMat() || right->getType().isCoopMat()) { |
3391 | | // Operations on two cooperative matrices must have identical types |
3392 | 0 | if (left->getType().isCoopMat() && right->getType().isCoopMat() && |
3393 | 0 | left->getType() != right->getType()) { |
3394 | 0 | return false; |
3395 | 0 | } |
3396 | 0 | switch (op) { |
3397 | 0 | case EOpMul: |
3398 | 0 | case EOpMulAssign: |
3399 | | // Mul not supported in NV_cooperative_matrix |
3400 | 0 | if (left->getType().isCoopMatNV() && right->getType().isCoopMatNV()) { |
3401 | 0 | return false; |
3402 | 0 | } |
3403 | | // NV_cooperative_matrix supports MulAssign is for mat*=scalar only. |
3404 | | // KHR_cooperative_matrix supports it for mat*=mat as well. |
3405 | 0 | if (op == EOpMulAssign && right->getType().isCoopMatNV()) { |
3406 | 0 | return false; |
3407 | 0 | } |
3408 | | // Use MatrixTimesScalar if either operand is not a matrix. Otherwise use Mul. |
3409 | 0 | if (!left->getType().isCoopMat() || !right->getType().isCoopMat()) { |
3410 | 0 | node.setOp(op == EOpMulAssign ? EOpMatrixTimesScalarAssign : EOpMatrixTimesScalar); |
3411 | 0 | } |
3412 | | // In case of scalar*matrix, take the result type from the matrix. |
3413 | 0 | if (right->getType().isCoopMat()) { |
3414 | 0 | node.setType(right->getType()); |
3415 | 0 | } |
3416 | 0 | return true; |
3417 | 0 | case EOpAdd: |
3418 | 0 | case EOpSub: |
3419 | 0 | case EOpDiv: |
3420 | 0 | case EOpAssign: |
3421 | | // These require both to be cooperative matrices |
3422 | 0 | if (!left->getType().isCoopMat() || !right->getType().isCoopMat()) { |
3423 | 0 | return false; |
3424 | 0 | } |
3425 | 0 | return true; |
3426 | 0 | default: |
3427 | 0 | break; |
3428 | 0 | } |
3429 | 0 | return false; |
3430 | 0 | } |
3431 | | |
3432 | 3 | if (left->getType().isCoopVecNV() || right->getType().isCoopVecNV()) { |
3433 | | // Operations on two cooperative vectors must have identical types |
3434 | 0 | if (left->getType().isCoopVecNV() && right->getType().isCoopVecNV() && |
3435 | 0 | left->getType() != right->getType()) { |
3436 | 0 | return false; |
3437 | 0 | } |
3438 | 0 | switch (op) { |
3439 | 0 | case EOpMul: |
3440 | 0 | case EOpMulAssign: |
3441 | | // Use VectorTimesScalar if either operand is not a vector. Otherwise use Mul. |
3442 | 0 | if (!left->getType().isCoopVecNV() || !right->getType().isCoopVecNV()) { |
3443 | 0 | node.setOp(op == EOpMulAssign ? EOpVectorTimesScalarAssign : EOpVectorTimesScalar); |
3444 | 0 | } |
3445 | | // In case of scalar*vector, take the result type from the vector. |
3446 | 0 | if (right->getType().isCoopVecNV()) { |
3447 | 0 | node.setType(right->getType()); |
3448 | 0 | } |
3449 | 0 | return true; |
3450 | 0 | case EOpLeftShift: |
3451 | 0 | case EOpLeftShiftAssign: |
3452 | 0 | case EOpRightShift: |
3453 | 0 | case EOpRightShiftAssign: |
3454 | 0 | case EOpAdd: |
3455 | 0 | case EOpSub: |
3456 | 0 | case EOpDiv: |
3457 | 0 | case EOpAssign: |
3458 | | // These require both to be cooperative vectors |
3459 | 0 | if (!left->getType().isCoopVecNV() || !right->getType().isCoopVecNV()) { |
3460 | 0 | return false; |
3461 | 0 | } |
3462 | 0 | return true; |
3463 | 0 | default: |
3464 | 0 | break; |
3465 | 0 | } |
3466 | 0 | return false; |
3467 | 0 | } |
3468 | | |
3469 | 3 | bool vectorAndLongVectorMatch = TType::vectorAndLongVectorMatch(left->getType(), right->getType()); |
3470 | | |
3471 | | // Finish handling the case, for all ops, where both operands are scalars. |
3472 | 3 | if (left->isScalar() && right->isScalar()) |
3473 | 3 | return true; |
3474 | | |
3475 | | // Finish handling the case, for all ops, where there are two vectors of different sizes |
3476 | 0 | if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize() && right->getVectorSize() > 1) |
3477 | 0 | return false; |
3478 | | |
3479 | | // Finish handling the case, for all ops, where there are two vectors of different sizes |
3480 | 0 | if (left->getType().isLongVector() && right->getType().isLongVector() && !left->getType().sameLongVectorShape(right->getType())) |
3481 | 0 | return false; |
3482 | | |
3483 | | // |
3484 | | // We now have a mix of scalars, vectors, or matrices, for non-relational operations. |
3485 | | // |
3486 | | |
3487 | | // Can these two operands be combined, what is the resulting type? |
3488 | 0 | TBasicType basicType = left->getBasicType(); |
3489 | 0 | switch (op) { |
3490 | 0 | case EOpMul: |
3491 | 0 | if (!left->isMatrix() && right->isMatrix()) { |
3492 | 0 | if (left->isVector()) { |
3493 | 0 | if (left->getVectorSize() != right->getMatrixRows()) |
3494 | 0 | return false; |
3495 | 0 | node.setOp(op = EOpVectorTimesMatrix); |
3496 | 0 | node.setType(TType(basicType, EvqTemporary, right->getMatrixCols())); |
3497 | 0 | } else { |
3498 | 0 | node.setOp(op = EOpMatrixTimesScalar); |
3499 | 0 | node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), right->getMatrixRows())); |
3500 | 0 | } |
3501 | 0 | } else if (left->isMatrix() && !right->isMatrix()) { |
3502 | 0 | if (right->isVector()) { |
3503 | 0 | if (left->getMatrixCols() != right->getVectorSize()) |
3504 | 0 | return false; |
3505 | 0 | node.setOp(op = EOpMatrixTimesVector); |
3506 | 0 | node.setType(TType(basicType, EvqTemporary, left->getMatrixRows())); |
3507 | 0 | } else { |
3508 | 0 | node.setOp(op = EOpMatrixTimesScalar); |
3509 | 0 | } |
3510 | 0 | } else if (left->isMatrix() && right->isMatrix()) { |
3511 | 0 | if (left->getMatrixCols() != right->getMatrixRows()) |
3512 | 0 | return false; |
3513 | 0 | node.setOp(op = EOpMatrixTimesMatrix); |
3514 | 0 | node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), left->getMatrixRows())); |
3515 | 0 | } else if (! left->isMatrix() && ! right->isMatrix()) { |
3516 | 0 | if (left->isVector() && right->isVector()) { |
3517 | 0 | ; // leave as component product |
3518 | 0 | } else if (left->isVector() || right->isVector()) { |
3519 | 0 | node.setOp(op = EOpVectorTimesScalar); |
3520 | 0 | if (right->isVector()) |
3521 | 0 | node.setType(TType(basicType, EvqTemporary, right->getVectorSize())); |
3522 | 0 | } |
3523 | 0 | } else { |
3524 | 0 | return false; |
3525 | 0 | } |
3526 | 0 | break; |
3527 | 0 | case EOpMulAssign: |
3528 | 0 | if (! left->isMatrix() && right->isMatrix()) { |
3529 | 0 | if (left->isVector()) { |
3530 | 0 | if (left->getVectorSize() != right->getMatrixRows() || left->getVectorSize() != right->getMatrixCols()) |
3531 | 0 | return false; |
3532 | 0 | node.setOp(op = EOpVectorTimesMatrixAssign); |
3533 | 0 | } else { |
3534 | 0 | return false; |
3535 | 0 | } |
3536 | 0 | } else if (left->isMatrix() && !right->isMatrix()) { |
3537 | 0 | if (right->isVector()) { |
3538 | 0 | return false; |
3539 | 0 | } else { |
3540 | 0 | node.setOp(op = EOpMatrixTimesScalarAssign); |
3541 | 0 | } |
3542 | 0 | } else if (left->isMatrix() && right->isMatrix()) { |
3543 | 0 | if (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixCols() != right->getMatrixRows()) |
3544 | 0 | return false; |
3545 | 0 | node.setOp(op = EOpMatrixTimesMatrixAssign); |
3546 | 0 | } else if (!left->isMatrix() && !right->isMatrix()) { |
3547 | 0 | if (left->isVector() && right->isVector()) { |
3548 | | // leave as component product |
3549 | 0 | } else if (left->isVector() || right->isVector()) { |
3550 | 0 | if (! left->isVector()) |
3551 | 0 | return false; |
3552 | 0 | node.setOp(op = EOpVectorTimesScalarAssign); |
3553 | 0 | } |
3554 | 0 | } else { |
3555 | 0 | return false; |
3556 | 0 | } |
3557 | 0 | break; |
3558 | | |
3559 | 0 | case EOpRightShift: |
3560 | 0 | case EOpLeftShift: |
3561 | 0 | case EOpRightShiftAssign: |
3562 | 0 | case EOpLeftShiftAssign: |
3563 | 0 | if (right->isVector() && (! left->isVector() || right->getVectorSize() != left->getVectorSize())) |
3564 | 0 | return false; |
3565 | 0 | break; |
3566 | | |
3567 | 0 | case EOpAssign: |
3568 | 0 | if ((left->getVectorSize() != right->getVectorSize() || left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows()) && |
3569 | 0 | !vectorAndLongVectorMatch) |
3570 | 0 | return false; |
3571 | 0 | [[fallthrough]]; |
3572 | |
|
3573 | 0 | case EOpAdd: |
3574 | 0 | case EOpSub: |
3575 | 0 | case EOpDiv: |
3576 | 0 | case EOpMod: |
3577 | 0 | case EOpAnd: |
3578 | 0 | case EOpInclusiveOr: |
3579 | 0 | case EOpExclusiveOr: |
3580 | 0 | case EOpAddAssign: |
3581 | 0 | case EOpSubAssign: |
3582 | 0 | case EOpDivAssign: |
3583 | 0 | case EOpModAssign: |
3584 | 0 | case EOpAndAssign: |
3585 | 0 | case EOpInclusiveOrAssign: |
3586 | 0 | case EOpExclusiveOrAssign: |
3587 | |
|
3588 | 0 | if ((left->isMatrix() && right->isVector()) || |
3589 | 0 | (left->isVector() && right->isMatrix()) || |
3590 | 0 | left->getBasicType() != right->getBasicType()) |
3591 | 0 | return false; |
3592 | 0 | if (left->isMatrix() && right->isMatrix() && (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows())) |
3593 | 0 | return false; |
3594 | 0 | if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize()) |
3595 | 0 | return false; |
3596 | 0 | if ((right->isVector() || right->isMatrix()) && !vectorAndLongVectorMatch) { |
3597 | 0 | node.getWritableType().shallowCopy(right->getType()); |
3598 | 0 | node.getWritableType().getQualifier().makeTemporary(); |
3599 | 0 | } |
3600 | 0 | break; |
3601 | | |
3602 | 0 | default: |
3603 | 0 | return false; |
3604 | 0 | } |
3605 | | |
3606 | | // |
3607 | | // One more check for assignment. |
3608 | | // |
3609 | 0 | switch (op) { |
3610 | | // The resulting type has to match the left operand. |
3611 | 0 | case EOpAssign: |
3612 | 0 | case EOpAddAssign: |
3613 | 0 | case EOpSubAssign: |
3614 | 0 | case EOpMulAssign: |
3615 | 0 | case EOpDivAssign: |
3616 | 0 | case EOpModAssign: |
3617 | 0 | case EOpAndAssign: |
3618 | 0 | case EOpInclusiveOrAssign: |
3619 | 0 | case EOpExclusiveOrAssign: |
3620 | 0 | case EOpLeftShiftAssign: |
3621 | 0 | case EOpRightShiftAssign: |
3622 | 0 | if (node.getType() != left->getType()) |
3623 | 0 | return false; |
3624 | 0 | break; |
3625 | 0 | default: |
3626 | 0 | break; |
3627 | 0 | } |
3628 | | |
3629 | 0 | return true; |
3630 | 0 | } |
3631 | | |
3632 | | // |
3633 | | // See TIntermediate::promote |
3634 | | // |
3635 | | bool TIntermediate::promoteAggregate(TIntermAggregate& node) |
3636 | 0 | { |
3637 | 0 | TOperator op = node.getOp(); |
3638 | 0 | TIntermSequence& args = node.getSequence(); |
3639 | 0 | const int numArgs = static_cast<int>(args.size()); |
3640 | | |
3641 | | // Presently, only hlsl does intrinsic promotions. |
3642 | 0 | if (getSource() != EShSourceHlsl) |
3643 | 0 | return true; |
3644 | | |
3645 | | // set of opcodes that can be promoted in this manner. |
3646 | 0 | switch (op) { |
3647 | 0 | case EOpAtan: |
3648 | 0 | case EOpClamp: |
3649 | 0 | case EOpCross: |
3650 | 0 | case EOpDistance: |
3651 | 0 | case EOpDot: |
3652 | 0 | case EOpDst: |
3653 | 0 | case EOpFaceForward: |
3654 | | // case EOpFindMSB: TODO: |
3655 | | // case EOpFindLSB: TODO: |
3656 | 0 | case EOpFma: |
3657 | 0 | case EOpMod: |
3658 | 0 | case EOpFrexp: |
3659 | 0 | case EOpLdexp: |
3660 | 0 | case EOpMix: |
3661 | 0 | case EOpLit: |
3662 | 0 | case EOpMax: |
3663 | 0 | case EOpMin: |
3664 | 0 | case EOpModf: |
3665 | | // case EOpGenMul: TODO: |
3666 | 0 | case EOpPow: |
3667 | 0 | case EOpReflect: |
3668 | 0 | case EOpRefract: |
3669 | | // case EOpSinCos: TODO: |
3670 | 0 | case EOpSmoothStep: |
3671 | 0 | case EOpStep: |
3672 | 0 | break; |
3673 | 0 | default: |
3674 | 0 | return true; |
3675 | 0 | } |
3676 | | |
3677 | | // TODO: array and struct behavior |
3678 | | |
3679 | | // Try converting all nodes to the given node's type |
3680 | 0 | TIntermSequence convertedArgs(numArgs, nullptr); |
3681 | | |
3682 | | // Try to convert all types to the nonConvArg type. |
3683 | 0 | for (int nonConvArg = 0; nonConvArg < numArgs; ++nonConvArg) { |
3684 | | // Try converting all args to this arg's type |
3685 | 0 | for (int convArg = 0; convArg < numArgs; ++convArg) { |
3686 | 0 | convertedArgs[convArg] = addConversion(op, args[nonConvArg]->getAsTyped()->getType(), |
3687 | 0 | args[convArg]->getAsTyped()); |
3688 | 0 | } |
3689 | | |
3690 | | // If we successfully converted all the args, use the result. |
3691 | 0 | if (std::all_of(convertedArgs.begin(), convertedArgs.end(), |
3692 | 0 | [](const TIntermNode* node) { return node != nullptr; })) { |
3693 | |
|
3694 | 0 | std::swap(args, convertedArgs); |
3695 | 0 | return true; |
3696 | 0 | } |
3697 | 0 | } |
3698 | | |
3699 | 0 | return false; |
3700 | 0 | } |
3701 | | |
3702 | | // Propagate precision qualifiers *up* from children to parent, and then |
3703 | | // back *down* again to the children's subtrees. |
3704 | | void TIntermAggregate::updatePrecision() |
3705 | 0 | { |
3706 | 0 | if (getBasicType() == EbtInt || getBasicType() == EbtUint || |
3707 | 0 | getBasicType() == EbtFloat) { |
3708 | 0 | TPrecisionQualifier maxPrecision = EpqNone; |
3709 | 0 | TIntermSequence operands = getSequence(); |
3710 | 0 | for (unsigned int i = 0; i < operands.size(); ++i) { |
3711 | 0 | TIntermTyped* typedNode = operands[i]->getAsTyped(); |
3712 | 0 | assert(typedNode); |
3713 | 0 | maxPrecision = std::max(maxPrecision, typedNode->getQualifier().precision); |
3714 | 0 | } |
3715 | 0 | getQualifier().precision = maxPrecision; |
3716 | 0 | for (unsigned int i = 0; i < operands.size(); ++i) { |
3717 | 0 | TIntermTyped* typedNode = operands[i]->getAsTyped(); |
3718 | 0 | assert(typedNode); |
3719 | 0 | typedNode->propagatePrecision(maxPrecision); |
3720 | 0 | } |
3721 | 0 | } |
3722 | 0 | } |
3723 | | |
3724 | | // Propagate precision qualifiers *up* from children to parent, and then |
3725 | | // back *down* again to the children's subtrees. |
3726 | | void TIntermBinary::updatePrecision() |
3727 | 13 | { |
3728 | 13 | if (getBasicType() == EbtInt || getBasicType() == EbtUint || |
3729 | 12 | getBasicType() == EbtFloat) { |
3730 | 12 | if (op == EOpRightShift || op == EOpLeftShift) { |
3731 | | // For shifts get precision from left side only and thus no need to propagate |
3732 | 0 | getQualifier().precision = left->getQualifier().precision; |
3733 | 12 | } else { |
3734 | 12 | getQualifier().precision = std::max(right->getQualifier().precision, left->getQualifier().precision); |
3735 | 12 | if (getQualifier().precision != EpqNone) { |
3736 | 0 | left->propagatePrecision(getQualifier().precision); |
3737 | 0 | right->propagatePrecision(getQualifier().precision); |
3738 | 0 | } |
3739 | 12 | } |
3740 | 12 | } |
3741 | 13 | } |
3742 | | |
3743 | | // Recursively propagate precision qualifiers *down* the subtree of the current node, |
3744 | | // until reaching a node that already has a precision qualifier or otherwise does |
3745 | | // not participate in precision propagation. |
3746 | | void TIntermTyped::propagatePrecision(TPrecisionQualifier newPrecision) |
3747 | 0 | { |
3748 | 0 | if (getQualifier().precision != EpqNone || |
3749 | 0 | (getBasicType() != EbtInt && getBasicType() != EbtUint && |
3750 | 0 | getBasicType() != EbtFloat && getBasicType() != EbtFloat16)) |
3751 | 0 | return; |
3752 | | |
3753 | 0 | getQualifier().precision = newPrecision; |
3754 | |
|
3755 | 0 | TIntermBinary* binaryNode = getAsBinaryNode(); |
3756 | 0 | if (binaryNode) { |
3757 | 0 | binaryNode->getLeft()->propagatePrecision(newPrecision); |
3758 | 0 | binaryNode->getRight()->propagatePrecision(newPrecision); |
3759 | |
|
3760 | 0 | return; |
3761 | 0 | } |
3762 | | |
3763 | 0 | TIntermUnary* unaryNode = getAsUnaryNode(); |
3764 | 0 | if (unaryNode) { |
3765 | 0 | unaryNode->getOperand()->propagatePrecision(newPrecision); |
3766 | |
|
3767 | 0 | return; |
3768 | 0 | } |
3769 | | |
3770 | 0 | TIntermAggregate* aggregateNode = getAsAggregate(); |
3771 | 0 | if (aggregateNode) { |
3772 | 0 | TIntermSequence operands = aggregateNode->getSequence(); |
3773 | 0 | for (unsigned int i = 0; i < operands.size(); ++i) { |
3774 | 0 | TIntermTyped* typedNode = operands[i]->getAsTyped(); |
3775 | 0 | if (! typedNode) |
3776 | 0 | break; |
3777 | 0 | typedNode->propagatePrecision(newPrecision); |
3778 | 0 | } |
3779 | |
|
3780 | 0 | return; |
3781 | 0 | } |
3782 | | |
3783 | 0 | TIntermSelection* selectionNode = getAsSelectionNode(); |
3784 | 0 | if (selectionNode) { |
3785 | 0 | TIntermTyped* typedNode = selectionNode->getTrueBlock()->getAsTyped(); |
3786 | 0 | if (typedNode) { |
3787 | 0 | typedNode->propagatePrecision(newPrecision); |
3788 | 0 | typedNode = selectionNode->getFalseBlock()->getAsTyped(); |
3789 | 0 | if (typedNode) |
3790 | 0 | typedNode->propagatePrecision(newPrecision); |
3791 | 0 | } |
3792 | |
|
3793 | 0 | return; |
3794 | 0 | } |
3795 | 0 | } |
3796 | | |
3797 | | TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) const |
3798 | 208 | { |
3799 | 208 | const TConstUnionArray& rightUnionArray = node->getConstArray(); |
3800 | 208 | int size = node->getType().computeNumComponents(); |
3801 | | |
3802 | 208 | TConstUnionArray leftUnionArray(size); |
3803 | | |
3804 | 420 | for (int i=0; i < size; i++) { |
3805 | | |
3806 | 212 | #define PROMOTE(Set, CType, Get) leftUnionArray[i].Set(static_cast<CType>(rightUnionArray[i].Get())) |
3807 | 212 | #define PROMOTE_TO_BOOL(Get) leftUnionArray[i].setBConst(rightUnionArray[i].Get() != 0) |
3808 | | |
3809 | 212 | #define TO_ALL(Get) \ |
3810 | 212 | switch (promoteTo) { \ |
3811 | 0 | case EbtBFloat16: PROMOTE(setDConst, double, Get); break; \ |
3812 | 0 | case EbtFloatE5M2: PROMOTE(setDConst, double, Get); break; \ |
3813 | 0 | case EbtFloatE4M3: PROMOTE(setDConst, double, Get); break; \ |
3814 | 0 | case EbtFloat16: PROMOTE(setDConst, double, Get); break; \ |
3815 | 0 | case EbtFloat: PROMOTE(setDConst, double, Get); break; \ |
3816 | 0 | case EbtDouble: PROMOTE(setDConst, double, Get); break; \ |
3817 | 0 | case EbtInt8: PROMOTE(setI8Const, signed char, Get); break; \ |
3818 | 0 | case EbtInt16: PROMOTE(setI16Const, short, Get); break; \ |
3819 | 5 | case EbtInt: PROMOTE(setIConst, int, Get); break; \ |
3820 | 0 | case EbtInt64: PROMOTE(setI64Const, long long, Get); break; \ |
3821 | 0 | case EbtUint8: PROMOTE(setU8Const, unsigned char, Get); break; \ |
3822 | 0 | case EbtUint16: PROMOTE(setU16Const, unsigned short, Get); break; \ |
3823 | 179 | case EbtUint: PROMOTE(setUConst, unsigned int, Get); break; \ |
3824 | 0 | case EbtUint64: PROMOTE(setU64Const, unsigned long long, Get); break; \ |
3825 | 28 | case EbtBool: PROMOTE_TO_BOOL(Get); break; \ |
3826 | 0 | default: return node; \ |
3827 | 212 | } |
3828 | | |
3829 | 212 | switch (node->getType().getBasicType()) { |
3830 | 1 | case EbtFloat: TO_ALL(getDConst); break; |
3831 | 202 | case EbtInt: TO_ALL(getIConst); break; |
3832 | 0 | case EbtUint: TO_ALL(getUConst); break; |
3833 | 9 | case EbtBool: TO_ALL(getBConst); break; |
3834 | 0 | case EbtFloat16: TO_ALL(getDConst); break; |
3835 | 0 | case EbtBFloat16: TO_ALL(getDConst); break; |
3836 | 0 | case EbtFloatE5M2: TO_ALL(getDConst); break; |
3837 | 0 | case EbtFloatE4M3: TO_ALL(getDConst); break; |
3838 | 0 | case EbtDouble: TO_ALL(getDConst); break; |
3839 | 0 | case EbtInt8: TO_ALL(getI8Const); break; |
3840 | 0 | case EbtInt16: TO_ALL(getI16Const); break; |
3841 | 0 | case EbtInt64: TO_ALL(getI64Const); break; |
3842 | 0 | case EbtUint8: TO_ALL(getU8Const); break; |
3843 | 0 | case EbtUint16: TO_ALL(getU16Const); break; |
3844 | 0 | case EbtUint64: TO_ALL(getU64Const); break; |
3845 | 0 | default: return node; |
3846 | 212 | } |
3847 | 212 | } |
3848 | | |
3849 | 208 | const TType& t = node->getType(); |
3850 | | |
3851 | 208 | return addConstantUnion(leftUnionArray, TType(promoteTo, t.getQualifier().storage, t.getVectorSize(), t.getMatrixCols(), t.getMatrixRows()), |
3852 | 208 | node->getLoc()); |
3853 | 208 | } |
3854 | | |
3855 | | void TIntermAggregate::setPragmaTable(const TPragmaTable& pTable) |
3856 | 0 | { |
3857 | 0 | assert(pragmaTable == nullptr); |
3858 | 0 | pragmaTable = new TPragmaTable; |
3859 | 0 | *pragmaTable = pTable; |
3860 | 0 | } |
3861 | | |
3862 | | // If either node is a specialization constant, while the other is |
3863 | | // a constant (or specialization constant), the result is still |
3864 | | // a specialization constant. |
3865 | | bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TIntermTyped& node2) |
3866 | 8 | { |
3867 | 8 | return (node1.getType().getQualifier().isSpecConstant() && node2.getType().getQualifier().isConstant()) || |
3868 | 8 | (node2.getType().getQualifier().isSpecConstant() && node1.getType().getQualifier().isConstant()); |
3869 | 8 | } |
3870 | | |
3871 | | struct TextureUpgradeAndSamplerRemovalTransform : public TIntermTraverser { |
3872 | 0 | void visitSymbol(TIntermSymbol* symbol) override { |
3873 | 0 | if (symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isTexture()) { |
3874 | 0 | symbol->getWritableType().getSampler().setCombined(true); |
3875 | 0 | } |
3876 | 0 | } |
3877 | 0 | bool visitAggregate(TVisit, TIntermAggregate* ag) override { |
3878 | 0 | using namespace std; |
3879 | 0 | TIntermSequence& seq = ag->getSequence(); |
3880 | 0 | TQualifierList& qual = ag->getQualifierList(); |
3881 | | |
3882 | | // qual and seq are indexed using the same indices, so we have to modify both in lock-step |
3883 | 0 | assert(seq.size() == qual.size() || qual.empty()); |
3884 | |
|
3885 | 0 | size_t write = 0; |
3886 | 0 | for (size_t i = 0; i < seq.size(); ++i) { |
3887 | 0 | TIntermSymbol* symbol = seq[i]->getAsSymbolNode(); |
3888 | 0 | if (symbol && symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isPureSampler()) { |
3889 | | // remove pure sampler variables |
3890 | 0 | continue; |
3891 | 0 | } |
3892 | | |
3893 | 0 | TIntermNode* result = seq[i]; |
3894 | | |
3895 | | // replace constructors with sampler/textures |
3896 | 0 | TIntermAggregate *constructor = seq[i]->getAsAggregate(); |
3897 | 0 | if (constructor && constructor->getOp() == EOpConstructTextureSampler) { |
3898 | 0 | if (!constructor->getSequence().empty()) |
3899 | 0 | result = constructor->getSequence()[0]; |
3900 | 0 | } |
3901 | | |
3902 | | // write new node & qualifier |
3903 | 0 | seq[write] = result; |
3904 | 0 | if (!qual.empty()) |
3905 | 0 | qual[write] = qual[i]; |
3906 | 0 | write++; |
3907 | 0 | } |
3908 | |
|
3909 | 0 | seq.resize(write); |
3910 | 0 | if (!qual.empty()) |
3911 | 0 | qual.resize(write); |
3912 | |
|
3913 | 0 | return true; |
3914 | 0 | } |
3915 | | }; |
3916 | | |
3917 | | void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root) |
3918 | 0 | { |
3919 | 0 | TextureUpgradeAndSamplerRemovalTransform transform; |
3920 | 0 | root->traverse(&transform); |
3921 | 0 | } |
3922 | | |
3923 | | const char* TIntermediate::getResourceName(TResourceType res) |
3924 | 0 | { |
3925 | 0 | switch (res) { |
3926 | 0 | case EResSampler: return "shift-sampler-binding"; |
3927 | 0 | case EResTexture: return "shift-texture-binding"; |
3928 | 0 | case EResImage: return "shift-image-binding"; |
3929 | 0 | case EResUbo: return "shift-ubo-binding"; |
3930 | 0 | case EResSsbo: return "shift-ssbo-binding"; |
3931 | 0 | case EResUav: return "shift-uav-binding"; |
3932 | 0 | case EResCombinedSampler: return "shift-combined-sampler-binding"; |
3933 | 0 | case EResAs: return "shift-as-binding"; |
3934 | 0 | case EResTensor: return nullptr; |
3935 | 0 | default: |
3936 | | assert(0); // internal error: should only be called with valid resource types. |
3937 | 0 | return nullptr; |
3938 | 0 | } |
3939 | 0 | } |
3940 | | |
3941 | | |
3942 | | } // end namespace glslang |