blob: 6602ba28e937380066a2c5675fab4dbe8e15e0cb [file] [log] [blame]
John Kesseniche01a9bc2016-03-12 20:11:22 -07001//
John Kessenich715c3532017-10-28 14:42:44 -06002// Copyright (C) 2017 Google, Inc.
3// Copyright (C) 2017 LunarG, Inc.
John Kesseniche01a9bc2016-03-12 20:11:22 -07004//
John Kessenich927608b2017-01-06 12:34:14 -07005// All rights reserved.
John Kesseniche01a9bc2016-03-12 20:11:22 -07006//
John Kessenich927608b2017-01-06 12:34:14 -07007// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions
9// are met:
John Kesseniche01a9bc2016-03-12 20:11:22 -070010//
11// Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13//
14// Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following
16// disclaimer in the documentation and/or other materials provided
17// with the distribution.
18//
19// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
20// contributors may be used to endorse or promote products derived
21// from this software without specific prior written permission.
22//
John Kessenich927608b2017-01-06 12:34:14 -070023// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34// POSSIBILITY OF SUCH DAMAGE.
John Kesseniche01a9bc2016-03-12 20:11:22 -070035//
36
37#include "hlslParseHelper.h"
38#include "hlslScanContext.h"
39#include "hlslGrammar.h"
steve-lunarg1868b142016-10-20 13:07:10 -060040#include "hlslAttributes.h"
John Kesseniche01a9bc2016-03-12 20:11:22 -070041
42#include "../glslang/MachineIndependent/Scan.h"
43#include "../glslang/MachineIndependent/preprocessor/PpContext.h"
44
45#include "../glslang/OSDependent/osinclude.h"
46
John Kesseniche01a9bc2016-03-12 20:11:22 -070047#include <algorithm>
steve-lunarga2b01a02016-11-28 17:09:54 -070048#include <functional>
steve-lunarg9088be42016-11-01 10:31:42 -060049#include <cctype>
steve-lunargcd6829b2016-12-28 10:03:58 -070050#include <array>
steve-lunarg858c9282017-01-07 08:54:10 -070051#include <set>
John Kesseniche01a9bc2016-03-12 20:11:22 -070052
53namespace glslang {
54
John Kessenichd3f11222016-11-05 10:15:53 -060055HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins,
John Kessenich2ceec682017-07-28 16:20:13 -060056 int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
57 TInfoSink& infoSink,
steve-lunargf1e0c872016-10-31 15:13:43 -060058 const TString sourceEntryPointName,
John Kesseniche01a9bc2016-03-12 20:11:22 -070059 bool forwardCompatible, EShMessages messages) :
John Kessenich2ceec682017-07-28 16:20:13 -060060 TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink,
John Kessenich9855bda2017-09-11 21:48:19 -060061 forwardCompatible, messages, &sourceEntryPointName),
John Kessenich9b2531b2017-06-06 19:19:45 -060062 annotationNestingLevel(0),
Daniel Koch15bb4372017-04-21 16:16:43 -040063 inputPatch(nullptr),
Alex Szpakowski5f316d92017-01-08 18:21:17 -040064 nextInLocation(0), nextOutLocation(0),
steve-lunarg858c9282017-01-07 08:54:10 -070065 entryPointFunction(nullptr),
steve-lunarg08e0c082017-03-29 20:01:13 -060066 entryPointFunctionBody(nullptr),
LoopDawg307b6502017-07-05 11:33:06 -060067 gsStreamOutput(nullptr),
LoopDawg5e5b12e2017-08-28 14:02:19 -060068 clipDistanceInput(nullptr),
69 cullDistanceInput(nullptr),
70 clipDistanceOutput(nullptr),
71 cullDistanceOutput(nullptr)
John Kesseniche01a9bc2016-03-12 20:11:22 -070072{
John Kesseniche01a9bc2016-03-12 20:11:22 -070073 globalUniformDefaults.clear();
John Kessenich10f7fc72016-09-25 20:25:06 -060074 globalUniformDefaults.layoutMatrix = ElmRowMajor;
John Kessenichb901ade2016-06-16 20:59:42 -060075 globalUniformDefaults.layoutPacking = ElpStd140;
John Kesseniche01a9bc2016-03-12 20:11:22 -070076
77 globalBufferDefaults.clear();
John Kessenich10f7fc72016-09-25 20:25:06 -060078 globalBufferDefaults.layoutMatrix = ElmRowMajor;
John Kessenichb901ade2016-06-16 20:59:42 -060079 globalBufferDefaults.layoutPacking = ElpStd430;
John Kesseniche01a9bc2016-03-12 20:11:22 -070080
81 globalInputDefaults.clear();
82 globalOutputDefaults.clear();
83
LoopDawg5e5b12e2017-08-28 14:02:19 -060084 clipSemanticNSizeIn.fill(0);
85 cullSemanticNSizeIn.fill(0);
86 clipSemanticNSizeOut.fill(0);
87 cullSemanticNSizeOut.fill(0);
LoopDawg307b6502017-07-05 11:33:06 -060088
John Kessenichecba76f2017-01-06 00:34:48 -070089 // "Shaders in the transform
John Kesseniche01a9bc2016-03-12 20:11:22 -070090 // feedback capturing mode have an initial global default of
91 // layout(xfb_buffer = 0) out;"
92 if (language == EShLangVertex ||
93 language == EShLangTessControl ||
94 language == EShLangTessEvaluation ||
95 language == EShLangGeometry)
96 globalOutputDefaults.layoutXfbBuffer = 0;
97
98 if (language == EShLangGeometry)
99 globalOutputDefaults.layoutStream = 0;
John Kessenichd4032292016-09-09 11:43:11 -0600100
101 if (spvVersion.spv == 0 || spvVersion.vulkan == 0)
102 infoSink.info << "ERROR: HLSL currently only supported when requesting SPIR-V for Vulkan.\n";
John Kesseniche01a9bc2016-03-12 20:11:22 -0700103}
104
105HlslParseContext::~HlslParseContext()
106{
107}
108
LoopDawg62561462016-07-22 20:46:03 -0600109void HlslParseContext::initializeExtensionBehavior()
110{
111 TParseContextBase::initializeExtensionBehavior();
112
113 // HLSL allows #line by default.
114 extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable;
115}
116
John Kesseniche01a9bc2016-03-12 20:11:22 -0700117void HlslParseContext::setLimits(const TBuiltInResource& r)
118{
119 resources = r;
120 intermediate.setLimits(resources);
121}
122
123//
124// Parse an array of strings using the parser in HlslRules.
125//
126// Returns true for successful acceptance of the shader, false if any errors.
127//
128bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError)
129{
130 currentScanner = &input;
131 ppContext.setInput(input, versionWillBeError);
132
John Kesseniche01a9bc2016-03-12 20:11:22 -0700133 HlslScanContext scanContext(*this, ppContext);
134 HlslGrammar grammar(scanContext, *this);
John Kessenich7f702122016-09-15 22:49:31 -0600135 if (!grammar.parse()) {
John Kessenich219b0252016-08-23 17:51:13 -0600136 // Print a message formated such that if you click on the message it will take you right to
137 // the line through most UIs.
dankbakerafe6e9c2016-08-21 12:29:08 -0400138 const glslang::TSourceLoc& sourceLoc = input.getSourceLoc();
John Kessenich2ceec682017-07-28 16:20:13 -0600139 infoSink.info << sourceLoc.name << "(" << sourceLoc.line << "): error at column " << sourceLoc.column
140 << ", HLSL parsing failed.\n";
John Kessenich142785f2016-09-19 14:56:55 -0600141 ++numErrors;
John Kessenich7f702122016-09-15 22:49:31 -0600142 return false;
dankbakerafe6e9c2016-08-21 12:29:08 -0400143 }
John Kessenich7f702122016-09-15 22:49:31 -0600144
John Kessenichd3f11222016-11-05 10:15:53 -0600145 finish();
146
John Kesseniche01a9bc2016-03-12 20:11:22 -0700147 return numErrors == 0;
148}
149
steve-lunarg07830e82016-10-10 10:00:14 -0600150//
151// Return true if this l-value node should be converted in some manner.
152// For instance: turning a load aggregate into a store in an l-value.
153//
steve-lunarg90707962016-10-07 19:35:40 -0600154bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const
155{
John Kessenichf3150742017-06-02 16:28:39 -0600156 if (node == nullptr || node->getAsTyped() == nullptr)
steve-lunarg90707962016-10-07 19:35:40 -0600157 return false;
158
159 const TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
steve-lunargcd6829b2016-12-28 10:03:58 -0700160 const TIntermBinary* lhsAsBinary = node->getAsBinaryNode();
161
162 // If it's a swizzled/indexed aggregate, look at the left node instead.
163 if (lhsAsBinary != nullptr &&
164 (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect))
165 lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
steve-lunarg90707962016-10-07 19:35:40 -0600166 if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad)
167 return true;
168
169 return false;
170}
171
John Kessenich2ceec682017-07-28 16:20:13 -0600172void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName,
173 TTypeList* newTypeList)
John Kessenich88c44642017-02-03 14:06:36 -0700174{
John Kessenich65ee2302017-02-06 18:44:52 -0700175 newTypeList = nullptr;
John Kessenichbf472862017-02-05 20:27:30 -0700176 correctUniform(memberType.getQualifier());
John Kessenich65ee2302017-02-06 18:44:52 -0700177 if (memberType.isStruct()) {
178 auto it = ioTypeMap.find(memberType.getStruct());
179 if (it != ioTypeMap.end() && it->second.uniform)
180 newTypeList = it->second.uniform;
181 }
182 TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList);
John Kessenich88c44642017-02-03 14:06:36 -0700183}
184
steve-lunarg0de16da2016-10-08 10:54:52 -0600185//
steve-lunarg4f2da272016-10-10 15:24:57 -0600186// Return a TLayoutFormat corresponding to the given texture type.
187//
188TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType)
189{
LoopDawg5ee05892017-07-31 13:41:42 -0600190 if (txType.isStruct()) {
191 // TODO: implement.
192 error(loc, "unimplemented: structure type in image or buffer", "", "");
193 return ElfNone;
194 }
195
steve-lunarg4f2da272016-10-10 15:24:57 -0600196 const int components = txType.getVectorSize();
LoopDawg5ee05892017-07-31 13:41:42 -0600197 const TBasicType txBasicType = txType.getBasicType();
steve-lunarg4f2da272016-10-10 15:24:57 -0600198
baldurkca735702016-10-28 17:57:25 +0200199 const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat {
steve-lunargcce8d482016-10-14 18:36:42 -0600200 if (intermediate.getNoStorageFormat())
201 return ElfNone;
202
steve-lunarg4f2da272016-10-10 15:24:57 -0600203 return components == 1 ? v1 :
204 components == 2 ? v2 : v4;
205 };
206
LoopDawg5ee05892017-07-31 13:41:42 -0600207 switch (txBasicType) {
steve-lunarg8b0227c2016-10-14 16:40:32 -0600208 case EbtFloat: return selectFormat(ElfR32f, ElfRg32f, ElfRgba32f);
209 case EbtInt: return selectFormat(ElfR32i, ElfRg32i, ElfRgba32i);
210 case EbtUint: return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui);
steve-lunarg4f2da272016-10-10 15:24:57 -0600211 default:
212 error(loc, "unknown basic type in image format", "", "");
213 return ElfNone;
214 }
215}
216
217//
steve-lunarg0de16da2016-10-08 10:54:52 -0600218// Both test and if necessary, spit out an error, to see if the node is really
219// an l-value that can be operated on this way.
220//
221// Returns true if there was an error.
222//
223bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
224{
225 if (shouldConvertLValue(node)) {
226 // if we're writing to a texture, it must be an RW form.
227
228 TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
229 TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -0700230
steve-lunarg0de16da2016-10-08 10:54:52 -0600231 if (!object->getType().getSampler().isImage()) {
232 error(loc, "operator[] on a non-RW texture must be an r-value", "", "");
233 return true;
234 }
235 }
236
John Kessenich15fa7ef2017-09-07 04:33:11 -0600237 // We tolerate samplers as l-values, even though they are nominally
238 // illegal, because we expect a later optimization to eliminate them.
239 if (node->getType().getBasicType() == EbtSampler) {
240 intermediate.setNeedsLegalization();
241 return false;
242 }
243
steve-lunarg0de16da2016-10-08 10:54:52 -0600244 // Let the base class check errors
245 return TParseContextBase::lValueErrorCheck(loc, op, node);
246}
steve-lunarg90707962016-10-07 19:35:40 -0600247
248//
249// This function handles l-value conversions and verifications. It uses, but is not synonymous
250// with lValueErrorCheck. That function accepts an l-value directly, while this one must be
251// given the surrounding tree - e.g, with an assignment, so we can convert the assign into a
252// series of other image operations.
253//
254// Most things are passed through unmodified, except for error checking.
John Kessenichecba76f2017-01-06 00:34:48 -0700255//
John Kessenichf3150742017-06-02 16:28:39 -0600256TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node)
steve-lunarg90707962016-10-07 19:35:40 -0600257{
steve-lunarg07830e82016-10-10 10:00:14 -0600258 if (node == nullptr)
259 return nullptr;
260
steve-lunarg90707962016-10-07 19:35:40 -0600261 TIntermBinary* nodeAsBinary = node->getAsBinaryNode();
262 TIntermUnary* nodeAsUnary = node->getAsUnaryNode();
263 TIntermAggregate* sequence = nullptr;
264
265 TIntermTyped* lhs = nodeAsUnary ? nodeAsUnary->getOperand() :
266 nodeAsBinary ? nodeAsBinary->getLeft() :
267 nullptr;
268
steve-lunarg90707962016-10-07 19:35:40 -0600269 // Early bail out if there is no conversion to apply
270 if (!shouldConvertLValue(lhs)) {
steve-lunarg0de16da2016-10-08 10:54:52 -0600271 if (lhs != nullptr)
272 if (lValueErrorCheck(loc, op, lhs))
273 return nullptr;
steve-lunarg90707962016-10-07 19:35:40 -0600274 return node;
275 }
276
277 // *** If we get here, we're going to apply some conversion to an l-value.
278
279 // Helper to create a load.
280 const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) {
281 TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad);
282 loadOp->setLoc(loc);
283 loadOp->getSequence().push_back(object);
284 loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode()));
285 loadOp->setType(derefType);
286
287 sequence = intermediate.growAggregate(sequence,
288 intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc),
289 loc);
290 };
291
292 // Helper to create a store.
293 const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) {
294 TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore);
295 storeOp->getSequence().push_back(object);
296 storeOp->getSequence().push_back(coord);
297 storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp));
298 storeOp->setLoc(loc);
299 storeOp->setType(TType(EbtVoid));
300
301 sequence = intermediate.growAggregate(sequence, storeOp);
302 };
303
304 // Helper to create an assign.
steve-lunargb3da8a92016-10-12 12:38:12 -0600305 const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) {
steve-lunarg90707962016-10-07 19:35:40 -0600306 sequence = intermediate.growAggregate(sequence,
steve-lunargb3da8a92016-10-12 12:38:12 -0600307 intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()),
steve-lunarg90707962016-10-07 19:35:40 -0600308 loc);
309 };
310
311 // Helper to complete sequence by adding trailing variable, so we evaluate to the right value.
baldurkca735702016-10-28 17:57:25 +0200312 const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* {
steve-lunarg90707962016-10-07 19:35:40 -0600313 // Add a trailing use of the temp, so the sequence returns the proper value.
314 sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp));
315 sequence->setOperator(EOpSequence);
316 sequence->setLoc(loc);
317 sequence->setType(derefType);
318
319 return sequence;
320 };
321
322 // Helper to add unary op
steve-lunargb3da8a92016-10-12 12:38:12 -0600323 const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) {
steve-lunarg90707962016-10-07 19:35:40 -0600324 sequence = intermediate.growAggregate(sequence,
steve-lunargb3da8a92016-10-12 12:38:12 -0600325 intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc,
326 rhsTmp->getType()),
steve-lunarg90707962016-10-07 19:35:40 -0600327 loc);
328 };
329
steve-lunargcd6829b2016-12-28 10:03:58 -0700330 // Return true if swizzle or index writes all components of the given variable.
331 const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool {
332 if (swizzle == nullptr) // not a swizzle or index
333 return true;
334
335 // Track which components are being set.
336 std::array<bool, 4> compIsSet;
337 compIsSet.fill(false);
338
339 const TIntermConstantUnion* asConst = swizzle->getRight()->getAsConstantUnion();
340 const TIntermAggregate* asAggregate = swizzle->getRight()->getAsAggregate();
341
342 // This could be either a direct index, or a swizzle.
343 if (asConst) {
344 compIsSet[asConst->getConstArray()[0].getIConst()] = true;
345 } else if (asAggregate) {
346 const TIntermSequence& seq = asAggregate->getSequence();
347 for (int comp=0; comp<int(seq.size()); ++comp)
348 compIsSet[seq[comp]->getAsConstantUnion()->getConstArray()[0].getIConst()] = true;
349 } else {
350 assert(0);
351 }
352
353 // Return true if all components are being set by the index or swizzle
354 return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(),
355 [](bool isSet) { return isSet; } );
356 };
357
steve-lunargcd6829b2016-12-28 10:03:58 -0700358 // Create swizzle matching input swizzle
359 const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* {
360 if (swizzle)
361 return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType());
362 else
363 return var;
364 };
365
366 TIntermBinary* lhsAsBinary = lhs->getAsBinaryNode();
steve-lunarg90707962016-10-07 19:35:40 -0600367 TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate();
steve-lunargcd6829b2016-12-28 10:03:58 -0700368 bool lhsIsSwizzle = false;
369
370 // If it's a swizzled L-value, remember the swizzle, and use the LHS.
371 if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) {
372 lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
373 lhsIsSwizzle = true;
374 }
John Kessenichecba76f2017-01-06 00:34:48 -0700375
steve-lunarg90707962016-10-07 19:35:40 -0600376 TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
377 TIntermTyped* coord = lhsAsAggregate->getSequence()[1]->getAsTyped();
378
steve-lunarg8b0227c2016-10-14 16:40:32 -0600379 const TSampler& texSampler = object->getType().getSampler();
380
LoopDawg5ee05892017-07-31 13:41:42 -0600381 TType objDerefType;
382 getTextureReturnType(texSampler, objDerefType);
steve-lunarg90707962016-10-07 19:35:40 -0600383
384 if (nodeAsBinary) {
385 TIntermTyped* rhs = nodeAsBinary->getRight();
386 const TOperator assignOp = nodeAsBinary->getOp();
387
388 bool isModifyOp = false;
389
390 switch (assignOp) {
391 case EOpAddAssign:
392 case EOpSubAssign:
393 case EOpMulAssign:
394 case EOpVectorTimesMatrixAssign:
395 case EOpVectorTimesScalarAssign:
396 case EOpMatrixTimesScalarAssign:
397 case EOpMatrixTimesMatrixAssign:
398 case EOpDivAssign:
399 case EOpModAssign:
400 case EOpAndAssign:
401 case EOpInclusiveOrAssign:
402 case EOpExclusiveOrAssign:
403 case EOpLeftShiftAssign:
404 case EOpRightShiftAssign:
405 isModifyOp = true;
406 // fall through...
407 case EOpAssign:
408 {
John Kessenich2ceec682017-07-28 16:20:13 -0600409 // Since this is an lvalue, we'll convert an image load to a sequence like this
410 // (to still provide the value):
steve-lunarg90707962016-10-07 19:35:40 -0600411 // OpSequence
412 // OpImageStore(object, lhs, rhs)
413 // rhs
John Kessenich2ceec682017-07-28 16:20:13 -0600414 // But if it's not a simple symbol RHS (say, a fn call), we don't want to duplicate the RHS,
415 // so we'll convert instead to this:
steve-lunarg90707962016-10-07 19:35:40 -0600416 // OpSequence
417 // rhsTmp = rhs
418 // OpImageStore(object, coord, rhsTmp)
419 // rhsTmp
420 // If this is a read-modify-write op, like +=, we issue:
421 // OpSequence
422 // coordtmp = load's param1
423 // rhsTmp = OpImageLoad(object, coordTmp)
John Kessenich64285c92017-01-05 10:45:32 -0700424 // rhsTmp op= rhs
steve-lunarg90707962016-10-07 19:35:40 -0600425 // OpImageStore(object, coordTmp, rhsTmp)
426 // rhsTmp
steve-lunargcd6829b2016-12-28 10:03:58 -0700427 //
428 // If the lvalue is swizzled, we apply that when writing the temp variable, like so:
429 // ...
430 // rhsTmp.some_swizzle = ...
431 // For partial writes, an error is generated.
steve-lunarg90707962016-10-07 19:35:40 -0600432
433 TIntermSymbol* rhsTmp = rhs->getAsSymbolNode();
434 TIntermTyped* coordTmp = coord;
John Kessenichecba76f2017-01-06 00:34:48 -0700435
steve-lunargcd6829b2016-12-28 10:03:58 -0700436 if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) {
John Kessenich82460b52017-04-04 11:47:42 -0600437 rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
steve-lunarg90707962016-10-07 19:35:40 -0600438
steve-lunargcd6829b2016-12-28 10:03:58 -0700439 // Partial updates not yet supported
440 if (!writesAllComponents(rhsTmp, lhsAsBinary)) {
441 error(loc, "unimplemented: partial image updates", "", "");
442 }
443
steve-lunarg90707962016-10-07 19:35:40 -0600444 // Assign storeTemp = rhs
445 if (isModifyOp) {
446 // We have to make a temp var for the coordinate, to avoid evaluating it twice.
John Kessenich82460b52017-04-04 11:47:42 -0600447 coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
steve-lunargb3da8a92016-10-12 12:38:12 -0600448 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600449 makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
450 }
451
452 // rhsTmp op= rhs.
steve-lunargcd6829b2016-12-28 10:03:58 -0700453 makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs);
steve-lunarg90707962016-10-07 19:35:40 -0600454 }
John Kessenichecba76f2017-01-06 00:34:48 -0700455
steve-lunarg90707962016-10-07 19:35:40 -0600456 makeStore(object, coordTmp, rhsTmp); // add a store
457 return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
458 }
459
460 default:
461 break;
462 }
463 }
464
465 if (nodeAsUnary) {
466 const TOperator assignOp = nodeAsUnary->getOp();
467
468 switch (assignOp) {
469 case EOpPreIncrement:
470 case EOpPreDecrement:
471 {
472 // We turn this into:
473 // OpSequence
474 // coordtmp = load's param1
475 // rhsTmp = OpImageLoad(object, coordTmp)
476 // rhsTmp op
477 // OpImageStore(object, coordTmp, rhsTmp)
478 // rhsTmp
John Kessenichecba76f2017-01-06 00:34:48 -0700479
John Kessenich82460b52017-04-04 11:47:42 -0600480 TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
481 TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
John Kessenichecba76f2017-01-06 00:34:48 -0700482
steve-lunargb3da8a92016-10-12 12:38:12 -0600483 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600484 makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
steve-lunargb3da8a92016-10-12 12:38:12 -0600485 makeUnary(assignOp, rhsTmp); // op rhsTmp
steve-lunarg90707962016-10-07 19:35:40 -0600486 makeStore(object, coordTmp, rhsTmp); // OpImageStore(object, coordTmp, rhsTmp)
487 return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
488 }
489
490 case EOpPostIncrement:
491 case EOpPostDecrement:
492 {
493 // We turn this into:
494 // OpSequence
495 // coordtmp = load's param1
496 // rhsTmp1 = OpImageLoad(object, coordTmp)
497 // rhsTmp2 = rhsTmp1
498 // rhsTmp2 op
499 // OpImageStore(object, coordTmp, rhsTmp2)
500 // rhsTmp1 (pre-op value)
John Kessenich82460b52017-04-04 11:47:42 -0600501 TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre", objDerefType);
502 TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType);
503 TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
steve-lunarg90707962016-10-07 19:35:40 -0600504
steve-lunargb3da8a92016-10-12 12:38:12 -0600505 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600506 makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp)
steve-lunargb3da8a92016-10-12 12:38:12 -0600507 makeBinary(EOpAssign, rhsTmp2, rhsTmp1); // rhsTmp2 = rhsTmp1
508 makeUnary(assignOp, rhsTmp2); // rhsTmp op
steve-lunarg90707962016-10-07 19:35:40 -0600509 makeStore(object, coordTmp, rhsTmp2); // OpImageStore(object, coordTmp, rhsTmp2)
510 return finishSequence(rhsTmp1, objDerefType); // return rhsTmp from sequence
steve-lunarg90707962016-10-07 19:35:40 -0600511 }
John Kessenichecba76f2017-01-06 00:34:48 -0700512
steve-lunarg90707962016-10-07 19:35:40 -0600513 default:
514 break;
515 }
516 }
517
steve-lunarg0de16da2016-10-08 10:54:52 -0600518 if (lhs)
519 if (lValueErrorCheck(loc, op, lhs))
520 return nullptr;
John Kessenichecba76f2017-01-06 00:34:48 -0700521
steve-lunarg90707962016-10-07 19:35:40 -0600522 return node;
523}
524
John Kesseniche01a9bc2016-03-12 20:11:22 -0700525void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
526{
527 if (pragmaCallback)
528 pragmaCallback(loc.line, tokens);
529
530 if (tokens.size() == 0)
531 return;
LoopDawg6a264be2017-08-07 12:08:50 -0600532
533 // These pragmas are case insensitive in HLSL, so we'll compare in lower case.
534 TVector<TString> lowerTokens = tokens;
535
536 for (auto it = lowerTokens.begin(); it != lowerTokens.end(); ++it)
537 std::transform(it->begin(), it->end(), it->begin(), ::tolower);
538
539 // Handle pack_matrix
540 if (tokens.size() == 4 && lowerTokens[0] == "pack_matrix" && tokens[1] == "(" && tokens[3] == ")") {
541 // Note that HLSL semantic order is Mrc, not Mcr like SPIR-V, so we reverse the sense.
542 // Row major becomes column major and vice versa.
543
544 if (lowerTokens[2] == "row_major") {
545 globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmColumnMajor;
546 } else if (lowerTokens[2] == "column_major") {
547 globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
548 } else {
549 // unknown majorness strings are treated as (HLSL column major)==(SPIR-V row major)
550 warn(loc, "unknown pack_matrix pragma value", tokens[2].c_str(), "");
551 globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
552 }
553 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700554}
555
556//
John Kessenichc142c882017-01-13 19:34:22 -0700557// Look at a '.' matrix selector string and change it into components
John Kessenich001dfa12017-01-12 16:51:18 -0700558// for a matrix. There are two types:
559//
560// _21 second row, first column (one based)
561// _m21 third row, second column (zero based)
562//
563// Returns true if there is no error.
564//
John Kessenichc142c882017-01-13 19:34:22 -0700565bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows,
566 TSwizzleSelectors<TMatrixSelector>& components)
John Kessenich001dfa12017-01-12 16:51:18 -0700567{
John Kessenich33dadd12017-01-13 20:22:00 -0700568 int startPos[MaxSwizzleSelectors];
John Kessenich001dfa12017-01-12 16:51:18 -0700569 int numComps = 0;
570 TString compString = fields;
571
572 // Find where each component starts,
573 // recording the first character position after the '_'.
574 for (size_t c = 0; c < compString.size(); ++c) {
575 if (compString[c] == '_') {
John Kessenich33dadd12017-01-13 20:22:00 -0700576 if (numComps >= MaxSwizzleSelectors) {
John Kessenich001dfa12017-01-12 16:51:18 -0700577 error(loc, "matrix component swizzle has too many components", compString.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -0700578 return false;
579 }
John Kessenich001dfa12017-01-12 16:51:18 -0700580 if (c > compString.size() - 3 ||
581 ((compString[c+1] == 'm' || compString[c+1] == 'M') && c > compString.size() - 4)) {
582 error(loc, "matrix component swizzle missing", compString.c_str(), "");
583 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700584 }
Graham Wihlidal6f332f32017-02-17 19:05:14 +0100585 startPos[numComps++] = (int)c + 1;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700586 }
John Kessenich001dfa12017-01-12 16:51:18 -0700587 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700588
John Kessenich001dfa12017-01-12 16:51:18 -0700589 // Process each component
590 for (int i = 0; i < numComps; ++i) {
591 int pos = startPos[i];
592 int bias = -1;
593 if (compString[pos] == 'm' || compString[pos] == 'M') {
594 bias = 0;
595 ++pos;
596 }
John Kessenichc142c882017-01-13 19:34:22 -0700597 TMatrixSelector comp;
John Kessenich001dfa12017-01-12 16:51:18 -0700598 comp.coord1 = compString[pos+0] - '0' + bias;
599 comp.coord2 = compString[pos+1] - '0' + bias;
600 if (comp.coord1 < 0 || comp.coord1 >= cols) {
601 error(loc, "matrix row component out of range", compString.c_str(), "");
602 return false;
603 }
604 if (comp.coord2 < 0 || comp.coord2 >= rows) {
605 error(loc, "matrix column component out of range", compString.c_str(), "");
606 return false;
607 }
608 components.push_back(comp);
609 }
610
611 return true;
612}
613
614// If the 'comps' express a column of a matrix,
615// return the column. Column means the first coords all match.
616//
617// Otherwise, return -1.
618//
John Kessenichc142c882017-01-13 19:34:22 -0700619int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>& selector)
John Kessenich001dfa12017-01-12 16:51:18 -0700620{
621 int col = -1;
622
623 // right number of comps?
John Kessenichc142c882017-01-13 19:34:22 -0700624 if (selector.size() != rows)
John Kessenich001dfa12017-01-12 16:51:18 -0700625 return -1;
626
627 // all comps in the same column?
628 // rows in order?
John Kessenichc142c882017-01-13 19:34:22 -0700629 col = selector[0].coord1;
John Kessenich001dfa12017-01-12 16:51:18 -0700630 for (int i = 0; i < rows; ++i) {
John Kessenichc142c882017-01-13 19:34:22 -0700631 if (col != selector[i].coord1)
John Kessenich001dfa12017-01-12 16:51:18 -0700632 return -1;
John Kessenichc142c882017-01-13 19:34:22 -0700633 if (i != selector[i].coord2)
John Kessenich001dfa12017-01-12 16:51:18 -0700634 return -1;
635 }
636
637 return col;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700638}
639
640//
John Kesseniche01a9bc2016-03-12 20:11:22 -0700641// Handle seeing a variable identifier in the grammar.
642//
John Kessenichf4ba25e2017-03-21 18:35:04 -0600643TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700644{
John Kessenich37789792017-03-21 23:56:40 -0600645 int thisDepth;
646 TSymbol* symbol = symbolTable.find(*string, thisDepth);
John Kesseniche6e74942016-06-11 16:43:14 -0600647 if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
648 error(loc, "expected symbol, not user-defined type", string->c_str(), "");
649 return nullptr;
650 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700651
652 // Error check for requiring specific extensions present.
653 if (symbol && symbol->getNumExtensions())
654 requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str());
655
John Kessenich37789792017-03-21 23:56:40 -0600656 const TVariable* variable = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700657 const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr;
John Kesseniche6e74942016-06-11 16:43:14 -0600658 TIntermTyped* node = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700659 if (anon) {
John Kessenich37789792017-03-21 23:56:40 -0600660 // It was a member of an anonymous container, which could be a 'this' structure.
John Kesseniche01a9bc2016-03-12 20:11:22 -0700661
662 // Create a subtree for its dereference.
John Kessenich37789792017-03-21 23:56:40 -0600663 if (thisDepth > 0) {
664 variable = getImplicitThis(thisDepth);
665 if (variable == nullptr)
666 error(loc, "cannot access member variables (static member function?)", "this", "");
667 }
668 if (variable == nullptr)
669 variable = anon->getAnonContainer().getAsVariable();
670
John Kesseniche01a9bc2016-03-12 20:11:22 -0700671 TIntermTyped* container = intermediate.addSymbol(*variable, loc);
672 TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc);
673 node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc);
674
675 node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
676 if (node->getType().hiddenMember())
677 error(loc, "member of nameless block was not redeclared", string->c_str(), "");
678 } else {
679 // Not a member of an anonymous container.
680
681 // The symbol table search was done in the lexical phase.
682 // See if it was a variable.
683 variable = symbol ? symbol->getAsVariable() : nullptr;
684 if (variable) {
685 if ((variable->getType().getBasicType() == EbtBlock ||
686 variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) {
687 error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), "");
688 variable = nullptr;
689 }
690 } else {
691 if (symbol)
692 error(loc, "variable name expected", string->c_str(), "");
693 }
694
695 // Recovery, if it wasn't found or was not a variable.
John Kessenichf02c8e62017-06-19 16:25:44 -0600696 if (variable == nullptr) {
John Kessenich1e275c82016-12-14 17:02:32 -0700697 error(loc, "unknown variable", string->c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -0700698 variable = new TVariable(string, TType(EbtVoid));
John Kessenich1e275c82016-12-14 17:02:32 -0700699 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700700
701 if (variable->getType().getQualifier().isFrontEndConstant())
702 node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
703 else
704 node = intermediate.addSymbol(*variable, loc);
705 }
706
707 if (variable->getType().getQualifier().isIo())
708 intermediate.addIoAccessed(*string);
709
710 return node;
711}
712
713//
steve-lunarg6b43d272016-10-06 20:12:24 -0600714// Handle operator[] on any objects it applies to. Currently:
715// Textures
716// Buffers
717//
718TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
719{
720 // handle r-value operator[] on textures and images. l-values will be processed later.
721 if (base->getType().getBasicType() == EbtSampler && !base->isArray()) {
722 const TSampler& sampler = base->getType().getSampler();
723 if (sampler.isImage() || sampler.isTexture()) {
LoopDawg726bf962017-05-12 17:14:31 -0600724 if (! mipsOperatorMipArg.empty() && mipsOperatorMipArg.back().mipLevel == nullptr) {
725 // The first operator[] to a .mips[] sequence is the mip level. We'll remember it.
726 mipsOperatorMipArg.back().mipLevel = index;
727 return base; // next [] index is to the same base.
728 } else {
729 TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch);
steve-lunarg6b43d272016-10-06 20:12:24 -0600730
LoopDawg5ee05892017-07-31 13:41:42 -0600731 TType sampReturnType;
732 getTextureReturnType(sampler, sampReturnType);
733
734 load->setType(sampReturnType);
LoopDawg726bf962017-05-12 17:14:31 -0600735 load->setLoc(loc);
736 load->getSequence().push_back(base);
737 load->getSequence().push_back(index);
steve-lunarg07830e82016-10-10 10:00:14 -0600738
LoopDawg726bf962017-05-12 17:14:31 -0600739 // Textures need a MIP. If we saw one go by, use it. Otherwise, use zero.
740 if (sampler.isTexture()) {
741 if (! mipsOperatorMipArg.empty()) {
742 load->getSequence().push_back(mipsOperatorMipArg.back().mipLevel);
743 mipsOperatorMipArg.pop_back();
744 } else {
745 load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true));
746 }
747 }
steve-lunarg07830e82016-10-10 10:00:14 -0600748
LoopDawg726bf962017-05-12 17:14:31 -0600749 return load;
750 }
steve-lunarg6b43d272016-10-06 20:12:24 -0600751 }
752 }
753
steve-lunargdd8287a2017-02-23 18:04:12 -0700754 // Handle operator[] on structured buffers: this indexes into the array element of the buffer.
755 // indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO).
756 TIntermTyped* sbArray = indexStructBufferContent(loc, base);
757 if (sbArray != nullptr) {
758 if (sbArray == nullptr)
759 return nullptr;
760
761 // Now we'll apply the [] index to that array
762 const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
763
764 TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc);
765 const TType derefType(sbArray->getType(), 0);
766 element->setType(derefType);
767 return element;
768 }
769
steve-lunarg6b43d272016-10-06 20:12:24 -0600770 return nullptr;
771}
772
773//
steve-lunargf8203a02017-04-20 09:00:56 -0600774// Cast index value to a uint if it isn't already (for operator[], load indexes, etc)
775TIntermTyped* HlslParseContext::makeIntegerIndex(TIntermTyped* index)
776{
777 const TBasicType indexBasicType = index->getType().getBasicType();
778 const int vecSize = index->getType().getVectorSize();
779
780 // We can use int types directly as the index
781 if (indexBasicType == EbtInt || indexBasicType == EbtUint ||
782 indexBasicType == EbtInt64 || indexBasicType == EbtUint64)
783 return index;
784
785 // Cast index to unsigned integer if it isn't one.
786 return intermediate.addConversion(EOpConstructUint, TType(EbtUint, EvqTemporary, vecSize), index);
787}
788
789//
John Kesseniche01a9bc2016-03-12 20:11:22 -0700790// Handle seeing a base[index] dereference in the grammar.
791//
792TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
793{
steve-lunargf8203a02017-04-20 09:00:56 -0600794 index = makeIntegerIndex(index);
steve-lunarg2efd6c62017-04-06 20:22:20 -0600795
796 if (index == nullptr) {
John Kessenichf0bc5982017-06-20 13:19:53 -0600797 error(loc, " unknown index type ", "", "");
steve-lunarg2efd6c62017-04-06 20:22:20 -0600798 return nullptr;
799 }
800
steve-lunargf8203a02017-04-20 09:00:56 -0600801 TIntermTyped* result = handleBracketOperator(loc, base, index);
802
803 if (result != nullptr)
804 return result; // it was handled as an operator[]
805
steve-lunargcf43e662016-09-22 14:35:23 -0600806 bool flattened = false;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700807 int indexValue = 0;
John Kessenichbdbbc682017-09-14 19:45:28 -0600808 if (index->getQualifier().isFrontEndConstant())
John Kesseniche01a9bc2016-03-12 20:11:22 -0700809 indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
John Kesseniche01a9bc2016-03-12 20:11:22 -0700810
811 variableCheck(base);
812 if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) {
813 if (base->getAsSymbolNode())
John Kessenich2ceec682017-07-28 16:20:13 -0600814 error(loc, " left of '[' is not of type array, matrix, or vector ",
815 base->getAsSymbolNode()->getName().c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -0700816 else
817 error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
John Kessenichbdbbc682017-09-14 19:45:28 -0600818 } else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) {
819 // both base and index are front-end constants
820 checkIndex(loc, base->getType(), indexValue);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700821 return intermediate.foldDereference(base, indexValue, loc);
John Kessenichbdbbc682017-09-14 19:45:28 -0600822 } else {
John Kesseniche01a9bc2016-03-12 20:11:22 -0700823 // at least one of base and index is variable...
824
John Kessenich8bcdf2e2017-07-30 16:54:02 -0600825 if (base->getAsSymbolNode() && wasFlattened(base)) {
steve-lunarge0b9deb2016-09-16 13:26:37 -0600826 if (index->getQualifier().storage != EvqConst)
steve-lunarg132d3312016-12-19 15:48:01 -0700827 error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), "");
steve-lunarge0b9deb2016-09-16 13:26:37 -0600828
steve-lunarga2e75312016-12-14 15:22:25 -0700829 result = flattenAccess(base, indexValue);
steve-lunargcf43e662016-09-22 14:35:23 -0600830 flattened = (result != base);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700831 } else {
John Kessenichbdbbc682017-09-14 19:45:28 -0600832 if (index->getQualifier().isFrontEndConstant()) {
steve-lunarge0b9deb2016-09-16 13:26:37 -0600833 if (base->getType().isImplicitlySizedArray())
834 updateImplicitArraySize(loc, base, indexValue);
John Kessenichbdbbc682017-09-14 19:45:28 -0600835 else
836 checkIndex(loc, base->getType(), indexValue);
steve-lunarge0b9deb2016-09-16 13:26:37 -0600837 result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
838 } else {
839 result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
840 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700841 }
842 }
843
844 if (result == nullptr) {
845 // Insert dummy error-recovery result
846 result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
847 } else {
steve-lunargcf43e662016-09-22 14:35:23 -0600848 // If the array reference was flattened, it has the correct type. E.g, if it was
849 // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps.
850 // In that case, we preserve the qualifiers.
851 if (!flattened) {
852 // Insert valid dereferenced result
853 TType newType(base->getType(), 0); // dereferenced type
854 if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
855 newType.getQualifier().storage = EvqConst;
856 else
857 newType.getQualifier().storage = EvqTemporary;
858 result->setType(newType);
859 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700860 }
861
862 return result;
863}
864
John Kesseniche01a9bc2016-03-12 20:11:22 -0700865// Handle seeing a binary node with a math operation.
John Kessenich2ceec682017-07-28 16:20:13 -0600866TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op,
867 TIntermTyped* left, TIntermTyped* right)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700868{
869 TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
John Kessenichf02c8e62017-06-19 16:25:44 -0600870 if (result == nullptr)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700871 binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
872
873 return result;
874}
875
876// Handle seeing a unary node with a math operation.
John Kessenich2ceec682017-07-28 16:20:13 -0600877TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op,
878 TIntermTyped* childNode)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700879{
880 TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc);
881
882 if (result)
883 return result;
884 else
885 unaryOpError(loc, str, childNode->getCompleteString());
886
887 return childNode;
888}
steve-lunarg5da1f032017-02-12 17:50:28 -0700889//
890// Return true if the name is a struct buffer method
891//
892bool HlslParseContext::isStructBufferMethod(const TString& name) const
893{
894 return
895 name == "GetDimensions" ||
896 name == "Load" ||
897 name == "Load2" ||
898 name == "Load3" ||
899 name == "Load4" ||
900 name == "Store" ||
901 name == "Store2" ||
902 name == "Store3" ||
903 name == "Store4" ||
904 name == "InterlockedAdd" ||
905 name == "InterlockedAnd" ||
906 name == "InterlockedCompareExchange" ||
907 name == "InterlockedCompareStore" ||
908 name == "InterlockedExchange" ||
909 name == "InterlockedMax" ||
910 name == "InterlockedMin" ||
911 name == "InterlockedOr" ||
steve-lunarg8e26feb2017-04-10 08:19:21 -0600912 name == "InterlockedXor" ||
913 name == "IncrementCounter" ||
914 name == "DecrementCounter" ||
915 name == "Append" ||
916 name == "Consume";
steve-lunarg5da1f032017-02-12 17:50:28 -0700917}
918
919//
John Kessenich516d92d2017-03-08 20:09:03 -0700920// Handle seeing a base.field dereference in the grammar, where 'field' is a
921// swizzle or member variable.
John Kesseniche01a9bc2016-03-12 20:11:22 -0700922//
923TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field)
924{
925 variableCheck(base);
926
John Kesseniche01a9bc2016-03-12 20:11:22 -0700927 if (base->isArray()) {
928 error(loc, "cannot apply to an array:", ".", field.c_str());
John Kesseniche01a9bc2016-03-12 20:11:22 -0700929 return base;
930 }
931
John Kesseniche01a9bc2016-03-12 20:11:22 -0700932 TIntermTyped* result = base;
LoopDawg726bf962017-05-12 17:14:31 -0600933
934 if (base->getType().getBasicType() == EbtSampler) {
935 // Handle .mips[mipid][pos] operation on textures
936 const TSampler& sampler = base->getType().getSampler();
937 if (sampler.isTexture() && field == "mips") {
938 // Push a null to signify that we expect a mip level under operator[] next.
939 mipsOperatorMipArg.push_back(tMipsOperatorData(loc, nullptr));
940 // Keep 'result' pointing to 'base', since we expect an operator[] to go by next.
941 } else {
942 if (field == "mips")
John Kessenich2ceec682017-07-28 16:20:13 -0600943 error(loc, "unexpected texture type for .mips[][] operator:",
944 base->getType().getCompleteString().c_str(), "");
LoopDawg726bf962017-05-12 17:14:31 -0600945 else
John Kessenich2ceec682017-07-28 16:20:13 -0600946 error(loc, "unexpected operator on texture type:", field.c_str(),
947 base->getType().getCompleteString().c_str());
LoopDawg726bf962017-05-12 17:14:31 -0600948 }
949 } else if (base->isVector() || base->isScalar()) {
John Kessenichc142c882017-01-13 19:34:22 -0700950 TSwizzleSelectors<TVectorSelector> selectors;
951 parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700952
953 if (base->isScalar()) {
John Kessenichc142c882017-01-13 19:34:22 -0700954 if (selectors.size() == 1)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700955 return result;
956 else {
John Kessenichc142c882017-01-13 19:34:22 -0700957 TType type(base->getBasicType(), EvqTemporary, selectors.size());
John Kessenicha26a5172016-07-28 15:29:35 -0600958 return addConstructor(loc, base, type);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700959 }
960 }
John Kessenich7d01bd62016-09-02 22:21:25 -0600961 if (base->getVectorSize() == 1) {
962 TType scalarType(base->getBasicType(), EvqTemporary, 1);
John Kessenichc142c882017-01-13 19:34:22 -0700963 if (selectors.size() == 1)
John Kessenich7d01bd62016-09-02 22:21:25 -0600964 return addConstructor(loc, base, scalarType);
965 else {
John Kessenichc142c882017-01-13 19:34:22 -0700966 TType vectorType(base->getBasicType(), EvqTemporary, selectors.size());
John Kessenich7d01bd62016-09-02 22:21:25 -0600967 return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType);
968 }
969 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700970
971 if (base->getType().getQualifier().isFrontEndConstant())
John Kessenichc142c882017-01-13 19:34:22 -0700972 result = intermediate.foldSwizzle(base, selectors, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700973 else {
John Kessenichc142c882017-01-13 19:34:22 -0700974 if (selectors.size() == 1) {
975 TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700976 result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
John Kessenichf6640762016-08-01 19:44:00 -0600977 result->setType(TType(base->getBasicType(), EvqTemporary));
John Kesseniche01a9bc2016-03-12 20:11:22 -0700978 } else {
John Kessenichc142c882017-01-13 19:34:22 -0700979 TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700980 result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
John Kessenich2ceec682017-07-28 16:20:13 -0600981 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
982 selectors.size()));
John Kesseniche01a9bc2016-03-12 20:11:22 -0700983 }
984 }
John Kessenich001dfa12017-01-12 16:51:18 -0700985 } else if (base->isMatrix()) {
John Kessenichc142c882017-01-13 19:34:22 -0700986 TSwizzleSelectors<TMatrixSelector> selectors;
987 if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors))
John Kessenich001dfa12017-01-12 16:51:18 -0700988 return result;
989
John Kessenichc142c882017-01-13 19:34:22 -0700990 if (selectors.size() == 1) {
John Kessenich001dfa12017-01-12 16:51:18 -0700991 // Representable by m[c][r]
John Kessenichfdf63472017-01-13 12:27:52 -0700992 if (base->getType().getQualifier().isFrontEndConstant()) {
John Kessenichc142c882017-01-13 19:34:22 -0700993 result = intermediate.foldDereference(base, selectors[0].coord1, loc);
994 result = intermediate.foldDereference(result, selectors[0].coord2, loc);
John Kessenich001dfa12017-01-12 16:51:18 -0700995 } else {
John Kessenich2ceec682017-07-28 16:20:13 -0600996 result = intermediate.addIndex(EOpIndexDirect, base,
997 intermediate.addConstantUnion(selectors[0].coord1, loc),
998 loc);
John Kessenich001dfa12017-01-12 16:51:18 -0700999 TType dereferencedCol(base->getType(), 0);
1000 result->setType(dereferencedCol);
John Kessenich2ceec682017-07-28 16:20:13 -06001001 result = intermediate.addIndex(EOpIndexDirect, result,
1002 intermediate.addConstantUnion(selectors[0].coord2, loc),
1003 loc);
John Kessenich001dfa12017-01-12 16:51:18 -07001004 TType dereferenced(dereferencedCol, 0);
1005 result->setType(dereferenced);
1006 }
1007 } else {
John Kessenichc142c882017-01-13 19:34:22 -07001008 int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors);
John Kessenich001dfa12017-01-12 16:51:18 -07001009 if (column >= 0) {
1010 // Representable by m[c]
John Kessenichfdf63472017-01-13 12:27:52 -07001011 if (base->getType().getQualifier().isFrontEndConstant())
John Kessenich001dfa12017-01-12 16:51:18 -07001012 result = intermediate.foldDereference(base, column, loc);
1013 else {
John Kessenich2ceec682017-07-28 16:20:13 -06001014 result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc),
1015 loc);
John Kessenich001dfa12017-01-12 16:51:18 -07001016 TType dereferenced(base->getType(), 0);
1017 result->setType(dereferenced);
1018 }
1019 } else {
1020 // general case, not a column, not a single component
John Kessenichc142c882017-01-13 19:34:22 -07001021 TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
John Kessenichfdf63472017-01-13 12:27:52 -07001022 result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc);
John Kessenich2ceec682017-07-28 16:20:13 -06001023 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
1024 selectors.size()));
John Kessenichfdf63472017-01-13 12:27:52 -07001025 }
John Kessenich001dfa12017-01-12 16:51:18 -07001026 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07001027 } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
1028 const TTypeList* fields = base->getType().getStruct();
1029 bool fieldFound = false;
1030 int member;
1031 for (member = 0; member < (int)fields->size(); ++member) {
1032 if ((*fields)[member].type->getFieldName() == field) {
1033 fieldFound = true;
1034 break;
1035 }
1036 }
1037 if (fieldFound) {
John Kessenich8bcdf2e2017-07-30 16:54:02 -06001038 if (base->getAsSymbolNode() && wasFlattened(base)) {
steve-lunarga2e75312016-12-14 15:22:25 -07001039 result = flattenAccess(base, member);
1040 } else {
John Kessenich8bcdf2e2017-07-30 16:54:02 -06001041 if (base->getType().getQualifier().storage == EvqConst)
1042 result = intermediate.foldDereference(base, member, loc);
1043 else {
1044 TIntermTyped* index = intermediate.addConstantUnion(member, loc);
1045 result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc);
1046 result->setType(*(*fields)[member].type);
John Kessenichcd0a78a2016-09-09 16:32:09 -06001047 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07001048 }
1049 } else
1050 error(loc, "no such field in structure", field.c_str(), "");
1051 } else
1052 error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str());
1053
1054 return result;
1055}
1056
John Kessenich516d92d2017-03-08 20:09:03 -07001057//
John Kessenich5f12d2f2017-03-11 09:39:55 -07001058// Return true if the field should be treated as a built-in method.
1059// Return false otherwise.
John Kessenich516d92d2017-03-08 20:09:03 -07001060//
steve-lunarge7d07522017-03-19 18:12:37 -06001061bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field)
John Kessenich516d92d2017-03-08 20:09:03 -07001062{
John Kessenich54ee28f2017-03-11 14:13:00 -07001063 if (base == nullptr)
1064 return false;
1065
John Kessenich516d92d2017-03-08 20:09:03 -07001066 variableCheck(base);
1067
John Kessenich5f12d2f2017-03-11 09:39:55 -07001068 if (base->getType().getBasicType() == EbtSampler) {
1069 return true;
1070 } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) {
1071 return true;
John Kessenich516d92d2017-03-08 20:09:03 -07001072 } else if (field == "Append" ||
1073 field == "RestartStrip") {
1074 // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but
1075 // the code is around in the shader source.
John Kessenich5f12d2f2017-03-11 09:39:55 -07001076 return true;
1077 } else
1078 return false;
John Kessenich516d92d2017-03-08 20:09:03 -07001079}
1080
John Kessenichecd08bc2017-08-07 23:40:05 -06001081// Independently establish a built-in that is a member of a structure.
1082// 'arraySizes' are what's desired for the independent built-in, whatever
1083// the higher-level source/expression of them was.
1084void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes,
1085 const TQualifier& outerQualifier)
1086{
John Kessenich3322dd82017-08-08 20:02:21 -06001087 // Because of arrays of structs, we might be asked more than once,
1088 // but the arraySizes passed in should have captured the whole thing
1089 // the first time.
1090 // However, clip/cull rely on multiple updates.
1091 if (!isClipOrCullDistance(memberType))
1092 if (splitBuiltIns.find(tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)) !=
1093 splitBuiltIns.end())
1094 return;
John Kessenichecd08bc2017-08-07 23:40:05 -06001095
John Kesseniche516d432017-08-09 14:29:29 -06001096 TVariable* ioVar = makeInternalVariable(baseName + "." + memberType.getFieldName(), memberType);
John Kessenich3322dd82017-08-08 20:02:21 -06001097
1098 if (arraySizes != nullptr && !memberType.isArray())
John Kessenichecd08bc2017-08-07 23:40:05 -06001099 ioVar->getWritableType().newArraySizes(*arraySizes);
1100
John Kessenichecd08bc2017-08-07 23:40:05 -06001101 splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar;
1102 if (!isClipOrCullDistance(ioVar->getType()))
1103 trackLinkage(*ioVar);
1104
1105 // Merge qualifier from the user structure
1106 mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier);
LoopDawg5e5b12e2017-08-28 14:02:19 -06001107
1108 // Fix the builtin type if needed (e.g, some types require fixed array sizes, no matter how the
1109 // shader declared them). This is done after mergeQualifiers(), in case fixBuiltInIoType looks
1110 // at the qualifier to determine e.g, in or out qualifications.
1111 fixBuiltInIoType(ioVar->getWritableType());
1112
John Kessenich3322dd82017-08-08 20:02:21 -06001113 // But, not location, we're losing that
1114 ioVar->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
John Kessenichecd08bc2017-08-07 23:40:05 -06001115}
1116
John Kessenich8bcdf2e2017-07-30 16:54:02 -06001117// Split a type into
1118// 1. a struct of non-I/O members
John Kessenichecd08bc2017-08-07 23:40:05 -06001119// 2. a collection of independent I/O variables
steve-lunarga2e75312016-12-14 15:22:25 -07001120void HlslParseContext::split(const TVariable& variable)
1121{
steve-lunarga2e75312016-12-14 15:22:25 -07001122 // Create a new variable:
John Kessenichecd08bc2017-08-07 23:40:05 -06001123 const TType& clonedType = *variable.getType().clone();
1124 const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier());
John Kessenichd5aedc12017-08-06 19:42:42 -06001125 splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType);
steve-lunarga2e75312016-12-14 15:22:25 -07001126}
1127
John Kessenich8bcdf2e2017-07-30 16:54:02 -06001128// Recursive implementation of split().
steve-lunarga2e75312016-12-14 15:22:25 -07001129// Returns reference to the modified type.
John Kessenichecd08bc2017-08-07 23:40:05 -06001130const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier)
steve-lunarga2e75312016-12-14 15:22:25 -07001131{
steve-lunarga2e75312016-12-14 15:22:25 -07001132 if (type.isStruct()) {
1133 TTypeList* userStructure = type.getWritableStruct();
John Kessenich6042eb42017-08-02 17:08:43 -06001134 for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) {
John Kessenichecd08bc2017-08-07 23:40:05 -06001135 if (ioType->type->isBuiltIn()) {
1136 // move out the built-in
1137 splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier);
John Kessenich6042eb42017-08-02 17:08:43 -06001138 ioType = userStructure->erase(ioType);
1139 } else {
John Kesseniche516d432017-08-09 14:29:29 -06001140 split(*ioType->type, name + "." + ioType->type->getFieldName(), outerQualifier);
John Kessenich6042eb42017-08-02 17:08:43 -06001141 ++ioType;
1142 }
steve-lunarga2e75312016-12-14 15:22:25 -07001143 }
steve-lunarga2e75312016-12-14 15:22:25 -07001144 }
1145
1146 return type;
1147}
1148
John Kessenich41aa1992017-10-11 14:03:45 -06001149// Is this an aggregate that should be flattened?
1150// Can be applied to intermediate levels of type in a hierarchy.
1151// Some things like flattening uniform arrays are only about the top level
1152// of the aggregate, triggered on 'topLevel'.
1153bool HlslParseContext::shouldFlatten(const TType& type, TStorageQualifier qualifier, bool topLevel) const
steve-lunarge0b9deb2016-09-16 13:26:37 -06001154{
John Kessenich41aa1992017-10-11 14:03:45 -06001155 switch (qualifier) {
1156 case EvqVaryingIn:
1157 case EvqVaryingOut:
1158 return type.isStruct() || type.isArray();
1159 case EvqUniform:
LoopDawg2915da32017-10-20 12:02:38 -06001160 return (type.isArray() && intermediate.getFlattenUniformArrays() && topLevel) ||
1161 (type.isStruct() && type.containsOpaque());
John Kessenich41aa1992017-10-11 14:03:45 -06001162 default:
John Kessenichdc005fb2017-11-16 16:03:18 -07001163 return false;
John Kessenich41aa1992017-10-11 14:03:45 -06001164 };
steve-lunarge0b9deb2016-09-16 13:26:37 -06001165}
1166
steve-lunarga2b01a02016-11-28 17:09:54 -07001167// Top level variable flattening: construct data
John Kessenichd5aedc12017-08-06 19:42:42 -06001168void HlslParseContext::flatten(const TVariable& variable, bool linkage)
steve-lunarge0b9deb2016-09-16 13:26:37 -06001169{
1170 const TType& type = variable.getType();
1171
John Kessenich3322dd82017-08-08 20:02:21 -06001172 // If it's a standalone built-in, there is nothing to flatten
1173 if (type.isBuiltIn() && !type.isStruct())
1174 return;
1175
rdba2f0e0e2017-01-20 14:46:39 +01001176 auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(),
John Kessenich89f8d1e2017-06-27 15:17:38 -06001177 TFlattenData(type.getQualifier().layoutBinding,
1178 type.getQualifier().layoutLocation)));
steve-lunarga2e75312016-12-14 15:22:25 -07001179
rdba2f0e0e2017-01-20 14:46:39 +01001180 // the item is a map pair, so first->second is the TFlattenData itself.
John Kesseniche516d432017-08-09 14:29:29 -06001181 flatten(variable, type, entry.first->second, variable.getName(), linkage, type.getQualifier(), nullptr);
steve-lunarga2b01a02016-11-28 17:09:54 -07001182}
John Kessenichecba76f2017-01-06 00:34:48 -07001183
steve-lunarga2b01a02016-11-28 17:09:54 -07001184// Recursively flatten the given variable at the provided type, building the flattenData as we go.
1185//
1186// This is mutually recursive with flattenStruct and flattenArray.
1187// We are going to flatten an arbitrarily nested composite structure into a linear sequence of
1188// members, and later on, we want to turn a path through the tree structure into a final
1189// location in this linear sequence.
1190//
1191// If the tree was N-ary, that can be directly calculated. However, we are dealing with
John Kessenich64285c92017-01-05 10:45:32 -07001192// arbitrary numbers - perhaps a struct of 7 members containing an array of 3. Thus, we must
steve-lunarga2b01a02016-11-28 17:09:54 -07001193// build a data structure to allow the sequence of bracket and dot operators on arrays and
1194// structs to arrive at the proper member.
1195//
1196// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers.
1197// The leaves are the indexes into the flattened member array.
1198// Each level will have the next location for the Nth item stored sequentially, so for instance:
1199//
1200// struct { float2 a[2]; int b; float4 c[3] };
1201//
1202// This will produce the following flattened tree:
1203// Pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
1204// (3, 7, 8, 5, 6, 0, 1, 2, 11, 12, 13, 3, 4, 5}
1205//
1206// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse:
1207// (0+2) = 8 --> (8+1) = 12 --> 12 = 4
1208//
1209// so the 4th flattened member in traversal order is ours.
1210//
John Kessenich318a3792017-07-30 23:39:48 -06001211int HlslParseContext::flatten(const TVariable& variable, const TType& type,
John Kessenich3322dd82017-08-08 20:02:21 -06001212 TFlattenData& flattenData, TString name, bool linkage,
1213 const TQualifier& outerQualifier,
1214 const TArraySizes* builtInArraySizes)
steve-lunarga2b01a02016-11-28 17:09:54 -07001215{
steve-lunarga2b01a02016-11-28 17:09:54 -07001216 // If something is an arrayed struct, the array flattener will recursively call flatten()
1217 // to then flatten the struct, so this is an "if else": we don't do both.
steve-lunarge0b9deb2016-09-16 13:26:37 -06001218 if (type.isArray())
John Kessenich3322dd82017-08-08 20:02:21 -06001219 return flattenArray(variable, type, flattenData, name, linkage, outerQualifier);
steve-lunarga2b01a02016-11-28 17:09:54 -07001220 else if (type.isStruct())
John Kessenich3322dd82017-08-08 20:02:21 -06001221 return flattenStruct(variable, type, flattenData, name, linkage, outerQualifier, builtInArraySizes);
steve-lunarga2b01a02016-11-28 17:09:54 -07001222 else {
1223 assert(0); // should never happen
1224 return -1;
1225 }
1226}
1227
1228// Add a single flattened member to the flattened data being tracked for the composite
1229// Returns true for the final flattening level.
John Kessenich318a3792017-07-30 23:39:48 -06001230int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData,
John Kessenich3322dd82017-08-08 20:02:21 -06001231 const TString& memberName, bool linkage,
1232 const TQualifier& outerQualifier,
1233 const TArraySizes* builtInArraySizes)
steve-lunarga2b01a02016-11-28 17:09:54 -07001234{
John Kessenich41aa1992017-10-11 14:03:45 -06001235 if (!shouldFlatten(type, outerQualifier.storage, false)) {
steve-lunarga2b01a02016-11-28 17:09:54 -07001236 // This is as far as we flatten. Insert the variable.
steve-lunarga2e75312016-12-14 15:22:25 -07001237 TVariable* memberVariable = makeInternalVariable(memberName, type);
steve-lunarga2b01a02016-11-28 17:09:54 -07001238 mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier());
1239
1240 if (flattenData.nextBinding != TQualifier::layoutBindingEnd)
1241 memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++;
1242
John Kesseniche29ff3c2017-08-11 00:17:26 -06001243 if (memberVariable->getType().isBuiltIn()) {
1244 // inherited locations are nonsensical for built-ins (TODO: what if semantic had a number)
1245 memberVariable->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
1246 } else {
John Kessenich89f8d1e2017-06-27 15:17:38 -06001247 // inherited locations must be auto bumped, not replicated
John Kessenichecd08bc2017-08-07 23:40:05 -06001248 if (flattenData.nextLocation != TQualifier::layoutLocationEnd) {
John Kessenich89f8d1e2017-06-27 15:17:38 -06001249 memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation;
1250 flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType());
1251 nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation);
1252 }
John Kessenich89f8d1e2017-06-27 15:17:38 -06001253 }
1254
Jamie Madill3ec327c2016-12-13 17:30:58 -05001255 flattenData.offsets.push_back(static_cast<int>(flattenData.members.size()));
steve-lunarga2b01a02016-11-28 17:09:54 -07001256 flattenData.members.push_back(memberVariable);
1257
John Kessenichd5aedc12017-08-06 19:42:42 -06001258 if (linkage)
John Kessenich02467d82017-01-19 15:41:47 -07001259 trackLinkage(*memberVariable);
steve-lunarga2b01a02016-11-28 17:09:54 -07001260
John Kessenichd5aedc12017-08-06 19:42:42 -06001261 return static_cast<int>(flattenData.offsets.size()) - 1; // location of the member reference
steve-lunarga2b01a02016-11-28 17:09:54 -07001262 } else {
1263 // Further recursion required
John Kessenich3322dd82017-08-08 20:02:21 -06001264 return flatten(variable, type, flattenData, memberName, linkage, outerQualifier, builtInArraySizes);
steve-lunarga2b01a02016-11-28 17:09:54 -07001265 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06001266}
1267
John Kessenichf9115002016-09-18 23:10:22 -06001268// Figure out the mapping between an aggregate's top members and an
John Kessenichcd0a78a2016-09-09 16:32:09 -06001269// equivalent set of individual variables.
1270//
1271// Assumes shouldFlatten() or equivalent was called first.
John Kessenich318a3792017-07-30 23:39:48 -06001272int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type,
John Kessenich3322dd82017-08-08 20:02:21 -06001273 TFlattenData& flattenData, TString name, bool linkage,
1274 const TQualifier& outerQualifier,
1275 const TArraySizes* builtInArraySizes)
John Kessenichcd0a78a2016-09-09 16:32:09 -06001276{
steve-lunarga2b01a02016-11-28 17:09:54 -07001277 assert(type.isStruct());
John Kessenichcd0a78a2016-09-09 16:32:09 -06001278
steve-lunarga2b01a02016-11-28 17:09:54 -07001279 auto members = *type.getStruct();
1280
1281 // Reserve space for this tree level.
Jamie Madill3ec327c2016-12-13 17:30:58 -05001282 int start = static_cast<int>(flattenData.offsets.size());
John Kessenich3322dd82017-08-08 20:02:21 -06001283 int pos = start;
steve-lunarga2b01a02016-11-28 17:09:54 -07001284 flattenData.offsets.resize(int(pos + members.size()), -1);
1285
John Kessenichcd0a78a2016-09-09 16:32:09 -06001286 for (int member = 0; member < (int)members.size(); ++member) {
steve-lunarga2b01a02016-11-28 17:09:54 -07001287 TType& dereferencedType = *members[member].type;
John Kessenich3322dd82017-08-08 20:02:21 -06001288 if (dereferencedType.isBuiltIn())
1289 splitBuiltIn(variable.getName(), dereferencedType, builtInArraySizes, outerQualifier);
1290 else {
John Kesseniche516d432017-08-09 14:29:29 -06001291 const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
1292 name + "." + dereferencedType.getFieldName(),
1293 linkage, outerQualifier,
John Kessenich3322dd82017-08-08 20:02:21 -06001294 builtInArraySizes == nullptr && dereferencedType.isArray()
1295 ? &dereferencedType.getArraySizes()
1296 : builtInArraySizes);
1297 flattenData.offsets[pos++] = mpos;
1298 }
John Kessenichcd0a78a2016-09-09 16:32:09 -06001299 }
1300
steve-lunarga2b01a02016-11-28 17:09:54 -07001301 return start;
John Kessenichcd0a78a2016-09-09 16:32:09 -06001302}
1303
steve-lunarge0b9deb2016-09-16 13:26:37 -06001304// Figure out mapping between an array's members and an
1305// equivalent set of individual variables.
1306//
1307// Assumes shouldFlatten() or equivalent was called first.
John Kessenich318a3792017-07-30 23:39:48 -06001308int HlslParseContext::flattenArray(const TVariable& variable, const TType& type,
John Kessenich3322dd82017-08-08 20:02:21 -06001309 TFlattenData& flattenData, TString name, bool linkage,
1310 const TQualifier& outerQualifier)
steve-lunarge0b9deb2016-09-16 13:26:37 -06001311{
John Kessenich318a3792017-07-30 23:39:48 -06001312 assert(type.isArray() && !type.isImplicitlySizedArray());
steve-lunarge0b9deb2016-09-16 13:26:37 -06001313
steve-lunarga2b01a02016-11-28 17:09:54 -07001314 const int size = type.getOuterArraySize();
steve-lunarge0b9deb2016-09-16 13:26:37 -06001315 const TType dereferencedType(type, 0);
steve-lunarge0b9deb2016-09-16 13:26:37 -06001316
steve-lunarga2b01a02016-11-28 17:09:54 -07001317 if (name.empty())
1318 name = variable.getName();
steve-lunarge0b9deb2016-09-16 13:26:37 -06001319
steve-lunarga2b01a02016-11-28 17:09:54 -07001320 // Reserve space for this tree level.
Jamie Madill3ec327c2016-12-13 17:30:58 -05001321 int start = static_cast<int>(flattenData.offsets.size());
steve-lunarga2b01a02016-11-28 17:09:54 -07001322 int pos = start;
1323 flattenData.offsets.resize(int(pos + size), -1);
1324
John Kessenichecba76f2017-01-06 00:34:48 -07001325 for (int element=0; element < size; ++element) {
steve-lunarge0b9deb2016-09-16 13:26:37 -06001326 char elementNumBuf[20]; // sufficient for MAXINT
1327 snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element);
John Kessenich318a3792017-07-30 23:39:48 -06001328 const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
John Kessenich3322dd82017-08-08 20:02:21 -06001329 name + elementNumBuf, linkage, outerQualifier,
1330 type.getArraySizes());
steve-lunarge0b9deb2016-09-16 13:26:37 -06001331
steve-lunarga2b01a02016-11-28 17:09:54 -07001332 flattenData.offsets[pos++] = mpos;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001333 }
John Kessenichd3f11222016-11-05 10:15:53 -06001334
steve-lunarga2b01a02016-11-28 17:09:54 -07001335 return start;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001336}
1337
steve-lunarga2b01a02016-11-28 17:09:54 -07001338// Return true if we have flattened this node.
1339bool HlslParseContext::wasFlattened(const TIntermTyped* node) const
1340{
steve-lunarga2e75312016-12-14 15:22:25 -07001341 return node != nullptr && node->getAsSymbolNode() != nullptr &&
John Kessenich318a3792017-07-30 23:39:48 -06001342 wasFlattened(node->getAsSymbolNode()->getId());
steve-lunarga2b01a02016-11-28 17:09:54 -07001343}
1344
steve-lunarga2e75312016-12-14 15:22:25 -07001345// Return true if we have split this structure
1346bool HlslParseContext::wasSplit(const TIntermTyped* node) const
1347{
1348 return node != nullptr && node->getAsSymbolNode() != nullptr &&
John Kessenich8bcdf2e2017-07-30 16:54:02 -06001349 wasSplit(node->getAsSymbolNode()->getId());
steve-lunarga2e75312016-12-14 15:22:25 -07001350}
1351
steve-lunarge0b9deb2016-09-16 13:26:37 -06001352// Turn an access into an aggregate that was flattened to instead be
1353// an access to the individual variable the member was flattened to.
John Kessenich8bcdf2e2017-07-30 16:54:02 -06001354// Assumes wasFlattened() or equivalent was called first.
steve-lunarga2e75312016-12-14 15:22:25 -07001355TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
John Kessenichcd0a78a2016-09-09 16:32:09 -06001356{
steve-lunarga2b01a02016-11-28 17:09:54 -07001357 const TType dereferencedType(base->getType(), member); // dereferenced type
John Kessenichcd0a78a2016-09-09 16:32:09 -06001358 const TIntermSymbol& symbolNode = *base->getAsSymbolNode();
John Kessenich41aa1992017-10-11 14:03:45 -06001359 TIntermTyped* flattened = flattenAccess(symbolNode.getId(), member, base->getQualifier().storage,
1360 dereferencedType, symbolNode.getFlattenSubset());
John Kessenich750c2d02017-05-26 00:01:36 -06001361
1362 return flattened ? flattened : base;
1363}
John Kessenich41aa1992017-10-11 14:03:45 -06001364TIntermTyped* HlslParseContext::flattenAccess(int uniqueId, int member, TStorageQualifier outerStorage,
1365 const TType& dereferencedType, int subset)
John Kessenich750c2d02017-05-26 00:01:36 -06001366{
1367 const auto flattenData = flattenMap.find(uniqueId);
steve-lunarga2b01a02016-11-28 17:09:54 -07001368
1369 if (flattenData == flattenMap.end())
John Kessenich750c2d02017-05-26 00:01:36 -06001370 return nullptr;
John Kessenichcd0a78a2016-09-09 16:32:09 -06001371
steve-lunarga2b01a02016-11-28 17:09:54 -07001372 // Calculate new cumulative offset from the packed tree
John Kessenichd1be7542017-06-29 17:43:31 -06001373 int newSubset = flattenData->second.offsets[subset >= 0 ? subset + member : member];
steve-lunarga2b01a02016-11-28 17:09:54 -07001374
John Kessenichd1be7542017-06-29 17:43:31 -06001375 TIntermSymbol* subsetSymbol;
John Kessenich41aa1992017-10-11 14:03:45 -06001376 if (!shouldFlatten(dereferencedType, outerStorage, false)) {
steve-lunarga2b01a02016-11-28 17:09:54 -07001377 // Finished flattening: create symbol for variable
John Kessenichd1be7542017-06-29 17:43:31 -06001378 member = flattenData->second.offsets[newSubset];
steve-lunarga2b01a02016-11-28 17:09:54 -07001379 const TVariable* memberVariable = flattenData->second.members[member];
John Kessenichd1be7542017-06-29 17:43:31 -06001380 subsetSymbol = intermediate.addSymbol(*memberVariable);
1381 subsetSymbol->setFlattenSubset(-1);
steve-lunarga2b01a02016-11-28 17:09:54 -07001382 } else {
John Kessenichd1be7542017-06-29 17:43:31 -06001383
steve-lunarga2b01a02016-11-28 17:09:54 -07001384 // If this is not the final flattening, accumulate the position and return
1385 // an object of the partially dereferenced type.
John Kessenichd1be7542017-06-29 17:43:31 -06001386 subsetSymbol = new TIntermSymbol(uniqueId, "flattenShadow", dereferencedType);
1387 subsetSymbol->setFlattenSubset(newSubset);
steve-lunarga2b01a02016-11-28 17:09:54 -07001388 }
John Kessenichd1be7542017-06-29 17:43:31 -06001389
1390 return subsetSymbol;
John Kessenichcd0a78a2016-09-09 16:32:09 -06001391}
1392
John Kessenich700bdeb2017-10-04 13:27:43 -06001393// For finding where the first leaf is in a subtree of a multi-level aggregate
1394// that is just getting a subset assigned. Follows the same logic as flattenAccess,
1395// but logically going down the "left-most" tree branch each step of the way.
1396//
1397// Returns the offset into the first leaf of the subset.
1398int HlslParseContext::findSubtreeOffset(const TIntermNode& node) const
1399{
1400 const TIntermSymbol* sym = node.getAsSymbolNode();
1401 if (sym == nullptr)
1402 return 0;
1403 if (!sym->isArray() && !sym->isStruct())
1404 return 0;
1405 int subset = sym->getFlattenSubset();
1406 if (subset == -1)
1407 return 0;
1408
1409 // Getting this far means a partial aggregate is identified by the flatten subset.
1410 // Find the first leaf of the subset.
1411
1412 const auto flattenData = flattenMap.find(sym->getId());
1413 if (flattenData == flattenMap.end())
1414 return 0;
1415
1416 return findSubtreeOffset(sym->getType(), subset, flattenData->second.offsets);
1417
1418 do {
1419 subset = flattenData->second.offsets[subset];
1420 } while (true);
1421}
1422// Recursively do the desent
1423int HlslParseContext::findSubtreeOffset(const TType& type, int subset, const TVector<int>& offsets) const
1424{
1425 if (!type.isArray() && !type.isStruct())
1426 return offsets[subset];
1427 TType derefType(type, 0);
1428 return findSubtreeOffset(derefType, offsets[subset], offsets);
1429};
1430
steve-lunarga2e75312016-12-14 15:22:25 -07001431// Find and return the split IO TVariable for id, or nullptr if none.
John Kessenichd5aedc12017-08-06 19:42:42 -06001432TVariable* HlslParseContext::getSplitNonIoVar(int id) const
steve-lunarga2e75312016-12-14 15:22:25 -07001433{
John Kessenichd5aedc12017-08-06 19:42:42 -06001434 const auto splitNonIoVar = splitNonIoVars.find(id);
1435 if (splitNonIoVar == splitNonIoVars.end())
steve-lunarga2e75312016-12-14 15:22:25 -07001436 return nullptr;
1437
John Kessenichd5aedc12017-08-06 19:42:42 -06001438 return splitNonIoVar->second;
steve-lunarga2e75312016-12-14 15:22:25 -07001439}
1440
John Kessenich8bcdf2e2017-07-30 16:54:02 -06001441// Pass through to base class after remembering built-in mappings.
steve-lunarg858c9282017-01-07 08:54:10 -07001442void HlslParseContext::trackLinkage(TSymbol& symbol)
1443{
1444 TBuiltInVariable biType = symbol.getType().getQualifier().builtIn;
steve-lunarg067eb9b2017-04-01 15:34:48 -06001445
steve-lunarg858c9282017-01-07 08:54:10 -07001446 if (biType != EbvNone)
John Kessenich2b4f77f2017-08-04 13:51:54 -06001447 builtInTessLinkageSymbols[biType] = symbol.clone();
steve-lunarg858c9282017-01-07 08:54:10 -07001448
1449 TParseContextBase::trackLinkage(symbol);
1450}
1451
1452
John Kessenich8bcdf2e2017-07-30 16:54:02 -06001453// Returns true if the built-in is a clip or cull distance variable.
LoopDawg307b6502017-07-05 11:33:06 -06001454bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn)
1455{
1456 return builtIn == EbvClipDistance || builtIn == EbvCullDistance;
1457}
1458
steve-lunarg194f0f32017-03-17 18:51:05 -06001459// Some types require fixed array sizes in SPIR-V, but can be scalars or
1460// arrays of sizes SPIR-V doesn't allow. For example, tessellation factors.
1461// This creates the right size. A conversion is performed when the internal
steve-lunargccb076a2017-04-05 11:03:02 -06001462// type is copied to or from the external type. This corrects the externally
1463// facing input or output type to abide downstream semantics.
1464void HlslParseContext::fixBuiltInIoType(TType& type)
steve-lunarg194f0f32017-03-17 18:51:05 -06001465{
steve-lunargccb076a2017-04-05 11:03:02 -06001466 int requiredArraySize = 0;
steve-lunarg194f0f32017-03-17 18:51:05 -06001467
1468 switch (type.getQualifier().builtIn) {
steve-lunargccb076a2017-04-05 11:03:02 -06001469 case EbvTessLevelOuter: requiredArraySize = 4; break;
1470 case EbvTessLevelInner: requiredArraySize = 2; break;
LoopDawgc44b95f2017-06-22 12:08:00 -06001471
steve-lunargccb076a2017-04-05 11:03:02 -06001472 case EbvTessCoord:
1473 {
1474 // tesscoord is always a vec3 for the IO variable, no matter the shader's
1475 // declared vector size.
1476 TType tessCoordType(type.getBasicType(), type.getQualifier().storage, 3);
1477
1478 tessCoordType.getQualifier() = type.getQualifier();
1479 type.shallowCopy(tessCoordType);
1480
1481 break;
1482 }
steve-lunarg194f0f32017-03-17 18:51:05 -06001483 default:
LoopDawg307b6502017-07-05 11:33:06 -06001484 if (isClipOrCullDistance(type)) {
LoopDawg5e5b12e2017-08-28 14:02:19 -06001485 const int loc = type.getQualifier().layoutLocation;
1486
LoopDawg307b6502017-07-05 11:33:06 -06001487 if (type.getQualifier().builtIn == EbvClipDistance) {
LoopDawg5e5b12e2017-08-28 14:02:19 -06001488 if (type.getQualifier().storage == EvqVaryingIn)
1489 clipSemanticNSizeIn[loc] = type.getVectorSize();
1490 else
1491 clipSemanticNSizeOut[loc] = type.getVectorSize();
LoopDawg307b6502017-07-05 11:33:06 -06001492 } else {
LoopDawg5e5b12e2017-08-28 14:02:19 -06001493 if (type.getQualifier().storage == EvqVaryingIn)
1494 cullSemanticNSizeIn[loc] = type.getVectorSize();
1495 else
1496 cullSemanticNSizeOut[loc] = type.getVectorSize();
LoopDawg307b6502017-07-05 11:33:06 -06001497 }
1498 }
1499
steve-lunarg194f0f32017-03-17 18:51:05 -06001500 return;
1501 }
1502
steve-lunargccb076a2017-04-05 11:03:02 -06001503 // Alter or set array size as needed.
1504 if (requiredArraySize > 0) {
John Kessenich01109542017-08-11 00:14:46 -06001505 if (!type.isArray() || type.getOuterArraySize() != requiredArraySize) {
steve-lunargccb076a2017-04-05 11:03:02 -06001506 TArraySizes arraySizes;
1507 arraySizes.addInnerSize(requiredArraySize);
1508 type.newArraySizes(arraySizes);
1509 }
steve-lunarg194f0f32017-03-17 18:51:05 -06001510 }
1511}
1512
John Kessenich7dc630f2016-09-16 01:44:43 -06001513// Variables that correspond to the user-interface in and out of a stage
John Kessenich54596ff2017-06-20 03:20:59 -06001514// (not the built-in interface) are
1515// - assigned locations
John Kessenich54596ff2017-06-20 03:20:59 -06001516// - registered as a linkage node (part of the stage's external interface).
John Kessenich7dc630f2016-09-16 01:44:43 -06001517// Assumes it is called in the order in which locations should be assigned.
John Kessenich54596ff2017-06-20 03:20:59 -06001518void HlslParseContext::assignToInterface(TVariable& variable)
John Kessenich7dc630f2016-09-16 01:44:43 -06001519{
1520 const auto assignLocation = [&](TVariable& variable) {
John Kessenich54596ff2017-06-20 03:20:59 -06001521 TType& type = variable.getWritableType();
John Kessenichcca42a82017-08-03 18:41:48 -06001522 if (!type.isStruct() || type.getStruct()->size() > 0) {
1523 TQualifier& qualifier = type.getQualifier();
1524 if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
1525 if (qualifier.builtIn == EbvNone && !qualifier.hasLocation()) {
1526 // Strip off the outer array dimension for those having an extra one.
1527 int size;
1528 if (type.isArray() && qualifier.isArrayedIo(language)) {
1529 TType elementType(type, 0);
1530 size = intermediate.computeTypeLocationSize(elementType);
1531 } else
1532 size = intermediate.computeTypeLocationSize(type);
steve-lunarg7afe1342017-03-18 22:24:14 -06001533
John Kessenichcca42a82017-08-03 18:41:48 -06001534 if (qualifier.storage == EvqVaryingIn) {
1535 variable.getWritableType().getQualifier().layoutLocation = nextInLocation;
1536 nextInLocation += size;
1537 } else {
1538 variable.getWritableType().getQualifier().layoutLocation = nextOutLocation;
1539 nextOutLocation += size;
1540 }
John Kessenich7dc630f2016-09-16 01:44:43 -06001541 }
John Kessenichcca42a82017-08-03 18:41:48 -06001542 trackLinkage(variable);
John Kessenich7dc630f2016-09-16 01:44:43 -06001543 }
John Kessenich7dc630f2016-09-16 01:44:43 -06001544 }
1545 };
1546
steve-lunarga2b01a02016-11-28 17:09:54 -07001547 if (wasFlattened(variable.getUniqueId())) {
1548 auto& memberList = flattenMap[variable.getUniqueId()].members;
John Kessenich7dc630f2016-09-16 01:44:43 -06001549 for (auto member = memberList.begin(); member != memberList.end(); ++member)
1550 assignLocation(**member);
steve-lunarga2e75312016-12-14 15:22:25 -07001551 } else if (wasSplit(variable.getUniqueId())) {
John Kessenichd5aedc12017-08-06 19:42:42 -06001552 TVariable* splitIoVar = getSplitNonIoVar(variable.getUniqueId());
steve-lunarg4198b8b2017-03-09 19:10:57 -07001553 assignLocation(*splitIoVar);
steve-lunarga2e75312016-12-14 15:22:25 -07001554 } else {
John Kessenich7dc630f2016-09-16 01:44:43 -06001555 assignLocation(variable);
steve-lunarga2e75312016-12-14 15:22:25 -07001556 }
John Kessenich7dc630f2016-09-16 01:44:43 -06001557}
1558
John Kesseniche01a9bc2016-03-12 20:11:22 -07001559//
1560// Handle seeing a function declarator in the grammar. This is the precursor
1561// to recognizing a function prototype or function definition.
1562//
John Kessenich088d52b2017-03-11 17:55:28 -07001563void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype)
John Kesseniche01a9bc2016-03-12 20:11:22 -07001564{
1565 //
1566 // Multiple declarations of the same function name are allowed.
1567 //
1568 // If this is a definition, the definition production code will check for redefinitions
1569 // (we don't know at this point if it's a definition or not).
1570 //
John Kesseniche01a9bc2016-03-12 20:11:22 -07001571 bool builtIn;
1572 TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
1573 const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
1574
1575 if (prototype) {
1576 // All built-in functions are defined, even though they don't have a body.
1577 // Count their prototype as a definition instead.
1578 if (symbolTable.atBuiltInLevel())
1579 function.setDefined();
1580 else {
1581 if (prevDec && ! builtIn)
1582 symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const
1583 function.setPrototyped();
1584 }
1585 }
1586
1587 // This insert won't actually insert it if it's a duplicate signature, but it will still check for
1588 // other forms of name collisions.
1589 if (! symbolTable.insert(function))
1590 error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07001591}
1592
steve-lunarg2bb1f392017-04-27 11:22:32 -06001593// For struct buffers with counters, we must pass the counter buffer as hidden parameter.
1594// This adds the hidden parameter to the parameter list in 'paramNodes' if needed.
1595// Otherwise, it's a no-op
John Kessenich2ceec682017-07-28 16:20:13 -06001596void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param,
1597 TIntermAggregate*& paramNodes)
steve-lunarg2bb1f392017-04-27 11:22:32 -06001598{
1599 if (! hasStructBuffCounter(*param.type))
1600 return;
1601
1602 const TString counterBlockName(getStructBuffCounterName(*param.name));
1603
1604 TType counterType;
1605 counterBufferType(loc, counterType);
1606 TVariable *variable = makeInternalVariable(counterBlockName, counterType);
1607
1608 if (! symbolTable.insert(*variable))
1609 error(loc, "redefinition", variable->getName().c_str(), "");
1610
1611 paramNodes = intermediate.growAggregate(paramNodes,
1612 intermediate.addSymbol(*variable, loc),
1613 loc);
1614}
1615
John Kesseniche01a9bc2016-03-12 20:11:22 -07001616//
John Kessenichecba76f2017-01-06 00:34:48 -07001617// Handle seeing the function prototype in front of a function definition in the grammar.
John Kesseniche01a9bc2016-03-12 20:11:22 -07001618// The body is handled after this function returns.
1619//
John Kessenichca71d942017-03-07 20:44:09 -07001620// Returns an aggregate of parameter-symbol nodes.
1621//
John Kessenichecba76f2017-01-06 00:34:48 -07001622TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
John Kesseniche18fd202018-01-30 11:01:39 -07001623 const TAttributes& attributes,
John Kessenich2ceec682017-07-28 16:20:13 -06001624 TIntermNode*& entryPointTree)
John Kesseniche01a9bc2016-03-12 20:11:22 -07001625{
1626 currentCaller = function.getMangledName();
1627 TSymbol* symbol = symbolTable.find(function.getMangledName());
1628 TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1629
John Kessenichf02c8e62017-06-19 16:25:44 -06001630 if (prevDec == nullptr)
John Kesseniche01a9bc2016-03-12 20:11:22 -07001631 error(loc, "can't find function", function.getName().c_str(), "");
1632 // Note: 'prevDec' could be 'function' if this is the first time we've seen function
1633 // as it would have just been put in the symbol table. Otherwise, we're looking up
1634 // an earlier occurrence.
1635
1636 if (prevDec && prevDec->isDefined()) {
1637 // Then this function already has a body.
1638 error(loc, "function already has a body", function.getName().c_str(), "");
1639 }
1640 if (prevDec && ! prevDec->isDefined()) {
1641 prevDec->setDefined();
1642
1643 // Remember the return type for later checking for RETURN statements.
1644 currentFunctionType = &(prevDec->getType());
1645 } else
1646 currentFunctionType = new TType(EbtVoid);
1647 functionReturnsValue = false;
1648
John Kessenich94dfb7a2017-01-18 16:45:02 -07001649 // Entry points need different I/O and other handling, transform it so the
1650 // rest of this function doesn't care.
John Kessenich02467d82017-01-19 15:41:47 -07001651 entryPointTree = transformEntryPoint(loc, function, attributes);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001652
1653 //
1654 // New symbol table scope for body of function plus its arguments
1655 //
John Kessenich077e0522016-06-09 02:02:17 -06001656 pushScope();
John Kesseniche01a9bc2016-03-12 20:11:22 -07001657
1658 //
1659 // Insert parameters into the symbol table.
1660 // If the parameter has no name, it's not an error, just don't insert it
1661 // (could be used for unused args).
1662 //
John Kessenich7dc630f2016-09-16 01:44:43 -06001663 // Also, accumulate the list of parameters into the AST, so lower level code
John Kesseniche01a9bc2016-03-12 20:11:22 -07001664 // knows where to find parameters.
1665 //
1666 TIntermAggregate* paramNodes = new TIntermAggregate;
1667 for (int i = 0; i < function.getParamCount(); i++) {
1668 TParameter& param = function[i];
1669 if (param.name != nullptr) {
John Kessenich02467d82017-01-19 15:41:47 -07001670 TVariable *variable = new TVariable(param.name, *param.type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001671
John Kessenich37789792017-03-21 23:56:40 -06001672 if (i == 0 && function.hasImplicitThis()) {
John Kessenich7a41f962017-03-22 11:38:22 -06001673 // Anonymous 'this' members are already in a symbol-table level,
1674 // and we need to know what function parameter to map them to.
John Kessenich37789792017-03-21 23:56:40 -06001675 symbolTable.makeInternalVariable(*variable);
1676 pushImplicitThis(variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001677 }
John Kessenich750c2d02017-05-26 00:01:36 -06001678
John Kessenich7a41f962017-03-22 11:38:22 -06001679 // Insert the parameters with name in the symbol table.
1680 if (! symbolTable.insert(*variable))
1681 error(loc, "redefinition", variable->getName().c_str(), "");
steve-lunarge404e082017-04-20 16:37:14 -06001682
John Kessenich750c2d02017-05-26 00:01:36 -06001683 // Add parameters to the AST list.
John Kessenich41aa1992017-10-11 14:03:45 -06001684 if (shouldFlatten(variable->getType(), variable->getType().getQualifier().storage, true)) {
John Kessenich750c2d02017-05-26 00:01:36 -06001685 // Expand the AST parameter nodes (but not the name mangling or symbol table view)
1686 // for structures that need to be flattened.
John Kessenichd5aedc12017-08-06 19:42:42 -06001687 flatten(*variable, false);
John Kessenich750c2d02017-05-26 00:01:36 -06001688 const TTypeList* structure = variable->getType().getStruct();
1689 for (int mem = 0; mem < (int)structure->size(); ++mem) {
John Kessenich750c2d02017-05-26 00:01:36 -06001690 paramNodes = intermediate.growAggregate(paramNodes,
John Kessenich2ceec682017-07-28 16:20:13 -06001691 flattenAccess(variable->getUniqueId(), mem,
John Kessenich41aa1992017-10-11 14:03:45 -06001692 variable->getType().getQualifier().storage,
1693 *(*structure)[mem].type),
John Kessenich750c2d02017-05-26 00:01:36 -06001694 loc);
John Kessenich750c2d02017-05-26 00:01:36 -06001695 }
1696 } else {
1697 // Add the parameter to the AST
1698 paramNodes = intermediate.growAggregate(paramNodes,
1699 intermediate.addSymbol(*variable, loc),
1700 loc);
1701 }
1702
1703 // Add hidden AST parameter for struct buffer counters, if needed.
steve-lunarg2bb1f392017-04-27 11:22:32 -06001704 addStructBufferHiddenCounterParam(loc, param, paramNodes);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001705 } else
John Kessenich1c7e7072016-04-03 20:36:48 -06001706 paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001707 }
John Kessenich37789792017-03-21 23:56:40 -06001708 if (function.hasIllegalImplicitThis())
1709 pushImplicitThis(nullptr);
steve-lunarga2e75312016-12-14 15:22:25 -07001710
John Kesseniche01a9bc2016-03-12 20:11:22 -07001711 intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
1712 loopNestingLevel = 0;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001713 controlFlowNestingLevel = 0;
John Kessenich517fe7a2016-11-26 13:31:47 -07001714 postEntryPointReturn = false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001715
John Kessenich94dfb7a2017-01-18 16:45:02 -07001716 return paramNodes;
1717}
John Kessenichecba76f2017-01-06 00:34:48 -07001718
steve-lunarge752f462017-03-22 18:39:25 -06001719// Handle all [attrib] attribute for the shader entry point
John Kesseniche18fd202018-01-30 11:01:39 -07001720void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes)
John Kessenich94dfb7a2017-01-18 16:45:02 -07001721{
John Kesseniche18fd202018-01-30 11:01:39 -07001722 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
1723 switch (it->name) {
1724 case EatNumThreads:
1725 {
1726 const TIntermSequence& sequence = it->args->getSequence();
1727 for (int lid = 0; lid < int(sequence.size()); ++lid)
1728 intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
1729 break;
steve-lunarg858c9282017-01-07 08:54:10 -07001730 }
John Kesseniche18fd202018-01-30 11:01:39 -07001731 case EatMaxVertexCount:
1732 {
1733 int maxVertexCount;
steve-lunarg858c9282017-01-07 08:54:10 -07001734
John Kesseniche18fd202018-01-30 11:01:39 -07001735 if (! it->getInt(maxVertexCount)) {
1736 error(loc, "invalid maxvertexcount", "", "");
1737 } else {
1738 if (! intermediate.setVertices(maxVertexCount))
1739 error(loc, "cannot change previously set maxvertexcount attribute", "", "");
1740 }
1741 break;
steve-lunarg858c9282017-01-07 08:54:10 -07001742 }
John Kesseniche18fd202018-01-30 11:01:39 -07001743 case EatPatchConstantFunc:
1744 {
1745 TString pcfName;
1746 if (! it->getString(pcfName, 0, false)) {
1747 error(loc, "invalid patch constant function", "", "");
steve-lunarg858c9282017-01-07 08:54:10 -07001748 } else {
John Kesseniche18fd202018-01-30 11:01:39 -07001749 patchConstantFunctionName = pcfName;
steve-lunarg858c9282017-01-07 08:54:10 -07001750 }
John Kesseniche18fd202018-01-30 11:01:39 -07001751 break;
steve-lunarg858c9282017-01-07 08:54:10 -07001752 }
John Kesseniche18fd202018-01-30 11:01:39 -07001753 case EatDomain:
1754 {
1755 // Handle [domain("...")]
1756 TString domainStr;
1757 if (! it->getString(domainStr)) {
1758 error(loc, "invalid domain", "", "");
steve-lunarg858c9282017-01-07 08:54:10 -07001759 } else {
John Kesseniche18fd202018-01-30 11:01:39 -07001760 TLayoutGeometry domain = ElgNone;
steve-lunarg858c9282017-01-07 08:54:10 -07001761
John Kesseniche18fd202018-01-30 11:01:39 -07001762 if (domainStr == "tri") {
1763 domain = ElgTriangles;
1764 } else if (domainStr == "quad") {
1765 domain = ElgQuads;
1766 } else if (domainStr == "isoline") {
1767 domain = ElgIsolines;
1768 } else {
1769 error(loc, "unsupported domain type", domainStr.c_str(), "");
1770 }
1771
1772 if (language == EShLangTessEvaluation) {
1773 if (! intermediate.setInputPrimitive(domain))
1774 error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
1775 } else {
1776 if (! intermediate.setOutputPrimitive(domain))
1777 error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
steve-lunarg858c9282017-01-07 08:54:10 -07001778 }
1779 }
John Kesseniche18fd202018-01-30 11:01:39 -07001780 break;
steve-lunarg858c9282017-01-07 08:54:10 -07001781 }
John Kesseniche18fd202018-01-30 11:01:39 -07001782 case EatOutputTopology:
1783 {
1784 // Handle [outputtopology("...")]
1785 TString topologyStr;
1786 if (! it->getString(topologyStr)) {
1787 error(loc, "invalid outputtopology", "", "");
steve-lunarg858c9282017-01-07 08:54:10 -07001788 } else {
John Kesseniche18fd202018-01-30 11:01:39 -07001789 TVertexOrder vertexOrder = EvoNone;
1790 TLayoutGeometry primitive = ElgNone;
steve-lunarg858c9282017-01-07 08:54:10 -07001791
John Kesseniche18fd202018-01-30 11:01:39 -07001792 if (topologyStr == "point") {
1793 intermediate.setPointMode();
1794 } else if (topologyStr == "line") {
1795 primitive = ElgIsolines;
1796 } else if (topologyStr == "triangle_cw") {
1797 vertexOrder = EvoCw;
1798 primitive = ElgTriangles;
1799 } else if (topologyStr == "triangle_ccw") {
1800 vertexOrder = EvoCcw;
1801 primitive = ElgTriangles;
1802 } else {
1803 error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
1804 }
1805
1806 if (vertexOrder != EvoNone) {
1807 if (! intermediate.setVertexOrder(vertexOrder)) {
1808 error(loc, "cannot change previously set outputtopology",
1809 TQualifier::getVertexOrderString(vertexOrder), "");
1810 }
1811 }
1812 if (primitive != ElgNone)
1813 intermediate.setOutputPrimitive(primitive);
1814 }
1815 break;
steve-lunarg858c9282017-01-07 08:54:10 -07001816 }
John Kesseniche18fd202018-01-30 11:01:39 -07001817 case EatPartitioning:
1818 {
1819 // Handle [partitioning("...")]
1820 TString partitionStr;
1821 if (! it->getString(partitionStr)) {
1822 error(loc, "invalid partitioning", "", "");
1823 } else {
1824 TVertexSpacing partitioning = EvsNone;
1825
1826 if (partitionStr == "integer") {
1827 partitioning = EvsEqual;
1828 } else if (partitionStr == "fractional_even") {
1829 partitioning = EvsFractionalEven;
1830 } else if (partitionStr == "fractional_odd") {
1831 partitioning = EvsFractionalOdd;
1832 //} else if (partition == "pow2") { // TODO: currently nothing to map this to.
1833 } else {
1834 error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
1835 }
steve-lunarg858c9282017-01-07 08:54:10 -07001836
John Kesseniche18fd202018-01-30 11:01:39 -07001837 if (! intermediate.setVertexSpacing(partitioning))
1838 error(loc, "cannot change previously set partitioning",
1839 TQualifier::getVertexSpacingString(partitioning), "");
steve-lunarg858c9282017-01-07 08:54:10 -07001840 }
John Kesseniche18fd202018-01-30 11:01:39 -07001841 break;
1842 }
1843 case EatOutputControlPoints:
1844 {
1845 // Handle [outputcontrolpoints("...")]
1846 int ctrlPoints;
1847 if (! it->getInt(ctrlPoints)) {
1848 error(loc, "invalid outputcontrolpoints", "", "");
1849 } else {
1850 if (! intermediate.setVertices(ctrlPoints)) {
1851 error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
1852 }
1853 }
1854 break;
1855 }
1856 case EatBuiltIn:
1857 case EatLocation:
1858 // tolerate these because of dual use of entrypoint and type attributes
1859 break;
1860 default:
1861 warn(loc, "attribute does not apply to entry point", "", "");
1862 break;
steve-lunarg858c9282017-01-07 08:54:10 -07001863 }
1864 }
steve-lunarge752f462017-03-22 18:39:25 -06001865}
1866
John Kessenich77ea30b2017-09-30 14:34:50 -06001867// Update the given type with any type-like attribute information in the
1868// attributes.
John Kesseniche18fd202018-01-30 11:01:39 -07001869void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type,
1870 bool allowEntry)
John Kessenich77ea30b2017-09-30 14:34:50 -06001871{
John Kessenichcc951f82017-12-06 07:33:36 -07001872 if (attributes.size() == 0)
1873 return;
1874
John Kessenich77ea30b2017-09-30 14:34:50 -06001875 int value;
John Kessenichcc951f82017-12-06 07:33:36 -07001876 TString builtInString;
John Kesseniche18fd202018-01-30 11:01:39 -07001877 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
1878 switch (it->name) {
1879 case EatLocation:
1880 // location
1881 if (it->getInt(value))
1882 type.getQualifier().layoutLocation = value;
1883 break;
1884 case EatBinding:
1885 // binding
1886 if (it->getInt(value)) {
1887 type.getQualifier().layoutBinding = value;
1888 type.getQualifier().layoutSet = 0;
1889 }
1890 // set
1891 if (it->getInt(value, 1))
1892 type.getQualifier().layoutSet = value;
1893 break;
1894 case EatGlobalBinding:
1895 // global cbuffer binding
1896 if (it->getInt(value))
1897 globalUniformBinding = value;
1898 // global cbuffer binding
1899 if (it->getInt(value, 1))
1900 globalUniformSet = value;
1901 break;
1902 case EatInputAttachment:
1903 // input attachment
1904 if (it->getInt(value))
1905 type.getQualifier().layoutAttachment = value;
1906 break;
1907 case EatBuiltIn:
1908 // PointSize built-in
1909 if (it->getString(builtInString, 0, false)) {
1910 if (builtInString == "PointSize")
1911 type.getQualifier().builtIn = EbvPointSize;
1912 }
1913 break;
1914 case EatPushConstant:
1915 // push_constant
1916 type.getQualifier().layoutPushConstant = true;
1917 break;
1918 case EatConstantId:
1919 // specialization constant
1920 if (it->getInt(value)) {
1921 TSourceLoc loc;
1922 loc.init();
1923 setSpecConstantId(loc, type.getQualifier(), value);
1924 }
1925 break;
1926 default:
1927 if (! allowEntry)
1928 warn(loc, "attribute does not apply to a type", "", "");
1929 break;
1930 }
John Kessenich046bae02017-12-23 17:29:45 -07001931 }
John Kessenich77ea30b2017-09-30 14:34:50 -06001932}
1933
steve-lunarge752f462017-03-22 18:39:25 -06001934//
1935// Do all special handling for the entry point, including wrapping
1936// the shader's entry point with the official entry point that will call it.
1937//
1938// The following:
1939//
1940// retType shaderEntryPoint(args...) // shader declared entry point
1941// { body }
1942//
1943// Becomes
1944//
1945// out retType ret;
1946// in iargs<that are input>...;
1947// out oargs<that are output> ...;
1948//
1949// void shaderEntryPoint() // synthesized, but official, entry point
1950// {
1951// args<that are input> = iargs...;
1952// ret = @shaderEntryPoint(args...);
1953// oargs = args<that are output>...;
1954// }
John Kessenich54596ff2017-06-20 03:20:59 -06001955// retType @shaderEntryPoint(args...)
1956// { body }
steve-lunarge752f462017-03-22 18:39:25 -06001957//
1958// The symbol table will still map the original entry point name to the
John Kessenich54596ff2017-06-20 03:20:59 -06001959// the modified function and its new name:
steve-lunarge752f462017-03-22 18:39:25 -06001960//
1961// symbol table: shaderEntryPoint -> @shaderEntryPoint
1962//
1963// Returns nullptr if no entry-point tree was built, otherwise, returns
1964// a subtree that creates the entry point.
1965//
John Kessenich2ceec682017-07-28 16:20:13 -06001966TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction,
John Kesseniche18fd202018-01-30 11:01:39 -07001967 const TAttributes& attributes)
steve-lunarge752f462017-03-22 18:39:25 -06001968{
steve-lunargf38cca32017-04-03 09:27:53 -06001969 // Return true if this is a tessellation patch constant function input to a domain shader.
1970 const auto isDsPcfInput = [this](const TType& type) {
1971 return language == EShLangTessEvaluation &&
1972 type.contains([](const TType* t) {
1973 return t->getQualifier().builtIn == EbvTessLevelOuter ||
John Kessenichcca42a82017-08-03 18:41:48 -06001974 t->getQualifier().builtIn == EbvTessLevelInner;
steve-lunargf38cca32017-04-03 09:27:53 -06001975 });
1976 };
1977
steve-lunarge752f462017-03-22 18:39:25 -06001978 // if we aren't in the entry point, fix the IO as such and exit
1979 if (userFunction.getName().compare(intermediate.getEntryPointName().c_str()) != 0) {
1980 remapNonEntryPointIO(userFunction);
1981 return nullptr;
1982 }
1983
1984 entryPointFunction = &userFunction; // needed in finish()
1985
1986 // Handle entry point attributes
steve-lunarge7412492017-03-23 11:56:07 -06001987 handleEntryPointAttributes(loc, attributes);
steve-lunarge752f462017-03-22 18:39:25 -06001988
1989 // entry point logic...
John Kessenich02467d82017-01-19 15:41:47 -07001990
1991 // Move parameters and return value to shader in/out
1992 TVariable* entryPointOutput; // gets created in remapEntryPointIO
1993 TVector<TVariable*> inputs;
1994 TVector<TVariable*> outputs;
1995 remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs);
1996
1997 // Further this return/in/out transform by flattening, splitting, and assigning locations
1998 const auto makeVariableInOut = [&](TVariable& variable) {
1999 if (variable.getType().isStruct()) {
John Kesseniche29ff3c2017-08-11 00:17:26 -06002000 if (variable.getType().getQualifier().isArrayedIo(language)) {
2001 if (variable.getType().containsBuiltIn())
2002 split(variable);
John Kessenich41aa1992017-10-11 14:03:45 -06002003 } else if (shouldFlatten(variable.getType(), EvqVaryingIn /* not assigned yet, but close enough */, true))
John Kessenichd5aedc12017-08-06 19:42:42 -06002004 flatten(variable, false /* don't track linkage here, it will be tracked in assignToInterface() */);
John Kessenich02467d82017-01-19 15:41:47 -07002005 }
John Kesseniche29ff3c2017-08-11 00:17:26 -06002006 // TODO: flatten arrays too
2007 // TODO: flatten everything in I/O
2008 // TODO: replace all split with flatten, make all paths can create flattened I/O, then split code can be removed
steve-lunarg067eb9b2017-04-01 15:34:48 -06002009
LoopDawg307b6502017-07-05 11:33:06 -06002010 // For clip and cull distance, multiple output variables potentially get merged
2011 // into one in assignClipCullDistance. That code in assignClipCullDistance
2012 // handles the interface logic, so we avoid it here in that case.
2013 if (!isClipOrCullDistance(variable.getType()))
2014 assignToInterface(variable);
John Kessenich02467d82017-01-19 15:41:47 -07002015 };
John Kessenich89f8d1e2017-06-27 15:17:38 -06002016 if (entryPointOutput != nullptr)
John Kessenich02467d82017-01-19 15:41:47 -07002017 makeVariableInOut(*entryPointOutput);
2018 for (auto it = inputs.begin(); it != inputs.end(); ++it)
John Kessenichb6be80f2017-08-04 12:04:44 -06002019 if (!isDsPcfInput((*it)->getType())) // wait until the end for PCF input (see comment below)
steve-lunargf38cca32017-04-03 09:27:53 -06002020 makeVariableInOut(*(*it));
John Kessenich02467d82017-01-19 15:41:47 -07002021 for (auto it = outputs.begin(); it != outputs.end(); ++it)
2022 makeVariableInOut(*(*it));
2023
steve-lunargf38cca32017-04-03 09:27:53 -06002024 // In the domain shader, PCF input must be at the end of the linkage. That's because in the
2025 // hull shader there is no ordering: the output comes from the separate PCF, which does not
2026 // participate in the argument list. That is always put at the end of the HS linkage, so the
2027 // input side of the DS must match. The argument may be in any position in the DS argument list
2028 // however, so this ensures the linkage is built in the correct order regardless of argument order.
2029 if (language == EShLangTessEvaluation) {
2030 for (auto it = inputs.begin(); it != inputs.end(); ++it)
John Kessenichb6be80f2017-08-04 12:04:44 -06002031 if (isDsPcfInput((*it)->getType()))
steve-lunargf38cca32017-04-03 09:27:53 -06002032 makeVariableInOut(*(*it));
2033 }
2034
John Kessenich02467d82017-01-19 15:41:47 -07002035 // Synthesize the call
2036
2037 pushScope(); // matches the one in handleFunctionBody()
2038
2039 // new signature
2040 TType voidType(EbtVoid);
2041 TFunction synthEntryPoint(&userFunction.getName(), voidType);
2042 TIntermAggregate* synthParams = new TIntermAggregate();
2043 intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc);
2044 intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str());
2045 intermediate.incrementEntryPointCount();
2046 TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table
2047
2048 // change original name
2049 userFunction.addPrefix("@"); // change the name in the function, but not in the symbol table
2050
2051 // Copy inputs (shader-in -> calling arg), while building up the call node
2052 TVector<TVariable*> argVars;
2053 TIntermAggregate* synthBody = new TIntermAggregate();
2054 auto inputIt = inputs.begin();
2055 TIntermTyped* callingArgs = nullptr;
steve-lunarg08e0c082017-03-29 20:01:13 -06002056
John Kessenich02467d82017-01-19 15:41:47 -07002057 for (int i = 0; i < userFunction.getParamCount(); i++) {
2058 TParameter& param = userFunction[i];
2059 argVars.push_back(makeInternalVariable(*param.name, *param.type));
2060 argVars.back()->getWritableType().getQualifier().makeTemporary();
LoopDawg4a145db2017-09-13 08:44:39 -06002061
2062 // Track the input patch, which is the only non-builtin supported by hull shader PCF.
2063 if (param.getDeclaredBuiltIn() == EbvInputPatch)
2064 inputPatch = argVars.back();
2065
John Kessenich02467d82017-01-19 15:41:47 -07002066 TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
2067 handleFunctionArgument(&callee, callingArgs, arg);
2068 if (param.type->getQualifier().isParamInput()) {
2069 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
2070 intermediate.addSymbol(**inputIt)));
2071 inputIt++;
2072 }
2073 }
2074
2075 // Call
2076 currentCaller = synthEntryPoint.getMangledName();
2077 TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
2078 currentCaller = userFunction.getMangledName();
2079
2080 // Return value
steve-lunarge752f462017-03-22 18:39:25 -06002081 if (entryPointOutput) {
2082 TIntermTyped* returnAssign;
2083
steve-lunarge7412492017-03-23 11:56:07 -06002084 // For hull shaders, the wrapped entry point return value is written to
2085 // an array element as indexed by invocation ID, which we might have to make up.
2086 // This is required to match SPIR-V semantics.
steve-lunarge752f462017-03-22 18:39:25 -06002087 if (language == EShLangTessControl) {
John Kessenich2b4f77f2017-08-04 13:51:54 -06002088 TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
steve-lunarge752f462017-03-22 18:39:25 -06002089
2090 // If there is no user declared invocation ID, we must make one.
2091 if (invocationIdSym == nullptr) {
2092 TType invocationIdType(EbtUint, EvqIn, 1);
2093 TString* invocationIdName = NewPoolTString("InvocationId");
2094 invocationIdType.getQualifier().builtIn = EbvInvocationId;
2095
2096 TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType);
2097
2098 globalQualifierFix(loc, variable->getWritableType().getQualifier());
2099 trackLinkage(*variable);
2100
2101 invocationIdSym = intermediate.addSymbol(*variable);
2102 }
2103
2104 TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput),
2105 invocationIdSym, loc);
LoopDawga5d86162017-09-10 09:46:55 -06002106
2107 // Set the type of the array element being dereferenced
2108 const TType derefElementType(entryPointOutput->getType(), 0);
2109 element->setType(derefElementType);
steve-lunarge752f462017-03-22 18:39:25 -06002110
2111 returnAssign = handleAssign(loc, EOpAssign, element, callReturn);
2112 } else {
2113 returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn);
2114 }
steve-lunarge752f462017-03-22 18:39:25 -06002115 intermediate.growAggregate(synthBody, returnAssign);
2116 } else
John Kessenich02467d82017-01-19 15:41:47 -07002117 intermediate.growAggregate(synthBody, callReturn);
2118
2119 // Output copies
2120 auto outputIt = outputs.begin();
2121 for (int i = 0; i < userFunction.getParamCount(); i++) {
2122 TParameter& param = userFunction[i];
steve-lunarg08e0c082017-03-29 20:01:13 -06002123
2124 // GS outputs are via emit, so we do not copy them here.
John Kessenich02467d82017-01-19 15:41:47 -07002125 if (param.type->getQualifier().isParamOutput()) {
steve-lunarga4bfed12017-04-23 19:44:28 -06002126 if (param.getDeclaredBuiltIn() == EbvGsOutputStream) {
steve-lunarg08e0c082017-03-29 20:01:13 -06002127 // GS output stream does not assign outputs here: it's the Append() method
2128 // which writes to the output, probably multiple times separated by Emit.
2129 // We merely remember the output to use, here.
2130 gsStreamOutput = *outputIt;
2131 } else {
2132 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign,
2133 intermediate.addSymbol(**outputIt),
2134 intermediate.addSymbol(*argVars[i])));
2135 }
2136
John Kessenich02467d82017-01-19 15:41:47 -07002137 outputIt++;
2138 }
2139 }
2140
2141 // Put the pieces together to form a full function subtree
2142 // for the synthesized entry point.
2143 synthBody->setOperator(EOpSequence);
2144 TIntermNode* synthFunctionDef = synthParams;
2145 handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef);
2146
steve-lunarg858c9282017-01-07 08:54:10 -07002147 entryPointFunctionBody = synthBody;
2148
John Kessenich02467d82017-01-19 15:41:47 -07002149 return synthFunctionDef;
John Kesseniche01a9bc2016-03-12 20:11:22 -07002150}
2151
John Kessenich2ceec682017-07-28 16:20:13 -06002152void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody,
2153 TIntermNode*& node)
John Kessenicha3051662016-09-02 19:13:36 -06002154{
2155 node = intermediate.growAggregate(node, functionBody);
2156 intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc);
2157 node->getAsAggregate()->setName(function.getMangledName().c_str());
2158
2159 popScope();
John Kessenich37789792017-03-21 23:56:40 -06002160 if (function.hasImplicitThis())
2161 popImplicitThis();
John Kessenicha3051662016-09-02 19:13:36 -06002162
2163 if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue)
2164 error(loc, "function does not return a value:", "", function.getName().c_str());
2165}
2166
John Kessenich830b0cc2016-08-29 18:10:47 -06002167// AST I/O is done through shader globals declared in the 'in' or 'out'
2168// storage class. An HLSL entry point has a return value, input parameters
2169// and output parameters. These need to get remapped to the AST I/O.
John Kessenich727b3742017-02-03 17:57:55 -07002170void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue,
John Kessenich02467d82017-01-19 15:41:47 -07002171 TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
John Kessenich830b0cc2016-08-29 18:10:47 -06002172{
John Kessenich4329d552017-06-21 01:35:57 -06002173 // We might have in input structure type with no decorations that caused it
2174 // to look like an input type, yet it has (e.g.) interpolation types that
2175 // must be modified that turn it into an input type.
2176 // Hence, a missing ioTypeMap for 'input' might need to be synthesized.
2177 const auto synthesizeEditedInput = [this](TType& type) {
2178 // True if a type needs to be 'flat'
2179 const auto needsFlat = [](const TType& type) {
2180 return type.containsBasicType(EbtInt) ||
2181 type.containsBasicType(EbtUint) ||
2182 type.containsBasicType(EbtInt64) ||
2183 type.containsBasicType(EbtUint64) ||
2184 type.containsBasicType(EbtBool) ||
2185 type.containsBasicType(EbtDouble);
2186 };
2187
2188 if (language == EShLangFragment && needsFlat(type)) {
2189 if (type.isStruct()) {
2190 TTypeList* finalList = nullptr;
2191 auto it = ioTypeMap.find(type.getStruct());
2192 if (it == ioTypeMap.end() || it->second.input == nullptr) {
2193 // Getting here means we have no input struct, but we need one.
2194 auto list = new TTypeList;
2195 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
2196 TType* newType = new TType;
2197 newType->shallowCopy(*member->type);
2198 TTypeLoc typeLoc = { newType, member->loc };
2199 list->push_back(typeLoc);
2200 }
2201 // install the new input type
2202 if (it == ioTypeMap.end()) {
2203 tIoKinds newLists = { list, nullptr, nullptr };
2204 ioTypeMap[type.getStruct()] = newLists;
2205 } else
2206 it->second.input = list;
2207 finalList = list;
2208 } else
2209 finalList = it->second.input;
2210 // edit for 'flat'
2211 for (auto member = finalList->begin(); member != finalList->end(); ++member) {
2212 if (needsFlat(*member->type)) {
2213 member->type->getQualifier().clearInterpolation();
2214 member->type->getQualifier().flat = true;
2215 }
2216 }
2217 } else {
2218 type.getQualifier().clearInterpolation();
2219 type.getQualifier().flat = true;
2220 }
2221 }
2222 };
2223
John Kessenichbf472862017-02-05 20:27:30 -07002224 // Do the actual work to make a type be a shader input or output variable,
2225 // and clear the original to be non-IO (for use as a normal function parameter/return).
baldurk5d5db802017-03-09 17:48:59 +00002226 const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* {
John Kessenich727b3742017-02-03 17:57:55 -07002227 TVariable* ioVariable = makeInternalVariable(name, type);
John Kessenichbf472862017-02-05 20:27:30 -07002228 clearUniformInputOutput(type.getQualifier());
John Kessenich4329d552017-06-21 01:35:57 -06002229 if (type.isStruct()) {
John Kessenichbf472862017-02-05 20:27:30 -07002230 auto newLists = ioTypeMap.find(ioVariable->getType().getStruct());
2231 if (newLists != ioTypeMap.end()) {
2232 if (storage == EvqVaryingIn && newLists->second.input)
2233 ioVariable->getWritableType().setStruct(newLists->second.input);
2234 else if (storage == EvqVaryingOut && newLists->second.output)
2235 ioVariable->getWritableType().setStruct(newLists->second.output);
2236 }
John Kessenich9e079532016-09-02 20:05:19 -06002237 }
steve-lunarg7afe1342017-03-18 22:24:14 -06002238 if (storage == EvqVaryingIn) {
John Kessenichbf472862017-02-05 20:27:30 -07002239 correctInput(ioVariable->getWritableType().getQualifier());
steve-lunarg7afe1342017-03-18 22:24:14 -06002240 if (language == EShLangTessEvaluation)
2241 if (!ioVariable->getType().isArray())
2242 ioVariable->getWritableType().getQualifier().patch = true;
2243 } else {
John Kessenichbf472862017-02-05 20:27:30 -07002244 correctOutput(ioVariable->getWritableType().getQualifier());
steve-lunarg7afe1342017-03-18 22:24:14 -06002245 }
John Kessenichbf472862017-02-05 20:27:30 -07002246 ioVariable->getWritableType().getQualifier().storage = storage;
steve-lunarg067eb9b2017-04-01 15:34:48 -06002247
steve-lunargccb076a2017-04-05 11:03:02 -06002248 fixBuiltInIoType(ioVariable->getWritableType());
2249
John Kessenich727b3742017-02-03 17:57:55 -07002250 return ioVariable;
John Kessenich9e079532016-09-02 20:05:19 -06002251 };
2252
John Kessenich830b0cc2016-08-29 18:10:47 -06002253 // return value is actually a shader-scoped output (out)
steve-lunarge752f462017-03-22 18:39:25 -06002254 if (function.getType().getBasicType() == EbtVoid) {
John Kessenich02467d82017-01-19 15:41:47 -07002255 returnValue = nullptr;
steve-lunarge752f462017-03-22 18:39:25 -06002256 } else {
2257 if (language == EShLangTessControl) {
2258 // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an
2259 // array in SPIR-V semantics. We'll write to it indexed by invocation ID.
2260
2261 returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2262
2263 TType outputType;
2264 outputType.shallowCopy(function.getType());
2265
2266 // vertices has necessarily already been set when handling entry point attributes.
2267 TArraySizes arraySizes;
2268 arraySizes.addInnerSize(intermediate.getVertices());
2269 outputType.newArraySizes(arraySizes);
2270
2271 clearUniformInputOutput(function.getWritableType().getQualifier());
2272 returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut);
2273 } else {
2274 returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2275 }
2276 }
John Kessenich830b0cc2016-08-29 18:10:47 -06002277
2278 // parameters are actually shader-scoped inputs and outputs (in or out)
2279 for (int i = 0; i < function.getParamCount(); i++) {
John Kessenichcd0a78a2016-09-09 16:32:09 -06002280 TType& paramType = *function[i].type;
John Kessenich02467d82017-01-19 15:41:47 -07002281 if (paramType.getQualifier().isParamInput()) {
John Kessenich4329d552017-06-21 01:35:57 -06002282 synthesizeEditedInput(paramType);
John Kessenichbf472862017-02-05 20:27:30 -07002283 TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
John Kessenich02467d82017-01-19 15:41:47 -07002284 inputs.push_back(argAsGlobal);
2285 }
2286 if (paramType.getQualifier().isParamOutput()) {
John Kessenichbf472862017-02-05 20:27:30 -07002287 TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut);
John Kessenich02467d82017-01-19 15:41:47 -07002288 outputs.push_back(argAsGlobal);
John Kessenich02467d82017-01-19 15:41:47 -07002289 }
John Kessenich830b0cc2016-08-29 18:10:47 -06002290 }
2291}
2292
John Kessenich07350f32016-09-02 20:23:27 -06002293// An HLSL function that looks like an entry point, but is not,
2294// declares entry point IO built-ins, but these have to be undone.
John Kessenich6fccb3c2016-09-19 16:01:41 -06002295void HlslParseContext::remapNonEntryPointIO(TFunction& function)
John Kessenich07350f32016-09-02 20:23:27 -06002296{
John Kessenich07350f32016-09-02 20:23:27 -06002297 // return value
2298 if (function.getType().getBasicType() != EbtVoid)
John Kessenichbf472862017-02-05 20:27:30 -07002299 clearUniformInputOutput(function.getWritableType().getQualifier());
John Kessenich07350f32016-09-02 20:23:27 -06002300
steve-lunargdd8287a2017-02-23 18:04:12 -07002301 // parameters.
2302 // References to structuredbuffer types are left unmodified
John Kessenich07350f32016-09-02 20:23:27 -06002303 for (int i = 0; i < function.getParamCount(); i++)
steve-lunargdd8287a2017-02-23 18:04:12 -07002304 if (!isReference(*function[i].type))
2305 clearUniformInputOutput(function[i].type->getQualifier());
John Kessenich07350f32016-09-02 20:23:27 -06002306}
2307
steve-lunargc4a13072016-08-09 11:28:03 -06002308// Handle function returns, including type conversions to the function return type
2309// if necessary.
2310TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
2311{
John Kessenicha3051662016-09-02 19:13:36 -06002312 functionReturnsValue = true;
John Kessenich6a70eb72016-08-28 15:00:23 -06002313
steve-lunargc4a13072016-08-09 11:28:03 -06002314 if (currentFunctionType->getBasicType() == EbtVoid) {
2315 error(loc, "void function cannot return a value", "return", "");
2316 return intermediate.addBranch(EOpReturn, loc);
2317 } else if (*currentFunctionType != value->getType()) {
John Kessenich087a4542016-10-06 16:56:54 -06002318 value = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
2319 if (value && *currentFunctionType != value->getType())
John Kessenichd5d9ffb2017-04-18 21:07:05 -06002320 value = intermediate.addUniShapeConversion(EOpReturn, *currentFunctionType, value);
John Kessenich778806a2017-08-19 17:29:44 -06002321 if (value == nullptr || *currentFunctionType != value->getType()) {
steve-lunargc4a13072016-08-09 11:28:03 -06002322 error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
John Kessenich087a4542016-10-06 16:56:54 -06002323 return value;
steve-lunargc4a13072016-08-09 11:28:03 -06002324 }
John Kessenich6a70eb72016-08-28 15:00:23 -06002325 }
2326
John Kessenich02467d82017-01-19 15:41:47 -07002327 return intermediate.addBranch(EOpReturn, value, loc);
steve-lunargc4a13072016-08-09 11:28:03 -06002328}
2329
steve-lunarga2e75312016-12-14 15:22:25 -07002330void HlslParseContext::handleFunctionArgument(TFunction* function,
2331 TIntermTyped*& arguments, TIntermTyped* newArg)
John Kessenichd016be12016-03-13 11:24:20 -06002332{
steve-lunarg26d31452016-12-23 18:56:57 -07002333 TParameter param = { 0, new TType, nullptr };
John Kessenich4678ca92016-05-13 09:33:42 -06002334 param.type->shallowCopy(newArg->getType());
steve-lunarga2e75312016-12-14 15:22:25 -07002335
John Kessenichd016be12016-03-13 11:24:20 -06002336 function->addParameter(param);
John Kessenich4678ca92016-05-13 09:33:42 -06002337 if (arguments)
2338 arguments = intermediate.growAggregate(arguments, newArg);
2339 else
2340 arguments = newArg;
John Kessenichd016be12016-03-13 11:24:20 -06002341}
2342
LoopDawgb22c0692017-12-06 16:52:03 -07002343// Position may require special handling: we can optionally invert Y.
2344// See: https://github.com/KhronosGroup/glslang/issues/1173
2345// https://github.com/KhronosGroup/glslang/issues/494
2346TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op,
2347 TIntermTyped* left, TIntermTyped* right)
2348{
2349 // If we are not asked for Y inversion, use a plain old assign.
2350 if (!intermediate.getInvertY())
2351 return intermediate.addAssign(op, left, right, loc);
2352
2353 // If we get here, we should invert Y.
2354 TIntermAggregate* assignList = nullptr;
2355
2356 // If this is a complex rvalue, we don't want to dereference it many times. Create a temporary.
2357 TVariable* rhsTempVar = nullptr;
2358 rhsTempVar = makeInternalVariable("@position", right->getType());
2359 rhsTempVar->getWritableType().getQualifier().makeTemporary();
2360
2361 {
2362 TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2363 assignList = intermediate.growAggregate(assignList,
2364 intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
2365 }
2366
2367 // pos.y = -pos.y
2368 {
2369 const int Y = 1;
2370
2371 TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
2372 TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
2373 TIntermTyped* index = intermediate.addConstantUnion(Y, loc);
2374
2375 TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
2376 TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
2377
2378 const TType derefType(right->getType(), 0);
2379
2380 lhsElement->setType(derefType);
2381 rhsElement->setType(derefType);
2382
2383 TIntermTyped* yNeg = intermediate.addUnaryMath(EOpNegative, rhsElement, loc);
2384
2385 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, yNeg, loc));
2386 }
2387
2388 // Assign the rhs temp (now with Y inversion) to the final output
2389 {
2390 TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
2391 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
2392 }
2393
2394 assert(assignList != nullptr);
2395 assignList->setOperator(EOpSequence);
2396
2397 return assignList;
2398}
2399
LoopDawgc44b95f2017-06-22 12:08:00 -06002400// Clip and cull distance require special handling due to a semantic mismatch. In HLSL,
2401// these can be float scalar, float vector, or arrays of float scalar or float vector.
2402// In SPIR-V, they are arrays of scalar floats in all cases. We must copy individual components
2403// (e.g, both x and y components of a float2) out into the destination float array.
2404//
2405// The values are assigned to sequential members of the output array. The inner dimension
2406// is vector components. The outer dimension is array elements.
LoopDawg307b6502017-07-05 11:33:06 -06002407TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId,
LoopDawgc44b95f2017-06-22 12:08:00 -06002408 TIntermTyped* left, TIntermTyped* right)
2409{
LoopDawge2cda3c2017-08-23 12:34:42 -06002410 switch (language) {
2411 case EShLangFragment:
2412 case EShLangVertex:
LoopDawg5e5b12e2017-08-28 14:02:19 -06002413 case EShLangGeometry:
LoopDawge2cda3c2017-08-23 12:34:42 -06002414 break;
2415 default:
2416 error(loc, "unimplemented: clip/cull not currently implemented for this stage", "", "");
2417 return nullptr;
2418 }
2419
LoopDawg307b6502017-07-05 11:33:06 -06002420 TVariable** clipCullVar = nullptr;
2421
LoopDawge2cda3c2017-08-23 12:34:42 -06002422 // Figure out if we are assigning to, or from, clip or cull distance.
2423 const bool isOutput = isClipOrCullDistance(left->getType());
LoopDawg307b6502017-07-05 11:33:06 -06002424
LoopDawge2cda3c2017-08-23 12:34:42 -06002425 // This is the rvalue or lvalue holding the clip or cull distance.
2426 TIntermTyped* clipCullNode = isOutput ? left : right;
2427 // This is the value going into or out of the clip or cull distance.
2428 TIntermTyped* internalNode = isOutput ? right : left;
2429
2430 const TBuiltInVariable builtInType = clipCullNode->getQualifier().builtIn;
LoopDawg307b6502017-07-05 11:33:06 -06002431
LoopDawg5e5b12e2017-08-28 14:02:19 -06002432 decltype(clipSemanticNSizeIn)* semanticNSize = nullptr;
LoopDawg307b6502017-07-05 11:33:06 -06002433
2434 // Refer to either the clip or the cull distance, depending on semantic.
2435 switch (builtInType) {
2436 case EbvClipDistance:
LoopDawg5e5b12e2017-08-28 14:02:19 -06002437 clipCullVar = isOutput ? &clipDistanceOutput : &clipDistanceInput;
2438 semanticNSize = isOutput ? &clipSemanticNSizeOut : &clipSemanticNSizeIn;
LoopDawg307b6502017-07-05 11:33:06 -06002439 break;
2440 case EbvCullDistance:
LoopDawg5e5b12e2017-08-28 14:02:19 -06002441 clipCullVar = isOutput ? &cullDistanceOutput : &cullDistanceInput;
2442 semanticNSize = isOutput ? &cullSemanticNSizeOut : &cullSemanticNSizeIn;
LoopDawg307b6502017-07-05 11:33:06 -06002443 break;
2444
2445 // called invalidly: we expected a clip or a cull distance.
2446 // static compile time problem: should not happen.
2447 default: assert(0); return nullptr;
2448 }
2449
2450 // This is the offset in the destination array of a given semantic's data
2451 std::array<int, maxClipCullRegs> semanticOffset;
2452
2453 // Calculate offset of variable of semantic N in destination array
2454 int arrayLoc = 0;
2455 int vecItems = 0;
2456
2457 for (int x = 0; x < maxClipCullRegs; ++x) {
2458 // See if we overflowed the vec4 packing
2459 if ((vecItems + (*semanticNSize)[x]) > 4) {
2460 arrayLoc = (arrayLoc + 3) & (~0x3); // round up to next multiple of 4
2461 vecItems = 0;
2462 }
2463
2464 semanticOffset[x] = arrayLoc;
2465 vecItems += (*semanticNSize)[x];
2466 arrayLoc += (*semanticNSize)[x];
2467 }
LoopDawg5e5b12e2017-08-28 14:02:19 -06002468
2469
2470 // It can have up to 2 array dimensions (in the case of geometry shader inputs)
2471 const TArraySizes* const internalArraySizes = internalNode->getType().getArraySizes();
2472 const int internalArrayDims = internalNode->getType().isArray() ? internalArraySizes->getNumDims() : 0;
LoopDawge2cda3c2017-08-23 12:34:42 -06002473 // vector sizes:
LoopDawg5e5b12e2017-08-28 14:02:19 -06002474 const int internalVectorSize = internalNode->getType().getVectorSize();
2475 // array sizes, or 1 if it's not an array:
2476 const int internalInnerArraySize = (internalArrayDims > 0 ? internalArraySizes->getDimSize(internalArrayDims-1) : 1);
2477 const int internalOuterArraySize = (internalArrayDims > 1 ? internalArraySizes->getDimSize(0) : 1);
2478
2479 // The created type may be an array of arrays, e.g, for geometry shader inputs.
2480 const bool isImplicitlyArrayed = (language == EShLangGeometry && !isOutput);
LoopDawg307b6502017-07-05 11:33:06 -06002481
John Kessenich2ceec682017-07-28 16:20:13 -06002482 // If we haven't created the output already, create it now.
LoopDawg307b6502017-07-05 11:33:06 -06002483 if (*clipCullVar == nullptr) {
LoopDawge2cda3c2017-08-23 12:34:42 -06002484 // ClipDistance and CullDistance are handled specially in the entry point input/output copy
2485 // algorithm, because they may need to be unpacked from components of vectors (or a scalar)
2486 // into a float array, or vice versa. Here, we make the array the right size and type,
LoopDawg5e5b12e2017-08-28 14:02:19 -06002487 // which depends on the incoming data, which has several potential dimensions:
2488 // * Semantic ID
2489 // * vector size
2490 // * array size
2491 // Of those, semantic ID and array size cannot appear simultaneously.
2492 //
2493 // Also to note: for implicitly arrayed forms (e.g, geometry shader inputs), we need to create two
2494 // array dimensions. The shader's declaration may have one or two array dimensions. One is always
2495 // the geometry's dimension.
2496
2497 const bool useInnerSize = internalArrayDims > 1 || !isImplicitlyArrayed;
2498
2499 const int requiredInnerArraySize = arrayLoc * (useInnerSize ? internalInnerArraySize : 1);
2500 const int requiredOuterArraySize = (internalArrayDims > 0) ? internalArraySizes->getDimSize(0) : 1;
LoopDawg307b6502017-07-05 11:33:06 -06002501
LoopDawge2cda3c2017-08-23 12:34:42 -06002502 TType clipCullType(EbtFloat, clipCullNode->getType().getQualifier().storage, 1);
2503 clipCullType.getQualifier() = clipCullNode->getType().getQualifier();
LoopDawg307b6502017-07-05 11:33:06 -06002504
2505 // Create required array dimension
2506 TArraySizes arraySizes;
LoopDawg5e5b12e2017-08-28 14:02:19 -06002507 if (isImplicitlyArrayed)
2508 arraySizes.addInnerSize(requiredOuterArraySize);
2509 arraySizes.addInnerSize(requiredInnerArraySize);
LoopDawg307b6502017-07-05 11:33:06 -06002510 clipCullType.newArraySizes(arraySizes);
2511
2512 // Obtain symbol name: we'll use that for the symbol we introduce.
LoopDawge2cda3c2017-08-23 12:34:42 -06002513 TIntermSymbol* sym = clipCullNode->getAsSymbolNode();
LoopDawg307b6502017-07-05 11:33:06 -06002514 assert(sym != nullptr);
2515
2516 // We are moving the semantic ID from the layout location, so it is no longer needed or
2517 // desired there.
2518 clipCullType.getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
2519
2520 // Create variable and track its linkage
2521 *clipCullVar = makeInternalVariable(sym->getName().c_str(), clipCullType);
2522
2523 trackLinkage(**clipCullVar);
2524 }
2525
2526 // Create symbol for the clip or cull variable.
LoopDawge2cda3c2017-08-23 12:34:42 -06002527 TIntermSymbol* clipCullSym = intermediate.addSymbol(**clipCullVar);
LoopDawg307b6502017-07-05 11:33:06 -06002528
LoopDawg307b6502017-07-05 11:33:06 -06002529 // vector sizes:
LoopDawg5e5b12e2017-08-28 14:02:19 -06002530 const int clipCullVectorSize = clipCullSym->getType().getVectorSize();
2531
2532 // array sizes, or 1 if it's not an array:
2533 const TArraySizes* const clipCullArraySizes = clipCullSym->getType().getArraySizes();
2534 const int clipCullOuterArraySize = isImplicitlyArrayed ? clipCullArraySizes->getDimSize(0) : 1;
2535 const int clipCullInnerArraySize = clipCullArraySizes->getDimSize(isImplicitlyArrayed ? 1 : 0);
LoopDawgc44b95f2017-06-22 12:08:00 -06002536
LoopDawge2cda3c2017-08-23 12:34:42 -06002537 // clipCullSym has got to be an array of scalar floats, per SPIR-V semantics.
LoopDawgc44b95f2017-06-22 12:08:00 -06002538 // fixBuiltInIoType() should have handled that upstream.
LoopDawge2cda3c2017-08-23 12:34:42 -06002539 assert(clipCullSym->getType().isArray());
2540 assert(clipCullSym->getType().getVectorSize() == 1);
2541 assert(clipCullSym->getType().getBasicType() == EbtFloat);
LoopDawgc44b95f2017-06-22 12:08:00 -06002542
LoopDawgc44b95f2017-06-22 12:08:00 -06002543 // We may be creating multiple sub-assignments. This is an aggregate to hold them.
2544 // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed.
2545 TIntermAggregate* assignList = nullptr;
2546
LoopDawge2cda3c2017-08-23 12:34:42 -06002547 // Holds individual component assignments as we make them.
2548 TIntermTyped* clipCullAssign = nullptr;
2549
LoopDawgc44b95f2017-06-22 12:08:00 -06002550 // If the types are homomorphic, use a simple assign. No need to mess about with
2551 // individual components.
LoopDawge2cda3c2017-08-23 12:34:42 -06002552 if (clipCullSym->getType().isArray() == internalNode->getType().isArray() &&
LoopDawg5e5b12e2017-08-28 14:02:19 -06002553 clipCullInnerArraySize == internalInnerArraySize &&
2554 clipCullOuterArraySize == internalOuterArraySize &&
2555 clipCullVectorSize == internalVectorSize) {
LoopDawge2cda3c2017-08-23 12:34:42 -06002556
2557 if (isOutput)
2558 clipCullAssign = intermediate.addAssign(op, clipCullSym, internalNode, loc);
2559 else
2560 clipCullAssign = intermediate.addAssign(op, internalNode, clipCullSym, loc);
2561
2562 assignList = intermediate.growAggregate(assignList, clipCullAssign);
LoopDawgc44b95f2017-06-22 12:08:00 -06002563 assignList->setOperator(EOpSequence);
LoopDawge2cda3c2017-08-23 12:34:42 -06002564
LoopDawgc44b95f2017-06-22 12:08:00 -06002565 return assignList;
2566 }
2567
LoopDawge2cda3c2017-08-23 12:34:42 -06002568 // We are going to copy each component of the internal (per array element if indicated) to sequential
2569 // array elements of the clipCullSym. This tracks the lhs element we're writing to as we go along.
LoopDawg307b6502017-07-05 11:33:06 -06002570 // We may be starting in the middle - e.g, for a non-zero semantic ID calculated above.
LoopDawg5e5b12e2017-08-28 14:02:19 -06002571 int clipCullInnerArrayPos = semanticOffset[semanticId];
2572 int clipCullOuterArrayPos = 0;
2573
2574 // Lambda to add an index to a node, set the type of the result, and return the new node.
2575 const auto addIndex = [this, &loc](TIntermTyped* node, int pos) -> TIntermTyped* {
2576 const TType derefType(node->getType(), 0);
2577 node = intermediate.addIndex(EOpIndexDirect, node, intermediate.addConstantUnion(pos, loc), loc);
2578 node->setType(derefType);
2579 return node;
2580 };
LoopDawgc44b95f2017-06-22 12:08:00 -06002581
LoopDawge2cda3c2017-08-23 12:34:42 -06002582 // Loop through every component of every element of the internal, and copy to or from the matching external.
LoopDawg5e5b12e2017-08-28 14:02:19 -06002583 for (int internalOuterArrayPos = 0; internalOuterArrayPos < internalOuterArraySize; ++internalOuterArrayPos) {
2584 for (int internalInnerArrayPos = 0; internalInnerArrayPos < internalInnerArraySize; ++internalInnerArrayPos) {
2585 for (int internalComponent = 0; internalComponent < internalVectorSize; ++internalComponent) {
2586 // clip/cull array member to read from / write to:
2587 TIntermTyped* clipCullMember = clipCullSym;
LoopDawgd6f4d9b2017-08-28 14:05:41 -06002588
LoopDawg5e5b12e2017-08-28 14:02:19 -06002589 // If implicitly arrayed, there is an outer array dimension involved
2590 if (isImplicitlyArrayed)
2591 clipCullMember = addIndex(clipCullMember, clipCullOuterArrayPos);
2592
2593 // Index into proper array position for clip cull member
2594 clipCullMember = addIndex(clipCullMember, clipCullInnerArrayPos++);
2595
2596 // if needed, start over with next outer array slice.
2597 if (isImplicitlyArrayed && clipCullInnerArrayPos >= clipCullInnerArraySize) {
2598 clipCullInnerArrayPos = semanticOffset[semanticId];
2599 ++clipCullOuterArrayPos;
2600 }
2601
2602 // internal member to read from / write to:
2603 TIntermTyped* internalMember = internalNode;
2604
2605 // If internal node has outer array dimension, index appropriately.
2606 if (internalArrayDims > 1)
2607 internalMember = addIndex(internalMember, internalOuterArrayPos);
2608
2609 // If internal node has inner array dimension, index appropriately.
2610 if (internalArrayDims > 0)
2611 internalMember = addIndex(internalMember, internalInnerArrayPos);
2612
2613 // If internal node is a vector, extract the component of interest.
2614 if (internalNode->getType().isVector())
2615 internalMember = addIndex(internalMember, internalComponent);
2616
2617 // Create an assignment: output from internal to clip cull, or input from clip cull to internal.
2618 if (isOutput)
2619 clipCullAssign = intermediate.addAssign(op, clipCullMember, internalMember, loc);
2620 else
2621 clipCullAssign = intermediate.addAssign(op, internalMember, clipCullMember, loc);
2622
2623 // Track assignment in the sequence.
2624 assignList = intermediate.growAggregate(assignList, clipCullAssign);
LoopDawgd6f4d9b2017-08-28 14:05:41 -06002625 }
LoopDawgc44b95f2017-06-22 12:08:00 -06002626 }
2627 }
2628
2629 assert(assignList != nullptr);
2630 assignList->setOperator(EOpSequence);
2631
2632 return assignList;
2633}
2634
John Kessenichd21baed2016-09-16 03:05:12 -06002635// Some simple source assignments need to be flattened to a sequence
John Kessenichfdf63472017-01-13 12:27:52 -07002636// of AST assignments. Catch these and flatten, otherwise, pass through
John Kessenichd21baed2016-09-16 03:05:12 -06002637// to intermediate.addAssign().
John Kessenichfdf63472017-01-13 12:27:52 -07002638//
2639// Also, assignment to matrix swizzles requires multiple component assignments,
2640// intercept those as well.
John Kessenich2ceec682017-07-28 16:20:13 -06002641TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
2642 TIntermTyped* right)
John Kessenichd21baed2016-09-16 03:05:12 -06002643{
steve-lunarg90707962016-10-07 19:35:40 -06002644 if (left == nullptr || right == nullptr)
2645 return nullptr;
2646
John Kessenich15fa7ef2017-09-07 04:33:11 -06002647 // writing to opaques will require fixing transforms
2648 if (left->getType().containsOpaque())
2649 intermediate.setNeedsLegalization();
2650
John Kessenichfdf63472017-01-13 12:27:52 -07002651 if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle)
2652 return handleAssignToMatrixSwizzle(loc, op, left, right);
2653
LoopDawga5d86162017-09-10 09:46:55 -06002654 // Return true if the given node is an index operation into a split variable.
2655 const auto indexesSplit = [this](const TIntermTyped* node) -> bool {
2656 const TIntermBinary* binaryNode = node->getAsBinaryNode();
2657
2658 if (binaryNode == nullptr)
2659 return false;
2660
2661 return (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect) &&
2662 wasSplit(binaryNode->getLeft());
2663 };
2664
LoopDawgb22c0692017-12-06 16:52:03 -07002665 // Return true if this stage assigns clip position with potentially inverted Y
2666 const auto assignsClipPos = [this](const TIntermTyped* node) -> bool {
2667 return node->getType().getQualifier().builtIn == EbvPosition &&
2668 (language == EShLangVertex || language == EShLangGeometry || language == EShLangTessEvaluation);
2669 };
2670
LoopDawga5d86162017-09-10 09:46:55 -06002671 const bool isSplitLeft = wasSplit(left) || indexesSplit(left);
2672 const bool isSplitRight = wasSplit(right) || indexesSplit(right);
John Kessenichd2ce8382016-09-16 19:44:00 -06002673
steve-lunarg132d3312016-12-19 15:48:01 -07002674 const bool isFlattenLeft = wasFlattened(left);
2675 const bool isFlattenRight = wasFlattened(right);
steve-lunarga2e75312016-12-14 15:22:25 -07002676
LoopDawga5d86162017-09-10 09:46:55 -06002677 // OK to do a single assign if neither side is split or flattened. Otherwise,
2678 // fall through to a member-wise copy.
John Kessenich318a3792017-07-30 23:39:48 -06002679 if (!isFlattenLeft && !isFlattenRight && !isSplitLeft && !isSplitRight) {
LoopDawgc44b95f2017-06-22 12:08:00 -06002680 // Clip and cull distance requires more processing. See comment above assignClipCullDistance.
LoopDawge2cda3c2017-08-23 12:34:42 -06002681 if (isClipOrCullDistance(left->getType()) || isClipOrCullDistance(right->getType())) {
2682 const bool isOutput = isClipOrCullDistance(left->getType());
2683
2684 const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation;
LoopDawg307b6502017-07-05 11:33:06 -06002685 return assignClipCullDistance(loc, op, semanticId, left, right);
LoopDawgb22c0692017-12-06 16:52:03 -07002686 } else if (assignsClipPos(left)) {
2687 // Position can require special handling: see comment above assignPosition
2688 return assignPosition(loc, op, left, right);
LoopDawg307b6502017-07-05 11:33:06 -06002689 }
LoopDawgc44b95f2017-06-22 12:08:00 -06002690
John Kessenichd21baed2016-09-16 03:05:12 -06002691 return intermediate.addAssign(op, left, right, loc);
LoopDawgc44b95f2017-06-22 12:08:00 -06002692 }
John Kessenichd21baed2016-09-16 03:05:12 -06002693
steve-lunarge0b9deb2016-09-16 13:26:37 -06002694 TIntermAggregate* assignList = nullptr;
John Kessenichf9115002016-09-18 23:10:22 -06002695 const TVector<TVariable*>* leftVariables = nullptr;
2696 const TVector<TVariable*>* rightVariables = nullptr;
steve-lunarge0b9deb2016-09-16 13:26:37 -06002697
steve-lunarg2199c242016-10-02 22:13:22 -06002698 // A temporary to store the right node's value, so we don't keep indirecting into it
2699 // if it's not a simple symbol.
John Kessenich0e6e2ff2017-07-16 05:46:13 -06002700 TVariable* rhsTempVar = nullptr;
steve-lunarg2199c242016-10-02 22:13:22 -06002701
2702 // If the RHS is a simple symbol node, we'll copy it for each member.
2703 TIntermSymbol* cloneSymNode = nullptr;
2704
steve-lunarg2199c242016-10-02 22:13:22 -06002705 int memberCount = 0;
2706
2707 // Track how many items there are to copy.
2708 if (left->getType().isStruct())
Alex Szpakowski49ad2b72016-10-08 22:07:20 -03002709 memberCount = (int)left->getType().getStruct()->size();
steve-lunarg2199c242016-10-02 22:13:22 -06002710 if (left->getType().isArray())
2711 memberCount = left->getType().getCumulativeArraySize();
2712
steve-lunarg132d3312016-12-19 15:48:01 -07002713 if (isFlattenLeft)
steve-lunarga2b01a02016-11-28 17:09:54 -07002714 leftVariables = &flattenMap.find(left->getAsSymbolNode()->getId())->second.members;
steve-lunarg2199c242016-10-02 22:13:22 -06002715
steve-lunarg132d3312016-12-19 15:48:01 -07002716 if (isFlattenRight) {
steve-lunarga2b01a02016-11-28 17:09:54 -07002717 rightVariables = &flattenMap.find(right->getAsSymbolNode()->getId())->second.members;
steve-lunarg2199c242016-10-02 22:13:22 -06002718 } else {
2719 // The RHS is not flattened. There are several cases:
2720 // 1. 1 item to copy: Use the RHS directly.
2721 // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp.
2722 // 3. >1 item, complex RHS: assign it to a new temp variable, and create a TIntermSymbol for each member.
John Kessenichecba76f2017-01-06 00:34:48 -07002723
steve-lunarg2199c242016-10-02 22:13:22 -06002724 if (memberCount <= 1) {
2725 // case 1: we'll use the symbol directly below. Nothing to do.
2726 } else {
2727 if (right->getAsSymbolNode() != nullptr) {
2728 // case 2: we'll copy the symbol per iteration below.
2729 cloneSymNode = right->getAsSymbolNode();
2730 } else {
2731 // case 3: assign to a temp, and indirect into that.
2732 rhsTempVar = makeInternalVariable("flattenTemp", right->getType());
2733 rhsTempVar->getWritableType().getQualifier().makeTemporary();
2734 TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc);
2735
2736 // Add this to the aggregate being built.
John Kessenich02a14e72017-06-29 18:23:31 -06002737 assignList = intermediate.growAggregate(assignList,
2738 intermediate.addAssign(op, noFlattenRHS, right, loc), loc);
steve-lunarg2199c242016-10-02 22:13:22 -06002739 }
2740 }
2741 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06002742
John Kessenich8bcdf2e2017-07-30 16:54:02 -06002743 // When dealing with split arrayed structures of built-ins, the arrayness is moved to the extracted built-in
steve-lunargec712eb2017-02-02 16:32:02 -07002744 // variables, which is awkward when copying between split and unsplit structures. This variable tracks
2745 // array indirections so they can be percolated from outer structs to inner variables.
2746 std::vector <int> arrayElement;
2747
John Kessenich700bdeb2017-10-04 13:27:43 -06002748 TStorageQualifier leftStorage = left->getType().getQualifier().storage;
2749 TStorageQualifier rightStorage = right->getType().getQualifier().storage;
steve-lunarg132d3312016-12-19 15:48:01 -07002750
John Kessenich700bdeb2017-10-04 13:27:43 -06002751 int leftOffset = findSubtreeOffset(*left);
2752 int rightOffset = findSubtreeOffset(*right);
2753
John Kessenich41aa1992017-10-11 14:03:45 -06002754 const auto getMember = [&](bool isLeft, const TType& type, int member, TIntermTyped* splitNode, int splitMember,
2755 bool flattened)
John Kessenich02a14e72017-06-29 18:23:31 -06002756 -> TIntermTyped * {
John Kessenich318a3792017-07-30 23:39:48 -06002757 const bool split = isLeft ? isSplitLeft : isSplitRight;
2758
steve-lunarge0b9deb2016-09-16 13:26:37 -06002759 TIntermTyped* subTree;
John Kessenich700bdeb2017-10-04 13:27:43 -06002760 const TType derefType(type, member);
John Kessenich3322dd82017-08-08 20:02:21 -06002761 const TVariable* builtInVar = nullptr;
2762 if ((flattened || split) && derefType.isBuiltIn()) {
John Kessenich3322dd82017-08-08 20:02:21 -06002763 auto splitPair = splitBuiltIns.find(HlslParseContext::tInterstageIoData(
2764 derefType.getQualifier().builtIn,
John Kessenich700bdeb2017-10-04 13:27:43 -06002765 isLeft ? leftStorage : rightStorage));
John Kessenich3322dd82017-08-08 20:02:21 -06002766 if (splitPair != splitBuiltIns.end())
2767 builtInVar = splitPair->second;
2768 }
2769 if (builtInVar != nullptr) {
2770 // copy from interstage IO built-in if needed
2771 subTree = intermediate.addSymbol(*builtInVar);
steve-lunargec712eb2017-02-02 16:32:02 -07002772
LoopDawg0cff5102017-12-12 16:21:22 -07002773 if (subTree->getType().isArray()) {
2774 // Arrayness of builtIn symbols isn't handled by the normal recursion:
2775 // it's been extracted and moved to the built-in.
2776 if (!arrayElement.empty()) {
2777 const TType splitDerefType(subTree->getType(), arrayElement.back());
2778 subTree = intermediate.addIndex(EOpIndexDirect, subTree,
2779 intermediate.addConstantUnion(arrayElement.back(), loc), loc);
2780 subTree->setType(splitDerefType);
2781 } else if (splitNode->getAsOperator() != nullptr && (splitNode->getAsOperator()->getOp() == EOpIndexIndirect)) {
2782 // This might also be a stage with arrayed outputs, in which case there's an index
2783 // operation we should transfer to the output builtin.
2784
2785 const TType splitDerefType(subTree->getType(), 0);
2786 subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
2787 splitNode->getAsBinaryNode()->getRight(), loc);
2788 subTree->setType(splitDerefType);
2789 }
steve-lunargec712eb2017-02-02 16:32:02 -07002790 }
John Kessenich41aa1992017-10-11 14:03:45 -06002791 } else if (flattened && !shouldFlatten(derefType, isLeft ? leftStorage : rightStorage, false)) {
Unknowna6085872017-10-03 09:10:26 +02002792 if (isLeft)
John Kessenich700bdeb2017-10-04 13:27:43 -06002793 subTree = intermediate.addSymbol(*(*leftVariables)[leftOffset++]);
Unknowna6085872017-10-03 09:10:26 +02002794 else
John Kessenich700bdeb2017-10-04 13:27:43 -06002795 subTree = intermediate.addSymbol(*(*rightVariables)[rightOffset++]);
steve-lunarga2b01a02016-11-28 17:09:54 -07002796 } else {
John Kessenich318a3792017-07-30 23:39:48 -06002797 // Index operator if it's an aggregate, else EOpNull
John Kessenich700bdeb2017-10-04 13:27:43 -06002798 const TOperator accessOp = type.isArray() ? EOpIndexDirect
2799 : type.isStruct() ? EOpIndexDirectStruct
John Kessenich318a3792017-07-30 23:39:48 -06002800 : EOpNull;
2801 if (accessOp == EOpNull) {
steve-lunarg194f0f32017-03-17 18:51:05 -06002802 subTree = splitNode;
2803 } else {
John Kessenich318a3792017-07-30 23:39:48 -06002804 subTree = intermediate.addIndex(accessOp, splitNode, intermediate.addConstantUnion(splitMember, loc),
2805 loc);
steve-lunarg194f0f32017-03-17 18:51:05 -06002806 const TType splitDerefType(splitNode->getType(), splitMember);
steve-lunarg194f0f32017-03-17 18:51:05 -06002807 subTree->setType(splitDerefType);
2808 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06002809 }
2810
2811 return subTree;
2812 };
2813
steve-lunarga2b01a02016-11-28 17:09:54 -07002814 // Use the proper RHS node: a new symbol from a TVariable, copy
2815 // of an TIntermSymbol node, or sometimes the right node directly.
John Kessenich02a14e72017-06-29 18:23:31 -06002816 right = rhsTempVar != nullptr ? intermediate.addSymbol(*rhsTempVar, loc) :
2817 cloneSymNode != nullptr ? intermediate.addSymbol(*cloneSymNode) :
steve-lunarga2b01a02016-11-28 17:09:54 -07002818 right;
steve-lunarge0b9deb2016-09-16 13:26:37 -06002819
John Kessenichd21baed2016-09-16 03:05:12 -06002820 // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the
2821 // whole thing. So, we'll resort to an explicit type via std::function.
John Kessenich41aa1992017-10-11 14:03:45 -06002822 const std::function<void(TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
2823 bool topLevel)>
2824 traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
2825 bool topLevel) -> void {
John Kessenichd21baed2016-09-16 03:05:12 -06002826 // If we get here, we are assigning to or from a whole array or struct that must be
2827 // flattened, so have to do member-by-member assignment:
2828
John Kessenich41aa1992017-10-11 14:03:45 -06002829 bool shouldFlattenSubsetLeft = isFlattenLeft && shouldFlatten(left->getType(), leftStorage, topLevel);
2830 bool shouldFlattenSubsetRight = isFlattenRight && shouldFlatten(right->getType(), rightStorage, topLevel);
2831
2832 if ((left->getType().isArray() || right->getType().isArray()) &&
2833 (shouldFlattenSubsetLeft || isSplitLeft ||
2834 shouldFlattenSubsetRight || isSplitRight)) {
John Kessenich318a3792017-07-30 23:39:48 -06002835 const int elementsL = left->getType().isArray() ? left->getType().getOuterArraySize() : 1;
steve-lunarg194f0f32017-03-17 18:51:05 -06002836 const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1;
2837
John Kessenich15fa7ef2017-09-07 04:33:11 -06002838 // The arrays might not be the same size,
2839 // e.g., if the size has been forced for EbvTessLevelInner/Outer.
steve-lunarg194f0f32017-03-17 18:51:05 -06002840 const int elementsToCopy = std::min(elementsL, elementsR);
John Kessenichd2ce8382016-09-16 19:44:00 -06002841
steve-lunarg132d3312016-12-19 15:48:01 -07002842 // array case
John Kessenich750c2d02017-05-26 00:01:36 -06002843 for (int element = 0; element < elementsToCopy; ++element) {
steve-lunarg858c9282017-01-07 08:54:10 -07002844 arrayElement.push_back(element);
2845
steve-lunargec712eb2017-02-02 16:32:02 -07002846 // Add a new AST symbol node if we have a temp variable holding a complex RHS.
John Kessenich41aa1992017-10-11 14:03:45 -06002847 TIntermTyped* subLeft = getMember(true, left->getType(), element, left, element,
2848 shouldFlattenSubsetLeft);
2849 TIntermTyped* subRight = getMember(false, right->getType(), element, right, element,
2850 shouldFlattenSubsetRight);
John Kessenichd2ce8382016-09-16 19:44:00 -06002851
John Kessenich41aa1992017-10-11 14:03:45 -06002852 TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left->getType(), element, splitLeft,
2853 element, shouldFlattenSubsetLeft)
John Kessenich02a14e72017-06-29 18:23:31 -06002854 : subLeft;
John Kessenich41aa1992017-10-11 14:03:45 -06002855 TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), element, splitRight,
2856 element, shouldFlattenSubsetRight)
John Kessenich02a14e72017-06-29 18:23:31 -06002857 : subRight;
steve-lunarg65cdff92017-01-19 15:18:00 -07002858
John Kessenich41aa1992017-10-11 14:03:45 -06002859 traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
steve-lunargec712eb2017-02-02 16:32:02 -07002860
2861 arrayElement.pop_back();
John Kessenichd2ce8382016-09-16 19:44:00 -06002862 }
John Kessenich41aa1992017-10-11 14:03:45 -06002863 } else if (left->getType().isStruct() && (shouldFlattenSubsetLeft || isSplitLeft ||
2864 shouldFlattenSubsetRight || isSplitRight)) {
John Kessenichfcea3022016-09-16 21:16:04 -06002865 // struct case
steve-lunarga2e75312016-12-14 15:22:25 -07002866 const auto& membersL = *left->getType().getStruct();
2867 const auto& membersR = *right->getType().getStruct();
John Kessenichfcea3022016-09-16 21:16:04 -06002868
steve-lunarg132d3312016-12-19 15:48:01 -07002869 // These track the members in the split structures corresponding to the same in the unsplit structures,
2870 // which we traverse in parallel.
steve-lunarga2e75312016-12-14 15:22:25 -07002871 int memberL = 0;
2872 int memberR = 0;
steve-lunarge0b9deb2016-09-16 13:26:37 -06002873
steve-lunargd8e34c52017-03-24 08:56:37 -06002874 // Handle empty structure assignment
2875 if (int(membersL.size()) == 0 && int(membersR.size()) == 0)
2876 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
2877
steve-lunarga2e75312016-12-14 15:22:25 -07002878 for (int member = 0; member < int(membersL.size()); ++member) {
2879 const TType& typeL = *membersL[member].type;
2880 const TType& typeR = *membersR[member].type;
2881
John Kessenich41aa1992017-10-11 14:03:45 -06002882 TIntermTyped* subLeft = getMember(true, left->getType(), member, left, member,
2883 shouldFlattenSubsetLeft);
2884 TIntermTyped* subRight = getMember(false, right->getType(), member, right, member,
2885 shouldFlattenSubsetRight);
steve-lunarga2e75312016-12-14 15:22:25 -07002886
steve-lunarg132d3312016-12-19 15:48:01 -07002887 // If there is no splitting, use the same values to avoid inefficiency.
John Kessenich41aa1992017-10-11 14:03:45 -06002888 TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left->getType(), member, splitLeft,
2889 memberL, shouldFlattenSubsetLeft)
John Kessenich02a14e72017-06-29 18:23:31 -06002890 : subLeft;
John Kessenich41aa1992017-10-11 14:03:45 -06002891 TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), member, splitRight,
2892 memberR, shouldFlattenSubsetRight)
John Kessenich02a14e72017-06-29 18:23:31 -06002893 : subRight;
steve-lunarg132d3312016-12-19 15:48:01 -07002894
LoopDawge2cda3c2017-08-23 12:34:42 -06002895 if (isClipOrCullDistance(subSplitLeft->getType()) || isClipOrCullDistance(subSplitRight->getType())) {
John Kessenich8bcdf2e2017-07-30 16:54:02 -06002896 // Clip and cull distance built-in assignment is complex in its own right, and is handled in
LoopDawgc44b95f2017-06-22 12:08:00 -06002897 // a separate function dedicated to that task. See comment above assignClipCullDistance;
LoopDawg307b6502017-07-05 11:33:06 -06002898
LoopDawge2cda3c2017-08-23 12:34:42 -06002899 const bool isOutput = isClipOrCullDistance(subSplitLeft->getType());
2900
John Kessenich8bcdf2e2017-07-30 16:54:02 -06002901 // Since all clip/cull semantics boil down to the same built-in type, we need to get the
LoopDawg307b6502017-07-05 11:33:06 -06002902 // semantic ID from the dereferenced type's layout location, to avoid an N-1 mapping.
LoopDawge2cda3c2017-08-23 12:34:42 -06002903 const TType derefType((isOutput ? left : right)->getType(), member);
LoopDawg307b6502017-07-05 11:33:06 -06002904 const int semanticId = derefType.getQualifier().layoutLocation;
2905
2906 TIntermAggregate* clipCullAssign = assignClipCullDistance(loc, op, semanticId,
2907 subSplitLeft, subSplitRight);
2908
2909 assignList = intermediate.growAggregate(assignList, clipCullAssign, loc);
LoopDawgb22c0692017-12-06 16:52:03 -07002910 } else if (assignsClipPos(subSplitLeft)) {
2911 // Position can require special handling: see comment above assignPosition
2912 TIntermTyped* positionAssign = assignPosition(loc, op, subSplitLeft, subSplitRight);
2913 assignList = intermediate.growAggregate(assignList, positionAssign, loc);
John Kessenich41aa1992017-10-11 14:03:45 -06002914 } else if (!shouldFlattenSubsetLeft && !shouldFlattenSubsetRight &&
2915 !typeL.containsBuiltIn() && !typeR.containsBuiltIn()) {
John Kessenich02a14e72017-06-29 18:23:31 -06002916 // If this is the final flattening (no nested types below to flatten)
2917 // we'll copy the member, else recurse into the type hierarchy.
2918 // However, if splitting the struct, that means we can copy a whole
2919 // subtree here IFF it does not itself contain any interstage built-in
2920 // IO variables, so we only have to recurse into it if there's something
2921 // for splitting to do. That can save a lot of AST verbosity for
LoopDawgc44b95f2017-06-22 12:08:00 -06002922 // a bunch of memberwise copies.
2923
John Kessenich02a14e72017-06-29 18:23:31 -06002924 assignList = intermediate.growAggregate(assignList,
2925 intermediate.addAssign(op, subSplitLeft, subSplitRight, loc),
2926 loc);
steve-lunarga2e75312016-12-14 15:22:25 -07002927 } else {
John Kessenich41aa1992017-10-11 14:03:45 -06002928 traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
steve-lunarga2e75312016-12-14 15:22:25 -07002929 }
2930
John Kessenich6042eb42017-08-02 17:08:43 -06002931 memberL += (typeL.isBuiltIn() ? 0 : 1);
2932 memberR += (typeR.isBuiltIn() ? 0 : 1);
steve-lunarge0b9deb2016-09-16 13:26:37 -06002933 }
2934 } else {
steve-lunarg194f0f32017-03-17 18:51:05 -06002935 // Member copy
2936 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
steve-lunarge0b9deb2016-09-16 13:26:37 -06002937 }
2938
2939 };
2940
steve-lunarg132d3312016-12-19 15:48:01 -07002941 TIntermTyped* splitLeft = left;
2942 TIntermTyped* splitRight = right;
2943
2944 // If either left or right was a split structure, we must read or write it, but still have to
John Kessenich8bcdf2e2017-07-30 16:54:02 -06002945 // parallel-recurse through the unsplit structure to identify the built-in IO vars.
LoopDawga5d86162017-09-10 09:46:55 -06002946 // The left can be either a symbol, or an index into a symbol (e.g, array reference)
2947 if (isSplitLeft) {
2948 if (indexesSplit(left)) {
2949 // Index case: Refer to the indexed symbol, if the left is an index operator.
2950 const TIntermSymbol* symNode = left->getAsBinaryNode()->getLeft()->getAsSymbolNode();
2951
2952 TIntermTyped* splitLeftNonIo = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
2953
2954 splitLeft = intermediate.addIndex(left->getAsBinaryNode()->getOp(), splitLeftNonIo,
2955 left->getAsBinaryNode()->getRight(), loc);
2956
2957 const TType derefType(splitLeftNonIo->getType(), 0);
2958 splitLeft->setType(derefType);
2959 } else {
2960 // Symbol case: otherwise, if not indexed, we have the symbol directly.
2961 const TIntermSymbol* symNode = left->getAsSymbolNode();
2962 splitLeft = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
2963 }
2964 }
steve-lunarg132d3312016-12-19 15:48:01 -07002965
2966 if (isSplitRight)
John Kessenichd5aedc12017-08-06 19:42:42 -06002967 splitRight = intermediate.addSymbol(*getSplitNonIoVar(right->getAsSymbolNode()->getId()), loc);
steve-lunarg132d3312016-12-19 15:48:01 -07002968
steve-lunarga2b01a02016-11-28 17:09:54 -07002969 // This makes the whole assignment, recursing through subtypes as needed.
John Kessenich41aa1992017-10-11 14:03:45 -06002970 traverse(left, right, splitLeft, splitRight, true);
steve-lunarge0b9deb2016-09-16 13:26:37 -06002971
2972 assert(assignList != nullptr);
John Kessenichd21baed2016-09-16 03:05:12 -06002973 assignList->setOperator(EOpSequence);
2974
2975 return assignList;
2976}
2977
John Kessenichfdf63472017-01-13 12:27:52 -07002978// An assignment to matrix swizzle must be decomposed into individual assignments.
2979// These must be selected component-wise from the RHS and stored component-wise
2980// into the LHS.
John Kessenich2ceec682017-07-28 16:20:13 -06002981TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
2982 TIntermTyped* right)
John Kessenichfdf63472017-01-13 12:27:52 -07002983{
2984 assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle);
2985
2986 if (op != EOpAssign)
2987 error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", "");
2988
2989 // isolate the matrix and swizzle nodes
2990 TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped();
2991 const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence();
2992
2993 // if the RHS isn't already a simple vector, let's store into one
2994 TIntermSymbol* vector = right->getAsSymbolNode();
2995 TIntermTyped* vectorAssign = nullptr;
2996 if (vector == nullptr) {
2997 // create a new intermediate vector variable to assign to
Graham Wihlidal6f332f32017-02-17 19:05:14 +01002998 TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2);
John Kessenichfdf63472017-01-13 12:27:52 -07002999 vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc);
3000
3001 // assign the right to the new vector
3002 vectorAssign = handleAssign(loc, op, vector, right);
3003 }
3004
3005 // Assign the vector components to the matrix components.
3006 // Store this as a sequence, so a single aggregate node represents this
3007 // entire operation.
3008 TIntermAggregate* result = intermediate.makeAggregate(vectorAssign);
3009 TType columnType(matrix->getType(), 0);
3010 TType componentType(columnType, 0);
3011 TType indexType(EbtInt);
3012 for (int i = 0; i < (int)swizzle.size(); i += 2) {
3013 // the right component, single index into the RHS vector
3014 TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector,
3015 intermediate.addConstantUnion(i/2, loc), loc);
3016
3017 // the left component, double index into the LHS matrix
3018 TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix,
3019 intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(),
3020 indexType, loc),
3021 loc);
3022 leftComp->setType(columnType);
3023 leftComp = intermediate.addIndex(EOpIndexDirect, leftComp,
3024 intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(),
3025 indexType, loc),
3026 loc);
3027 leftComp->setType(componentType);
3028
3029 // Add the assignment to the aggregate
3030 result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc));
3031 }
3032
3033 result->setOp(EOpSequence);
3034
3035 return result;
3036}
3037
LoopDawg58910702016-06-13 09:22:28 -06003038//
3039// HLSL atomic operations have slightly different arguments than
3040// GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic.
3041// This provides the post-decomposition equivalent opcode.
3042//
3043TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage)
3044{
3045 switch (op) {
steve-lunarg22322362016-10-19 10:15:25 -06003046 case EOpInterlockedAdd: return isImage ? EOpImageAtomicAdd : EOpAtomicAdd;
3047 case EOpInterlockedAnd: return isImage ? EOpImageAtomicAnd : EOpAtomicAnd;
LoopDawg58910702016-06-13 09:22:28 -06003048 case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap;
steve-lunarg22322362016-10-19 10:15:25 -06003049 case EOpInterlockedMax: return isImage ? EOpImageAtomicMax : EOpAtomicMax;
3050 case EOpInterlockedMin: return isImage ? EOpImageAtomicMin : EOpAtomicMin;
3051 case EOpInterlockedOr: return isImage ? EOpImageAtomicOr : EOpAtomicOr;
3052 case EOpInterlockedXor: return isImage ? EOpImageAtomicXor : EOpAtomicXor;
LoopDawg58910702016-06-13 09:22:28 -06003053 case EOpInterlockedExchange: return isImage ? EOpImageAtomicExchange : EOpAtomicExchange;
John Kessenichecba76f2017-01-06 00:34:48 -07003054 case EOpInterlockedCompareStore: // TODO: ...
LoopDawg58910702016-06-13 09:22:28 -06003055 default:
3056 error(loc, "unknown atomic operation", "unknown op", "");
3057 return EOpNull;
3058 }
3059}
3060
LoopDawg4624a022016-06-20 13:26:59 -06003061//
LoopDawga2b79912016-07-14 14:45:14 -06003062// Create a combined sampler/texture from separate sampler and texture.
LoopDawg4624a022016-06-20 13:26:59 -06003063//
John Kessenich2ceec682017-07-28 16:20:13 -06003064TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex,
3065 TIntermTyped* argSampler)
LoopDawga2b79912016-07-14 14:45:14 -06003066{
3067 TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler);
3068
3069 txcombine->getSequence().push_back(argTex);
3070 txcombine->getSequence().push_back(argSampler);
3071
3072 TSampler samplerType = argTex->getType().getSampler();
3073 samplerType.combined = true;
3074
LoopDawg73c57bb2017-10-05 16:25:52 -06003075 // TODO:
3076 // This block exists until the spec no longer requires shadow modes on texture objects.
3077 // It can be deleted after that, along with the shadowTextureVariant member.
LoopDawg195f5842017-09-27 09:12:51 -06003078 {
LoopDawg73c57bb2017-10-05 16:25:52 -06003079 const bool shadowMode = argSampler->getType().getSampler().shadow;
3080
LoopDawg195f5842017-09-27 09:12:51 -06003081 TIntermSymbol* texSymbol = argTex->getAsSymbolNode();
3082
3083 if (texSymbol == nullptr)
3084 texSymbol = argTex->getAsBinaryNode()->getLeft()->getAsSymbolNode();
3085
LoopDawg73c57bb2017-10-05 16:25:52 -06003086 if (texSymbol == nullptr) {
3087 error(loc, "unable to find texture symbol", "", "");
3088 return nullptr;
LoopDawg195f5842017-09-27 09:12:51 -06003089 }
LoopDawg195f5842017-09-27 09:12:51 -06003090
LoopDawg73c57bb2017-10-05 16:25:52 -06003091 // This forces the texture's shadow state to be the sampler's
3092 // shadow state. This depends on downstream optimization to
3093 // DCE one variant in [shadow, nonshadow] if both are present,
3094 // or the SPIR-V module would be invalid.
3095 int newId = texSymbol->getId();
3096
3097 // Check to see if this texture has been given a shadow mode already.
3098 // If so, look up the one we already have.
3099 const auto textureShadowEntry = textureShadowVariant.find(texSymbol->getId());
3100
3101 if (textureShadowEntry != textureShadowVariant.end())
3102 newId = textureShadowEntry->second->get(shadowMode);
3103 else
3104 textureShadowVariant[texSymbol->getId()] = new tShadowTextureSymbols;
3105
3106 // Sometimes we have to create another symbol (if this texture has been seen before,
3107 // and we haven't created the form for this shadow mode).
3108 if (newId == -1) {
3109 TType texType;
3110 texType.shallowCopy(argTex->getType());
3111 texType.getSampler().shadow = shadowMode; // set appropriate shadow mode.
3112 globalQualifierFix(loc, texType.getQualifier());
3113
3114 TVariable* newTexture = makeInternalVariable(texSymbol->getName(), texType);
3115
3116 trackLinkage(*newTexture);
3117
3118 newId = newTexture->getUniqueId();
3119 }
3120
3121 assert(newId != -1);
3122
3123 if (textureShadowVariant.find(newId) == textureShadowVariant.end())
3124 textureShadowVariant[newId] = textureShadowVariant[texSymbol->getId()];
3125
3126 textureShadowVariant[newId]->set(shadowMode, newId);
3127
3128 // Remember this shadow mode in the texture and the merged type.
3129 argTex->getWritableType().getSampler().shadow = shadowMode;
3130 samplerType.shadow = shadowMode;
3131
LoopDawg028c5a82017-10-07 16:42:37 -06003132 texSymbol->switchId(newId);
LoopDawg73c57bb2017-10-05 16:25:52 -06003133 }
LoopDawg195f5842017-09-27 09:12:51 -06003134
LoopDawga2b79912016-07-14 14:45:14 -06003135 txcombine->setType(TType(samplerType, EvqTemporary));
3136 txcombine->setLoc(loc);
3137
3138 return txcombine;
3139}
3140
steve-lunarg8e26feb2017-04-10 08:19:21 -06003141// Return true if this a buffer type that has an associated counter buffer.
steve-lunarga4bfed12017-04-23 19:44:28 -06003142bool HlslParseContext::hasStructBuffCounter(const TType& type) const
steve-lunarg8e26feb2017-04-10 08:19:21 -06003143{
steve-lunarga4bfed12017-04-23 19:44:28 -06003144 switch (type.getQualifier().declaredBuiltIn) {
steve-lunarg8e26feb2017-04-10 08:19:21 -06003145 case EbvAppendConsume: // fall through...
3146 case EbvRWStructuredBuffer: // ...
3147 return true;
3148 default:
John Kessenich750c2d02017-05-26 00:01:36 -06003149 return false; // the other structuredbuffer types do not have a counter.
steve-lunarg8e26feb2017-04-10 08:19:21 -06003150 }
3151}
3152
steve-lunarg2bb1f392017-04-27 11:22:32 -06003153void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type)
3154{
3155 // Counter type
3156 TType* counterType = new TType(EbtInt, EvqBuffer);
3157 counterType->setFieldName("@count");
3158
3159 TTypeList* blockStruct = new TTypeList;
3160 TTypeLoc member = { counterType, loc };
3161 blockStruct->push_back(member);
3162
3163 TType blockType(blockStruct, "", counterType->getQualifier());
3164 blockType.getQualifier().storage = EvqBuffer;
3165
3166 type.shallowCopy(blockType);
3167 shareStructBufferType(type);
3168}
3169
3170// knowledge of how to construct block name, in one place instead of N places.
3171TString HlslParseContext::getStructBuffCounterName(const TString& blockName) const
3172{
3173 return blockName + "@count";
3174}
3175
steve-lunarg8e26feb2017-04-10 08:19:21 -06003176// declare counter for a structured buffer type
3177void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name)
3178{
3179 // Bail out if not a struct buffer
3180 if (! isStructBufferType(bufferType))
3181 return;
3182
steve-lunarga4bfed12017-04-23 19:44:28 -06003183 if (! hasStructBuffCounter(bufferType))
steve-lunarg8e26feb2017-04-10 08:19:21 -06003184 return;
3185
steve-lunarg2bb1f392017-04-27 11:22:32 -06003186 TType blockType;
3187 counterBufferType(loc, blockType);
steve-lunarg8e26feb2017-04-10 08:19:21 -06003188
steve-lunarg2bb1f392017-04-27 11:22:32 -06003189 TString* blockName = new TString(getStructBuffCounterName(name));
steve-lunarg8e26feb2017-04-10 08:19:21 -06003190
steve-lunarg2bb1f392017-04-27 11:22:32 -06003191 // Counter buffer does not have its own counter buffer. TODO: there should be a better way to track this.
steve-lunarg8e26feb2017-04-10 08:19:21 -06003192 structBufferCounter[*blockName] = false;
3193
steve-lunarg8e26feb2017-04-10 08:19:21 -06003194 shareStructBufferType(blockType);
3195 declareBlock(loc, blockType, blockName);
3196}
3197
3198// return the counter that goes with a given structuredbuffer
3199TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer)
3200{
3201 // Bail out if not a struct buffer
3202 if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
3203 return nullptr;
3204
steve-lunarg2bb1f392017-04-27 11:22:32 -06003205 const TString counterBlockName(getStructBuffCounterName(buffer->getAsSymbolNode()->getName()));
steve-lunarg8e26feb2017-04-10 08:19:21 -06003206
3207 // Mark the counter as being used
steve-lunarg2bb1f392017-04-27 11:22:32 -06003208 structBufferCounter[counterBlockName] = true;
steve-lunarg8e26feb2017-04-10 08:19:21 -06003209
steve-lunarg2bb1f392017-04-27 11:22:32 -06003210 TIntermTyped* counterVar = handleVariable(loc, &counterBlockName); // find the block structure
steve-lunarg8e26feb2017-04-10 08:19:21 -06003211 TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct
3212
3213 TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc);
3214 counterMember->setType(TType(EbtInt));
3215 return counterMember;
3216}
3217
3218
LoopDawga2b79912016-07-14 14:45:14 -06003219//
steve-lunarg5da1f032017-02-12 17:50:28 -07003220// Decompose structure buffer methods into AST
3221//
3222void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3223{
steve-lunarg8e26feb2017-04-10 08:19:21 -06003224 if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr)
steve-lunarg5da1f032017-02-12 17:50:28 -07003225 return;
3226
3227 const TOperator op = node->getAsOperator()->getOp();
steve-lunarg8e26feb2017-04-10 08:19:21 -06003228 TIntermAggregate* argAggregate = arguments->getAsAggregate();
steve-lunargdb2e3b42017-03-31 12:47:34 -06003229
steve-lunargdd8287a2017-02-23 18:04:12 -07003230 // Buffer is the object upon which method is called, so always arg 0
steve-lunarg8e26feb2017-04-10 08:19:21 -06003231 TIntermTyped* bufferObj = nullptr;
3232
3233 // The parameters can be an aggregate, or just a the object as a symbol if there are no fn params.
3234 if (argAggregate) {
3235 if (argAggregate->getSequence().empty())
3236 return;
3237 bufferObj = argAggregate->getSequence()[0]->getAsTyped();
3238 } else {
3239 bufferObj = arguments->getAsSymbolNode();
3240 }
steve-lunargdd8287a2017-02-23 18:04:12 -07003241
steve-lunarg12bc9aa2017-04-13 18:42:58 -06003242 if (bufferObj == nullptr || bufferObj->getAsSymbolNode() == nullptr)
3243 return;
3244
steve-lunarg12bc9aa2017-04-13 18:42:58 -06003245 // Some methods require a hidden internal counter, obtained via getStructBufferCounter().
3246 // This lambda adds something to it and returns the old value.
3247 const auto incDecCounter = [&](int incval) -> TIntermTyped* {
3248 TIntermTyped* incrementValue = intermediate.addConstantUnion(incval, loc, true);
3249 TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member
3250
3251 if (counter == nullptr)
3252 return nullptr;
3253
3254 TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd);
3255 counterIncrement->setType(TType(EbtUint, EvqTemporary));
3256 counterIncrement->setLoc(loc);
3257 counterIncrement->getSequence().push_back(counter);
3258 counterIncrement->getSequence().push_back(incrementValue);
3259
3260 return counterIncrement;
3261 };
steve-lunargdd8287a2017-02-23 18:04:12 -07003262
3263 // Index to obtain the runtime sized array out of the buffer.
3264 TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj);
3265 if (argArray == nullptr)
3266 return; // It might not be a struct buffer method.
3267
steve-lunarg5da1f032017-02-12 17:50:28 -07003268 switch (op) {
3269 case EOpMethodLoad:
3270 {
steve-lunargf8203a02017-04-20 09:00:56 -06003271 TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index
steve-lunarg5da1f032017-02-12 17:50:28 -07003272
steve-lunarge404e082017-04-20 16:37:14 -06003273 const TType& bufferType = bufferObj->getType();
3274
steve-lunarga4bfed12017-04-23 19:44:28 -06003275 const TBuiltInVariable builtInType = bufferType.getQualifier().declaredBuiltIn;
3276
steve-lunarg5da1f032017-02-12 17:50:28 -07003277 // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
3278 // buffer then, but that's what it calls itself.
steve-lunarge404e082017-04-20 16:37:14 -06003279 const bool isByteAddressBuffer = (builtInType == EbvByteAddressBuffer ||
steve-lunarga4bfed12017-04-23 19:44:28 -06003280 builtInType == EbvRWByteAddressBuffer);
steve-lunarge404e082017-04-20 16:37:14 -06003281
steve-lunarg12bc9aa2017-04-13 18:42:58 -06003282
steve-lunarg5da1f032017-02-12 17:50:28 -07003283 if (isByteAddressBuffer)
John Kessenich2ceec682017-07-28 16:20:13 -06003284 argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex,
3285 intermediate.addConstantUnion(2, loc, true),
steve-lunarg5da1f032017-02-12 17:50:28 -07003286 loc, TType(EbtInt));
3287
3288 // Index into the array to find the item being loaded.
3289 const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
3290
3291 node = intermediate.addIndex(idxOp, argArray, argIndex, loc);
3292
3293 const TType derefType(argArray->getType(), 0);
3294 node->setType(derefType);
3295 }
3296
3297 break;
3298
3299 case EOpMethodLoad2:
3300 case EOpMethodLoad3:
3301 case EOpMethodLoad4:
3302 {
steve-lunargf8203a02017-04-20 09:00:56 -06003303 TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index
steve-lunarg5da1f032017-02-12 17:50:28 -07003304
3305 TOperator constructOp = EOpNull;
3306 int size = 0;
3307
3308 switch (op) {
3309 case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break;
3310 case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break;
3311 case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break;
3312 default: assert(0);
3313 }
3314
3315 TIntermTyped* body = nullptr;
3316
3317 // First, we'll store the address in a variable to avoid multiple shifts
3318 // (we must convert the byte address to an item address)
3319 TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
John Kessenich2ceec682017-07-28 16:20:13 -06003320 intermediate.addConstantUnion(2, loc, true),
3321 loc, TType(EbtInt));
steve-lunarg5da1f032017-02-12 17:50:28 -07003322
3323 TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
3324 TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
3325
3326 body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
3327
3328 TIntermTyped* vec = nullptr;
3329
3330 // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2
3331 // address conversion.
3332 for (int idx=0; idx<size; ++idx) {
3333 TIntermTyped* offsetIdx = byteAddrIdxVar;
3334
3335 // add index offset
3336 if (idx != 0)
John Kessenich2ceec682017-07-28 16:20:13 -06003337 offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx,
3338 intermediate.addConstantUnion(idx, loc, true),
steve-lunarg5da1f032017-02-12 17:50:28 -07003339 loc, TType(EbtInt));
3340
John Kessenich2ceec682017-07-28 16:20:13 -06003341 const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
3342 : EOpIndexIndirect;
steve-lunarg5da1f032017-02-12 17:50:28 -07003343
Jean-François Marquis3f0aff82017-12-15 12:57:33 -05003344 TIntermTyped* indexVal = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
3345
3346 TType derefType(argArray->getType(), 0);
3347 derefType.getQualifier().makeTemporary();
3348 indexVal->setType(derefType);
3349
3350 vec = intermediate.growAggregate(vec, indexVal);
steve-lunarg5da1f032017-02-12 17:50:28 -07003351 }
3352
3353 vec->setType(TType(argArray->getBasicType(), EvqTemporary, size));
3354 vec->getAsAggregate()->setOperator(constructOp);
3355
3356 body = intermediate.growAggregate(body, vec);
3357 body->setType(vec->getType());
3358 body->getAsAggregate()->setOperator(EOpSequence);
3359
3360 node = body;
3361 }
3362
3363 break;
3364
3365 case EOpMethodStore:
3366 case EOpMethodStore2:
3367 case EOpMethodStore3:
3368 case EOpMethodStore4:
3369 {
steve-lunargf8203a02017-04-20 09:00:56 -06003370 TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index
steve-lunarg5da1f032017-02-12 17:50:28 -07003371 TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped(); // value
3372
3373 // Index into the array to find the item being loaded.
3374 // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
LoopDawg8c49f9b2017-11-29 10:00:01 -07003375 // buffer then, but that's what it calls itself).
steve-lunarg5da1f032017-02-12 17:50:28 -07003376
3377 int size = 0;
3378
3379 switch (op) {
3380 case EOpMethodStore: size = 1; break;
3381 case EOpMethodStore2: size = 2; break;
3382 case EOpMethodStore3: size = 3; break;
3383 case EOpMethodStore4: size = 4; break;
3384 default: assert(0);
3385 }
3386
3387 TIntermAggregate* body = nullptr;
3388
3389 // First, we'll store the address in a variable to avoid multiple shifts
3390 // (we must convert the byte address to an item address)
3391 TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
3392 intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt));
3393
3394 TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
3395 TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
3396
3397 body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
3398
3399 for (int idx=0; idx<size; ++idx) {
3400 TIntermTyped* offsetIdx = byteAddrIdxVar;
3401 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
3402
3403 // add index offset
3404 if (idx != 0)
3405 offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, idxConst, loc, TType(EbtInt));
3406
John Kessenich2ceec682017-07-28 16:20:13 -06003407 const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
3408 : EOpIndexIndirect;
steve-lunarg5da1f032017-02-12 17:50:28 -07003409
3410 TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
Sebastian Tafuri59677da2017-11-27 16:27:09 +01003411 const TType derefType(argArray->getType(), 0);
3412 lValue->setType(derefType);
3413
Jean-François Marquis3f0aff82017-12-15 12:57:33 -05003414 TIntermTyped* rValue;
3415 if (size == 1) {
3416 rValue = argValue;
3417 } else {
3418 rValue = intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc);
3419 const TType indexType(argValue->getType(), 0);
3420 rValue->setType(indexType);
3421 }
steve-lunarg5da1f032017-02-12 17:50:28 -07003422
3423 TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
3424
3425 body = intermediate.growAggregate(body, assign);
3426 }
3427
3428 body->setOperator(EOpSequence);
3429 node = body;
3430 }
3431
3432 break;
3433
3434 case EOpMethodGetDimensions:
3435 {
Daniel Koch197082c2017-02-28 09:01:43 -05003436 const int numArgs = (int)argAggregate->getSequence().size();
steve-lunarg5da1f032017-02-12 17:50:28 -07003437 TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped(); // out num items
3438 TIntermTyped* argStride = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr; // out stride
3439
3440 TIntermAggregate* body = nullptr;
3441
3442 // Length output:
3443 if (argArray->getType().isRuntimeSizedArray()) {
3444 TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray,
3445 argNumItems->getType());
3446 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc);
3447 body = intermediate.growAggregate(body, assign, loc);
3448 } else {
3449 const int length = argArray->getType().getOuterArraySize();
John Kessenich2ceec682017-07-28 16:20:13 -06003450 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems,
3451 intermediate.addConstantUnion(length, loc, true), loc);
steve-lunarg5da1f032017-02-12 17:50:28 -07003452 body = intermediate.growAggregate(body, assign, loc);
3453 }
3454
3455 // Stride output:
3456 if (argStride != nullptr) {
3457 int size;
3458 int stride;
3459 intermediate.getBaseAlignment(argArray->getType(), size, stride, false,
3460 argArray->getType().getQualifier().layoutMatrix == ElmRowMajor);
3461
John Kessenich2ceec682017-07-28 16:20:13 -06003462 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride,
3463 intermediate.addConstantUnion(stride, loc, true), loc);
steve-lunarg5da1f032017-02-12 17:50:28 -07003464
3465 body = intermediate.growAggregate(body, assign);
3466 }
3467
3468 body->setOperator(EOpSequence);
3469 node = body;
3470 }
3471
3472 break;
3473
3474 case EOpInterlockedAdd:
3475 case EOpInterlockedAnd:
3476 case EOpInterlockedExchange:
3477 case EOpInterlockedMax:
3478 case EOpInterlockedMin:
3479 case EOpInterlockedOr:
3480 case EOpInterlockedXor:
3481 case EOpInterlockedCompareExchange:
3482 case EOpInterlockedCompareStore:
3483 {
3484 // We'll replace the first argument with the block dereference, and let
3485 // downstream decomposition handle the rest.
3486
3487 TIntermSequence& sequence = argAggregate->getSequence();
3488
steve-lunargf8203a02017-04-20 09:00:56 -06003489 TIntermTyped* argIndex = makeIntegerIndex(sequence[1]->getAsTyped()); // index
steve-lunarg5da1f032017-02-12 17:50:28 -07003490 argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true),
3491 loc, TType(EbtInt));
3492
3493 const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
3494 TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc);
3495
3496 const TType derefType(argArray->getType(), 0);
3497 element->setType(derefType);
3498
3499 // Replace the numeric byte offset parameter with array reference.
3500 sequence[1] = element;
3501 sequence.erase(sequence.begin(), sequence.begin()+1);
3502 }
3503 break;
3504
steve-lunarg8e26feb2017-04-10 08:19:21 -06003505 case EOpMethodIncrementCounter:
steve-lunarg12bc9aa2017-04-13 18:42:58 -06003506 {
3507 node = incDecCounter(1);
3508 break;
3509 }
3510
steve-lunarg8e26feb2017-04-10 08:19:21 -06003511 case EOpMethodDecrementCounter:
3512 {
steve-lunarg12bc9aa2017-04-13 18:42:58 -06003513 TIntermTyped* preIncValue = incDecCounter(-1); // result is original value
3514 node = intermediate.addBinaryNode(EOpAdd, preIncValue, intermediate.addConstantUnion(-1, loc, true), loc,
3515 preIncValue->getType());
3516 break;
steve-lunarg8e26feb2017-04-10 08:19:21 -06003517 }
steve-lunarg12bc9aa2017-04-13 18:42:58 -06003518
3519 case EOpMethodAppend:
3520 {
3521 TIntermTyped* oldCounter = incDecCounter(1);
3522
3523 TIntermTyped* lValue = intermediate.addIndex(EOpIndexIndirect, argArray, oldCounter, loc);
3524 TIntermTyped* rValue = argAggregate->getSequence()[1]->getAsTyped();
3525
3526 const TType derefType(argArray->getType(), 0);
3527 lValue->setType(derefType);
3528
steve-lunarg350b9482017-04-15 08:18:16 -06003529 node = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
steve-lunarg12bc9aa2017-04-13 18:42:58 -06003530
3531 break;
3532 }
3533
3534 case EOpMethodConsume:
3535 {
3536 TIntermTyped* oldCounter = incDecCounter(-1);
3537
John Kessenich2ceec682017-07-28 16:20:13 -06003538 TIntermTyped* newCounter = intermediate.addBinaryNode(EOpAdd, oldCounter,
3539 intermediate.addConstantUnion(-1, loc, true), loc,
3540 oldCounter->getType());
steve-lunarg12bc9aa2017-04-13 18:42:58 -06003541
3542 node = intermediate.addIndex(EOpIndexIndirect, argArray, newCounter, loc);
3543
3544 const TType derefType(argArray->getType(), 0);
3545 node->setType(derefType);
3546
3547 break;
3548 }
steve-lunarg8e26feb2017-04-10 08:19:21 -06003549
steve-lunarg5da1f032017-02-12 17:50:28 -07003550 default:
3551 break; // most pass through unchanged
3552 }
3553}
steve-lunargd4d0b292017-04-26 08:31:56 -06003554
3555// Create array of standard sample positions for given sample count.
3556// TODO: remove when a real method to query sample pos exists in SPIR-V.
3557TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count)
3558{
3559 struct tSamplePos { float x, y; };
3560
3561 static const tSamplePos pos1[] = {
3562 { 0.0/16.0, 0.0/16.0 },
3563 };
3564
3565 // standard sample positions for 2, 4, 8, and 16 samples.
3566 static const tSamplePos pos2[] = {
3567 { 4.0/16.0, 4.0/16.0 }, {-4.0/16.0, -4.0/16.0 },
3568 };
3569
3570 static const tSamplePos pos4[] = {
3571 {-2.0/16.0, -6.0/16.0 }, { 6.0/16.0, -2.0/16.0 }, {-6.0/16.0, 2.0/16.0 }, { 2.0/16.0, 6.0/16.0 },
3572 };
3573
3574 static const tSamplePos pos8[] = {
3575 { 1.0/16.0, -3.0/16.0 }, {-1.0/16.0, 3.0/16.0 }, { 5.0/16.0, 1.0/16.0 }, {-3.0/16.0, -5.0/16.0 },
3576 {-5.0/16.0, 5.0/16.0 }, {-7.0/16.0, -1.0/16.0 }, { 3.0/16.0, 7.0/16.0 }, { 7.0/16.0, -7.0/16.0 },
3577 };
3578
3579 static const tSamplePos pos16[] = {
3580 { 1.0/16.0, 1.0/16.0 }, {-1.0/16.0, -3.0/16.0 }, {-3.0/16.0, 2.0/16.0 }, { 4.0/16.0, -1.0/16.0 },
3581 {-5.0/16.0, -2.0/16.0 }, { 2.0/16.0, 5.0/16.0 }, { 5.0/16.0, 3.0/16.0 }, { 3.0/16.0, -5.0/16.0 },
3582 {-2.0/16.0, 6.0/16.0 }, { 0.0/16.0, -7.0/16.0 }, {-4.0/16.0, -6.0/16.0 }, {-6.0/16.0, 4.0/16.0 },
3583 {-8.0/16.0, 0.0/16.0 }, { 7.0/16.0, -4.0/16.0 }, { 6.0/16.0, 7.0/16.0 }, {-7.0/16.0, -8.0/16.0 },
3584 };
3585
3586 const tSamplePos* sampleLoc = nullptr;
3587 int numSamples = count;
3588
3589 switch (count) {
3590 case 2: sampleLoc = pos2; break;
3591 case 4: sampleLoc = pos4; break;
3592 case 8: sampleLoc = pos8; break;
3593 case 16: sampleLoc = pos16; break;
3594 default:
3595 sampleLoc = pos1;
3596 numSamples = 1;
3597 }
3598
3599 TConstUnionArray* values = new TConstUnionArray(numSamples*2);
steve-lunarg5da1f032017-02-12 17:50:28 -07003600
steve-lunargd4d0b292017-04-26 08:31:56 -06003601 for (int pos=0; pos<count; ++pos) {
3602 TConstUnion x, y;
3603 x.setDConst(sampleLoc[pos].x);
3604 y.setDConst(sampleLoc[pos].y);
3605
3606 (*values)[pos*2+0] = x;
3607 (*values)[pos*2+1] = y;
3608 }
3609
3610 TType retType(EbtFloat, EvqConst, 2);
3611
3612 if (numSamples != 1) {
3613 TArraySizes arraySizes;
3614 arraySizes.addInnerSize(numSamples);
3615 retType.newArraySizes(arraySizes);
3616 }
3617
3618 return new TIntermConstantUnion(*values, retType);
3619}
3620
steve-lunarg5da1f032017-02-12 17:50:28 -07003621//
LoopDawga2b79912016-07-14 14:45:14 -06003622// Decompose DX9 and DX10 sample intrinsics & object methods into AST
3623//
3624void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
LoopDawg4624a022016-06-20 13:26:59 -06003625{
John Kessenichf02c8e62017-06-19 16:25:44 -06003626 if (node == nullptr || !node->getAsOperator())
LoopDawg4624a022016-06-20 13:26:59 -06003627 return;
3628
LoopDawg5ee05892017-07-31 13:41:42 -06003629 // Sampler return must always be a vec4, but we can construct a shorter vector or a structure from it.
3630 const auto convertReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
steve-lunarg8b0227c2016-10-14 16:40:32 -06003631 result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize()));
3632
LoopDawg5ee05892017-07-31 13:41:42 -06003633 TIntermTyped* convertedResult = nullptr;
3634
3635 TType retType;
3636 getTextureReturnType(sampler, retType);
steve-lunarg8b0227c2016-10-14 16:40:32 -06003637
LoopDawg5ee05892017-07-31 13:41:42 -06003638 if (retType.isStruct()) {
3639 // For type convenience, conversionAggregate points to the convertedResult (we know it's an aggregate here)
3640 TIntermAggregate* conversionAggregate = new TIntermAggregate;
3641 convertedResult = conversionAggregate;
steve-lunarg8b0227c2016-10-14 16:40:32 -06003642
LoopDawg5ee05892017-07-31 13:41:42 -06003643 // Convert vector output to return structure. We will need a temp symbol to copy the results to.
3644 TVariable* structVar = makeInternalVariable("@sampleStructTemp", retType);
3645
3646 // We also need a temp symbol to hold the result of the texture. We don't want to re-fetch the
3647 // sample each time we'll index into the result, so we'll copy to this, and index into the copy.
3648 TVariable* sampleShadow = makeInternalVariable("@sampleResultShadow", result->getType());
3649
3650 // Initial copy from texture to our sample result shadow.
3651 TIntermTyped* shadowCopy = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*sampleShadow, loc),
3652 result, loc);
3653
3654 conversionAggregate->getSequence().push_back(shadowCopy);
3655
3656 unsigned vec4Pos = 0;
3657
3658 for (unsigned m = 0; m < unsigned(retType.getStruct()->size()); ++m) {
3659 const TType memberType(retType, m); // dereferenced type of the member we're about to assign.
3660
3661 // Check for bad struct members. This should have been caught upstream. Complain, because
3662 // wwe don't know what to do with it. This algorithm could be generalized to handle
3663 // other things, e.g, sub-structures, but HLSL doesn't allow them.
3664 if (!memberType.isVector() && !memberType.isScalar()) {
3665 error(loc, "expected: scalar or vector type in texture structure", "", "");
3666 return nullptr;
3667 }
3668
3669 // Index into the struct variable to find the member to assign.
3670 TIntermTyped* structMember = intermediate.addIndex(EOpIndexDirectStruct,
3671 intermediate.addSymbol(*structVar, loc),
3672 intermediate.addConstantUnion(m, loc), loc);
3673
3674 structMember->setType(memberType);
3675
3676 // Assign each component of (possible) vector in struct member.
3677 for (int component = 0; component < memberType.getVectorSize(); ++component) {
3678 TIntermTyped* vec4Member = intermediate.addIndex(EOpIndexDirect,
3679 intermediate.addSymbol(*sampleShadow, loc),
3680 intermediate.addConstantUnion(vec4Pos++, loc), loc);
3681 vec4Member->setType(TType(memberType.getBasicType(), EvqTemporary, 1));
3682
3683 TIntermTyped* memberAssign = nullptr;
3684
3685 if (memberType.isVector()) {
3686 // Vector member: we need to create an access chain to the vector component.
3687
3688 TIntermTyped* structVecComponent = intermediate.addIndex(EOpIndexDirect, structMember,
3689 intermediate.addConstantUnion(component, loc), loc);
3690
3691 memberAssign = intermediate.addAssign(EOpAssign, structVecComponent, vec4Member, loc);
3692 } else {
3693 // Scalar member: we can assign to it directly.
3694 memberAssign = intermediate.addAssign(EOpAssign, structMember, vec4Member, loc);
3695 }
3696
3697
3698 conversionAggregate->getSequence().push_back(memberAssign);
3699 }
3700 }
3701
3702 // Add completed variable so the expression results in the whole struct value we just built.
3703 conversionAggregate->getSequence().push_back(intermediate.addSymbol(*structVar, loc));
3704
3705 // Make it a sequence.
3706 intermediate.setAggregateOperator(conversionAggregate, EOpSequence, retType, loc);
3707 } else {
3708 // vector clamp the output if template vector type is smaller than sample result.
3709 if (retType.getVectorSize() < node->getVectorSize()) {
3710 // Too many components. Construct shorter vector from it.
3711 const TOperator op = intermediate.mapTypeToConstructorOp(retType);
3712
3713 convertedResult = constructBuiltIn(retType, op, result, loc, false);
3714 } else {
3715 // Enough components. Use directly.
3716 convertedResult = result;
3717 }
steve-lunarg8b0227c2016-10-14 16:40:32 -06003718 }
3719
LoopDawg5ee05892017-07-31 13:41:42 -06003720 convertedResult->setLoc(loc);
3721 return convertedResult;
steve-lunarg8b0227c2016-10-14 16:40:32 -06003722 };
3723
LoopDawg4624a022016-06-20 13:26:59 -06003724 const TOperator op = node->getAsOperator()->getOp();
3725 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
3726
John Kessenich6e2295d2017-05-24 16:02:56 -06003727 // Bail out if not a sampler method.
3728 // Note though this is odd to do before checking the op, because the op
3729 // could be something that takes the arguments, and the function in question
3730 // takes the result of the op. So, this is not the final word.
steve-lunarg5da1f032017-02-12 17:50:28 -07003731 if (arguments != nullptr) {
John Kessenich6e2295d2017-05-24 16:02:56 -06003732 if (argAggregate == nullptr) {
3733 if (arguments->getAsTyped()->getBasicType() != EbtSampler)
3734 return;
3735 } else {
3736 if (argAggregate->getSequence().size() == 0 ||
3737 argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler)
3738 return;
3739 }
steve-lunarg5da1f032017-02-12 17:50:28 -07003740 }
3741
LoopDawg4624a022016-06-20 13:26:59 -06003742 switch (op) {
LoopDawga2b79912016-07-14 14:45:14 -06003743 // **** DX9 intrinsics: ****
LoopDawg4624a022016-06-20 13:26:59 -06003744 case EOpTexture:
3745 {
LoopDawga2b79912016-07-14 14:45:14 -06003746 // Texture with ddx & ddy is really gradient form in HLSL
steve-lunarg8b0227c2016-10-14 16:40:32 -06003747 if (argAggregate->getSequence().size() == 4)
LoopDawg4624a022016-06-20 13:26:59 -06003748 node->getAsAggregate()->setOperator(EOpTextureGrad);
LoopDawg4624a022016-06-20 13:26:59 -06003749
3750 break;
3751 }
3752
3753 case EOpTextureBias:
3754 {
3755 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // sampler
3756 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // coord
3757
3758 // HLSL puts bias in W component of coordinate. We extract it and add it to
3759 // the argument list, instead
3760 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
3761 TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
3762
3763 TOperator constructOp = EOpNull;
steve-lunarg8b0227c2016-10-14 16:40:32 -06003764 const TSampler& sampler = arg0->getType().getSampler();
3765
3766 switch (sampler.dim) {
LoopDawg4624a022016-06-20 13:26:59 -06003767 case Esd1D: constructOp = EOpConstructFloat; break; // 1D
3768 case Esd2D: constructOp = EOpConstructVec2; break; // 2D
3769 case Esd3D: constructOp = EOpConstructVec3; break; // 3D
3770 case EsdCube: constructOp = EOpConstructVec3; break; // also 3D
3771 default: break;
3772 }
steve-lunarg8b0227c2016-10-14 16:40:32 -06003773
LoopDawg4624a022016-06-20 13:26:59 -06003774 TIntermAggregate* constructCoord = new TIntermAggregate(constructOp);
3775 constructCoord->getSequence().push_back(arg1);
3776 constructCoord->setLoc(loc);
3777
steve-lunarg6b596682016-10-20 14:50:12 -06003778 // The input vector should never be less than 2, since there's always a bias.
3779 // The max is for safety, and should be a no-op.
3780 constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0)));
3781
LoopDawg4624a022016-06-20 13:26:59 -06003782 TIntermAggregate* tex = new TIntermAggregate(EOpTexture);
3783 tex->getSequence().push_back(arg0); // sampler
3784 tex->getSequence().push_back(constructCoord); // coordinate
3785 tex->getSequence().push_back(bias); // bias
John Kessenichecba76f2017-01-06 00:34:48 -07003786
LoopDawg5ee05892017-07-31 13:41:42 -06003787 node = convertReturn(tex, sampler);
LoopDawg4624a022016-06-20 13:26:59 -06003788
3789 break;
3790 }
3791
LoopDawga2b79912016-07-14 14:45:14 -06003792 // **** DX10 methods: ****
3793 case EOpMethodSample: // fall through
3794 case EOpMethodSampleBias: // ...
3795 {
3796 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
3797 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
3798 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
3799 TIntermTyped* argBias = nullptr;
3800 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06003801 const TSampler& sampler = argTex->getType().getSampler();
LoopDawga2b79912016-07-14 14:45:14 -06003802
3803 int nextArg = 3;
3804
3805 if (op == EOpMethodSampleBias) // SampleBias has a bias arg
3806 argBias = argAggregate->getSequence()[nextArg++]->getAsTyped();
3807
3808 TOperator textureOp = EOpTexture;
3809
John Kesseniche4821e42016-07-16 10:19:43 -06003810 if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form
LoopDawga2b79912016-07-14 14:45:14 -06003811 textureOp = EOpTextureOffset;
3812 argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped();
3813 }
3814
3815 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
3816
3817 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
3818 txsample->getSequence().push_back(txcombine);
3819 txsample->getSequence().push_back(argCoord);
3820
3821 if (argBias != nullptr)
3822 txsample->getSequence().push_back(argBias);
3823
3824 if (argOffset != nullptr)
3825 txsample->getSequence().push_back(argOffset);
3826
LoopDawg5ee05892017-07-31 13:41:42 -06003827 node = convertReturn(txsample, sampler);
LoopDawga2b79912016-07-14 14:45:14 -06003828
3829 break;
3830 }
John Kessenichecba76f2017-01-06 00:34:48 -07003831
LoopDawga2b79912016-07-14 14:45:14 -06003832 case EOpMethodSampleGrad: // ...
3833 {
3834 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
3835 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
3836 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
3837 TIntermTyped* argDDX = argAggregate->getSequence()[3]->getAsTyped();
3838 TIntermTyped* argDDY = argAggregate->getSequence()[4]->getAsTyped();
3839 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06003840 const TSampler& sampler = argTex->getType().getSampler();
LoopDawga2b79912016-07-14 14:45:14 -06003841
3842 TOperator textureOp = EOpTextureGrad;
3843
3844 if (argAggregate->getSequence().size() == 6) { // last parameter is offset form
3845 textureOp = EOpTextureGradOffset;
3846 argOffset = argAggregate->getSequence()[5]->getAsTyped();
3847 }
3848
3849 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
3850
3851 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
3852 txsample->getSequence().push_back(txcombine);
3853 txsample->getSequence().push_back(argCoord);
3854 txsample->getSequence().push_back(argDDX);
3855 txsample->getSequence().push_back(argDDY);
3856
3857 if (argOffset != nullptr)
3858 txsample->getSequence().push_back(argOffset);
3859
LoopDawg5ee05892017-07-31 13:41:42 -06003860 node = convertReturn(txsample, sampler);
LoopDawga2b79912016-07-14 14:45:14 -06003861
3862 break;
3863 }
3864
LoopDawg5d58fae2016-07-15 11:22:24 -06003865 case EOpMethodGetDimensions:
3866 {
3867 // AST returns a vector of results, which we break apart component-wise into
3868 // separate values to assign to the HLSL method's outputs, ala:
3869 // tx . GetDimensions(width, height);
3870 // float2 sizeQueryTemp = EOpTextureQuerySize
3871 // width = sizeQueryTemp.X;
3872 // height = sizeQueryTemp.Y;
3873
3874 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
3875 const TType& texType = argTex->getType();
3876
3877 assert(texType.getBasicType() == EbtSampler);
3878
steve-lunarg8b0227c2016-10-14 16:40:32 -06003879 const TSampler& sampler = texType.getSampler();
3880 const TSamplerDim dim = sampler.dim;
3881 const bool isImage = sampler.isImage();
steve-lunarg3ce45362017-03-07 19:30:25 -07003882 const bool isMs = sampler.isMultiSample();
John Kessenich267590d2016-08-05 17:34:34 -06003883 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawg5d58fae2016-07-15 11:22:24 -06003884
3885 int numDims = 0;
3886
3887 switch (dim) {
steve-lunargbb0183f2016-10-04 16:58:14 -06003888 case Esd1D: numDims = 1; break; // W
3889 case Esd2D: numDims = 2; break; // W, H
3890 case Esd3D: numDims = 3; break; // W, H, D
3891 case EsdCube: numDims = 2; break; // W, H (cube)
steve-lunarg6b43d272016-10-06 20:12:24 -06003892 case EsdBuffer: numDims = 1; break; // W (buffers)
steve-lunarg3ce45362017-03-07 19:30:25 -07003893 case EsdRect: numDims = 2; break; // W, H (rect)
LoopDawg5d58fae2016-07-15 11:22:24 -06003894 default:
3895 assert(0 && "unhandled texture dimension");
3896 }
3897
3898 // Arrayed adds another dimension for the number of array elements
steve-lunarg8b0227c2016-10-14 16:40:32 -06003899 if (sampler.isArrayed())
LoopDawg5d58fae2016-07-15 11:22:24 -06003900 ++numDims;
3901
steve-lunarg3ce45362017-03-07 19:30:25 -07003902 // Establish whether the method itself is querying mip levels. This can be false even
3903 // if the underlying query requires a MIP level, due to the available HLSL method overloads.
3904 const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0)));
3905
3906 // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query).
3907 // True if:
3908 // 1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query)
3909 // or,
3910 // 2. There is a LOD (because the non-LOD query cannot be used in that case, per spec)
3911 const bool mipRequired =
3912 ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1...
3913 mipQuery; // 2...
LoopDawg5d58fae2016-07-15 11:22:24 -06003914
3915 // AST assumes integer return. Will be converted to float if required.
steve-lunargbb0183f2016-10-04 16:58:14 -06003916 TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize);
LoopDawg5d58fae2016-07-15 11:22:24 -06003917 sizeQuery->getSequence().push_back(argTex);
steve-lunarg3ce45362017-03-07 19:30:25 -07003918
3919 // If we're building an LOD query, add the LOD.
3920 if (mipRequired) {
3921 // If the base HLSL query had no MIP level given, use level 0.
3922 TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() :
3923 intermediate.addConstantUnion(0, loc, true);
LoopDawg5d58fae2016-07-15 11:22:24 -06003924 sizeQuery->getSequence().push_back(queryLod);
3925 }
steve-lunarg3ce45362017-03-07 19:30:25 -07003926
LoopDawg5d58fae2016-07-15 11:22:24 -06003927 sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims));
3928 sizeQuery->setLoc(loc);
3929
3930 // Return value from size query
3931 TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType());
3932 tempArg->getWritableType().getQualifier().makeTemporary();
steve-lunarg2199c242016-10-02 22:13:22 -06003933 TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign,
3934 intermediate.addSymbol(*tempArg, loc),
3935 sizeQuery, loc);
LoopDawg5d58fae2016-07-15 11:22:24 -06003936
3937 // Compound statement for assigning outputs
3938 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc);
3939 // Index of first output parameter
3940 const int outParamBase = mipQuery ? 2 : 1;
3941
3942 for (int compNum = 0; compNum < numDims; ++compNum) {
3943 TIntermTyped* indexedOut = nullptr;
steve-lunarg2199c242016-10-02 22:13:22 -06003944 TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc);
LoopDawg5d58fae2016-07-15 11:22:24 -06003945
3946 if (numDims > 1) {
3947 TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true);
3948 indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc);
3949 indexedOut->setType(TType(EbtUint, EvqTemporary, 1));
3950 indexedOut->setLoc(loc);
3951 } else {
3952 indexedOut = sizeQueryReturn;
3953 }
John Kessenichecba76f2017-01-06 00:34:48 -07003954
LoopDawg5d58fae2016-07-15 11:22:24 -06003955 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped();
3956 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc);
3957
3958 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
3959 }
3960
3961 // handle mip level parameter
3962 if (mipQuery) {
3963 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
3964
3965 TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels);
3966 levelsQuery->getSequence().push_back(argTex);
3967 levelsQuery->setType(TType(EbtUint, EvqTemporary, 1));
3968 levelsQuery->setLoc(loc);
3969
3970 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc);
3971 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
3972 }
3973
steve-lunarg1e19d902016-07-26 15:19:28 -06003974 // 2DMS formats query # samples, which needs a different query op
steve-lunarg8b0227c2016-10-14 16:40:32 -06003975 if (sampler.isMultiSample()) {
steve-lunarg1e19d902016-07-26 15:19:28 -06003976 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
3977
3978 TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
3979 samplesQuery->getSequence().push_back(argTex);
3980 samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
3981 samplesQuery->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07003982
steve-lunarg1e19d902016-07-26 15:19:28 -06003983 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc);
3984 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
3985 }
3986
LoopDawg5d58fae2016-07-15 11:22:24 -06003987 compoundStatement->setOperator(EOpSequence);
3988 compoundStatement->setLoc(loc);
3989 compoundStatement->setType(TType(EbtVoid));
3990
3991 node = compoundStatement;
3992
3993 break;
3994 }
3995
LoopDawga78b0292016-07-19 14:28:05 -06003996 case EOpMethodSampleCmp: // fall through...
3997 case EOpMethodSampleCmpLevelZero:
3998 {
3999 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4000 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
4001 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
4002 TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped();
4003 TIntermTyped* argOffset = nullptr;
4004
steve-lunarg3cbc32f2017-04-21 09:54:53 -06004005 // Sampler argument should be a sampler.
4006 if (argSamp->getType().getBasicType() != EbtSampler) {
4007 error(loc, "expected: sampler type", "", "");
4008 return;
4009 }
4010
4011 // Sampler should be a SamplerComparisonState
4012 if (! argSamp->getType().getSampler().isShadow()) {
4013 error(loc, "expected: SamplerComparisonState", "", "");
4014 return;
4015 }
4016
LoopDawga78b0292016-07-19 14:28:05 -06004017 // optional offset value
4018 if (argAggregate->getSequence().size() > 4)
4019 argOffset = argAggregate->getSequence()[4]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -07004020
LoopDawga78b0292016-07-19 14:28:05 -06004021 const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp
4022
4023 // AST wants comparison value as one of the texture coordinates
4024 TOperator constructOp = EOpNull;
4025 switch (coordDimWithCmpVal) {
4026 // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val
4027 case 2: constructOp = EOpConstructVec2; break;
4028 case 3: constructOp = EOpConstructVec3; break;
4029 case 4: constructOp = EOpConstructVec4; break;
4030 case 5: constructOp = EOpConstructVec4; break; // cubeArrayShadow, cmp value is separate arg.
4031 default: assert(0); break;
4032 }
4033
4034 TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp);
4035 coordWithCmp->getSequence().push_back(argCoord);
4036 if (coordDimWithCmpVal != 5) // cube array shadow is special.
4037 coordWithCmp->getSequence().push_back(argCmpVal);
4038 coordWithCmp->setLoc(loc);
steve-lunarg6b596682016-10-20 14:50:12 -06004039 coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4)));
LoopDawga78b0292016-07-19 14:28:05 -06004040
4041 TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture);
4042 if (argOffset != nullptr)
4043 textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset);
4044
4045 // Create combined sampler & texture op
4046 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4047 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4048 txsample->getSequence().push_back(txcombine);
4049 txsample->getSequence().push_back(coordWithCmp);
4050
4051 if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord.
4052 txsample->getSequence().push_back(argCmpVal);
4053
4054 // the LevelZero form uses 0 as an explicit LOD
4055 if (op == EOpMethodSampleCmpLevelZero)
4056 txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true));
4057
4058 // Add offset if present
4059 if (argOffset != nullptr)
4060 txsample->getSequence().push_back(argOffset);
4061
4062 txsample->setType(node->getType());
4063 txsample->setLoc(loc);
4064 node = txsample;
4065
4066 break;
4067 }
4068
LoopDawgf2451012016-07-20 16:34:44 -06004069 case EOpMethodLoad:
4070 {
4071 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4072 TIntermTyped* argCoord = argAggregate->getSequence()[1]->getAsTyped();
4073 TIntermTyped* argOffset = nullptr;
4074 TIntermTyped* lodComponent = nullptr;
4075 TIntermTyped* coordSwizzle = nullptr;
4076
steve-lunarg8b0227c2016-10-14 16:40:32 -06004077 const TSampler& sampler = argTex->getType().getSampler();
4078 const bool isMS = sampler.isMultiSample();
4079 const bool isBuffer = sampler.dim == EsdBuffer;
4080 const bool isImage = sampler.isImage();
LoopDawgf2451012016-07-20 16:34:44 -06004081 const TBasicType coordBaseType = argCoord->getType().getBasicType();
4082
4083 // Last component of coordinate is the mip level, for non-MS. we separate them here:
steve-lunargbb0183f2016-10-04 16:58:14 -06004084 if (isMS || isBuffer || isImage) {
4085 // MS, Buffer, and Image have no LOD
steve-lunarg1e19d902016-07-26 15:19:28 -06004086 coordSwizzle = argCoord;
LoopDawgf2451012016-07-20 16:34:44 -06004087 } else {
4088 // Extract coordinate
John Kessenichc142c882017-01-13 19:34:22 -07004089 int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1);
4090 TSwizzleSelectors<TVectorSelector> coordFields;
4091 for (int i = 0; i < swizzleSize; ++i)
4092 coordFields.push_back(i);
LoopDawgf2451012016-07-20 16:34:44 -06004093 TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc);
4094 coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc);
John Kessenichc142c882017-01-13 19:34:22 -07004095 coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size()));
LoopDawgf2451012016-07-20 16:34:44 -06004096
4097 // Extract LOD
John Kessenichc142c882017-01-13 19:34:22 -07004098 TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true);
LoopDawgf2451012016-07-20 16:34:44 -06004099 lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc);
4100 lodComponent->setType(TType(coordBaseType, EvqTemporary, 1));
4101 }
4102
John Kessenich267590d2016-08-05 17:34:34 -06004103 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawgf2451012016-07-20 16:34:44 -06004104 const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4));
4105
LoopDawgf2451012016-07-20 16:34:44 -06004106 // Create texel fetch
steve-lunargbb0183f2016-10-04 16:58:14 -06004107 const TOperator fetchOp = (isImage ? EOpImageLoad :
4108 hasOffset ? EOpTextureFetchOffset :
4109 EOpTextureFetch);
LoopDawgf2451012016-07-20 16:34:44 -06004110 TIntermAggregate* txfetch = new TIntermAggregate(fetchOp);
4111
LoopDawgf2451012016-07-20 16:34:44 -06004112 // Build up the fetch
4113 txfetch->getSequence().push_back(argTex);
4114 txfetch->getSequence().push_back(coordSwizzle);
4115
steve-lunarg1e19d902016-07-26 15:19:28 -06004116 if (isMS) {
4117 // add 2DMS sample index
4118 TIntermTyped* argSampleIdx = argAggregate->getSequence()[2]->getAsTyped();
4119 txfetch->getSequence().push_back(argSampleIdx);
steve-lunargd53f7172016-07-27 15:46:48 -06004120 } else if (isBuffer) {
4121 // Nothing else to do for buffers.
steve-lunargbb0183f2016-10-04 16:58:14 -06004122 } else if (isImage) {
4123 // Nothing else to do for images.
steve-lunarg1e19d902016-07-26 15:19:28 -06004124 } else {
steve-lunargd53f7172016-07-27 15:46:48 -06004125 // 2DMS and buffer have no LOD, but everything else does.
LoopDawgf2451012016-07-20 16:34:44 -06004126 txfetch->getSequence().push_back(lodComponent);
steve-lunarg1e19d902016-07-26 15:19:28 -06004127 }
LoopDawgf2451012016-07-20 16:34:44 -06004128
steve-lunarg1e19d902016-07-26 15:19:28 -06004129 // Obtain offset arg, if there is one.
4130 if (hasOffset) {
4131 const int offsetPos = (isMS ? 3 : 2);
4132 argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped();
LoopDawgf2451012016-07-20 16:34:44 -06004133 txfetch->getSequence().push_back(argOffset);
steve-lunarg1e19d902016-07-26 15:19:28 -06004134 }
LoopDawgf2451012016-07-20 16:34:44 -06004135
LoopDawg5ee05892017-07-31 13:41:42 -06004136 node = convertReturn(txfetch, sampler);
LoopDawgf2451012016-07-20 16:34:44 -06004137
4138 break;
4139 }
4140
LoopDawg3ef78522016-07-21 15:02:16 -06004141 case EOpMethodSampleLevel:
4142 {
4143 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4144 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
4145 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
4146 TIntermTyped* argLod = argAggregate->getSequence()[3]->getAsTyped();
4147 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06004148 const TSampler& sampler = argTex->getType().getSampler();
John Kessenichecba76f2017-01-06 00:34:48 -07004149
John Kessenich267590d2016-08-05 17:34:34 -06004150 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawg3ef78522016-07-21 15:02:16 -06004151
4152 if (numArgs == 5) // offset, if present
4153 argOffset = argAggregate->getSequence()[4]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -07004154
LoopDawg3ef78522016-07-21 15:02:16 -06004155 const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset);
4156 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
4157
4158 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4159
4160 txsample->getSequence().push_back(txcombine);
4161 txsample->getSequence().push_back(argCoord);
4162 txsample->getSequence().push_back(argLod);
4163
4164 if (argOffset != nullptr)
4165 txsample->getSequence().push_back(argOffset);
4166
LoopDawg5ee05892017-07-31 13:41:42 -06004167 node = convertReturn(txsample, sampler);
LoopDawg3ef78522016-07-21 15:02:16 -06004168
4169 break;
4170 }
4171
LoopDawga2f3d282016-07-22 08:28:11 -06004172 case EOpMethodGather:
4173 {
4174 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4175 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
4176 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
4177 TIntermTyped* argOffset = nullptr;
4178
4179 // Offset is optional
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06004180 if (argAggregate->getSequence().size() > 3)
LoopDawga2f3d282016-07-22 08:28:11 -06004181 argOffset = argAggregate->getSequence()[3]->getAsTyped();
4182
4183 const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset);
4184 TIntermAggregate* txgather = new TIntermAggregate(textureOp);
4185
4186 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4187
4188 txgather->getSequence().push_back(txcombine);
4189 txgather->getSequence().push_back(argCoord);
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06004190 // Offset if not given is implicitly channel 0 (red)
LoopDawga2f3d282016-07-22 08:28:11 -06004191
4192 if (argOffset != nullptr)
4193 txgather->getSequence().push_back(argOffset);
4194
4195 txgather->setType(node->getType());
4196 txgather->setLoc(loc);
4197 node = txgather;
4198
4199 break;
4200 }
John Kessenichecba76f2017-01-06 00:34:48 -07004201
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06004202 case EOpMethodGatherRed: // fall through...
4203 case EOpMethodGatherGreen: // ...
4204 case EOpMethodGatherBlue: // ...
4205 case EOpMethodGatherAlpha: // ...
4206 case EOpMethodGatherCmpRed: // ...
4207 case EOpMethodGatherCmpGreen: // ...
4208 case EOpMethodGatherCmpBlue: // ...
4209 case EOpMethodGatherCmpAlpha: // ...
4210 {
4211 int channel = 0; // the channel we are gathering
4212 int cmpValues = 0; // 1 if there is a compare value (handier than a bool below)
4213
4214 switch (op) {
4215 case EOpMethodGatherCmpRed: cmpValues = 1; // fall through
4216 case EOpMethodGatherRed: channel = 0; break;
4217 case EOpMethodGatherCmpGreen: cmpValues = 1; // fall through
4218 case EOpMethodGatherGreen: channel = 1; break;
4219 case EOpMethodGatherCmpBlue: cmpValues = 1; // fall through
4220 case EOpMethodGatherBlue: channel = 2; break;
4221 case EOpMethodGatherCmpAlpha: cmpValues = 1; // fall through
4222 case EOpMethodGatherAlpha: channel = 3; break;
4223 default: assert(0); break;
4224 }
4225
4226 // For now, we have nothing to map the component-wise comparison forms
4227 // to, because neither GLSL nor SPIR-V has such an opcode. Issue an
4228 // unimplemented error instead. Most of the machinery is here if that
steve-lunargd00b0262017-03-09 08:59:45 -07004229 // should ever become available. However, red can be passed through
4230 // to OpImageDrefGather. G/B/A cannot, because that opcode does not
4231 // accept a component.
4232 if (cmpValues != 0 && op != EOpMethodGatherCmpRed) {
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06004233 error(loc, "unimplemented: component-level gather compare", "", "");
4234 return;
4235 }
4236
4237 int arg = 0;
4238
4239 TIntermTyped* argTex = argAggregate->getSequence()[arg++]->getAsTyped();
4240 TIntermTyped* argSamp = argAggregate->getSequence()[arg++]->getAsTyped();
4241 TIntermTyped* argCoord = argAggregate->getSequence()[arg++]->getAsTyped();
4242 TIntermTyped* argOffset = nullptr;
4243 TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr };
4244 // TIntermTyped* argStatus = nullptr; // TODO: residency
4245 TIntermTyped* argCmp = nullptr;
4246
4247 const TSamplerDim dim = argTex->getType().getSampler().dim;
4248
baldurk1eb1c112016-08-15 18:01:15 +02004249 const int argSize = (int)argAggregate->getSequence().size();
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06004250 bool hasStatus = (argSize == (5+cmpValues) || argSize == (8+cmpValues));
4251 bool hasOffset1 = false;
4252 bool hasOffset4 = false;
4253
steve-lunarg3cbc32f2017-04-21 09:54:53 -06004254 // Sampler argument should be a sampler.
4255 if (argSamp->getType().getBasicType() != EbtSampler) {
4256 error(loc, "expected: sampler type", "", "");
4257 return;
4258 }
4259
4260 // Cmp forms require SamplerComparisonState
4261 if (cmpValues > 0 && ! argSamp->getType().getSampler().isShadow()) {
4262 error(loc, "expected: SamplerComparisonState", "", "");
4263 return;
4264 }
4265
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06004266 // Only 2D forms can have offsets. Discover if we have 0, 1 or 4 offsets.
4267 if (dim == Esd2D) {
4268 hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues));
4269 hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues));
4270 }
4271
4272 assert(!(hasOffset1 && hasOffset4));
4273
4274 TOperator textureOp = EOpTextureGather;
4275
4276 // Compare forms have compare value
4277 if (cmpValues != 0)
4278 argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
4279
4280 // Some forms have single offset
4281 if (hasOffset1) {
4282 textureOp = EOpTextureGatherOffset; // single offset form
4283 argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
4284 }
4285
4286 // Some forms have 4 gather offsets
4287 if (hasOffset4) {
4288 textureOp = EOpTextureGatherOffsets; // note plural, for 4 offset form
4289 for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
4290 argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped();
4291 }
4292
4293 // Residency status
4294 if (hasStatus) {
4295 // argStatus = argAggregate->getSequence()[arg++]->getAsTyped();
4296 error(loc, "unimplemented: residency status", "", "");
4297 return;
4298 }
4299
4300 TIntermAggregate* txgather = new TIntermAggregate(textureOp);
4301 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4302
4303 TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true);
4304
4305 txgather->getSequence().push_back(txcombine);
4306 txgather->getSequence().push_back(argCoord);
4307
4308 // AST wants an array of 4 offsets, where HLSL has separate args. Here
4309 // we construct an array from the separate args.
4310 if (hasOffset4) {
4311 TType arrayType(EbtInt, EvqTemporary, 2);
4312 TArraySizes arraySizes;
4313 arraySizes.addInnerSize(4);
4314 arrayType.newArraySizes(arraySizes);
4315
4316 TIntermAggregate* initList = new TIntermAggregate(EOpNull);
4317
4318 for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
4319 initList->getSequence().push_back(argOffsets[offsetNum]);
4320
4321 argOffset = addConstructor(loc, initList, arrayType);
4322 }
4323
4324 // Add comparison value if we have one
steve-lunargd00b0262017-03-09 08:59:45 -07004325 if (argCmp != nullptr)
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06004326 txgather->getSequence().push_back(argCmp);
4327
4328 // Add offset (either 1, or an array of 4) if we have one
4329 if (argOffset != nullptr)
4330 txgather->getSequence().push_back(argOffset);
4331
Rex Xud41a6962017-03-13 17:07:18 +08004332 // Add channel value if the sampler is not shadow
4333 if (! argSamp->getType().getSampler().isShadow())
4334 txgather->getSequence().push_back(argChannel);
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06004335
4336 txgather->setType(node->getType());
4337 txgather->setLoc(loc);
4338 node = txgather;
4339
4340 break;
4341 }
4342
steve-lunarg68f2c142016-07-26 08:57:53 -06004343 case EOpMethodCalculateLevelOfDetail:
4344 case EOpMethodCalculateLevelOfDetailUnclamped:
4345 {
4346 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4347 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
4348 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
4349
4350 TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod);
4351
4352 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
4353 txquerylod->getSequence().push_back(txcombine);
4354 txquerylod->getSequence().push_back(argCoord);
4355
4356 TIntermTyped* lodComponent = intermediate.addConstantUnion(0, loc, true);
4357 TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc);
4358 lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1));
4359
4360 node = lodComponentIdx;
4361
4362 // We cannot currently obtain the unclamped LOD
4363 if (op == EOpMethodCalculateLevelOfDetailUnclamped)
4364 error(loc, "unimplemented: CalculateLevelOfDetailUnclamped", "", "");
4365
4366 break;
4367 }
4368
4369 case EOpMethodGetSamplePosition:
4370 {
steve-lunargd4d0b292017-04-26 08:31:56 -06004371 // TODO: this entire decomposition exists because there is not yet a way to query
4372 // the sample position directly through SPIR-V. Instead, we return fixed sample
4373 // positions for common cases. *** If the sample positions are set differently,
4374 // this will be wrong. ***
4375
4376 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
4377 TIntermTyped* argSampIdx = argAggregate->getSequence()[1]->getAsTyped();
4378
4379 TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
4380 samplesQuery->getSequence().push_back(argTex);
4381 samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
4382 samplesQuery->setLoc(loc);
4383
4384 TIntermAggregate* compoundStatement = nullptr;
4385
4386 TVariable* outSampleCount = makeInternalVariable("@sampleCount", TType(EbtUint));
4387 outSampleCount->getWritableType().getQualifier().makeTemporary();
4388 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*outSampleCount, loc),
4389 samplesQuery, loc);
4390 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
4391
4392 TIntermTyped* idxtest[4];
4393
4394 // Create tests against 2, 4, 8, and 16 sample values
4395 int count = 0;
4396 for (int val = 2; val <= 16; val *= 2)
4397 idxtest[count++] =
4398 intermediate.addBinaryNode(EOpEqual,
4399 intermediate.addSymbol(*outSampleCount, loc),
4400 intermediate.addConstantUnion(val, loc),
4401 loc, TType(EbtBool));
4402
4403 const TOperator idxOp = (argSampIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
4404
4405 // Create index ops into position arrays given sample index.
4406 // TODO: should it be clamped?
4407 TIntermTyped* index[4];
4408 count = 0;
4409 for (int val = 2; val <= 16; val *= 2) {
4410 index[count] = intermediate.addIndex(idxOp, getSamplePosArray(val), argSampIdx, loc);
4411 index[count++]->setType(TType(EbtFloat, EvqTemporary, 2));
4412 }
4413
4414 // Create expression as:
4415 // (sampleCount == 2) ? pos2[idx] :
4416 // (sampleCount == 4) ? pos4[idx] :
4417 // (sampleCount == 8) ? pos8[idx] :
4418 // (sampleCount == 16) ? pos16[idx] : float2(0,0);
4419 TIntermTyped* test =
4420 intermediate.addSelection(idxtest[0], index[0],
4421 intermediate.addSelection(idxtest[1], index[1],
4422 intermediate.addSelection(idxtest[2], index[2],
4423 intermediate.addSelection(idxtest[3], index[3],
4424 getSamplePosArray(1), loc), loc), loc), loc);
4425
4426 compoundStatement = intermediate.growAggregate(compoundStatement, test);
4427 compoundStatement->setOperator(EOpSequence);
4428 compoundStatement->setLoc(loc);
4429 compoundStatement->setType(TType(EbtFloat, EvqTemporary, 2));
4430
4431 node = compoundStatement;
4432
steve-lunarg68f2c142016-07-26 08:57:53 -06004433 break;
4434 }
4435
LoopDawg7f93d562017-09-27 09:04:43 -06004436 case EOpSubpassLoad:
4437 {
4438 const TIntermTyped* argSubpass =
4439 argAggregate ? argAggregate->getSequence()[0]->getAsTyped() :
4440 arguments->getAsTyped();
4441
4442 const TSampler& sampler = argSubpass->getType().getSampler();
4443
4444 // subpass load: the multisample form is overloaded. Here, we convert that to
4445 // the EOpSubpassLoadMS opcode.
4446 if (argAggregate != nullptr && argAggregate->getSequence().size() > 1)
4447 node->getAsOperator()->setOp(EOpSubpassLoadMS);
4448
4449 node = convertReturn(node, sampler);
4450
4451 break;
4452 }
4453
4454
LoopDawg4624a022016-06-20 13:26:59 -06004455 default:
4456 break; // most pass through unchanged
4457 }
4458}
4459
4460//
steve-lunargf49cdf42016-11-17 15:04:20 -07004461// Decompose geometry shader methods
4462//
4463void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
4464{
John Kessenichf02c8e62017-06-19 16:25:44 -06004465 if (node == nullptr || !node->getAsOperator())
steve-lunargf49cdf42016-11-17 15:04:20 -07004466 return;
4467
4468 const TOperator op = node->getAsOperator()->getOp();
4469 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4470
4471 switch (op) {
4472 case EOpMethodAppend:
4473 if (argAggregate) {
LoopDawgc6510a32017-05-13 09:20:11 -06004474 // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
4475 if (language != EShLangGeometry) {
4476 node = nullptr;
4477 return;
4478 }
4479
steve-lunargf49cdf42016-11-17 15:04:20 -07004480 TIntermAggregate* sequence = nullptr;
4481 TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex);
4482
4483 emit->setLoc(loc);
4484 emit->setType(TType(EbtVoid));
4485
steve-lunarg08e0c082017-03-29 20:01:13 -06004486 // find the matching output
4487 if (gsStreamOutput == nullptr) {
4488 error(loc, "unable to find output symbol for Append()", "", "");
4489 return;
4490 }
4491
steve-lunargf49cdf42016-11-17 15:04:20 -07004492 sequence = intermediate.growAggregate(sequence,
John Kessenichecba76f2017-01-06 00:34:48 -07004493 handleAssign(loc, EOpAssign,
steve-lunarg08e0c082017-03-29 20:01:13 -06004494 intermediate.addSymbol(*gsStreamOutput, loc),
steve-lunarga2e75312016-12-14 15:22:25 -07004495 argAggregate->getSequence()[1]->getAsTyped()),
steve-lunargf49cdf42016-11-17 15:04:20 -07004496 loc);
4497
4498 sequence = intermediate.growAggregate(sequence, emit);
4499
4500 sequence->setOperator(EOpSequence);
4501 sequence->setLoc(loc);
4502 sequence->setType(TType(EbtVoid));
4503 node = sequence;
4504 }
4505 break;
4506
4507 case EOpMethodRestartStrip:
4508 {
LoopDawgc6510a32017-05-13 09:20:11 -06004509 // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
4510 if (language != EShLangGeometry) {
4511 node = nullptr;
4512 return;
4513 }
4514
steve-lunargf49cdf42016-11-17 15:04:20 -07004515 TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive);
4516 cut->setLoc(loc);
4517 cut->setType(TType(EbtVoid));
4518 node = cut;
4519 }
4520 break;
4521
4522 default:
4523 break; // most pass through unchanged
4524 }
4525}
4526
4527//
LoopDawg592860c2016-06-09 08:57:35 -06004528// Optionally decompose intrinsics to AST opcodes.
4529//
4530void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
4531{
steve-lunarg22322362016-10-19 10:15:25 -06004532 // Helper to find image data for image atomics:
4533 // OpImageLoad(image[idx])
4534 // We take the image load apart and add its params to the atomic op aggregate node
4535 const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) {
4536 TIntermAggregate* loadOp = load->getAsAggregate();
4537 if (loadOp == nullptr) {
4538 error(loc, "unknown image type in atomic operation", "", "");
4539 node = nullptr;
4540 return;
4541 }
4542
4543 atomic->getSequence().push_back(loadOp->getSequence()[0]);
4544 atomic->getSequence().push_back(loadOp->getSequence()[1]);
4545 };
4546
4547 // Return true if this is an imageLoad, which we will change to an image atomic.
baldurkca735702016-10-28 17:57:25 +02004548 const auto isImageParam = [](TIntermTyped* image) -> bool {
steve-lunarg22322362016-10-19 10:15:25 -06004549 TIntermAggregate* imageAggregate = image->getAsAggregate();
4550 return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad;
4551 };
4552
LoopDawg592860c2016-06-09 08:57:35 -06004553 // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST
4554 // opcodes for compatibility with existing software stacks.
4555 static const bool decomposeHlslIntrinsics = true;
4556
4557 if (!decomposeHlslIntrinsics || !node || !node->getAsOperator())
4558 return;
John Kessenichecba76f2017-01-06 00:34:48 -07004559
LoopDawg592860c2016-06-09 08:57:35 -06004560 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
4561 TIntermUnary* fnUnary = node->getAsUnaryNode();
4562 const TOperator op = node->getAsOperator()->getOp();
4563
4564 switch (op) {
4565 case EOpGenMul:
4566 {
4567 // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul
steve-lunarg297ae212016-08-24 14:36:13 -06004568 // Since we are treating HLSL rows like GLSL columns (the first matrix indirection),
4569 // we must reverse the operand order here. Hence, arg0 gets sequence[1], etc.
4570 TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped();
4571 TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped();
LoopDawg592860c2016-06-09 08:57:35 -06004572
4573 if (arg0->isVector() && arg1->isVector()) { // vec * vec
4574 node->getAsAggregate()->setOperator(EOpDot);
4575 } else {
4576 node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1);
4577 }
4578
4579 break;
4580 }
4581
4582 case EOpRcp:
4583 {
4584 // rcp(a) -> 1 / a
4585 TIntermTyped* arg0 = fnUnary->getOperand();
4586 TBasicType type0 = arg0->getBasicType();
4587 TIntermTyped* one = intermediate.addConstantUnion(1, type0, loc, true);
4588 node = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0);
4589
4590 break;
4591 }
4592
LoopDawg54b9ff92017-07-05 12:07:16 -06004593 case EOpAny: // fall through
4594 case EOpAll:
4595 {
4596 TIntermTyped* typedArg = arguments->getAsTyped();
4597
4598 // HLSL allows float/etc types here, and the SPIR-V opcode requires a bool.
4599 // We'll convert here. Note that for efficiency, we could add a smarter
4600 // decomposition for some type cases, e.g, maybe by decomposing a dot product.
4601 if (typedArg->getType().getBasicType() != EbtBool) {
4602 const TType boolType(EbtBool, EvqTemporary,
4603 typedArg->getVectorSize(),
4604 typedArg->getMatrixCols(),
4605 typedArg->getMatrixRows(),
4606 typedArg->isVector());
4607
4608 typedArg = intermediate.addConversion(EOpConstructBool, boolType, typedArg);
4609 node->getAsUnaryNode()->setOperand(typedArg);
4610 }
4611
4612 break;
4613 }
4614
LoopDawg592860c2016-06-09 08:57:35 -06004615 case EOpSaturate:
4616 {
4617 // saturate(a) -> clamp(a,0,1)
4618 TIntermTyped* arg0 = fnUnary->getOperand();
4619 TBasicType type0 = arg0->getBasicType();
4620 TIntermAggregate* clamp = new TIntermAggregate(EOpClamp);
4621
4622 clamp->getSequence().push_back(arg0);
4623 clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true));
4624 clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true));
4625 clamp->setLoc(loc);
4626 clamp->setType(node->getType());
LoopDawg58910702016-06-13 09:22:28 -06004627 clamp->getWritableType().getQualifier().makeTemporary();
LoopDawg592860c2016-06-09 08:57:35 -06004628 node = clamp;
4629
4630 break;
4631 }
4632
4633 case EOpSinCos:
4634 {
4635 // sincos(a,b,c) -> b = sin(a), c = cos(a)
4636 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
4637 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
4638 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();
4639
4640 TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0);
4641 TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0);
4642 TIntermTyped* sinAssign = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc);
4643 TIntermTyped* cosAssign = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc);
4644
4645 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc);
4646 compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign);
4647 compoundStatement->setOperator(EOpSequence);
4648 compoundStatement->setLoc(loc);
LoopDawg4624a022016-06-20 13:26:59 -06004649 compoundStatement->setType(TType(EbtVoid));
LoopDawg592860c2016-06-09 08:57:35 -06004650
4651 node = compoundStatement;
4652
4653 break;
4654 }
4655
4656 case EOpClip:
4657 {
4658 // clip(a) -> if (any(a<0)) discard;
4659 TIntermTyped* arg0 = fnUnary->getOperand();
4660 TBasicType type0 = arg0->getBasicType();
4661 TIntermTyped* compareNode = nullptr;
4662
4663 // For non-scalars: per experiment with FXC compiler, discard if any component < 0.
4664 if (!arg0->isScalar()) {
4665 // component-wise compare: a < 0
4666 TIntermAggregate* less = new TIntermAggregate(EOpLessThan);
4667 less->getSequence().push_back(arg0);
4668 less->setLoc(loc);
4669
4670 // make vec or mat of bool matching dimensions of input
4671 less->setType(TType(EbtBool, EvqTemporary,
4672 arg0->getType().getVectorSize(),
4673 arg0->getType().getMatrixCols(),
4674 arg0->getType().getMatrixRows(),
4675 arg0->getType().isVector()));
4676
4677 // calculate # of components for comparison const
John Kessenichecba76f2017-01-06 00:34:48 -07004678 const int constComponentCount =
LoopDawg592860c2016-06-09 08:57:35 -06004679 std::max(arg0->getType().getVectorSize(), 1) *
4680 std::max(arg0->getType().getMatrixCols(), 1) *
4681 std::max(arg0->getType().getMatrixRows(), 1);
4682
4683 TConstUnion zero;
John Kessenich1a4bbc42017-10-16 13:11:53 -06004684 if (arg0->getType().isIntegerDomain())
4685 zero.setDConst(0);
4686 else
4687 zero.setDConst(0.0);
LoopDawg592860c2016-06-09 08:57:35 -06004688 TConstUnionArray zeros(constComponentCount, zero);
4689
4690 less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true));
4691
4692 compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool));
4693 } else {
John Kessenich1a4bbc42017-10-16 13:11:53 -06004694 TIntermTyped* zero;
4695 if (arg0->getType().isIntegerDomain())
4696 zero = intermediate.addConstantUnion(0, loc, true);
4697 else
4698 zero = intermediate.addConstantUnion(0.0, type0, loc, true);
LoopDawg592860c2016-06-09 08:57:35 -06004699 compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero);
4700 }
John Kessenichecba76f2017-01-06 00:34:48 -07004701
LoopDawg592860c2016-06-09 08:57:35 -06004702 TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc);
4703
4704 node = new TIntermSelection(compareNode, killNode, nullptr);
4705 node->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07004706
LoopDawg592860c2016-06-09 08:57:35 -06004707 break;
4708 }
4709
4710 case EOpLog10:
4711 {
4712 // log10(a) -> log2(a) * 0.301029995663981 (== 1/log2(10))
4713 TIntermTyped* arg0 = fnUnary->getOperand();
4714 TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0);
4715 TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true);
4716
4717 node = handleBinaryMath(loc, "mul", EOpMul, log2, base);
4718
4719 break;
4720 }
4721
4722 case EOpDst:
4723 {
4724 // dest.x = 1;
4725 // dest.y = src0.y * src1.y;
4726 // dest.z = src0.z;
4727 // dest.w = src1.w;
4728
4729 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
4730 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
LoopDawg592860c2016-06-09 08:57:35 -06004731
LoopDawg592860c2016-06-09 08:57:35 -06004732 TIntermTyped* y = intermediate.addConstantUnion(1, loc, true);
4733 TIntermTyped* z = intermediate.addConstantUnion(2, loc, true);
4734 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
4735
4736 TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc);
4737 TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc);
4738 TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc);
4739 TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
4740
4741 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
4742
4743 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
4744 dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y));
4745 dst->getSequence().push_back(src0z);
4746 dst->getSequence().push_back(src1w);
LoopDawg6e72fdd2016-06-15 09:50:24 -06004747 dst->setType(TType(EbtFloat, EvqTemporary, 4));
LoopDawg592860c2016-06-09 08:57:35 -06004748 dst->setLoc(loc);
4749 node = dst;
4750
4751 break;
4752 }
4753
LoopDawg58910702016-06-13 09:22:28 -06004754 case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value
4755 case EOpInterlockedMin: // ...
4756 case EOpInterlockedMax: // ...
4757 case EOpInterlockedAnd: // ...
4758 case EOpInterlockedOr: // ...
4759 case EOpInterlockedXor: // ...
4760 case EOpInterlockedExchange: // always has output arg
4761 {
steve-lunarg22322362016-10-19 10:15:25 -06004762 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest
4763 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // value
4764 TIntermTyped* arg2 = nullptr;
LoopDawg58910702016-06-13 09:22:28 -06004765
steve-lunarg22322362016-10-19 10:15:25 -06004766 if (argAggregate->getSequence().size() > 2)
4767 arg2 = argAggregate->getSequence()[2]->getAsTyped();
4768
4769 const bool isImage = isImageParam(arg0);
LoopDawg58910702016-06-13 09:22:28 -06004770 const TOperator atomicOp = mapAtomicOp(loc, op, isImage);
steve-lunarg22322362016-10-19 10:15:25 -06004771 TIntermAggregate* atomic = new TIntermAggregate(atomicOp);
4772 atomic->setType(arg0->getType());
4773 atomic->getWritableType().getQualifier().makeTemporary();
4774 atomic->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07004775
steve-lunarg22322362016-10-19 10:15:25 -06004776 if (isImage) {
4777 // orig_value = imageAtomicOp(image, loc, data)
4778 imageAtomicParams(atomic, arg0);
LoopDawg58910702016-06-13 09:22:28 -06004779 atomic->getSequence().push_back(arg1);
LoopDawg58910702016-06-13 09:22:28 -06004780
steve-lunarg22322362016-10-19 10:15:25 -06004781 if (argAggregate->getSequence().size() > 2) {
4782 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
4783 } else {
4784 node = atomic; // no assignment needed, as there was no out var.
4785 }
LoopDawg58910702016-06-13 09:22:28 -06004786 } else {
steve-lunarg22322362016-10-19 10:15:25 -06004787 // Normal memory variable:
4788 // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value
4789 if (argAggregate->getSequence().size() > 2) {
4790 // optional output param is present. return value goes to arg2.
4791 atomic->getSequence().push_back(arg0);
4792 atomic->getSequence().push_back(arg1);
4793
4794 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
4795 } else {
4796 // Set the matching operator. Since output is absent, this is all we need to do.
4797 node->getAsAggregate()->setOperator(atomicOp);
4798 }
LoopDawg58910702016-06-13 09:22:28 -06004799 }
4800
4801 break;
4802 }
4803
4804 case EOpInterlockedCompareExchange:
4805 {
4806 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest
4807 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // cmp
4808 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); // value
4809 TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped(); // orig
4810
steve-lunarg22322362016-10-19 10:15:25 -06004811 const bool isImage = isImageParam(arg0);
LoopDawg58910702016-06-13 09:22:28 -06004812 TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage));
LoopDawg58910702016-06-13 09:22:28 -06004813 atomic->setLoc(loc);
4814 atomic->setType(arg2->getType());
4815 atomic->getWritableType().getQualifier().makeTemporary();
4816
steve-lunarg22322362016-10-19 10:15:25 -06004817 if (isImage) {
4818 imageAtomicParams(atomic, arg0);
4819 } else {
4820 atomic->getSequence().push_back(arg0);
4821 }
4822
4823 atomic->getSequence().push_back(arg1);
4824 atomic->getSequence().push_back(arg2);
LoopDawg58910702016-06-13 09:22:28 -06004825 node = intermediate.addAssign(EOpAssign, arg3, atomic, loc);
John Kessenichecba76f2017-01-06 00:34:48 -07004826
LoopDawg58910702016-06-13 09:22:28 -06004827 break;
4828 }
4829
LoopDawg6e72fdd2016-06-15 09:50:24 -06004830 case EOpEvaluateAttributeSnapped:
4831 {
4832 // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels
4833 // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y:
4834 // iU = (iU<<28)>>28
4835 // fU = ((float)iU)/16
4836 // Targets might handle this natively, in which case they can disable
4837 // decompositions.
4838
4839 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // value
4840 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // offset
4841
4842 TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true);
4843 TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift,
4844 handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28),
4845 i28);
4846
4847 TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true);
4848 TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul,
4849 intermediate.addConversion(EOpConstructFloat,
4850 TType(EbtFloat, EvqTemporary, 2), iU),
4851 recip16);
John Kessenichecba76f2017-01-06 00:34:48 -07004852
LoopDawg6e72fdd2016-06-15 09:50:24 -06004853 TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset);
4854 interp->getSequence().push_back(arg0);
4855 interp->getSequence().push_back(floatOffset);
4856 interp->setLoc(loc);
4857 interp->setType(arg0->getType());
4858 interp->getWritableType().getQualifier().makeTemporary();
4859
4860 node = interp;
4861
4862 break;
4863 }
4864
4865 case EOpLit:
4866 {
4867 TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped();
4868 TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped();
4869 TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped();
4870
4871 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
4872
4873 // Ambient
4874 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
4875
4876 // Diffuse:
4877 TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
4878 TIntermAggregate* diffuse = new TIntermAggregate(EOpMax);
4879 diffuse->getSequence().push_back(n_dot_l);
4880 diffuse->getSequence().push_back(zero);
4881 diffuse->setLoc(loc);
4882 diffuse->setType(TType(EbtFloat));
4883 dst->getSequence().push_back(diffuse);
4884
4885 // Specular:
4886 TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin);
4887 min_ndot->getSequence().push_back(n_dot_l);
4888 min_ndot->getSequence().push_back(n_dot_h);
4889 min_ndot->setLoc(loc);
4890 min_ndot->setType(TType(EbtFloat));
4891
4892 TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero);
4893 TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m); // n_dot_h * m
4894
4895 dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc));
John Kessenichecba76f2017-01-06 00:34:48 -07004896
LoopDawg6e72fdd2016-06-15 09:50:24 -06004897 // One:
4898 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
4899
4900 dst->setLoc(loc);
4901 dst->setType(TType(EbtFloat, EvqTemporary, 4));
4902 node = dst;
4903 break;
4904 }
4905
LoopDawg1b7fd0f2016-06-22 15:20:14 -06004906 case EOpAsDouble:
4907 {
4908 // asdouble accepts two 32 bit ints. we can use EOpUint64BitsToDouble, but must
4909 // first construct a uint64.
4910 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
4911 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
4912
4913 if (arg0->getType().isVector()) { // TODO: ...
4914 error(loc, "double2 conversion not implemented", "asdouble", "");
4915 break;
4916 }
4917
4918 TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2);
4919
4920 uint64->getSequence().push_back(arg0);
4921 uint64->getSequence().push_back(arg1);
4922 uint64->setType(TType(EbtUint, EvqTemporary, 2)); // convert 2 uints to a uint2
4923 uint64->setLoc(loc);
4924
4925 // bitcast uint2 to a double
4926 TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble);
4927 convert->getAsUnaryNode()->setOperand(uint64);
4928 convert->setLoc(loc);
4929 convert->setType(TType(EbtDouble, EvqTemporary));
4930 node = convert;
John Kessenichecba76f2017-01-06 00:34:48 -07004931
LoopDawg1b7fd0f2016-06-22 15:20:14 -06004932 break;
4933 }
John Kessenichecba76f2017-01-06 00:34:48 -07004934
LoopDawg6e72fdd2016-06-15 09:50:24 -06004935 case EOpF16tof32:
steve-lunarg86b510e2017-02-27 13:09:32 -07004936 {
4937 // input uvecN with low 16 bits of each component holding a float16. convert to float32.
4938 TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
steve-lunargfdbfb652017-02-28 14:14:14 -07004939 TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
steve-lunarg86b510e2017-02-27 13:09:32 -07004940 const int vecSize = argValue->getType().getVectorSize();
4941
4942 TOperator constructOp = EOpNull;
4943 switch (vecSize) {
4944 case 1: constructOp = EOpNull; break; // direct use, no construct needed
4945 case 2: constructOp = EOpConstructVec2; break;
4946 case 3: constructOp = EOpConstructVec3; break;
4947 case 4: constructOp = EOpConstructVec4; break;
4948 default: assert(0); break;
4949 }
4950
4951 // For scalar case, we don't need to construct another type.
4952 TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
4953
4954 if (result) {
4955 result->setType(TType(EbtFloat, EvqTemporary, vecSize));
4956 result->setLoc(loc);
4957 }
4958
4959 for (int idx = 0; idx < vecSize; ++idx) {
4960 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
4961 TIntermTyped* component = argValue->getType().isVector() ?
4962 intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
4963
4964 if (component != argValue)
4965 component->setType(TType(argValue->getBasicType(), EvqTemporary));
4966
4967 TIntermTyped* unpackOp = new TIntermUnary(EOpUnpackHalf2x16);
4968 unpackOp->setType(TType(EbtFloat, EvqTemporary, 2));
4969 unpackOp->getAsUnaryNode()->setOperand(component);
4970 unpackOp->setLoc(loc);
4971
4972 TIntermTyped* lowOrder = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc);
4973
4974 if (result != nullptr) {
4975 result->getSequence().push_back(lowOrder);
4976 node = result;
4977 } else {
4978 node = lowOrder;
4979 }
4980 }
4981
4982 break;
4983 }
4984
LoopDawg6e72fdd2016-06-15 09:50:24 -06004985 case EOpF32tof16:
4986 {
steve-lunarg86b510e2017-02-27 13:09:32 -07004987 // input floatN converted to 16 bit float in low order bits of each component of uintN
4988 TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
4989
4990 TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
4991 const int vecSize = argValue->getType().getVectorSize();
4992
4993 TOperator constructOp = EOpNull;
4994 switch (vecSize) {
4995 case 1: constructOp = EOpNull; break; // direct use, no construct needed
4996 case 2: constructOp = EOpConstructUVec2; break;
4997 case 3: constructOp = EOpConstructUVec3; break;
4998 case 4: constructOp = EOpConstructUVec4; break;
4999 default: assert(0); break;
5000 }
5001
5002 // For scalar case, we don't need to construct another type.
5003 TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
5004
5005 if (result) {
5006 result->setType(TType(EbtUint, EvqTemporary, vecSize));
5007 result->setLoc(loc);
5008 }
5009
5010 for (int idx = 0; idx < vecSize; ++idx) {
5011 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
5012 TIntermTyped* component = argValue->getType().isVector() ?
5013 intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
5014
5015 if (component != argValue)
5016 component->setType(TType(argValue->getBasicType(), EvqTemporary));
5017
5018 TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2);
5019 vec2ComponentAndZero->getSequence().push_back(component);
5020 vec2ComponentAndZero->getSequence().push_back(zero);
5021 vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2));
5022 vec2ComponentAndZero->setLoc(loc);
5023
5024 TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16);
5025 packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero);
5026 packOp->setLoc(loc);
5027 packOp->setType(TType(EbtUint, EvqTemporary));
5028
5029 if (result != nullptr) {
5030 result->getSequence().push_back(packOp);
5031 node = result;
5032 } else {
5033 node = packOp;
5034 }
5035 }
5036
LoopDawg6e72fdd2016-06-15 09:50:24 -06005037 break;
5038 }
5039
steve-lunarg7ea7ff42017-01-03 14:42:18 -07005040 case EOpD3DCOLORtoUBYTE4:
5041 {
5042 // ivec4 ( x.zyxw * 255.001953 );
5043 TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
John Kessenichc142c882017-01-13 19:34:22 -07005044 TSwizzleSelectors<TVectorSelector> selectors;
5045 selectors.push_back(2);
5046 selectors.push_back(1);
5047 selectors.push_back(0);
5048 selectors.push_back(3);
5049 TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc);
steve-lunarg7ea7ff42017-01-03 14:42:18 -07005050 TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc);
5051 swizzled->setType(arg0->getType());
5052 swizzled->getWritableType().getQualifier().makeTemporary();
5053
5054 TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true);
5055 TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled);
5056 rangeConverted->setType(arg0->getType());
5057 rangeConverted->getWritableType().getQualifier().makeTemporary();
5058
5059 node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted);
5060 node->setLoc(loc);
5061 node->setType(TType(EbtInt, EvqTemporary, 4));
5062 break;
5063 }
5064
steve-lunarg13975522017-04-03 20:05:21 -06005065 case EOpIsFinite:
5066 {
5067 // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate
5068 // it to !isnan && !isinf
5069
5070 TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
5071
5072 // We'll make a temporary in case the RHS is cmoplex
5073 TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType());
5074 tempArg->getWritableType().getQualifier().makeTemporary();
5075
5076 TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign,
5077 intermediate.addSymbol(*tempArg, loc),
5078 arg0, loc);
5079
5080 TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc);
5081
John Kessenich2ceec682017-07-28 16:20:13 -06005082 const TType boolType(EbtBool, EvqTemporary, arg0->getVectorSize(), arg0->getMatrixCols(),
5083 arg0->getMatrixRows());
steve-lunarg9e5a19f2017-04-10 08:27:34 -06005084
steve-lunarg13975522017-04-03 20:05:21 -06005085 TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc));
steve-lunarg9e5a19f2017-04-10 08:27:34 -06005086 isnan->setType(boolType);
steve-lunarg13975522017-04-03 20:05:21 -06005087
5088 TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan);
steve-lunarg9e5a19f2017-04-10 08:27:34 -06005089 notnan->setType(boolType);
steve-lunarg13975522017-04-03 20:05:21 -06005090
5091 TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc));
steve-lunarg9e5a19f2017-04-10 08:27:34 -06005092 isinf->setType(boolType);
steve-lunarg13975522017-04-03 20:05:21 -06005093
5094 TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf);
steve-lunarg9e5a19f2017-04-10 08:27:34 -06005095 notinf->setType(boolType);
steve-lunarg13975522017-04-03 20:05:21 -06005096
5097 TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf);
steve-lunarg9e5a19f2017-04-10 08:27:34 -06005098 andNode->setType(boolType);
steve-lunarg13975522017-04-03 20:05:21 -06005099
5100 compoundStatement = intermediate.growAggregate(compoundStatement, andNode);
5101 compoundStatement->setOperator(EOpSequence);
5102 compoundStatement->setLoc(loc);
steve-lunarg9e5a19f2017-04-10 08:27:34 -06005103 compoundStatement->setType(boolType);
steve-lunarg13975522017-04-03 20:05:21 -06005104
5105 node = compoundStatement;
5106
5107 break;
5108 }
5109
LoopDawg592860c2016-06-09 08:57:35 -06005110 default:
5111 break; // most pass through unchanged
5112 }
5113}
5114
John Kesseniche01a9bc2016-03-12 20:11:22 -07005115//
5116// Handle seeing function call syntax in the grammar, which could be any of
5117// - .length() method
5118// - constructor
5119// - a call to a built-in function mapped to an operator
5120// - a call to a built-in function that will remain a function call (e.g., texturing)
5121// - user function
5122// - subroutine call (not implemented yet)
5123//
steve-lunarg26d31452016-12-23 18:56:57 -07005124TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005125{
5126 TIntermTyped* result = nullptr;
5127
5128 TOperator op = function->getBuiltInOp();
John Kessenich88e88e52017-03-08 21:16:35 -07005129 if (op != EOpNull) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005130 //
5131 // Then this should be a constructor.
5132 // Don't go through the symbol table for constructors.
5133 // Their parameters will be verified algorithmically.
5134 //
5135 TType type(EbtVoid); // use this to get the type back
5136 if (! constructorError(loc, arguments, *function, op, type)) {
5137 //
5138 // It's a constructor, of type 'type'.
5139 //
John Kessenichc633f642017-04-03 21:48:37 -06005140 result = handleConstructor(loc, arguments, type);
John Kessenichbb79abc2017-10-07 13:23:09 -06005141 if (result == nullptr) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005142 error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), "");
John Kessenichbb79abc2017-10-07 13:23:09 -06005143 return nullptr;
5144 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005145 }
5146 } else {
5147 //
5148 // Find it in the symbol table.
5149 //
steve-lunarg5da1f032017-02-12 17:50:28 -07005150 const TFunction* fnCandidate = nullptr;
Daniel Koch197082c2017-02-28 09:01:43 -05005151 bool builtIn = false;
John Kessenich0a2a0cd2017-05-16 23:16:26 -06005152 int thisDepth = 0;
steve-lunarg5da1f032017-02-12 17:50:28 -07005153
LoopDawg2e629102017-11-22 10:33:34 -07005154 // For mat mul, the situation is unusual: we have to compare vector sizes to mat row or col sizes,
5155 // and clamp the opposite arg. Since that's complex, we farm it off to a separate method.
5156 // It doesn't naturally fall out of processing an argument at a time in isolation.
5157 if (function->getName() == "mul")
5158 addGenMulArgumentConversion(loc, *function, arguments);
5159
steve-lunarg2bb1f392017-04-27 11:22:32 -06005160 TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr;
5161
steve-lunarg5da1f032017-02-12 17:50:28 -07005162 // TODO: this needs improvement: there's no way at present to look up a signature in
5163 // the symbol table for an arbitrary type. This is a temporary hack until that ability exists.
5164 // It will have false positives, since it doesn't check arg counts or types.
steve-lunarg8e26feb2017-04-10 08:19:21 -06005165 if (arguments) {
5166 // Check if first argument is struct buffer type. It may be an aggregate or a symbol, so we
5167 // look for either case.
steve-lunargdb2e3b42017-03-31 12:47:34 -06005168
steve-lunarg8e26feb2017-04-10 08:19:21 -06005169 TIntermTyped* arg0 = nullptr;
5170
steve-lunarg2bb1f392017-04-27 11:22:32 -06005171 if (aggregate && aggregate->getSequence().size() > 0)
5172 arg0 = aggregate->getSequence()[0]->getAsTyped();
steve-lunarg8e26feb2017-04-10 08:19:21 -06005173 else if (arguments->getAsSymbolNode())
5174 arg0 = arguments->getAsSymbolNode();
5175
5176 if (arg0 != nullptr && isStructBufferType(arg0->getType())) {
steve-lunarge7d07522017-03-19 18:12:37 -06005177 static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1;
5178
5179 if (function->getName().length() > methodPrefixSize &&
5180 isStructBufferMethod(function->getName().substr(methodPrefixSize))) {
steve-lunarg5da1f032017-02-12 17:50:28 -07005181 const TString mangle = function->getName() + "(";
5182 TSymbol* symbol = symbolTable.find(mangle, &builtIn);
5183
5184 if (symbol)
5185 fnCandidate = symbol->getAsFunction();
5186 }
5187 }
5188 }
5189
5190 if (fnCandidate == nullptr)
John Kessenich0a2a0cd2017-05-16 23:16:26 -06005191 fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments);
steve-lunarg5da1f032017-02-12 17:50:28 -07005192
John Kesseniche01a9bc2016-03-12 20:11:22 -07005193 if (fnCandidate) {
5194 // This is a declared function that might map to
5195 // - a built-in operator,
5196 // - a built-in function not mapped to an operator, or
5197 // - a user function.
5198
5199 // Error check for a function requiring specific extensions present.
5200 if (builtIn && fnCandidate->getNumExtensions())
John Kessenich2ceec682017-07-28 16:20:13 -06005201 requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(),
5202 fnCandidate->getName().c_str());
John Kesseniche01a9bc2016-03-12 20:11:22 -07005203
John Kessenich0a2a0cd2017-05-16 23:16:26 -06005204 // turn an implicit member-function resolution into an explicit call
5205 TString callerName;
5206 if (thisDepth == 0)
5207 callerName = fnCandidate->getMangledName();
5208 else {
5209 // get the explicit (full) name of the function
5210 callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth];
5211 callerName += fnCandidate->getMangledName();
5212 // insert the implicit calling argument
5213 pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments);
5214 }
5215
John Kessenich750c2d02017-05-26 00:01:36 -06005216 // Convert 'in' arguments, so that types match.
5217 // However, skip those that need expansion, that is covered next.
John Kessenichd8fe2ca2016-10-01 17:11:21 -06005218 if (arguments)
5219 addInputArgumentConversions(*fnCandidate, arguments);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005220
John Kessenich750c2d02017-05-26 00:01:36 -06005221 // Expand arguments. Some arguments must physically expand to a different set
5222 // than what the shader declared and passes.
5223 if (arguments && !builtIn)
5224 expandArguments(loc, *fnCandidate, arguments);
5225
5226 // Expansion may have changed the form of arguments
5227 aggregate = arguments ? arguments->getAsAggregate() : nullptr;
steve-lunarg2bb1f392017-04-27 11:22:32 -06005228
John Kesseniche01a9bc2016-03-12 20:11:22 -07005229 op = fnCandidate->getBuiltInOp();
5230 if (builtIn && op != EOpNull) {
5231 // A function call mapped to a built-in operation.
John Kessenich2ceec682017-07-28 16:20:13 -06005232 result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments,
5233 fnCandidate->getType());
John Kesseniche01a9bc2016-03-12 20:11:22 -07005234 if (result == nullptr) {
5235 error(arguments->getLoc(), " wrong operand type", "Internal Error",
5236 "built in unary operator function. Type: %s",
5237 static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str());
5238 } else if (result->getAsOperator()) {
5239 builtInOpCheck(loc, *fnCandidate, *result->getAsOperator());
5240 }
5241 } else {
5242 // This is a function call not mapped to built-in operator.
5243 // It could still be a built-in function, but only if PureOperatorBuiltins == false.
5244 result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc);
5245 TIntermAggregate* call = result->getAsAggregate();
John Kessenich0a2a0cd2017-05-16 23:16:26 -06005246 call->setName(callerName);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005247
5248 // this is how we know whether the given function is a built-in function or a user-defined function
5249 // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also
5250 // if builtIn == true, it's definitely a built-in function with EOpNull
5251 if (! builtIn) {
5252 call->setUserDefined();
John Kessenich0a2a0cd2017-05-16 23:16:26 -06005253 intermediate.addToCallGraph(infoSink, currentCaller, callerName);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005254 }
5255 }
5256
steve-lunargef33ec02016-11-02 12:42:34 -06005257 // for decompositions, since we want to operate on the function node, not the aggregate holding
5258 // output conversions.
John Kessenichecba76f2017-01-06 00:34:48 -07005259 const TIntermTyped* fnNode = result;
steve-lunargef33ec02016-11-02 12:42:34 -06005260
steve-lunarg5da1f032017-02-12 17:50:28 -07005261 decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions
5262 decomposeIntrinsic(loc, result, arguments); // HLSL->AST intrinsic decompositions
5263 decomposeSampleMethods(loc, result, arguments); // HLSL->AST sample method decompositions
5264 decomposeGeometryMethods(loc, result, arguments); // HLSL->AST geometry method decompositions
steve-lunargef33ec02016-11-02 12:42:34 -06005265
John Kessenich750c2d02017-05-26 00:01:36 -06005266 // Create the qualifier list, carried in the AST for the call.
5267 // Because some arguments expand to multiple arguments, the qualifier list will
5268 // be longer than the formal parameter list.
5269 if (result == fnNode && result->getAsAggregate()) {
5270 TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList();
5271 for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
5272 TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage;
5273 if (hasStructBuffCounter(*(*fnCandidate)[i].type)) {
5274 // add buffer and counter buffer argument qualifier
5275 qualifierList.push_back(qual);
5276 qualifierList.push_back(qual);
John Kessenich41aa1992017-10-11 14:03:45 -06005277 } else if (shouldFlatten(*(*fnCandidate)[i].type, (*fnCandidate)[i].type->getQualifier().storage,
5278 true)) {
John Kessenich750c2d02017-05-26 00:01:36 -06005279 // add structure member expansion
5280 for (int memb = 0; memb < (int)(*fnCandidate)[i].type->getStruct()->size(); ++memb)
5281 qualifierList.push_back(qual);
5282 } else {
5283 // Normal 1:1 case
5284 qualifierList.push_back(qual);
5285 }
5286 }
5287 }
5288
John Kesseniche01a9bc2016-03-12 20:11:22 -07005289 // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore.
5290 // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output.
5291 // Also, build the qualifier list for user function calls, which are always called with an aggregate.
steve-lunargef33ec02016-11-02 12:42:34 -06005292 // We don't do this is if there has been a decomposition, which will have added its own conversions
5293 // for output parameters.
John Kessenich750c2d02017-05-26 00:01:36 -06005294 if (result == fnNode && result->getAsAggregate())
steve-lunargef33ec02016-11-02 12:42:34 -06005295 result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator());
John Kesseniche01a9bc2016-03-12 20:11:22 -07005296 }
5297 }
5298
5299 // generic error recovery
John Kessenich2ceec682017-07-28 16:20:13 -06005300 // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to
5301 // reduce cascades
John Kesseniche01a9bc2016-03-12 20:11:22 -07005302 if (result == nullptr)
5303 result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
5304
5305 return result;
5306}
5307
John Kessenich0a2a0cd2017-05-16 23:16:26 -06005308// An initial argument list is difficult: it can be null, or a single node,
5309// or an aggregate if more than one argument. Add one to the front, maintaining
5310// this lack of uniformity.
5311void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments)
5312{
5313 if (arguments == nullptr)
5314 arguments = front;
5315 else if (arguments->getAsAggregate() != nullptr)
5316 arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front);
5317 else
5318 arguments = intermediate.growAggregate(front, arguments);
5319}
5320
John Kesseniche01a9bc2016-03-12 20:11:22 -07005321//
LoopDawg2e629102017-11-22 10:33:34 -07005322// HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat. This is a
5323// situation not well suited to resolution in intrinsic selection, but we can do so here, since we
LoopDawgcee29b02017-11-27 09:42:53 -07005324// can look at both arguments insert explicit shape changes if required.
LoopDawg2e629102017-11-22 10:33:34 -07005325//
5326void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args)
5327{
5328 TIntermAggregate* argAggregate = args ? args->getAsAggregate() : nullptr;
5329
5330 if (argAggregate == nullptr || argAggregate->getSequence().size() != 2) {
5331 // It really ought to have two arguments.
5332 error(loc, "expected: mul arguments", "", "");
5333 return;
5334 }
5335
5336 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
5337 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
5338
5339 if (arg0->isVector() && arg1->isVector()) {
5340 // For:
5341 // vec * vec: it's handled during intrinsic selection, so while we could do it here,
5342 // we can also ignore it, which is easier.
5343 } else if (arg0->isVector() && arg1->isMatrix()) {
5344 // vec * mat: we clamp the vec if the mat col is smaller, else clamp the mat col.
5345 if (arg0->getVectorSize() < arg1->getMatrixCols()) {
5346 // vec is smaller, so truncate larger mat dimension
5347 const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5348 0, arg0->getVectorSize(), arg1->getMatrixRows());
5349 arg1 = addConstructor(loc, arg1, truncType);
5350 } else if (arg0->getVectorSize() > arg1->getMatrixCols()) {
5351 // vec is larger, so truncate vec to mat size
5352 const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5353 arg1->getMatrixCols());
5354 arg0 = addConstructor(loc, arg0, truncType);
5355 }
5356 } else if (arg0->isMatrix() && arg1->isVector()) {
5357 // mat * vec: we clamp the vec if the mat col is smaller, else clamp the mat col.
5358 if (arg1->getVectorSize() < arg0->getMatrixRows()) {
5359 // vec is smaller, so truncate larger mat dimension
5360 const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5361 0, arg0->getMatrixCols(), arg1->getVectorSize());
5362 arg0 = addConstructor(loc, arg0, truncType);
5363 } else if (arg1->getVectorSize() > arg0->getMatrixRows()) {
5364 // vec is larger, so truncate vec to mat size
5365 const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5366 arg0->getMatrixRows());
5367 arg1 = addConstructor(loc, arg1, truncType);
5368 }
5369 } else if (arg0->isMatrix() && arg1->isMatrix()) {
LoopDawgcee29b02017-11-27 09:42:53 -07005370 // mat * mat: we clamp the smaller inner dimension to match the other matrix size.
5371 // Remember, HLSL Mrc = GLSL/SPIRV Mcr.
5372 if (arg0->getMatrixRows() > arg1->getMatrixCols()) {
5373 const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
5374 0, arg0->getMatrixCols(), arg1->getMatrixCols());
5375 arg0 = addConstructor(loc, arg0, truncType);
5376 } else if (arg0->getMatrixRows() < arg1->getMatrixCols()) {
5377 const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
5378 0, arg0->getMatrixRows(), arg1->getMatrixRows());
5379 arg1 = addConstructor(loc, arg1, truncType);
5380 }
LoopDawg2e629102017-11-22 10:33:34 -07005381 } else {
LoopDawgcee29b02017-11-27 09:42:53 -07005382 // It's something with scalars: we'll just leave it alone. Function selection will handle it
5383 // downstream.
LoopDawg2e629102017-11-22 10:33:34 -07005384 }
5385
LoopDawg8c49f9b2017-11-29 10:00:01 -07005386 // Warn if we altered one of the arguments
5387 if (arg0 != argAggregate->getSequence()[0] || arg1 != argAggregate->getSequence()[1])
5388 warn(loc, "mul() matrix size mismatch", "", "");
5389
LoopDawgcee29b02017-11-27 09:42:53 -07005390 // Put arguments back. (They might be unchanged, in which case this is harmless).
LoopDawg2e629102017-11-22 10:33:34 -07005391 argAggregate->getSequence()[0] = arg0;
5392 argAggregate->getSequence()[1] = arg1;
5393
5394 call[0].type = &arg0->getWritableType();
5395 call[1].type = &arg1->getWritableType();
5396}
5397
5398//
John Kesseniche01a9bc2016-03-12 20:11:22 -07005399// Add any needed implicit conversions for function-call arguments to input parameters.
5400//
steve-lunarg26d31452016-12-23 18:56:57 -07005401void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005402{
5403 TIntermAggregate* aggregate = arguments->getAsAggregate();
John Kessenich750c2d02017-05-26 00:01:36 -06005404
5405 // Replace a single argument with a single argument.
5406 const auto setArg = [&](int paramNum, TIntermTyped* arg) {
John Kessenich5159d4f2016-09-19 00:06:19 -06005407 if (function.getParamCount() == 1)
5408 arguments = arg;
5409 else {
John Kessenich750c2d02017-05-26 00:01:36 -06005410 if (aggregate == nullptr)
John Kessenich5159d4f2016-09-19 00:06:19 -06005411 arguments = arg;
John Kessenich750c2d02017-05-26 00:01:36 -06005412 else
5413 aggregate->getSequence()[paramNum] = arg;
John Kessenich5159d4f2016-09-19 00:06:19 -06005414 }
5415 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07005416
5417 // Process each argument's conversion
John Kessenich750c2d02017-05-26 00:01:36 -06005418 for (int param = 0; param < function.getParamCount(); ++param) {
5419 if (! function[param].type->getQualifier().isParamInput())
John Kessenichc86d38b2016-10-01 13:30:37 -06005420 continue;
5421
John Kesseniche01a9bc2016-03-12 20:11:22 -07005422 // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
5423 // is the single argument itself or its children are the arguments. Only one argument
5424 // means take 'arguments' itself as the one argument.
John Kessenichd8fe2ca2016-10-01 17:11:21 -06005425 TIntermTyped* arg = function.getParamCount() == 1
5426 ? arguments->getAsTyped()
John Kessenich750c2d02017-05-26 00:01:36 -06005427 : (aggregate ?
5428 aggregate->getSequence()[param]->getAsTyped() :
5429 arguments->getAsTyped());
5430 if (*function[param].type != arg->getType()) {
John Kessenichc86d38b2016-10-01 13:30:37 -06005431 // In-qualified arguments just need an extra node added above the argument to
5432 // convert to the correct type.
John Kessenich750c2d02017-05-26 00:01:36 -06005433 TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[param].type, arg);
John Kessenich1e275c82016-12-14 17:02:32 -07005434 if (convArg != nullptr)
John Kessenich750c2d02017-05-26 00:01:36 -06005435 convArg = intermediate.addUniShapeConversion(EOpFunctionCall, *function[param].type, convArg);
John Kessenich1e275c82016-12-14 17:02:32 -07005436 if (convArg != nullptr)
John Kessenich750c2d02017-05-26 00:01:36 -06005437 setArg(param, convArg);
John Kessenich1e275c82016-12-14 17:02:32 -07005438 else
John Kessenich750c2d02017-05-26 00:01:36 -06005439 error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", param);
John Kessenich5159d4f2016-09-19 00:06:19 -06005440 } else {
John Kessenich8bcdf2e2017-07-30 16:54:02 -06005441 if (wasFlattened(arg)) {
John Kessenich750c2d02017-05-26 00:01:36 -06005442 // If both formal and calling arg are to be flattened, leave that to argument
5443 // expansion, not conversion.
John Kessenich41aa1992017-10-11 14:03:45 -06005444 if (!shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
John Kessenich750c2d02017-05-26 00:01:36 -06005445 // Will make a two-level subtree.
5446 // The deepest will copy member-by-member to build the structure to pass.
5447 // The level above that will be a two-operand EOpComma sequence that follows the copy by the
5448 // object itself.
5449 TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[param].type);
5450 internalAggregate->getWritableType().getQualifier().makeTemporary();
5451 TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(),
5452 internalAggregate->getName(),
5453 internalAggregate->getType());
5454 internalSymbolNode->setLoc(arg->getLoc());
5455 // This makes the deepest level, the member-wise copy
John Kessenich2ceec682017-07-28 16:20:13 -06005456 TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign,
5457 internalSymbolNode, arg)->getAsAggregate();
John Kessenich6714bcc2016-09-21 17:50:12 -06005458
John Kessenich750c2d02017-05-26 00:01:36 -06005459 // Now, pair that with the resulting aggregate.
5460 assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc());
5461 assignAgg->setOperator(EOpComma);
5462 assignAgg->setType(internalAggregate->getType());
5463 setArg(param, assignAgg);
5464 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005465 }
5466 }
5467 }
5468}
5469
5470//
John Kessenich750c2d02017-05-26 00:01:36 -06005471// Add any needed implicit expansion of calling arguments from what the shader listed to what's
5472// internally needed for the AST (given the constraints downstream).
5473//
5474void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments)
5475{
5476 TIntermAggregate* aggregate = arguments->getAsAggregate();
5477 int functionParamNumberOffset = 0;
5478
5479 // Replace a single argument with a single argument.
5480 const auto setArg = [&](int paramNum, TIntermTyped* arg) {
5481 if (function.getParamCount() + functionParamNumberOffset == 1)
5482 arguments = arg;
5483 else {
5484 if (aggregate == nullptr)
5485 arguments = arg;
5486 else
5487 aggregate->getSequence()[paramNum] = arg;
5488 }
5489 };
5490
5491 // Replace a single argument with a list of arguments
5492 const auto setArgList = [&](int paramNum, const TVector<TIntermTyped*>& args) {
5493 if (args.size() == 1)
5494 setArg(paramNum, args.front());
John Kessenich4e2f05d2017-08-29 12:36:09 -06005495 else if (args.size() > 1) {
John Kessenich750c2d02017-05-26 00:01:36 -06005496 if (function.getParamCount() + functionParamNumberOffset == 1) {
5497 arguments = intermediate.makeAggregate(args.front());
5498 std::for_each(args.begin() + 1, args.end(),
5499 [&](TIntermTyped* arg) {
5500 arguments = intermediate.growAggregate(arguments, arg);
5501 });
5502 } else {
5503 auto it = aggregate->getSequence().erase(aggregate->getSequence().begin() + paramNum);
5504 aggregate->getSequence().insert(it, args.begin(), args.end());
5505 }
John Kessenich4e2f05d2017-08-29 12:36:09 -06005506 functionParamNumberOffset += (int)(args.size() - 1);
John Kessenich750c2d02017-05-26 00:01:36 -06005507 }
John Kessenich750c2d02017-05-26 00:01:36 -06005508 };
5509
5510 // Process each argument's conversion
5511 for (int param = 0; param < function.getParamCount(); ++param) {
5512 // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
5513 // is the single argument itself or its children are the arguments. Only one argument
5514 // means take 'arguments' itself as the one argument.
5515 TIntermTyped* arg = function.getParamCount() == 1
5516 ? arguments->getAsTyped()
5517 : (aggregate ?
5518 aggregate->getSequence()[param + functionParamNumberOffset]->getAsTyped() :
5519 arguments->getAsTyped());
5520
John Kessenich41aa1992017-10-11 14:03:45 -06005521 if (wasFlattened(arg) && shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
John Kessenich750c2d02017-05-26 00:01:36 -06005522 // Need to pass the structure members instead of the structure.
5523 TVector<TIntermTyped*> memberArgs;
John Kessenichd1be7542017-06-29 17:43:31 -06005524 for (int memb = 0; memb < (int)arg->getType().getStruct()->size(); ++memb)
John Kessenich750c2d02017-05-26 00:01:36 -06005525 memberArgs.push_back(flattenAccess(arg, memb));
John Kessenich750c2d02017-05-26 00:01:36 -06005526 setArgList(param + functionParamNumberOffset, memberArgs);
5527 }
5528 }
5529
5530 // TODO: if we need both hidden counter args (below) and struct expansion (above)
5531 // the two algorithms need to be merged: Each assumes the list starts out 1:1 between
5532 // parameters and arguments.
5533
5534 // If any argument is a pass-by-reference struct buffer with an associated counter
5535 // buffer, we have to add another hidden parameter for that counter.
5536 if (aggregate)
5537 addStructBuffArguments(loc, aggregate);
5538}
5539
5540//
John Kesseniche01a9bc2016-03-12 20:11:22 -07005541// Add any needed implicit output conversions for function-call arguments. This
5542// can require a new tree topology, complicated further by whether the function
5543// has a return value.
5544//
5545// Returns a node of a subtree that evaluates to the return value of the function.
5546//
steve-lunargef33ec02016-11-02 12:42:34 -06005547TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005548{
steve-lunargef33ec02016-11-02 12:42:34 -06005549 assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr);
5550
5551 const TSourceLoc& loc = intermNode.getLoc();
5552
5553 TIntermSequence argSequence; // temp sequence for unary node args
5554
5555 if (intermNode.getAsUnaryNode())
5556 argSequence.push_back(intermNode.getAsUnaryNode()->getOperand());
5557
5558 TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence;
5559
John Kessenichd8fe2ca2016-10-01 17:11:21 -06005560 const auto needsConversion = [&](int argNum) {
5561 return function[argNum].type->getQualifier().isParamOutput() &&
5562 (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() ||
steve-lunarg90707962016-10-07 19:35:40 -06005563 shouldConvertLValue(arguments[argNum]) ||
steve-lunarga2b01a02016-11-28 17:09:54 -07005564 wasFlattened(arguments[argNum]->getAsTyped()));
John Kessenichd8fe2ca2016-10-01 17:11:21 -06005565 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07005566
5567 // Will there be any output conversions?
5568 bool outputConversions = false;
5569 for (int i = 0; i < function.getParamCount(); ++i) {
John Kessenichd8fe2ca2016-10-01 17:11:21 -06005570 if (needsConversion(i)) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005571 outputConversions = true;
5572 break;
5573 }
5574 }
5575
5576 if (! outputConversions)
5577 return &intermNode;
5578
5579 // Setup for the new tree, if needed:
5580 //
5581 // Output conversions need a different tree topology.
5582 // Out-qualified arguments need a temporary of the correct type, with the call
5583 // followed by an assignment of the temporary to the original argument:
5584 // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...)
5585 // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet)
5586 // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment.
5587 TIntermTyped* conversionTree = nullptr;
5588 TVariable* tempRet = nullptr;
5589 if (intermNode.getBasicType() != EbtVoid) {
5590 // do the "tempRet = function(...), " bit from above
5591 tempRet = makeInternalVariable("tempReturn", intermNode.getType());
steve-lunargef33ec02016-11-02 12:42:34 -06005592 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
5593 conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005594 } else
5595 conversionTree = &intermNode;
5596
5597 conversionTree = intermediate.makeAggregate(conversionTree);
5598
5599 // Process each argument's conversion
5600 for (int i = 0; i < function.getParamCount(); ++i) {
John Kessenichd8fe2ca2016-10-01 17:11:21 -06005601 if (needsConversion(i)) {
5602 // Out-qualified arguments needing conversion need to use the topology setup above.
5603 // Do the " ...(tempArg, ...), arg = tempArg" bit from above.
5604
5605 // Make a temporary for what the function expects the argument to look like.
5606 TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type);
5607 tempArg->getWritableType().getQualifier().makeTemporary();
steve-lunargef33ec02016-11-02 12:42:34 -06005608 TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc);
John Kessenichd8fe2ca2016-10-01 17:11:21 -06005609
5610 // This makes the deepest level, the member-wise copy
John Kessenich2ceec682017-07-28 16:20:13 -06005611 TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(),
5612 tempArgNode);
steve-lunarg90707962016-10-07 19:35:40 -06005613 tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign);
John Kessenichd8fe2ca2016-10-01 17:11:21 -06005614 conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc());
5615
5616 // replace the argument with another node for the same tempArg variable
steve-lunargef33ec02016-11-02 12:42:34 -06005617 arguments[i] = intermediate.addSymbol(*tempArg, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005618 }
5619 }
5620
5621 // Finalize the tree topology (see bigger comment above).
5622 if (tempRet) {
5623 // do the "..., tempRet" bit from above
steve-lunargef33ec02016-11-02 12:42:34 -06005624 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
5625 conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005626 }
steve-lunargef33ec02016-11-02 12:42:34 -06005627
5628 conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005629
5630 return conversionTree;
5631}
5632
5633//
steve-lunarg2bb1f392017-04-27 11:22:32 -06005634// Add any needed "hidden" counter buffer arguments for function calls.
5635//
5636// Modifies the 'aggregate' argument if needed. Otherwise, is no-op.
5637//
5638void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate)
5639{
5640 // See if there are any SB types with counters.
5641 const bool hasStructBuffArg =
5642 std::any_of(aggregate->getSequence().begin(),
5643 aggregate->getSequence().end(),
5644 [this](const TIntermNode* node) {
5645 return (node->getAsTyped() != nullptr) && hasStructBuffCounter(node->getAsTyped()->getType());
5646 });
5647
5648 // Nothing to do, if we didn't find one.
5649 if (! hasStructBuffArg)
5650 return;
5651
5652 TIntermSequence argsWithCounterBuffers;
5653
John Kessenich750c2d02017-05-26 00:01:36 -06005654 for (int param = 0; param < int(aggregate->getSequence().size()); ++param) {
steve-lunarg2bb1f392017-04-27 11:22:32 -06005655 argsWithCounterBuffers.push_back(aggregate->getSequence()[param]);
5656
5657 if (hasStructBuffCounter(aggregate->getSequence()[param]->getAsTyped()->getType())) {
5658 const TIntermSymbol* blockSym = aggregate->getSequence()[param]->getAsSymbolNode();
5659 if (blockSym != nullptr) {
5660 TType counterType;
5661 counterBufferType(loc, counterType);
5662
5663 const TString counterBlockName(getStructBuffCounterName(blockSym->getName()));
5664
5665 TVariable* variable = makeInternalVariable(counterBlockName, counterType);
5666
5667 // Mark this buffer as requiring a counter block. TODO: there should be a better
5668 // way to track it.
5669 structBufferCounter[counterBlockName] = true;
5670
5671 TIntermSymbol* sym = intermediate.addSymbol(*variable, loc);
5672 argsWithCounterBuffers.push_back(sym);
5673 }
5674 }
5675 }
5676
5677 // Swap with the temp list we've built up.
5678 aggregate->getSequence().swap(argsWithCounterBuffers);
5679}
5680
5681
5682//
John Kesseniche01a9bc2016-03-12 20:11:22 -07005683// Do additional checking of built-in function calls that is not caught
5684// by normal semantic checks on argument type, extension tagging, etc.
5685//
5686// Assumes there has been a semantically correct match to a built-in function prototype.
5687//
5688void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode)
5689{
5690 // Set up convenience accessors to the argument(s). There is almost always
5691 // multiple arguments for the cases below, but when there might be one,
5692 // check the unaryArg first.
5693 const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference
5694 const TIntermTyped* unaryArg = nullptr;
5695 const TIntermTyped* arg0 = nullptr;
5696 if (callNode.getAsAggregate()) {
5697 argp = &callNode.getAsAggregate()->getSequence();
5698 if (argp->size() > 0)
5699 arg0 = (*argp)[0]->getAsTyped();
5700 } else {
5701 assert(callNode.getAsUnaryNode());
5702 unaryArg = callNode.getAsUnaryNode()->getOperand();
5703 arg0 = unaryArg;
5704 }
5705 const TIntermSequence& aggArgs = *argp; // only valid when unaryArg is nullptr
5706
John Kesseniche01a9bc2016-03-12 20:11:22 -07005707 switch (callNode.getOp()) {
5708 case EOpTextureGather:
5709 case EOpTextureGatherOffset:
5710 case EOpTextureGatherOffsets:
5711 {
5712 // Figure out which variants are allowed by what extensions,
5713 // and what arguments must be constant for which situations.
5714
5715 TString featureString = fnCandidate.getName() + "(...)";
5716 const char* feature = featureString.c_str();
5717 int compArg = -1; // track which argument, if any, is the constant component argument
5718 switch (callNode.getOp()) {
5719 case EOpTextureGather:
5720 // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
5721 // otherwise, need GL_ARB_texture_gather.
John Kessenich2ceec682017-07-28 16:20:13 -06005722 if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect ||
5723 fnCandidate[0].type->getSampler().shadow) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005724 if (! fnCandidate[0].type->getSampler().shadow)
5725 compArg = 2;
5726 }
5727 break;
5728 case EOpTextureGatherOffset:
5729 // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
5730 if (! fnCandidate[0].type->getSampler().shadow)
5731 compArg = 3;
5732 break;
5733 case EOpTextureGatherOffsets:
5734 if (! fnCandidate[0].type->getSampler().shadow)
5735 compArg = 3;
5736 break;
5737 default:
5738 break;
5739 }
5740
5741 if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
5742 if (aggArgs[compArg]->getAsConstantUnion()) {
5743 int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
5744 if (value < 0 || value > 3)
5745 error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
5746 } else
5747 error(loc, "must be a compile-time constant:", feature, "component argument");
5748 }
5749
5750 break;
5751 }
5752
5753 case EOpTextureOffset:
5754 case EOpTextureFetchOffset:
5755 case EOpTextureProjOffset:
5756 case EOpTextureLodOffset:
5757 case EOpTextureProjLodOffset:
5758 case EOpTextureGradOffset:
5759 case EOpTextureProjGradOffset:
5760 {
5761 // Handle texture-offset limits checking
5762 // Pick which argument has to hold constant offsets
5763 int arg = -1;
5764 switch (callNode.getOp()) {
5765 case EOpTextureOffset: arg = 2; break;
5766 case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break;
5767 case EOpTextureProjOffset: arg = 2; break;
5768 case EOpTextureLodOffset: arg = 3; break;
5769 case EOpTextureProjLodOffset: arg = 3; break;
5770 case EOpTextureGradOffset: arg = 4; break;
5771 case EOpTextureProjGradOffset: arg = 4; break;
5772 default:
5773 assert(0);
5774 break;
5775 }
5776
5777 if (arg > 0) {
John Kessenichf02c8e62017-06-19 16:25:44 -06005778 if (aggArgs[arg]->getAsConstantUnion() == nullptr)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005779 error(loc, "argument must be compile-time constant", "texel offset", "");
5780 else {
5781 const TType& type = aggArgs[arg]->getAsTyped()->getType();
5782 for (int c = 0; c < type.getVectorSize(); ++c) {
5783 int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
5784 if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
John Kessenich2ceec682017-07-28 16:20:13 -06005785 error(loc, "value is out of range:", "texel offset",
5786 "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005787 }
5788 }
5789 }
5790
5791 break;
5792 }
5793
5794 case EOpTextureQuerySamples:
5795 case EOpImageQuerySamples:
5796 break;
5797
5798 case EOpImageAtomicAdd:
5799 case EOpImageAtomicMin:
5800 case EOpImageAtomicMax:
5801 case EOpImageAtomicAnd:
5802 case EOpImageAtomicOr:
5803 case EOpImageAtomicXor:
5804 case EOpImageAtomicExchange:
5805 case EOpImageAtomicCompSwap:
5806 break;
5807
5808 case EOpInterpolateAtCentroid:
5809 case EOpInterpolateAtSample:
5810 case EOpInterpolateAtOffset:
John Kesseniche01a9bc2016-03-12 20:11:22 -07005811 // Make sure the first argument is an interpolant, or an array element of an interpolant
5812 if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
5813 // It might still be an array element.
5814 //
5815 // We could check more, but the semantics of the first argument are already met; the
5816 // only way to turn an array into a float/vec* is array dereference and swizzle.
5817 //
5818 // ES and desktop 4.3 and earlier: swizzles may not be used
5819 // desktop 4.4 and later: swizzles may be used
5820 const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true);
5821 if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
John Kessenich2ceec682017-07-28 16:20:13 -06005822 error(loc, "first argument must be an interpolant, or interpolant-array element",
5823 fnCandidate.getName().c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005824 }
5825 break;
5826
5827 default:
5828 break;
5829 }
5830}
5831
5832//
John Kessenichc633f642017-04-03 21:48:37 -06005833// Handle seeing something in a grammar production that can be done by calling
5834// a constructor.
John Kesseniche01a9bc2016-03-12 20:11:22 -07005835//
John Kessenichc633f642017-04-03 21:48:37 -06005836// The constructor still must be "handled" by handleFunctionCall(), which will
5837// then call handleConstructor().
5838//
5839TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005840{
John Kessenicha26a5172016-07-28 15:29:35 -06005841 TOperator op = intermediate.mapTypeToConstructorOp(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005842
5843 if (op == EOpNull) {
5844 error(loc, "cannot construct this type", type.getBasicString(), "");
John Kessenichd016be12016-03-13 11:24:20 -06005845 return nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005846 }
5847
5848 TString empty("");
5849
5850 return new TFunction(&empty, type, op);
5851}
5852
5853//
John Kessenich630dd7d2016-06-12 23:52:12 -06005854// Handle seeing a "COLON semantic" at the end of a type declaration,
5855// by updating the type according to the semantic.
5856//
John Kessenich89f8d1e2017-06-27 15:17:38 -06005857void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn,
5858 const TString& upperCase)
John Kessenich630dd7d2016-06-12 23:52:12 -06005859{
LoopDawg307b6502017-07-05 11:33:06 -06005860 // Parse and return semantic number. If limit is 0, it will be ignored. Otherwise, if the parsed
5861 // semantic number is >= limit, errorMsg is issued and 0 is returned.
5862 // TODO: it would be nicer if limit and errorMsg had default parameters, but some compilers don't yet
5863 // accept those in lambda functions.
5864 const auto getSemanticNumber = [this, loc](const TString& semantic, unsigned int limit, const char* errorMsg) -> unsigned int {
John Kessenich89f8d1e2017-06-27 15:17:38 -06005865 size_t pos = semantic.find_last_not_of("0123456789");
5866 if (pos == std::string::npos)
5867 return 0u;
LoopDawg307b6502017-07-05 11:33:06 -06005868
5869 unsigned int semanticNum = (unsigned int)atoi(semantic.c_str() + pos + 1);
5870
5871 if (limit != 0 && semanticNum >= limit) {
5872 error(loc, errorMsg, semantic.c_str(), "");
5873 return 0u;
5874 }
5875
5876 return semanticNum;
John Kessenich89f8d1e2017-06-27 15:17:38 -06005877 };
Dan Baker26aa8a42016-08-25 17:13:25 -04005878
John Kessenich6e1d50a2017-03-09 14:37:32 -07005879 switch(builtIn) {
John Kessenich89f8d1e2017-06-27 15:17:38 -06005880 case EbvNone:
5881 // Get location numbers from fragment outputs, instead of
5882 // auto-assigning them.
5883 if (language == EShLangFragment && upperCase.compare(0, 9, "SV_TARGET") == 0) {
LoopDawg307b6502017-07-05 11:33:06 -06005884 qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr);
John Kessenich89f8d1e2017-06-27 15:17:38 -06005885 nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u);
LoopDawg307b6502017-07-05 11:33:06 -06005886 } else if (upperCase.compare(0, 15, "SV_CLIPDISTANCE") == 0) {
5887 builtIn = EbvClipDistance;
5888 qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid clip semantic");
5889 } else if (upperCase.compare(0, 15, "SV_CULLDISTANCE") == 0) {
5890 builtIn = EbvCullDistance;
5891 qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid cull semantic");
John Kessenich89f8d1e2017-06-27 15:17:38 -06005892 }
5893 break;
John Kessenich6e1d50a2017-03-09 14:37:32 -07005894 case EbvPosition:
John Kessenich89f8d1e2017-06-27 15:17:38 -06005895 // adjust for stage in/out
John Kessenich6e1d50a2017-03-09 14:37:32 -07005896 if (language == EShLangFragment)
5897 builtIn = EbvFragCoord;
5898 break;
Rex Xu37cdcee2017-06-29 17:46:34 +08005899 case EbvFragStencilRef:
John Kessenich6e1d50a2017-03-09 14:37:32 -07005900 error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", "");
5901 break;
steve-lunarg7afe1342017-03-18 22:24:14 -06005902 case EbvTessLevelInner:
5903 case EbvTessLevelOuter:
5904 qualifier.patch = true;
5905 break;
John Kessenich6e1d50a2017-03-09 14:37:32 -07005906 default:
5907 break;
Dan Bakerdeec03c2016-08-25 11:59:17 -04005908 }
Dan Bakerdeec03c2016-08-25 11:59:17 -04005909
John Kessenich788fbeb2017-12-15 08:15:26 -07005910 if (qualifier.builtIn == EbvNone)
5911 qualifier.builtIn = builtIn;
John Kessenich2dd643f2017-03-14 21:50:06 -06005912 qualifier.semanticName = intermediate.addSemanticName(upperCase);
John Kessenich630dd7d2016-06-12 23:52:12 -06005913}
5914
5915//
John Kessenich96e9f472016-07-29 14:28:39 -06005916// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN"
John Kessenich82d6baf2016-07-29 13:03:05 -06005917//
5918// 'location' has the "c[Subcomponent]" part.
5919// 'component' points to the "component" part, or nullptr if not present.
5920//
John Kessenich7735b942016-09-05 12:40:06 -06005921void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location,
5922 const glslang::TString* component)
John Kessenich82d6baf2016-07-29 13:03:05 -06005923{
5924 if (location.size() == 0 || location[0] != 'c') {
5925 error(loc, "expected 'c'", "packoffset", "");
5926 return;
5927 }
5928 if (location.size() == 1)
5929 return;
5930 if (! isdigit(location[1])) {
5931 error(loc, "expected number after 'c'", "packoffset", "");
5932 return;
5933 }
5934
John Kessenich7735b942016-09-05 12:40:06 -06005935 qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str());
John Kessenich96e9f472016-07-29 14:28:39 -06005936 if (component != nullptr) {
John Kessenich82d6baf2016-07-29 13:03:05 -06005937 int componentOffset = 0;
5938 switch ((*component)[0]) {
5939 case 'x': componentOffset = 0; break;
5940 case 'y': componentOffset = 4; break;
5941 case 'z': componentOffset = 8; break;
5942 case 'w': componentOffset = 12; break;
5943 default:
5944 componentOffset = -1;
5945 break;
5946 }
5947 if (componentOffset < 0 || component->size() > 1) {
5948 error(loc, "expected {x, y, z, w} for component", "packoffset", "");
5949 return;
5950 }
John Kessenich7735b942016-09-05 12:40:06 -06005951 qualifier.layoutOffset += componentOffset;
John Kessenich82d6baf2016-07-29 13:03:05 -06005952 }
5953}
5954
5955//
John Kessenich96e9f472016-07-29 14:28:39 -06005956// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN"
5957//
5958// 'profile' points to the shader_profile part, or nullptr if not present.
5959// 'desc' is the type# part.
5960//
John Kessenich7735b942016-09-05 12:40:06 -06005961void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile,
John Kessenichcfd7ce82016-09-05 16:03:12 -06005962 const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc)
John Kessenich96e9f472016-07-29 14:28:39 -06005963{
5964 if (profile != nullptr)
5965 warn(loc, "ignoring shader_profile", "register", "");
5966
John Kessenichb38f0712016-07-30 10:29:54 -06005967 if (desc.size() < 1) {
5968 error(loc, "expected register type", "register", "");
John Kessenich96e9f472016-07-29 14:28:39 -06005969 return;
5970 }
5971
John Kessenichb38f0712016-07-30 10:29:54 -06005972 int regNumber = 0;
5973 if (desc.size() > 1) {
5974 if (isdigit(desc[1]))
5975 regNumber = atoi(desc.substr(1, desc.size()).c_str());
5976 else {
5977 error(loc, "expected register number after register type", "register", "");
5978 return;
5979 }
John Kessenich96e9f472016-07-29 14:28:39 -06005980 }
5981
John Kessenichb38f0712016-07-30 10:29:54 -06005982 // TODO: learn what all these really mean and how they interact with regNumber and subComponent
LoopDawg52017192017-07-14 15:15:47 -06005983 const std::vector<std::string>& resourceInfo = intermediate.getResourceSetBinding();
steve-lunarg9088be42016-11-01 10:31:42 -06005984 switch (std::tolower(desc[0])) {
John Kessenich96e9f472016-07-29 14:28:39 -06005985 case 'b':
5986 case 't':
5987 case 'c':
5988 case 's':
steve-lunarg9088be42016-11-01 10:31:42 -06005989 case 'u':
John Kessenich471bfed2017-12-06 08:17:21 -07005990 // if nothing else has set the binding, do so now
5991 // (other mechanisms override this one)
5992 if (!qualifier.hasBinding())
5993 qualifier.layoutBinding = regNumber + subComponent;
LoopDawg52017192017-07-14 15:15:47 -06005994
5995 // This handles per-register layout sets numbers. For the global mode which sets
5996 // every symbol to the same value, see setLinkageLayoutSets().
5997 if ((resourceInfo.size() % 3) == 0) {
5998 // Apply per-symbol resource set and binding.
5999 for (auto it = resourceInfo.cbegin(); it != resourceInfo.cend(); it = it + 3) {
6000 if (strcmp(desc.c_str(), it[0].c_str()) == 0) {
6001 qualifier.layoutSet = atoi(it[1].c_str());
6002 qualifier.layoutBinding = atoi(it[2].c_str()) + subComponent;
6003 break;
6004 }
Hyangran Park36dc8292017-05-02 16:27:29 +09006005 }
6006 }
John Kessenich96e9f472016-07-29 14:28:39 -06006007 break;
6008 default:
6009 warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]);
6010 break;
6011 }
John Kessenichcfd7ce82016-09-05 16:03:12 -06006012
6013 // space
6014 unsigned int setNumber;
baldurk54a28de2016-10-13 19:23:39 +02006015 const auto crackSpace = [&]() -> bool {
John Kessenichcfd7ce82016-09-05 16:03:12 -06006016 const int spaceLen = 5;
6017 if (spaceDesc->size() < spaceLen + 1)
6018 return false;
6019 if (spaceDesc->compare(0, spaceLen, "space") != 0)
6020 return false;
6021 if (! isdigit((*spaceDesc)[spaceLen]))
6022 return false;
6023 setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str());
6024 return true;
6025 };
6026
John Kessenich471bfed2017-12-06 08:17:21 -07006027 // if nothing else has set the set, do so now
6028 // (other mechanisms override this one)
6029 if (spaceDesc && !qualifier.hasSet()) {
John Kessenichcfd7ce82016-09-05 16:03:12 -06006030 if (! crackSpace()) {
6031 error(loc, "expected spaceN", "register", "");
6032 return;
6033 }
6034 qualifier.layoutSet = setNumber;
6035 }
John Kessenich96e9f472016-07-29 14:28:39 -06006036}
6037
John Kessenich7e997e22017-03-30 22:09:30 -06006038// Convert to a scalar boolean, or if not allowed by HLSL semantics,
6039// report an error and return nullptr.
John Kessenich2ceec682017-07-28 16:20:13 -06006040TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition,
6041 bool mustBeScalar)
John Kessenich7e997e22017-03-30 22:09:30 -06006042{
John Kessenich636b62d2017-04-11 19:45:00 -06006043 if (mustBeScalar && !condition->getType().isScalarOrVec1()) {
John Kessenich7e997e22017-03-30 22:09:30 -06006044 error(loc, "requires a scalar", "conditional expression", "");
6045 return nullptr;
6046 }
6047
John Kessenich2ceec682017-07-28 16:20:13 -06006048 return intermediate.addConversion(EOpConstructBool, TType(EbtBool, EvqTemporary, condition->getVectorSize()),
6049 condition);
John Kessenich7e997e22017-03-30 22:09:30 -06006050}
6051
John Kessenich96e9f472016-07-29 14:28:39 -06006052//
John Kesseniche01a9bc2016-03-12 20:11:22 -07006053// Same error message for all places assignments don't work.
6054//
6055void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right)
6056{
6057 error(loc, "", op, "cannot convert from '%s' to '%s'",
6058 right.c_str(), left.c_str());
6059}
6060
6061//
6062// Same error message for all places unary operations don't work.
6063//
6064void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand)
6065{
6066 error(loc, " wrong operand type", op,
6067 "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)",
6068 op, operand.c_str());
6069}
6070
6071//
6072// Same error message for all binary operations don't work.
6073//
6074void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right)
6075{
6076 error(loc, " wrong operand types:", op,
6077 "no operation '%s' exists that takes a left-hand operand of type '%s' and "
6078 "a right operand of type '%s' (or there is no acceptable conversion)",
6079 op, left.c_str(), right.c_str());
6080}
6081
6082//
6083// A basic type of EbtVoid is a key that the name string was seen in the source, but
6084// it was not found as a variable in the symbol table. If so, give the error
6085// message and insert a dummy variable in the symbol table to prevent future errors.
6086//
6087void HlslParseContext::variableCheck(TIntermTyped*& nodePtr)
6088{
6089 TIntermSymbol* symbol = nodePtr->getAsSymbolNode();
6090 if (! symbol)
6091 return;
6092
6093 if (symbol->getType().getBasicType() == EbtVoid) {
6094 error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), "");
6095
6096 // Add to symbol table to prevent future error messages on the same name
6097 if (symbol->getName().size() > 0) {
6098 TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat));
6099 symbolTable.insert(*fakeVariable);
6100
6101 // substitute a symbol node for this new variable
6102 nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc());
6103 }
6104 }
6105}
6106
6107//
6108// Both test, and if necessary spit out an error, to see if the node is really
6109// a constant.
6110//
6111void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token)
6112{
6113 if (node->getQualifier().storage != EvqConst)
6114 error(node->getLoc(), "constant expression required", token, "");
6115}
6116
6117//
6118// Both test, and if necessary spit out an error, to see if the node is really
6119// an integer.
6120//
6121void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token)
6122{
6123 if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar())
6124 return;
6125
6126 error(node->getLoc(), "scalar integer expression required", token, "");
6127}
6128
6129//
6130// Both test, and if necessary spit out an error, to see if we are currently
6131// globally scoped.
6132//
6133void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token)
6134{
6135 if (! symbolTable.atGlobalLevel())
6136 error(loc, "not allowed in nested scope", token, "");
6137}
6138
John Kessenich7f349c72016-07-08 22:09:10 -06006139bool HlslParseContext::builtInName(const TString& /*identifier*/)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006140{
6141 return false;
6142}
6143
6144//
6145// Make sure there is enough data and not too many arguments provided to the
6146// constructor to build something of the type of the constructor. Also returns
6147// the type of the constructor.
6148//
6149// Returns true if there was an error in construction.
6150//
John Kessenichf97f2ce2016-11-27 22:51:36 -07006151bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function,
John Kessenich7f349c72016-07-08 22:09:10 -06006152 TOperator op, TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006153{
6154 type.shallowCopy(function.getType());
6155
6156 bool constructingMatrix = false;
6157 switch (op) {
6158 case EOpConstructTextureSampler:
6159 return constructorTextureSamplerError(loc, function);
6160 case EOpConstructMat2x2:
6161 case EOpConstructMat2x3:
6162 case EOpConstructMat2x4:
6163 case EOpConstructMat3x2:
6164 case EOpConstructMat3x3:
6165 case EOpConstructMat3x4:
6166 case EOpConstructMat4x2:
6167 case EOpConstructMat4x3:
6168 case EOpConstructMat4x4:
6169 case EOpConstructDMat2x2:
6170 case EOpConstructDMat2x3:
6171 case EOpConstructDMat2x4:
6172 case EOpConstructDMat3x2:
6173 case EOpConstructDMat3x3:
6174 case EOpConstructDMat3x4:
6175 case EOpConstructDMat4x2:
6176 case EOpConstructDMat4x3:
6177 case EOpConstructDMat4x4:
LoopDawg174ccb82017-05-20 21:40:27 -06006178 case EOpConstructIMat2x2:
6179 case EOpConstructIMat2x3:
6180 case EOpConstructIMat2x4:
6181 case EOpConstructIMat3x2:
6182 case EOpConstructIMat3x3:
6183 case EOpConstructIMat3x4:
6184 case EOpConstructIMat4x2:
6185 case EOpConstructIMat4x3:
6186 case EOpConstructIMat4x4:
6187 case EOpConstructUMat2x2:
6188 case EOpConstructUMat2x3:
6189 case EOpConstructUMat2x4:
6190 case EOpConstructUMat3x2:
6191 case EOpConstructUMat3x3:
6192 case EOpConstructUMat3x4:
6193 case EOpConstructUMat4x2:
6194 case EOpConstructUMat4x3:
6195 case EOpConstructUMat4x4:
6196 case EOpConstructBMat2x2:
6197 case EOpConstructBMat2x3:
6198 case EOpConstructBMat2x4:
6199 case EOpConstructBMat3x2:
6200 case EOpConstructBMat3x3:
6201 case EOpConstructBMat3x4:
6202 case EOpConstructBMat4x2:
6203 case EOpConstructBMat4x3:
6204 case EOpConstructBMat4x4:
John Kesseniche01a9bc2016-03-12 20:11:22 -07006205 constructingMatrix = true;
6206 break;
6207 default:
6208 break;
6209 }
6210
6211 //
6212 // Walk the arguments for first-pass checks and collection of information.
6213 //
6214
6215 int size = 0;
6216 bool constType = true;
6217 bool full = false;
6218 bool overFull = false;
6219 bool matrixInMatrix = false;
6220 bool arrayArg = false;
6221 for (int arg = 0; arg < function.getParamCount(); ++arg) {
6222 if (function[arg].type->isArray()) {
6223 if (! function[arg].type->isExplicitlySizedArray()) {
6224 // Can't construct from an unsized array.
6225 error(loc, "array argument must be sized", "constructor", "");
6226 return true;
6227 }
6228 arrayArg = true;
6229 }
6230 if (constructingMatrix && function[arg].type->isMatrix())
6231 matrixInMatrix = true;
6232
6233 // 'full' will go to true when enough args have been seen. If we loop
6234 // again, there is an extra argument.
6235 if (full) {
6236 // For vectors and matrices, it's okay to have too many components
6237 // available, but not okay to have unused arguments.
6238 overFull = true;
6239 }
6240
6241 size += function[arg].type->computeNumComponents();
6242 if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
6243 full = true;
6244
6245 if (function[arg].type->getQualifier().storage != EvqConst)
6246 constType = false;
6247 }
6248
6249 if (constType)
6250 type.getQualifier().storage = EvqConst;
6251
6252 if (type.isArray()) {
6253 if (function.getParamCount() == 0) {
6254 error(loc, "array constructor must have at least one argument", "constructor", "");
6255 return true;
6256 }
6257
6258 if (type.isImplicitlySizedArray()) {
6259 // auto adapt the constructor type to the number of arguments
6260 type.changeOuterArraySize(function.getParamCount());
John Kessenich82ae8c32017-06-13 23:13:10 -06006261 } else if (type.getOuterArraySize() != function.getParamCount() &&
6262 type.computeNumComponents() > size) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006263 error(loc, "array constructor needs one argument per array element", "constructor", "");
6264 return true;
6265 }
6266
6267 if (type.isArrayOfArrays()) {
6268 // Types have to match, but we're still making the type.
6269 // Finish making the type, and the comparison is done later
6270 // when checking for conversion.
6271 TArraySizes& arraySizes = type.getArraySizes();
6272
6273 // At least the dimensionalities have to match.
John Kessenich2ceec682017-07-28 16:20:13 -06006274 if (! function[0].type->isArray() ||
6275 arraySizes.getNumDims() != function[0].type->getArraySizes().getNumDims() + 1) {
John Kessenich509c4212016-11-27 17:26:21 -07006276 error(loc, "array constructor argument not correct type to construct array element", "constructor", "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006277 return true;
6278 }
6279
6280 if (arraySizes.isInnerImplicit()) {
6281 // "Arrays of arrays ..., and the size for any dimension is optional"
6282 // That means we need to adopt (from the first argument) the other array sizes into the type.
6283 for (int d = 1; d < arraySizes.getNumDims(); ++d) {
6284 if (arraySizes.getDimSize(d) == UnsizedArraySize) {
6285 arraySizes.setDimSize(d, function[0].type->getArraySizes().getDimSize(d - 1));
6286 }
6287 }
6288 }
6289 }
6290 }
6291
John Kessenich82ae8c32017-06-13 23:13:10 -06006292 // Some array -> array type casts are okay
6293 if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() &&
6294 !type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() &&
6295 type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1)
6296 return false;
6297
John Kesseniche01a9bc2016-03-12 20:11:22 -07006298 if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
6299 error(loc, "constructing non-array constituent from array argument", "constructor", "");
6300 return true;
6301 }
6302
6303 if (matrixInMatrix && ! type.isArray()) {
6304 return false;
6305 }
6306
6307 if (overFull) {
6308 error(loc, "too many arguments", "constructor", "");
6309 return true;
6310 }
6311
John Kessenich82460b52017-04-04 11:47:42 -06006312 if (op == EOpConstructStruct && ! type.isArray() && isScalarConstructor(node))
John Kessenichf97f2ce2016-11-27 22:51:36 -07006313 return false;
6314
John Kesseniche01a9bc2016-03-12 20:11:22 -07006315 if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) {
6316 error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", "");
6317 return true;
6318 }
6319
6320 if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) ||
6321 (op == EOpConstructStruct && size < type.computeNumComponents())) {
6322 error(loc, "not enough data provided for construction", "constructor", "");
6323 return true;
6324 }
6325
John Kesseniche01a9bc2016-03-12 20:11:22 -07006326 return false;
6327}
6328
John Kessenich82460b52017-04-04 11:47:42 -06006329// See if 'node', in the context of constructing aggregates, is a scalar argument
6330// to a constructor.
6331//
6332bool HlslParseContext::isScalarConstructor(const TIntermNode* node)
John Kessenichf97f2ce2016-11-27 22:51:36 -07006333{
John Kessenich82460b52017-04-04 11:47:42 -06006334 // Obviously, it must be a scalar, but an aggregate node might not be fully
6335 // completed yet: holding a sequence of initializers under an aggregate
6336 // would not yet be typed, so don't check it's type. This corresponds to
6337 // the aggregate operator also not being set yet. (An aggregate operation
6338 // that legitimately yields a scalar will have a getOp() of that operator,
6339 // not EOpNull.)
6340
6341 return node->getAsTyped() != nullptr &&
6342 node->getAsTyped()->isScalar() &&
6343 (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull);
John Kessenichf97f2ce2016-11-27 22:51:36 -07006344}
6345
John Kesseniche01a9bc2016-03-12 20:11:22 -07006346// Verify all the correct semantics for constructing a combined texture/sampler.
6347// Return true if the semantics are incorrect.
6348bool HlslParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function)
6349{
6350 TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change
6351 const char* token = constructorName.c_str();
6352
6353 // exactly two arguments needed
6354 if (function.getParamCount() != 2) {
6355 error(loc, "sampler-constructor requires two arguments", token, "");
6356 return true;
6357 }
6358
6359 // For now, not allowing arrayed constructors, the rest of this function
6360 // is set up to allow them, if this test is removed:
6361 if (function.getType().isArray()) {
6362 error(loc, "sampler-constructor cannot make an array of samplers", token, "");
6363 return true;
6364 }
6365
6366 // first argument
6367 // * the constructor's first argument must be a texture type
6368 // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array)
6369 // of the texture type must match that of the constructed sampler type
6370 // (that is, the suffixes of the type of the first argument and the
6371 // type of the constructor will be spelled the same way)
6372 if (function[0].type->getBasicType() != EbtSampler ||
6373 ! function[0].type->getSampler().isTexture() ||
6374 function[0].type->isArray()) {
6375 error(loc, "sampler-constructor first argument must be a scalar textureXXX type", token, "");
6376 return true;
6377 }
6378 // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=()
6379 TSampler texture = function.getType().getSampler();
6380 texture.combined = false;
6381 texture.shadow = false;
6382 if (texture != function[0].type->getSampler()) {
6383 error(loc, "sampler-constructor first argument must match type and dimensionality of constructor type", token, "");
6384 return true;
6385 }
6386
6387 // second argument
6388 // * the constructor's second argument must be a scalar of type
6389 // *sampler* or *samplerShadow*
6390 // * the presence or absence of depth comparison (Shadow) must match
6391 // between the constructed sampler type and the type of the second argument
6392 if (function[1].type->getBasicType() != EbtSampler ||
6393 ! function[1].type->getSampler().isPureSampler() ||
6394 function[1].type->isArray()) {
6395 error(loc, "sampler-constructor second argument must be a scalar type 'sampler'", token, "");
6396 return true;
6397 }
6398 if (function.getType().getSampler().shadow != function[1].type->getSampler().shadow) {
John Kessenich2ceec682017-07-28 16:20:13 -06006399 error(loc, "sampler-constructor second argument presence of shadow must match constructor presence of shadow",
6400 token, "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006401 return true;
6402 }
6403
6404 return false;
6405}
6406
6407// Checks to see if a void variable has been declared and raise an error message for such a case
6408//
6409// returns true in case of an error
6410//
6411bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType)
6412{
6413 if (basicType == EbtVoid) {
6414 error(loc, "illegal use of type 'void'", identifier.c_str(), "");
6415 return true;
6416 }
6417
6418 return false;
6419}
6420
John Kesseniche01a9bc2016-03-12 20:11:22 -07006421//
6422// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
6423//
John Kessenich7f349c72016-07-08 22:09:10 -06006424void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006425{
6426 // move from parameter/unknown qualifiers to pipeline in/out qualifiers
6427 switch (qualifier.storage) {
6428 case EvqIn:
6429 qualifier.storage = EvqVaryingIn;
6430 break;
6431 case EvqOut:
6432 qualifier.storage = EvqVaryingOut;
6433 break;
6434 default:
6435 break;
6436 }
6437}
6438
6439//
6440// Merge characteristics of the 'src' qualifier into the 'dst'.
6441// If there is duplication, issue error messages, unless 'force'
6442// is specified, which means to just override default settings.
6443//
6444// Also, when force is false, it will be assumed that 'src' follows
6445// 'dst', for the purpose of error checking order for versions
6446// that require specific orderings of qualifiers.
6447//
John Kessenich34e7ee72016-09-16 17:10:39 -06006448void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006449{
6450 // Storage qualification
6451 if (dst.storage == EvqTemporary || dst.storage == EvqGlobal)
6452 dst.storage = src.storage;
6453 else if ((dst.storage == EvqIn && src.storage == EvqOut) ||
John Kessenich3d157c52016-07-25 16:05:33 -06006454 (dst.storage == EvqOut && src.storage == EvqIn))
John Kesseniche01a9bc2016-03-12 20:11:22 -07006455 dst.storage = EvqInOut;
6456 else if ((dst.storage == EvqIn && src.storage == EvqConst) ||
John Kessenich3d157c52016-07-25 16:05:33 -06006457 (dst.storage == EvqConst && src.storage == EvqIn))
John Kesseniche01a9bc2016-03-12 20:11:22 -07006458 dst.storage = EvqConstReadOnly;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006459
John Kesseniche01a9bc2016-03-12 20:11:22 -07006460 // Layout qualifiers
6461 mergeObjectLayoutQualifiers(dst, src, false);
6462
6463 // individual qualifiers
6464 bool repeated = false;
6465#define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field;
6466 MERGE_SINGLETON(invariant);
John Kessenich17f07862016-05-04 12:36:14 -06006467 MERGE_SINGLETON(noContraction);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006468 MERGE_SINGLETON(centroid);
6469 MERGE_SINGLETON(smooth);
6470 MERGE_SINGLETON(flat);
6471 MERGE_SINGLETON(nopersp);
6472 MERGE_SINGLETON(patch);
6473 MERGE_SINGLETON(sample);
6474 MERGE_SINGLETON(coherent);
6475 MERGE_SINGLETON(volatil);
6476 MERGE_SINGLETON(restrict);
6477 MERGE_SINGLETON(readonly);
6478 MERGE_SINGLETON(writeonly);
6479 MERGE_SINGLETON(specConstant);
6480}
6481
6482// used to flatten the sampler type space into a single dimension
6483// correlates with the declaration of defaultSamplerPrecision[]
6484int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler)
6485{
6486 int arrayIndex = sampler.arrayed ? 1 : 0;
6487 int shadowIndex = sampler.shadow ? 1 : 0;
6488 int externalIndex = sampler.external ? 1 : 0;
6489
John Kessenich2ceec682017-07-28 16:20:13 -06006490 return EsdNumDims *
6491 (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006492}
6493
6494//
6495// Do size checking for an array type's size.
6496//
6497void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair)
6498{
6499 bool isConst = false;
6500 sizePair.size = 1;
6501 sizePair.node = nullptr;
6502
6503 TIntermConstantUnion* constant = expr->getAsConstantUnion();
6504 if (constant) {
6505 // handle true (non-specialization) constant
6506 sizePair.size = constant->getConstArray()[0].getIConst();
6507 isConst = true;
6508 } else {
6509 // see if it's a specialization constant instead
6510 if (expr->getQualifier().isSpecConstant()) {
6511 isConst = true;
6512 sizePair.node = expr;
6513 TIntermSymbol* symbol = expr->getAsSymbolNode();
6514 if (symbol && symbol->getConstArray().size() > 0)
6515 sizePair.size = symbol->getConstArray()[0].getIConst();
6516 }
6517 }
6518
6519 if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
6520 error(loc, "array size must be a constant integer expression", "", "");
6521 return;
6522 }
6523
6524 if (sizePair.size <= 0) {
6525 error(loc, "array size must be a positive integer", "", "");
6526 return;
6527 }
6528}
6529
6530//
6531// Require array to be completely sized
6532//
6533void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes)
6534{
6535 if (arraySizes.isImplicit())
6536 error(loc, "array size required", "", "");
6537}
6538
6539void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type)
6540{
6541 const TTypeList& structure = *type.getStruct();
6542 for (int m = 0; m < (int)structure.size(); ++m) {
6543 const TType& member = *structure[m].type;
6544 if (member.isArray())
6545 arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes());
6546 }
6547}
6548
6549// Merge array dimensions listed in 'sizes' onto the type's array dimensions.
6550//
6551// From the spec: "vec4[2] a[3]; // size-3 array of size-2 array of vec4"
6552//
6553// That means, the 'sizes' go in front of the 'type' as outermost sizes.
6554// 'type' is the type part of the declaration (to the left)
6555// 'sizes' is the arrayness tagged on the identifier (to the right)
6556//
6557void HlslParseContext::arrayDimMerge(TType& type, const TArraySizes* sizes)
6558{
6559 if (sizes)
6560 type.addArrayOuterSizes(*sizes);
6561}
6562
6563//
6564// Do all the semantic checking for declaring or redeclaring an array, with and
6565// without a size, and make the right changes to the symbol table.
6566//
John Kessenich2ceec682017-07-28 16:20:13 -06006567void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
6568 TSymbol*& symbol, bool track)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006569{
John Kessenichf02c8e62017-06-19 16:25:44 -06006570 if (symbol == nullptr) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006571 bool currentScope;
6572 symbol = symbolTable.find(identifier, nullptr, &currentScope);
6573
6574 if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) {
6575 // bad shader (errors already reported) trying to redeclare a built-in name as an array
6576 return;
6577 }
6578 if (symbol == nullptr || ! currentScope) {
6579 //
6580 // Successfully process a new definition.
6581 // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations)
6582 //
6583 symbol = new TVariable(&identifier, type);
6584 symbolTable.insert(*symbol);
John Kessenichd3f11222016-11-05 10:15:53 -06006585 if (track && symbolTable.atGlobalLevel())
John Kessenich02467d82017-01-19 15:41:47 -07006586 trackLinkage(*symbol);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006587
John Kesseniche01a9bc2016-03-12 20:11:22 -07006588 return;
6589 }
6590 if (symbol->getAsAnonMember()) {
6591 error(loc, "cannot redeclare a user-block member array", identifier.c_str(), "");
6592 symbol = nullptr;
6593 return;
6594 }
6595 }
6596
6597 //
6598 // Process a redeclaration.
6599 //
6600
John Kessenichf02c8e62017-06-19 16:25:44 -06006601 if (symbol == nullptr) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006602 error(loc, "array variable name expected", identifier.c_str(), "");
6603 return;
6604 }
6605
6606 // redeclareBuiltinVariable() should have already done the copyUp()
6607 TType& existingType = symbol->getWritableType();
6608
John Kesseniche01a9bc2016-03-12 20:11:22 -07006609 if (existingType.isExplicitlySizedArray()) {
John Kessenich2ceec682017-07-28 16:20:13 -06006610 // be more lenient for input arrays to geometry shaders and tessellation control outputs,
6611 // where the redeclaration is the same size
John Kesseniche01a9bc2016-03-12 20:11:22 -07006612 return;
6613 }
6614
6615 existingType.updateArraySizes(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006616}
6617
6618void HlslParseContext::updateImplicitArraySize(const TSourceLoc& loc, TIntermNode *node, int index)
6619{
6620 // maybe there is nothing to do...
6621 TIntermTyped* typedNode = node->getAsTyped();
6622 if (typedNode->getType().getImplicitArraySize() > index)
6623 return;
6624
6625 // something to do...
6626
6627 // Figure out what symbol to lookup, as we will use its type to edit for the size change,
6628 // as that type will be shared through shallow copies for future references.
6629 TSymbol* symbol = nullptr;
6630 int blockIndex = -1;
6631 const TString* lookupName = nullptr;
6632 if (node->getAsSymbolNode())
6633 lookupName = &node->getAsSymbolNode()->getName();
6634 else if (node->getAsBinaryNode()) {
6635 const TIntermBinary* deref = node->getAsBinaryNode();
6636 // This has to be the result of a block dereference, unless it's bad shader code
6637 // If it's a uniform block, then an error will be issued elsewhere, but
6638 // return early now to avoid crashing later in this function.
6639 if (! deref->getLeft()->getAsSymbolNode() || deref->getLeft()->getBasicType() != EbtBlock ||
6640 deref->getLeft()->getType().getQualifier().storage == EvqUniform ||
6641 deref->getRight()->getAsConstantUnion() == nullptr)
6642 return;
6643
6644 blockIndex = deref->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
6645
6646 lookupName = &deref->getLeft()->getAsSymbolNode()->getName();
6647 if (IsAnonymous(*lookupName))
6648 lookupName = &(*deref->getLeft()->getType().getStruct())[blockIndex].type->getFieldName();
6649 }
6650
6651 // Lookup the symbol, should only fail if shader code is incorrect
6652 symbol = symbolTable.find(*lookupName);
6653 if (symbol == nullptr)
6654 return;
6655
6656 if (symbol->getAsFunction()) {
6657 error(loc, "array variable name expected", symbol->getName().c_str(), "");
6658 return;
6659 }
6660
6661 symbol->getWritableType().setImplicitArraySize(index + 1);
6662}
6663
6664//
John Kessenichaa6d5622016-12-30 16:42:57 -07006665// Enforce non-initializer type/qualifier rules.
6666//
John Kessenich2ceec682017-07-28 16:20:13 -06006667void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type,
6668 TIntermTyped*& initializer)
John Kessenichaa6d5622016-12-30 16:42:57 -07006669{
6670 //
6671 // Make the qualifier make sense, given that there is an initializer.
6672 //
6673 if (initializer == nullptr) {
6674 if (type.getQualifier().storage == EvqConst ||
6675 type.getQualifier().storage == EvqConstReadOnly) {
6676 initializer = intermediate.makeAggregate(loc);
6677 warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), "");
6678 }
6679 }
6680}
6681
6682//
John Kesseniche01a9bc2016-03-12 20:11:22 -07006683// See if the identifier is a built-in symbol that can be redeclared, and if so,
6684// copy the symbol table's read-only built-in variable to the current
6685// global level, where it can be modified based on the passed in type.
6686//
6687// Returns nullptr if no redeclaration took place; meaning a normal declaration still
6688// needs to occur for it, not necessarily an error.
6689//
6690// Returns a redeclared and type-modified variable if a redeclared occurred.
6691//
John Kessenich7f349c72016-07-08 22:09:10 -06006692TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier,
6693 const TQualifier& /*qualifier*/,
John Kessenichd3f11222016-11-05 10:15:53 -06006694 const TShaderQualifiers& /*publicType*/)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006695{
6696 if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel())
6697 return nullptr;
6698
6699 return nullptr;
6700}
6701
6702//
steve-lunargdd8287a2017-02-23 18:04:12 -07006703// Generate index to the array element in a structure buffer (SSBO)
6704//
6705TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const
6706{
6707 // Bail out if not a struct buffer
6708 if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
6709 return nullptr;
6710
6711 // Runtime sized array is always the last element.
6712 const TTypeList* bufferStruct = buffer->getType().getStruct();
6713 TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc);
6714
6715 TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc);
6716 argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type);
6717
6718 return argArray;
6719}
6720
6721//
6722// IFF type is a structuredbuffer/byteaddressbuffer type, return the content
6723// (template) type. E.g, StructuredBuffer<MyType> -> MyType. Else return nullptr.
6724//
6725TType* HlslParseContext::getStructBufferContentType(const TType& type) const
6726{
6727 if (type.getBasicType() != EbtBlock)
6728 return nullptr;
6729
Daniel Kochd9b7a852017-03-03 10:34:07 -05006730 const int memberCount = (int)type.getStruct()->size();
steve-lunargdd8287a2017-02-23 18:04:12 -07006731 assert(memberCount > 0);
6732
6733 TType* contentType = (*type.getStruct())[memberCount-1].type;
6734
6735 return contentType->isRuntimeSizedArray() ? contentType : nullptr;
6736}
6737
6738//
6739// If an existing struct buffer has a sharable type, then share it.
6740//
6741void HlslParseContext::shareStructBufferType(TType& type)
6742{
6743 // PackOffset must be equivalent to share types on a per-member basis.
6744 // Note: cannot use auto type due to recursion. Thus, this is a std::function.
6745 const std::function<bool(TType& lhs, TType& rhs)>
6746 compareQualifiers = [&](TType& lhs, TType& rhs) -> bool {
6747 if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset)
6748 return false;
6749
6750 if (lhs.isStruct() != rhs.isStruct())
6751 return false;
6752
6753 if (lhs.isStruct() && rhs.isStruct()) {
6754 if (lhs.getStruct()->size() != rhs.getStruct()->size())
6755 return false;
6756
6757 for (int i = 0; i < int(lhs.getStruct()->size()); ++i)
6758 if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type))
6759 return false;
6760 }
6761
6762 return true;
6763 };
6764
6765 // We need to compare certain qualifiers in addition to the type.
6766 const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool {
6767 if (lhs.getQualifier().readonly != rhs.getQualifier().readonly)
6768 return false;
6769
6770 // If both are structures, recursively look for packOffset equality
6771 // as well as type equality.
6772 return compareQualifiers(lhs, rhs) && lhs == rhs;
6773 };
6774
steve-lunargdd8287a2017-02-23 18:04:12 -07006775 // This is an exhaustive O(N) search, but real world shaders have
6776 // only a small number of these.
6777 for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) {
6778 // If the deep structure matches, modulo qualifiers, use it
6779 if (typeEqual(*structBufferTypes[idx], type)) {
6780 type.shallowCopy(*structBufferTypes[idx]);
6781 return;
6782 }
6783 }
6784
6785 // Otherwise, remember it:
6786 TType* typeCopy = new TType;
6787 typeCopy->shallowCopy(type);
6788 structBufferTypes.push_back(typeCopy);
steve-lunargdd8287a2017-02-23 18:04:12 -07006789}
6790
John Kessenich5aa59e22016-06-17 15:50:47 -06006791void HlslParseContext::paramFix(TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006792{
John Kessenich5aa59e22016-06-17 15:50:47 -06006793 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006794 case EvqConst:
John Kesseniche01a9bc2016-03-12 20:11:22 -07006795 type.getQualifier().storage = EvqConstReadOnly;
6796 break;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006797 case EvqGlobal:
John Kessenich3d1b7092017-08-23 14:33:31 -06006798 case EvqUniform:
John Kesseniche01a9bc2016-03-12 20:11:22 -07006799 case EvqTemporary:
6800 type.getQualifier().storage = EvqIn;
6801 break;
steve-lunargdd8287a2017-02-23 18:04:12 -07006802 case EvqBuffer:
6803 {
6804 // SSBO parameter. These do not go through the declareBlock path since they are fn parameters.
6805 correctUniform(type.getQualifier());
6806 TQualifier bufferQualifier = globalBufferDefaults;
6807 mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true);
6808 bufferQualifier.storage = type.getQualifier().storage;
6809 bufferQualifier.readonly = type.getQualifier().readonly;
6810 bufferQualifier.coherent = type.getQualifier().coherent;
steve-lunarg2bb1f392017-04-27 11:22:32 -06006811 bufferQualifier.declaredBuiltIn = type.getQualifier().declaredBuiltIn;
steve-lunargdd8287a2017-02-23 18:04:12 -07006812 type.getQualifier() = bufferQualifier;
6813 break;
6814 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07006815 default:
John Kesseniche01a9bc2016-03-12 20:11:22 -07006816 break;
6817 }
6818}
6819
John Kesseniche01a9bc2016-03-12 20:11:22 -07006820void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
6821{
6822 if (type.containsSpecializationSize())
6823 error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
6824}
6825
6826//
6827// Layout qualifier stuff.
6828//
6829
6830// Put the id's layout qualification into the public type, for qualifiers not having a number set.
6831// This is before we know any type information for error checking.
John Kessenichb9e39122016-08-17 10:22:08 -06006832void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006833{
6834 std::transform(id.begin(), id.end(), id.begin(), ::tolower);
6835
6836 if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
John Kessenich10f7fc72016-09-25 20:25:06 -06006837 qualifier.layoutMatrix = ElmRowMajor;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006838 return;
6839 }
6840 if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
John Kessenich10f7fc72016-09-25 20:25:06 -06006841 qualifier.layoutMatrix = ElmColumnMajor;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006842 return;
6843 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07006844 if (id == "push_constant") {
6845 requireVulkan(loc, "push_constant");
John Kessenichb9e39122016-08-17 10:22:08 -06006846 qualifier.layoutPushConstant = true;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006847 return;
6848 }
6849 if (language == EShLangGeometry || language == EShLangTessEvaluation) {
6850 if (id == TQualifier::getGeometryString(ElgTriangles)) {
John Kessenich927608b2017-01-06 12:34:14 -07006851 // publicType.shaderQualifiers.geometry = ElgTriangles;
John Kessenichb9e39122016-08-17 10:22:08 -06006852 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006853 return;
6854 }
6855 if (language == EShLangGeometry) {
6856 if (id == TQualifier::getGeometryString(ElgPoints)) {
John Kessenich927608b2017-01-06 12:34:14 -07006857 // publicType.shaderQualifiers.geometry = ElgPoints;
John Kessenichb9e39122016-08-17 10:22:08 -06006858 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006859 return;
6860 }
6861 if (id == TQualifier::getGeometryString(ElgLineStrip)) {
John Kessenich927608b2017-01-06 12:34:14 -07006862 // publicType.shaderQualifiers.geometry = ElgLineStrip;
John Kessenichb9e39122016-08-17 10:22:08 -06006863 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006864 return;
6865 }
6866 if (id == TQualifier::getGeometryString(ElgLines)) {
John Kessenich927608b2017-01-06 12:34:14 -07006867 // publicType.shaderQualifiers.geometry = ElgLines;
John Kessenichb9e39122016-08-17 10:22:08 -06006868 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006869 return;
6870 }
6871 if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
John Kessenich927608b2017-01-06 12:34:14 -07006872 // publicType.shaderQualifiers.geometry = ElgLinesAdjacency;
John Kessenichb9e39122016-08-17 10:22:08 -06006873 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006874 return;
6875 }
6876 if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
John Kessenich927608b2017-01-06 12:34:14 -07006877 // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency;
John Kessenichb9e39122016-08-17 10:22:08 -06006878 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006879 return;
6880 }
6881 if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
John Kessenich927608b2017-01-06 12:34:14 -07006882 // publicType.shaderQualifiers.geometry = ElgTriangleStrip;
John Kessenichb9e39122016-08-17 10:22:08 -06006883 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006884 return;
6885 }
6886 } else {
6887 assert(language == EShLangTessEvaluation);
6888
6889 // input primitive
6890 if (id == TQualifier::getGeometryString(ElgTriangles)) {
John Kessenich927608b2017-01-06 12:34:14 -07006891 // publicType.shaderQualifiers.geometry = ElgTriangles;
John Kessenichb9e39122016-08-17 10:22:08 -06006892 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006893 return;
6894 }
6895 if (id == TQualifier::getGeometryString(ElgQuads)) {
John Kessenich927608b2017-01-06 12:34:14 -07006896 // publicType.shaderQualifiers.geometry = ElgQuads;
John Kessenichb9e39122016-08-17 10:22:08 -06006897 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006898 return;
6899 }
6900 if (id == TQualifier::getGeometryString(ElgIsolines)) {
John Kessenich927608b2017-01-06 12:34:14 -07006901 // publicType.shaderQualifiers.geometry = ElgIsolines;
John Kessenichb9e39122016-08-17 10:22:08 -06006902 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006903 return;
6904 }
6905
6906 // vertex spacing
6907 if (id == TQualifier::getVertexSpacingString(EvsEqual)) {
John Kessenich927608b2017-01-06 12:34:14 -07006908 // publicType.shaderQualifiers.spacing = EvsEqual;
John Kessenichb9e39122016-08-17 10:22:08 -06006909 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006910 return;
6911 }
6912 if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) {
John Kessenich927608b2017-01-06 12:34:14 -07006913 // publicType.shaderQualifiers.spacing = EvsFractionalEven;
John Kessenichb9e39122016-08-17 10:22:08 -06006914 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006915 return;
6916 }
6917 if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) {
John Kessenich927608b2017-01-06 12:34:14 -07006918 // publicType.shaderQualifiers.spacing = EvsFractionalOdd;
John Kessenichb9e39122016-08-17 10:22:08 -06006919 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006920 return;
6921 }
6922
6923 // triangle order
6924 if (id == TQualifier::getVertexOrderString(EvoCw)) {
John Kessenich927608b2017-01-06 12:34:14 -07006925 // publicType.shaderQualifiers.order = EvoCw;
John Kessenichb9e39122016-08-17 10:22:08 -06006926 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006927 return;
6928 }
6929 if (id == TQualifier::getVertexOrderString(EvoCcw)) {
John Kessenich927608b2017-01-06 12:34:14 -07006930 // publicType.shaderQualifiers.order = EvoCcw;
John Kessenichb9e39122016-08-17 10:22:08 -06006931 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006932 return;
6933 }
6934
6935 // point mode
6936 if (id == "point_mode") {
John Kessenich927608b2017-01-06 12:34:14 -07006937 // publicType.shaderQualifiers.pointMode = true;
John Kessenichb9e39122016-08-17 10:22:08 -06006938 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006939 return;
6940 }
6941 }
6942 }
6943 if (language == EShLangFragment) {
6944 if (id == "origin_upper_left") {
John Kessenich927608b2017-01-06 12:34:14 -07006945 // publicType.shaderQualifiers.originUpperLeft = true;
John Kessenichb9e39122016-08-17 10:22:08 -06006946 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006947 return;
6948 }
6949 if (id == "pixel_center_integer") {
John Kessenich927608b2017-01-06 12:34:14 -07006950 // publicType.shaderQualifiers.pixelCenterInteger = true;
John Kessenichb9e39122016-08-17 10:22:08 -06006951 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006952 return;
6953 }
6954 if (id == "early_fragment_tests") {
John Kessenich927608b2017-01-06 12:34:14 -07006955 // publicType.shaderQualifiers.earlyFragmentTests = true;
John Kessenichb9e39122016-08-17 10:22:08 -06006956 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006957 return;
6958 }
6959 for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) {
6960 if (id == TQualifier::getLayoutDepthString(depth)) {
John Kessenich927608b2017-01-06 12:34:14 -07006961 // publicType.shaderQualifiers.layoutDepth = depth;
John Kessenichb9e39122016-08-17 10:22:08 -06006962 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006963 return;
6964 }
6965 }
6966 if (id.compare(0, 13, "blend_support") == 0) {
6967 bool found = false;
6968 for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
6969 if (id == TQualifier::getBlendEquationString(be)) {
6970 requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation");
6971 intermediate.addBlendEquation(be);
John Kessenich927608b2017-01-06 12:34:14 -07006972 // publicType.shaderQualifiers.blendEquation = true;
John Kessenichb9e39122016-08-17 10:22:08 -06006973 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006974 found = true;
6975 break;
6976 }
6977 }
6978 if (! found)
6979 error(loc, "unknown blend equation", "blend_support", "");
6980 return;
6981 }
6982 }
6983 error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
6984}
6985
6986// Put the id's layout qualifier value into the public type, for qualifiers having a number set.
6987// This is before we know any type information for error checking.
John Kessenich2ceec682017-07-28 16:20:13 -06006988void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id,
6989 const TIntermTyped* node)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006990{
6991 const char* feature = "layout-id value";
John Kessenich927608b2017-01-06 12:34:14 -07006992 // const char* nonLiteralFeature = "non-literal layout-id value";
John Kesseniche01a9bc2016-03-12 20:11:22 -07006993
6994 integerCheck(node, feature);
6995 const TIntermConstantUnion* constUnion = node->getAsConstantUnion();
6996 int value = 0;
6997 if (constUnion) {
6998 value = constUnion->getConstArray()[0].getIConst();
6999 }
7000
7001 std::transform(id.begin(), id.end(), id.begin(), ::tolower);
7002
7003 if (id == "offset") {
John Kessenichb9e39122016-08-17 10:22:08 -06007004 qualifier.layoutOffset = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007005 return;
7006 } else if (id == "align") {
7007 // "The specified alignment must be a power of 2, or a compile-time error results."
7008 if (! IsPow2(value))
7009 error(loc, "must be a power of 2", "align", "");
7010 else
John Kessenichb9e39122016-08-17 10:22:08 -06007011 qualifier.layoutAlign = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007012 return;
7013 } else if (id == "location") {
7014 if ((unsigned int)value >= TQualifier::layoutLocationEnd)
7015 error(loc, "location is too large", id.c_str(), "");
7016 else
John Kessenichb9e39122016-08-17 10:22:08 -06007017 qualifier.layoutLocation = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007018 return;
7019 } else if (id == "set") {
7020 if ((unsigned int)value >= TQualifier::layoutSetEnd)
7021 error(loc, "set is too large", id.c_str(), "");
7022 else
John Kessenichb9e39122016-08-17 10:22:08 -06007023 qualifier.layoutSet = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007024 return;
7025 } else if (id == "binding") {
7026 if ((unsigned int)value >= TQualifier::layoutBindingEnd)
7027 error(loc, "binding is too large", id.c_str(), "");
7028 else
John Kessenichb9e39122016-08-17 10:22:08 -06007029 qualifier.layoutBinding = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007030 return;
7031 } else if (id == "component") {
7032 if ((unsigned)value >= TQualifier::layoutComponentEnd)
7033 error(loc, "component is too large", id.c_str(), "");
7034 else
John Kessenichb9e39122016-08-17 10:22:08 -06007035 qualifier.layoutComponent = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007036 return;
7037 } else if (id.compare(0, 4, "xfb_") == 0) {
John Kessenichecba76f2017-01-06 00:34:48 -07007038 // "Any shader making any static use (after preprocessing) of any of these
7039 // *xfb_* qualifiers will cause the shader to be in a transform feedback
7040 // capturing mode and hence responsible for describing the transform feedback
John Kesseniche01a9bc2016-03-12 20:11:22 -07007041 // setup."
7042 intermediate.setXfbMode();
7043 if (id == "xfb_buffer") {
7044 // "It is a compile-time error to specify an *xfb_buffer* that is greater than
7045 // the implementation-dependent constant gl_MaxTransformFeedbackBuffers."
7046 if (value >= resources.maxTransformFeedbackBuffers)
John Kessenich2ceec682017-07-28 16:20:13 -06007047 error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d",
7048 resources.maxTransformFeedbackBuffers);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007049 if (value >= (int)TQualifier::layoutXfbBufferEnd)
7050 error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1);
7051 else
John Kessenichb9e39122016-08-17 10:22:08 -06007052 qualifier.layoutXfbBuffer = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007053 return;
7054 } else if (id == "xfb_offset") {
7055 if (value >= (int)TQualifier::layoutXfbOffsetEnd)
7056 error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1);
7057 else
John Kessenichb9e39122016-08-17 10:22:08 -06007058 qualifier.layoutXfbOffset = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007059 return;
7060 } else if (id == "xfb_stride") {
John Kessenichecba76f2017-01-06 00:34:48 -07007061 // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the
John Kesseniche01a9bc2016-03-12 20:11:22 -07007062 // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
7063 if (value > 4 * resources.maxTransformFeedbackInterleavedComponents)
John Kessenich2ceec682017-07-28 16:20:13 -06007064 error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d",
7065 resources.maxTransformFeedbackInterleavedComponents);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007066 else if (value >= (int)TQualifier::layoutXfbStrideEnd)
7067 error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1);
7068 if (value < (int)TQualifier::layoutXfbStrideEnd)
John Kessenichb9e39122016-08-17 10:22:08 -06007069 qualifier.layoutXfbStride = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007070 return;
7071 }
7072 }
7073
7074 if (id == "input_attachment_index") {
7075 requireVulkan(loc, "input_attachment_index");
7076 if (value >= (int)TQualifier::layoutAttachmentEnd)
7077 error(loc, "attachment index is too large", id.c_str(), "");
7078 else
John Kessenichb9e39122016-08-17 10:22:08 -06007079 qualifier.layoutAttachment = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007080 return;
7081 }
7082 if (id == "constant_id") {
John Kessenich046bae02017-12-23 17:29:45 -07007083 setSpecConstantId(loc, qualifier, value);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007084 return;
7085 }
7086
7087 switch (language) {
7088 case EShLangVertex:
7089 break;
7090
7091 case EShLangTessControl:
7092 if (id == "vertices") {
7093 if (value == 0)
7094 error(loc, "must be greater than 0", "vertices", "");
7095 else
John Kessenich927608b2017-01-06 12:34:14 -07007096 // publicType.shaderQualifiers.vertices = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007097 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007098 return;
7099 }
7100 break;
7101
7102 case EShLangTessEvaluation:
7103 break;
7104
7105 case EShLangGeometry:
7106 if (id == "invocations") {
7107 if (value == 0)
7108 error(loc, "must be at least 1", "invocations", "");
7109 else
John Kessenich927608b2017-01-06 12:34:14 -07007110 // publicType.shaderQualifiers.invocations = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007111 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007112 return;
7113 }
7114 if (id == "max_vertices") {
John Kessenich927608b2017-01-06 12:34:14 -07007115 // publicType.shaderQualifiers.vertices = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007116 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007117 if (value > resources.maxGeometryOutputVertices)
7118 error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", "");
7119 return;
7120 }
7121 if (id == "stream") {
John Kessenichb9e39122016-08-17 10:22:08 -06007122 qualifier.layoutStream = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007123 return;
7124 }
7125 break;
7126
7127 case EShLangFragment:
7128 if (id == "index") {
John Kessenichb9e39122016-08-17 10:22:08 -06007129 qualifier.layoutIndex = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007130 return;
7131 }
7132 break;
7133
7134 case EShLangCompute:
7135 if (id.compare(0, 11, "local_size_") == 0) {
7136 if (id == "local_size_x") {
John Kessenich927608b2017-01-06 12:34:14 -07007137 // publicType.shaderQualifiers.localSize[0] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007138 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007139 return;
7140 }
7141 if (id == "local_size_y") {
John Kessenich927608b2017-01-06 12:34:14 -07007142 // publicType.shaderQualifiers.localSize[1] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007143 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007144 return;
7145 }
7146 if (id == "local_size_z") {
John Kessenich927608b2017-01-06 12:34:14 -07007147 // publicType.shaderQualifiers.localSize[2] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007148 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007149 return;
7150 }
John Kessenichb901ade2016-06-16 20:59:42 -06007151 if (spvVersion.spv != 0) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07007152 if (id == "local_size_x_id") {
John Kessenich927608b2017-01-06 12:34:14 -07007153 // publicType.shaderQualifiers.localSizeSpecId[0] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007154 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007155 return;
7156 }
7157 if (id == "local_size_y_id") {
John Kessenich927608b2017-01-06 12:34:14 -07007158 // publicType.shaderQualifiers.localSizeSpecId[1] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007159 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007160 return;
7161 }
7162 if (id == "local_size_z_id") {
John Kessenich927608b2017-01-06 12:34:14 -07007163 // publicType.shaderQualifiers.localSizeSpecId[2] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06007164 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007165 return;
7166 }
7167 }
7168 }
7169 break;
7170
7171 default:
7172 break;
7173 }
7174
7175 error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
7176}
7177
John Kessenich046bae02017-12-23 17:29:45 -07007178void HlslParseContext::setSpecConstantId(const TSourceLoc& loc, TQualifier& qualifier, int value)
7179{
7180 if (value >= (int)TQualifier::layoutSpecConstantIdEnd) {
7181 error(loc, "specialization-constant id is too large", "constant_id", "");
7182 } else {
7183 qualifier.layoutSpecConstantId = value;
7184 qualifier.specConstant = true;
7185 if (! intermediate.addUsedConstantId(value))
7186 error(loc, "specialization-constant id already used", "constant_id", "");
7187 }
7188 return;
7189}
7190
John Kesseniche01a9bc2016-03-12 20:11:22 -07007191// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
7192//
7193// "More than one layout qualifier may appear in a single declaration.
John Kessenichecba76f2017-01-06 00:34:48 -07007194// Additionally, the same layout-qualifier-name can occur multiple times
7195// within a layout qualifier or across multiple layout qualifiers in the
7196// same declaration. When the same layout-qualifier-name occurs
7197// multiple times, in a single declaration, the last occurrence overrides
7198// the former occurrence(s). Further, if such a layout-qualifier-name
7199// will effect subsequent declarations or other observable behavior, it
7200// is only the last occurrence that will have any effect, behaving as if
7201// the earlier occurrence(s) within the declaration are not present.
7202// This is also true for overriding layout-qualifier-names, where one
7203// overrides the other (e.g., row_major vs. column_major); only the last
7204// occurrence has any effect."
John Kesseniche01a9bc2016-03-12 20:11:22 -07007205//
7206void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly)
7207{
7208 if (src.hasMatrix())
7209 dst.layoutMatrix = src.layoutMatrix;
7210 if (src.hasPacking())
7211 dst.layoutPacking = src.layoutPacking;
7212
7213 if (src.hasStream())
7214 dst.layoutStream = src.layoutStream;
7215
7216 if (src.hasFormat())
7217 dst.layoutFormat = src.layoutFormat;
7218
7219 if (src.hasXfbBuffer())
7220 dst.layoutXfbBuffer = src.layoutXfbBuffer;
7221
7222 if (src.hasAlign())
7223 dst.layoutAlign = src.layoutAlign;
7224
7225 if (! inheritOnly) {
7226 if (src.hasLocation())
7227 dst.layoutLocation = src.layoutLocation;
7228 if (src.hasComponent())
7229 dst.layoutComponent = src.layoutComponent;
7230 if (src.hasIndex())
7231 dst.layoutIndex = src.layoutIndex;
7232
7233 if (src.hasOffset())
7234 dst.layoutOffset = src.layoutOffset;
7235
7236 if (src.hasSet())
7237 dst.layoutSet = src.layoutSet;
7238 if (src.layoutBinding != TQualifier::layoutBindingEnd)
7239 dst.layoutBinding = src.layoutBinding;
7240
7241 if (src.hasXfbStride())
7242 dst.layoutXfbStride = src.layoutXfbStride;
7243 if (src.hasXfbOffset())
7244 dst.layoutXfbOffset = src.layoutXfbOffset;
7245 if (src.hasAttachment())
7246 dst.layoutAttachment = src.layoutAttachment;
7247 if (src.hasSpecConstantId())
7248 dst.layoutSpecConstantId = src.layoutSpecConstantId;
7249
7250 if (src.layoutPushConstant)
7251 dst.layoutPushConstant = true;
7252 }
7253}
7254
LoopDawg2e629102017-11-22 10:33:34 -07007255
John Kesseniche01a9bc2016-03-12 20:11:22 -07007256//
7257// Look up a function name in the symbol table, and make sure it is a function.
7258//
John Kessenichfcc0aa32016-08-24 18:34:43 -06007259// First, look for an exact match. If there is none, use the generic selector
John Kessenichecba76f2017-01-06 00:34:48 -07007260// TParseContextBase::selectFunction() to find one, parameterized by the
John Kessenichfcc0aa32016-08-24 18:34:43 -06007261// convertible() and better() predicates defined below.
7262//
John Kesseniche01a9bc2016-03-12 20:11:22 -07007263// Return the function symbol if found, otherwise nullptr.
7264//
John Kessenich0a2a0cd2017-05-16 23:16:26 -06007265const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth,
steve-lunarg26d31452016-12-23 18:56:57 -07007266 TIntermTyped*& args)
John Kesseniche01a9bc2016-03-12 20:11:22 -07007267{
John Kesseniche01a9bc2016-03-12 20:11:22 -07007268 if (symbolTable.isFunctionNameVariable(call.getName())) {
7269 error(loc, "can't use function syntax on variable", call.getName().c_str(), "");
7270 return nullptr;
7271 }
7272
7273 // first, look for an exact match
John Kessenich0a2a0cd2017-05-16 23:16:26 -06007274 bool dummyScope;
7275 TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007276 if (symbol)
7277 return symbol->getAsFunction();
7278
John Kessenichfcc0aa32016-08-24 18:34:43 -06007279 // no exact match, use the generic selector, parameterized by the GLSL rules
John Kesseniche01a9bc2016-03-12 20:11:22 -07007280
John Kessenichfcc0aa32016-08-24 18:34:43 -06007281 // create list of candidates to send
John Kessenich0a04b4d2016-08-19 07:27:28 -06007282 TVector<const TFunction*> candidateList;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007283 symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
John Kessenichecba76f2017-01-06 00:34:48 -07007284
John Kessenich0a2a0cd2017-05-16 23:16:26 -06007285 // These built-in ops can accept any type, so we bypass the argument selection
steve-lunargf49cdf42016-11-17 15:04:20 -07007286 if (candidateList.size() == 1 && builtIn &&
7287 (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
steve-lunarg8e26feb2017-04-10 08:19:21 -06007288 candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip ||
7289 candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter ||
steve-lunarg12bc9aa2017-04-13 18:42:58 -06007290 candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter ||
7291 candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
7292 candidateList[0]->getBuiltInOp() == EOpMethodConsume)) {
steve-lunargf49cdf42016-11-17 15:04:20 -07007293 return candidateList[0];
7294 }
7295
steve-lunarg05f75142016-12-06 15:50:11 -07007296 bool allowOnlyUpConversions = true;
7297
John Kessenichfcc0aa32016-08-24 18:34:43 -06007298 // can 'from' convert to 'to'?
steve-lunarg05f75142016-12-06 15:50:11 -07007299 const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool {
John Kessenichfcc0aa32016-08-24 18:34:43 -06007300 if (from == to)
7301 return true;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007302
7303 // no aggregate conversions
John Kessenichecba76f2017-01-06 00:34:48 -07007304 if (from.isArray() || to.isArray() ||
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007305 from.isStruct() || to.isStruct())
John Kessenichfcc0aa32016-08-24 18:34:43 -06007306 return false;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007307
steve-lunarg05f75142016-12-06 15:50:11 -07007308 switch (op) {
7309 case EOpInterlockedAdd:
7310 case EOpInterlockedAnd:
7311 case EOpInterlockedCompareExchange:
7312 case EOpInterlockedCompareStore:
7313 case EOpInterlockedExchange:
7314 case EOpInterlockedMax:
7315 case EOpInterlockedMin:
7316 case EOpInterlockedOr:
7317 case EOpInterlockedXor:
7318 // We do not promote the texture or image type for these ocodes. Normally that would not
7319 // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this
7320 // stage it's merely e.g, a basic integer type.
John Kessenichecba76f2017-01-06 00:34:48 -07007321 //
steve-lunarg05f75142016-12-06 15:50:11 -07007322 // Instead, we want to promote other arguments, but stay within the same family. In other
7323 // words, InterlockedAdd(RWBuffer<int>, ...) will always use the int flavor, never the uint flavor,
7324 // but it is allowed to promote its other arguments.
7325 if (arg == 0)
7326 return false;
t.jung84e59202017-01-02 17:10:27 +01007327 break;
7328 case EOpMethodSample:
7329 case EOpMethodSampleBias:
7330 case EOpMethodSampleCmp:
7331 case EOpMethodSampleCmpLevelZero:
7332 case EOpMethodSampleGrad:
7333 case EOpMethodSampleLevel:
7334 case EOpMethodLoad:
7335 case EOpMethodGetDimensions:
7336 case EOpMethodGetSamplePosition:
7337 case EOpMethodGather:
7338 case EOpMethodCalculateLevelOfDetail:
7339 case EOpMethodCalculateLevelOfDetailUnclamped:
7340 case EOpMethodGatherRed:
7341 case EOpMethodGatherGreen:
7342 case EOpMethodGatherBlue:
7343 case EOpMethodGatherAlpha:
7344 case EOpMethodGatherCmp:
7345 case EOpMethodGatherCmpRed:
7346 case EOpMethodGatherCmpGreen:
7347 case EOpMethodGatherCmpBlue:
7348 case EOpMethodGatherCmpAlpha:
7349 case EOpMethodAppend:
7350 case EOpMethodRestartStrip:
7351 // those are method calls, the object type can not be changed
7352 // they are equal if the dim and type match (is dim sufficient?)
7353 if (arg == 0)
7354 return from.getSampler().type == to.getSampler().type &&
7355 from.getSampler().arrayed == to.getSampler().arrayed &&
7356 from.getSampler().shadow == to.getSampler().shadow &&
7357 from.getSampler().ms == to.getSampler().ms &&
7358 from.getSampler().dim == to.getSampler().dim;
7359 break;
steve-lunarg05f75142016-12-06 15:50:11 -07007360 default:
7361 break;
7362 }
7363
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007364 // basic types have to be convertible
steve-lunarg05f75142016-12-06 15:50:11 -07007365 if (allowOnlyUpConversions)
7366 if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall))
7367 return false;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007368
7369 // shapes have to be convertible
steve-lunargd9cb8322016-11-11 15:37:10 -07007370 if ((from.isScalarOrVec1() && to.isScalarOrVec1()) ||
7371 (from.isScalarOrVec1() && to.isVector()) ||
LoopDawge2713122017-06-09 14:36:46 -06007372 (from.isScalarOrVec1() && to.isMatrix()) ||
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007373 (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize()))
7374 return true;
7375
7376 // TODO: what are the matrix rules? they go here
7377
7378 return false;
John Kessenichfcc0aa32016-08-24 18:34:43 -06007379 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07007380
John Kessenichfcc0aa32016-08-24 18:34:43 -06007381 // Is 'to2' a better conversion than 'to1'?
7382 // Ties should not be considered as better.
7383 // Assumes 'convertible' already said true.
baldurk54a28de2016-10-13 19:23:39 +02007384 const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool {
John Kessenich90dd70f2016-08-25 10:49:21 -06007385 // exact match is always better than mismatch
John Kessenichfcc0aa32016-08-24 18:34:43 -06007386 if (from == to2)
7387 return from != to1;
7388 if (from == to1)
7389 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007390
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007391 // shape changes are always worse
7392 if (from.isScalar() || from.isVector()) {
7393 if (from.getVectorSize() == to2.getVectorSize() &&
7394 from.getVectorSize() != to1.getVectorSize())
John Kessenichfcc0aa32016-08-24 18:34:43 -06007395 return true;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007396 if (from.getVectorSize() == to1.getVectorSize() &&
7397 from.getVectorSize() != to2.getVectorSize())
7398 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007399 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07007400
steve-lunarg26d31452016-12-23 18:56:57 -07007401 // Handle sampler betterness: An exact sampler match beats a non-exact match.
7402 // (If we just looked at basic type, all EbtSamplers would look the same).
7403 // If any type is not a sampler, just use the linearize function below.
7404 if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) {
7405 // We can ignore the vector size in the comparison.
7406 TSampler to1Sampler = to1.getSampler();
7407 TSampler to2Sampler = to2.getSampler();
7408
7409 to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize;
7410
7411 if (from.getSampler() == to2Sampler)
7412 return from.getSampler() != to1Sampler;
7413 if (from.getSampler() == to1Sampler)
7414 return false;
7415 }
7416
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007417 // Might or might not be changing shape, which means basic type might
7418 // or might not match, so within that, the question is how big a
7419 // basic-type conversion is being done.
7420 //
7421 // Use a hierarchy of domains, translated to order of magnitude
7422 // in a linearized view:
7423 // - floating-point vs. integer
7424 // - 32 vs. 64 bit (or width in general)
7425 // - bool vs. non bool
7426 // - signed vs. not signed
baldurk54a28de2016-10-13 19:23:39 +02007427 const auto linearize = [](const TBasicType& basicType) -> int {
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007428 switch (basicType) {
7429 case EbtBool: return 1;
7430 case EbtInt: return 10;
7431 case EbtUint: return 11;
7432 case EbtInt64: return 20;
7433 case EbtUint64: return 21;
7434 case EbtFloat: return 100;
7435 case EbtDouble: return 110;
7436 default: return 0;
7437 }
7438 };
John Kessenich90dd70f2016-08-25 10:49:21 -06007439
John Kesseniche3f2c8f2016-08-25 15:57:56 -06007440 return std::abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) <
7441 std::abs(linearize(to1.getBasicType()) - linearize(from.getBasicType()));
John Kessenichfcc0aa32016-08-24 18:34:43 -06007442 };
7443
7444 // for ambiguity reporting
7445 bool tie = false;
John Kessenichecba76f2017-01-06 00:34:48 -07007446
John Kessenichfcc0aa32016-08-24 18:34:43 -06007447 // send to the generic selector
7448 const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie);
7449
steve-lunargef33ec02016-11-02 12:42:34 -06007450 if (bestMatch == nullptr) {
steve-lunarg05f75142016-12-06 15:50:11 -07007451 // If there is nothing selected by allowing only up-conversions (to a larger linearize() value),
7452 // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any
7453 // upconversions possible.
7454 allowOnlyUpConversions = false;
7455 bestMatch = selectFunction(candidateList, call, convertible, better, tie);
7456 }
7457
7458 if (bestMatch == nullptr) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07007459 error(loc, "no matching overloaded function found", call.getName().c_str(), "");
steve-lunargef33ec02016-11-02 12:42:34 -06007460 return nullptr;
7461 }
7462
John Kessenich8bcdf2e2017-07-30 16:54:02 -06007463 // For built-ins, we can convert across the arguments. This will happen in several steps:
steve-lunargef33ec02016-11-02 12:42:34 -06007464 // Step 1: If there's an exact match, use it.
7465 // Step 2a: Otherwise, get the operator from the best match and promote arguments:
7466 // Step 2b: reconstruct the TFunction based on the new arg types
7467 // Step 3: Re-select after type promotion is applied, to find proper candidate.
7468 if (builtIn) {
7469 // Step 1: If there's an exact match, use it.
7470 if (call.getMangledName() == bestMatch->getMangledName())
7471 return bestMatch;
7472
7473 // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we
7474 // are that kind of operator.
7475 if (args != nullptr) {
7476 // The arg list can be a unary node, or an aggregate. We have to handle both.
7477 // We will use the normal promote() facilities, which require an interm node.
7478 TIntermOperator* promote = nullptr;
7479
7480 if (call.getParamCount() == 1) {
7481 promote = new TIntermUnary(bestMatch->getBuiltInOp());
7482 promote->getAsUnaryNode()->setOperand(args->getAsTyped());
7483 } else {
7484 promote = new TIntermAggregate(bestMatch->getBuiltInOp());
7485 promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
7486 }
7487
7488 if (! intermediate.promote(promote))
7489 return nullptr;
7490
7491 // Obtain the promoted arg list.
7492 if (call.getParamCount() == 1) {
7493 args = promote->getAsUnaryNode()->getOperand();
7494 } else {
7495 promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
7496 }
7497 }
7498
7499 // Step 2b: reconstruct the TFunction based on the new arg types
7500 TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp());
7501
7502 if (args->getAsAggregate()) {
7503 // Handle aggregates: put all args into the new function call
7504 for (int arg=0; arg<int(args->getAsAggregate()->getSequence().size()); ++arg) {
7505 // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly.
steve-lunarg26d31452016-12-23 18:56:57 -07007506 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06007507 param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType());
7508 convertedCall.addParameter(param);
7509 }
7510 } else if (args->getAsUnaryNode()) {
7511 // Handle unaries: put all args into the new function call
steve-lunarg26d31452016-12-23 18:56:57 -07007512 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06007513 param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType());
7514 convertedCall.addParameter(param);
7515 } else if (args->getAsTyped()) {
7516 // Handle bare e.g, floats, not in an aggregate.
steve-lunarg26d31452016-12-23 18:56:57 -07007517 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06007518 param.type->shallowCopy(args->getAsTyped()->getType());
7519 convertedCall.addParameter(param);
7520 } else {
7521 assert(0); // unknown argument list.
7522 return nullptr;
7523 }
7524
7525 // Step 3: Re-select after type promotion, to find proper candidate
7526 // send to the generic selector
7527 bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie);
7528
7529 // At this point, there should be no tie.
7530 }
7531
7532 if (tie)
John Kessenichfcc0aa32016-08-24 18:34:43 -06007533 error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07007534
steve-lunarg26d31452016-12-23 18:56:57 -07007535 // Append default parameter values if needed
7536 if (!tie && bestMatch != nullptr) {
7537 for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) {
7538 handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue);
7539 }
7540 }
7541
John Kessenichfcc0aa32016-08-24 18:34:43 -06007542 return bestMatch;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007543}
7544
7545//
John Kessenich5e69ec62016-07-05 00:02:40 -06007546// Do everything necessary to handle a typedef declaration, for a single symbol.
John Kessenichecba76f2017-01-06 00:34:48 -07007547//
John Kessenich5e69ec62016-07-05 00:02:40 -06007548// 'parseType' is the type part of the declaration (to the left)
7549// 'arraySizes' is the arrayness tagged on the identifier (to the right)
7550//
John Kessenich4dc835c2017-03-28 23:43:10 -06007551void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType)
John Kessenich5e69ec62016-07-05 00:02:40 -06007552{
John Kessenichdd402602017-02-08 13:59:30 -07007553 TVariable* typeSymbol = new TVariable(&identifier, parseType, true);
John Kessenich5e69ec62016-07-05 00:02:40 -06007554 if (! symbolTable.insert(*typeSymbol))
7555 error(loc, "name already defined", "typedef", identifier.c_str());
7556}
7557
John Kessenich727b3742017-02-03 17:57:55 -07007558// Do everything necessary to handle a struct declaration, including
7559// making IO aliases because HLSL allows mixed IO in a struct that specializes
7560// based on the usage (input, output, uniform, none).
7561void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type)
steve-lunarga2e75312016-12-14 15:22:25 -07007562{
John Kessenich727b3742017-02-03 17:57:55 -07007563 // If it was named, which means the type can be reused later, add
7564 // it to the symbol table. (Unless it's a block, in which
7565 // case the name is not a type.)
7566 if (type.getBasicType() == EbtBlock || structName.size() == 0)
John Kessenichabd8dca2017-02-01 18:09:17 -07007567 return;
steve-lunarg5d3023a2017-01-25 10:03:17 -07007568
John Kessenich727b3742017-02-03 17:57:55 -07007569 TVariable* userTypeDef = new TVariable(&structName, type, true);
7570 if (! symbolTable.insert(*userTypeDef)) {
7571 error(loc, "redefinition", structName.c_str(), "struct");
7572 return;
7573 }
steve-lunarg5d3023a2017-01-25 10:03:17 -07007574
John Kessenich727b3742017-02-03 17:57:55 -07007575 // See if we need IO aliases for the structure typeList
7576
John Kessenichbf472862017-02-05 20:27:30 -07007577 const auto condAlloc = [](bool pred, TTypeList*& list) {
7578 if (pred && list == nullptr)
7579 list = new TTypeList;
7580 };
7581
7582 tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found
John Kessenich727b3742017-02-03 17:57:55 -07007583 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
John Kessenichbf472862017-02-05 20:27:30 -07007584 condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform);
7585 condAlloc( hasInput(member->type->getQualifier()), newLists.input);
7586 condAlloc( hasOutput(member->type->getQualifier()), newLists.output);
7587
John Kessenich727b3742017-02-03 17:57:55 -07007588 if (member->type->isStruct()) {
John Kessenichbf472862017-02-05 20:27:30 -07007589 auto it = ioTypeMap.find(member->type->getStruct());
7590 if (it != ioTypeMap.end()) {
7591 condAlloc(it->second.uniform != nullptr, newLists.uniform);
7592 condAlloc(it->second.input != nullptr, newLists.input);
7593 condAlloc(it->second.output != nullptr, newLists.output);
John Kessenich727b3742017-02-03 17:57:55 -07007594 }
7595 }
7596 }
John Kessenichbf472862017-02-05 20:27:30 -07007597 if (newLists.uniform == nullptr &&
7598 newLists.input == nullptr &&
John Kessenich65ee2302017-02-06 18:44:52 -07007599 newLists.output == nullptr) {
7600 // Won't do any IO caching, clear up the type and get out now.
7601 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member)
7602 clearUniformInputOutput(member->type->getQualifier());
John Kessenichabd8dca2017-02-01 18:09:17 -07007603 return;
John Kessenich65ee2302017-02-06 18:44:52 -07007604 }
steve-lunarga2e75312016-12-14 15:22:25 -07007605
John Kessenich727b3742017-02-03 17:57:55 -07007606 // We have IO involved.
steve-lunarga2e75312016-12-14 15:22:25 -07007607
John Kessenich727b3742017-02-03 17:57:55 -07007608 // Make a pure typeList for the symbol table, and cache side copies of IO versions.
John Kessenich727b3742017-02-03 17:57:55 -07007609 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
John Kessenichbf472862017-02-05 20:27:30 -07007610 const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) {
7611 if (s != nullptr) {
7612 ioMember.type = new TType;
7613 ioMember.type->shallowCopy(*member->type);
7614 ioMember.type->setStruct(s);
7615 }
7616 };
7617 const auto newMember = [&](TTypeLoc& m) {
7618 if (m.type == nullptr) {
7619 m.type = new TType;
7620 m.type->shallowCopy(*member->type);
7621 }
7622 };
7623
7624 TTypeLoc newUniformMember = { nullptr, member->loc };
7625 TTypeLoc newInputMember = { nullptr, member->loc };
7626 TTypeLoc newOutputMember = { nullptr, member->loc };
John Kessenich727b3742017-02-03 17:57:55 -07007627 if (member->type->isStruct()) {
7628 // swap in an IO child if there is one
7629 auto it = ioTypeMap.find(member->type->getStruct());
John Kessenichbf472862017-02-05 20:27:30 -07007630 if (it != ioTypeMap.end()) {
7631 inheritStruct(it->second.uniform, newUniformMember);
7632 inheritStruct(it->second.input, newInputMember);
7633 inheritStruct(it->second.output, newOutputMember);
7634 }
John Kessenich727b3742017-02-03 17:57:55 -07007635 }
John Kessenichbf472862017-02-05 20:27:30 -07007636 if (newLists.uniform) {
7637 newMember(newUniformMember);
LoopDawg6a264be2017-08-07 12:08:50 -06007638
7639 // inherit default matrix layout (changeable via #pragma pack_matrix), if none given.
7640 if (member->type->isMatrix() && member->type->getQualifier().layoutMatrix == ElmNone)
7641 newUniformMember.type->getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix;
7642
John Kessenichbf472862017-02-05 20:27:30 -07007643 correctUniform(newUniformMember.type->getQualifier());
7644 newLists.uniform->push_back(newUniformMember);
7645 }
7646 if (newLists.input) {
7647 newMember(newInputMember);
7648 correctInput(newInputMember.type->getQualifier());
7649 newLists.input->push_back(newInputMember);
7650 }
7651 if (newLists.output) {
7652 newMember(newOutputMember);
7653 correctOutput(newOutputMember.type->getQualifier());
7654 newLists.output->push_back(newOutputMember);
7655 }
7656
7657 // make original pure
7658 clearUniformInputOutput(member->type->getQualifier());
John Kessenich727b3742017-02-03 17:57:55 -07007659 }
John Kessenichbf472862017-02-05 20:27:30 -07007660 ioTypeMap[type.getStruct()] = newLists;
steve-lunarga2e75312016-12-14 15:22:25 -07007661}
7662
John Kessenich854fe242017-03-02 14:30:59 -07007663// Lookup a user-type by name.
7664// If found, fill in the type and return the defining symbol.
7665// If not found, return nullptr.
7666TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type)
7667{
7668 TSymbol* symbol = symbolTable.find(typeName);
7669 if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
7670 type.shallowCopy(symbol->getType());
7671 return symbol;
7672 } else
7673 return nullptr;
7674}
7675
John Kessenich5e69ec62016-07-05 00:02:40 -06007676//
John Kesseniche01a9bc2016-03-12 20:11:22 -07007677// Do everything necessary to handle a variable (non-block) declaration.
7678// Either redeclaring a variable, or making a new one, updating the symbol
7679// table, and all error checking.
7680//
7681// Returns a subtree node that computes an initializer, if needed.
7682// Returns nullptr if there is no code to execute for initialization.
7683//
John Kessenich5e69ec62016-07-05 00:02:40 -06007684// 'parseType' is the type part of the declaration (to the left)
John Kesseniche01a9bc2016-03-12 20:11:22 -07007685// 'arraySizes' is the arrayness tagged on the identifier (to the right)
7686//
John Kessenich2ceec682017-07-28 16:20:13 -06007687TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type,
7688 TIntermTyped* initializer)
John Kesseniche01a9bc2016-03-12 20:11:22 -07007689{
John Kesseniche01a9bc2016-03-12 20:11:22 -07007690 if (voidErrorCheck(loc, identifier, type.getBasicType()))
7691 return nullptr;
7692
LoopDawg0fca0ba2017-07-10 15:43:40 -06007693 // Global consts with initializers that are non-const act like EvqGlobal in HLSL.
7694 // This test is implicitly recursive, because initializers propagate constness
7695 // up the aggregate node tree during creation. E.g, for:
7696 // { { 1, 2 }, { 3, 4 } }
7697 // the initializer list is marked EvqConst at the top node, and remains so here. However:
7698 // { 1, { myvar, 2 }, 3 }
7699 // is not a const intializer, and still becomes EvqGlobal here.
7700
7701 const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst);
7702
7703 if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) {
7704 // Force to global
7705 type.getQualifier().storage = EvqGlobal;
7706 }
7707
John Kessenichaa6d5622016-12-30 16:42:57 -07007708 // make const and initialization consistent
7709 fixConstInit(loc, identifier, type, initializer);
7710
John Kesseniche01a9bc2016-03-12 20:11:22 -07007711 // Check for redeclaration of built-ins and/or attempting to declare a reserved name
John Kessenichd3f11222016-11-05 10:15:53 -06007712 TSymbol* symbol = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007713
7714 inheritGlobalDefaults(type.getQualifier());
7715
John Kessenich41aa1992017-10-11 14:03:45 -06007716 const bool flattenVar = shouldFlatten(type, type.getQualifier().storage, true);
steve-lunarga2e75312016-12-14 15:22:25 -07007717
John Kessenichbf472862017-02-05 20:27:30 -07007718 // correct IO in the type
steve-lunarg5d3023a2017-01-25 10:03:17 -07007719 switch (type.getQualifier().storage) {
7720 case EvqGlobal:
7721 case EvqTemporary:
John Kessenichbf472862017-02-05 20:27:30 -07007722 clearUniformInputOutput(type.getQualifier());
7723 break;
7724 case EvqUniform:
7725 case EvqBuffer:
7726 correctUniform(type.getQualifier());
John Kessenich65ee2302017-02-06 18:44:52 -07007727 if (type.isStruct()) {
7728 auto it = ioTypeMap.find(type.getStruct());
7729 if (it != ioTypeMap.end())
7730 type.setStruct(it->second.uniform);
7731 }
steve-lunargdd8287a2017-02-23 18:04:12 -07007732
John Kessenichbf472862017-02-05 20:27:30 -07007733 break;
steve-lunarg5d3023a2017-01-25 10:03:17 -07007734 default:
7735 break;
7736 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06007737
John Kesseniche01a9bc2016-03-12 20:11:22 -07007738 // Declare the variable
John Kesseniche82061d2016-09-27 14:38:57 -06007739 if (type.isArray()) {
7740 // array case
steve-lunarg5d3023a2017-01-25 10:03:17 -07007741 declareArray(loc, identifier, type, symbol, !flattenVar);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007742 } else {
7743 // non-array case
John Kessenichf02c8e62017-06-19 16:25:44 -06007744 if (symbol == nullptr)
steve-lunarg5d3023a2017-01-25 10:03:17 -07007745 symbol = declareNonArray(loc, identifier, type, !flattenVar);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007746 else if (type != symbol->getType())
7747 error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
7748 }
7749
John Kessenichf02c8e62017-06-19 16:25:44 -06007750 if (symbol == nullptr)
John Kesseniche01a9bc2016-03-12 20:11:22 -07007751 return nullptr;
7752
John Kessenich0e6e2ff2017-07-16 05:46:13 -06007753 if (flattenVar)
John Kessenichd5aedc12017-08-06 19:42:42 -06007754 flatten(*symbol->getAsVariable(), symbolTable.atGlobalLevel());
John Kessenich0e6e2ff2017-07-16 05:46:13 -06007755
7756 if (initializer == nullptr)
7757 return nullptr;
7758
John Kesseniche01a9bc2016-03-12 20:11:22 -07007759 // Deal with initializer
John Kessenich0e6e2ff2017-07-16 05:46:13 -06007760 TVariable* variable = symbol->getAsVariable();
7761 if (variable == nullptr) {
7762 error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
7763 return nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007764 }
John Kessenich092b7d22017-09-30 14:54:18 -06007765 return executeInitializer(loc, initializer, variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007766}
7767
7768// Pick up global defaults from the provide global defaults into dst.
7769void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const
7770{
7771 if (dst.storage == EvqVaryingOut) {
7772 if (! dst.hasStream() && language == EShLangGeometry)
7773 dst.layoutStream = globalOutputDefaults.layoutStream;
7774 if (! dst.hasXfbBuffer())
7775 dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
7776 }
7777}
7778
7779//
7780// Make an internal-only variable whose name is for debug purposes only
7781// and won't be searched for. Callers will only use the return value to use
7782// the variable, not the name to look it up. It is okay if the name
7783// is the same as other names; there won't be any conflict.
7784//
7785TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const
7786{
John Kesseniche48b8d72017-01-19 15:29:25 -07007787 TString* nameString = NewPoolTString(name);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007788 TVariable* variable = new TVariable(nameString, type);
7789 symbolTable.makeInternalVariable(*variable);
7790
7791 return variable;
7792}
7793
John Kessenich82460b52017-04-04 11:47:42 -06007794// Make a symbol node holding a new internal temporary variable.
John Kessenich2ceec682017-07-28 16:20:13 -06007795TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name,
7796 const TType& type) const
John Kessenich82460b52017-04-04 11:47:42 -06007797{
7798 TVariable* tmpVar = makeInternalVariable(name, type);
7799 tmpVar->getWritableType().getQualifier().makeTemporary();
7800
7801 return intermediate.addSymbol(*tmpVar, loc);
7802}
7803
John Kesseniche01a9bc2016-03-12 20:11:22 -07007804//
7805// Declare a non-array variable, the main point being there is no redeclaration
7806// for resizing allowed.
7807//
7808// Return the successfully declared variable.
7809//
John Kessenich2ceec682017-07-28 16:20:13 -06007810TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
7811 bool track)
John Kesseniche01a9bc2016-03-12 20:11:22 -07007812{
7813 // make a new variable
7814 TVariable* variable = new TVariable(&identifier, type);
7815
7816 // add variable to symbol table
John Kessenichd3f11222016-11-05 10:15:53 -06007817 if (symbolTable.insert(*variable)) {
steve-lunarga2b01a02016-11-28 17:09:54 -07007818 if (track && symbolTable.atGlobalLevel())
John Kessenich02467d82017-01-19 15:41:47 -07007819 trackLinkage(*variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007820 return variable;
7821 }
John Kessenichd3f11222016-11-05 10:15:53 -06007822
7823 error(loc, "redefinition", variable->getName().c_str(), "");
7824 return nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007825}
7826
7827//
7828// Handle all types of initializers from the grammar.
7829//
7830// Returning nullptr just means there is no code to execute to handle the
7831// initializer, which will, for example, be the case for constant initializers.
7832//
John Kessenich092b7d22017-09-30 14:54:18 -06007833TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
John Kesseniche01a9bc2016-03-12 20:11:22 -07007834{
7835 //
7836 // Identifier must be of type constant, a global, or a temporary, and
7837 // starting at version 120, desktop allows uniforms to have initializers.
7838 //
7839 TStorageQualifier qualifier = variable->getType().getQualifier().storage;
7840
7841 //
7842 // If the initializer was from braces { ... }, we convert the whole subtree to a
7843 // constructor-style subtree, allowing the rest of the code to operate
7844 // identically for both kinds of initializers.
7845 //
John Kessenich085b8332017-01-05 10:28:26 -07007846 //
7847 // Type can't be deduced from the initializer list, so a skeletal type to
7848 // follow has to be passed in. Constness and specialization-constness
7849 // should be deduced bottom up, not dictated by the skeletal type.
7850 //
7851 TType skeletalType;
7852 skeletalType.shallowCopy(variable->getType());
7853 skeletalType.getQualifier().makeTemporary();
John Kessenich98ad4852016-11-27 17:39:07 -07007854 if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
John Kessenich82460b52017-04-04 11:47:42 -06007855 initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
John Kessenichf02c8e62017-06-19 16:25:44 -06007856 if (initializer == nullptr) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07007857 // error recovery; don't leave const without constant values
7858 if (qualifier == EvqConst)
7859 variable->getWritableType().getQualifier().storage = EvqTemporary;
7860 return nullptr;
7861 }
7862
7863 // Fix outer arrayness if variable is unsized, getting size from the initializer
7864 if (initializer->getType().isExplicitlySizedArray() &&
7865 variable->getType().isImplicitlySizedArray())
7866 variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize());
7867
7868 // Inner arrayness can also get set by an initializer
7869 if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() &&
7870 initializer->getType().getArraySizes()->getNumDims() ==
7871 variable->getType().getArraySizes()->getNumDims()) {
7872 // adopt unsized sizes from the initializer's sizes
7873 for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) {
7874 if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize)
7875 variable->getWritableType().getArraySizes().setDimSize(d, initializer->getType().getArraySizes()->getDimSize(d));
7876 }
7877 }
7878
7879 // Uniform and global consts require a constant initializer
7880 if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) {
7881 error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
7882 variable->getWritableType().getQualifier().storage = EvqTemporary;
7883 return nullptr;
7884 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07007885
John Kessenich6fa17642017-04-07 15:33:08 -06007886 // Const variables require a constant initializer
John Kesseniche01a9bc2016-03-12 20:11:22 -07007887 if (qualifier == EvqConst) {
7888 if (initializer->getType().getQualifier().storage != EvqConst) {
7889 variable->getWritableType().getQualifier().storage = EvqConstReadOnly;
7890 qualifier = EvqConstReadOnly;
7891 }
7892 }
7893
7894 if (qualifier == EvqConst || qualifier == EvqUniform) {
7895 // Compile-time tagging of the variable with its constant value...
7896
7897 initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
John Kessenich20518152017-04-12 14:56:52 -06007898 if (initializer != nullptr && variable->getType() != initializer->getType())
John Kessenichd5d9ffb2017-04-18 21:07:05 -06007899 initializer = intermediate.addUniShapeConversion(EOpAssign, variable->getType(), initializer);
John Kessenich20518152017-04-12 14:56:52 -06007900 if (initializer == nullptr || !initializer->getAsConstantUnion() ||
7901 variable->getType() != initializer->getType()) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07007902 error(loc, "non-matching or non-convertible constant type for const initializer",
7903 variable->getType().getStorageQualifierString(), "");
7904 variable->getWritableType().getQualifier().storage = EvqTemporary;
7905 return nullptr;
7906 }
7907
7908 variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
7909 } else {
7910 // normal assigning of a value to a variable...
7911 specializationCheck(loc, initializer->getType(), "initializer");
7912 TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
John Kessenich15fa7ef2017-09-07 04:33:11 -06007913 TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
7914 if (initNode == nullptr)
7915 assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
7916 return initNode;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007917 }
7918
7919 return nullptr;
7920}
7921
7922//
7923// Reprocess any initializer-list { ... } parts of the initializer.
7924// Need to hierarchically assign correct types and implicit
7925// conversions. Will do this mimicking the same process used for
7926// creating a constructor-style initializer, ensuring we get the
7927// same form.
7928//
John Kessenichf97f2ce2016-11-27 22:51:36 -07007929// Returns a node representing an expression for the initializer list expressed
7930// as the correct type.
7931//
7932// Returns nullptr if there is an error.
7933//
John Kessenich82460b52017-04-04 11:47:42 -06007934TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type,
7935 TIntermTyped* initializer, TIntermTyped* scalarInit)
John Kesseniche01a9bc2016-03-12 20:11:22 -07007936{
7937 // Will operate recursively. Once a subtree is found that is constructor style,
7938 // everything below it is already good: Only the "top part" of the initializer
7939 // can be an initializer list, where "top part" can extend for several (or all) levels.
7940
7941 // see if we have bottomed out in the tree within the initializer-list part
7942 TIntermAggregate* initList = initializer->getAsAggregate();
John Kessenichf02c8e62017-06-19 16:25:44 -06007943 if (initList == nullptr || initList->getOp() != EOpNull) {
John Kessenich98ad4852016-11-27 17:39:07 -07007944 // We don't have a list, but if it's a scalar and the 'type' is a
7945 // composite, we need to lengthen below to make it useful.
7946 // Otherwise, this is an already formed object to initialize with.
7947 if (type.isScalar() || !initializer->getType().isScalar())
7948 return initializer;
7949 else
7950 initList = intermediate.makeAggregate(initializer);
7951 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07007952
7953 // Of the initializer-list set of nodes, need to process bottom up,
7954 // so recurse deep, then process on the way up.
7955
7956 // Go down the tree here...
7957 if (type.isArray()) {
7958 // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate.
7959 // Later on, initializer execution code will deal with array size logic.
7960 TType arrayType;
7961 arrayType.shallowCopy(type); // sharing struct stuff is fine
7962 arrayType.newArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below
7963
7964 // edit array sizes to fill in unsized dimensions
John Kessenich98ad4852016-11-27 17:39:07 -07007965 if (type.isImplicitlySizedArray())
7966 arrayType.changeOuterArraySize((int)initList->getSequence().size());
John Kessenich53864842016-12-30 15:59:28 -07007967
7968 // set unsized array dimensions that can be derived from the initializer's first element
7969 if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) {
7970 TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped();
7971 if (firstInit->getType().isArray() &&
7972 arrayType.getArraySizes().getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) {
7973 for (int d = 1; d < arrayType.getArraySizes().getNumDims(); ++d) {
7974 if (arrayType.getArraySizes().getDimSize(d) == UnsizedArraySize)
7975 arrayType.getArraySizes().setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1));
7976 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07007977 }
7978 }
7979
John Kessenich98ad4852016-11-27 17:39:07 -07007980 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06007981 lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit);
John Kessenich98ad4852016-11-27 17:39:07 -07007982
7983 // recursively process each element
John Kesseniche01a9bc2016-03-12 20:11:22 -07007984 TType elementType(arrayType, 0); // dereferenced type
John Kessenich98ad4852016-11-27 17:39:07 -07007985 for (int i = 0; i < arrayType.getOuterArraySize(); ++i) {
John Kessenich2ceec682017-07-28 16:20:13 -06007986 initList->getSequence()[i] = convertInitializerList(loc, elementType,
7987 initList->getSequence()[i]->getAsTyped(), scalarInit);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007988 if (initList->getSequence()[i] == nullptr)
7989 return nullptr;
7990 }
7991
John Kessenicha26a5172016-07-28 15:29:35 -06007992 return addConstructor(loc, initList, arrayType);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007993 } else if (type.isStruct()) {
John Kessenichbb79abc2017-10-07 13:23:09 -06007994 // do we have implicit assignments to opaques?
7995 for (size_t i = initList->getSequence().size(); i < type.getStruct()->size(); ++i) {
7996 if ((*type.getStruct())[i].type->containsOpaque()) {
7997 error(loc, "cannot implicitly initialize opaque members", "initializer list", "");
7998 return nullptr;
7999 }
8000 }
8001
John Kessenich98ad4852016-11-27 17:39:07 -07008002 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06008003 lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()), scalarInit);
John Kessenich98ad4852016-11-27 17:39:07 -07008004
John Kesseniche01a9bc2016-03-12 20:11:22 -07008005 if (type.getStruct()->size() != initList->getSequence().size()) {
8006 error(loc, "wrong number of structure members", "initializer list", "");
8007 return nullptr;
8008 }
8009 for (size_t i = 0; i < type.getStruct()->size(); ++i) {
John Kessenich2ceec682017-07-28 16:20:13 -06008010 initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type,
8011 initList->getSequence()[i]->getAsTyped(), scalarInit);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008012 if (initList->getSequence()[i] == nullptr)
8013 return nullptr;
8014 }
8015 } else if (type.isMatrix()) {
steve-lunarg297ae212016-08-24 14:36:13 -06008016 if (type.computeNumComponents() == (int)initList->getSequence().size()) {
8017 // This means the matrix is initialized component-wise, rather than as
8018 // a series of rows and columns. We can just use the list directly as
8019 // a constructor; no further processing needed.
8020 } else {
John Kessenich98ad4852016-11-27 17:39:07 -07008021 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06008022 lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit);
John Kessenich98ad4852016-11-27 17:39:07 -07008023
steve-lunarg297ae212016-08-24 14:36:13 -06008024 if (type.getMatrixCols() != (int)initList->getSequence().size()) {
8025 error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
John Kesseniche01a9bc2016-03-12 20:11:22 -07008026 return nullptr;
steve-lunarg297ae212016-08-24 14:36:13 -06008027 }
8028 TType vectorType(type, 0); // dereferenced type
8029 for (int i = 0; i < type.getMatrixCols(); ++i) {
John Kessenich2ceec682017-07-28 16:20:13 -06008030 initList->getSequence()[i] = convertInitializerList(loc, vectorType,
8031 initList->getSequence()[i]->getAsTyped(), scalarInit);
steve-lunarg297ae212016-08-24 14:36:13 -06008032 if (initList->getSequence()[i] == nullptr)
8033 return nullptr;
8034 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07008035 }
8036 } else if (type.isVector()) {
John Kessenich98ad4852016-11-27 17:39:07 -07008037 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06008038 lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit);
John Kessenich98ad4852016-11-27 17:39:07 -07008039
8040 // error check; we're at bottom, so work is finished below
John Kesseniche01a9bc2016-03-12 20:11:22 -07008041 if (type.getVectorSize() != (int)initList->getSequence().size()) {
John Kessenich2ceec682017-07-28 16:20:13 -06008042 error(loc, "wrong vector size (or rows in a matrix column):", "initializer list",
8043 type.getCompleteString().c_str());
John Kesseniche01a9bc2016-03-12 20:11:22 -07008044 return nullptr;
8045 }
steve-lunargfe5a3ff2016-07-30 10:36:09 -06008046 } else if (type.isScalar()) {
John Kessenich53864842016-12-30 15:59:28 -07008047 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06008048 lengthenList(loc, initList->getSequence(), 1, scalarInit);
John Kessenich53864842016-12-30 15:59:28 -07008049
steve-lunargfe5a3ff2016-07-30 10:36:09 -06008050 if ((int)initList->getSequence().size() != 1) {
8051 error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str());
8052 return nullptr;
8053 }
John Kessenich98ad4852016-11-27 17:39:07 -07008054 } else {
John Kesseniche01a9bc2016-03-12 20:11:22 -07008055 error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
8056 return nullptr;
8057 }
8058
John Kessenichff132132016-07-29 18:22:22 -06008059 // Now that the subtree is processed, process this node as if the
8060 // initializer list is a set of arguments to a constructor.
John Kessenichc633f642017-04-03 21:48:37 -06008061 TIntermTyped* emulatedConstructorArguments;
John Kessenichff132132016-07-29 18:22:22 -06008062 if (initList->getSequence().size() == 1)
John Kessenichc633f642017-04-03 21:48:37 -06008063 emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped();
John Kessenichff132132016-07-29 18:22:22 -06008064 else
8065 emulatedConstructorArguments = initList;
John Kessenich98ad4852016-11-27 17:39:07 -07008066
John Kessenich64285c92017-01-05 10:45:32 -07008067 return addConstructor(loc, emulatedConstructorArguments, type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008068}
8069
John Kessenich98ad4852016-11-27 17:39:07 -07008070// Lengthen list to be long enough to cover any gap from the current list size
8071// to 'size'. If the list is longer, do nothing.
8072// The value to lengthen with is the default for short lists.
John Kessenich82460b52017-04-04 11:47:42 -06008073//
8074// By default, lists that are too short due to lack of initializers initialize to zero.
8075// Alternatively, it could be a scalar initializer for a structure. Both cases are handled,
8076// based on whether something is passed in as 'scalarInit'.
8077//
8078// 'scalarInit' must be safe to use each time this is called (no side effects replication).
8079//
8080void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit)
John Kessenich98ad4852016-11-27 17:39:07 -07008081{
John Kessenich82460b52017-04-04 11:47:42 -06008082 for (int c = (int)list.size(); c < size; ++c) {
8083 if (scalarInit == nullptr)
8084 list.push_back(intermediate.addConstantUnion(0, loc));
8085 else
8086 list.push_back(scalarInit);
8087 }
John Kessenich98ad4852016-11-27 17:39:07 -07008088}
8089
John Kesseniche01a9bc2016-03-12 20:11:22 -07008090//
8091// Test for the correctness of the parameters passed to various constructor functions
8092// and also convert them to the right data type, if allowed and required.
8093//
8094// Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
8095//
John Kessenichc633f642017-04-03 21:48:37 -06008096TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07008097{
John Kessenichc633f642017-04-03 21:48:37 -06008098 if (node == nullptr)
John Kesseniche01a9bc2016-03-12 20:11:22 -07008099 return nullptr;
8100
John Kessenich82460b52017-04-04 11:47:42 -06008101 // Handle the idiom "(struct type)<scalar value>"
8102 if (type.isStruct() && isScalarConstructor(node)) {
8103 // 'node' will almost always get used multiple times, so should not be used directly,
8104 // it would create a DAG instead of a tree, which might be okay (would
8105 // like to formalize that for constants and symbols), but if it has
8106 // side effects, they would get executed multiple times, which is not okay.
8107 if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) {
8108 TIntermAggregate* seq = intermediate.makeAggregate(loc);
8109 TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType());
8110 seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc));
8111 seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy));
8112 seq->setOp(EOpComma);
8113 seq->setType(type);
8114 return seq;
8115 } else
8116 return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node);
8117 }
John Kessenichf97f2ce2016-11-27 22:51:36 -07008118
John Kessenichc633f642017-04-03 21:48:37 -06008119 return addConstructor(loc, node, type);
8120}
8121
8122// Add a constructor, either from the grammar, or other programmatic reasons.
8123//
John Kessenich82ae8c32017-06-13 23:13:10 -06008124// 'node' is what to construct from.
8125// 'type' is what type to construct.
8126//
8127// Returns the constructed object.
John Kessenichc633f642017-04-03 21:48:37 -06008128// Return nullptr if it can't be done.
8129//
8130TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
8131{
John Kessenichc64a9dd2017-09-15 13:15:23 -06008132 TIntermAggregate* aggrNode = node->getAsAggregate();
John Kessenicha26a5172016-07-28 15:29:35 -06008133 TOperator op = intermediate.mapTypeToConstructorOp(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008134
8135 // Combined texture-sampler constructors are completely semantic checked
8136 // in constructorTextureSamplerError()
8137 if (op == EOpConstructTextureSampler)
John Kessenichc64a9dd2017-09-15 13:15:23 -06008138 return intermediate.setAggregateOperator(aggrNode, op, type, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008139
8140 TTypeList::const_iterator memberTypes;
8141 if (op == EOpConstructStruct)
8142 memberTypes = type.getStruct()->begin();
8143
8144 TType elementType;
8145 if (type.isArray()) {
8146 TType dereferenced(type, 0);
8147 elementType.shallowCopy(dereferenced);
8148 } else
8149 elementType.shallowCopy(type);
8150
8151 bool singleArg;
John Kessenich82ae8c32017-06-13 23:13:10 -06008152 if (aggrNode != nullptr) {
xavierae8af5d2017-08-20 10:44:21 +02008153 if (aggrNode->getOp() != EOpNull)
John Kesseniche01a9bc2016-03-12 20:11:22 -07008154 singleArg = true;
8155 else
8156 singleArg = false;
8157 } else
8158 singleArg = true;
8159
8160 TIntermTyped *newNode;
8161 if (singleArg) {
John Kessenich82ae8c32017-06-13 23:13:10 -06008162 // Handle array -> array conversion
8163 // Constructing an array of one type from an array of another type is allowed,
8164 // assuming there are enough components available (semantic-checked earlier).
8165 if (type.isArray() && node->isArray())
8166 newNode = convertArray(node, type);
8167
John Kesseniche01a9bc2016-03-12 20:11:22 -07008168 // If structure constructor or array constructor is being called
John Kessenichc64a9dd2017-09-15 13:15:23 -06008169 // for only one parameter inside the aggregate, we need to call constructAggregate function once.
John Kessenich82ae8c32017-06-13 23:13:10 -06008170 else if (type.isArray())
John Kesseniche01a9bc2016-03-12 20:11:22 -07008171 newNode = constructAggregate(node, elementType, 1, node->getLoc());
8172 else if (op == EOpConstructStruct)
8173 newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
LoopDawge2713122017-06-09 14:36:46 -06008174 else {
8175 // shape conversion for matrix constructor from scalar. HLSL semantics are: scalar
8176 // is replicated into every element of the matrix (not just the diagnonal), so
8177 // that is handled specially here.
8178 if (type.isMatrix() && node->getType().isScalarOrVec1())
8179 node = intermediate.addShapeConversion(type, node);
8180
John Kessenichc633f642017-04-03 21:48:37 -06008181 newNode = constructBuiltIn(type, op, node, node->getLoc(), false);
LoopDawge2713122017-06-09 14:36:46 -06008182 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07008183
8184 if (newNode && (type.isArray() || op == EOpConstructStruct))
8185 newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc);
8186
8187 return newNode;
8188 }
8189
8190 //
8191 // Handle list of arguments.
8192 //
John Kessenichc64a9dd2017-09-15 13:15:23 -06008193 TIntermSequence& sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor
John Kesseniche01a9bc2016-03-12 20:11:22 -07008194 // if the structure constructor contains more than one parameter, then construct
8195 // each parameter
8196
8197 int paramCount = 0; // keeps a track of the constructor parameter number being checked
8198
8199 // for each parameter to the constructor call, check to see if the right type is passed or convert them
8200 // to the right type if possible (and allowed).
8201 // for structure constructors, just check if the right type is passed, no conversion is allowed.
8202
8203 for (TIntermSequence::iterator p = sequenceVector.begin();
8204 p != sequenceVector.end(); p++, paramCount++) {
8205 if (type.isArray())
8206 newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc());
8207 else if (op == EOpConstructStruct)
8208 newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc());
8209 else
8210 newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
8211
8212 if (newNode)
8213 *p = newNode;
8214 else
8215 return nullptr;
8216 }
8217
8218 TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
8219
8220 return constructor;
8221}
8222
8223// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value
8224// for the parameter to the constructor (passed to this function). Essentially, it converts
8225// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a
8226// float, then float is converted to int.
8227//
8228// Returns nullptr for an error or the constructed node.
8229//
John Kessenich2ceec682017-07-28 16:20:13 -06008230TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node,
8231 const TSourceLoc& loc, bool subset)
John Kesseniche01a9bc2016-03-12 20:11:22 -07008232{
8233 TIntermTyped* newNode;
8234 TOperator basicOp;
8235
8236 //
8237 // First, convert types as needed.
8238 //
8239 switch (op) {
8240 case EOpConstructVec2:
8241 case EOpConstructVec3:
8242 case EOpConstructVec4:
8243 case EOpConstructMat2x2:
8244 case EOpConstructMat2x3:
8245 case EOpConstructMat2x4:
8246 case EOpConstructMat3x2:
8247 case EOpConstructMat3x3:
8248 case EOpConstructMat3x4:
8249 case EOpConstructMat4x2:
8250 case EOpConstructMat4x3:
8251 case EOpConstructMat4x4:
8252 case EOpConstructFloat:
8253 basicOp = EOpConstructFloat;
8254 break;
8255
8256 case EOpConstructDVec2:
8257 case EOpConstructDVec3:
8258 case EOpConstructDVec4:
8259 case EOpConstructDMat2x2:
8260 case EOpConstructDMat2x3:
8261 case EOpConstructDMat2x4:
8262 case EOpConstructDMat3x2:
8263 case EOpConstructDMat3x3:
8264 case EOpConstructDMat3x4:
8265 case EOpConstructDMat4x2:
8266 case EOpConstructDMat4x3:
8267 case EOpConstructDMat4x4:
8268 case EOpConstructDouble:
8269 basicOp = EOpConstructDouble;
8270 break;
8271
8272 case EOpConstructIVec2:
8273 case EOpConstructIVec3:
8274 case EOpConstructIVec4:
LoopDawg174ccb82017-05-20 21:40:27 -06008275 case EOpConstructIMat2x2:
8276 case EOpConstructIMat2x3:
8277 case EOpConstructIMat2x4:
8278 case EOpConstructIMat3x2:
8279 case EOpConstructIMat3x3:
8280 case EOpConstructIMat3x4:
8281 case EOpConstructIMat4x2:
8282 case EOpConstructIMat4x3:
8283 case EOpConstructIMat4x4:
John Kesseniche01a9bc2016-03-12 20:11:22 -07008284 case EOpConstructInt:
8285 basicOp = EOpConstructInt;
8286 break;
8287
8288 case EOpConstructUVec2:
8289 case EOpConstructUVec3:
8290 case EOpConstructUVec4:
LoopDawg174ccb82017-05-20 21:40:27 -06008291 case EOpConstructUMat2x2:
8292 case EOpConstructUMat2x3:
8293 case EOpConstructUMat2x4:
8294 case EOpConstructUMat3x2:
8295 case EOpConstructUMat3x3:
8296 case EOpConstructUMat3x4:
8297 case EOpConstructUMat4x2:
8298 case EOpConstructUMat4x3:
8299 case EOpConstructUMat4x4:
John Kesseniche01a9bc2016-03-12 20:11:22 -07008300 case EOpConstructUint:
8301 basicOp = EOpConstructUint;
8302 break;
8303
8304 case EOpConstructBVec2:
8305 case EOpConstructBVec3:
8306 case EOpConstructBVec4:
LoopDawg174ccb82017-05-20 21:40:27 -06008307 case EOpConstructBMat2x2:
8308 case EOpConstructBMat2x3:
8309 case EOpConstructBMat2x4:
8310 case EOpConstructBMat3x2:
8311 case EOpConstructBMat3x3:
8312 case EOpConstructBMat3x4:
8313 case EOpConstructBMat4x2:
8314 case EOpConstructBMat4x3:
8315 case EOpConstructBMat4x4:
John Kesseniche01a9bc2016-03-12 20:11:22 -07008316 case EOpConstructBool:
8317 basicOp = EOpConstructBool;
8318 break;
8319
8320 default:
8321 error(loc, "unsupported construction", "", "");
8322
8323 return nullptr;
8324 }
8325 newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc());
8326 if (newNode == nullptr) {
8327 error(loc, "can't convert", "constructor", "");
8328 return nullptr;
8329 }
8330
8331 //
8332 // Now, if there still isn't an operation to do the construction, and we need one, add one.
8333 //
8334
8335 // Otherwise, skip out early.
8336 if (subset || (newNode != node && newNode->getType() == type))
8337 return newNode;
8338
8339 // setAggregateOperator will insert a new node for the constructor, as needed.
8340 return intermediate.setAggregateOperator(newNode, op, type, loc);
8341}
8342
John Kessenich82ae8c32017-06-13 23:13:10 -06008343// Convert the array in node to the requested type, which is also an array.
8344// Returns nullptr on failure, otherwise returns aggregate holding the list of
8345// elements needed to construct the array.
8346TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type)
8347{
8348 assert(node->isArray() && type.isArray());
8349 if (node->getType().computeNumComponents() < type.computeNumComponents())
8350 return nullptr;
8351
8352 // TODO: write an argument replicator, for the case the argument should not be
8353 // executed multiple times, yet multiple copies are needed.
8354
8355 TIntermTyped* constructee = node->getAsTyped();
8356 // track where we are in consuming the argument
8357 int constructeeElement = 0;
8358 int constructeeComponent = 0;
8359
8360 // bump up to the next component to consume
8361 const auto getNextComponent = [&]() {
8362 TIntermTyped* component;
8363 component = handleBracketDereference(node->getLoc(), constructee,
8364 intermediate.addConstantUnion(constructeeElement, node->getLoc()));
8365 if (component->isVector())
8366 component = handleBracketDereference(node->getLoc(), component,
8367 intermediate.addConstantUnion(constructeeComponent, node->getLoc()));
8368 // bump component pointer up
8369 ++constructeeComponent;
8370 if (constructeeComponent == constructee->getVectorSize()) {
8371 constructeeComponent = 0;
8372 ++constructeeElement;
8373 }
8374 return component;
8375 };
8376
8377 // make one subnode per constructed array element
8378 TIntermAggregate* constructor = nullptr;
8379 TType derefType(type, 0);
8380 TType speculativeComponentType(derefType, 0);
8381 TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType;
8382 TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType);
8383 TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize());
8384 for (int e = 0; e < type.getOuterArraySize(); ++e) {
8385 // construct an element
8386 TIntermTyped* elementArg;
8387 if (type.getVectorSize() == constructee->getVectorSize()) {
8388 // same element shape
8389 elementArg = handleBracketDereference(node->getLoc(), constructee,
8390 intermediate.addConstantUnion(e, node->getLoc()));
8391 } else {
8392 // mismatched element shapes
8393 if (type.getVectorSize() == 1)
8394 elementArg = getNextComponent();
8395 else {
8396 // make a vector
8397 TIntermAggregate* elementConstructee = nullptr;
8398 for (int c = 0; c < type.getVectorSize(); ++c)
8399 elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent());
8400 elementArg = addConstructor(node->getLoc(), elementConstructee, crossType);
8401 }
8402 }
8403 // convert basic types
8404 elementArg = intermediate.addConversion(componentOp, derefType, elementArg);
8405 if (elementArg == nullptr)
8406 return nullptr;
8407 // combine with top-level constructor
8408 constructor = intermediate.growAggregate(constructor, elementArg);
8409 }
8410
8411 return constructor;
8412}
8413
John Kesseniche01a9bc2016-03-12 20:11:22 -07008414// This function tests for the type of the parameters to the structure or array constructor. Raises
8415// an error message if the expected type does not match the parameter passed to the constructor.
8416//
8417// Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
8418//
John Kessenich82ae8c32017-06-13 23:13:10 -06008419TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount,
8420 const TSourceLoc& loc)
John Kesseniche01a9bc2016-03-12 20:11:22 -07008421{
John Kessenich82ae8c32017-06-13 23:13:10 -06008422 // Handle cases that map more 1:1 between constructor arguments and constructed.
John Kesseniche01a9bc2016-03-12 20:11:22 -07008423 TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
John Kessenichf02c8e62017-06-19 16:25:44 -06008424 if (converted == nullptr || converted->getType() != type) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07008425 error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
8426 node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
8427
8428 return nullptr;
8429 }
8430
8431 return converted;
8432}
8433
8434//
8435// Do everything needed to add an interface block.
8436//
John Kessenich3d157c52016-07-25 16:05:33 -06008437void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName, TArraySizes* arraySizes)
John Kesseniche01a9bc2016-03-12 20:11:22 -07008438{
John Kessenich3d157c52016-07-25 16:05:33 -06008439 assert(type.getWritableStruct() != nullptr);
8440
John Kessenich65ee2302017-02-06 18:44:52 -07008441 // Clean up top-level decorations that don't belong.
8442 switch (type.getQualifier().storage) {
8443 case EvqUniform:
8444 case EvqBuffer:
8445 correctUniform(type.getQualifier());
8446 break;
8447 case EvqVaryingIn:
8448 correctInput(type.getQualifier());
8449 break;
8450 case EvqVaryingOut:
8451 correctOutput(type.getQualifier());
8452 break;
8453 default:
8454 break;
8455 }
8456
John Kessenich3d157c52016-07-25 16:05:33 -06008457 TTypeList& typeList = *type.getWritableStruct();
John Kesseniche01a9bc2016-03-12 20:11:22 -07008458 // fix and check for member storage qualifiers and types that don't belong within a block
8459 for (unsigned int member = 0; member < typeList.size(); ++member) {
8460 TType& memberType = *typeList[member].type;
8461 TQualifier& memberQualifier = memberType.getQualifier();
8462 const TSourceLoc& memberLoc = typeList[member].loc;
8463 globalQualifierFix(memberLoc, memberQualifier);
John Kessenich3d157c52016-07-25 16:05:33 -06008464 memberQualifier.storage = type.getQualifier().storage;
John Kessenich65ee2302017-02-06 18:44:52 -07008465
8466 if (memberType.isStruct()) {
8467 // clean up and pick up the right set of decorations
8468 auto it = ioTypeMap.find(memberType.getStruct());
8469 switch (type.getQualifier().storage) {
8470 case EvqUniform:
8471 case EvqBuffer:
8472 correctUniform(type.getQualifier());
8473 if (it != ioTypeMap.end() && it->second.uniform)
LoopDawg898f5fb2017-08-04 15:40:53 -06008474 memberType.setStruct(it->second.uniform);
John Kessenich65ee2302017-02-06 18:44:52 -07008475 break;
8476 case EvqVaryingIn:
8477 correctInput(type.getQualifier());
8478 if (it != ioTypeMap.end() && it->second.input)
LoopDawg898f5fb2017-08-04 15:40:53 -06008479 memberType.setStruct(it->second.input);
John Kessenich65ee2302017-02-06 18:44:52 -07008480 break;
8481 case EvqVaryingOut:
8482 correctOutput(type.getQualifier());
8483 if (it != ioTypeMap.end() && it->second.output)
LoopDawg898f5fb2017-08-04 15:40:53 -06008484 memberType.setStruct(it->second.output);
John Kessenich65ee2302017-02-06 18:44:52 -07008485 break;
8486 default:
8487 break;
8488 }
8489 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07008490 }
8491
John Kesseniche01a9bc2016-03-12 20:11:22 -07008492 // Make default block qualification, and adjust the member qualifications
8493
8494 TQualifier defaultQualification;
John Kessenich3d157c52016-07-25 16:05:33 -06008495 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07008496 case EvqUniform: defaultQualification = globalUniformDefaults; break;
8497 case EvqBuffer: defaultQualification = globalBufferDefaults; break;
8498 case EvqVaryingIn: defaultQualification = globalInputDefaults; break;
8499 case EvqVaryingOut: defaultQualification = globalOutputDefaults; break;
8500 default: defaultQualification.clear(); break;
8501 }
8502
8503 // Special case for "push_constant uniform", which has a default of std430,
8504 // contrary to normal uniform defaults, and can't have a default tracked for it.
John Kessenich3d157c52016-07-25 16:05:33 -06008505 if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking())
8506 type.getQualifier().layoutPacking = ElpStd430;
John Kesseniche01a9bc2016-03-12 20:11:22 -07008507
8508 // fix and check for member layout qualifiers
8509
John Kessenich3d157c52016-07-25 16:05:33 -06008510 mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008511
8512 bool memberWithLocation = false;
8513 bool memberWithoutLocation = false;
8514 for (unsigned int member = 0; member < typeList.size(); ++member) {
8515 TQualifier& memberQualifier = typeList[member].type->getQualifier();
8516 const TSourceLoc& memberLoc = typeList[member].loc;
8517 if (memberQualifier.hasStream()) {
8518 if (defaultQualification.layoutStream != memberQualifier.layoutStream)
8519 error(memberLoc, "member cannot contradict block", "stream", "");
8520 }
8521
John Kessenichecba76f2017-01-06 00:34:48 -07008522 // "This includes a block's inheritance of the
8523 // current global default buffer, a block member's inheritance of the block's
8524 // buffer, and the requirement that any *xfb_buffer* declared on a block
John Kesseniche01a9bc2016-03-12 20:11:22 -07008525 // member must match the buffer inherited from the block."
8526 if (memberQualifier.hasXfbBuffer()) {
8527 if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer)
8528 error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
8529 }
8530
John Kesseniche01a9bc2016-03-12 20:11:22 -07008531 if (memberQualifier.hasLocation()) {
John Kessenich3d157c52016-07-25 16:05:33 -06008532 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07008533 case EvqVaryingIn:
8534 case EvqVaryingOut:
8535 memberWithLocation = true;
8536 break;
8537 default:
8538 break;
8539 }
8540 } else
8541 memberWithoutLocation = true;
John Kesseniche01a9bc2016-03-12 20:11:22 -07008542
8543 TQualifier newMemberQualification = defaultQualification;
John Kessenich34e7ee72016-09-16 17:10:39 -06008544 mergeQualifiers(newMemberQualification, memberQualifier);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008545 memberQualifier = newMemberQualification;
8546 }
8547
8548 // Process the members
John Kessenich3d157c52016-07-25 16:05:33 -06008549 fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation);
8550 fixBlockXfbOffsets(type.getQualifier(), typeList);
8551 fixBlockUniformOffsets(type.getQualifier(), typeList);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008552
8553 // reverse merge, so that currentBlockQualifier now has all layout information
8554 // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
John Kessenich3d157c52016-07-25 16:05:33 -06008555 mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008556
8557 //
8558 // Build and add the interface block as a new type named 'blockName'
8559 //
8560
steve-lunarg8ffc36a2016-09-21 14:19:40 -06008561 // Use the instance name as the interface name if one exists, else the block name.
8562 const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName();
8563
8564 TType blockType(&typeList, interfaceName, type.getQualifier());
John Kesseniche01a9bc2016-03-12 20:11:22 -07008565 if (arraySizes)
8566 blockType.newArraySizes(*arraySizes);
8567
John Kesseniche01a9bc2016-03-12 20:11:22 -07008568 // Add the variable, as anonymous or named instanceName.
8569 // Make an anonymous variable if no name was provided.
John Kessenichf02c8e62017-06-19 16:25:44 -06008570 if (instanceName == nullptr)
John Kesseniche01a9bc2016-03-12 20:11:22 -07008571 instanceName = NewPoolTString("");
8572
8573 TVariable& variable = *new TVariable(instanceName, blockType);
8574 if (! symbolTable.insert(variable)) {
8575 if (*instanceName == "")
John Kessenich2ceec682017-07-28 16:20:13 -06008576 error(loc, "nameless block contains a member that already has a name at global scope",
8577 "" /* blockName->c_str() */, "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07008578 else
8579 error(loc, "block instance name redefinition", variable.getName().c_str(), "");
8580
8581 return;
8582 }
8583
Sebastian Tafuri5133b102017-10-07 00:12:50 +02008584 // Save it in the AST for linker use.
8585 if (symbolTable.atGlobalLevel())
8586 trackLinkage(variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008587}
8588
8589//
John Kessenichecba76f2017-01-06 00:34:48 -07008590// "For a block, this process applies to the entire block, or until the first member
8591// is reached that has a location layout qualifier. When a block member is declared with a location
John Kesseniche01a9bc2016-03-12 20:11:22 -07008592// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level
John Kessenichecba76f2017-01-06 00:34:48 -07008593// declaration. Subsequent members are again assigned consecutive locations, based on the newest location,
8594// until the next member declared with a location qualifier. The values used for locations do not have to be
John Kesseniche01a9bc2016-03-12 20:11:22 -07008595// declared in increasing order."
8596void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation)
8597{
John Kessenichecba76f2017-01-06 00:34:48 -07008598 // "If a block has no block-level location layout qualifier, it is required that either all or none of its members
John Kesseniche01a9bc2016-03-12 20:11:22 -07008599 // have a location layout qualifier, or a compile-time error results."
8600 if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation)
8601 error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", "");
8602 else {
8603 if (memberWithLocation) {
8604 // remove any block-level location and make it per *every* member
8605 int nextLocation = 0; // by the rule above, initial value is not relevant
8606 if (qualifier.hasAnyLocation()) {
8607 nextLocation = qualifier.layoutLocation;
8608 qualifier.layoutLocation = TQualifier::layoutLocationEnd;
8609 if (qualifier.hasComponent()) {
8610 // "It is a compile-time error to apply the *component* qualifier to a ... block"
8611 error(loc, "cannot apply to a block", "component", "");
8612 }
8613 if (qualifier.hasIndex()) {
8614 error(loc, "cannot apply to a block", "index", "");
8615 }
8616 }
8617 for (unsigned int member = 0; member < typeList.size(); ++member) {
8618 TQualifier& memberQualifier = typeList[member].type->getQualifier();
8619 const TSourceLoc& memberLoc = typeList[member].loc;
8620 if (! memberQualifier.hasLocation()) {
8621 if (nextLocation >= (int)TQualifier::layoutLocationEnd)
8622 error(memberLoc, "location is too large", "location", "");
8623 memberQualifier.layoutLocation = nextLocation;
8624 memberQualifier.layoutComponent = 0;
8625 }
John Kessenich2ceec682017-07-28 16:20:13 -06008626 nextLocation = memberQualifier.layoutLocation +
8627 intermediate.computeTypeLocationSize(*typeList[member].type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008628 }
8629 }
8630 }
8631}
8632
8633void HlslParseContext::fixBlockXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
8634{
John Kessenichecba76f2017-01-06 00:34:48 -07008635 // "If a block is qualified with xfb_offset, all its
8636 // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any
8637 // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer
John Kesseniche01a9bc2016-03-12 20:11:22 -07008638 // offsets."
8639
8640 if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset())
8641 return;
8642
8643 int nextOffset = qualifier.layoutXfbOffset;
8644 for (unsigned int member = 0; member < typeList.size(); ++member) {
8645 TQualifier& memberQualifier = typeList[member].type->getQualifier();
8646 bool containsDouble = false;
8647 int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, containsDouble);
8648 // see if we need to auto-assign an offset to this member
8649 if (! memberQualifier.hasXfbOffset()) {
8650 // "if applied to an aggregate containing a double, the offset must also be a multiple of 8"
8651 if (containsDouble)
8652 RoundToPow2(nextOffset, 8);
8653 memberQualifier.layoutXfbOffset = nextOffset;
8654 } else
8655 nextOffset = memberQualifier.layoutXfbOffset;
8656 nextOffset += memberSize;
8657 }
8658
8659 // The above gave all block members an offset, so we can take it off the block now,
8660 // which will avoid double counting the offset usage.
8661 qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd;
8662}
8663
John Kessenichecba76f2017-01-06 00:34:48 -07008664// Calculate and save the offset of each block member, using the recursively
John Kesseniche01a9bc2016-03-12 20:11:22 -07008665// defined block offset rules and the user-provided offset and align.
8666//
John Kessenichecba76f2017-01-06 00:34:48 -07008667// Also, compute and save the total size of the block. For the block's size, arrayness
John Kesseniche01a9bc2016-03-12 20:11:22 -07008668// is not taken into account, as each element is backed by a separate buffer.
8669//
John Kessenich6dbc0a72016-09-27 19:13:05 -06008670void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList)
John Kesseniche01a9bc2016-03-12 20:11:22 -07008671{
8672 if (! qualifier.isUniformOrBuffer())
8673 return;
8674 if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430)
8675 return;
8676
8677 int offset = 0;
8678 int memberSize;
8679 for (unsigned int member = 0; member < typeList.size(); ++member) {
8680 TQualifier& memberQualifier = typeList[member].type->getQualifier();
8681 const TSourceLoc& memberLoc = typeList[member].loc;
8682
8683 // "When align is applied to an array, it effects only the start of the array, not the array's internal stride."
8684
8685 // modify just the children's view of matrix layout, if there is one for this member
8686 TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
8687 int dummyStride;
John Kessenich6dbc0a72016-09-27 19:13:05 -06008688 int memberAlignment = intermediate.getBaseAlignment(*typeList[member].type, memberSize, dummyStride,
8689 qualifier.layoutPacking == ElpStd140,
John Kessenich2ceec682017-07-28 16:20:13 -06008690 subMatrixLayout != ElmNone
8691 ? subMatrixLayout == ElmRowMajor
8692 : qualifier.layoutMatrix == ElmRowMajor);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008693 if (memberQualifier.hasOffset()) {
John Kessenichecba76f2017-01-06 00:34:48 -07008694 // "The specified offset must be a multiple
John Kesseniche01a9bc2016-03-12 20:11:22 -07008695 // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
8696 if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment))
8697 error(memberLoc, "must be a multiple of the member's alignment", "offset", "");
8698
John Kessenichecba76f2017-01-06 00:34:48 -07008699 // "The offset qualifier forces the qualified member to start at or after the specified
8700 // integral-constant expression, which will be its byte offset from the beginning of the buffer.
8701 // "The actual offset of a member is computed as
John Kesseniche01a9bc2016-03-12 20:11:22 -07008702 // follows: If offset was declared, start with that offset, otherwise start with the next available offset."
8703 offset = std::max(offset, memberQualifier.layoutOffset);
8704 }
8705
John Kessenichecba76f2017-01-06 00:34:48 -07008706 // "The actual alignment of a member will be the greater of the specified align alignment and the standard
John Kesseniche01a9bc2016-03-12 20:11:22 -07008707 // (e.g., std140) base alignment for the member's type."
8708 if (memberQualifier.hasAlign())
8709 memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign);
8710
8711 // "If the resulting offset is not a multiple of the actual alignment,
John Kessenichecba76f2017-01-06 00:34:48 -07008712 // increase it to the first offset that is a multiple of
John Kesseniche01a9bc2016-03-12 20:11:22 -07008713 // the actual alignment."
8714 RoundToPow2(offset, memberAlignment);
8715 typeList[member].type->getQualifier().layoutOffset = offset;
8716 offset += memberSize;
8717 }
8718}
8719
8720// For an identifier that is already declared, add more qualification to it.
8721void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
8722{
8723 TSymbol* symbol = symbolTable.find(identifier);
John Kessenichf02c8e62017-06-19 16:25:44 -06008724 if (symbol == nullptr) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07008725 error(loc, "identifier not previously declared", identifier.c_str(), "");
8726 return;
8727 }
8728 if (symbol->getAsFunction()) {
8729 error(loc, "cannot re-qualify a function name", identifier.c_str(), "");
8730 return;
8731 }
8732
8733 if (qualifier.isAuxiliary() ||
8734 qualifier.isMemory() ||
8735 qualifier.isInterpolation() ||
8736 qualifier.hasLayout() ||
8737 qualifier.storage != EvqTemporary ||
8738 qualifier.precision != EpqNone) {
8739 error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), "");
8740 return;
8741 }
8742
8743 // For read-only built-ins, add a new symbol for holding the modified qualifier.
8744 // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block)
8745 if (symbol->isReadOnly())
8746 symbol = symbolTable.copyUp(symbol);
8747
8748 if (qualifier.invariant) {
8749 if (intermediate.inIoAccessed(identifier))
8750 error(loc, "cannot change qualification after use", "invariant", "");
8751 symbol->getWritableType().getQualifier().invariant = true;
John Kessenich17f07862016-05-04 12:36:14 -06008752 } else if (qualifier.noContraction) {
8753 if (intermediate.inIoAccessed(identifier))
8754 error(loc, "cannot change qualification after use", "precise", "");
8755 symbol->getWritableType().getQualifier().noContraction = true;
8756 } else if (qualifier.specConstant) {
8757 symbol->getWritableType().getQualifier().makeSpecConstant();
8758 if (qualifier.hasSpecConstantId())
8759 symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId;
John Kesseniche01a9bc2016-03-12 20:11:22 -07008760 } else
8761 warn(loc, "unknown requalification", "", "");
8762}
8763
8764void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers)
8765{
8766 for (unsigned int i = 0; i < identifiers.size(); ++i)
8767 addQualifierToExisting(loc, qualifier, *identifiers[i]);
8768}
8769
8770//
steve-lunargf49cdf42016-11-17 15:04:20 -07008771// Update the intermediate for the given input geometry
8772//
8773bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
8774{
8775 switch (geometry) {
8776 case ElgPoints: // fall through
8777 case ElgLines: // ...
8778 case ElgTriangles: // ...
8779 case ElgLinesAdjacency: // ...
8780 case ElgTrianglesAdjacency: // ...
8781 if (! intermediate.setInputPrimitive(geometry)) {
8782 error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
8783 return false;
8784 }
8785 break;
8786
8787 default:
8788 error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), "");
8789 return false;
8790 }
8791
8792 return true;
8793}
8794
8795//
8796// Update the intermediate for the given output geometry
8797//
8798bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
8799{
LoopDawg7573a2a2017-11-15 11:33:25 -07008800 // If this is not a geometry shader, ignore. It might be a mixed shader including several stages.
8801 // Since that's an OK situation, return true for success.
8802 if (language != EShLangGeometry)
8803 return true;
8804
steve-lunargf49cdf42016-11-17 15:04:20 -07008805 switch (geometry) {
8806 case ElgPoints:
8807 case ElgLineStrip:
8808 case ElgTriangleStrip:
8809 if (! intermediate.setOutputPrimitive(geometry)) {
8810 error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
8811 return false;
8812 }
8813 break;
8814 default:
8815 error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), "");
8816 return false;
8817 }
8818
8819 return true;
8820}
8821
8822//
John Kesseniche18fd202018-01-30 11:01:39 -07008823// Selection attributes
Rex Xu57e65922017-07-04 23:23:40 +08008824//
John Kesseniche18fd202018-01-30 11:01:39 -07008825void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection,
8826 const TAttributes& attributes)
Rex Xu57e65922017-07-04 23:23:40 +08008827{
John Kesseniche18fd202018-01-30 11:01:39 -07008828 if (selection == nullptr)
8829 return;
8830
8831 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
8832 switch (it->name) {
8833 case EatFlatten:
8834 selection->setFlatten();
8835 break;
8836 case EatBranch:
8837 selection->setDontFlatten();
8838 break;
8839 default:
8840 warn(loc, "attribute does not apply to a selection", "", "");
8841 break;
8842 }
8843 }
8844}
8845
8846//
8847// Switch attributes
8848//
8849void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection,
8850 const TAttributes& attributes)
8851{
8852 if (selection == nullptr)
8853 return;
8854
8855 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
8856 switch (it->name) {
8857 case EatFlatten:
8858 selection->setFlatten();
8859 break;
8860 case EatBranch:
8861 selection->setDontFlatten();
8862 break;
8863 default:
8864 warn(loc, "attribute does not apply to a switch", "", "");
8865 break;
8866 }
8867 }
Rex Xu57e65922017-07-04 23:23:40 +08008868}
8869
8870//
John Kessenicha2858d92018-01-31 08:11:18 -07008871// Loop attributes
steve-lunargf1709e72017-05-02 20:14:50 -06008872//
John Kesseniche18fd202018-01-30 11:01:39 -07008873void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
8874 const TAttributes& attributes)
steve-lunargf1709e72017-05-02 20:14:50 -06008875{
John Kesseniche18fd202018-01-30 11:01:39 -07008876 if (loop == nullptr)
8877 return;
8878
8879 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
8880 switch (it->name) {
8881 case EatUnroll:
8882 loop->setUnroll();
8883 break;
8884 case EatLoop:
8885 loop->setDontUnroll();
8886 break;
8887 default:
8888 warn(loc, "attribute does not apply to a loop", "", "");
8889 break;
8890 }
8891 }
steve-lunargf1709e72017-05-02 20:14:50 -06008892}
8893
steve-lunargf1709e72017-05-02 20:14:50 -06008894//
John Kesseniche01a9bc2016-03-12 20:11:22 -07008895// Updating default qualifier for the case of a declaration with just a qualifier,
8896// no type, block, or identifier.
8897//
8898void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType)
8899{
8900 if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) {
8901 assert(language == EShLangTessControl || language == EShLangGeometry);
John Kessenich7f349c72016-07-08 22:09:10 -06008902 // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices";
John Kesseniche01a9bc2016-03-12 20:11:22 -07008903 }
8904 if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) {
8905 if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations))
8906 error(loc, "cannot change previously set layout value", "invocations", "");
8907 }
8908 if (publicType.shaderQualifiers.geometry != ElgNone) {
8909 if (publicType.qualifier.storage == EvqVaryingIn) {
8910 switch (publicType.shaderQualifiers.geometry) {
8911 case ElgPoints:
8912 case ElgLines:
8913 case ElgLinesAdjacency:
8914 case ElgTriangles:
8915 case ElgTrianglesAdjacency:
8916 case ElgQuads:
8917 case ElgIsolines:
John Kesseniche01a9bc2016-03-12 20:11:22 -07008918 break;
8919 default:
John Kessenich2ceec682017-07-28 16:20:13 -06008920 error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
8921 "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07008922 }
8923 } else if (publicType.qualifier.storage == EvqVaryingOut) {
steve-lunargf49cdf42016-11-17 15:04:20 -07008924 handleOutputGeometry(loc, publicType.shaderQualifiers.geometry);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008925 } else
John Kessenich2ceec682017-07-28 16:20:13 -06008926 error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
8927 GetStorageQualifierString(publicType.qualifier.storage));
John Kesseniche01a9bc2016-03-12 20:11:22 -07008928 }
8929 if (publicType.shaderQualifiers.spacing != EvsNone)
8930 intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing);
8931 if (publicType.shaderQualifiers.order != EvoNone)
8932 intermediate.setVertexOrder(publicType.shaderQualifiers.order);
8933 if (publicType.shaderQualifiers.pointMode)
8934 intermediate.setPointMode();
8935 for (int i = 0; i < 3; ++i) {
8936 if (publicType.shaderQualifiers.localSize[i] > 1) {
8937 int max = 0;
8938 switch (i) {
8939 case 0: max = resources.maxComputeWorkGroupSizeX; break;
8940 case 1: max = resources.maxComputeWorkGroupSizeY; break;
8941 case 2: max = resources.maxComputeWorkGroupSizeZ; break;
8942 default: break;
8943 }
8944 if (intermediate.getLocalSize(i) > (unsigned int)max)
8945 error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
8946
8947 // Fix the existing constant gl_WorkGroupSize with this new information.
8948 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
8949 workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
8950 }
8951 if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
8952 intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]);
8953 // Set the workgroup built-in variable as a specialization constant
8954 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
8955 workGroupSize->getWritableType().getQualifier().specConstant = true;
8956 }
8957 }
8958 if (publicType.shaderQualifiers.earlyFragmentTests)
8959 intermediate.setEarlyFragmentTests();
8960
8961 const TQualifier& qualifier = publicType.qualifier;
8962
8963 switch (qualifier.storage) {
8964 case EvqUniform:
8965 if (qualifier.hasMatrix())
8966 globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
8967 if (qualifier.hasPacking())
8968 globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
8969 break;
8970 case EvqBuffer:
8971 if (qualifier.hasMatrix())
8972 globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
8973 if (qualifier.hasPacking())
8974 globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
8975 break;
8976 case EvqVaryingIn:
8977 break;
8978 case EvqVaryingOut:
8979 if (qualifier.hasStream())
8980 globalOutputDefaults.layoutStream = qualifier.layoutStream;
8981 if (qualifier.hasXfbBuffer())
8982 globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer;
8983 if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) {
8984 if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride))
John Kessenich2ceec682017-07-28 16:20:13 -06008985 error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d",
8986 qualifier.layoutXfbBuffer);
John Kesseniche01a9bc2016-03-12 20:11:22 -07008987 }
8988 break;
8989 default:
8990 error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
8991 return;
8992 }
8993}
8994
8995//
8996// Take the sequence of statements that has been built up since the last case/default,
8997// put it on the list of top-level nodes for the current (inner-most) switch statement,
8998// and follow that by the case/default we are on now. (See switch topology comment on
8999// TIntermSwitch.)
9000//
9001void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
9002{
9003 TIntermSequence* switchSequence = switchSequenceStack.back();
9004
9005 if (statements) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07009006 statements->setOperator(EOpSequence);
9007 switchSequence->push_back(statements);
9008 }
9009 if (branchNode) {
9010 // check all previous cases for the same label (or both are 'default')
9011 for (unsigned int s = 0; s < switchSequence->size(); ++s) {
9012 TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
9013 if (prevBranch) {
9014 TIntermTyped* prevExpression = prevBranch->getExpression();
9015 TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
9016 if (prevExpression == nullptr && newExpression == nullptr)
9017 error(branchNode->getLoc(), "duplicate label", "default", "");
9018 else if (prevExpression != nullptr &&
9019 newExpression != nullptr &&
9020 prevExpression->getAsConstantUnion() &&
9021 newExpression->getAsConstantUnion() &&
9022 prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() ==
9023 newExpression->getAsConstantUnion()->getConstArray()[0].getIConst())
9024 error(branchNode->getLoc(), "duplicated value", "case", "");
9025 }
9026 }
9027 switchSequence->push_back(branchNode);
9028 }
9029}
9030
9031//
9032// Turn the top-level node sequence built up of wrapupSwitchSubsequence
9033// into a switch node.
9034//
John Kessenich2ceec682017-07-28 16:20:13 -06009035TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression,
John Kesseniche18fd202018-01-30 11:01:39 -07009036 TIntermAggregate* lastStatements, const TAttributes& attributes)
John Kesseniche01a9bc2016-03-12 20:11:22 -07009037{
9038 wrapupSwitchSubsequence(lastStatements, nullptr);
9039
9040 if (expression == nullptr ||
9041 (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) ||
9042 expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector())
9043 error(loc, "condition must be a scalar integer expression", "switch", "");
9044
9045 // If there is nothing to do, drop the switch but still execute the expression
9046 TIntermSequence* switchSequence = switchSequenceStack.back();
9047 if (switchSequence->size() == 0)
9048 return expression;
9049
9050 if (lastStatements == nullptr) {
9051 // emulate a break for error recovery
9052 lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc));
9053 lastStatements->setOperator(EOpSequence);
9054 switchSequence->push_back(lastStatements);
9055 }
9056
9057 TIntermAggregate* body = new TIntermAggregate(EOpSequence);
9058 body->getSequence() = *switchSequenceStack.back();
9059 body->setLoc(loc);
9060
9061 TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
9062 switchNode->setLoc(loc);
John Kesseniche18fd202018-01-30 11:01:39 -07009063 handleSwitchAttributes(loc, switchNode, attributes);
John Kesseniche01a9bc2016-03-12 20:11:22 -07009064
9065 return switchNode;
9066}
9067
John Kessenich37789792017-03-21 23:56:40 -06009068// Make a new symbol-table level that is made out of the members of a structure.
9069// This should be done as an anonymous struct (name is "") so that the symbol table
John Kessenich0a2a0cd2017-05-16 23:16:26 -06009070// finds the members with no explicit reference to a 'this' variable.
9071void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector<TFunctionDeclarator>& functionDeclarators)
John Kessenich37789792017-03-21 23:56:40 -06009072{
John Kessenich0a2a0cd2017-05-16 23:16:26 -06009073 // member variables
John Kessenich37789792017-03-21 23:56:40 -06009074 TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct);
9075 symbolTable.pushThis(thisVariable);
John Kessenich0a2a0cd2017-05-16 23:16:26 -06009076
9077 // member functions
9078 for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) {
9079 // member should have a prefix matching currentTypePrefix.back()
9080 // but, symbol lookup within the class scope will just use the
9081 // unprefixed name. Hence, there are two: one fully prefixed and
9082 // one with no prefix.
9083 TFunction& member = *it->function->clone();
9084 member.removePrefix(currentTypePrefix.back());
9085 symbolTable.insert(member);
9086 }
John Kessenich37789792017-03-21 23:56:40 -06009087}
9088
John Kessenichf3d88bd2017-03-19 12:24:29 -06009089// Track levels of class/struct/namespace nesting with a prefix string using
John Kessenich54ee28f2017-03-11 14:13:00 -07009090// the type names separated by the scoping operator. E.g., two levels
9091// would look like:
9092//
9093// outer::inner
9094//
9095// The string is empty when at normal global level.
9096//
John Kessenichf3d88bd2017-03-19 12:24:29 -06009097void HlslParseContext::pushNamespace(const TString& typeName)
John Kessenich54ee28f2017-03-11 14:13:00 -07009098{
9099 // make new type prefix
9100 TString newPrefix;
John Kessenich0a2a0cd2017-05-16 23:16:26 -06009101 if (currentTypePrefix.size() > 0)
John Kessenich54ee28f2017-03-11 14:13:00 -07009102 newPrefix = currentTypePrefix.back();
John Kessenich54ee28f2017-03-11 14:13:00 -07009103 newPrefix.append(typeName);
John Kessenich0a2a0cd2017-05-16 23:16:26 -06009104 newPrefix.append(scopeMangler);
John Kessenich54ee28f2017-03-11 14:13:00 -07009105 currentTypePrefix.push_back(newPrefix);
9106}
9107
John Kessenichf3d88bd2017-03-19 12:24:29 -06009108// Opposite of pushNamespace(), see above
9109void HlslParseContext::popNamespace()
John Kessenich54ee28f2017-03-11 14:13:00 -07009110{
9111 currentTypePrefix.pop_back();
9112}
9113
9114// Use the class/struct nesting string to create a global name for
John Kessenichf3d88bd2017-03-19 12:24:29 -06009115// a member of a class/struct.
John Kessenich9855bda2017-09-11 21:48:19 -06009116void HlslParseContext::getFullNamespaceName(TString*& name) const
John Kessenich54ee28f2017-03-11 14:13:00 -07009117{
John Kessenich4dc835c2017-03-28 23:43:10 -06009118 if (currentTypePrefix.size() == 0)
9119 return;
John Kessenich54ee28f2017-03-11 14:13:00 -07009120
John Kessenich4dc835c2017-03-28 23:43:10 -06009121 TString* fullName = NewPoolTString(currentTypePrefix.back().c_str());
John Kessenich4dc835c2017-03-28 23:43:10 -06009122 fullName->append(*name);
9123 name = fullName;
John Kessenich54ee28f2017-03-11 14:13:00 -07009124}
9125
John Kessenichf3d88bd2017-03-19 12:24:29 -06009126// Helper function to add the namespace scope mangling syntax to a string.
9127void HlslParseContext::addScopeMangler(TString& name)
9128{
9129 name.append(scopeMangler);
9130}
9131
John Kessenichbf472862017-02-05 20:27:30 -07009132// Return true if this has uniform-interface like decorations.
9133bool HlslParseContext::hasUniform(const TQualifier& qualifier) const
9134{
9135 return qualifier.hasUniformLayout() ||
9136 qualifier.layoutPushConstant;
9137}
9138
9139// Potentially not the opposite of hasUniform(), as if some characteristic is
9140// ever used for more than one thing (e.g., uniform or input), hasUniform() should
9141// say it exists, but clearUniform() should leave it in place.
9142void HlslParseContext::clearUniform(TQualifier& qualifier)
9143{
9144 qualifier.clearUniformLayout();
9145 qualifier.layoutPushConstant = false;
9146}
9147
9148// Return false if builtIn by itself doesn't force this qualifier to be an input qualifier.
9149bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const
9150{
9151 switch (qualifier.builtIn) {
9152 case EbvPosition:
9153 case EbvPointSize:
9154 return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment;
9155 case EbvClipDistance:
9156 case EbvCullDistance:
9157 return language != EShLangVertex && language != EShLangCompute;
9158 case EbvFragCoord:
9159 case EbvFace:
9160 case EbvHelperInvocation:
9161 case EbvLayer:
9162 case EbvPointCoord:
9163 case EbvSampleId:
9164 case EbvSampleMask:
9165 case EbvSamplePosition:
9166 case EbvViewportIndex:
9167 return language == EShLangFragment;
9168 case EbvGlobalInvocationId:
9169 case EbvLocalInvocationIndex:
9170 case EbvLocalInvocationId:
9171 case EbvNumWorkGroups:
9172 case EbvWorkGroupId:
9173 case EbvWorkGroupSize:
9174 return language == EShLangCompute;
9175 case EbvInvocationId:
9176 return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry;
9177 case EbvPatchVertices:
9178 return language == EShLangTessControl || language == EShLangTessEvaluation;
9179 case EbvInstanceId:
9180 case EbvInstanceIndex:
9181 case EbvVertexId:
9182 case EbvVertexIndex:
9183 return language == EShLangVertex;
9184 case EbvPrimitiveId:
LoopDawg280c75c2017-12-08 12:01:16 -07009185 return language == EShLangGeometry || language == EShLangFragment || language == EShLangTessControl;
John Kessenichbf472862017-02-05 20:27:30 -07009186 case EbvTessLevelInner:
9187 case EbvTessLevelOuter:
9188 return language == EShLangTessEvaluation;
steve-lunarg7afe1342017-03-18 22:24:14 -06009189 case EbvTessCoord:
9190 return language == EShLangTessEvaluation;
John Kessenichbf472862017-02-05 20:27:30 -07009191 default:
9192 return false;
9193 }
9194}
9195
John Kessenich65ee2302017-02-06 18:44:52 -07009196// Return true if there are decorations to preserve for input-like storage.
John Kessenichbf472862017-02-05 20:27:30 -07009197bool HlslParseContext::hasInput(const TQualifier& qualifier) const
9198{
9199 if (qualifier.hasAnyLocation())
9200 return true;
9201
John Kessenich65ee2302017-02-06 18:44:52 -07009202 if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample))
9203 return true;
9204
9205 if (language == EShLangTessEvaluation && qualifier.patch)
John Kessenichbf472862017-02-05 20:27:30 -07009206 return true;
9207
9208 if (isInputBuiltIn(qualifier))
9209 return true;
9210
9211 return false;
9212}
9213
9214// Return false if builtIn by itself doesn't force this qualifier to be an output qualifier.
9215bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const
9216{
9217 switch (qualifier.builtIn) {
9218 case EbvPosition:
9219 case EbvPointSize:
9220 case EbvClipVertex:
9221 case EbvClipDistance:
9222 case EbvCullDistance:
9223 return language != EShLangFragment && language != EShLangCompute;
9224 case EbvFragDepth:
John Kessenich65ee2302017-02-06 18:44:52 -07009225 case EbvFragDepthGreater:
9226 case EbvFragDepthLesser:
John Kessenichbf472862017-02-05 20:27:30 -07009227 case EbvSampleMask:
9228 return language == EShLangFragment;
9229 case EbvLayer:
9230 case EbvViewportIndex:
Arseny Kapoulkinec92860e2017-12-13 16:08:20 -08009231 return language == EShLangGeometry || language == EShLangVertex;
John Kessenichbf472862017-02-05 20:27:30 -07009232 case EbvPrimitiveId:
John Kessenich87982be2017-12-08 19:41:05 -07009233 return language == EShLangGeometry;
John Kessenichbf472862017-02-05 20:27:30 -07009234 case EbvTessLevelInner:
9235 case EbvTessLevelOuter:
9236 return language == EShLangTessControl;
9237 default:
9238 return false;
9239 }
9240}
9241
John Kessenich65ee2302017-02-06 18:44:52 -07009242// Return true if there are decorations to preserve for output-like storage.
John Kessenichbf472862017-02-05 20:27:30 -07009243bool HlslParseContext::hasOutput(const TQualifier& qualifier) const
9244{
9245 if (qualifier.hasAnyLocation())
9246 return true;
9247
John Kessenich65ee2302017-02-06 18:44:52 -07009248 if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb())
9249 return true;
9250
9251 if (language == EShLangTessControl && qualifier.patch)
John Kessenichbf472862017-02-05 20:27:30 -07009252 return true;
9253
9254 if (language == EShLangGeometry && qualifier.hasStream())
9255 return true;
9256
9257 if (isOutputBuiltIn(qualifier))
9258 return true;
9259
9260 return false;
9261}
9262
9263// Make the IO decorations etc. be appropriate only for an input interface.
9264void HlslParseContext::correctInput(TQualifier& qualifier)
9265{
9266 clearUniform(qualifier);
9267 if (language == EShLangVertex)
9268 qualifier.clearInterstage();
John Kessenich65ee2302017-02-06 18:44:52 -07009269 if (language != EShLangTessEvaluation)
9270 qualifier.patch = false;
9271 if (language != EShLangFragment) {
9272 qualifier.clearInterpolation();
9273 qualifier.sample = false;
9274 }
9275
John Kessenichbf472862017-02-05 20:27:30 -07009276 qualifier.clearStreamLayout();
9277 qualifier.clearXfbLayout();
9278
9279 if (! isInputBuiltIn(qualifier))
9280 qualifier.builtIn = EbvNone;
9281}
9282
9283// Make the IO decorations etc. be appropriate only for an output interface.
9284void HlslParseContext::correctOutput(TQualifier& qualifier)
9285{
9286 clearUniform(qualifier);
9287 if (language == EShLangFragment)
9288 qualifier.clearInterstage();
9289 if (language != EShLangGeometry)
9290 qualifier.clearStreamLayout();
9291 if (language == EShLangFragment)
9292 qualifier.clearXfbLayout();
John Kessenich65ee2302017-02-06 18:44:52 -07009293 if (language != EShLangTessControl)
9294 qualifier.patch = false;
John Kessenichbf472862017-02-05 20:27:30 -07009295
9296 switch (qualifier.builtIn) {
9297 case EbvFragDepthGreater:
9298 intermediate.setDepth(EldGreater);
9299 qualifier.builtIn = EbvFragDepth;
9300 break;
9301 case EbvFragDepthLesser:
9302 intermediate.setDepth(EldLess);
9303 qualifier.builtIn = EbvFragDepth;
9304 break;
9305 default:
9306 break;
9307 }
9308
9309 if (! isOutputBuiltIn(qualifier))
9310 qualifier.builtIn = EbvNone;
9311}
9312
9313// Make the IO decorations etc. be appropriate only for uniform type interfaces.
9314void HlslParseContext::correctUniform(TQualifier& qualifier)
9315{
steve-lunarga4bfed12017-04-23 19:44:28 -06009316 if (qualifier.declaredBuiltIn == EbvNone)
9317 qualifier.declaredBuiltIn = qualifier.builtIn;
9318
John Kessenichbf472862017-02-05 20:27:30 -07009319 qualifier.builtIn = EbvNone;
9320 qualifier.clearInterstage();
9321 qualifier.clearInterstageLayout();
9322}
9323
9324// Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface.
9325void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier)
9326{
9327 clearUniform(qualifier);
9328 correctUniform(qualifier);
9329}
9330
steve-lunarge752f462017-03-22 18:39:25 -06009331
LoopDawg5ee05892017-07-31 13:41:42 -06009332// Set texture return type. Returns success (not all types are valid).
9333bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc)
9334{
9335 // Seed the output with an invalid index. We will set it to a valid one if we can.
9336 sampler.structReturnIndex = TSampler::noReturnStruct;
9337
9338 // Arrays aren't supported.
9339 if (retType.isArray()) {
9340 error(loc, "Arrays not supported in texture template types", "", "");
9341 return false;
9342 }
9343
9344 // If return type is a vector, remember the vector size in the sampler, and return.
9345 if (retType.isVector() || retType.isScalar()) {
9346 sampler.vectorSize = retType.getVectorSize();
9347 return true;
9348 }
9349
9350 // If it wasn't a vector, it must be a struct meeting certain requirements. The requirements
9351 // are checked below: just check for struct-ness here.
9352 if (!retType.isStruct()) {
9353 error(loc, "Invalid texture template type", "", "");
9354 return false;
9355 }
9356
LoopDawg7f93d562017-09-27 09:04:43 -06009357 // TODO: Subpass doesn't handle struct returns, due to some oddities with fn overloading.
9358 if (sampler.isSubpass()) {
9359 error(loc, "Unimplemented: structure template type in subpass input", "", "");
9360 return false;
9361 }
9362
LoopDawg5ee05892017-07-31 13:41:42 -06009363 TTypeList* members = retType.getWritableStruct();
9364
9365 // Check for too many or not enough structure members.
9366 if (members->size() > 4 || members->size() == 0) {
9367 error(loc, "Invalid member count in texture template structure", "", "");
9368 return false;
9369 }
9370
9371 // Error checking: We must have <= 4 total components, all of the same basic type.
9372 unsigned totalComponents = 0;
9373 for (unsigned m = 0; m < members->size(); ++m) {
9374 // Check for bad member types
9375 if (!(*members)[m].type->isScalar() && !(*members)[m].type->isVector()) {
9376 error(loc, "Invalid texture template struct member type", "", "");
9377 return false;
9378 }
9379
9380 const unsigned memberVectorSize = (*members)[m].type->getVectorSize();
9381 totalComponents += memberVectorSize;
9382
9383 // too many total member components
9384 if (totalComponents > 4) {
9385 error(loc, "Too many components in texture template structure type", "", "");
9386 return false;
9387 }
9388
9389 // All members must be of a common basic type
9390 if ((*members)[m].type->getBasicType() != (*members)[0].type->getBasicType()) {
9391 error(loc, "Texture template structure members must same basic type", "", "");
9392 return false;
9393 }
9394 }
9395
9396 // If the structure in the return type already exists in the table, we'll use it. Otherwise, we'll make
9397 // a new entry. This is a linear search, but it hardly ever happens, and the list cannot be very large.
9398 for (unsigned int idx = 0; idx < textureReturnStruct.size(); ++idx) {
9399 if (textureReturnStruct[idx] == members) {
9400 sampler.structReturnIndex = idx;
9401 return true;
9402 }
9403 }
9404
9405 // It wasn't found as an existing entry. See if we have room for a new one.
9406 if (textureReturnStruct.size() >= TSampler::structReturnSlots) {
9407 error(loc, "Texture template struct return slots exceeded", "", "");
9408 return false;
9409 }
9410
9411 // Insert it in the vector that tracks struct return types.
9412 sampler.structReturnIndex = unsigned(textureReturnStruct.size());
9413 textureReturnStruct.push_back(members);
9414
9415 // Success!
9416 return true;
9417}
9418
9419// Return the sampler return type in retType.
9420void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const
9421{
9422 if (sampler.hasReturnStruct()) {
9423 assert(textureReturnStruct.size() >= sampler.structReturnIndex);
9424
9425 // We land here if the texture return is a structure.
9426 TTypeList* blockStruct = textureReturnStruct[sampler.structReturnIndex];
9427
9428 const TType resultType(blockStruct, "");
9429 retType.shallowCopy(resultType);
9430 } else {
9431 // We land here if the texture return is a vector or scalar.
9432 const TType resultType(sampler.type, EvqTemporary, sampler.getVectorSize());
9433 retType.shallowCopy(resultType);
9434 }
9435}
9436
9437
John Kessenich2b4f77f2017-08-04 13:51:54 -06009438// Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type
9439TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const
steve-lunarge752f462017-03-22 18:39:25 -06009440{
John Kessenich2b4f77f2017-08-04 13:51:54 -06009441 const auto it = builtInTessLinkageSymbols.find(biType);
9442 if (it == builtInTessLinkageSymbols.end()) // if it wasn't declared by the user, return nullptr
steve-lunarge752f462017-03-22 18:39:25 -06009443 return nullptr;
9444
9445 return intermediate.addSymbol(*it->second->getAsVariable());
9446}
9447
LoopDawg4a145db2017-09-13 08:44:39 -06009448// Find the patch constant function (issues error, returns nullptr if not found)
9449const TFunction* HlslParseContext::findPatchConstantFunction(const TSourceLoc& loc)
steve-lunarg858c9282017-01-07 08:54:10 -07009450{
steve-lunarg858c9282017-01-07 08:54:10 -07009451 if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) {
9452 error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), "");
LoopDawg4a145db2017-09-13 08:44:39 -06009453 return nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07009454 }
9455
9456 const TString mangledName = patchConstantFunctionName + "(";
9457
9458 // create list of PCF candidates
9459 TVector<const TFunction*> candidateList;
9460 bool builtIn;
9461 symbolTable.findFunctionNameList(mangledName, candidateList, builtIn);
9462
9463 // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not
9464 // allow any disambiguation of overloads.
9465 if (candidateList.empty()) {
9466 error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), "");
LoopDawg4a145db2017-09-13 08:44:39 -06009467 return nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07009468 }
9469
9470 // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions,
9471 // HLSL picks the last one in shader source order. Since that isn't yet implemented here, error
9472 // out if there is more than one candidate.
9473 if (candidateList.size() > 1) {
9474 error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), "");
LoopDawg4a145db2017-09-13 08:44:39 -06009475 return nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07009476 }
9477
LoopDawg4a145db2017-09-13 08:44:39 -06009478 return candidateList[0];
9479}
9480
9481// Finalization step: Add patch constant function invocation
9482void HlslParseContext::addPatchConstantInvocation()
9483{
9484 TSourceLoc loc;
9485 loc.init();
9486
9487 // If there's no patch constant function, or we're not a HS, do nothing.
9488 if (patchConstantFunctionName.empty() || language != EShLangTessControl)
9489 return;
9490
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009491 // Look for built-in variables in a function's parameter list.
steve-lunarg858c9282017-01-07 08:54:10 -07009492 const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) {
9493 for (int p=0; p<function.getParamCount(); ++p) {
steve-lunarg067eb9b2017-04-01 15:34:48 -06009494 TStorageQualifier storage = function[p].type->getQualifier().storage;
9495
9496 if (storage == EvqConstReadOnly) // treated identically to input
9497 storage = EvqIn;
steve-lunarg858c9282017-01-07 08:54:10 -07009498
steve-lunarga4bfed12017-04-23 19:44:28 -06009499 if (function[p].getDeclaredBuiltIn() != EbvNone)
9500 builtIns.insert(HlslParseContext::tInterstageIoData(function[p].getDeclaredBuiltIn(), storage));
steve-lunarg858c9282017-01-07 08:54:10 -07009501 else
baldurk5d5db802017-03-09 17:48:59 +00009502 builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage));
steve-lunarg858c9282017-01-07 08:54:10 -07009503 }
9504 };
9505
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009506 // If we synthesize a built-in interface variable, we must add it to the linkage.
steve-lunarg858c9282017-01-07 08:54:10 -07009507 const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) {
9508 if (name == nullptr) {
9509 error(loc, "unable to locate patch function parameter name", "", "");
9510 return;
9511 } else {
9512 TVariable& variable = *new TVariable(name, type);
9513 if (! symbolTable.insert(variable)) {
9514 error(loc, "unable to declare patch constant function interface variable", name->c_str(), "");
9515 return;
9516 }
9517
9518 globalQualifierFix(loc, variable.getWritableType().getQualifier());
9519
9520 if (symbolNode != nullptr)
9521 *symbolNode = intermediate.addSymbol(variable);
9522
9523 trackLinkage(variable);
9524 }
9525 };
9526
mchock-nv933c10c2017-09-11 15:20:52 -07009527 const auto isOutputPatch = [](TFunction& patchConstantFunction, int param) {
steve-lunarg067eb9b2017-04-01 15:34:48 -06009528 const TType& type = *patchConstantFunction[param].type;
steve-lunarga4bfed12017-04-23 19:44:28 -06009529 const TBuiltInVariable biType = patchConstantFunction[param].getDeclaredBuiltIn();
steve-lunarg067eb9b2017-04-01 15:34:48 -06009530
9531 return type.isArray() && !type.isRuntimeSizedArray() && biType == EbvOutputPatch;
steve-lunarg858c9282017-01-07 08:54:10 -07009532 };
9533
9534 // We will perform these steps. Each is in a scoped block for separation: they could
9535 // become separate functions to make addPatchConstantInvocation shorter.
9536 //
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009537 // 1. Union the interfaces, and create built-ins for anything present in the PCF and
9538 // declared as a built-in variable that isn't present in the entry point's signature.
steve-lunarg858c9282017-01-07 08:54:10 -07009539 //
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009540 // 2. Synthesizes a call to the patchconstfunction using built-in variables from either main,
9541 // or the ones we created. Matching is based on built-in type. We may use synthesized
steve-lunarg858c9282017-01-07 08:54:10 -07009542 // variables from (1) above.
steve-lunarg9cee73e2017-03-14 17:37:10 -06009543 //
9544 // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them.
steve-lunarg858c9282017-01-07 08:54:10 -07009545 //
9546 // 3. Create a return sequence: copy the return value (if any) from the PCF to a
9547 // (non-sanitized) output variable. In case this may involve multiple copies, such as for
9548 // an arrayed variable, a temporary copy of the PCF output is created to avoid multiple
9549 // indirections into a complex R-value coming from the call to the PCF.
steve-lunarg9cee73e2017-03-14 17:37:10 -06009550 //
9551 // 4. Create a barrier.
9552 //
9553 // 5/5B. Call the PCF inside an if test for (invocation id == 0).
steve-lunarg858c9282017-01-07 08:54:10 -07009554
LoopDawg4a145db2017-09-13 08:44:39 -06009555 TFunction* patchConstantFunctionPtr = const_cast<TFunction*>(findPatchConstantFunction(loc));
9556
9557 if (patchConstantFunctionPtr == nullptr)
9558 return;
9559
9560 TFunction& patchConstantFunction = *patchConstantFunctionPtr;
9561
steve-lunarg858c9282017-01-07 08:54:10 -07009562 const int pcfParamCount = patchConstantFunction.getParamCount();
John Kessenich2b4f77f2017-08-04 13:51:54 -06009563 TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
steve-lunarg858c9282017-01-07 08:54:10 -07009564 TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence();
9565
steve-lunarg067eb9b2017-04-01 15:34:48 -06009566 int outPatchParam = -1; // -1 means there isn't one.
steve-lunarg9cee73e2017-03-14 17:37:10 -06009567
steve-lunarg858c9282017-01-07 08:54:10 -07009568 // ================ Step 1A: Union Interfaces ================
9569 // Our patch constant function.
9570 {
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009571 std::set<tInterstageIoData> pcfBuiltIns; // patch constant function built-ins
9572 std::set<tInterstageIoData> epfBuiltIns; // entry point function built-ins
steve-lunarg858c9282017-01-07 08:54:10 -07009573
9574 assert(entryPointFunction);
9575 assert(entryPointFunctionBody);
9576
9577 findBuiltIns(patchConstantFunction, pcfBuiltIns);
9578 findBuiltIns(*entryPointFunction, epfBuiltIns);
9579
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009580 // Find the set of built-ins in the PCF that are not present in the entry point.
steve-lunarg858c9282017-01-07 08:54:10 -07009581 std::set<tInterstageIoData> notInEntryPoint;
9582
9583 notInEntryPoint = pcfBuiltIns;
9584
baldurk5d5db802017-03-09 17:48:59 +00009585 // std::set_difference not usable on unordered containers
9586 for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi)
9587 notInEntryPoint.erase(*bi);
steve-lunarg858c9282017-01-07 08:54:10 -07009588
9589 // Now we'll add those to the entry and to the linkage.
9590 for (int p=0; p<pcfParamCount; ++p) {
steve-lunarga4bfed12017-04-23 19:44:28 -06009591 const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn();
steve-lunarg067eb9b2017-04-01 15:34:48 -06009592 TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage;
steve-lunarg858c9282017-01-07 08:54:10 -07009593
steve-lunarg067eb9b2017-04-01 15:34:48 -06009594 // Track whether there is an output patch param
9595 if (isOutputPatch(patchConstantFunction, p)) {
9596 if (outPatchParam >= 0) {
9597 // Presently we only support one per ctrl pt input.
9598 error(loc, "unimplemented: multiple output patches in patch constant function", "", "");
steve-lunarg9cee73e2017-03-14 17:37:10 -06009599 return;
9600 }
steve-lunarg067eb9b2017-04-01 15:34:48 -06009601 outPatchParam = p;
steve-lunarg9cee73e2017-03-14 17:37:10 -06009602 }
steve-lunarg858c9282017-01-07 08:54:10 -07009603
steve-lunarg9cee73e2017-03-14 17:37:10 -06009604 if (biType != EbvNone) {
9605 TType* paramType = patchConstantFunction[p].type->clone();
steve-lunarg9cee73e2017-03-14 17:37:10 -06009606
steve-lunarg067eb9b2017-04-01 15:34:48 -06009607 if (storage == EvqConstReadOnly) // treated identically to input
9608 storage = EvqIn;
9609
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009610 // Presently, the only non-built-in we support is InputPatch, which is treated as
9611 // a pseudo-built-in.
steve-lunarg067eb9b2017-04-01 15:34:48 -06009612 if (biType == EbvInputPatch) {
John Kessenich2b4f77f2017-08-04 13:51:54 -06009613 builtInTessLinkageSymbols[biType] = inputPatch;
steve-lunarg067eb9b2017-04-01 15:34:48 -06009614 } else if (biType == EbvOutputPatch) {
9615 // Nothing...
9616 } else {
9617 // Use the original declaration type for the linkage
9618 paramType->getQualifier().builtIn = biType;
9619
9620 if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1)
9621 addToLinkage(*paramType, patchConstantFunction[p].name, nullptr);
9622 }
steve-lunarg9cee73e2017-03-14 17:37:10 -06009623 }
steve-lunarg858c9282017-01-07 08:54:10 -07009624 }
9625
9626 // If we didn't find it because the shader made one, add our own.
9627 if (invocationIdSym == nullptr) {
9628 TType invocationIdType(EbtUint, EvqIn, 1);
9629 TString* invocationIdName = NewPoolTString("InvocationId");
9630 invocationIdType.getQualifier().builtIn = EbvInvocationId;
9631 addToLinkage(invocationIdType, invocationIdName, &invocationIdSym);
9632 }
9633
9634 assert(invocationIdSym);
9635 }
9636
9637 TIntermTyped* pcfArguments = nullptr;
steve-lunarg9cee73e2017-03-14 17:37:10 -06009638 TVariable* perCtrlPtVar = nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07009639
9640 // ================ Step 1B: Argument synthesis ================
9641 // Create pcfArguments for synthesis of patchconstantfunction invocation
steve-lunarg858c9282017-01-07 08:54:10 -07009642 {
9643 for (int p=0; p<pcfParamCount; ++p) {
LoopDawg4a145db2017-09-13 08:44:39 -06009644 TIntermTyped* inputArg = nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07009645
steve-lunarg067eb9b2017-04-01 15:34:48 -06009646 if (p == outPatchParam) {
steve-lunarg9cee73e2017-03-14 17:37:10 -06009647 if (perCtrlPtVar == nullptr) {
steve-lunarg067eb9b2017-04-01 15:34:48 -06009648 perCtrlPtVar = makeInternalVariable(*patchConstantFunction[outPatchParam].name,
9649 *patchConstantFunction[outPatchParam].type);
steve-lunarg9cee73e2017-03-14 17:37:10 -06009650
9651 perCtrlPtVar->getWritableType().getQualifier().makeTemporary();
9652 }
9653 inputArg = intermediate.addSymbol(*perCtrlPtVar, loc);
9654 } else {
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009655 // find which built-in it is
steve-lunarga4bfed12017-04-23 19:44:28 -06009656 const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn();
steve-lunarg9cee73e2017-03-14 17:37:10 -06009657
LoopDawg4a145db2017-09-13 08:44:39 -06009658 if (biType == EbvInputPatch && inputPatch == nullptr) {
9659 error(loc, "unimplemented: PCF input patch without entry point input patch parameter", "", "");
9660 return;
9661 }
9662
John Kessenich2b4f77f2017-08-04 13:51:54 -06009663 inputArg = findTessLinkageSymbol(biType);
steve-lunarg067eb9b2017-04-01 15:34:48 -06009664
steve-lunarg9cee73e2017-03-14 17:37:10 -06009665 if (inputArg == nullptr) {
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009666 error(loc, "unable to find patch constant function built-in variable", "", "");
steve-lunarg9cee73e2017-03-14 17:37:10 -06009667 return;
9668 }
steve-lunarg858c9282017-01-07 08:54:10 -07009669 }
9670
9671 if (pcfParamCount == 1)
steve-lunarg9cee73e2017-03-14 17:37:10 -06009672 pcfArguments = inputArg;
steve-lunarg858c9282017-01-07 08:54:10 -07009673 else
steve-lunarg9cee73e2017-03-14 17:37:10 -06009674 pcfArguments = intermediate.growAggregate(pcfArguments, inputArg);
steve-lunarg858c9282017-01-07 08:54:10 -07009675 }
9676 }
9677
9678 // ================ Step 2: Synthesize call to PCF ================
steve-lunarg9cee73e2017-03-14 17:37:10 -06009679 TIntermAggregate* pcfCallSequence = nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07009680 TIntermTyped* pcfCall = nullptr;
9681
9682 {
9683 // Create a function call to the patchconstantfunction
9684 if (pcfArguments)
9685 addInputArgumentConversions(patchConstantFunction, pcfArguments);
9686
9687 // Synthetic call.
9688 pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc);
9689 pcfCall->getAsAggregate()->setUserDefined();
9690 pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName());
steve-lunarg9cee73e2017-03-14 17:37:10 -06009691 intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(),
9692 patchConstantFunction.getMangledName());
steve-lunarg858c9282017-01-07 08:54:10 -07009693
9694 if (pcfCall->getAsAggregate()) {
9695 TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList();
9696 for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) {
9697 TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage;
9698 qualifierList.push_back(qual);
9699 }
9700 pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator());
9701 }
9702 }
9703
steve-lunarg9cee73e2017-03-14 17:37:10 -06009704 // ================ Step 2B: Per Control Point synthesis ================
9705 // If there is per control point data, we must either emulate that with multiple
9706 // invocations of the entry point to build up an array, or (TODO:) use a yet
9707 // unavailable extension to look across the SIMD lanes. This is the former
9708 // as a placeholder for the latter.
steve-lunarg067eb9b2017-04-01 15:34:48 -06009709 if (outPatchParam >= 0) {
steve-lunarg9cee73e2017-03-14 17:37:10 -06009710 // We must introduce a local temp variable of the type wanted by the PCF input.
steve-lunarg067eb9b2017-04-01 15:34:48 -06009711 const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize();
steve-lunarg9cee73e2017-03-14 17:37:10 -06009712
9713 if (entryPointFunction->getType().getBasicType() == EbtVoid) {
9714 error(loc, "entry point must return a value for use with patch constant function", "", "");
9715 return;
9716 }
9717
9718 // Create calls to wrapped main to fill in the array. We will substitute fixed values
9719 // of invocation ID when calling the wrapped main.
9720
9721 // This is the type of the each member of the per ctrl point array.
9722 const TType derefType(perCtrlPtVar->getType(), 0);
9723
9724 for (int cpt = 0; cpt < arraySize; ++cpt) {
9725 // TODO: improve. substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab
9726 // for this function.
9727 const TString origName = entryPointFunction->getName().substr(1);
9728 TFunction callee(&origName, TType(EbtVoid));
9729 TIntermTyped* callingArgs = nullptr;
9730
9731 for (int i = 0; i < entryPointFunction->getParamCount(); i++) {
9732 TParameter& param = (*entryPointFunction)[i];
9733 TType& paramType = *param.type;
9734
9735 if (paramType.getQualifier().isParamOutput()) {
9736 error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", "");
9737 return;
9738 }
9739
9740 if (paramType.getQualifier().isParamInput()) {
9741 TIntermTyped* arg = nullptr;
steve-lunarga4bfed12017-04-23 19:44:28 -06009742 if ((*entryPointFunction)[i].getDeclaredBuiltIn() == EbvInvocationId) {
steve-lunarg9cee73e2017-03-14 17:37:10 -06009743 // substitute invocation ID with the array element ID
9744 arg = intermediate.addConstantUnion(cpt, loc);
9745 } else {
9746 TVariable* argVar = makeInternalVariable(*param.name, *param.type);
9747 argVar->getWritableType().getQualifier().makeTemporary();
9748 arg = intermediate.addSymbol(*argVar);
9749 }
9750
9751 handleFunctionArgument(&callee, callingArgs, arg);
9752 }
9753 }
9754
9755 // Call and assign to per ctrl point variable
9756 currentCaller = intermediate.getEntryPointMangledName().c_str();
9757 TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
9758 TIntermTyped* index = intermediate.addConstantUnion(cpt, loc);
9759 TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc);
9760 TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc);
9761 element->setType(derefType);
9762 element->setLoc(loc);
9763
9764 pcfCallSequence = intermediate.growAggregate(pcfCallSequence,
9765 handleAssign(loc, EOpAssign, element, callReturn));
9766 }
9767 }
9768
steve-lunarg858c9282017-01-07 08:54:10 -07009769 // ================ Step 3: Create return Sequence ================
9770 // Return sequence: copy PCF result to a temporary, then to shader output variable.
9771 if (pcfCall->getBasicType() != EbtVoid) {
9772 const TType* retType = &patchConstantFunction.getType(); // return type from the PCF
9773 TType outType; // output type that goes with the return type.
9774 outType.shallowCopy(*retType);
9775
9776 // substitute the output type
9777 const auto newLists = ioTypeMap.find(retType->getStruct());
9778 if (newLists != ioTypeMap.end())
9779 outType.setStruct(newLists->second.output);
9780
John Kessenich8bcdf2e2017-07-30 16:54:02 -06009781 // Substitute the top level type's built-in type
steve-lunarg858c9282017-01-07 08:54:10 -07009782 if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone)
9783 outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType();
9784
steve-lunarg9cee73e2017-03-14 17:37:10 -06009785 outType.getQualifier().patch = true; // make it a per-patch variable
9786
steve-lunarg858c9282017-01-07 08:54:10 -07009787 TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType);
9788 pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut;
9789
John Kessenich6042eb42017-08-02 17:08:43 -06009790 if (pcfOutput->getType().containsBuiltIn())
steve-lunarg858c9282017-01-07 08:54:10 -07009791 split(*pcfOutput);
9792
John Kessenich54596ff2017-06-20 03:20:59 -06009793 assignToInterface(*pcfOutput);
steve-lunarg9cee73e2017-03-14 17:37:10 -06009794
steve-lunarg858c9282017-01-07 08:54:10 -07009795 TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc);
9796
9797 // The call to the PCF is a complex R-value: we want to store it in a temp to avoid
9798 // repeated calls to the PCF:
9799 TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType);
9800 pcfCallResult->getWritableType().getQualifier().makeTemporary();
steve-lunarg858c9282017-01-07 08:54:10 -07009801
steve-lunarg9cee73e2017-03-14 17:37:10 -06009802 TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc);
9803 TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall);
John Kessenich2ceec682017-07-28 16:20:13 -06009804 TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym,
9805 intermediate.addSymbol(*pcfCallResult, loc));
steve-lunarg858c9282017-01-07 08:54:10 -07009806
steve-lunarg9cee73e2017-03-14 17:37:10 -06009807 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign);
9808 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut);
9809 } else {
9810 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall);
steve-lunarg858c9282017-01-07 08:54:10 -07009811 }
9812
9813 // ================ Step 4: Barrier ================
9814 TIntermTyped* barrier = new TIntermAggregate(EOpBarrier);
9815 barrier->setLoc(loc);
9816 barrier->setType(TType(EbtVoid));
9817 epBodySeq.insert(epBodySeq.end(), barrier);
9818
steve-lunarg9cee73e2017-03-14 17:37:10 -06009819 // ================ Step 5: Test on invocation ID ================
steve-lunarg858c9282017-01-07 08:54:10 -07009820 TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
9821 TIntermTyped* cmp = intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool));
9822
steve-lunarg9cee73e2017-03-14 17:37:10 -06009823
9824 // ================ Step 5B: Create if statement on Invocation ID == 0 ================
9825 intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc);
9826 TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr);
steve-lunarg858c9282017-01-07 08:54:10 -07009827 invocationIdTest->setLoc(loc);
9828
9829 // add our test sequence before the return.
9830 epBodySeq.insert(epBodySeq.end(), invocationIdTest);
9831}
9832
steve-lunarg8e26feb2017-04-10 08:19:21 -06009833// Finalization step: remove unused buffer blocks from linkage (we don't know until the
9834// shader is entirely compiled)
9835void HlslParseContext::removeUnusedStructBufferCounters()
9836{
9837 const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(),
9838 [this](const TSymbol* sym) {
9839 const auto sbcIt = structBufferCounter.find(sym->getName());
9840 return sbcIt != structBufferCounter.end() && !sbcIt->second;
9841 });
9842
9843 linkageSymbols.erase(endIt, linkageSymbols.end());
9844}
9845
LoopDawg195f5842017-09-27 09:12:51 -06009846// Finalization step: patch texture shadow modes to match samplers they were combined with
9847void HlslParseContext::fixTextureShadowModes()
9848{
9849 for (auto symbol = linkageSymbols.begin(); symbol != linkageSymbols.end(); ++symbol) {
9850 TSampler& sampler = (*symbol)->getWritableType().getSampler();
9851
9852 if (sampler.isTexture()) {
LoopDawg73c57bb2017-10-05 16:25:52 -06009853 const auto shadowMode = textureShadowVariant.find((*symbol)->getUniqueId());
9854 if (shadowMode != textureShadowVariant.end()) {
9855
9856 if (shadowMode->second->overloaded())
9857 // Texture needs legalization if it's been seen with both shadow and non-shadow modes.
9858 intermediate.setNeedsLegalization();
9859
9860 sampler.shadow = shadowMode->second->isShadowId((*symbol)->getUniqueId());
9861 }
LoopDawg195f5842017-09-27 09:12:51 -06009862 }
9863 }
9864}
9865
steve-lunarga2e75312016-12-14 15:22:25 -07009866// post-processing
9867void HlslParseContext::finish()
9868{
LoopDawg726bf962017-05-12 17:14:31 -06009869 // Error check: There was a dangling .mips operator. These are not nested constructs in the grammar, so
9870 // cannot be detected there. This is not strictly needed in a non-validating parser; it's just helpful.
9871 if (! mipsOperatorMipArg.empty()) {
9872 error(mipsOperatorMipArg.back().loc, "unterminated mips operator:", "", "");
9873 }
9874
LoopDawg73c57bb2017-10-05 16:25:52 -06009875 removeUnusedStructBufferCounters();
9876 addPatchConstantInvocation();
9877 fixTextureShadowModes();
9878
John Kessenich15fa7ef2017-09-07 04:33:11 -06009879 // Communicate out (esp. for command line) that we formed AST that will make
9880 // illegal AST SPIR-V and it needs transforms to legalize it.
9881 if (intermediate.needsLegalization())
9882 infoSink.info << "WARNING: AST will form illegal SPIR-V; need to transform to legalize";
9883
steve-lunarga2e75312016-12-14 15:22:25 -07009884 TParseContextBase::finish();
9885}
9886
John Kesseniche01a9bc2016-03-12 20:11:22 -07009887} // end namespace glslang