blob: 935365c8e0cc1aba09f6cfd679f02a45c7169aaf [file] [log] [blame]
John Kesseniche01a9bc2016-03-12 20:11:22 -07001//
John Kessenich927608b2017-01-06 12:34:14 -07002// Copyright (C) 2016 Google, Inc.
3// Copyright (C) 2016 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 Kessenichb901ade2016-06-16 20:59:42 -060056 int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink,
steve-lunargf1e0c872016-10-31 15:13:43 -060057 const TString sourceEntryPointName,
John Kesseniche01a9bc2016-03-12 20:11:22 -070058 bool forwardCompatible, EShMessages messages) :
John Kessenichd3f11222016-11-05 10:15:53 -060059 TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink, forwardCompatible, messages),
John Kessenicha1e2d492016-09-20 13:22:58 -060060 contextPragma(true, false),
61 loopNestingLevel(0), annotationNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0),
John Kessenich517fe7a2016-11-26 13:31:47 -070062 postEntryPointReturn(false),
John Kesseniche01a9bc2016-03-12 20:11:22 -070063 limits(resources.limits),
steve-lunarg132d3312016-12-19 15:48:01 -070064 builtInIoIndex(nullptr),
Alex Szpakowski5f316d92017-01-08 18:21:17 -040065 builtInIoBase(nullptr),
66 nextInLocation(0), nextOutLocation(0),
steve-lunarg858c9282017-01-07 08:54:10 -070067 sourceEntryPointName(sourceEntryPointName),
68 entryPointFunction(nullptr),
steve-lunarg08e0c082017-03-29 20:01:13 -060069 entryPointFunctionBody(nullptr),
steve-lunarg067eb9b2017-04-01 15:34:48 -060070 gsStreamOutput(nullptr),
71 inputPatch(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
John Kessenichecba76f2017-01-06 00:34:48 -070084 // "Shaders in the transform
John Kesseniche01a9bc2016-03-12 20:11:22 -070085 // feedback capturing mode have an initial global default of
86 // layout(xfb_buffer = 0) out;"
87 if (language == EShLangVertex ||
88 language == EShLangTessControl ||
89 language == EShLangTessEvaluation ||
90 language == EShLangGeometry)
91 globalOutputDefaults.layoutXfbBuffer = 0;
92
93 if (language == EShLangGeometry)
94 globalOutputDefaults.layoutStream = 0;
John Kessenichd4032292016-09-09 11:43:11 -060095
96 if (spvVersion.spv == 0 || spvVersion.vulkan == 0)
97 infoSink.info << "ERROR: HLSL currently only supported when requesting SPIR-V for Vulkan.\n";
John Kesseniche01a9bc2016-03-12 20:11:22 -070098}
99
100HlslParseContext::~HlslParseContext()
101{
102}
103
LoopDawg62561462016-07-22 20:46:03 -0600104void HlslParseContext::initializeExtensionBehavior()
105{
106 TParseContextBase::initializeExtensionBehavior();
107
108 // HLSL allows #line by default.
109 extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable;
110}
111
John Kesseniche01a9bc2016-03-12 20:11:22 -0700112void HlslParseContext::setLimits(const TBuiltInResource& r)
113{
114 resources = r;
115 intermediate.setLimits(resources);
116}
117
118//
119// Parse an array of strings using the parser in HlslRules.
120//
121// Returns true for successful acceptance of the shader, false if any errors.
122//
123bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError)
124{
125 currentScanner = &input;
126 ppContext.setInput(input, versionWillBeError);
127
John Kesseniche01a9bc2016-03-12 20:11:22 -0700128 HlslScanContext scanContext(*this, ppContext);
129 HlslGrammar grammar(scanContext, *this);
John Kessenich7f702122016-09-15 22:49:31 -0600130 if (!grammar.parse()) {
John Kessenich219b0252016-08-23 17:51:13 -0600131 // Print a message formated such that if you click on the message it will take you right to
132 // the line through most UIs.
dankbakerafe6e9c2016-08-21 12:29:08 -0400133 const glslang::TSourceLoc& sourceLoc = input.getSourceLoc();
John Kessenich142785f2016-09-19 14:56:55 -0600134 infoSink.info << sourceLoc.name << "(" << sourceLoc.line << "): error at column " << sourceLoc.column << ", HLSL parsing failed.\n";
135 ++numErrors;
John Kessenich7f702122016-09-15 22:49:31 -0600136 return false;
dankbakerafe6e9c2016-08-21 12:29:08 -0400137 }
John Kessenich7f702122016-09-15 22:49:31 -0600138
John Kessenichd3f11222016-11-05 10:15:53 -0600139 finish();
140
John Kesseniche01a9bc2016-03-12 20:11:22 -0700141 return numErrors == 0;
142}
143
steve-lunarg07830e82016-10-10 10:00:14 -0600144//
145// Return true if this l-value node should be converted in some manner.
146// For instance: turning a load aggregate into a store in an l-value.
147//
steve-lunarg90707962016-10-07 19:35:40 -0600148bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const
149{
150 if (node == nullptr)
151 return false;
152
153 const TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
steve-lunargcd6829b2016-12-28 10:03:58 -0700154 const TIntermBinary* lhsAsBinary = node->getAsBinaryNode();
155
156 // If it's a swizzled/indexed aggregate, look at the left node instead.
157 if (lhsAsBinary != nullptr &&
158 (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect))
159 lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
steve-lunarg0de16da2016-10-08 10:54:52 -0600160
steve-lunarg90707962016-10-07 19:35:40 -0600161 if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad)
162 return true;
163
164 return false;
165}
166
John Kessenich4dc835c2017-03-28 23:43:10 -0600167void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* newTypeList)
John Kessenich88c44642017-02-03 14:06:36 -0700168{
John Kessenich65ee2302017-02-06 18:44:52 -0700169 newTypeList = nullptr;
John Kessenichbf472862017-02-05 20:27:30 -0700170 correctUniform(memberType.getQualifier());
John Kessenich65ee2302017-02-06 18:44:52 -0700171 if (memberType.isStruct()) {
172 auto it = ioTypeMap.find(memberType.getStruct());
173 if (it != ioTypeMap.end() && it->second.uniform)
174 newTypeList = it->second.uniform;
175 }
176 TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList);
John Kessenich88c44642017-02-03 14:06:36 -0700177}
178
steve-lunarg0de16da2016-10-08 10:54:52 -0600179//
steve-lunarg4f2da272016-10-10 15:24:57 -0600180// Return a TLayoutFormat corresponding to the given texture type.
181//
182TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType)
183{
184 const int components = txType.getVectorSize();
185
baldurkca735702016-10-28 17:57:25 +0200186 const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat {
steve-lunargcce8d482016-10-14 18:36:42 -0600187 if (intermediate.getNoStorageFormat())
188 return ElfNone;
189
steve-lunarg4f2da272016-10-10 15:24:57 -0600190 return components == 1 ? v1 :
191 components == 2 ? v2 : v4;
192 };
193
194 switch (txType.getBasicType()) {
steve-lunarg8b0227c2016-10-14 16:40:32 -0600195 case EbtFloat: return selectFormat(ElfR32f, ElfRg32f, ElfRgba32f);
196 case EbtInt: return selectFormat(ElfR32i, ElfRg32i, ElfRgba32i);
197 case EbtUint: return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui);
steve-lunarg4f2da272016-10-10 15:24:57 -0600198 default:
199 error(loc, "unknown basic type in image format", "", "");
200 return ElfNone;
201 }
202}
203
204//
steve-lunarg0de16da2016-10-08 10:54:52 -0600205// Both test and if necessary, spit out an error, to see if the node is really
206// an l-value that can be operated on this way.
207//
208// Returns true if there was an error.
209//
210bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
211{
212 if (shouldConvertLValue(node)) {
213 // if we're writing to a texture, it must be an RW form.
214
215 TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
216 TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -0700217
steve-lunarg0de16da2016-10-08 10:54:52 -0600218 if (!object->getType().getSampler().isImage()) {
219 error(loc, "operator[] on a non-RW texture must be an r-value", "", "");
220 return true;
221 }
222 }
223
224 // Let the base class check errors
225 return TParseContextBase::lValueErrorCheck(loc, op, node);
226}
steve-lunarg90707962016-10-07 19:35:40 -0600227
228//
229// This function handles l-value conversions and verifications. It uses, but is not synonymous
230// with lValueErrorCheck. That function accepts an l-value directly, while this one must be
231// given the surrounding tree - e.g, with an assignment, so we can convert the assign into a
232// series of other image operations.
233//
234// Most things are passed through unmodified, except for error checking.
John Kessenichecba76f2017-01-06 00:34:48 -0700235//
steve-lunarg90707962016-10-07 19:35:40 -0600236TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped* node)
237{
steve-lunarg07830e82016-10-10 10:00:14 -0600238 if (node == nullptr)
239 return nullptr;
240
steve-lunarg90707962016-10-07 19:35:40 -0600241 TIntermBinary* nodeAsBinary = node->getAsBinaryNode();
242 TIntermUnary* nodeAsUnary = node->getAsUnaryNode();
243 TIntermAggregate* sequence = nullptr;
244
245 TIntermTyped* lhs = nodeAsUnary ? nodeAsUnary->getOperand() :
246 nodeAsBinary ? nodeAsBinary->getLeft() :
247 nullptr;
248
steve-lunarg90707962016-10-07 19:35:40 -0600249 // Early bail out if there is no conversion to apply
250 if (!shouldConvertLValue(lhs)) {
steve-lunarg0de16da2016-10-08 10:54:52 -0600251 if (lhs != nullptr)
252 if (lValueErrorCheck(loc, op, lhs))
253 return nullptr;
steve-lunarg90707962016-10-07 19:35:40 -0600254 return node;
255 }
256
257 // *** If we get here, we're going to apply some conversion to an l-value.
258
259 // Helper to create a load.
260 const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) {
261 TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad);
262 loadOp->setLoc(loc);
263 loadOp->getSequence().push_back(object);
264 loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode()));
265 loadOp->setType(derefType);
266
267 sequence = intermediate.growAggregate(sequence,
268 intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc),
269 loc);
270 };
271
272 // Helper to create a store.
273 const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) {
274 TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore);
275 storeOp->getSequence().push_back(object);
276 storeOp->getSequence().push_back(coord);
277 storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp));
278 storeOp->setLoc(loc);
279 storeOp->setType(TType(EbtVoid));
280
281 sequence = intermediate.growAggregate(sequence, storeOp);
282 };
283
284 // Helper to create an assign.
steve-lunargb3da8a92016-10-12 12:38:12 -0600285 const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) {
steve-lunarg90707962016-10-07 19:35:40 -0600286 sequence = intermediate.growAggregate(sequence,
steve-lunargb3da8a92016-10-12 12:38:12 -0600287 intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()),
steve-lunarg90707962016-10-07 19:35:40 -0600288 loc);
289 };
290
291 // Helper to complete sequence by adding trailing variable, so we evaluate to the right value.
baldurkca735702016-10-28 17:57:25 +0200292 const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* {
steve-lunarg90707962016-10-07 19:35:40 -0600293 // Add a trailing use of the temp, so the sequence returns the proper value.
294 sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp));
295 sequence->setOperator(EOpSequence);
296 sequence->setLoc(loc);
297 sequence->setType(derefType);
298
299 return sequence;
300 };
301
302 // Helper to add unary op
steve-lunargb3da8a92016-10-12 12:38:12 -0600303 const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) {
steve-lunarg90707962016-10-07 19:35:40 -0600304 sequence = intermediate.growAggregate(sequence,
steve-lunargb3da8a92016-10-12 12:38:12 -0600305 intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc,
306 rhsTmp->getType()),
steve-lunarg90707962016-10-07 19:35:40 -0600307 loc);
308 };
309
steve-lunargcd6829b2016-12-28 10:03:58 -0700310 // Return true if swizzle or index writes all components of the given variable.
311 const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool {
312 if (swizzle == nullptr) // not a swizzle or index
313 return true;
314
315 // Track which components are being set.
316 std::array<bool, 4> compIsSet;
317 compIsSet.fill(false);
318
319 const TIntermConstantUnion* asConst = swizzle->getRight()->getAsConstantUnion();
320 const TIntermAggregate* asAggregate = swizzle->getRight()->getAsAggregate();
321
322 // This could be either a direct index, or a swizzle.
323 if (asConst) {
324 compIsSet[asConst->getConstArray()[0].getIConst()] = true;
325 } else if (asAggregate) {
326 const TIntermSequence& seq = asAggregate->getSequence();
327 for (int comp=0; comp<int(seq.size()); ++comp)
328 compIsSet[seq[comp]->getAsConstantUnion()->getConstArray()[0].getIConst()] = true;
329 } else {
330 assert(0);
331 }
332
333 // Return true if all components are being set by the index or swizzle
334 return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(),
335 [](bool isSet) { return isSet; } );
336 };
337
steve-lunargcd6829b2016-12-28 10:03:58 -0700338 // Create swizzle matching input swizzle
339 const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* {
340 if (swizzle)
341 return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType());
342 else
343 return var;
344 };
345
346 TIntermBinary* lhsAsBinary = lhs->getAsBinaryNode();
steve-lunarg90707962016-10-07 19:35:40 -0600347 TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate();
steve-lunargcd6829b2016-12-28 10:03:58 -0700348 bool lhsIsSwizzle = false;
349
350 // If it's a swizzled L-value, remember the swizzle, and use the LHS.
351 if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) {
352 lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
353 lhsIsSwizzle = true;
354 }
John Kessenichecba76f2017-01-06 00:34:48 -0700355
steve-lunarg90707962016-10-07 19:35:40 -0600356 TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
357 TIntermTyped* coord = lhsAsAggregate->getSequence()[1]->getAsTyped();
358
steve-lunarg8b0227c2016-10-14 16:40:32 -0600359 const TSampler& texSampler = object->getType().getSampler();
360
steve-lunarg8b0227c2016-10-14 16:40:32 -0600361 const TType objDerefType(texSampler.type, EvqTemporary, texSampler.vectorSize);
steve-lunarg90707962016-10-07 19:35:40 -0600362
363 if (nodeAsBinary) {
364 TIntermTyped* rhs = nodeAsBinary->getRight();
365 const TOperator assignOp = nodeAsBinary->getOp();
366
367 bool isModifyOp = false;
368
369 switch (assignOp) {
370 case EOpAddAssign:
371 case EOpSubAssign:
372 case EOpMulAssign:
373 case EOpVectorTimesMatrixAssign:
374 case EOpVectorTimesScalarAssign:
375 case EOpMatrixTimesScalarAssign:
376 case EOpMatrixTimesMatrixAssign:
377 case EOpDivAssign:
378 case EOpModAssign:
379 case EOpAndAssign:
380 case EOpInclusiveOrAssign:
381 case EOpExclusiveOrAssign:
382 case EOpLeftShiftAssign:
383 case EOpRightShiftAssign:
384 isModifyOp = true;
385 // fall through...
386 case EOpAssign:
387 {
388 // Since this is an lvalue, we'll convert an image load to a sequence like this (to still provide the value):
389 // OpSequence
390 // OpImageStore(object, lhs, rhs)
391 // rhs
392 // But if it's not a simple symbol RHS (say, a fn call), we don't want to duplicate the RHS, so we'll convert
393 // instead to this:
394 // OpSequence
395 // rhsTmp = rhs
396 // OpImageStore(object, coord, rhsTmp)
397 // rhsTmp
398 // If this is a read-modify-write op, like +=, we issue:
399 // OpSequence
400 // coordtmp = load's param1
401 // rhsTmp = OpImageLoad(object, coordTmp)
John Kessenich64285c92017-01-05 10:45:32 -0700402 // rhsTmp op= rhs
steve-lunarg90707962016-10-07 19:35:40 -0600403 // OpImageStore(object, coordTmp, rhsTmp)
404 // rhsTmp
steve-lunargcd6829b2016-12-28 10:03:58 -0700405 //
406 // If the lvalue is swizzled, we apply that when writing the temp variable, like so:
407 // ...
408 // rhsTmp.some_swizzle = ...
409 // For partial writes, an error is generated.
steve-lunarg90707962016-10-07 19:35:40 -0600410
411 TIntermSymbol* rhsTmp = rhs->getAsSymbolNode();
412 TIntermTyped* coordTmp = coord;
John Kessenichecba76f2017-01-06 00:34:48 -0700413
steve-lunargcd6829b2016-12-28 10:03:58 -0700414 if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) {
John Kessenich82460b52017-04-04 11:47:42 -0600415 rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
steve-lunarg90707962016-10-07 19:35:40 -0600416
steve-lunargcd6829b2016-12-28 10:03:58 -0700417 // Partial updates not yet supported
418 if (!writesAllComponents(rhsTmp, lhsAsBinary)) {
419 error(loc, "unimplemented: partial image updates", "", "");
420 }
421
steve-lunarg90707962016-10-07 19:35:40 -0600422 // Assign storeTemp = rhs
423 if (isModifyOp) {
424 // We have to make a temp var for the coordinate, to avoid evaluating it twice.
John Kessenich82460b52017-04-04 11:47:42 -0600425 coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
steve-lunargb3da8a92016-10-12 12:38:12 -0600426 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600427 makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
428 }
429
430 // rhsTmp op= rhs.
steve-lunargcd6829b2016-12-28 10:03:58 -0700431 makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs);
steve-lunarg90707962016-10-07 19:35:40 -0600432 }
John Kessenichecba76f2017-01-06 00:34:48 -0700433
steve-lunarg90707962016-10-07 19:35:40 -0600434 makeStore(object, coordTmp, rhsTmp); // add a store
435 return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
436 }
437
438 default:
439 break;
440 }
441 }
442
443 if (nodeAsUnary) {
444 const TOperator assignOp = nodeAsUnary->getOp();
445
446 switch (assignOp) {
447 case EOpPreIncrement:
448 case EOpPreDecrement:
449 {
450 // We turn this into:
451 // OpSequence
452 // coordtmp = load's param1
453 // rhsTmp = OpImageLoad(object, coordTmp)
454 // rhsTmp op
455 // OpImageStore(object, coordTmp, rhsTmp)
456 // rhsTmp
John Kessenichecba76f2017-01-06 00:34:48 -0700457
John Kessenich82460b52017-04-04 11:47:42 -0600458 TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
459 TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
John Kessenichecba76f2017-01-06 00:34:48 -0700460
steve-lunargb3da8a92016-10-12 12:38:12 -0600461 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600462 makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
steve-lunargb3da8a92016-10-12 12:38:12 -0600463 makeUnary(assignOp, rhsTmp); // op rhsTmp
steve-lunarg90707962016-10-07 19:35:40 -0600464 makeStore(object, coordTmp, rhsTmp); // OpImageStore(object, coordTmp, rhsTmp)
465 return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
466 }
467
468 case EOpPostIncrement:
469 case EOpPostDecrement:
470 {
471 // We turn this into:
472 // OpSequence
473 // coordtmp = load's param1
474 // rhsTmp1 = OpImageLoad(object, coordTmp)
475 // rhsTmp2 = rhsTmp1
476 // rhsTmp2 op
477 // OpImageStore(object, coordTmp, rhsTmp2)
478 // rhsTmp1 (pre-op value)
John Kessenich82460b52017-04-04 11:47:42 -0600479 TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre", objDerefType);
480 TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType);
481 TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
steve-lunarg90707962016-10-07 19:35:40 -0600482
steve-lunargb3da8a92016-10-12 12:38:12 -0600483 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600484 makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp)
steve-lunargb3da8a92016-10-12 12:38:12 -0600485 makeBinary(EOpAssign, rhsTmp2, rhsTmp1); // rhsTmp2 = rhsTmp1
486 makeUnary(assignOp, rhsTmp2); // rhsTmp op
steve-lunarg90707962016-10-07 19:35:40 -0600487 makeStore(object, coordTmp, rhsTmp2); // OpImageStore(object, coordTmp, rhsTmp2)
488 return finishSequence(rhsTmp1, objDerefType); // return rhsTmp from sequence
steve-lunarg90707962016-10-07 19:35:40 -0600489 }
John Kessenichecba76f2017-01-06 00:34:48 -0700490
steve-lunarg90707962016-10-07 19:35:40 -0600491 default:
492 break;
493 }
494 }
495
steve-lunarg0de16da2016-10-08 10:54:52 -0600496 if (lhs)
497 if (lValueErrorCheck(loc, op, lhs))
498 return nullptr;
John Kessenichecba76f2017-01-06 00:34:48 -0700499
steve-lunarg90707962016-10-07 19:35:40 -0600500 return node;
501}
502
John Kesseniche01a9bc2016-03-12 20:11:22 -0700503void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
504{
505 if (pragmaCallback)
506 pragmaCallback(loc.line, tokens);
507
508 if (tokens.size() == 0)
509 return;
510}
511
512//
John Kessenichc142c882017-01-13 19:34:22 -0700513// Look at a '.' matrix selector string and change it into components
John Kessenich001dfa12017-01-12 16:51:18 -0700514// for a matrix. There are two types:
515//
516// _21 second row, first column (one based)
517// _m21 third row, second column (zero based)
518//
519// Returns true if there is no error.
520//
John Kessenichc142c882017-01-13 19:34:22 -0700521bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows,
522 TSwizzleSelectors<TMatrixSelector>& components)
John Kessenich001dfa12017-01-12 16:51:18 -0700523{
John Kessenich33dadd12017-01-13 20:22:00 -0700524 int startPos[MaxSwizzleSelectors];
John Kessenich001dfa12017-01-12 16:51:18 -0700525 int numComps = 0;
526 TString compString = fields;
527
528 // Find where each component starts,
529 // recording the first character position after the '_'.
530 for (size_t c = 0; c < compString.size(); ++c) {
531 if (compString[c] == '_') {
John Kessenich33dadd12017-01-13 20:22:00 -0700532 if (numComps >= MaxSwizzleSelectors) {
John Kessenich001dfa12017-01-12 16:51:18 -0700533 error(loc, "matrix component swizzle has too many components", compString.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -0700534 return false;
535 }
John Kessenich001dfa12017-01-12 16:51:18 -0700536 if (c > compString.size() - 3 ||
537 ((compString[c+1] == 'm' || compString[c+1] == 'M') && c > compString.size() - 4)) {
538 error(loc, "matrix component swizzle missing", compString.c_str(), "");
539 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700540 }
Graham Wihlidal6f332f32017-02-17 19:05:14 +0100541 startPos[numComps++] = (int)c + 1;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700542 }
John Kessenich001dfa12017-01-12 16:51:18 -0700543 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700544
John Kessenich001dfa12017-01-12 16:51:18 -0700545 // Process each component
546 for (int i = 0; i < numComps; ++i) {
547 int pos = startPos[i];
548 int bias = -1;
549 if (compString[pos] == 'm' || compString[pos] == 'M') {
550 bias = 0;
551 ++pos;
552 }
John Kessenichc142c882017-01-13 19:34:22 -0700553 TMatrixSelector comp;
John Kessenich001dfa12017-01-12 16:51:18 -0700554 comp.coord1 = compString[pos+0] - '0' + bias;
555 comp.coord2 = compString[pos+1] - '0' + bias;
556 if (comp.coord1 < 0 || comp.coord1 >= cols) {
557 error(loc, "matrix row component out of range", compString.c_str(), "");
558 return false;
559 }
560 if (comp.coord2 < 0 || comp.coord2 >= rows) {
561 error(loc, "matrix column component out of range", compString.c_str(), "");
562 return false;
563 }
564 components.push_back(comp);
565 }
566
567 return true;
568}
569
570// If the 'comps' express a column of a matrix,
571// return the column. Column means the first coords all match.
572//
573// Otherwise, return -1.
574//
John Kessenichc142c882017-01-13 19:34:22 -0700575int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>& selector)
John Kessenich001dfa12017-01-12 16:51:18 -0700576{
577 int col = -1;
578
579 // right number of comps?
John Kessenichc142c882017-01-13 19:34:22 -0700580 if (selector.size() != rows)
John Kessenich001dfa12017-01-12 16:51:18 -0700581 return -1;
582
583 // all comps in the same column?
584 // rows in order?
John Kessenichc142c882017-01-13 19:34:22 -0700585 col = selector[0].coord1;
John Kessenich001dfa12017-01-12 16:51:18 -0700586 for (int i = 0; i < rows; ++i) {
John Kessenichc142c882017-01-13 19:34:22 -0700587 if (col != selector[i].coord1)
John Kessenich001dfa12017-01-12 16:51:18 -0700588 return -1;
John Kessenichc142c882017-01-13 19:34:22 -0700589 if (i != selector[i].coord2)
John Kessenich001dfa12017-01-12 16:51:18 -0700590 return -1;
591 }
592
593 return col;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700594}
595
596//
John Kesseniche01a9bc2016-03-12 20:11:22 -0700597// Handle seeing a variable identifier in the grammar.
598//
John Kessenichf4ba25e2017-03-21 18:35:04 -0600599TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700600{
John Kessenich37789792017-03-21 23:56:40 -0600601 int thisDepth;
602 TSymbol* symbol = symbolTable.find(*string, thisDepth);
John Kesseniche6e74942016-06-11 16:43:14 -0600603 if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
604 error(loc, "expected symbol, not user-defined type", string->c_str(), "");
605 return nullptr;
606 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700607
608 // Error check for requiring specific extensions present.
609 if (symbol && symbol->getNumExtensions())
610 requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str());
611
John Kessenich37789792017-03-21 23:56:40 -0600612 const TVariable* variable = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700613 const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr;
John Kesseniche6e74942016-06-11 16:43:14 -0600614 TIntermTyped* node = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700615 if (anon) {
John Kessenich37789792017-03-21 23:56:40 -0600616 // It was a member of an anonymous container, which could be a 'this' structure.
John Kesseniche01a9bc2016-03-12 20:11:22 -0700617
618 // Create a subtree for its dereference.
John Kessenich37789792017-03-21 23:56:40 -0600619 if (thisDepth > 0) {
620 variable = getImplicitThis(thisDepth);
621 if (variable == nullptr)
622 error(loc, "cannot access member variables (static member function?)", "this", "");
623 }
624 if (variable == nullptr)
625 variable = anon->getAnonContainer().getAsVariable();
626
John Kesseniche01a9bc2016-03-12 20:11:22 -0700627 TIntermTyped* container = intermediate.addSymbol(*variable, loc);
628 TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc);
629 node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc);
630
631 node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
632 if (node->getType().hiddenMember())
633 error(loc, "member of nameless block was not redeclared", string->c_str(), "");
634 } else {
635 // Not a member of an anonymous container.
636
637 // The symbol table search was done in the lexical phase.
638 // See if it was a variable.
639 variable = symbol ? symbol->getAsVariable() : nullptr;
640 if (variable) {
641 if ((variable->getType().getBasicType() == EbtBlock ||
642 variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) {
643 error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), "");
644 variable = nullptr;
645 }
646 } else {
647 if (symbol)
648 error(loc, "variable name expected", string->c_str(), "");
649 }
650
651 // Recovery, if it wasn't found or was not a variable.
John Kessenich1e275c82016-12-14 17:02:32 -0700652 if (! variable) {
653 error(loc, "unknown variable", string->c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -0700654 variable = new TVariable(string, TType(EbtVoid));
John Kessenich1e275c82016-12-14 17:02:32 -0700655 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700656
657 if (variable->getType().getQualifier().isFrontEndConstant())
658 node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
659 else
660 node = intermediate.addSymbol(*variable, loc);
661 }
662
663 if (variable->getType().getQualifier().isIo())
664 intermediate.addIoAccessed(*string);
665
666 return node;
667}
668
669//
steve-lunarg6b43d272016-10-06 20:12:24 -0600670// Handle operator[] on any objects it applies to. Currently:
671// Textures
672// Buffers
673//
674TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
675{
676 // handle r-value operator[] on textures and images. l-values will be processed later.
677 if (base->getType().getBasicType() == EbtSampler && !base->isArray()) {
678 const TSampler& sampler = base->getType().getSampler();
679 if (sampler.isImage() || sampler.isTexture()) {
steve-lunarg07830e82016-10-10 10:00:14 -0600680 TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch);
steve-lunarg6b43d272016-10-06 20:12:24 -0600681
steve-lunarg8b0227c2016-10-14 16:40:32 -0600682 load->setType(TType(sampler.type, EvqTemporary, sampler.vectorSize));
steve-lunarg6b43d272016-10-06 20:12:24 -0600683 load->setLoc(loc);
684 load->getSequence().push_back(base);
685 load->getSequence().push_back(index);
steve-lunarg07830e82016-10-10 10:00:14 -0600686
687 // Textures need a MIP. First indirection is always to mip 0. If there's another, we'll add it
688 // later.
689 if (sampler.isTexture())
690 load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true));
691
steve-lunarg6b43d272016-10-06 20:12:24 -0600692 return load;
693 }
694 }
695
steve-lunargdd8287a2017-02-23 18:04:12 -0700696 // Handle operator[] on structured buffers: this indexes into the array element of the buffer.
697 // indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO).
698 TIntermTyped* sbArray = indexStructBufferContent(loc, base);
699 if (sbArray != nullptr) {
700 if (sbArray == nullptr)
701 return nullptr;
702
703 // Now we'll apply the [] index to that array
704 const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
705
706 TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc);
707 const TType derefType(sbArray->getType(), 0);
708 element->setType(derefType);
709 return element;
710 }
711
steve-lunarg6b43d272016-10-06 20:12:24 -0600712 return nullptr;
713}
714
715//
John Kesseniche01a9bc2016-03-12 20:11:22 -0700716// Handle seeing a base[index] dereference in the grammar.
717//
718TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
719{
steve-lunarg6b43d272016-10-06 20:12:24 -0600720 TIntermTyped* result = handleBracketOperator(loc, base, index);
721
722 if (result != nullptr)
723 return result; // it was handled as an operator[]
John Kesseniche01a9bc2016-03-12 20:11:22 -0700724
steve-lunargcf43e662016-09-22 14:35:23 -0600725 bool flattened = false;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700726 int indexValue = 0;
727 if (index->getQualifier().storage == EvqConst) {
728 indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
729 checkIndex(loc, base->getType(), indexValue);
730 }
731
732 variableCheck(base);
733 if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) {
734 if (base->getAsSymbolNode())
735 error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), "");
736 else
737 error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
738 } else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
739 return intermediate.foldDereference(base, indexValue, loc);
740 else {
741 // at least one of base and index is variable...
742
John Kessenich02467d82017-01-19 15:41:47 -0700743 if (base->getAsSymbolNode() && (wasFlattened(base) || shouldFlattenUniform(base->getType()))) {
steve-lunarge0b9deb2016-09-16 13:26:37 -0600744 if (index->getQualifier().storage != EvqConst)
steve-lunarg132d3312016-12-19 15:48:01 -0700745 error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), "");
steve-lunarge0b9deb2016-09-16 13:26:37 -0600746
steve-lunarga2e75312016-12-14 15:22:25 -0700747 result = flattenAccess(base, indexValue);
steve-lunargcf43e662016-09-22 14:35:23 -0600748 flattened = (result != base);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700749 } else {
steve-lunarg132d3312016-12-19 15:48:01 -0700750 splitAccessArray(loc, base, index);
751
steve-lunarge0b9deb2016-09-16 13:26:37 -0600752 if (index->getQualifier().storage == EvqConst) {
753 if (base->getType().isImplicitlySizedArray())
754 updateImplicitArraySize(loc, base, indexValue);
755 result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
756 } else {
757 result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
758 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700759 }
760 }
761
762 if (result == nullptr) {
763 // Insert dummy error-recovery result
764 result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
765 } else {
steve-lunargcf43e662016-09-22 14:35:23 -0600766 // If the array reference was flattened, it has the correct type. E.g, if it was
767 // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps.
768 // In that case, we preserve the qualifiers.
769 if (!flattened) {
770 // Insert valid dereferenced result
771 TType newType(base->getType(), 0); // dereferenced type
772 if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
773 newType.getQualifier().storage = EvqConst;
774 else
775 newType.getQualifier().storage = EvqTemporary;
776 result->setType(newType);
777 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700778 }
779
780 return result;
781}
782
John Kessenich7f349c72016-07-08 22:09:10 -0600783void HlslParseContext::checkIndex(const TSourceLoc& /*loc*/, const TType& /*type*/, int& /*index*/)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700784{
785 // HLSL todo: any rules for index fixups?
786}
787
John Kesseniche01a9bc2016-03-12 20:11:22 -0700788// Handle seeing a binary node with a math operation.
789TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right)
790{
791 TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
792 if (! result)
793 binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
794
795 return result;
796}
797
798// Handle seeing a unary node with a math operation.
799TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode)
800{
801 TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc);
802
803 if (result)
804 return result;
805 else
806 unaryOpError(loc, str, childNode->getCompleteString());
807
808 return childNode;
809}
steve-lunarg5da1f032017-02-12 17:50:28 -0700810//
811// Return true if the name is a struct buffer method
812//
813bool HlslParseContext::isStructBufferMethod(const TString& name) const
814{
815 return
816 name == "GetDimensions" ||
817 name == "Load" ||
818 name == "Load2" ||
819 name == "Load3" ||
820 name == "Load4" ||
821 name == "Store" ||
822 name == "Store2" ||
823 name == "Store3" ||
824 name == "Store4" ||
825 name == "InterlockedAdd" ||
826 name == "InterlockedAnd" ||
827 name == "InterlockedCompareExchange" ||
828 name == "InterlockedCompareStore" ||
829 name == "InterlockedExchange" ||
830 name == "InterlockedMax" ||
831 name == "InterlockedMin" ||
832 name == "InterlockedOr" ||
833 name == "InterlockedXor";
834}
835
836//
John Kessenich516d92d2017-03-08 20:09:03 -0700837// Handle seeing a base.field dereference in the grammar, where 'field' is a
838// swizzle or member variable.
John Kesseniche01a9bc2016-03-12 20:11:22 -0700839//
840TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field)
841{
842 variableCheck(base);
843
John Kesseniche01a9bc2016-03-12 20:11:22 -0700844 if (base->isArray()) {
845 error(loc, "cannot apply to an array:", ".", field.c_str());
John Kesseniche01a9bc2016-03-12 20:11:22 -0700846 return base;
847 }
848
John Kesseniche01a9bc2016-03-12 20:11:22 -0700849 TIntermTyped* result = base;
850 if (base->isVector() || base->isScalar()) {
John Kessenichc142c882017-01-13 19:34:22 -0700851 TSwizzleSelectors<TVectorSelector> selectors;
852 parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700853
854 if (base->isScalar()) {
John Kessenichc142c882017-01-13 19:34:22 -0700855 if (selectors.size() == 1)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700856 return result;
857 else {
John Kessenichc142c882017-01-13 19:34:22 -0700858 TType type(base->getBasicType(), EvqTemporary, selectors.size());
John Kessenicha26a5172016-07-28 15:29:35 -0600859 return addConstructor(loc, base, type);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700860 }
861 }
John Kessenich7d01bd62016-09-02 22:21:25 -0600862 if (base->getVectorSize() == 1) {
863 TType scalarType(base->getBasicType(), EvqTemporary, 1);
John Kessenichc142c882017-01-13 19:34:22 -0700864 if (selectors.size() == 1)
John Kessenich7d01bd62016-09-02 22:21:25 -0600865 return addConstructor(loc, base, scalarType);
866 else {
John Kessenichc142c882017-01-13 19:34:22 -0700867 TType vectorType(base->getBasicType(), EvqTemporary, selectors.size());
John Kessenich7d01bd62016-09-02 22:21:25 -0600868 return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType);
869 }
870 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700871
872 if (base->getType().getQualifier().isFrontEndConstant())
John Kessenichc142c882017-01-13 19:34:22 -0700873 result = intermediate.foldSwizzle(base, selectors, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700874 else {
John Kessenichc142c882017-01-13 19:34:22 -0700875 if (selectors.size() == 1) {
876 TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700877 result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
John Kessenichf6640762016-08-01 19:44:00 -0600878 result->setType(TType(base->getBasicType(), EvqTemporary));
John Kesseniche01a9bc2016-03-12 20:11:22 -0700879 } else {
John Kessenichc142c882017-01-13 19:34:22 -0700880 TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700881 result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
John Kessenichc142c882017-01-13 19:34:22 -0700882 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, selectors.size()));
John Kesseniche01a9bc2016-03-12 20:11:22 -0700883 }
884 }
John Kessenich001dfa12017-01-12 16:51:18 -0700885 } else if (base->isMatrix()) {
John Kessenichc142c882017-01-13 19:34:22 -0700886 TSwizzleSelectors<TMatrixSelector> selectors;
887 if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors))
John Kessenich001dfa12017-01-12 16:51:18 -0700888 return result;
889
John Kessenichc142c882017-01-13 19:34:22 -0700890 if (selectors.size() == 1) {
John Kessenich001dfa12017-01-12 16:51:18 -0700891 // Representable by m[c][r]
John Kessenichfdf63472017-01-13 12:27:52 -0700892 if (base->getType().getQualifier().isFrontEndConstant()) {
John Kessenichc142c882017-01-13 19:34:22 -0700893 result = intermediate.foldDereference(base, selectors[0].coord1, loc);
894 result = intermediate.foldDereference(result, selectors[0].coord2, loc);
John Kessenich001dfa12017-01-12 16:51:18 -0700895 } else {
John Kessenichc142c882017-01-13 19:34:22 -0700896 result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(selectors[0].coord1, loc), loc);
John Kessenich001dfa12017-01-12 16:51:18 -0700897 TType dereferencedCol(base->getType(), 0);
898 result->setType(dereferencedCol);
John Kessenichc142c882017-01-13 19:34:22 -0700899 result = intermediate.addIndex(EOpIndexDirect, result, intermediate.addConstantUnion(selectors[0].coord2, loc), loc);
John Kessenich001dfa12017-01-12 16:51:18 -0700900 TType dereferenced(dereferencedCol, 0);
901 result->setType(dereferenced);
902 }
903 } else {
John Kessenichc142c882017-01-13 19:34:22 -0700904 int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors);
John Kessenich001dfa12017-01-12 16:51:18 -0700905 if (column >= 0) {
906 // Representable by m[c]
John Kessenichfdf63472017-01-13 12:27:52 -0700907 if (base->getType().getQualifier().isFrontEndConstant())
John Kessenich001dfa12017-01-12 16:51:18 -0700908 result = intermediate.foldDereference(base, column, loc);
909 else {
910 result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc), loc);
911 TType dereferenced(base->getType(), 0);
912 result->setType(dereferenced);
913 }
914 } else {
915 // general case, not a column, not a single component
John Kessenichc142c882017-01-13 19:34:22 -0700916 TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
John Kessenichfdf63472017-01-13 12:27:52 -0700917 result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc);
John Kessenichc142c882017-01-13 19:34:22 -0700918 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, selectors.size()));
John Kessenichfdf63472017-01-13 12:27:52 -0700919 }
John Kessenich001dfa12017-01-12 16:51:18 -0700920 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700921 } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
922 const TTypeList* fields = base->getType().getStruct();
923 bool fieldFound = false;
924 int member;
925 for (member = 0; member < (int)fields->size(); ++member) {
926 if ((*fields)[member].type->getFieldName() == field) {
927 fieldFound = true;
928 break;
929 }
930 }
931 if (fieldFound) {
John Kessenich02467d82017-01-19 15:41:47 -0700932 if (base->getAsSymbolNode() && (wasFlattened(base) || shouldFlattenUniform(base->getType()))) {
steve-lunarga2e75312016-12-14 15:22:25 -0700933 result = flattenAccess(base, member);
934 } else {
935 // Update the base and member to access if this was a split structure.
steve-lunarg132d3312016-12-19 15:48:01 -0700936 result = splitAccessStruct(loc, base, member);
steve-lunarga2e75312016-12-14 15:22:25 -0700937 fields = base->getType().getStruct();
938
939 if (result == nullptr) {
940 if (base->getType().getQualifier().storage == EvqConst)
941 result = intermediate.foldDereference(base, member, loc);
942 else {
943 TIntermTyped* index = intermediate.addConstantUnion(member, loc);
944 result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc);
945 result->setType(*(*fields)[member].type);
946 }
John Kessenichcd0a78a2016-09-09 16:32:09 -0600947 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700948 }
949 } else
950 error(loc, "no such field in structure", field.c_str(), "");
951 } else
952 error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str());
953
954 return result;
955}
956
John Kessenich516d92d2017-03-08 20:09:03 -0700957//
John Kessenich5f12d2f2017-03-11 09:39:55 -0700958// Return true if the field should be treated as a built-in method.
959// Return false otherwise.
John Kessenich516d92d2017-03-08 20:09:03 -0700960//
steve-lunarge7d07522017-03-19 18:12:37 -0600961bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field)
John Kessenich516d92d2017-03-08 20:09:03 -0700962{
John Kessenich54ee28f2017-03-11 14:13:00 -0700963 if (base == nullptr)
964 return false;
965
John Kessenich516d92d2017-03-08 20:09:03 -0700966 variableCheck(base);
967
John Kessenich5f12d2f2017-03-11 09:39:55 -0700968 if (base->getType().getBasicType() == EbtSampler) {
969 return true;
970 } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) {
971 return true;
John Kessenich516d92d2017-03-08 20:09:03 -0700972 } else if (field == "Append" ||
973 field == "RestartStrip") {
974 // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but
975 // the code is around in the shader source.
John Kessenich5f12d2f2017-03-11 09:39:55 -0700976 return true;
977 } else
978 return false;
John Kessenich516d92d2017-03-08 20:09:03 -0700979}
980
steve-lunarg132d3312016-12-19 15:48:01 -0700981// Split the type of the given node into two structs:
steve-lunarga2e75312016-12-14 15:22:25 -0700982// 1. interstage IO
983// 2. everything else
984// IO members are put into the ioStruct. The type is modified to remove them.
985void HlslParseContext::split(TIntermTyped* node)
986{
987 if (node == nullptr)
988 return;
989
990 TIntermSymbol* symNode = node->getAsSymbolNode();
991
992 if (symNode == nullptr)
993 return;
994
995 // Create a new variable:
steve-lunarg132d3312016-12-19 15:48:01 -0700996 TType& splitType = split(*symNode->getType().clone(), symNode->getName());
997
998 splitIoVars[symNode->getId()] = makeInternalVariable(symNode->getName(), splitType);
steve-lunarga2e75312016-12-14 15:22:25 -0700999}
1000
steve-lunarg132d3312016-12-19 15:48:01 -07001001// Split the type of the given variable into two structs:
steve-lunarga2e75312016-12-14 15:22:25 -07001002void HlslParseContext::split(const TVariable& variable)
1003{
1004 const TType& type = variable.getType();
1005
John Kessenich02467d82017-01-19 15:41:47 -07001006 TString name = variable.getName();
steve-lunarg132d3312016-12-19 15:48:01 -07001007
steve-lunarga2e75312016-12-14 15:22:25 -07001008 // Create a new variable:
steve-lunarg132d3312016-12-19 15:48:01 -07001009 TType& splitType = split(*type.clone(), name);
1010
1011 splitIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType);
steve-lunarga2e75312016-12-14 15:22:25 -07001012}
1013
1014// Recursive implementation of split(const TVariable& variable).
1015// Returns reference to the modified type.
steve-lunarg132d3312016-12-19 15:48:01 -07001016TType& HlslParseContext::split(TType& type, TString name, const TType* outerStructType)
steve-lunarga2e75312016-12-14 15:22:25 -07001017{
steve-lunarg132d3312016-12-19 15:48:01 -07001018 const TArraySizes* arraySizes = nullptr;
1019
1020 // At the outer-most scope, remember the struct type so we can examine its storage class
1021 // at deeper levels.
1022 if (outerStructType == nullptr)
1023 outerStructType = &type;
1024
1025 if (type.isArray())
1026 arraySizes = &type.getArraySizes();
1027
steve-lunarga2e75312016-12-14 15:22:25 -07001028 // We can ignore arrayness: it's uninvolved.
1029 if (type.isStruct()) {
1030 TTypeList* userStructure = type.getWritableStruct();
John Kessenichecba76f2017-01-06 00:34:48 -07001031
John Kessenich02467d82017-01-19 15:41:47 -07001032 // Get iterator to (now at end) set of builtin interstage IO members
steve-lunarga2e75312016-12-14 15:22:25 -07001033 const auto firstIo = std::stable_partition(userStructure->begin(), userStructure->end(),
steve-lunarg46d54282017-01-07 09:07:14 -07001034 [this](const TTypeLoc& t) {return !t.type->isBuiltInInterstageIO(language);});
steve-lunarga2e75312016-12-14 15:22:25 -07001035
steve-lunarg132d3312016-12-19 15:48:01 -07001036 // Move those to the builtin IO. However, we also propagate arrayness (just one level is handled
1037 // now) to this variable.
steve-lunarga2e75312016-12-14 15:22:25 -07001038 for (auto ioType = firstIo; ioType != userStructure->end(); ++ioType) {
1039 const TType& memberType = *ioType->type;
steve-lunarg46d54282017-01-07 09:07:14 -07001040 TVariable* ioVar = makeInternalVariable(name + (name.empty() ? "" : "_") + memberType.getFieldName(), memberType);
steve-lunarg132d3312016-12-19 15:48:01 -07001041
1042 if (arraySizes)
1043 ioVar->getWritableType().newArraySizes(*arraySizes);
1044
steve-lunargccb076a2017-04-05 11:03:02 -06001045 fixBuiltInIoType(ioVar->getWritableType());
steve-lunarg194f0f32017-03-17 18:51:05 -06001046
steve-lunarg132d3312016-12-19 15:48:01 -07001047 interstageBuiltInIo[tInterstageIoData(memberType, *outerStructType)] = ioVar;
steve-lunarga2e75312016-12-14 15:22:25 -07001048
1049 // Merge qualifier from the user structure
steve-lunarg132d3312016-12-19 15:48:01 -07001050 mergeQualifiers(ioVar->getWritableType().getQualifier(), outerStructType->getQualifier());
steve-lunarga2e75312016-12-14 15:22:25 -07001051 }
1052
1053 // Erase the IO vars from the user structure.
1054 userStructure->erase(firstIo, userStructure->end());
1055
1056 // Recurse further into the members.
1057 for (unsigned int i = 0; i < userStructure->size(); ++i)
steve-lunarg132d3312016-12-19 15:48:01 -07001058 split(*(*userStructure)[i].type,
steve-lunarg46d54282017-01-07 09:07:14 -07001059 name + (name.empty() ? "" : "_") + (*userStructure)[i].type->getFieldName(),
steve-lunarg132d3312016-12-19 15:48:01 -07001060 outerStructType);
steve-lunarga2e75312016-12-14 15:22:25 -07001061 }
1062
1063 return type;
1064}
1065
steve-lunarge0b9deb2016-09-16 13:26:37 -06001066// Is this a uniform array which should be flattened?
1067bool HlslParseContext::shouldFlattenUniform(const TType& type) const
1068{
1069 const TStorageQualifier qualifier = type.getQualifier().storage;
1070
John Kessenich02467d82017-01-19 15:41:47 -07001071 return qualifier == EvqUniform &&
1072 ((type.isArray() && intermediate.getFlattenUniformArrays()) || type.isStruct()) &&
steve-lunarga2b01a02016-11-28 17:09:54 -07001073 type.containsOpaque();
steve-lunarge0b9deb2016-09-16 13:26:37 -06001074}
1075
steve-lunarga2b01a02016-11-28 17:09:54 -07001076// Top level variable flattening: construct data
steve-lunarge0b9deb2016-09-16 13:26:37 -06001077void HlslParseContext::flatten(const TSourceLoc& loc, const TVariable& variable)
1078{
1079 const TType& type = variable.getType();
1080
rdba2f0e0e2017-01-20 14:46:39 +01001081 auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(),
1082 TFlattenData(type.getQualifier().layoutBinding)));
steve-lunarga2e75312016-12-14 15:22:25 -07001083
rdba2f0e0e2017-01-20 14:46:39 +01001084 // the item is a map pair, so first->second is the TFlattenData itself.
steve-lunarga2b01a02016-11-28 17:09:54 -07001085 flatten(loc, variable, type, entry.first->second, "");
1086}
John Kessenichecba76f2017-01-06 00:34:48 -07001087
steve-lunarga2b01a02016-11-28 17:09:54 -07001088// Recursively flatten the given variable at the provided type, building the flattenData as we go.
1089//
1090// This is mutually recursive with flattenStruct and flattenArray.
1091// We are going to flatten an arbitrarily nested composite structure into a linear sequence of
1092// members, and later on, we want to turn a path through the tree structure into a final
1093// location in this linear sequence.
1094//
1095// If the tree was N-ary, that can be directly calculated. However, we are dealing with
John Kessenich64285c92017-01-05 10:45:32 -07001096// arbitrary numbers - perhaps a struct of 7 members containing an array of 3. Thus, we must
steve-lunarga2b01a02016-11-28 17:09:54 -07001097// build a data structure to allow the sequence of bracket and dot operators on arrays and
1098// structs to arrive at the proper member.
1099//
1100// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers.
1101// The leaves are the indexes into the flattened member array.
1102// Each level will have the next location for the Nth item stored sequentially, so for instance:
1103//
1104// struct { float2 a[2]; int b; float4 c[3] };
1105//
1106// This will produce the following flattened tree:
1107// Pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
1108// (3, 7, 8, 5, 6, 0, 1, 2, 11, 12, 13, 3, 4, 5}
1109//
1110// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse:
1111// (0+2) = 8 --> (8+1) = 12 --> 12 = 4
1112//
1113// so the 4th flattened member in traversal order is ours.
1114//
1115int HlslParseContext::flatten(const TSourceLoc& loc, const TVariable& variable, const TType& type,
1116 TFlattenData& flattenData, TString name)
1117{
steve-lunarga2b01a02016-11-28 17:09:54 -07001118 // If something is an arrayed struct, the array flattener will recursively call flatten()
1119 // to then flatten the struct, so this is an "if else": we don't do both.
steve-lunarge0b9deb2016-09-16 13:26:37 -06001120 if (type.isArray())
steve-lunarga2b01a02016-11-28 17:09:54 -07001121 return flattenArray(loc, variable, type, flattenData, name);
1122 else if (type.isStruct())
1123 return flattenStruct(loc, variable, type, flattenData, name);
1124 else {
1125 assert(0); // should never happen
1126 return -1;
1127 }
1128}
1129
1130// Add a single flattened member to the flattened data being tracked for the composite
1131// Returns true for the final flattening level.
John Kessenichecba76f2017-01-06 00:34:48 -07001132int HlslParseContext::addFlattenedMember(const TSourceLoc& loc,
John Kessenichabd8dca2017-02-01 18:09:17 -07001133 const TVariable& variable, const TType& type, TFlattenData& flattenData,
1134 const TString& memberName, bool track)
steve-lunarga2b01a02016-11-28 17:09:54 -07001135{
1136 if (isFinalFlattening(type)) {
1137 // This is as far as we flatten. Insert the variable.
steve-lunarga2e75312016-12-14 15:22:25 -07001138 TVariable* memberVariable = makeInternalVariable(memberName, type);
steve-lunarga2b01a02016-11-28 17:09:54 -07001139 mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier());
1140
1141 if (flattenData.nextBinding != TQualifier::layoutBindingEnd)
1142 memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++;
1143
Jamie Madill3ec327c2016-12-13 17:30:58 -05001144 flattenData.offsets.push_back(static_cast<int>(flattenData.members.size()));
steve-lunarga2b01a02016-11-28 17:09:54 -07001145 flattenData.members.push_back(memberVariable);
1146
1147 if (track)
John Kessenich02467d82017-01-19 15:41:47 -07001148 trackLinkage(*memberVariable);
steve-lunarga2b01a02016-11-28 17:09:54 -07001149
Jamie Madill3ec327c2016-12-13 17:30:58 -05001150 return static_cast<int>(flattenData.offsets.size())-1; // location of the member reference
steve-lunarga2b01a02016-11-28 17:09:54 -07001151 } else {
1152 // Further recursion required
1153 return flatten(loc, variable, type, flattenData, memberName);
1154 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06001155}
1156
John Kessenichf9115002016-09-18 23:10:22 -06001157// Figure out the mapping between an aggregate's top members and an
John Kessenichcd0a78a2016-09-09 16:32:09 -06001158// equivalent set of individual variables.
1159//
1160// Assumes shouldFlatten() or equivalent was called first.
John Kessenichecba76f2017-01-06 00:34:48 -07001161int HlslParseContext::flattenStruct(const TSourceLoc& loc, const TVariable& variable, const TType& type,
John Kessenichabd8dca2017-02-01 18:09:17 -07001162 TFlattenData& flattenData, TString name)
John Kessenichcd0a78a2016-09-09 16:32:09 -06001163{
steve-lunarga2b01a02016-11-28 17:09:54 -07001164 assert(type.isStruct());
John Kessenichcd0a78a2016-09-09 16:32:09 -06001165
steve-lunarga2b01a02016-11-28 17:09:54 -07001166 auto members = *type.getStruct();
1167
1168 // Reserve space for this tree level.
Jamie Madill3ec327c2016-12-13 17:30:58 -05001169 int start = static_cast<int>(flattenData.offsets.size());
steve-lunarga2b01a02016-11-28 17:09:54 -07001170 int pos = start;
1171 flattenData.offsets.resize(int(pos + members.size()), -1);
1172
John Kessenichcd0a78a2016-09-09 16:32:09 -06001173 for (int member = 0; member < (int)members.size(); ++member) {
steve-lunarga2b01a02016-11-28 17:09:54 -07001174 TType& dereferencedType = *members[member].type;
1175 const TString memberName = name + (name.empty() ? "" : ".") + dereferencedType.getFieldName();
1176
1177 const int mpos = addFlattenedMember(loc, variable, dereferencedType, flattenData, memberName, false);
1178 flattenData.offsets[pos++] = mpos;
John Kessenichcd0a78a2016-09-09 16:32:09 -06001179 }
1180
steve-lunarga2b01a02016-11-28 17:09:54 -07001181 return start;
John Kessenichcd0a78a2016-09-09 16:32:09 -06001182}
1183
steve-lunarge0b9deb2016-09-16 13:26:37 -06001184// Figure out mapping between an array's members and an
1185// equivalent set of individual variables.
1186//
1187// Assumes shouldFlatten() or equivalent was called first.
John Kessenichecba76f2017-01-06 00:34:48 -07001188int HlslParseContext::flattenArray(const TSourceLoc& loc, const TVariable& variable, const TType& type,
steve-lunarga2b01a02016-11-28 17:09:54 -07001189 TFlattenData& flattenData, TString name)
steve-lunarge0b9deb2016-09-16 13:26:37 -06001190{
steve-lunarge0b9deb2016-09-16 13:26:37 -06001191 assert(type.isArray());
1192
1193 if (type.isImplicitlySizedArray())
1194 error(loc, "cannot flatten implicitly sized array", variable.getName().c_str(), "");
1195
steve-lunarga2b01a02016-11-28 17:09:54 -07001196 const int size = type.getOuterArraySize();
steve-lunarge0b9deb2016-09-16 13:26:37 -06001197 const TType dereferencedType(type, 0);
steve-lunarge0b9deb2016-09-16 13:26:37 -06001198
steve-lunarga2b01a02016-11-28 17:09:54 -07001199 if (name.empty())
1200 name = variable.getName();
steve-lunarge0b9deb2016-09-16 13:26:37 -06001201
steve-lunarga2b01a02016-11-28 17:09:54 -07001202 // Reserve space for this tree level.
Jamie Madill3ec327c2016-12-13 17:30:58 -05001203 int start = static_cast<int>(flattenData.offsets.size());
steve-lunarga2b01a02016-11-28 17:09:54 -07001204 int pos = start;
1205 flattenData.offsets.resize(int(pos + size), -1);
1206
John Kessenichecba76f2017-01-06 00:34:48 -07001207 for (int element=0; element < size; ++element) {
steve-lunarge0b9deb2016-09-16 13:26:37 -06001208 char elementNumBuf[20]; // sufficient for MAXINT
1209 snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element);
steve-lunarga2b01a02016-11-28 17:09:54 -07001210 const int mpos = addFlattenedMember(loc, variable, dereferencedType, flattenData,
1211 name + elementNumBuf, true);
steve-lunarge0b9deb2016-09-16 13:26:37 -06001212
steve-lunarga2b01a02016-11-28 17:09:54 -07001213 flattenData.offsets[pos++] = mpos;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001214 }
John Kessenichd3f11222016-11-05 10:15:53 -06001215
steve-lunarga2b01a02016-11-28 17:09:54 -07001216 return start;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001217}
1218
steve-lunarga2b01a02016-11-28 17:09:54 -07001219// Return true if we have flattened this node.
1220bool HlslParseContext::wasFlattened(const TIntermTyped* node) const
1221{
steve-lunarga2e75312016-12-14 15:22:25 -07001222 return node != nullptr && node->getAsSymbolNode() != nullptr &&
steve-lunarga2b01a02016-11-28 17:09:54 -07001223 wasFlattened(node->getAsSymbolNode()->getId());
1224}
1225
steve-lunarga2e75312016-12-14 15:22:25 -07001226// Return true if we have split this structure
1227bool HlslParseContext::wasSplit(const TIntermTyped* node) const
1228{
1229 return node != nullptr && node->getAsSymbolNode() != nullptr &&
1230 wasSplit(node->getAsSymbolNode()->getId());
1231}
1232
steve-lunarge0b9deb2016-09-16 13:26:37 -06001233// Turn an access into an aggregate that was flattened to instead be
1234// an access to the individual variable the member was flattened to.
John Kessenichcd0a78a2016-09-09 16:32:09 -06001235// Assumes shouldFlatten() or equivalent was called first.
steve-lunarga2e75312016-12-14 15:22:25 -07001236TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
John Kessenichcd0a78a2016-09-09 16:32:09 -06001237{
steve-lunarga2b01a02016-11-28 17:09:54 -07001238 const TType dereferencedType(base->getType(), member); // dereferenced type
1239
John Kessenichcd0a78a2016-09-09 16:32:09 -06001240 const TIntermSymbol& symbolNode = *base->getAsSymbolNode();
1241
steve-lunarga2b01a02016-11-28 17:09:54 -07001242 const auto flattenData = flattenMap.find(symbolNode.getId());
1243
1244 if (flattenData == flattenMap.end())
John Kessenichcd0a78a2016-09-09 16:32:09 -06001245 return base;
1246
steve-lunarga2b01a02016-11-28 17:09:54 -07001247 // Calculate new cumulative offset from the packed tree
1248 flattenOffset.back() = flattenData->second.offsets[flattenOffset.back() + member];
1249
1250 if (isFinalFlattening(dereferencedType)) {
1251 // Finished flattening: create symbol for variable
1252 member = flattenData->second.offsets[flattenOffset.back()];
1253 const TVariable* memberVariable = flattenData->second.members[member];
1254 return intermediate.addSymbol(*memberVariable);
1255 } else {
1256 // If this is not the final flattening, accumulate the position and return
1257 // an object of the partially dereferenced type.
1258 return new TIntermSymbol(symbolNode.getId(), "flattenShadow", dereferencedType);
1259 }
John Kessenichcd0a78a2016-09-09 16:32:09 -06001260}
1261
steve-lunarga2e75312016-12-14 15:22:25 -07001262// Find and return the split IO TVariable for id, or nullptr if none.
1263TVariable* HlslParseContext::getSplitIoVar(int id) const
1264{
1265 const auto splitIoVar = splitIoVars.find(id);
1266
1267 if (splitIoVar == splitIoVars.end())
1268 return nullptr;
1269
1270 return splitIoVar->second;
1271}
1272
1273// Find and return the split IO TVariable for variable, or nullptr if none.
1274TVariable* HlslParseContext::getSplitIoVar(const TVariable* var) const
1275{
1276 if (var == nullptr)
1277 return nullptr;
1278
1279 return getSplitIoVar(var->getUniqueId());
1280}
1281
1282// Find and return the split IO TVariable for symbol in this node, or nullptr if none.
1283TVariable* HlslParseContext::getSplitIoVar(const TIntermTyped* node) const
1284{
1285 if (node == nullptr)
1286 return nullptr;
1287
1288 const TIntermSymbol* symbolNode = node->getAsSymbolNode();
1289
1290 if (symbolNode == nullptr)
1291 return nullptr;
1292
1293 return getSplitIoVar(symbolNode->getId());
1294}
1295
steve-lunarg132d3312016-12-19 15:48:01 -07001296// Remember the index used to dereference into this structure, in case it has to be moved to a
1297// split-off builtin IO member.
1298void HlslParseContext::splitAccessArray(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
1299{
1300 const TVariable* splitIoVar = getSplitIoVar(base);
steve-lunarga2e75312016-12-14 15:22:25 -07001301
steve-lunarg132d3312016-12-19 15:48:01 -07001302 // Not a split structure
1303 if (splitIoVar == nullptr)
1304 return;
1305
1306 if (builtInIoBase) {
1307 error(loc, "only one array dimension supported for builtIn IO variable", "", "");
1308 return;
1309 }
1310
1311 builtInIoBase = base;
1312 builtInIoIndex = index;
1313}
1314
steve-lunarg132d3312016-12-19 15:48:01 -07001315// Turn an access into an struct that was split to instead be an
1316// access to either the modified structure, or a direct reference to
1317// one of the split member variables.
1318TIntermTyped* HlslParseContext::splitAccessStruct(const TSourceLoc& loc, TIntermTyped*& base, int& member)
steve-lunarga2e75312016-12-14 15:22:25 -07001319{
1320 // nothing to do
steve-lunarg132d3312016-12-19 15:48:01 -07001321 if (base == nullptr)
steve-lunarga2e75312016-12-14 15:22:25 -07001322 return nullptr;
1323
steve-lunarg132d3312016-12-19 15:48:01 -07001324 // We have a pending bracket reference to an outer struct that we may want to move to an inner member.
1325 if (builtInIoBase)
1326 base = builtInIoBase;
1327
steve-lunarga2e75312016-12-14 15:22:25 -07001328 const TVariable* splitIoVar = getSplitIoVar(base);
1329
1330 if (splitIoVar == nullptr)
1331 return nullptr;
1332
1333 const TTypeList& members = *base->getType().getStruct();
1334
steve-lunarg132d3312016-12-19 15:48:01 -07001335 const TType& memberType = *members[member].type;
1336
steve-lunarg46d54282017-01-07 09:07:14 -07001337 if (memberType.isBuiltInInterstageIO(language)) {
steve-lunarg132d3312016-12-19 15:48:01 -07001338 // It's one of the interstage IO variables we split off.
1339 TIntermTyped* builtIn = intermediate.addSymbol(*interstageBuiltInIo[tInterstageIoData(memberType, base->getType())], loc);
1340
1341 // If there's an array reference to an outer split struct, we re-apply it here.
1342 if (builtInIoIndex != nullptr) {
1343 if (builtInIoIndex->getQualifier().storage == EvqConst)
1344 builtIn = intermediate.addIndex(EOpIndexDirect, builtIn, builtInIoIndex, loc);
1345 else
1346 builtIn = intermediate.addIndex(EOpIndexIndirect, builtIn, builtInIoIndex, loc);
1347
1348 builtIn->setType(memberType);
1349
1350 builtInIoIndex = nullptr;
1351 builtInIoBase = nullptr;
1352 }
1353
1354 return builtIn;
steve-lunarga2e75312016-12-14 15:22:25 -07001355 } else {
1356 // It's not an IO variable. Find the equivalent index into the new variable.
1357 base = intermediate.addSymbol(*splitIoVar, loc);
1358
1359 int newMember = 0;
1360 for (int m=0; m<member; ++m)
steve-lunarg46d54282017-01-07 09:07:14 -07001361 if (!members[m].type->isBuiltInInterstageIO(language))
steve-lunarga2e75312016-12-14 15:22:25 -07001362 ++newMember;
1363
1364 member = newMember;
1365
1366 return nullptr;
1367 }
1368}
1369
steve-lunarg858c9282017-01-07 08:54:10 -07001370// Pass through to base class after remembering builtin mappings.
1371void HlslParseContext::trackLinkage(TSymbol& symbol)
1372{
1373 TBuiltInVariable biType = symbol.getType().getQualifier().builtIn;
steve-lunarg067eb9b2017-04-01 15:34:48 -06001374
steve-lunarg858c9282017-01-07 08:54:10 -07001375 if (biType != EbvNone)
1376 builtInLinkageSymbols[biType] = symbol.clone();
1377
1378 TParseContextBase::trackLinkage(symbol);
1379}
1380
1381
steve-lunarg194f0f32017-03-17 18:51:05 -06001382// Some types require fixed array sizes in SPIR-V, but can be scalars or
1383// arrays of sizes SPIR-V doesn't allow. For example, tessellation factors.
1384// This creates the right size. A conversion is performed when the internal
steve-lunargccb076a2017-04-05 11:03:02 -06001385// type is copied to or from the external type. This corrects the externally
1386// facing input or output type to abide downstream semantics.
1387void HlslParseContext::fixBuiltInIoType(TType& type)
steve-lunarg194f0f32017-03-17 18:51:05 -06001388{
steve-lunargccb076a2017-04-05 11:03:02 -06001389 int requiredArraySize = 0;
steve-lunarg194f0f32017-03-17 18:51:05 -06001390
1391 switch (type.getQualifier().builtIn) {
steve-lunargccb076a2017-04-05 11:03:02 -06001392 case EbvTessLevelOuter: requiredArraySize = 4; break;
1393 case EbvTessLevelInner: requiredArraySize = 2; break;
steve-lunarg194f0f32017-03-17 18:51:05 -06001394 case EbvClipDistance: // TODO: ...
1395 case EbvCullDistance: // TODO: ...
steve-lunargccb076a2017-04-05 11:03:02 -06001396 return;
1397 case EbvTessCoord:
1398 {
1399 // tesscoord is always a vec3 for the IO variable, no matter the shader's
1400 // declared vector size.
1401 TType tessCoordType(type.getBasicType(), type.getQualifier().storage, 3);
1402
1403 tessCoordType.getQualifier() = type.getQualifier();
1404 type.shallowCopy(tessCoordType);
1405
1406 break;
1407 }
steve-lunarg194f0f32017-03-17 18:51:05 -06001408 default:
1409 return;
1410 }
1411
steve-lunargccb076a2017-04-05 11:03:02 -06001412 // Alter or set array size as needed.
1413 if (requiredArraySize > 0) {
1414 if (type.isArray()) {
1415 // Already an array. Fix the size.
1416 type.changeOuterArraySize(requiredArraySize);
1417 } else {
1418 // it wasn't an array, but needs to be.
1419 TArraySizes arraySizes;
1420 arraySizes.addInnerSize(requiredArraySize);
1421 type.newArraySizes(arraySizes);
1422 }
steve-lunarg194f0f32017-03-17 18:51:05 -06001423 }
1424}
1425
John Kessenich7dc630f2016-09-16 01:44:43 -06001426// Variables that correspond to the user-interface in and out of a stage
1427// (not the built-in interface) are assigned locations and
1428// registered as a linkage node (part of the stage's external interface).
1429//
1430// Assumes it is called in the order in which locations should be assigned.
1431void HlslParseContext::assignLocations(TVariable& variable)
1432{
1433 const auto assignLocation = [&](TVariable& variable) {
steve-lunarg7afe1342017-03-18 22:24:14 -06001434 const TType& type = variable.getType();
1435 const TQualifier& qualifier = type.getQualifier();
John Kessenich7dc630f2016-09-16 01:44:43 -06001436 if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
1437 if (qualifier.builtIn == EbvNone) {
steve-lunarg7afe1342017-03-18 22:24:14 -06001438 // Strip off the outer array dimension for those having an extra one.
1439 int size;
1440 if (type.isArray() && qualifier.isArrayedIo(language)) {
1441 TType elementType(type, 0);
1442 size = intermediate.computeTypeLocationSize(elementType);
1443 } else
1444 size = intermediate.computeTypeLocationSize(type);
1445
John Kessenich7dc630f2016-09-16 01:44:43 -06001446 if (qualifier.storage == EvqVaryingIn) {
1447 variable.getWritableType().getQualifier().layoutLocation = nextInLocation;
steve-lunarg7afe1342017-03-18 22:24:14 -06001448 nextInLocation += size;
John Kessenich7dc630f2016-09-16 01:44:43 -06001449 } else {
1450 variable.getWritableType().getQualifier().layoutLocation = nextOutLocation;
steve-lunarg7afe1342017-03-18 22:24:14 -06001451 nextOutLocation += size;
John Kessenich7dc630f2016-09-16 01:44:43 -06001452 }
1453 }
steve-lunarg858c9282017-01-07 08:54:10 -07001454
John Kessenichd3f11222016-11-05 10:15:53 -06001455 trackLinkage(variable);
John Kessenich7dc630f2016-09-16 01:44:43 -06001456 }
1457 };
1458
steve-lunarga2b01a02016-11-28 17:09:54 -07001459 if (wasFlattened(variable.getUniqueId())) {
1460 auto& memberList = flattenMap[variable.getUniqueId()].members;
John Kessenich7dc630f2016-09-16 01:44:43 -06001461 for (auto member = memberList.begin(); member != memberList.end(); ++member)
1462 assignLocation(**member);
steve-lunarga2e75312016-12-14 15:22:25 -07001463 } else if (wasSplit(variable.getUniqueId())) {
steve-lunarg132d3312016-12-19 15:48:01 -07001464 TVariable* splitIoVar = getSplitIoVar(&variable);
steve-lunarg4198b8b2017-03-09 19:10:57 -07001465 assignLocation(*splitIoVar);
steve-lunarga2e75312016-12-14 15:22:25 -07001466 } else {
John Kessenich7dc630f2016-09-16 01:44:43 -06001467 assignLocation(variable);
steve-lunarga2e75312016-12-14 15:22:25 -07001468 }
John Kessenich7dc630f2016-09-16 01:44:43 -06001469}
1470
John Kesseniche01a9bc2016-03-12 20:11:22 -07001471//
1472// Handle seeing a function declarator in the grammar. This is the precursor
1473// to recognizing a function prototype or function definition.
1474//
John Kessenich088d52b2017-03-11 17:55:28 -07001475void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype)
John Kesseniche01a9bc2016-03-12 20:11:22 -07001476{
1477 //
1478 // Multiple declarations of the same function name are allowed.
1479 //
1480 // If this is a definition, the definition production code will check for redefinitions
1481 // (we don't know at this point if it's a definition or not).
1482 //
John Kesseniche01a9bc2016-03-12 20:11:22 -07001483 bool builtIn;
1484 TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
1485 const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
1486
1487 if (prototype) {
1488 // All built-in functions are defined, even though they don't have a body.
1489 // Count their prototype as a definition instead.
1490 if (symbolTable.atBuiltInLevel())
1491 function.setDefined();
1492 else {
1493 if (prevDec && ! builtIn)
1494 symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const
1495 function.setPrototyped();
1496 }
1497 }
1498
1499 // This insert won't actually insert it if it's a duplicate signature, but it will still check for
1500 // other forms of name collisions.
1501 if (! symbolTable.insert(function))
1502 error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07001503}
1504
steve-lunarga2e75312016-12-14 15:22:25 -07001505// Add interstage IO variables to the linkage in canonical order.
1506void HlslParseContext::addInterstageIoToLinkage()
1507{
steve-lunarg46d54282017-01-07 09:07:14 -07001508 TSourceLoc loc;
1509 loc.init();
1510
steve-lunarg132d3312016-12-19 15:48:01 -07001511 std::vector<tInterstageIoData> io;
1512 io.reserve(interstageBuiltInIo.size());
steve-lunarga2e75312016-12-14 15:22:25 -07001513
steve-lunarg132d3312016-12-19 15:48:01 -07001514 for (auto ioVar = interstageBuiltInIo.begin(); ioVar != interstageBuiltInIo.end(); ++ioVar)
1515 io.push_back(ioVar->first);
steve-lunarga2e75312016-12-14 15:22:25 -07001516
steve-lunarg132d3312016-12-19 15:48:01 -07001517 // Our canonical order is the TBuiltInVariable numeric order.
1518 std::sort(io.begin(), io.end());
steve-lunarga2e75312016-12-14 15:22:25 -07001519
steve-lunarg46d54282017-01-07 09:07:14 -07001520 // We have to (potentially) track two IO blocks, one in, one out. E.g, a GS may have a
1521 // PerVertex block in both directions, possibly with different members.
steve-lunarg46d54282017-01-07 09:07:14 -07001522 for (int idx = 0; idx < int(io.size()); ++idx) {
1523 TVariable* var = interstageBuiltInIo[io[idx]];
1524
1525 // Add the loose interstage IO to the linkage
1526 if (var->getType().isLooseAndBuiltIn(language))
John Kessenich02467d82017-01-19 15:41:47 -07001527 trackLinkage(*var);
steve-lunarg46d54282017-01-07 09:07:14 -07001528 }
steve-lunarga2e75312016-12-14 15:22:25 -07001529}
1530
John Kesseniche01a9bc2016-03-12 20:11:22 -07001531//
John Kessenichecba76f2017-01-06 00:34:48 -07001532// Handle seeing the function prototype in front of a function definition in the grammar.
John Kesseniche01a9bc2016-03-12 20:11:22 -07001533// The body is handled after this function returns.
1534//
John Kessenichca71d942017-03-07 20:44:09 -07001535// Returns an aggregate of parameter-symbol nodes.
1536//
John Kessenichecba76f2017-01-06 00:34:48 -07001537TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
John Kessenich02467d82017-01-19 15:41:47 -07001538 const TAttributeMap& attributes, TIntermNode*& entryPointTree)
John Kesseniche01a9bc2016-03-12 20:11:22 -07001539{
1540 currentCaller = function.getMangledName();
1541 TSymbol* symbol = symbolTable.find(function.getMangledName());
1542 TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1543
1544 if (! prevDec)
1545 error(loc, "can't find function", function.getName().c_str(), "");
1546 // Note: 'prevDec' could be 'function' if this is the first time we've seen function
1547 // as it would have just been put in the symbol table. Otherwise, we're looking up
1548 // an earlier occurrence.
1549
1550 if (prevDec && prevDec->isDefined()) {
1551 // Then this function already has a body.
1552 error(loc, "function already has a body", function.getName().c_str(), "");
1553 }
1554 if (prevDec && ! prevDec->isDefined()) {
1555 prevDec->setDefined();
1556
1557 // Remember the return type for later checking for RETURN statements.
1558 currentFunctionType = &(prevDec->getType());
1559 } else
1560 currentFunctionType = new TType(EbtVoid);
1561 functionReturnsValue = false;
1562
John Kessenich94dfb7a2017-01-18 16:45:02 -07001563 // Entry points need different I/O and other handling, transform it so the
1564 // rest of this function doesn't care.
John Kessenich02467d82017-01-19 15:41:47 -07001565 entryPointTree = transformEntryPoint(loc, function, attributes);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001566
1567 //
1568 // New symbol table scope for body of function plus its arguments
1569 //
John Kessenich077e0522016-06-09 02:02:17 -06001570 pushScope();
John Kesseniche01a9bc2016-03-12 20:11:22 -07001571
1572 //
1573 // Insert parameters into the symbol table.
1574 // If the parameter has no name, it's not an error, just don't insert it
1575 // (could be used for unused args).
1576 //
John Kessenich7dc630f2016-09-16 01:44:43 -06001577 // Also, accumulate the list of parameters into the AST, so lower level code
John Kesseniche01a9bc2016-03-12 20:11:22 -07001578 // knows where to find parameters.
1579 //
1580 TIntermAggregate* paramNodes = new TIntermAggregate;
1581 for (int i = 0; i < function.getParamCount(); i++) {
1582 TParameter& param = function[i];
1583 if (param.name != nullptr) {
John Kessenich02467d82017-01-19 15:41:47 -07001584 TVariable *variable = new TVariable(param.name, *param.type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001585
John Kessenich37789792017-03-21 23:56:40 -06001586 if (i == 0 && function.hasImplicitThis()) {
John Kessenich7a41f962017-03-22 11:38:22 -06001587 // Anonymous 'this' members are already in a symbol-table level,
1588 // and we need to know what function parameter to map them to.
John Kessenich37789792017-03-21 23:56:40 -06001589 symbolTable.makeInternalVariable(*variable);
1590 pushImplicitThis(variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001591 }
John Kessenich7a41f962017-03-22 11:38:22 -06001592 // Insert the parameters with name in the symbol table.
1593 if (! symbolTable.insert(*variable))
1594 error(loc, "redefinition", variable->getName().c_str(), "");
John Kessenich37789792017-03-21 23:56:40 -06001595 // Add the parameter to the AST
1596 paramNodes = intermediate.growAggregate(paramNodes,
1597 intermediate.addSymbol(*variable, loc),
1598 loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001599 } else
John Kessenich1c7e7072016-04-03 20:36:48 -06001600 paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001601 }
John Kessenich37789792017-03-21 23:56:40 -06001602 if (function.hasIllegalImplicitThis())
1603 pushImplicitThis(nullptr);
steve-lunarga2e75312016-12-14 15:22:25 -07001604
John Kesseniche01a9bc2016-03-12 20:11:22 -07001605 intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
1606 loopNestingLevel = 0;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001607 controlFlowNestingLevel = 0;
John Kessenich517fe7a2016-11-26 13:31:47 -07001608 postEntryPointReturn = false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001609
John Kessenich94dfb7a2017-01-18 16:45:02 -07001610 return paramNodes;
1611}
John Kessenichecba76f2017-01-06 00:34:48 -07001612
steve-lunarge752f462017-03-22 18:39:25 -06001613
1614// Handle all [attrib] attribute for the shader entry point
steve-lunarge7412492017-03-23 11:56:07 -06001615void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributeMap& attributes)
John Kessenich94dfb7a2017-01-18 16:45:02 -07001616{
John Kessenich02467d82017-01-19 15:41:47 -07001617 // Handle entry-point function attributes
John Kessenich94dfb7a2017-01-18 16:45:02 -07001618 const TIntermAggregate* numThreads = attributes[EatNumThreads];
1619 if (numThreads != nullptr) {
1620 const TIntermSequence& sequence = numThreads->getSequence();
1621
1622 for (int lid = 0; lid < int(sequence.size()); ++lid)
1623 intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
1624 }
steve-lunarg858c9282017-01-07 08:54:10 -07001625
1626 // MaxVertexCount
John Kessenich94dfb7a2017-01-18 16:45:02 -07001627 const TIntermAggregate* maxVertexCount = attributes[EatMaxVertexCount];
steve-lunarg858c9282017-01-07 08:54:10 -07001628 if (maxVertexCount != nullptr) {
1629 if (! intermediate.setVertices(maxVertexCount->getSequence()[0]->getAsConstantUnion()->getConstArray()[0].getIConst())) {
1630 error(loc, "cannot change previously set maxvertexcount attribute", "", "");
1631 }
1632 }
1633
1634 // Handle [patchconstantfunction("...")]
1635 const TIntermAggregate* pcfAttr = attributes[EatPatchConstantFunc];
1636 if (pcfAttr != nullptr) {
1637 const TConstUnion& pcfName = pcfAttr->getSequence()[0]->getAsConstantUnion()->getConstArray()[0];
1638
1639 if (pcfName.getType() != EbtString) {
1640 error(loc, "invalid patch constant function", "", "");
1641 } else {
1642 patchConstantFunctionName = *pcfName.getSConst();
1643 }
1644 }
1645
1646 // Handle [domain("...")]
1647 const TIntermAggregate* domainAttr = attributes[EatDomain];
1648 if (domainAttr != nullptr) {
1649 const TConstUnion& domainType = domainAttr->getSequence()[0]->getAsConstantUnion()->getConstArray()[0];
1650 if (domainType.getType() != EbtString) {
1651 error(loc, "invalid domain", "", "");
1652 } else {
1653 TString domainStr = *domainType.getSConst();
1654 std::transform(domainStr.begin(), domainStr.end(), domainStr.begin(), ::tolower);
1655
1656 TLayoutGeometry domain = ElgNone;
1657
1658 if (domainStr == "tri") {
1659 domain = ElgTriangles;
1660 } else if (domainStr == "quad") {
1661 domain = ElgQuads;
1662 } else if (domainStr == "isoline") {
1663 domain = ElgIsolines;
1664 } else {
1665 error(loc, "unsupported domain type", domainStr.c_str(), "");
1666 }
1667
steve-lunarge752f462017-03-22 18:39:25 -06001668 if (language == EShLangTessEvaluation) {
1669 if (! intermediate.setInputPrimitive(domain))
1670 error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
1671 } else {
1672 if (! intermediate.setOutputPrimitive(domain))
1673 error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
steve-lunarg858c9282017-01-07 08:54:10 -07001674 }
1675 }
1676 }
1677
1678 // Handle [outputtoplogy("...")]
1679 const TIntermAggregate* topologyAttr = attributes[EatOutputTopology];
1680 if (topologyAttr != nullptr) {
1681 const TConstUnion& topoType = topologyAttr->getSequence()[0]->getAsConstantUnion()->getConstArray()[0];
1682 if (topoType.getType() != EbtString) {
1683 error(loc, "invalid outputtoplogy", "", "");
1684 } else {
1685 TString topologyStr = *topoType.getSConst();
1686 std::transform(topologyStr.begin(), topologyStr.end(), topologyStr.begin(), ::tolower);
1687
1688 TVertexOrder topology = EvoNone;
1689
1690 if (topologyStr == "point") {
1691 topology = EvoNone;
1692 } else if (topologyStr == "line") {
1693 topology = EvoNone;
1694 } else if (topologyStr == "triangle_cw") {
1695 topology = EvoCw;
1696 } else if (topologyStr == "triangle_ccw") {
1697 topology = EvoCcw;
1698 } else {
1699 error(loc, "unsupported outputtoplogy type", topologyStr.c_str(), "");
1700 }
1701
1702 if (topology != EvoNone) {
1703 if (! intermediate.setVertexOrder(topology)) {
1704 error(loc, "cannot change previously set outputtopology", TQualifier::getVertexOrderString(topology), "");
1705 }
1706 }
1707 }
1708 }
1709
1710 // Handle [partitioning("...")]
1711 const TIntermAggregate* partitionAttr = attributes[EatPartitioning];
1712 if (partitionAttr != nullptr) {
1713 const TConstUnion& partType = partitionAttr->getSequence()[0]->getAsConstantUnion()->getConstArray()[0];
1714 if (partType.getType() != EbtString) {
1715 error(loc, "invalid partitioning", "", "");
1716 } else {
1717 TString partitionStr = *partType.getSConst();
1718 std::transform(partitionStr.begin(), partitionStr.end(), partitionStr.begin(), ::tolower);
1719
1720 TVertexSpacing partitioning = EvsNone;
1721
1722 if (partitionStr == "integer") {
1723 partitioning = EvsEqual;
1724 } else if (partitionStr == "fractional_even") {
1725 partitioning = EvsFractionalEven;
1726 } else if (partitionStr == "fractional_odd") {
1727 partitioning = EvsFractionalOdd;
1728 //} else if (partition == "pow2") { // TODO: currently nothing to map this to.
1729 } else {
1730 error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
1731 }
1732
1733 if (! intermediate.setVertexSpacing(partitioning))
1734 error(loc, "cannot change previously set partitioning", TQualifier::getVertexSpacingString(partitioning), "");
1735 }
1736 }
1737
1738 // Handle [outputcontrolpoints("...")]
1739 const TIntermAggregate* outputControlPoints = attributes[EatOutputControlPoints];
1740 if (outputControlPoints != nullptr) {
1741 const TConstUnion& ctrlPointConst = outputControlPoints->getSequence()[0]->getAsConstantUnion()->getConstArray()[0];
1742 if (ctrlPointConst.getType() != EbtInt) {
1743 error(loc, "invalid outputcontrolpoints", "", "");
1744 } else {
1745 const int ctrlPoints = ctrlPointConst.getIConst();
1746 if (! intermediate.setVertices(ctrlPoints)) {
1747 error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
1748 }
1749 }
1750 }
steve-lunarge752f462017-03-22 18:39:25 -06001751}
1752
1753//
1754// Do all special handling for the entry point, including wrapping
1755// the shader's entry point with the official entry point that will call it.
1756//
1757// The following:
1758//
1759// retType shaderEntryPoint(args...) // shader declared entry point
1760// { body }
1761//
1762// Becomes
1763//
1764// out retType ret;
1765// in iargs<that are input>...;
1766// out oargs<that are output> ...;
1767//
1768// void shaderEntryPoint() // synthesized, but official, entry point
1769// {
1770// args<that are input> = iargs...;
1771// ret = @shaderEntryPoint(args...);
1772// oargs = args<that are output>...;
1773// }
1774//
1775// The symbol table will still map the original entry point name to the
1776// the modified function and it's new name:
1777//
1778// symbol table: shaderEntryPoint -> @shaderEntryPoint
1779//
1780// Returns nullptr if no entry-point tree was built, otherwise, returns
1781// a subtree that creates the entry point.
1782//
1783TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction, const TAttributeMap& attributes)
1784{
steve-lunargf38cca32017-04-03 09:27:53 -06001785 // Return true if this is a tessellation patch constant function input to a domain shader.
1786 const auto isDsPcfInput = [this](const TType& type) {
1787 return language == EShLangTessEvaluation &&
1788 type.contains([](const TType* t) {
1789 return t->getQualifier().builtIn == EbvTessLevelOuter ||
1790 t->getQualifier().builtIn == EbvTessLevelInner;
1791 });
1792 };
1793
steve-lunarge752f462017-03-22 18:39:25 -06001794 // if we aren't in the entry point, fix the IO as such and exit
1795 if (userFunction.getName().compare(intermediate.getEntryPointName().c_str()) != 0) {
1796 remapNonEntryPointIO(userFunction);
1797 return nullptr;
1798 }
1799
1800 entryPointFunction = &userFunction; // needed in finish()
1801
1802 // Handle entry point attributes
steve-lunarge7412492017-03-23 11:56:07 -06001803 handleEntryPointAttributes(loc, attributes);
steve-lunarge752f462017-03-22 18:39:25 -06001804
1805 // entry point logic...
John Kessenich02467d82017-01-19 15:41:47 -07001806
1807 // Move parameters and return value to shader in/out
1808 TVariable* entryPointOutput; // gets created in remapEntryPointIO
1809 TVector<TVariable*> inputs;
1810 TVector<TVariable*> outputs;
1811 remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs);
1812
1813 // Further this return/in/out transform by flattening, splitting, and assigning locations
1814 const auto makeVariableInOut = [&](TVariable& variable) {
1815 if (variable.getType().isStruct()) {
1816 const TStorageQualifier qualifier = variable.getType().getQualifier().storage;
1817 // struct inputs to the vertex stage and outputs from the fragment stage must be flattened
1818 if ((language == EShLangVertex && qualifier == EvqVaryingIn) ||
1819 (language == EShLangFragment && qualifier == EvqVaryingOut))
1820 flatten(loc, variable);
1821 // Mixture of IO and non-IO must be split
1822 else if (variable.getType().containsBuiltInInterstageIO(language))
1823 split(variable);
1824 }
steve-lunarg067eb9b2017-04-01 15:34:48 -06001825
John Kessenich02467d82017-01-19 15:41:47 -07001826 assignLocations(variable);
1827 };
1828 if (entryPointOutput)
1829 makeVariableInOut(*entryPointOutput);
1830 for (auto it = inputs.begin(); it != inputs.end(); ++it)
steve-lunargf38cca32017-04-03 09:27:53 -06001831 if (!isDsPcfInput((*it)->getType())) // skip domain shader PCF input (see comment below)
1832 makeVariableInOut(*(*it));
John Kessenich02467d82017-01-19 15:41:47 -07001833 for (auto it = outputs.begin(); it != outputs.end(); ++it)
1834 makeVariableInOut(*(*it));
1835
steve-lunargf38cca32017-04-03 09:27:53 -06001836 // In the domain shader, PCF input must be at the end of the linkage. That's because in the
1837 // hull shader there is no ordering: the output comes from the separate PCF, which does not
1838 // participate in the argument list. That is always put at the end of the HS linkage, so the
1839 // input side of the DS must match. The argument may be in any position in the DS argument list
1840 // however, so this ensures the linkage is built in the correct order regardless of argument order.
1841 if (language == EShLangTessEvaluation) {
1842 for (auto it = inputs.begin(); it != inputs.end(); ++it)
1843 if (isDsPcfInput((*it)->getType())) // skip domain shader PCF input (see comment below)
1844 makeVariableInOut(*(*it));
1845 }
1846
John Kessenich02467d82017-01-19 15:41:47 -07001847 // Synthesize the call
1848
1849 pushScope(); // matches the one in handleFunctionBody()
1850
1851 // new signature
1852 TType voidType(EbtVoid);
1853 TFunction synthEntryPoint(&userFunction.getName(), voidType);
1854 TIntermAggregate* synthParams = new TIntermAggregate();
1855 intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc);
1856 intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str());
1857 intermediate.incrementEntryPointCount();
1858 TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table
1859
1860 // change original name
1861 userFunction.addPrefix("@"); // change the name in the function, but not in the symbol table
1862
1863 // Copy inputs (shader-in -> calling arg), while building up the call node
1864 TVector<TVariable*> argVars;
1865 TIntermAggregate* synthBody = new TIntermAggregate();
1866 auto inputIt = inputs.begin();
1867 TIntermTyped* callingArgs = nullptr;
steve-lunarg08e0c082017-03-29 20:01:13 -06001868
John Kessenich02467d82017-01-19 15:41:47 -07001869 for (int i = 0; i < userFunction.getParamCount(); i++) {
1870 TParameter& param = userFunction[i];
1871 argVars.push_back(makeInternalVariable(*param.name, *param.type));
steve-lunarg08e0c082017-03-29 20:01:13 -06001872
John Kessenich02467d82017-01-19 15:41:47 -07001873 argVars.back()->getWritableType().getQualifier().makeTemporary();
steve-lunarg08e0c082017-03-29 20:01:13 -06001874
John Kessenich02467d82017-01-19 15:41:47 -07001875 TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
steve-lunarg08e0c082017-03-29 20:01:13 -06001876
John Kessenich02467d82017-01-19 15:41:47 -07001877 handleFunctionArgument(&callee, callingArgs, arg);
1878 if (param.type->getQualifier().isParamInput()) {
1879 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
1880 intermediate.addSymbol(**inputIt)));
1881 inputIt++;
1882 }
1883 }
1884
1885 // Call
1886 currentCaller = synthEntryPoint.getMangledName();
1887 TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
1888 currentCaller = userFunction.getMangledName();
1889
1890 // Return value
steve-lunarge752f462017-03-22 18:39:25 -06001891 if (entryPointOutput) {
1892 TIntermTyped* returnAssign;
1893
steve-lunarge7412492017-03-23 11:56:07 -06001894 // For hull shaders, the wrapped entry point return value is written to
1895 // an array element as indexed by invocation ID, which we might have to make up.
1896 // This is required to match SPIR-V semantics.
steve-lunarge752f462017-03-22 18:39:25 -06001897 if (language == EShLangTessControl) {
1898 TIntermSymbol* invocationIdSym = findLinkageSymbol(EbvInvocationId);
1899
1900 // If there is no user declared invocation ID, we must make one.
1901 if (invocationIdSym == nullptr) {
1902 TType invocationIdType(EbtUint, EvqIn, 1);
1903 TString* invocationIdName = NewPoolTString("InvocationId");
1904 invocationIdType.getQualifier().builtIn = EbvInvocationId;
1905
1906 TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType);
1907
1908 globalQualifierFix(loc, variable->getWritableType().getQualifier());
1909 trackLinkage(*variable);
1910
1911 invocationIdSym = intermediate.addSymbol(*variable);
1912 }
1913
1914 TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput),
1915 invocationIdSym, loc);
1916 element->setType(callReturn->getType());
1917
1918 returnAssign = handleAssign(loc, EOpAssign, element, callReturn);
1919 } else {
1920 returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn);
1921 }
1922
1923 intermediate.growAggregate(synthBody, returnAssign);
1924 } else
John Kessenich02467d82017-01-19 15:41:47 -07001925 intermediate.growAggregate(synthBody, callReturn);
1926
1927 // Output copies
1928 auto outputIt = outputs.begin();
1929 for (int i = 0; i < userFunction.getParamCount(); i++) {
1930 TParameter& param = userFunction[i];
steve-lunarg08e0c082017-03-29 20:01:13 -06001931
1932 // GS outputs are via emit, so we do not copy them here.
John Kessenich02467d82017-01-19 15:41:47 -07001933 if (param.type->getQualifier().isParamOutput()) {
steve-lunarg08e0c082017-03-29 20:01:13 -06001934 if (param.declaredBuiltIn == EbvGsOutputStream) {
1935 // GS output stream does not assign outputs here: it's the Append() method
1936 // which writes to the output, probably multiple times separated by Emit.
1937 // We merely remember the output to use, here.
1938 gsStreamOutput = *outputIt;
1939 } else {
1940 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign,
1941 intermediate.addSymbol(**outputIt),
1942 intermediate.addSymbol(*argVars[i])));
1943 }
1944
John Kessenich02467d82017-01-19 15:41:47 -07001945 outputIt++;
1946 }
1947 }
1948
1949 // Put the pieces together to form a full function subtree
1950 // for the synthesized entry point.
1951 synthBody->setOperator(EOpSequence);
1952 TIntermNode* synthFunctionDef = synthParams;
1953 handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef);
1954
steve-lunarg858c9282017-01-07 08:54:10 -07001955 entryPointFunctionBody = synthBody;
1956
John Kessenich02467d82017-01-19 15:41:47 -07001957 return synthFunctionDef;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001958}
1959
John Kessenicha3051662016-09-02 19:13:36 -06001960void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody, TIntermNode*& node)
1961{
1962 node = intermediate.growAggregate(node, functionBody);
1963 intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc);
1964 node->getAsAggregate()->setName(function.getMangledName().c_str());
1965
1966 popScope();
John Kessenich37789792017-03-21 23:56:40 -06001967 if (function.hasImplicitThis())
1968 popImplicitThis();
John Kessenicha3051662016-09-02 19:13:36 -06001969
1970 if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue)
1971 error(loc, "function does not return a value:", "", function.getName().c_str());
1972}
1973
John Kessenich830b0cc2016-08-29 18:10:47 -06001974// AST I/O is done through shader globals declared in the 'in' or 'out'
1975// storage class. An HLSL entry point has a return value, input parameters
1976// and output parameters. These need to get remapped to the AST I/O.
John Kessenich727b3742017-02-03 17:57:55 -07001977void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue,
John Kessenich02467d82017-01-19 15:41:47 -07001978 TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
John Kessenich830b0cc2016-08-29 18:10:47 -06001979{
John Kessenichbf472862017-02-05 20:27:30 -07001980 // Do the actual work to make a type be a shader input or output variable,
1981 // and clear the original to be non-IO (for use as a normal function parameter/return).
baldurk5d5db802017-03-09 17:48:59 +00001982 const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* {
John Kessenich727b3742017-02-03 17:57:55 -07001983 TVariable* ioVariable = makeInternalVariable(name, type);
John Kessenichbf472862017-02-05 20:27:30 -07001984 clearUniformInputOutput(type.getQualifier());
John Kessenich727b3742017-02-03 17:57:55 -07001985 if (type.getStruct() != nullptr) {
John Kessenichbf472862017-02-05 20:27:30 -07001986 auto newLists = ioTypeMap.find(ioVariable->getType().getStruct());
1987 if (newLists != ioTypeMap.end()) {
1988 if (storage == EvqVaryingIn && newLists->second.input)
1989 ioVariable->getWritableType().setStruct(newLists->second.input);
1990 else if (storage == EvqVaryingOut && newLists->second.output)
1991 ioVariable->getWritableType().setStruct(newLists->second.output);
1992 }
John Kessenich9e079532016-09-02 20:05:19 -06001993 }
steve-lunarg7afe1342017-03-18 22:24:14 -06001994 if (storage == EvqVaryingIn) {
John Kessenichbf472862017-02-05 20:27:30 -07001995 correctInput(ioVariable->getWritableType().getQualifier());
steve-lunarg7afe1342017-03-18 22:24:14 -06001996 if (language == EShLangTessEvaluation)
1997 if (!ioVariable->getType().isArray())
1998 ioVariable->getWritableType().getQualifier().patch = true;
1999 } else {
John Kessenichbf472862017-02-05 20:27:30 -07002000 correctOutput(ioVariable->getWritableType().getQualifier());
steve-lunarg7afe1342017-03-18 22:24:14 -06002001 }
John Kessenichbf472862017-02-05 20:27:30 -07002002 ioVariable->getWritableType().getQualifier().storage = storage;
steve-lunarg067eb9b2017-04-01 15:34:48 -06002003
steve-lunargccb076a2017-04-05 11:03:02 -06002004 fixBuiltInIoType(ioVariable->getWritableType());
2005
John Kessenich727b3742017-02-03 17:57:55 -07002006 return ioVariable;
John Kessenich9e079532016-09-02 20:05:19 -06002007 };
2008
John Kessenich830b0cc2016-08-29 18:10:47 -06002009 // return value is actually a shader-scoped output (out)
steve-lunarge752f462017-03-22 18:39:25 -06002010 if (function.getType().getBasicType() == EbtVoid) {
John Kessenich02467d82017-01-19 15:41:47 -07002011 returnValue = nullptr;
steve-lunarge752f462017-03-22 18:39:25 -06002012 } else {
2013 if (language == EShLangTessControl) {
2014 // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an
2015 // array in SPIR-V semantics. We'll write to it indexed by invocation ID.
2016
2017 returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2018
2019 TType outputType;
2020 outputType.shallowCopy(function.getType());
2021
2022 // vertices has necessarily already been set when handling entry point attributes.
2023 TArraySizes arraySizes;
2024 arraySizes.addInnerSize(intermediate.getVertices());
2025 outputType.newArraySizes(arraySizes);
2026
2027 clearUniformInputOutput(function.getWritableType().getQualifier());
2028 returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut);
2029 } else {
2030 returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
2031 }
2032 }
John Kessenich830b0cc2016-08-29 18:10:47 -06002033
2034 // parameters are actually shader-scoped inputs and outputs (in or out)
2035 for (int i = 0; i < function.getParamCount(); i++) {
John Kessenichcd0a78a2016-09-09 16:32:09 -06002036 TType& paramType = *function[i].type;
John Kessenich02467d82017-01-19 15:41:47 -07002037 if (paramType.getQualifier().isParamInput()) {
John Kessenichbf472862017-02-05 20:27:30 -07002038 TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
John Kessenich02467d82017-01-19 15:41:47 -07002039 inputs.push_back(argAsGlobal);
steve-lunarg067eb9b2017-04-01 15:34:48 -06002040
2041 if (function[i].declaredBuiltIn == EbvInputPatch)
2042 inputPatch = argAsGlobal;
John Kessenich02467d82017-01-19 15:41:47 -07002043 }
2044 if (paramType.getQualifier().isParamOutput()) {
John Kessenichbf472862017-02-05 20:27:30 -07002045 TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut);
John Kessenich02467d82017-01-19 15:41:47 -07002046 outputs.push_back(argAsGlobal);
John Kessenich02467d82017-01-19 15:41:47 -07002047 }
John Kessenich830b0cc2016-08-29 18:10:47 -06002048 }
2049}
2050
John Kessenich07350f32016-09-02 20:23:27 -06002051// An HLSL function that looks like an entry point, but is not,
2052// declares entry point IO built-ins, but these have to be undone.
John Kessenich6fccb3c2016-09-19 16:01:41 -06002053void HlslParseContext::remapNonEntryPointIO(TFunction& function)
John Kessenich07350f32016-09-02 20:23:27 -06002054{
John Kessenich07350f32016-09-02 20:23:27 -06002055 // return value
2056 if (function.getType().getBasicType() != EbtVoid)
John Kessenichbf472862017-02-05 20:27:30 -07002057 clearUniformInputOutput(function.getWritableType().getQualifier());
John Kessenich07350f32016-09-02 20:23:27 -06002058
steve-lunargdd8287a2017-02-23 18:04:12 -07002059 // parameters.
2060 // References to structuredbuffer types are left unmodified
John Kessenich07350f32016-09-02 20:23:27 -06002061 for (int i = 0; i < function.getParamCount(); i++)
steve-lunargdd8287a2017-02-23 18:04:12 -07002062 if (!isReference(*function[i].type))
2063 clearUniformInputOutput(function[i].type->getQualifier());
John Kessenich07350f32016-09-02 20:23:27 -06002064}
2065
steve-lunargc4a13072016-08-09 11:28:03 -06002066// Handle function returns, including type conversions to the function return type
2067// if necessary.
2068TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
2069{
John Kessenicha3051662016-09-02 19:13:36 -06002070 functionReturnsValue = true;
John Kessenich6a70eb72016-08-28 15:00:23 -06002071
steve-lunargc4a13072016-08-09 11:28:03 -06002072 if (currentFunctionType->getBasicType() == EbtVoid) {
2073 error(loc, "void function cannot return a value", "return", "");
2074 return intermediate.addBranch(EOpReturn, loc);
2075 } else if (*currentFunctionType != value->getType()) {
John Kessenich087a4542016-10-06 16:56:54 -06002076 value = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
2077 if (value && *currentFunctionType != value->getType())
2078 value = intermediate.addShapeConversion(EOpReturn, *currentFunctionType, value);
2079 if (value == nullptr) {
steve-lunargc4a13072016-08-09 11:28:03 -06002080 error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
John Kessenich087a4542016-10-06 16:56:54 -06002081 return value;
steve-lunargc4a13072016-08-09 11:28:03 -06002082 }
John Kessenich6a70eb72016-08-28 15:00:23 -06002083 }
2084
John Kessenich02467d82017-01-19 15:41:47 -07002085 return intermediate.addBranch(EOpReturn, value, loc);
steve-lunargc4a13072016-08-09 11:28:03 -06002086}
2087
steve-lunarga2e75312016-12-14 15:22:25 -07002088void HlslParseContext::handleFunctionArgument(TFunction* function,
2089 TIntermTyped*& arguments, TIntermTyped* newArg)
John Kessenichd016be12016-03-13 11:24:20 -06002090{
steve-lunarg26d31452016-12-23 18:56:57 -07002091 TParameter param = { 0, new TType, nullptr };
John Kessenich4678ca92016-05-13 09:33:42 -06002092 param.type->shallowCopy(newArg->getType());
steve-lunarga2e75312016-12-14 15:22:25 -07002093
John Kessenichd016be12016-03-13 11:24:20 -06002094 function->addParameter(param);
John Kessenich4678ca92016-05-13 09:33:42 -06002095 if (arguments)
2096 arguments = intermediate.growAggregate(arguments, newArg);
2097 else
2098 arguments = newArg;
John Kessenichd016be12016-03-13 11:24:20 -06002099}
2100
John Kessenichd21baed2016-09-16 03:05:12 -06002101// Some simple source assignments need to be flattened to a sequence
John Kessenichfdf63472017-01-13 12:27:52 -07002102// of AST assignments. Catch these and flatten, otherwise, pass through
John Kessenichd21baed2016-09-16 03:05:12 -06002103// to intermediate.addAssign().
John Kessenichfdf63472017-01-13 12:27:52 -07002104//
2105// Also, assignment to matrix swizzles requires multiple component assignments,
2106// intercept those as well.
2107TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right)
John Kessenichd21baed2016-09-16 03:05:12 -06002108{
steve-lunarg90707962016-10-07 19:35:40 -06002109 if (left == nullptr || right == nullptr)
2110 return nullptr;
2111
John Kessenichfdf63472017-01-13 12:27:52 -07002112 if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle)
2113 return handleAssignToMatrixSwizzle(loc, op, left, right);
2114
steve-lunarg132d3312016-12-19 15:48:01 -07002115 const bool isSplitLeft = wasSplit(left);
2116 const bool isSplitRight = wasSplit(right);
John Kessenichd2ce8382016-09-16 19:44:00 -06002117
steve-lunarg132d3312016-12-19 15:48:01 -07002118 const bool isFlattenLeft = wasFlattened(left);
2119 const bool isFlattenRight = wasFlattened(right);
steve-lunarga2e75312016-12-14 15:22:25 -07002120
2121 // OK to do a single assign if both are split, or both are unsplit. But if one is and the other
John Kessenichfdf63472017-01-13 12:27:52 -07002122 // isn't, we fall back to a member-wise copy.
steve-lunarg132d3312016-12-19 15:48:01 -07002123 if (! isFlattenLeft && ! isFlattenRight && !isSplitLeft && !isSplitRight)
John Kessenichd21baed2016-09-16 03:05:12 -06002124 return intermediate.addAssign(op, left, right, loc);
2125
steve-lunarge0b9deb2016-09-16 13:26:37 -06002126 TIntermAggregate* assignList = nullptr;
John Kessenichf9115002016-09-18 23:10:22 -06002127 const TVector<TVariable*>* leftVariables = nullptr;
2128 const TVector<TVariable*>* rightVariables = nullptr;
steve-lunarge0b9deb2016-09-16 13:26:37 -06002129
steve-lunarg2199c242016-10-02 22:13:22 -06002130 // A temporary to store the right node's value, so we don't keep indirecting into it
2131 // if it's not a simple symbol.
2132 TVariable* rhsTempVar = nullptr;
2133
2134 // If the RHS is a simple symbol node, we'll copy it for each member.
2135 TIntermSymbol* cloneSymNode = nullptr;
2136
steve-lunarg2199c242016-10-02 22:13:22 -06002137 int memberCount = 0;
2138
2139 // Track how many items there are to copy.
2140 if (left->getType().isStruct())
Alex Szpakowski49ad2b72016-10-08 22:07:20 -03002141 memberCount = (int)left->getType().getStruct()->size();
steve-lunarg2199c242016-10-02 22:13:22 -06002142 if (left->getType().isArray())
2143 memberCount = left->getType().getCumulativeArraySize();
2144
steve-lunarg132d3312016-12-19 15:48:01 -07002145 if (isFlattenLeft)
steve-lunarga2b01a02016-11-28 17:09:54 -07002146 leftVariables = &flattenMap.find(left->getAsSymbolNode()->getId())->second.members;
steve-lunarg2199c242016-10-02 22:13:22 -06002147
steve-lunarg132d3312016-12-19 15:48:01 -07002148 if (isFlattenRight) {
steve-lunarga2b01a02016-11-28 17:09:54 -07002149 rightVariables = &flattenMap.find(right->getAsSymbolNode()->getId())->second.members;
steve-lunarg2199c242016-10-02 22:13:22 -06002150 } else {
2151 // The RHS is not flattened. There are several cases:
2152 // 1. 1 item to copy: Use the RHS directly.
2153 // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp.
2154 // 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 -07002155
steve-lunarg2199c242016-10-02 22:13:22 -06002156 if (memberCount <= 1) {
2157 // case 1: we'll use the symbol directly below. Nothing to do.
2158 } else {
2159 if (right->getAsSymbolNode() != nullptr) {
2160 // case 2: we'll copy the symbol per iteration below.
2161 cloneSymNode = right->getAsSymbolNode();
2162 } else {
2163 // case 3: assign to a temp, and indirect into that.
2164 rhsTempVar = makeInternalVariable("flattenTemp", right->getType());
2165 rhsTempVar->getWritableType().getQualifier().makeTemporary();
2166 TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc);
2167
2168 // Add this to the aggregate being built.
2169 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, noFlattenRHS, right, loc), loc);
2170 }
2171 }
2172 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06002173
steve-lunarga2b01a02016-11-28 17:09:54 -07002174 int memberIdx = 0;
2175
steve-lunargec712eb2017-02-02 16:32:02 -07002176 // When dealing with split arrayed structures of builtins, the arrayness is moved to the extracted builtin
2177 // variables, which is awkward when copying between split and unsplit structures. This variable tracks
2178 // array indirections so they can be percolated from outer structs to inner variables.
2179 std::vector <int> arrayElement;
2180
steve-lunarg132d3312016-12-19 15:48:01 -07002181 // We track the outer-most aggregate, so that we can use its storage class later.
2182 const TIntermTyped* outerLeft = left;
2183 const TIntermTyped* outerRight = right;
2184
2185 const auto getMember = [&](bool isLeft, TIntermTyped* node, int member, TIntermTyped* splitNode, int splitMember) -> TIntermTyped * {
steve-lunarge0b9deb2016-09-16 13:26:37 -06002186 TIntermTyped* subTree;
steve-lunarga2e75312016-12-14 15:22:25 -07002187
steve-lunarg132d3312016-12-19 15:48:01 -07002188 const bool flattened = isLeft ? isFlattenLeft : isFlattenRight;
2189 const bool split = isLeft ? isSplitLeft : isSplitRight;
2190 const TIntermTyped* outer = isLeft ? outerLeft : outerRight;
2191 const TVector<TVariable*>& flatVariables = isLeft ? *leftVariables : *rightVariables;
steve-lunarg194f0f32017-03-17 18:51:05 -06002192
2193 // Index operator if it's an aggregate, else EOpNull
2194 const TOperator op = node->getType().isArray() ? EOpIndexDirect :
2195 node->getType().isStruct() ? EOpIndexDirectStruct : EOpNull;
2196
steve-lunarg132d3312016-12-19 15:48:01 -07002197 const TType derefType(node->getType(), member);
2198
steve-lunarg46d54282017-01-07 09:07:14 -07002199 if (split && derefType.isBuiltInInterstageIO(language)) {
steve-lunarg132d3312016-12-19 15:48:01 -07002200 // copy from interstage IO builtin if needed
baldurk5d5db802017-03-09 17:48:59 +00002201 subTree = intermediate.addSymbol(*interstageBuiltInIo.find(HlslParseContext::tInterstageIoData(derefType, outer->getType()))->second);
steve-lunargec712eb2017-02-02 16:32:02 -07002202
2203 // Arrayness of builtIn symbols isn't handled by the normal recursion: it's been extracted and moved to the builtin.
2204 if (subTree->getType().isArray() && !arrayElement.empty()) {
2205 const TType splitDerefType(subTree->getType(), arrayElement.back());
2206 subTree = intermediate.addIndex(EOpIndexDirect, subTree, intermediate.addConstantUnion(arrayElement.back(), loc), loc);
2207 subTree->setType(splitDerefType);
2208 }
steve-lunarg132d3312016-12-19 15:48:01 -07002209 } else if (flattened && isFinalFlattening(derefType)) {
2210 subTree = intermediate.addSymbol(*flatVariables[memberIdx++]);
steve-lunarga2b01a02016-11-28 17:09:54 -07002211 } else {
steve-lunarg194f0f32017-03-17 18:51:05 -06002212 if (op == EOpNull) {
2213 subTree = splitNode;
2214 } else {
2215 const TType splitDerefType(splitNode->getType(), splitMember);
steve-lunarg132d3312016-12-19 15:48:01 -07002216
steve-lunarg194f0f32017-03-17 18:51:05 -06002217 subTree = intermediate.addIndex(op, splitNode, intermediate.addConstantUnion(splitMember, loc), loc);
2218 subTree->setType(splitDerefType);
2219 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06002220 }
2221
2222 return subTree;
2223 };
2224
steve-lunarga2b01a02016-11-28 17:09:54 -07002225 // Use the proper RHS node: a new symbol from a TVariable, copy
2226 // of an TIntermSymbol node, or sometimes the right node directly.
2227 right = rhsTempVar ? intermediate.addSymbol(*rhsTempVar, loc) :
2228 cloneSymNode ? intermediate.addSymbol(*cloneSymNode) :
2229 right;
steve-lunarge0b9deb2016-09-16 13:26:37 -06002230
John Kessenichd21baed2016-09-16 03:05:12 -06002231 // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the
2232 // whole thing. So, we'll resort to an explicit type via std::function.
steve-lunarg132d3312016-12-19 15:48:01 -07002233 const std::function<void(TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight)>
2234 traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight) -> void {
John Kessenichd21baed2016-09-16 03:05:12 -06002235 // If we get here, we are assigning to or from a whole array or struct that must be
2236 // flattened, so have to do member-by-member assignment:
2237
steve-lunarg194f0f32017-03-17 18:51:05 -06002238 if (left->getType().isArray() || right->getType().isArray()) {
2239 const int elementsL = left->getType().isArray() ? left->getType().getOuterArraySize() : 1;
2240 const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1;
2241
2242 // The arrays may not be the same size, e.g, if the size has been forced for EbvTessLevelInner or Outer.
2243 const int elementsToCopy = std::min(elementsL, elementsR);
John Kessenichd2ce8382016-09-16 19:44:00 -06002244
steve-lunarg132d3312016-12-19 15:48:01 -07002245 // array case
steve-lunarg194f0f32017-03-17 18:51:05 -06002246 for (int element=0; element < elementsToCopy; ++element) {
steve-lunarg858c9282017-01-07 08:54:10 -07002247 arrayElement.push_back(element);
2248
steve-lunargec712eb2017-02-02 16:32:02 -07002249 // Add a new AST symbol node if we have a temp variable holding a complex RHS.
steve-lunarg132d3312016-12-19 15:48:01 -07002250 TIntermTyped* subLeft = getMember(true, left, element, left, element);
2251 TIntermTyped* subRight = getMember(false, right, element, right, element);
John Kessenichd2ce8382016-09-16 19:44:00 -06002252
steve-lunarg65cdff92017-01-19 15:18:00 -07002253 TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left, element, splitLeft, element) : subLeft;
2254 TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right, element, splitRight, element) : subRight;
2255
steve-lunarg194f0f32017-03-17 18:51:05 -06002256 traverse(subLeft, subRight, subSplitLeft, subSplitRight);
steve-lunargec712eb2017-02-02 16:32:02 -07002257
2258 arrayElement.pop_back();
John Kessenichd2ce8382016-09-16 19:44:00 -06002259 }
John Kessenichfcea3022016-09-16 21:16:04 -06002260 } else if (left->getType().isStruct()) {
2261 // struct case
steve-lunarga2e75312016-12-14 15:22:25 -07002262 const auto& membersL = *left->getType().getStruct();
2263 const auto& membersR = *right->getType().getStruct();
John Kessenichfcea3022016-09-16 21:16:04 -06002264
steve-lunarg132d3312016-12-19 15:48:01 -07002265 // These track the members in the split structures corresponding to the same in the unsplit structures,
2266 // which we traverse in parallel.
steve-lunarga2e75312016-12-14 15:22:25 -07002267 int memberL = 0;
2268 int memberR = 0;
steve-lunarge0b9deb2016-09-16 13:26:37 -06002269
steve-lunargd8e34c52017-03-24 08:56:37 -06002270 // Handle empty structure assignment
2271 if (int(membersL.size()) == 0 && int(membersR.size()) == 0)
2272 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
2273
steve-lunarga2e75312016-12-14 15:22:25 -07002274 for (int member = 0; member < int(membersL.size()); ++member) {
2275 const TType& typeL = *membersL[member].type;
2276 const TType& typeR = *membersR[member].type;
2277
steve-lunarg132d3312016-12-19 15:48:01 -07002278 TIntermTyped* subLeft = getMember(true, left, member, left, member);
2279 TIntermTyped* subRight = getMember(false, right, member, right, member);
steve-lunarga2e75312016-12-14 15:22:25 -07002280
steve-lunarg132d3312016-12-19 15:48:01 -07002281 // If there is no splitting, use the same values to avoid inefficiency.
2282 TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left, member, splitLeft, memberL) : subLeft;
2283 TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right, member, splitRight, memberR) : subRight;
2284
2285 // If this is the final flattening (no nested types below to flatten) we'll copy the member, else
2286 // recurse into the type hierarchy. However, if splitting the struct, that means we can copy a whole
2287 // subtree here IFF it does not itself contain any interstage built-in IO variables, so we only have to
2288 // recurse into it if there's something for splitting to do. That can save a lot of AST verbosity for
2289 // a bunch of memberwise copies.
steve-lunarg194f0f32017-03-17 18:51:05 -06002290 if ((!isFlattenLeft && !isFlattenRight &&
2291 !typeL.containsBuiltInInterstageIO(language) && !typeR.containsBuiltInInterstageIO(language))) {
steve-lunarg132d3312016-12-19 15:48:01 -07002292 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, subSplitLeft, subSplitRight, loc), loc);
steve-lunarga2e75312016-12-14 15:22:25 -07002293 } else {
steve-lunarg132d3312016-12-19 15:48:01 -07002294 traverse(subLeft, subRight, subSplitLeft, subSplitRight);
steve-lunarga2e75312016-12-14 15:22:25 -07002295 }
2296
steve-lunarg46d54282017-01-07 09:07:14 -07002297 memberL += (typeL.isBuiltInInterstageIO(language) ? 0 : 1);
2298 memberR += (typeR.isBuiltInInterstageIO(language) ? 0 : 1);
steve-lunarge0b9deb2016-09-16 13:26:37 -06002299 }
2300 } else {
steve-lunarg194f0f32017-03-17 18:51:05 -06002301 // Member copy
2302 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
steve-lunarge0b9deb2016-09-16 13:26:37 -06002303 }
2304
2305 };
2306
steve-lunarg132d3312016-12-19 15:48:01 -07002307 TIntermTyped* splitLeft = left;
2308 TIntermTyped* splitRight = right;
2309
2310 // If either left or right was a split structure, we must read or write it, but still have to
2311 // parallel-recurse through the unsplit structure to identify the builtin IO vars.
2312 if (isSplitLeft)
2313 splitLeft = intermediate.addSymbol(*getSplitIoVar(left), loc);
2314
2315 if (isSplitRight)
2316 splitRight = intermediate.addSymbol(*getSplitIoVar(right), loc);
2317
steve-lunarga2b01a02016-11-28 17:09:54 -07002318 // This makes the whole assignment, recursing through subtypes as needed.
steve-lunarg132d3312016-12-19 15:48:01 -07002319 traverse(left, right, splitLeft, splitRight);
steve-lunarge0b9deb2016-09-16 13:26:37 -06002320
2321 assert(assignList != nullptr);
John Kessenichd21baed2016-09-16 03:05:12 -06002322 assignList->setOperator(EOpSequence);
2323
2324 return assignList;
2325}
2326
John Kessenichfdf63472017-01-13 12:27:52 -07002327// An assignment to matrix swizzle must be decomposed into individual assignments.
2328// These must be selected component-wise from the RHS and stored component-wise
2329// into the LHS.
2330TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right)
2331{
2332 assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle);
2333
2334 if (op != EOpAssign)
2335 error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", "");
2336
2337 // isolate the matrix and swizzle nodes
2338 TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped();
2339 const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence();
2340
2341 // if the RHS isn't already a simple vector, let's store into one
2342 TIntermSymbol* vector = right->getAsSymbolNode();
2343 TIntermTyped* vectorAssign = nullptr;
2344 if (vector == nullptr) {
2345 // create a new intermediate vector variable to assign to
Graham Wihlidal6f332f32017-02-17 19:05:14 +01002346 TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2);
John Kessenichfdf63472017-01-13 12:27:52 -07002347 vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc);
2348
2349 // assign the right to the new vector
2350 vectorAssign = handleAssign(loc, op, vector, right);
2351 }
2352
2353 // Assign the vector components to the matrix components.
2354 // Store this as a sequence, so a single aggregate node represents this
2355 // entire operation.
2356 TIntermAggregate* result = intermediate.makeAggregate(vectorAssign);
2357 TType columnType(matrix->getType(), 0);
2358 TType componentType(columnType, 0);
2359 TType indexType(EbtInt);
2360 for (int i = 0; i < (int)swizzle.size(); i += 2) {
2361 // the right component, single index into the RHS vector
2362 TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector,
2363 intermediate.addConstantUnion(i/2, loc), loc);
2364
2365 // the left component, double index into the LHS matrix
2366 TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix,
2367 intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(),
2368 indexType, loc),
2369 loc);
2370 leftComp->setType(columnType);
2371 leftComp = intermediate.addIndex(EOpIndexDirect, leftComp,
2372 intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(),
2373 indexType, loc),
2374 loc);
2375 leftComp->setType(componentType);
2376
2377 // Add the assignment to the aggregate
2378 result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc));
2379 }
2380
2381 result->setOp(EOpSequence);
2382
2383 return result;
2384}
2385
LoopDawg58910702016-06-13 09:22:28 -06002386//
2387// HLSL atomic operations have slightly different arguments than
2388// GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic.
2389// This provides the post-decomposition equivalent opcode.
2390//
2391TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage)
2392{
2393 switch (op) {
steve-lunarg22322362016-10-19 10:15:25 -06002394 case EOpInterlockedAdd: return isImage ? EOpImageAtomicAdd : EOpAtomicAdd;
2395 case EOpInterlockedAnd: return isImage ? EOpImageAtomicAnd : EOpAtomicAnd;
LoopDawg58910702016-06-13 09:22:28 -06002396 case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap;
steve-lunarg22322362016-10-19 10:15:25 -06002397 case EOpInterlockedMax: return isImage ? EOpImageAtomicMax : EOpAtomicMax;
2398 case EOpInterlockedMin: return isImage ? EOpImageAtomicMin : EOpAtomicMin;
2399 case EOpInterlockedOr: return isImage ? EOpImageAtomicOr : EOpAtomicOr;
2400 case EOpInterlockedXor: return isImage ? EOpImageAtomicXor : EOpAtomicXor;
LoopDawg58910702016-06-13 09:22:28 -06002401 case EOpInterlockedExchange: return isImage ? EOpImageAtomicExchange : EOpAtomicExchange;
John Kessenichecba76f2017-01-06 00:34:48 -07002402 case EOpInterlockedCompareStore: // TODO: ...
LoopDawg58910702016-06-13 09:22:28 -06002403 default:
2404 error(loc, "unknown atomic operation", "unknown op", "");
2405 return EOpNull;
2406 }
2407}
2408
LoopDawg4624a022016-06-20 13:26:59 -06002409//
LoopDawga2b79912016-07-14 14:45:14 -06002410// Create a combined sampler/texture from separate sampler and texture.
LoopDawg4624a022016-06-20 13:26:59 -06002411//
LoopDawga2b79912016-07-14 14:45:14 -06002412TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, TIntermTyped* argSampler)
2413{
2414 TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler);
2415
2416 txcombine->getSequence().push_back(argTex);
2417 txcombine->getSequence().push_back(argSampler);
2418
2419 TSampler samplerType = argTex->getType().getSampler();
2420 samplerType.combined = true;
LoopDawga78b0292016-07-19 14:28:05 -06002421 samplerType.shadow = argSampler->getType().getSampler().shadow;
LoopDawga2b79912016-07-14 14:45:14 -06002422
2423 txcombine->setType(TType(samplerType, EvqTemporary));
2424 txcombine->setLoc(loc);
2425
2426 return txcombine;
2427}
2428
2429//
steve-lunarg5da1f032017-02-12 17:50:28 -07002430// Decompose structure buffer methods into AST
2431//
2432void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
2433{
2434 if (!node || !node->getAsOperator())
2435 return;
2436
2437 const TOperator op = node->getAsOperator()->getOp();
2438 TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
steve-lunargdd8287a2017-02-23 18:04:12 -07002439 if (argAggregate == nullptr)
steve-lunarg5da1f032017-02-12 17:50:28 -07002440 return;
2441
steve-lunargdb2e3b42017-03-31 12:47:34 -06002442 if (argAggregate->getSequence().empty())
2443 return;
2444
steve-lunargdd8287a2017-02-23 18:04:12 -07002445 // Buffer is the object upon which method is called, so always arg 0
2446 TIntermTyped* bufferObj = argAggregate->getSequence()[0]->getAsTyped();
2447
2448 // Index to obtain the runtime sized array out of the buffer.
2449 TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj);
2450 if (argArray == nullptr)
2451 return; // It might not be a struct buffer method.
2452
steve-lunarg5da1f032017-02-12 17:50:28 -07002453 switch (op) {
2454 case EOpMethodLoad:
2455 {
2456 TIntermTyped* argIndex = argAggregate->getSequence()[1]->getAsTyped(); // index
2457
2458 // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
2459 // buffer then, but that's what it calls itself.
2460 const bool isByteAddressBuffer = (argArray->getBasicType() == EbtUint);
2461 if (isByteAddressBuffer)
2462 argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true),
2463 loc, TType(EbtInt));
2464
2465 // Index into the array to find the item being loaded.
2466 const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
2467
2468 node = intermediate.addIndex(idxOp, argArray, argIndex, loc);
2469
2470 const TType derefType(argArray->getType(), 0);
2471 node->setType(derefType);
2472 }
2473
2474 break;
2475
2476 case EOpMethodLoad2:
2477 case EOpMethodLoad3:
2478 case EOpMethodLoad4:
2479 {
2480 TIntermTyped* argIndex = argAggregate->getSequence()[1]->getAsTyped(); // index
2481
2482 TOperator constructOp = EOpNull;
2483 int size = 0;
2484
2485 switch (op) {
2486 case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break;
2487 case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break;
2488 case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break;
2489 default: assert(0);
2490 }
2491
2492 TIntermTyped* body = nullptr;
2493
2494 // First, we'll store the address in a variable to avoid multiple shifts
2495 // (we must convert the byte address to an item address)
2496 TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
2497 intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt));
2498
2499 TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
2500 TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
2501
2502 body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
2503
2504 TIntermTyped* vec = nullptr;
2505
2506 // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2
2507 // address conversion.
2508 for (int idx=0; idx<size; ++idx) {
2509 TIntermTyped* offsetIdx = byteAddrIdxVar;
2510
2511 // add index offset
2512 if (idx != 0)
2513 offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, intermediate.addConstantUnion(idx, loc, true),
2514 loc, TType(EbtInt));
2515
2516 const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
2517
2518 vec = intermediate.growAggregate(vec, intermediate.addIndex(idxOp, argArray, offsetIdx, loc));
2519 }
2520
2521 vec->setType(TType(argArray->getBasicType(), EvqTemporary, size));
2522 vec->getAsAggregate()->setOperator(constructOp);
2523
2524 body = intermediate.growAggregate(body, vec);
2525 body->setType(vec->getType());
2526 body->getAsAggregate()->setOperator(EOpSequence);
2527
2528 node = body;
2529 }
2530
2531 break;
2532
2533 case EOpMethodStore:
2534 case EOpMethodStore2:
2535 case EOpMethodStore3:
2536 case EOpMethodStore4:
2537 {
2538 TIntermTyped* argIndex = argAggregate->getSequence()[1]->getAsTyped(); // address
2539 TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped(); // value
2540
2541 // Index into the array to find the item being loaded.
2542 // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
2543 // buffer then, but that's what it calls itself.
2544
2545 int size = 0;
2546
2547 switch (op) {
2548 case EOpMethodStore: size = 1; break;
2549 case EOpMethodStore2: size = 2; break;
2550 case EOpMethodStore3: size = 3; break;
2551 case EOpMethodStore4: size = 4; break;
2552 default: assert(0);
2553 }
2554
2555 TIntermAggregate* body = nullptr;
2556
2557 // First, we'll store the address in a variable to avoid multiple shifts
2558 // (we must convert the byte address to an item address)
2559 TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
2560 intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt));
2561
2562 TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
2563 TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
2564
2565 body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
2566
2567 for (int idx=0; idx<size; ++idx) {
2568 TIntermTyped* offsetIdx = byteAddrIdxVar;
2569 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
2570
2571 // add index offset
2572 if (idx != 0)
2573 offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, idxConst, loc, TType(EbtInt));
2574
2575 const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
2576
2577 TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
2578 TIntermTyped* rValue = (size == 1) ? argValue :
2579 intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc);
2580
2581 TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
2582
2583 body = intermediate.growAggregate(body, assign);
2584 }
2585
2586 body->setOperator(EOpSequence);
2587 node = body;
2588 }
2589
2590 break;
2591
2592 case EOpMethodGetDimensions:
2593 {
Daniel Koch197082c2017-02-28 09:01:43 -05002594 const int numArgs = (int)argAggregate->getSequence().size();
steve-lunarg5da1f032017-02-12 17:50:28 -07002595 TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped(); // out num items
2596 TIntermTyped* argStride = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr; // out stride
2597
2598 TIntermAggregate* body = nullptr;
2599
2600 // Length output:
2601 if (argArray->getType().isRuntimeSizedArray()) {
2602 TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray,
2603 argNumItems->getType());
2604 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc);
2605 body = intermediate.growAggregate(body, assign, loc);
2606 } else {
2607 const int length = argArray->getType().getOuterArraySize();
2608 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, intermediate.addConstantUnion(length, loc, true), loc);
2609 body = intermediate.growAggregate(body, assign, loc);
2610 }
2611
2612 // Stride output:
2613 if (argStride != nullptr) {
2614 int size;
2615 int stride;
2616 intermediate.getBaseAlignment(argArray->getType(), size, stride, false,
2617 argArray->getType().getQualifier().layoutMatrix == ElmRowMajor);
2618
2619 TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride, intermediate.addConstantUnion(stride, loc, true), loc);
2620
2621 body = intermediate.growAggregate(body, assign);
2622 }
2623
2624 body->setOperator(EOpSequence);
2625 node = body;
2626 }
2627
2628 break;
2629
2630 case EOpInterlockedAdd:
2631 case EOpInterlockedAnd:
2632 case EOpInterlockedExchange:
2633 case EOpInterlockedMax:
2634 case EOpInterlockedMin:
2635 case EOpInterlockedOr:
2636 case EOpInterlockedXor:
2637 case EOpInterlockedCompareExchange:
2638 case EOpInterlockedCompareStore:
2639 {
2640 // We'll replace the first argument with the block dereference, and let
2641 // downstream decomposition handle the rest.
2642
2643 TIntermSequence& sequence = argAggregate->getSequence();
2644
2645 TIntermTyped* argIndex = sequence[1]->getAsTyped(); // index
2646 argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true),
2647 loc, TType(EbtInt));
2648
2649 const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
2650 TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc);
2651
2652 const TType derefType(argArray->getType(), 0);
2653 element->setType(derefType);
2654
2655 // Replace the numeric byte offset parameter with array reference.
2656 sequence[1] = element;
2657 sequence.erase(sequence.begin(), sequence.begin()+1);
2658 }
2659 break;
2660
2661 default:
2662 break; // most pass through unchanged
2663 }
2664}
2665
2666//
LoopDawga2b79912016-07-14 14:45:14 -06002667// Decompose DX9 and DX10 sample intrinsics & object methods into AST
2668//
2669void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
LoopDawg4624a022016-06-20 13:26:59 -06002670{
2671 if (!node || !node->getAsOperator())
2672 return;
2673
baldurkca735702016-10-28 17:57:25 +02002674 const auto clampReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
steve-lunarg8b0227c2016-10-14 16:40:32 -06002675 // Sampler return must always be a vec4, but we can construct a shorter vector
2676 result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize()));
2677
John Kessenich4a346792016-10-16 11:50:46 -06002678 if (sampler.vectorSize < (unsigned)node->getVectorSize()) {
steve-lunarg8b0227c2016-10-14 16:40:32 -06002679 // Too many components. Construct shorter vector from it.
2680 const TType clampedType(result->getType().getBasicType(), EvqTemporary, sampler.vectorSize);
2681
steve-lunargcce8d482016-10-14 18:36:42 -06002682 const TOperator op = intermediate.mapTypeToConstructorOp(clampedType);
steve-lunarg8b0227c2016-10-14 16:40:32 -06002683
2684 result = constructBuiltIn(clampedType, op, result, loc, false);
2685 }
2686
2687 result->setLoc(loc);
2688 return result;
2689 };
2690
LoopDawg4624a022016-06-20 13:26:59 -06002691 const TOperator op = node->getAsOperator()->getOp();
2692 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
2693
steve-lunarg5da1f032017-02-12 17:50:28 -07002694 // Bail out if not a sampler method
2695 if (arguments != nullptr) {
2696 if ((argAggregate != nullptr && argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler))
2697 return;
2698
2699 if (argAggregate == nullptr && arguments->getAsTyped()->getBasicType() != EbtSampler)
2700 return;
2701 }
2702
LoopDawg4624a022016-06-20 13:26:59 -06002703 switch (op) {
LoopDawga2b79912016-07-14 14:45:14 -06002704 // **** DX9 intrinsics: ****
LoopDawg4624a022016-06-20 13:26:59 -06002705 case EOpTexture:
2706 {
LoopDawga2b79912016-07-14 14:45:14 -06002707 // Texture with ddx & ddy is really gradient form in HLSL
steve-lunarg8b0227c2016-10-14 16:40:32 -06002708 if (argAggregate->getSequence().size() == 4)
LoopDawg4624a022016-06-20 13:26:59 -06002709 node->getAsAggregate()->setOperator(EOpTextureGrad);
LoopDawg4624a022016-06-20 13:26:59 -06002710
2711 break;
2712 }
2713
2714 case EOpTextureBias:
2715 {
2716 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // sampler
2717 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // coord
2718
2719 // HLSL puts bias in W component of coordinate. We extract it and add it to
2720 // the argument list, instead
2721 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
2722 TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
2723
2724 TOperator constructOp = EOpNull;
steve-lunarg8b0227c2016-10-14 16:40:32 -06002725 const TSampler& sampler = arg0->getType().getSampler();
2726
2727 switch (sampler.dim) {
LoopDawg4624a022016-06-20 13:26:59 -06002728 case Esd1D: constructOp = EOpConstructFloat; break; // 1D
2729 case Esd2D: constructOp = EOpConstructVec2; break; // 2D
2730 case Esd3D: constructOp = EOpConstructVec3; break; // 3D
2731 case EsdCube: constructOp = EOpConstructVec3; break; // also 3D
2732 default: break;
2733 }
steve-lunarg8b0227c2016-10-14 16:40:32 -06002734
LoopDawg4624a022016-06-20 13:26:59 -06002735 TIntermAggregate* constructCoord = new TIntermAggregate(constructOp);
2736 constructCoord->getSequence().push_back(arg1);
2737 constructCoord->setLoc(loc);
2738
steve-lunarg6b596682016-10-20 14:50:12 -06002739 // The input vector should never be less than 2, since there's always a bias.
2740 // The max is for safety, and should be a no-op.
2741 constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0)));
2742
LoopDawg4624a022016-06-20 13:26:59 -06002743 TIntermAggregate* tex = new TIntermAggregate(EOpTexture);
2744 tex->getSequence().push_back(arg0); // sampler
2745 tex->getSequence().push_back(constructCoord); // coordinate
2746 tex->getSequence().push_back(bias); // bias
John Kessenichecba76f2017-01-06 00:34:48 -07002747
steve-lunarg8b0227c2016-10-14 16:40:32 -06002748 node = clampReturn(tex, sampler);
LoopDawg4624a022016-06-20 13:26:59 -06002749
2750 break;
2751 }
2752
LoopDawga2b79912016-07-14 14:45:14 -06002753 // **** DX10 methods: ****
2754 case EOpMethodSample: // fall through
2755 case EOpMethodSampleBias: // ...
2756 {
2757 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2758 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2759 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2760 TIntermTyped* argBias = nullptr;
2761 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06002762 const TSampler& sampler = argTex->getType().getSampler();
LoopDawga2b79912016-07-14 14:45:14 -06002763
2764 int nextArg = 3;
2765
2766 if (op == EOpMethodSampleBias) // SampleBias has a bias arg
2767 argBias = argAggregate->getSequence()[nextArg++]->getAsTyped();
2768
2769 TOperator textureOp = EOpTexture;
2770
John Kesseniche4821e42016-07-16 10:19:43 -06002771 if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form
LoopDawga2b79912016-07-14 14:45:14 -06002772 textureOp = EOpTextureOffset;
2773 argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped();
2774 }
2775
2776 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2777
2778 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
2779 txsample->getSequence().push_back(txcombine);
2780 txsample->getSequence().push_back(argCoord);
2781
2782 if (argBias != nullptr)
2783 txsample->getSequence().push_back(argBias);
2784
2785 if (argOffset != nullptr)
2786 txsample->getSequence().push_back(argOffset);
2787
steve-lunarg8b0227c2016-10-14 16:40:32 -06002788 node = clampReturn(txsample, sampler);
LoopDawga2b79912016-07-14 14:45:14 -06002789
2790 break;
2791 }
John Kessenichecba76f2017-01-06 00:34:48 -07002792
LoopDawga2b79912016-07-14 14:45:14 -06002793 case EOpMethodSampleGrad: // ...
2794 {
2795 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2796 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2797 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2798 TIntermTyped* argDDX = argAggregate->getSequence()[3]->getAsTyped();
2799 TIntermTyped* argDDY = argAggregate->getSequence()[4]->getAsTyped();
2800 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06002801 const TSampler& sampler = argTex->getType().getSampler();
LoopDawga2b79912016-07-14 14:45:14 -06002802
2803 TOperator textureOp = EOpTextureGrad;
2804
2805 if (argAggregate->getSequence().size() == 6) { // last parameter is offset form
2806 textureOp = EOpTextureGradOffset;
2807 argOffset = argAggregate->getSequence()[5]->getAsTyped();
2808 }
2809
2810 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2811
2812 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
2813 txsample->getSequence().push_back(txcombine);
2814 txsample->getSequence().push_back(argCoord);
2815 txsample->getSequence().push_back(argDDX);
2816 txsample->getSequence().push_back(argDDY);
2817
2818 if (argOffset != nullptr)
2819 txsample->getSequence().push_back(argOffset);
2820
steve-lunarg8b0227c2016-10-14 16:40:32 -06002821 node = clampReturn(txsample, sampler);
LoopDawga2b79912016-07-14 14:45:14 -06002822
2823 break;
2824 }
2825
LoopDawg5d58fae2016-07-15 11:22:24 -06002826 case EOpMethodGetDimensions:
2827 {
2828 // AST returns a vector of results, which we break apart component-wise into
2829 // separate values to assign to the HLSL method's outputs, ala:
2830 // tx . GetDimensions(width, height);
2831 // float2 sizeQueryTemp = EOpTextureQuerySize
2832 // width = sizeQueryTemp.X;
2833 // height = sizeQueryTemp.Y;
2834
2835 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2836 const TType& texType = argTex->getType();
2837
2838 assert(texType.getBasicType() == EbtSampler);
2839
steve-lunarg8b0227c2016-10-14 16:40:32 -06002840 const TSampler& sampler = texType.getSampler();
2841 const TSamplerDim dim = sampler.dim;
2842 const bool isImage = sampler.isImage();
steve-lunarg3ce45362017-03-07 19:30:25 -07002843 const bool isMs = sampler.isMultiSample();
John Kessenich267590d2016-08-05 17:34:34 -06002844 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawg5d58fae2016-07-15 11:22:24 -06002845
2846 int numDims = 0;
2847
2848 switch (dim) {
steve-lunargbb0183f2016-10-04 16:58:14 -06002849 case Esd1D: numDims = 1; break; // W
2850 case Esd2D: numDims = 2; break; // W, H
2851 case Esd3D: numDims = 3; break; // W, H, D
2852 case EsdCube: numDims = 2; break; // W, H (cube)
steve-lunarg6b43d272016-10-06 20:12:24 -06002853 case EsdBuffer: numDims = 1; break; // W (buffers)
steve-lunarg3ce45362017-03-07 19:30:25 -07002854 case EsdRect: numDims = 2; break; // W, H (rect)
LoopDawg5d58fae2016-07-15 11:22:24 -06002855 default:
2856 assert(0 && "unhandled texture dimension");
2857 }
2858
2859 // Arrayed adds another dimension for the number of array elements
steve-lunarg8b0227c2016-10-14 16:40:32 -06002860 if (sampler.isArrayed())
LoopDawg5d58fae2016-07-15 11:22:24 -06002861 ++numDims;
2862
steve-lunarg3ce45362017-03-07 19:30:25 -07002863 // Establish whether the method itself is querying mip levels. This can be false even
2864 // if the underlying query requires a MIP level, due to the available HLSL method overloads.
2865 const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0)));
2866
2867 // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query).
2868 // True if:
2869 // 1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query)
2870 // or,
2871 // 2. There is a LOD (because the non-LOD query cannot be used in that case, per spec)
2872 const bool mipRequired =
2873 ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1...
2874 mipQuery; // 2...
LoopDawg5d58fae2016-07-15 11:22:24 -06002875
2876 // AST assumes integer return. Will be converted to float if required.
steve-lunargbb0183f2016-10-04 16:58:14 -06002877 TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize);
LoopDawg5d58fae2016-07-15 11:22:24 -06002878 sizeQuery->getSequence().push_back(argTex);
steve-lunarg3ce45362017-03-07 19:30:25 -07002879
2880 // If we're building an LOD query, add the LOD.
2881 if (mipRequired) {
2882 // If the base HLSL query had no MIP level given, use level 0.
2883 TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() :
2884 intermediate.addConstantUnion(0, loc, true);
LoopDawg5d58fae2016-07-15 11:22:24 -06002885 sizeQuery->getSequence().push_back(queryLod);
2886 }
steve-lunarg3ce45362017-03-07 19:30:25 -07002887
LoopDawg5d58fae2016-07-15 11:22:24 -06002888 sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims));
2889 sizeQuery->setLoc(loc);
2890
2891 // Return value from size query
2892 TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType());
2893 tempArg->getWritableType().getQualifier().makeTemporary();
steve-lunarg2199c242016-10-02 22:13:22 -06002894 TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign,
2895 intermediate.addSymbol(*tempArg, loc),
2896 sizeQuery, loc);
LoopDawg5d58fae2016-07-15 11:22:24 -06002897
2898 // Compound statement for assigning outputs
2899 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc);
2900 // Index of first output parameter
2901 const int outParamBase = mipQuery ? 2 : 1;
2902
2903 for (int compNum = 0; compNum < numDims; ++compNum) {
2904 TIntermTyped* indexedOut = nullptr;
steve-lunarg2199c242016-10-02 22:13:22 -06002905 TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc);
LoopDawg5d58fae2016-07-15 11:22:24 -06002906
2907 if (numDims > 1) {
2908 TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true);
2909 indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc);
2910 indexedOut->setType(TType(EbtUint, EvqTemporary, 1));
2911 indexedOut->setLoc(loc);
2912 } else {
2913 indexedOut = sizeQueryReturn;
2914 }
John Kessenichecba76f2017-01-06 00:34:48 -07002915
LoopDawg5d58fae2016-07-15 11:22:24 -06002916 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped();
2917 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc);
2918
2919 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
2920 }
2921
2922 // handle mip level parameter
2923 if (mipQuery) {
2924 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
2925
2926 TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels);
2927 levelsQuery->getSequence().push_back(argTex);
2928 levelsQuery->setType(TType(EbtUint, EvqTemporary, 1));
2929 levelsQuery->setLoc(loc);
2930
2931 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc);
2932 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
2933 }
2934
steve-lunarg1e19d902016-07-26 15:19:28 -06002935 // 2DMS formats query # samples, which needs a different query op
steve-lunarg8b0227c2016-10-14 16:40:32 -06002936 if (sampler.isMultiSample()) {
steve-lunarg1e19d902016-07-26 15:19:28 -06002937 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
2938
2939 TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
2940 samplesQuery->getSequence().push_back(argTex);
2941 samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
2942 samplesQuery->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07002943
steve-lunarg1e19d902016-07-26 15:19:28 -06002944 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc);
2945 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
2946 }
2947
LoopDawg5d58fae2016-07-15 11:22:24 -06002948 compoundStatement->setOperator(EOpSequence);
2949 compoundStatement->setLoc(loc);
2950 compoundStatement->setType(TType(EbtVoid));
2951
2952 node = compoundStatement;
2953
2954 break;
2955 }
2956
LoopDawga78b0292016-07-19 14:28:05 -06002957 case EOpMethodSampleCmp: // fall through...
2958 case EOpMethodSampleCmpLevelZero:
2959 {
2960 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2961 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2962 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2963 TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped();
2964 TIntermTyped* argOffset = nullptr;
2965
2966 // optional offset value
2967 if (argAggregate->getSequence().size() > 4)
2968 argOffset = argAggregate->getSequence()[4]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -07002969
LoopDawga78b0292016-07-19 14:28:05 -06002970 const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp
2971
2972 // AST wants comparison value as one of the texture coordinates
2973 TOperator constructOp = EOpNull;
2974 switch (coordDimWithCmpVal) {
2975 // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val
2976 case 2: constructOp = EOpConstructVec2; break;
2977 case 3: constructOp = EOpConstructVec3; break;
2978 case 4: constructOp = EOpConstructVec4; break;
2979 case 5: constructOp = EOpConstructVec4; break; // cubeArrayShadow, cmp value is separate arg.
2980 default: assert(0); break;
2981 }
2982
2983 TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp);
2984 coordWithCmp->getSequence().push_back(argCoord);
2985 if (coordDimWithCmpVal != 5) // cube array shadow is special.
2986 coordWithCmp->getSequence().push_back(argCmpVal);
2987 coordWithCmp->setLoc(loc);
steve-lunarg6b596682016-10-20 14:50:12 -06002988 coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4)));
LoopDawga78b0292016-07-19 14:28:05 -06002989
2990 TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture);
2991 if (argOffset != nullptr)
2992 textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset);
2993
2994 // Create combined sampler & texture op
2995 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2996 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
2997 txsample->getSequence().push_back(txcombine);
2998 txsample->getSequence().push_back(coordWithCmp);
2999
3000 if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord.
3001 txsample->getSequence().push_back(argCmpVal);
3002
3003 // the LevelZero form uses 0 as an explicit LOD
3004 if (op == EOpMethodSampleCmpLevelZero)
3005 txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true));
3006
3007 // Add offset if present
3008 if (argOffset != nullptr)
3009 txsample->getSequence().push_back(argOffset);
3010
3011 txsample->setType(node->getType());
3012 txsample->setLoc(loc);
3013 node = txsample;
3014
3015 break;
3016 }
3017
LoopDawgf2451012016-07-20 16:34:44 -06003018 case EOpMethodLoad:
3019 {
3020 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
3021 TIntermTyped* argCoord = argAggregate->getSequence()[1]->getAsTyped();
3022 TIntermTyped* argOffset = nullptr;
3023 TIntermTyped* lodComponent = nullptr;
3024 TIntermTyped* coordSwizzle = nullptr;
3025
steve-lunarg8b0227c2016-10-14 16:40:32 -06003026 const TSampler& sampler = argTex->getType().getSampler();
3027 const bool isMS = sampler.isMultiSample();
3028 const bool isBuffer = sampler.dim == EsdBuffer;
3029 const bool isImage = sampler.isImage();
LoopDawgf2451012016-07-20 16:34:44 -06003030 const TBasicType coordBaseType = argCoord->getType().getBasicType();
3031
3032 // Last component of coordinate is the mip level, for non-MS. we separate them here:
steve-lunargbb0183f2016-10-04 16:58:14 -06003033 if (isMS || isBuffer || isImage) {
3034 // MS, Buffer, and Image have no LOD
steve-lunarg1e19d902016-07-26 15:19:28 -06003035 coordSwizzle = argCoord;
LoopDawgf2451012016-07-20 16:34:44 -06003036 } else {
3037 // Extract coordinate
John Kessenichc142c882017-01-13 19:34:22 -07003038 int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1);
3039 TSwizzleSelectors<TVectorSelector> coordFields;
3040 for (int i = 0; i < swizzleSize; ++i)
3041 coordFields.push_back(i);
LoopDawgf2451012016-07-20 16:34:44 -06003042 TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc);
3043 coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc);
John Kessenichc142c882017-01-13 19:34:22 -07003044 coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size()));
LoopDawgf2451012016-07-20 16:34:44 -06003045
3046 // Extract LOD
John Kessenichc142c882017-01-13 19:34:22 -07003047 TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true);
LoopDawgf2451012016-07-20 16:34:44 -06003048 lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc);
3049 lodComponent->setType(TType(coordBaseType, EvqTemporary, 1));
3050 }
3051
John Kessenich267590d2016-08-05 17:34:34 -06003052 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawgf2451012016-07-20 16:34:44 -06003053 const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4));
3054
LoopDawgf2451012016-07-20 16:34:44 -06003055 // Create texel fetch
steve-lunargbb0183f2016-10-04 16:58:14 -06003056 const TOperator fetchOp = (isImage ? EOpImageLoad :
3057 hasOffset ? EOpTextureFetchOffset :
3058 EOpTextureFetch);
LoopDawgf2451012016-07-20 16:34:44 -06003059 TIntermAggregate* txfetch = new TIntermAggregate(fetchOp);
3060
LoopDawgf2451012016-07-20 16:34:44 -06003061 // Build up the fetch
3062 txfetch->getSequence().push_back(argTex);
3063 txfetch->getSequence().push_back(coordSwizzle);
3064
steve-lunarg1e19d902016-07-26 15:19:28 -06003065 if (isMS) {
3066 // add 2DMS sample index
3067 TIntermTyped* argSampleIdx = argAggregate->getSequence()[2]->getAsTyped();
3068 txfetch->getSequence().push_back(argSampleIdx);
steve-lunargd53f7172016-07-27 15:46:48 -06003069 } else if (isBuffer) {
3070 // Nothing else to do for buffers.
steve-lunargbb0183f2016-10-04 16:58:14 -06003071 } else if (isImage) {
3072 // Nothing else to do for images.
steve-lunarg1e19d902016-07-26 15:19:28 -06003073 } else {
steve-lunargd53f7172016-07-27 15:46:48 -06003074 // 2DMS and buffer have no LOD, but everything else does.
LoopDawgf2451012016-07-20 16:34:44 -06003075 txfetch->getSequence().push_back(lodComponent);
steve-lunarg1e19d902016-07-26 15:19:28 -06003076 }
LoopDawgf2451012016-07-20 16:34:44 -06003077
steve-lunarg1e19d902016-07-26 15:19:28 -06003078 // Obtain offset arg, if there is one.
3079 if (hasOffset) {
3080 const int offsetPos = (isMS ? 3 : 2);
3081 argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped();
LoopDawgf2451012016-07-20 16:34:44 -06003082 txfetch->getSequence().push_back(argOffset);
steve-lunarg1e19d902016-07-26 15:19:28 -06003083 }
LoopDawgf2451012016-07-20 16:34:44 -06003084
steve-lunarg8b0227c2016-10-14 16:40:32 -06003085 node = clampReturn(txfetch, sampler);
LoopDawgf2451012016-07-20 16:34:44 -06003086
3087 break;
3088 }
3089
LoopDawg3ef78522016-07-21 15:02:16 -06003090 case EOpMethodSampleLevel:
3091 {
3092 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
3093 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
3094 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
3095 TIntermTyped* argLod = argAggregate->getSequence()[3]->getAsTyped();
3096 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06003097 const TSampler& sampler = argTex->getType().getSampler();
John Kessenichecba76f2017-01-06 00:34:48 -07003098
John Kessenich267590d2016-08-05 17:34:34 -06003099 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawg3ef78522016-07-21 15:02:16 -06003100
3101 if (numArgs == 5) // offset, if present
3102 argOffset = argAggregate->getSequence()[4]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -07003103
LoopDawg3ef78522016-07-21 15:02:16 -06003104 const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset);
3105 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
3106
3107 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
3108
3109 txsample->getSequence().push_back(txcombine);
3110 txsample->getSequence().push_back(argCoord);
3111 txsample->getSequence().push_back(argLod);
3112
3113 if (argOffset != nullptr)
3114 txsample->getSequence().push_back(argOffset);
3115
steve-lunarg8b0227c2016-10-14 16:40:32 -06003116 node = clampReturn(txsample, sampler);
LoopDawg3ef78522016-07-21 15:02:16 -06003117
3118 break;
3119 }
3120
LoopDawga2f3d282016-07-22 08:28:11 -06003121 case EOpMethodGather:
3122 {
3123 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
3124 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
3125 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
3126 TIntermTyped* argOffset = nullptr;
3127
3128 // Offset is optional
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06003129 if (argAggregate->getSequence().size() > 3)
LoopDawga2f3d282016-07-22 08:28:11 -06003130 argOffset = argAggregate->getSequence()[3]->getAsTyped();
3131
3132 const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset);
3133 TIntermAggregate* txgather = new TIntermAggregate(textureOp);
3134
3135 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
3136
3137 txgather->getSequence().push_back(txcombine);
3138 txgather->getSequence().push_back(argCoord);
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06003139 // Offset if not given is implicitly channel 0 (red)
LoopDawga2f3d282016-07-22 08:28:11 -06003140
3141 if (argOffset != nullptr)
3142 txgather->getSequence().push_back(argOffset);
3143
3144 txgather->setType(node->getType());
3145 txgather->setLoc(loc);
3146 node = txgather;
3147
3148 break;
3149 }
John Kessenichecba76f2017-01-06 00:34:48 -07003150
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06003151 case EOpMethodGatherRed: // fall through...
3152 case EOpMethodGatherGreen: // ...
3153 case EOpMethodGatherBlue: // ...
3154 case EOpMethodGatherAlpha: // ...
3155 case EOpMethodGatherCmpRed: // ...
3156 case EOpMethodGatherCmpGreen: // ...
3157 case EOpMethodGatherCmpBlue: // ...
3158 case EOpMethodGatherCmpAlpha: // ...
3159 {
3160 int channel = 0; // the channel we are gathering
3161 int cmpValues = 0; // 1 if there is a compare value (handier than a bool below)
3162
3163 switch (op) {
3164 case EOpMethodGatherCmpRed: cmpValues = 1; // fall through
3165 case EOpMethodGatherRed: channel = 0; break;
3166 case EOpMethodGatherCmpGreen: cmpValues = 1; // fall through
3167 case EOpMethodGatherGreen: channel = 1; break;
3168 case EOpMethodGatherCmpBlue: cmpValues = 1; // fall through
3169 case EOpMethodGatherBlue: channel = 2; break;
3170 case EOpMethodGatherCmpAlpha: cmpValues = 1; // fall through
3171 case EOpMethodGatherAlpha: channel = 3; break;
3172 default: assert(0); break;
3173 }
3174
3175 // For now, we have nothing to map the component-wise comparison forms
3176 // to, because neither GLSL nor SPIR-V has such an opcode. Issue an
3177 // unimplemented error instead. Most of the machinery is here if that
steve-lunargd00b0262017-03-09 08:59:45 -07003178 // should ever become available. However, red can be passed through
3179 // to OpImageDrefGather. G/B/A cannot, because that opcode does not
3180 // accept a component.
3181 if (cmpValues != 0 && op != EOpMethodGatherCmpRed) {
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06003182 error(loc, "unimplemented: component-level gather compare", "", "");
3183 return;
3184 }
3185
3186 int arg = 0;
3187
3188 TIntermTyped* argTex = argAggregate->getSequence()[arg++]->getAsTyped();
3189 TIntermTyped* argSamp = argAggregate->getSequence()[arg++]->getAsTyped();
3190 TIntermTyped* argCoord = argAggregate->getSequence()[arg++]->getAsTyped();
3191 TIntermTyped* argOffset = nullptr;
3192 TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr };
3193 // TIntermTyped* argStatus = nullptr; // TODO: residency
3194 TIntermTyped* argCmp = nullptr;
3195
3196 const TSamplerDim dim = argTex->getType().getSampler().dim;
3197
baldurk1eb1c112016-08-15 18:01:15 +02003198 const int argSize = (int)argAggregate->getSequence().size();
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06003199 bool hasStatus = (argSize == (5+cmpValues) || argSize == (8+cmpValues));
3200 bool hasOffset1 = false;
3201 bool hasOffset4 = false;
3202
3203 // Only 2D forms can have offsets. Discover if we have 0, 1 or 4 offsets.
3204 if (dim == Esd2D) {
3205 hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues));
3206 hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues));
3207 }
3208
3209 assert(!(hasOffset1 && hasOffset4));
3210
3211 TOperator textureOp = EOpTextureGather;
3212
3213 // Compare forms have compare value
3214 if (cmpValues != 0)
3215 argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
3216
3217 // Some forms have single offset
3218 if (hasOffset1) {
3219 textureOp = EOpTextureGatherOffset; // single offset form
3220 argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
3221 }
3222
3223 // Some forms have 4 gather offsets
3224 if (hasOffset4) {
3225 textureOp = EOpTextureGatherOffsets; // note plural, for 4 offset form
3226 for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
3227 argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped();
3228 }
3229
3230 // Residency status
3231 if (hasStatus) {
3232 // argStatus = argAggregate->getSequence()[arg++]->getAsTyped();
3233 error(loc, "unimplemented: residency status", "", "");
3234 return;
3235 }
3236
3237 TIntermAggregate* txgather = new TIntermAggregate(textureOp);
3238 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
3239
3240 TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true);
3241
3242 txgather->getSequence().push_back(txcombine);
3243 txgather->getSequence().push_back(argCoord);
3244
3245 // AST wants an array of 4 offsets, where HLSL has separate args. Here
3246 // we construct an array from the separate args.
3247 if (hasOffset4) {
3248 TType arrayType(EbtInt, EvqTemporary, 2);
3249 TArraySizes arraySizes;
3250 arraySizes.addInnerSize(4);
3251 arrayType.newArraySizes(arraySizes);
3252
3253 TIntermAggregate* initList = new TIntermAggregate(EOpNull);
3254
3255 for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
3256 initList->getSequence().push_back(argOffsets[offsetNum]);
3257
3258 argOffset = addConstructor(loc, initList, arrayType);
3259 }
3260
3261 // Add comparison value if we have one
steve-lunargd00b0262017-03-09 08:59:45 -07003262 if (argCmp != nullptr)
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06003263 txgather->getSequence().push_back(argCmp);
3264
3265 // Add offset (either 1, or an array of 4) if we have one
3266 if (argOffset != nullptr)
3267 txgather->getSequence().push_back(argOffset);
3268
Rex Xud41a6962017-03-13 17:07:18 +08003269 // Add channel value if the sampler is not shadow
3270 if (! argSamp->getType().getSampler().isShadow())
3271 txgather->getSequence().push_back(argChannel);
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06003272
3273 txgather->setType(node->getType());
3274 txgather->setLoc(loc);
3275 node = txgather;
3276
3277 break;
3278 }
3279
steve-lunarg68f2c142016-07-26 08:57:53 -06003280 case EOpMethodCalculateLevelOfDetail:
3281 case EOpMethodCalculateLevelOfDetailUnclamped:
3282 {
3283 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
3284 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
3285 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
3286
3287 TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod);
3288
3289 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
3290 txquerylod->getSequence().push_back(txcombine);
3291 txquerylod->getSequence().push_back(argCoord);
3292
3293 TIntermTyped* lodComponent = intermediate.addConstantUnion(0, loc, true);
3294 TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc);
3295 lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1));
3296
3297 node = lodComponentIdx;
3298
3299 // We cannot currently obtain the unclamped LOD
3300 if (op == EOpMethodCalculateLevelOfDetailUnclamped)
3301 error(loc, "unimplemented: CalculateLevelOfDetailUnclamped", "", "");
3302
3303 break;
3304 }
3305
3306 case EOpMethodGetSamplePosition:
3307 {
3308 error(loc, "unimplemented: GetSamplePosition", "", "");
3309 break;
3310 }
3311
LoopDawg4624a022016-06-20 13:26:59 -06003312 default:
3313 break; // most pass through unchanged
3314 }
3315}
3316
3317//
steve-lunargf49cdf42016-11-17 15:04:20 -07003318// Decompose geometry shader methods
3319//
3320void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3321{
3322 if (!node || !node->getAsOperator())
3323 return;
3324
3325 const TOperator op = node->getAsOperator()->getOp();
3326 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
3327
3328 switch (op) {
3329 case EOpMethodAppend:
3330 if (argAggregate) {
3331 TIntermAggregate* sequence = nullptr;
3332 TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex);
3333
3334 emit->setLoc(loc);
3335 emit->setType(TType(EbtVoid));
3336
steve-lunarg08e0c082017-03-29 20:01:13 -06003337 // find the matching output
3338 if (gsStreamOutput == nullptr) {
3339 error(loc, "unable to find output symbol for Append()", "", "");
3340 return;
3341 }
3342
steve-lunargf49cdf42016-11-17 15:04:20 -07003343 sequence = intermediate.growAggregate(sequence,
John Kessenichecba76f2017-01-06 00:34:48 -07003344 handleAssign(loc, EOpAssign,
steve-lunarg08e0c082017-03-29 20:01:13 -06003345 intermediate.addSymbol(*gsStreamOutput, loc),
steve-lunarga2e75312016-12-14 15:22:25 -07003346 argAggregate->getSequence()[1]->getAsTyped()),
steve-lunargf49cdf42016-11-17 15:04:20 -07003347 loc);
3348
3349 sequence = intermediate.growAggregate(sequence, emit);
3350
3351 sequence->setOperator(EOpSequence);
3352 sequence->setLoc(loc);
3353 sequence->setType(TType(EbtVoid));
3354 node = sequence;
3355 }
3356 break;
3357
3358 case EOpMethodRestartStrip:
3359 {
3360 TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive);
3361 cut->setLoc(loc);
3362 cut->setType(TType(EbtVoid));
3363 node = cut;
3364 }
3365 break;
3366
3367 default:
3368 break; // most pass through unchanged
3369 }
3370}
3371
3372//
LoopDawg592860c2016-06-09 08:57:35 -06003373// Optionally decompose intrinsics to AST opcodes.
3374//
3375void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
3376{
steve-lunarg22322362016-10-19 10:15:25 -06003377 // Helper to find image data for image atomics:
3378 // OpImageLoad(image[idx])
3379 // We take the image load apart and add its params to the atomic op aggregate node
3380 const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) {
3381 TIntermAggregate* loadOp = load->getAsAggregate();
3382 if (loadOp == nullptr) {
3383 error(loc, "unknown image type in atomic operation", "", "");
3384 node = nullptr;
3385 return;
3386 }
3387
3388 atomic->getSequence().push_back(loadOp->getSequence()[0]);
3389 atomic->getSequence().push_back(loadOp->getSequence()[1]);
3390 };
3391
3392 // Return true if this is an imageLoad, which we will change to an image atomic.
baldurkca735702016-10-28 17:57:25 +02003393 const auto isImageParam = [](TIntermTyped* image) -> bool {
steve-lunarg22322362016-10-19 10:15:25 -06003394 TIntermAggregate* imageAggregate = image->getAsAggregate();
3395 return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad;
3396 };
3397
LoopDawg592860c2016-06-09 08:57:35 -06003398 // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST
3399 // opcodes for compatibility with existing software stacks.
3400 static const bool decomposeHlslIntrinsics = true;
3401
3402 if (!decomposeHlslIntrinsics || !node || !node->getAsOperator())
3403 return;
John Kessenichecba76f2017-01-06 00:34:48 -07003404
LoopDawg592860c2016-06-09 08:57:35 -06003405 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
3406 TIntermUnary* fnUnary = node->getAsUnaryNode();
3407 const TOperator op = node->getAsOperator()->getOp();
3408
3409 switch (op) {
3410 case EOpGenMul:
3411 {
3412 // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul
steve-lunarg297ae212016-08-24 14:36:13 -06003413 // Since we are treating HLSL rows like GLSL columns (the first matrix indirection),
3414 // we must reverse the operand order here. Hence, arg0 gets sequence[1], etc.
3415 TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped();
3416 TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped();
LoopDawg592860c2016-06-09 08:57:35 -06003417
3418 if (arg0->isVector() && arg1->isVector()) { // vec * vec
3419 node->getAsAggregate()->setOperator(EOpDot);
3420 } else {
3421 node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1);
3422 }
3423
3424 break;
3425 }
3426
3427 case EOpRcp:
3428 {
3429 // rcp(a) -> 1 / a
3430 TIntermTyped* arg0 = fnUnary->getOperand();
3431 TBasicType type0 = arg0->getBasicType();
3432 TIntermTyped* one = intermediate.addConstantUnion(1, type0, loc, true);
3433 node = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0);
3434
3435 break;
3436 }
3437
3438 case EOpSaturate:
3439 {
3440 // saturate(a) -> clamp(a,0,1)
3441 TIntermTyped* arg0 = fnUnary->getOperand();
3442 TBasicType type0 = arg0->getBasicType();
3443 TIntermAggregate* clamp = new TIntermAggregate(EOpClamp);
3444
3445 clamp->getSequence().push_back(arg0);
3446 clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true));
3447 clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true));
3448 clamp->setLoc(loc);
3449 clamp->setType(node->getType());
LoopDawg58910702016-06-13 09:22:28 -06003450 clamp->getWritableType().getQualifier().makeTemporary();
LoopDawg592860c2016-06-09 08:57:35 -06003451 node = clamp;
3452
3453 break;
3454 }
3455
3456 case EOpSinCos:
3457 {
3458 // sincos(a,b,c) -> b = sin(a), c = cos(a)
3459 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
3460 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
3461 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();
3462
3463 TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0);
3464 TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0);
3465 TIntermTyped* sinAssign = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc);
3466 TIntermTyped* cosAssign = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc);
3467
3468 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc);
3469 compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign);
3470 compoundStatement->setOperator(EOpSequence);
3471 compoundStatement->setLoc(loc);
LoopDawg4624a022016-06-20 13:26:59 -06003472 compoundStatement->setType(TType(EbtVoid));
LoopDawg592860c2016-06-09 08:57:35 -06003473
3474 node = compoundStatement;
3475
3476 break;
3477 }
3478
3479 case EOpClip:
3480 {
3481 // clip(a) -> if (any(a<0)) discard;
3482 TIntermTyped* arg0 = fnUnary->getOperand();
3483 TBasicType type0 = arg0->getBasicType();
3484 TIntermTyped* compareNode = nullptr;
3485
3486 // For non-scalars: per experiment with FXC compiler, discard if any component < 0.
3487 if (!arg0->isScalar()) {
3488 // component-wise compare: a < 0
3489 TIntermAggregate* less = new TIntermAggregate(EOpLessThan);
3490 less->getSequence().push_back(arg0);
3491 less->setLoc(loc);
3492
3493 // make vec or mat of bool matching dimensions of input
3494 less->setType(TType(EbtBool, EvqTemporary,
3495 arg0->getType().getVectorSize(),
3496 arg0->getType().getMatrixCols(),
3497 arg0->getType().getMatrixRows(),
3498 arg0->getType().isVector()));
3499
3500 // calculate # of components for comparison const
John Kessenichecba76f2017-01-06 00:34:48 -07003501 const int constComponentCount =
LoopDawg592860c2016-06-09 08:57:35 -06003502 std::max(arg0->getType().getVectorSize(), 1) *
3503 std::max(arg0->getType().getMatrixCols(), 1) *
3504 std::max(arg0->getType().getMatrixRows(), 1);
3505
3506 TConstUnion zero;
3507 zero.setDConst(0.0);
3508 TConstUnionArray zeros(constComponentCount, zero);
3509
3510 less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true));
3511
3512 compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool));
3513 } else {
3514 TIntermTyped* zero = intermediate.addConstantUnion(0, type0, loc, true);
3515 compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero);
3516 }
John Kessenichecba76f2017-01-06 00:34:48 -07003517
LoopDawg592860c2016-06-09 08:57:35 -06003518 TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc);
3519
3520 node = new TIntermSelection(compareNode, killNode, nullptr);
3521 node->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07003522
LoopDawg592860c2016-06-09 08:57:35 -06003523 break;
3524 }
3525
3526 case EOpLog10:
3527 {
3528 // log10(a) -> log2(a) * 0.301029995663981 (== 1/log2(10))
3529 TIntermTyped* arg0 = fnUnary->getOperand();
3530 TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0);
3531 TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true);
3532
3533 node = handleBinaryMath(loc, "mul", EOpMul, log2, base);
3534
3535 break;
3536 }
3537
3538 case EOpDst:
3539 {
3540 // dest.x = 1;
3541 // dest.y = src0.y * src1.y;
3542 // dest.z = src0.z;
3543 // dest.w = src1.w;
3544
3545 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
3546 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
LoopDawg592860c2016-06-09 08:57:35 -06003547
LoopDawg592860c2016-06-09 08:57:35 -06003548 TIntermTyped* y = intermediate.addConstantUnion(1, loc, true);
3549 TIntermTyped* z = intermediate.addConstantUnion(2, loc, true);
3550 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
3551
3552 TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc);
3553 TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc);
3554 TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc);
3555 TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
3556
3557 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
3558
3559 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
3560 dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y));
3561 dst->getSequence().push_back(src0z);
3562 dst->getSequence().push_back(src1w);
LoopDawg6e72fdd2016-06-15 09:50:24 -06003563 dst->setType(TType(EbtFloat, EvqTemporary, 4));
LoopDawg592860c2016-06-09 08:57:35 -06003564 dst->setLoc(loc);
3565 node = dst;
3566
3567 break;
3568 }
3569
LoopDawg58910702016-06-13 09:22:28 -06003570 case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value
3571 case EOpInterlockedMin: // ...
3572 case EOpInterlockedMax: // ...
3573 case EOpInterlockedAnd: // ...
3574 case EOpInterlockedOr: // ...
3575 case EOpInterlockedXor: // ...
3576 case EOpInterlockedExchange: // always has output arg
3577 {
steve-lunarg22322362016-10-19 10:15:25 -06003578 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest
3579 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // value
3580 TIntermTyped* arg2 = nullptr;
LoopDawg58910702016-06-13 09:22:28 -06003581
steve-lunarg22322362016-10-19 10:15:25 -06003582 if (argAggregate->getSequence().size() > 2)
3583 arg2 = argAggregate->getSequence()[2]->getAsTyped();
3584
3585 const bool isImage = isImageParam(arg0);
LoopDawg58910702016-06-13 09:22:28 -06003586 const TOperator atomicOp = mapAtomicOp(loc, op, isImage);
steve-lunarg22322362016-10-19 10:15:25 -06003587 TIntermAggregate* atomic = new TIntermAggregate(atomicOp);
3588 atomic->setType(arg0->getType());
3589 atomic->getWritableType().getQualifier().makeTemporary();
3590 atomic->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07003591
steve-lunarg22322362016-10-19 10:15:25 -06003592 if (isImage) {
3593 // orig_value = imageAtomicOp(image, loc, data)
3594 imageAtomicParams(atomic, arg0);
LoopDawg58910702016-06-13 09:22:28 -06003595 atomic->getSequence().push_back(arg1);
LoopDawg58910702016-06-13 09:22:28 -06003596
steve-lunarg22322362016-10-19 10:15:25 -06003597 if (argAggregate->getSequence().size() > 2) {
3598 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
3599 } else {
3600 node = atomic; // no assignment needed, as there was no out var.
3601 }
LoopDawg58910702016-06-13 09:22:28 -06003602 } else {
steve-lunarg22322362016-10-19 10:15:25 -06003603 // Normal memory variable:
3604 // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value
3605 if (argAggregate->getSequence().size() > 2) {
3606 // optional output param is present. return value goes to arg2.
3607 atomic->getSequence().push_back(arg0);
3608 atomic->getSequence().push_back(arg1);
3609
3610 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
3611 } else {
3612 // Set the matching operator. Since output is absent, this is all we need to do.
3613 node->getAsAggregate()->setOperator(atomicOp);
3614 }
LoopDawg58910702016-06-13 09:22:28 -06003615 }
3616
3617 break;
3618 }
3619
3620 case EOpInterlockedCompareExchange:
3621 {
3622 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest
3623 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // cmp
3624 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); // value
3625 TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped(); // orig
3626
steve-lunarg22322362016-10-19 10:15:25 -06003627 const bool isImage = isImageParam(arg0);
LoopDawg58910702016-06-13 09:22:28 -06003628 TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage));
LoopDawg58910702016-06-13 09:22:28 -06003629 atomic->setLoc(loc);
3630 atomic->setType(arg2->getType());
3631 atomic->getWritableType().getQualifier().makeTemporary();
3632
steve-lunarg22322362016-10-19 10:15:25 -06003633 if (isImage) {
3634 imageAtomicParams(atomic, arg0);
3635 } else {
3636 atomic->getSequence().push_back(arg0);
3637 }
3638
3639 atomic->getSequence().push_back(arg1);
3640 atomic->getSequence().push_back(arg2);
LoopDawg58910702016-06-13 09:22:28 -06003641 node = intermediate.addAssign(EOpAssign, arg3, atomic, loc);
John Kessenichecba76f2017-01-06 00:34:48 -07003642
LoopDawg58910702016-06-13 09:22:28 -06003643 break;
3644 }
3645
LoopDawg6e72fdd2016-06-15 09:50:24 -06003646 case EOpEvaluateAttributeSnapped:
3647 {
3648 // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels
3649 // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y:
3650 // iU = (iU<<28)>>28
3651 // fU = ((float)iU)/16
3652 // Targets might handle this natively, in which case they can disable
3653 // decompositions.
3654
3655 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // value
3656 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // offset
3657
3658 TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true);
3659 TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift,
3660 handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28),
3661 i28);
3662
3663 TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true);
3664 TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul,
3665 intermediate.addConversion(EOpConstructFloat,
3666 TType(EbtFloat, EvqTemporary, 2), iU),
3667 recip16);
John Kessenichecba76f2017-01-06 00:34:48 -07003668
LoopDawg6e72fdd2016-06-15 09:50:24 -06003669 TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset);
3670 interp->getSequence().push_back(arg0);
3671 interp->getSequence().push_back(floatOffset);
3672 interp->setLoc(loc);
3673 interp->setType(arg0->getType());
3674 interp->getWritableType().getQualifier().makeTemporary();
3675
3676 node = interp;
3677
3678 break;
3679 }
3680
3681 case EOpLit:
3682 {
3683 TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped();
3684 TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped();
3685 TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped();
3686
3687 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
3688
3689 // Ambient
3690 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
3691
3692 // Diffuse:
3693 TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
3694 TIntermAggregate* diffuse = new TIntermAggregate(EOpMax);
3695 diffuse->getSequence().push_back(n_dot_l);
3696 diffuse->getSequence().push_back(zero);
3697 diffuse->setLoc(loc);
3698 diffuse->setType(TType(EbtFloat));
3699 dst->getSequence().push_back(diffuse);
3700
3701 // Specular:
3702 TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin);
3703 min_ndot->getSequence().push_back(n_dot_l);
3704 min_ndot->getSequence().push_back(n_dot_h);
3705 min_ndot->setLoc(loc);
3706 min_ndot->setType(TType(EbtFloat));
3707
3708 TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero);
3709 TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m); // n_dot_h * m
3710
3711 dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc));
John Kessenichecba76f2017-01-06 00:34:48 -07003712
LoopDawg6e72fdd2016-06-15 09:50:24 -06003713 // One:
3714 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
3715
3716 dst->setLoc(loc);
3717 dst->setType(TType(EbtFloat, EvqTemporary, 4));
3718 node = dst;
3719 break;
3720 }
3721
LoopDawg1b7fd0f2016-06-22 15:20:14 -06003722 case EOpAsDouble:
3723 {
3724 // asdouble accepts two 32 bit ints. we can use EOpUint64BitsToDouble, but must
3725 // first construct a uint64.
3726 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
3727 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
3728
3729 if (arg0->getType().isVector()) { // TODO: ...
3730 error(loc, "double2 conversion not implemented", "asdouble", "");
3731 break;
3732 }
3733
3734 TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2);
3735
3736 uint64->getSequence().push_back(arg0);
3737 uint64->getSequence().push_back(arg1);
3738 uint64->setType(TType(EbtUint, EvqTemporary, 2)); // convert 2 uints to a uint2
3739 uint64->setLoc(loc);
3740
3741 // bitcast uint2 to a double
3742 TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble);
3743 convert->getAsUnaryNode()->setOperand(uint64);
3744 convert->setLoc(loc);
3745 convert->setType(TType(EbtDouble, EvqTemporary));
3746 node = convert;
John Kessenichecba76f2017-01-06 00:34:48 -07003747
LoopDawg1b7fd0f2016-06-22 15:20:14 -06003748 break;
3749 }
John Kessenichecba76f2017-01-06 00:34:48 -07003750
LoopDawg6e72fdd2016-06-15 09:50:24 -06003751 case EOpF16tof32:
steve-lunarg86b510e2017-02-27 13:09:32 -07003752 {
3753 // input uvecN with low 16 bits of each component holding a float16. convert to float32.
3754 TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
steve-lunargfdbfb652017-02-28 14:14:14 -07003755 TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
steve-lunarg86b510e2017-02-27 13:09:32 -07003756 const int vecSize = argValue->getType().getVectorSize();
3757
3758 TOperator constructOp = EOpNull;
3759 switch (vecSize) {
3760 case 1: constructOp = EOpNull; break; // direct use, no construct needed
3761 case 2: constructOp = EOpConstructVec2; break;
3762 case 3: constructOp = EOpConstructVec3; break;
3763 case 4: constructOp = EOpConstructVec4; break;
3764 default: assert(0); break;
3765 }
3766
3767 // For scalar case, we don't need to construct another type.
3768 TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
3769
3770 if (result) {
3771 result->setType(TType(EbtFloat, EvqTemporary, vecSize));
3772 result->setLoc(loc);
3773 }
3774
3775 for (int idx = 0; idx < vecSize; ++idx) {
3776 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
3777 TIntermTyped* component = argValue->getType().isVector() ?
3778 intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
3779
3780 if (component != argValue)
3781 component->setType(TType(argValue->getBasicType(), EvqTemporary));
3782
3783 TIntermTyped* unpackOp = new TIntermUnary(EOpUnpackHalf2x16);
3784 unpackOp->setType(TType(EbtFloat, EvqTemporary, 2));
3785 unpackOp->getAsUnaryNode()->setOperand(component);
3786 unpackOp->setLoc(loc);
3787
3788 TIntermTyped* lowOrder = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc);
3789
3790 if (result != nullptr) {
3791 result->getSequence().push_back(lowOrder);
3792 node = result;
3793 } else {
3794 node = lowOrder;
3795 }
3796 }
3797
3798 break;
3799 }
3800
LoopDawg6e72fdd2016-06-15 09:50:24 -06003801 case EOpF32tof16:
3802 {
steve-lunarg86b510e2017-02-27 13:09:32 -07003803 // input floatN converted to 16 bit float in low order bits of each component of uintN
3804 TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
3805
3806 TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
3807 const int vecSize = argValue->getType().getVectorSize();
3808
3809 TOperator constructOp = EOpNull;
3810 switch (vecSize) {
3811 case 1: constructOp = EOpNull; break; // direct use, no construct needed
3812 case 2: constructOp = EOpConstructUVec2; break;
3813 case 3: constructOp = EOpConstructUVec3; break;
3814 case 4: constructOp = EOpConstructUVec4; break;
3815 default: assert(0); break;
3816 }
3817
3818 // For scalar case, we don't need to construct another type.
3819 TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
3820
3821 if (result) {
3822 result->setType(TType(EbtUint, EvqTemporary, vecSize));
3823 result->setLoc(loc);
3824 }
3825
3826 for (int idx = 0; idx < vecSize; ++idx) {
3827 TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
3828 TIntermTyped* component = argValue->getType().isVector() ?
3829 intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
3830
3831 if (component != argValue)
3832 component->setType(TType(argValue->getBasicType(), EvqTemporary));
3833
3834 TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2);
3835 vec2ComponentAndZero->getSequence().push_back(component);
3836 vec2ComponentAndZero->getSequence().push_back(zero);
3837 vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2));
3838 vec2ComponentAndZero->setLoc(loc);
3839
3840 TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16);
3841 packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero);
3842 packOp->setLoc(loc);
3843 packOp->setType(TType(EbtUint, EvqTemporary));
3844
3845 if (result != nullptr) {
3846 result->getSequence().push_back(packOp);
3847 node = result;
3848 } else {
3849 node = packOp;
3850 }
3851 }
3852
LoopDawg6e72fdd2016-06-15 09:50:24 -06003853 break;
3854 }
3855
steve-lunarg7ea7ff42017-01-03 14:42:18 -07003856 case EOpD3DCOLORtoUBYTE4:
3857 {
3858 // ivec4 ( x.zyxw * 255.001953 );
3859 TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
John Kessenichc142c882017-01-13 19:34:22 -07003860 TSwizzleSelectors<TVectorSelector> selectors;
3861 selectors.push_back(2);
3862 selectors.push_back(1);
3863 selectors.push_back(0);
3864 selectors.push_back(3);
3865 TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc);
steve-lunarg7ea7ff42017-01-03 14:42:18 -07003866 TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc);
3867 swizzled->setType(arg0->getType());
3868 swizzled->getWritableType().getQualifier().makeTemporary();
3869
3870 TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true);
3871 TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled);
3872 rangeConverted->setType(arg0->getType());
3873 rangeConverted->getWritableType().getQualifier().makeTemporary();
3874
3875 node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted);
3876 node->setLoc(loc);
3877 node->setType(TType(EbtInt, EvqTemporary, 4));
3878 break;
3879 }
3880
steve-lunarg13975522017-04-03 20:05:21 -06003881 case EOpIsFinite:
3882 {
3883 // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate
3884 // it to !isnan && !isinf
3885
3886 TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
3887
3888 // We'll make a temporary in case the RHS is cmoplex
3889 TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType());
3890 tempArg->getWritableType().getQualifier().makeTemporary();
3891
3892 TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign,
3893 intermediate.addSymbol(*tempArg, loc),
3894 arg0, loc);
3895
3896 TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc);
3897
3898 TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc));
3899 isnan->setType(TType(EbtBool));
3900
3901 TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan);
3902 notnan->setType(TType(EbtBool));
3903
3904 TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc));
3905 isinf->setType(TType(EbtBool));
3906
3907 TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf);
3908 notinf->setType(TType(EbtBool));
3909
3910 TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf);
3911 andNode->setType(TType(EbtBool));
3912
3913 compoundStatement = intermediate.growAggregate(compoundStatement, andNode);
3914 compoundStatement->setOperator(EOpSequence);
3915 compoundStatement->setLoc(loc);
3916 compoundStatement->setType(TType(EbtVoid));
3917
3918 node = compoundStatement;
3919
3920 break;
3921 }
3922
LoopDawg592860c2016-06-09 08:57:35 -06003923 default:
3924 break; // most pass through unchanged
3925 }
3926}
3927
John Kesseniche01a9bc2016-03-12 20:11:22 -07003928//
3929// Handle seeing function call syntax in the grammar, which could be any of
3930// - .length() method
3931// - constructor
3932// - a call to a built-in function mapped to an operator
3933// - a call to a built-in function that will remain a function call (e.g., texturing)
3934// - user function
3935// - subroutine call (not implemented yet)
3936//
steve-lunarg26d31452016-12-23 18:56:57 -07003937TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments)
John Kesseniche01a9bc2016-03-12 20:11:22 -07003938{
3939 TIntermTyped* result = nullptr;
3940
3941 TOperator op = function->getBuiltInOp();
John Kessenich88e88e52017-03-08 21:16:35 -07003942 if (op != EOpNull) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07003943 //
3944 // Then this should be a constructor.
3945 // Don't go through the symbol table for constructors.
3946 // Their parameters will be verified algorithmically.
3947 //
3948 TType type(EbtVoid); // use this to get the type back
3949 if (! constructorError(loc, arguments, *function, op, type)) {
3950 //
3951 // It's a constructor, of type 'type'.
3952 //
John Kessenichc633f642017-04-03 21:48:37 -06003953 result = handleConstructor(loc, arguments, type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003954 if (result == nullptr)
3955 error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), "");
3956 }
3957 } else {
3958 //
3959 // Find it in the symbol table.
3960 //
steve-lunarg5da1f032017-02-12 17:50:28 -07003961 const TFunction* fnCandidate = nullptr;
Daniel Koch197082c2017-02-28 09:01:43 -05003962 bool builtIn = false;
steve-lunarg5da1f032017-02-12 17:50:28 -07003963
3964 // TODO: this needs improvement: there's no way at present to look up a signature in
3965 // the symbol table for an arbitrary type. This is a temporary hack until that ability exists.
3966 // It will have false positives, since it doesn't check arg counts or types.
3967 if (arguments && arguments->getAsAggregate()) {
steve-lunargdb2e3b42017-03-31 12:47:34 -06003968 const TIntermSequence& sequence = arguments->getAsAggregate()->getSequence();
3969
3970 if (!sequence.empty() && isStructBufferType(sequence[0]->getAsTyped()->getType())) {
steve-lunarge7d07522017-03-19 18:12:37 -06003971 static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1;
3972
3973 if (function->getName().length() > methodPrefixSize &&
3974 isStructBufferMethod(function->getName().substr(methodPrefixSize))) {
steve-lunarg5da1f032017-02-12 17:50:28 -07003975 const TString mangle = function->getName() + "(";
3976 TSymbol* symbol = symbolTable.find(mangle, &builtIn);
3977
3978 if (symbol)
3979 fnCandidate = symbol->getAsFunction();
3980 }
3981 }
3982 }
3983
3984 if (fnCandidate == nullptr)
3985 fnCandidate = findFunction(loc, *function, builtIn, arguments);
3986
John Kesseniche01a9bc2016-03-12 20:11:22 -07003987 if (fnCandidate) {
3988 // This is a declared function that might map to
3989 // - a built-in operator,
3990 // - a built-in function not mapped to an operator, or
3991 // - a user function.
3992
3993 // Error check for a function requiring specific extensions present.
3994 if (builtIn && fnCandidate->getNumExtensions())
3995 requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str());
3996
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003997 // Convert 'in' arguments
3998 if (arguments)
3999 addInputArgumentConversions(*fnCandidate, arguments);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004000
4001 op = fnCandidate->getBuiltInOp();
4002 if (builtIn && op != EOpNull) {
4003 // A function call mapped to a built-in operation.
4004 result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments, fnCandidate->getType());
4005 if (result == nullptr) {
4006 error(arguments->getLoc(), " wrong operand type", "Internal Error",
4007 "built in unary operator function. Type: %s",
4008 static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str());
4009 } else if (result->getAsOperator()) {
4010 builtInOpCheck(loc, *fnCandidate, *result->getAsOperator());
4011 }
4012 } else {
4013 // This is a function call not mapped to built-in operator.
4014 // It could still be a built-in function, but only if PureOperatorBuiltins == false.
4015 result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc);
4016 TIntermAggregate* call = result->getAsAggregate();
4017 call->setName(fnCandidate->getMangledName());
4018
4019 // this is how we know whether the given function is a built-in function or a user-defined function
4020 // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also
4021 // if builtIn == true, it's definitely a built-in function with EOpNull
4022 if (! builtIn) {
4023 call->setUserDefined();
4024 intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName());
4025 }
4026 }
4027
steve-lunargef33ec02016-11-02 12:42:34 -06004028 // for decompositions, since we want to operate on the function node, not the aggregate holding
4029 // output conversions.
John Kessenichecba76f2017-01-06 00:34:48 -07004030 const TIntermTyped* fnNode = result;
steve-lunargef33ec02016-11-02 12:42:34 -06004031
steve-lunarg5da1f032017-02-12 17:50:28 -07004032 decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions
4033 decomposeIntrinsic(loc, result, arguments); // HLSL->AST intrinsic decompositions
4034 decomposeSampleMethods(loc, result, arguments); // HLSL->AST sample method decompositions
4035 decomposeGeometryMethods(loc, result, arguments); // HLSL->AST geometry method decompositions
steve-lunargef33ec02016-11-02 12:42:34 -06004036
John Kesseniche01a9bc2016-03-12 20:11:22 -07004037 // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore.
4038 // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output.
4039 // Also, build the qualifier list for user function calls, which are always called with an aggregate.
steve-lunargef33ec02016-11-02 12:42:34 -06004040 // We don't do this is if there has been a decomposition, which will have added its own conversions
4041 // for output parameters.
4042 if (result == fnNode && result->getAsAggregate()) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07004043 TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList();
4044 for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
4045 TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage;
4046 qualifierList.push_back(qual);
4047 }
steve-lunargef33ec02016-11-02 12:42:34 -06004048 result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator());
John Kesseniche01a9bc2016-03-12 20:11:22 -07004049 }
4050 }
4051 }
4052
4053 // generic error recovery
4054 // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to reduce cascades
4055 if (result == nullptr)
4056 result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
4057
4058 return result;
4059}
4060
John Kesseniche01a9bc2016-03-12 20:11:22 -07004061//
4062// Add any needed implicit conversions for function-call arguments to input parameters.
4063//
steve-lunarg26d31452016-12-23 18:56:57 -07004064void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004065{
4066 TIntermAggregate* aggregate = arguments->getAsAggregate();
steve-lunarg26d31452016-12-23 18:56:57 -07004067 const auto setArg = [&](int argNum, TIntermTyped* arg) {
John Kessenich5159d4f2016-09-19 00:06:19 -06004068 if (function.getParamCount() == 1)
4069 arguments = arg;
4070 else {
4071 if (aggregate)
4072 aggregate->getSequence()[argNum] = arg;
4073 else
4074 arguments = arg;
4075 }
4076 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07004077
4078 // Process each argument's conversion
4079 for (int i = 0; i < function.getParamCount(); ++i) {
John Kessenichc86d38b2016-10-01 13:30:37 -06004080 if (! function[i].type->getQualifier().isParamInput())
4081 continue;
4082
John Kesseniche01a9bc2016-03-12 20:11:22 -07004083 // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
4084 // is the single argument itself or its children are the arguments. Only one argument
4085 // means take 'arguments' itself as the one argument.
John Kessenichd8fe2ca2016-10-01 17:11:21 -06004086 TIntermTyped* arg = function.getParamCount() == 1
4087 ? arguments->getAsTyped()
4088 : (aggregate ? aggregate->getSequence()[i]->getAsTyped() : arguments->getAsTyped());
John Kesseniche01a9bc2016-03-12 20:11:22 -07004089 if (*function[i].type != arg->getType()) {
John Kessenichc86d38b2016-10-01 13:30:37 -06004090 // In-qualified arguments just need an extra node added above the argument to
4091 // convert to the correct type.
John Kessenich1e275c82016-12-14 17:02:32 -07004092 TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[i].type, arg);
4093 if (convArg != nullptr)
4094 convArg = intermediate.addShapeConversion(EOpFunctionCall, *function[i].type, convArg);
4095 if (convArg != nullptr)
4096 setArg(i, convArg);
4097 else
4098 error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", i);
John Kessenich5159d4f2016-09-19 00:06:19 -06004099 } else {
steve-lunarga2e75312016-12-14 15:22:25 -07004100 if (wasFlattened(arg) || wasSplit(arg)) {
John Kessenich6714bcc2016-09-21 17:50:12 -06004101 // Will make a two-level subtree.
4102 // The deepest will copy member-by-member to build the structure to pass.
John Kessenichd8fe2ca2016-10-01 17:11:21 -06004103 // The level above that will be a two-operand EOpComma sequence that follows the copy by the
John Kessenich6714bcc2016-09-21 17:50:12 -06004104 // object itself.
John Kessenich5159d4f2016-09-19 00:06:19 -06004105 TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[i].type);
John Kessenich28b28142016-09-19 00:19:49 -06004106 internalAggregate->getWritableType().getQualifier().makeTemporary();
John Kessenichecba76f2017-01-06 00:34:48 -07004107 TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(),
John Kessenich5159d4f2016-09-19 00:06:19 -06004108 internalAggregate->getName(),
4109 internalAggregate->getType());
John Kessenicha08c9292016-10-01 17:17:55 -06004110 internalSymbolNode->setLoc(arg->getLoc());
John Kessenich6714bcc2016-09-21 17:50:12 -06004111 // This makes the deepest level, the member-wise copy
John Kessenicha08c9292016-10-01 17:17:55 -06004112 TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign, internalSymbolNode, arg)->getAsAggregate();
John Kessenich6714bcc2016-09-21 17:50:12 -06004113
4114 // Now, pair that with the resulting aggregate.
John Kessenicha08c9292016-10-01 17:17:55 -06004115 assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc());
John Kessenich5159d4f2016-09-19 00:06:19 -06004116 assignAgg->setOperator(EOpComma);
John Kessenich6714bcc2016-09-21 17:50:12 -06004117 assignAgg->setType(internalAggregate->getType());
John Kessenich5159d4f2016-09-19 00:06:19 -06004118 setArg(i, assignAgg);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004119 }
4120 }
4121 }
4122}
4123
4124//
4125// Add any needed implicit output conversions for function-call arguments. This
4126// can require a new tree topology, complicated further by whether the function
4127// has a return value.
4128//
4129// Returns a node of a subtree that evaluates to the return value of the function.
4130//
steve-lunargef33ec02016-11-02 12:42:34 -06004131TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004132{
steve-lunargef33ec02016-11-02 12:42:34 -06004133 assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr);
4134
4135 const TSourceLoc& loc = intermNode.getLoc();
4136
4137 TIntermSequence argSequence; // temp sequence for unary node args
4138
4139 if (intermNode.getAsUnaryNode())
4140 argSequence.push_back(intermNode.getAsUnaryNode()->getOperand());
4141
4142 TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence;
4143
John Kessenichd8fe2ca2016-10-01 17:11:21 -06004144 const auto needsConversion = [&](int argNum) {
4145 return function[argNum].type->getQualifier().isParamOutput() &&
4146 (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() ||
steve-lunarg90707962016-10-07 19:35:40 -06004147 shouldConvertLValue(arguments[argNum]) ||
steve-lunarga2b01a02016-11-28 17:09:54 -07004148 wasFlattened(arguments[argNum]->getAsTyped()));
John Kessenichd8fe2ca2016-10-01 17:11:21 -06004149 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07004150
4151 // Will there be any output conversions?
4152 bool outputConversions = false;
4153 for (int i = 0; i < function.getParamCount(); ++i) {
John Kessenichd8fe2ca2016-10-01 17:11:21 -06004154 if (needsConversion(i)) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07004155 outputConversions = true;
4156 break;
4157 }
4158 }
4159
4160 if (! outputConversions)
4161 return &intermNode;
4162
4163 // Setup for the new tree, if needed:
4164 //
4165 // Output conversions need a different tree topology.
4166 // Out-qualified arguments need a temporary of the correct type, with the call
4167 // followed by an assignment of the temporary to the original argument:
4168 // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...)
4169 // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet)
4170 // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment.
4171 TIntermTyped* conversionTree = nullptr;
4172 TVariable* tempRet = nullptr;
4173 if (intermNode.getBasicType() != EbtVoid) {
4174 // do the "tempRet = function(...), " bit from above
4175 tempRet = makeInternalVariable("tempReturn", intermNode.getType());
steve-lunargef33ec02016-11-02 12:42:34 -06004176 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
4177 conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004178 } else
4179 conversionTree = &intermNode;
4180
4181 conversionTree = intermediate.makeAggregate(conversionTree);
4182
4183 // Process each argument's conversion
4184 for (int i = 0; i < function.getParamCount(); ++i) {
John Kessenichd8fe2ca2016-10-01 17:11:21 -06004185 if (needsConversion(i)) {
4186 // Out-qualified arguments needing conversion need to use the topology setup above.
4187 // Do the " ...(tempArg, ...), arg = tempArg" bit from above.
4188
4189 // Make a temporary for what the function expects the argument to look like.
4190 TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type);
4191 tempArg->getWritableType().getQualifier().makeTemporary();
steve-lunargef33ec02016-11-02 12:42:34 -06004192 TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc);
John Kessenichd8fe2ca2016-10-01 17:11:21 -06004193
4194 // This makes the deepest level, the member-wise copy
steve-lunarg90707962016-10-07 19:35:40 -06004195 TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(), tempArgNode);
4196 tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign);
John Kessenichd8fe2ca2016-10-01 17:11:21 -06004197 conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc());
4198
4199 // replace the argument with another node for the same tempArg variable
steve-lunargef33ec02016-11-02 12:42:34 -06004200 arguments[i] = intermediate.addSymbol(*tempArg, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004201 }
4202 }
4203
4204 // Finalize the tree topology (see bigger comment above).
4205 if (tempRet) {
4206 // do the "..., tempRet" bit from above
steve-lunargef33ec02016-11-02 12:42:34 -06004207 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
4208 conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004209 }
steve-lunargef33ec02016-11-02 12:42:34 -06004210
4211 conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004212
4213 return conversionTree;
4214}
4215
4216//
4217// Do additional checking of built-in function calls that is not caught
4218// by normal semantic checks on argument type, extension tagging, etc.
4219//
4220// Assumes there has been a semantically correct match to a built-in function prototype.
4221//
4222void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode)
4223{
4224 // Set up convenience accessors to the argument(s). There is almost always
4225 // multiple arguments for the cases below, but when there might be one,
4226 // check the unaryArg first.
4227 const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference
4228 const TIntermTyped* unaryArg = nullptr;
4229 const TIntermTyped* arg0 = nullptr;
4230 if (callNode.getAsAggregate()) {
4231 argp = &callNode.getAsAggregate()->getSequence();
4232 if (argp->size() > 0)
4233 arg0 = (*argp)[0]->getAsTyped();
4234 } else {
4235 assert(callNode.getAsUnaryNode());
4236 unaryArg = callNode.getAsUnaryNode()->getOperand();
4237 arg0 = unaryArg;
4238 }
4239 const TIntermSequence& aggArgs = *argp; // only valid when unaryArg is nullptr
4240
John Kesseniche01a9bc2016-03-12 20:11:22 -07004241 switch (callNode.getOp()) {
4242 case EOpTextureGather:
4243 case EOpTextureGatherOffset:
4244 case EOpTextureGatherOffsets:
4245 {
4246 // Figure out which variants are allowed by what extensions,
4247 // and what arguments must be constant for which situations.
4248
4249 TString featureString = fnCandidate.getName() + "(...)";
4250 const char* feature = featureString.c_str();
4251 int compArg = -1; // track which argument, if any, is the constant component argument
4252 switch (callNode.getOp()) {
4253 case EOpTextureGather:
4254 // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
4255 // otherwise, need GL_ARB_texture_gather.
4256 if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) {
4257 if (! fnCandidate[0].type->getSampler().shadow)
4258 compArg = 2;
4259 }
4260 break;
4261 case EOpTextureGatherOffset:
4262 // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
4263 if (! fnCandidate[0].type->getSampler().shadow)
4264 compArg = 3;
4265 break;
4266 case EOpTextureGatherOffsets:
4267 if (! fnCandidate[0].type->getSampler().shadow)
4268 compArg = 3;
4269 break;
4270 default:
4271 break;
4272 }
4273
4274 if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
4275 if (aggArgs[compArg]->getAsConstantUnion()) {
4276 int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
4277 if (value < 0 || value > 3)
4278 error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
4279 } else
4280 error(loc, "must be a compile-time constant:", feature, "component argument");
4281 }
4282
4283 break;
4284 }
4285
4286 case EOpTextureOffset:
4287 case EOpTextureFetchOffset:
4288 case EOpTextureProjOffset:
4289 case EOpTextureLodOffset:
4290 case EOpTextureProjLodOffset:
4291 case EOpTextureGradOffset:
4292 case EOpTextureProjGradOffset:
4293 {
4294 // Handle texture-offset limits checking
4295 // Pick which argument has to hold constant offsets
4296 int arg = -1;
4297 switch (callNode.getOp()) {
4298 case EOpTextureOffset: arg = 2; break;
4299 case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break;
4300 case EOpTextureProjOffset: arg = 2; break;
4301 case EOpTextureLodOffset: arg = 3; break;
4302 case EOpTextureProjLodOffset: arg = 3; break;
4303 case EOpTextureGradOffset: arg = 4; break;
4304 case EOpTextureProjGradOffset: arg = 4; break;
4305 default:
4306 assert(0);
4307 break;
4308 }
4309
4310 if (arg > 0) {
4311 if (! aggArgs[arg]->getAsConstantUnion())
4312 error(loc, "argument must be compile-time constant", "texel offset", "");
4313 else {
4314 const TType& type = aggArgs[arg]->getAsTyped()->getType();
4315 for (int c = 0; c < type.getVectorSize(); ++c) {
4316 int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
4317 if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
4318 error(loc, "value is out of range:", "texel offset", "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
4319 }
4320 }
4321 }
4322
4323 break;
4324 }
4325
4326 case EOpTextureQuerySamples:
4327 case EOpImageQuerySamples:
4328 break;
4329
4330 case EOpImageAtomicAdd:
4331 case EOpImageAtomicMin:
4332 case EOpImageAtomicMax:
4333 case EOpImageAtomicAnd:
4334 case EOpImageAtomicOr:
4335 case EOpImageAtomicXor:
4336 case EOpImageAtomicExchange:
4337 case EOpImageAtomicCompSwap:
4338 break;
4339
4340 case EOpInterpolateAtCentroid:
4341 case EOpInterpolateAtSample:
4342 case EOpInterpolateAtOffset:
John Kesseniche01a9bc2016-03-12 20:11:22 -07004343 // Make sure the first argument is an interpolant, or an array element of an interpolant
4344 if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
4345 // It might still be an array element.
4346 //
4347 // We could check more, but the semantics of the first argument are already met; the
4348 // only way to turn an array into a float/vec* is array dereference and swizzle.
4349 //
4350 // ES and desktop 4.3 and earlier: swizzles may not be used
4351 // desktop 4.4 and later: swizzles may be used
4352 const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true);
4353 if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
4354 error(loc, "first argument must be an interpolant, or interpolant-array element", fnCandidate.getName().c_str(), "");
4355 }
4356 break;
4357
4358 default:
4359 break;
4360 }
4361}
4362
4363//
John Kessenichc633f642017-04-03 21:48:37 -06004364// Handle seeing something in a grammar production that can be done by calling
4365// a constructor.
John Kesseniche01a9bc2016-03-12 20:11:22 -07004366//
John Kessenichc633f642017-04-03 21:48:37 -06004367// The constructor still must be "handled" by handleFunctionCall(), which will
4368// then call handleConstructor().
4369//
4370TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004371{
John Kessenicha26a5172016-07-28 15:29:35 -06004372 TOperator op = intermediate.mapTypeToConstructorOp(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004373
4374 if (op == EOpNull) {
4375 error(loc, "cannot construct this type", type.getBasicString(), "");
John Kessenichd016be12016-03-13 11:24:20 -06004376 return nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004377 }
4378
4379 TString empty("");
4380
4381 return new TFunction(&empty, type, op);
4382}
4383
4384//
John Kessenich630dd7d2016-06-12 23:52:12 -06004385// Handle seeing a "COLON semantic" at the end of a type declaration,
4386// by updating the type according to the semantic.
4387//
John Kessenich2dd643f2017-03-14 21:50:06 -06004388void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn, const TString& upperCase)
John Kessenich630dd7d2016-06-12 23:52:12 -06004389{
John Kessenich6e1d50a2017-03-09 14:37:32 -07004390 // adjust for stage in/out
Dan Baker26aa8a42016-08-25 17:13:25 -04004391
John Kessenich6e1d50a2017-03-09 14:37:32 -07004392 switch(builtIn) {
4393 case EbvPosition:
4394 if (language == EShLangFragment)
4395 builtIn = EbvFragCoord;
4396 break;
4397 case EbvStencilRef:
4398 error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", "");
4399 break;
steve-lunarg7afe1342017-03-18 22:24:14 -06004400 case EbvTessLevelInner:
4401 case EbvTessLevelOuter:
4402 qualifier.patch = true;
4403 break;
John Kessenich6e1d50a2017-03-09 14:37:32 -07004404 default:
4405 break;
Dan Bakerdeec03c2016-08-25 11:59:17 -04004406 }
Dan Bakerdeec03c2016-08-25 11:59:17 -04004407
John Kessenich6e1d50a2017-03-09 14:37:32 -07004408 qualifier.builtIn = builtIn;
John Kessenich2dd643f2017-03-14 21:50:06 -06004409 qualifier.semanticName = intermediate.addSemanticName(upperCase);
John Kessenich630dd7d2016-06-12 23:52:12 -06004410}
4411
4412//
John Kessenich96e9f472016-07-29 14:28:39 -06004413// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN"
John Kessenich82d6baf2016-07-29 13:03:05 -06004414//
4415// 'location' has the "c[Subcomponent]" part.
4416// 'component' points to the "component" part, or nullptr if not present.
4417//
John Kessenich7735b942016-09-05 12:40:06 -06004418void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location,
4419 const glslang::TString* component)
John Kessenich82d6baf2016-07-29 13:03:05 -06004420{
4421 if (location.size() == 0 || location[0] != 'c') {
4422 error(loc, "expected 'c'", "packoffset", "");
4423 return;
4424 }
4425 if (location.size() == 1)
4426 return;
4427 if (! isdigit(location[1])) {
4428 error(loc, "expected number after 'c'", "packoffset", "");
4429 return;
4430 }
4431
John Kessenich7735b942016-09-05 12:40:06 -06004432 qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str());
John Kessenich96e9f472016-07-29 14:28:39 -06004433 if (component != nullptr) {
John Kessenich82d6baf2016-07-29 13:03:05 -06004434 int componentOffset = 0;
4435 switch ((*component)[0]) {
4436 case 'x': componentOffset = 0; break;
4437 case 'y': componentOffset = 4; break;
4438 case 'z': componentOffset = 8; break;
4439 case 'w': componentOffset = 12; break;
4440 default:
4441 componentOffset = -1;
4442 break;
4443 }
4444 if (componentOffset < 0 || component->size() > 1) {
4445 error(loc, "expected {x, y, z, w} for component", "packoffset", "");
4446 return;
4447 }
John Kessenich7735b942016-09-05 12:40:06 -06004448 qualifier.layoutOffset += componentOffset;
John Kessenich82d6baf2016-07-29 13:03:05 -06004449 }
4450}
4451
4452//
John Kessenich96e9f472016-07-29 14:28:39 -06004453// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN"
4454//
4455// 'profile' points to the shader_profile part, or nullptr if not present.
4456// 'desc' is the type# part.
4457//
John Kessenich7735b942016-09-05 12:40:06 -06004458void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile,
John Kessenichcfd7ce82016-09-05 16:03:12 -06004459 const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc)
John Kessenich96e9f472016-07-29 14:28:39 -06004460{
4461 if (profile != nullptr)
4462 warn(loc, "ignoring shader_profile", "register", "");
4463
John Kessenichb38f0712016-07-30 10:29:54 -06004464 if (desc.size() < 1) {
4465 error(loc, "expected register type", "register", "");
John Kessenich96e9f472016-07-29 14:28:39 -06004466 return;
4467 }
4468
John Kessenichb38f0712016-07-30 10:29:54 -06004469 int regNumber = 0;
4470 if (desc.size() > 1) {
4471 if (isdigit(desc[1]))
4472 regNumber = atoi(desc.substr(1, desc.size()).c_str());
4473 else {
4474 error(loc, "expected register number after register type", "register", "");
4475 return;
4476 }
John Kessenich96e9f472016-07-29 14:28:39 -06004477 }
4478
John Kessenichb38f0712016-07-30 10:29:54 -06004479 // TODO: learn what all these really mean and how they interact with regNumber and subComponent
steve-lunarg9088be42016-11-01 10:31:42 -06004480 switch (std::tolower(desc[0])) {
John Kessenich96e9f472016-07-29 14:28:39 -06004481 case 'b':
4482 case 't':
4483 case 'c':
4484 case 's':
steve-lunarg9088be42016-11-01 10:31:42 -06004485 case 'u':
John Kessenich7735b942016-09-05 12:40:06 -06004486 qualifier.layoutBinding = regNumber + subComponent;
John Kessenich96e9f472016-07-29 14:28:39 -06004487 break;
4488 default:
4489 warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]);
4490 break;
4491 }
John Kessenichcfd7ce82016-09-05 16:03:12 -06004492
4493 // space
4494 unsigned int setNumber;
baldurk54a28de2016-10-13 19:23:39 +02004495 const auto crackSpace = [&]() -> bool {
John Kessenichcfd7ce82016-09-05 16:03:12 -06004496 const int spaceLen = 5;
4497 if (spaceDesc->size() < spaceLen + 1)
4498 return false;
4499 if (spaceDesc->compare(0, spaceLen, "space") != 0)
4500 return false;
4501 if (! isdigit((*spaceDesc)[spaceLen]))
4502 return false;
4503 setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str());
4504 return true;
4505 };
4506
4507 if (spaceDesc) {
4508 if (! crackSpace()) {
4509 error(loc, "expected spaceN", "register", "");
4510 return;
4511 }
4512 qualifier.layoutSet = setNumber;
4513 }
John Kessenich96e9f472016-07-29 14:28:39 -06004514}
4515
John Kessenich7e997e22017-03-30 22:09:30 -06004516// Convert to a scalar boolean, or if not allowed by HLSL semantics,
4517// report an error and return nullptr.
4518TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition)
4519{
4520 if (!condition->getType().isScalarOrVec1()) {
4521 error(loc, "requires a scalar", "conditional expression", "");
4522 return nullptr;
4523 }
4524
4525 return intermediate.addConversion(EOpConstructBool, TType(EbtBool), condition);
4526}
4527
John Kessenich96e9f472016-07-29 14:28:39 -06004528//
John Kesseniche01a9bc2016-03-12 20:11:22 -07004529// Same error message for all places assignments don't work.
4530//
4531void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right)
4532{
4533 error(loc, "", op, "cannot convert from '%s' to '%s'",
4534 right.c_str(), left.c_str());
4535}
4536
4537//
4538// Same error message for all places unary operations don't work.
4539//
4540void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand)
4541{
4542 error(loc, " wrong operand type", op,
4543 "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)",
4544 op, operand.c_str());
4545}
4546
4547//
4548// Same error message for all binary operations don't work.
4549//
4550void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right)
4551{
4552 error(loc, " wrong operand types:", op,
4553 "no operation '%s' exists that takes a left-hand operand of type '%s' and "
4554 "a right operand of type '%s' (or there is no acceptable conversion)",
4555 op, left.c_str(), right.c_str());
4556}
4557
4558//
4559// A basic type of EbtVoid is a key that the name string was seen in the source, but
4560// it was not found as a variable in the symbol table. If so, give the error
4561// message and insert a dummy variable in the symbol table to prevent future errors.
4562//
4563void HlslParseContext::variableCheck(TIntermTyped*& nodePtr)
4564{
4565 TIntermSymbol* symbol = nodePtr->getAsSymbolNode();
4566 if (! symbol)
4567 return;
4568
4569 if (symbol->getType().getBasicType() == EbtVoid) {
4570 error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), "");
4571
4572 // Add to symbol table to prevent future error messages on the same name
4573 if (symbol->getName().size() > 0) {
4574 TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat));
4575 symbolTable.insert(*fakeVariable);
4576
4577 // substitute a symbol node for this new variable
4578 nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc());
4579 }
4580 }
4581}
4582
4583//
4584// Both test, and if necessary spit out an error, to see if the node is really
4585// a constant.
4586//
4587void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token)
4588{
4589 if (node->getQualifier().storage != EvqConst)
4590 error(node->getLoc(), "constant expression required", token, "");
4591}
4592
4593//
4594// Both test, and if necessary spit out an error, to see if the node is really
4595// an integer.
4596//
4597void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token)
4598{
4599 if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar())
4600 return;
4601
4602 error(node->getLoc(), "scalar integer expression required", token, "");
4603}
4604
4605//
4606// Both test, and if necessary spit out an error, to see if we are currently
4607// globally scoped.
4608//
4609void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token)
4610{
4611 if (! symbolTable.atGlobalLevel())
4612 error(loc, "not allowed in nested scope", token, "");
4613}
4614
John Kessenich7f349c72016-07-08 22:09:10 -06004615bool HlslParseContext::builtInName(const TString& /*identifier*/)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004616{
4617 return false;
4618}
4619
4620//
4621// Make sure there is enough data and not too many arguments provided to the
4622// constructor to build something of the type of the constructor. Also returns
4623// the type of the constructor.
4624//
4625// Returns true if there was an error in construction.
4626//
John Kessenichf97f2ce2016-11-27 22:51:36 -07004627bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function,
John Kessenich7f349c72016-07-08 22:09:10 -06004628 TOperator op, TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004629{
4630 type.shallowCopy(function.getType());
4631
4632 bool constructingMatrix = false;
4633 switch (op) {
4634 case EOpConstructTextureSampler:
4635 return constructorTextureSamplerError(loc, function);
4636 case EOpConstructMat2x2:
4637 case EOpConstructMat2x3:
4638 case EOpConstructMat2x4:
4639 case EOpConstructMat3x2:
4640 case EOpConstructMat3x3:
4641 case EOpConstructMat3x4:
4642 case EOpConstructMat4x2:
4643 case EOpConstructMat4x3:
4644 case EOpConstructMat4x4:
4645 case EOpConstructDMat2x2:
4646 case EOpConstructDMat2x3:
4647 case EOpConstructDMat2x4:
4648 case EOpConstructDMat3x2:
4649 case EOpConstructDMat3x3:
4650 case EOpConstructDMat3x4:
4651 case EOpConstructDMat4x2:
4652 case EOpConstructDMat4x3:
4653 case EOpConstructDMat4x4:
4654 constructingMatrix = true;
4655 break;
4656 default:
4657 break;
4658 }
4659
4660 //
4661 // Walk the arguments for first-pass checks and collection of information.
4662 //
4663
4664 int size = 0;
4665 bool constType = true;
4666 bool full = false;
4667 bool overFull = false;
4668 bool matrixInMatrix = false;
4669 bool arrayArg = false;
4670 for (int arg = 0; arg < function.getParamCount(); ++arg) {
4671 if (function[arg].type->isArray()) {
4672 if (! function[arg].type->isExplicitlySizedArray()) {
4673 // Can't construct from an unsized array.
4674 error(loc, "array argument must be sized", "constructor", "");
4675 return true;
4676 }
4677 arrayArg = true;
4678 }
4679 if (constructingMatrix && function[arg].type->isMatrix())
4680 matrixInMatrix = true;
4681
4682 // 'full' will go to true when enough args have been seen. If we loop
4683 // again, there is an extra argument.
4684 if (full) {
4685 // For vectors and matrices, it's okay to have too many components
4686 // available, but not okay to have unused arguments.
4687 overFull = true;
4688 }
4689
4690 size += function[arg].type->computeNumComponents();
4691 if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
4692 full = true;
4693
4694 if (function[arg].type->getQualifier().storage != EvqConst)
4695 constType = false;
4696 }
4697
4698 if (constType)
4699 type.getQualifier().storage = EvqConst;
4700
4701 if (type.isArray()) {
4702 if (function.getParamCount() == 0) {
4703 error(loc, "array constructor must have at least one argument", "constructor", "");
4704 return true;
4705 }
4706
4707 if (type.isImplicitlySizedArray()) {
4708 // auto adapt the constructor type to the number of arguments
4709 type.changeOuterArraySize(function.getParamCount());
4710 } else if (type.getOuterArraySize() != function.getParamCount()) {
4711 error(loc, "array constructor needs one argument per array element", "constructor", "");
4712 return true;
4713 }
4714
4715 if (type.isArrayOfArrays()) {
4716 // Types have to match, but we're still making the type.
4717 // Finish making the type, and the comparison is done later
4718 // when checking for conversion.
4719 TArraySizes& arraySizes = type.getArraySizes();
4720
4721 // At least the dimensionalities have to match.
4722 if (! function[0].type->isArray() || arraySizes.getNumDims() != function[0].type->getArraySizes().getNumDims() + 1) {
John Kessenich509c4212016-11-27 17:26:21 -07004723 error(loc, "array constructor argument not correct type to construct array element", "constructor", "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004724 return true;
4725 }
4726
4727 if (arraySizes.isInnerImplicit()) {
4728 // "Arrays of arrays ..., and the size for any dimension is optional"
4729 // That means we need to adopt (from the first argument) the other array sizes into the type.
4730 for (int d = 1; d < arraySizes.getNumDims(); ++d) {
4731 if (arraySizes.getDimSize(d) == UnsizedArraySize) {
4732 arraySizes.setDimSize(d, function[0].type->getArraySizes().getDimSize(d - 1));
4733 }
4734 }
4735 }
4736 }
4737 }
4738
4739 if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
4740 error(loc, "constructing non-array constituent from array argument", "constructor", "");
4741 return true;
4742 }
4743
4744 if (matrixInMatrix && ! type.isArray()) {
4745 return false;
4746 }
4747
4748 if (overFull) {
4749 error(loc, "too many arguments", "constructor", "");
4750 return true;
4751 }
4752
John Kessenich82460b52017-04-04 11:47:42 -06004753 if (op == EOpConstructStruct && ! type.isArray() && isScalarConstructor(node))
John Kessenichf97f2ce2016-11-27 22:51:36 -07004754 return false;
4755
John Kesseniche01a9bc2016-03-12 20:11:22 -07004756 if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) {
4757 error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", "");
4758 return true;
4759 }
4760
4761 if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) ||
4762 (op == EOpConstructStruct && size < type.computeNumComponents())) {
4763 error(loc, "not enough data provided for construction", "constructor", "");
4764 return true;
4765 }
4766
John Kesseniche01a9bc2016-03-12 20:11:22 -07004767 return false;
4768}
4769
John Kessenich82460b52017-04-04 11:47:42 -06004770// See if 'node', in the context of constructing aggregates, is a scalar argument
4771// to a constructor.
4772//
4773bool HlslParseContext::isScalarConstructor(const TIntermNode* node)
John Kessenichf97f2ce2016-11-27 22:51:36 -07004774{
John Kessenich82460b52017-04-04 11:47:42 -06004775 // Obviously, it must be a scalar, but an aggregate node might not be fully
4776 // completed yet: holding a sequence of initializers under an aggregate
4777 // would not yet be typed, so don't check it's type. This corresponds to
4778 // the aggregate operator also not being set yet. (An aggregate operation
4779 // that legitimately yields a scalar will have a getOp() of that operator,
4780 // not EOpNull.)
4781
4782 return node->getAsTyped() != nullptr &&
4783 node->getAsTyped()->isScalar() &&
4784 (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull);
John Kessenichf97f2ce2016-11-27 22:51:36 -07004785}
4786
John Kesseniche01a9bc2016-03-12 20:11:22 -07004787// Verify all the correct semantics for constructing a combined texture/sampler.
4788// Return true if the semantics are incorrect.
4789bool HlslParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function)
4790{
4791 TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change
4792 const char* token = constructorName.c_str();
4793
4794 // exactly two arguments needed
4795 if (function.getParamCount() != 2) {
4796 error(loc, "sampler-constructor requires two arguments", token, "");
4797 return true;
4798 }
4799
4800 // For now, not allowing arrayed constructors, the rest of this function
4801 // is set up to allow them, if this test is removed:
4802 if (function.getType().isArray()) {
4803 error(loc, "sampler-constructor cannot make an array of samplers", token, "");
4804 return true;
4805 }
4806
4807 // first argument
4808 // * the constructor's first argument must be a texture type
4809 // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array)
4810 // of the texture type must match that of the constructed sampler type
4811 // (that is, the suffixes of the type of the first argument and the
4812 // type of the constructor will be spelled the same way)
4813 if (function[0].type->getBasicType() != EbtSampler ||
4814 ! function[0].type->getSampler().isTexture() ||
4815 function[0].type->isArray()) {
4816 error(loc, "sampler-constructor first argument must be a scalar textureXXX type", token, "");
4817 return true;
4818 }
4819 // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=()
4820 TSampler texture = function.getType().getSampler();
4821 texture.combined = false;
4822 texture.shadow = false;
4823 if (texture != function[0].type->getSampler()) {
4824 error(loc, "sampler-constructor first argument must match type and dimensionality of constructor type", token, "");
4825 return true;
4826 }
4827
4828 // second argument
4829 // * the constructor's second argument must be a scalar of type
4830 // *sampler* or *samplerShadow*
4831 // * the presence or absence of depth comparison (Shadow) must match
4832 // between the constructed sampler type and the type of the second argument
4833 if (function[1].type->getBasicType() != EbtSampler ||
4834 ! function[1].type->getSampler().isPureSampler() ||
4835 function[1].type->isArray()) {
4836 error(loc, "sampler-constructor second argument must be a scalar type 'sampler'", token, "");
4837 return true;
4838 }
4839 if (function.getType().getSampler().shadow != function[1].type->getSampler().shadow) {
4840 error(loc, "sampler-constructor second argument presence of shadow must match constructor presence of shadow", token, "");
4841 return true;
4842 }
4843
4844 return false;
4845}
4846
4847// Checks to see if a void variable has been declared and raise an error message for such a case
4848//
4849// returns true in case of an error
4850//
4851bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType)
4852{
4853 if (basicType == EbtVoid) {
4854 error(loc, "illegal use of type 'void'", identifier.c_str(), "");
4855 return true;
4856 }
4857
4858 return false;
4859}
4860
John Kesseniche01a9bc2016-03-12 20:11:22 -07004861//
4862// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
4863//
John Kessenich7f349c72016-07-08 22:09:10 -06004864void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004865{
4866 // move from parameter/unknown qualifiers to pipeline in/out qualifiers
4867 switch (qualifier.storage) {
4868 case EvqIn:
4869 qualifier.storage = EvqVaryingIn;
4870 break;
4871 case EvqOut:
4872 qualifier.storage = EvqVaryingOut;
4873 break;
4874 default:
4875 break;
4876 }
4877}
4878
4879//
4880// Merge characteristics of the 'src' qualifier into the 'dst'.
4881// If there is duplication, issue error messages, unless 'force'
4882// is specified, which means to just override default settings.
4883//
4884// Also, when force is false, it will be assumed that 'src' follows
4885// 'dst', for the purpose of error checking order for versions
4886// that require specific orderings of qualifiers.
4887//
John Kessenich34e7ee72016-09-16 17:10:39 -06004888void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004889{
4890 // Storage qualification
4891 if (dst.storage == EvqTemporary || dst.storage == EvqGlobal)
4892 dst.storage = src.storage;
4893 else if ((dst.storage == EvqIn && src.storage == EvqOut) ||
John Kessenich3d157c52016-07-25 16:05:33 -06004894 (dst.storage == EvqOut && src.storage == EvqIn))
John Kesseniche01a9bc2016-03-12 20:11:22 -07004895 dst.storage = EvqInOut;
4896 else if ((dst.storage == EvqIn && src.storage == EvqConst) ||
John Kessenich3d157c52016-07-25 16:05:33 -06004897 (dst.storage == EvqConst && src.storage == EvqIn))
John Kesseniche01a9bc2016-03-12 20:11:22 -07004898 dst.storage = EvqConstReadOnly;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004899
John Kesseniche01a9bc2016-03-12 20:11:22 -07004900 // Layout qualifiers
4901 mergeObjectLayoutQualifiers(dst, src, false);
4902
4903 // individual qualifiers
4904 bool repeated = false;
4905#define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field;
4906 MERGE_SINGLETON(invariant);
John Kessenich17f07862016-05-04 12:36:14 -06004907 MERGE_SINGLETON(noContraction);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004908 MERGE_SINGLETON(centroid);
4909 MERGE_SINGLETON(smooth);
4910 MERGE_SINGLETON(flat);
4911 MERGE_SINGLETON(nopersp);
4912 MERGE_SINGLETON(patch);
4913 MERGE_SINGLETON(sample);
4914 MERGE_SINGLETON(coherent);
4915 MERGE_SINGLETON(volatil);
4916 MERGE_SINGLETON(restrict);
4917 MERGE_SINGLETON(readonly);
4918 MERGE_SINGLETON(writeonly);
4919 MERGE_SINGLETON(specConstant);
4920}
4921
4922// used to flatten the sampler type space into a single dimension
4923// correlates with the declaration of defaultSamplerPrecision[]
4924int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler)
4925{
4926 int arrayIndex = sampler.arrayed ? 1 : 0;
4927 int shadowIndex = sampler.shadow ? 1 : 0;
4928 int externalIndex = sampler.external ? 1 : 0;
4929
4930 return EsdNumDims * (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim;
4931}
4932
4933//
4934// Do size checking for an array type's size.
4935//
4936void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair)
4937{
4938 bool isConst = false;
4939 sizePair.size = 1;
4940 sizePair.node = nullptr;
4941
4942 TIntermConstantUnion* constant = expr->getAsConstantUnion();
4943 if (constant) {
4944 // handle true (non-specialization) constant
4945 sizePair.size = constant->getConstArray()[0].getIConst();
4946 isConst = true;
4947 } else {
4948 // see if it's a specialization constant instead
4949 if (expr->getQualifier().isSpecConstant()) {
4950 isConst = true;
4951 sizePair.node = expr;
4952 TIntermSymbol* symbol = expr->getAsSymbolNode();
4953 if (symbol && symbol->getConstArray().size() > 0)
4954 sizePair.size = symbol->getConstArray()[0].getIConst();
4955 }
4956 }
4957
4958 if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
4959 error(loc, "array size must be a constant integer expression", "", "");
4960 return;
4961 }
4962
4963 if (sizePair.size <= 0) {
4964 error(loc, "array size must be a positive integer", "", "");
4965 return;
4966 }
4967}
4968
4969//
4970// Require array to be completely sized
4971//
4972void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes)
4973{
4974 if (arraySizes.isImplicit())
4975 error(loc, "array size required", "", "");
4976}
4977
4978void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type)
4979{
4980 const TTypeList& structure = *type.getStruct();
4981 for (int m = 0; m < (int)structure.size(); ++m) {
4982 const TType& member = *structure[m].type;
4983 if (member.isArray())
4984 arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes());
4985 }
4986}
4987
4988// Merge array dimensions listed in 'sizes' onto the type's array dimensions.
4989//
4990// From the spec: "vec4[2] a[3]; // size-3 array of size-2 array of vec4"
4991//
4992// That means, the 'sizes' go in front of the 'type' as outermost sizes.
4993// 'type' is the type part of the declaration (to the left)
4994// 'sizes' is the arrayness tagged on the identifier (to the right)
4995//
4996void HlslParseContext::arrayDimMerge(TType& type, const TArraySizes* sizes)
4997{
4998 if (sizes)
4999 type.addArrayOuterSizes(*sizes);
5000}
5001
5002//
5003// Do all the semantic checking for declaring or redeclaring an array, with and
5004// without a size, and make the right changes to the symbol table.
5005//
John Kessenich4dc835c2017-03-28 23:43:10 -06005006void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol, bool track)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005007{
5008 if (! symbol) {
5009 bool currentScope;
5010 symbol = symbolTable.find(identifier, nullptr, &currentScope);
5011
5012 if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) {
5013 // bad shader (errors already reported) trying to redeclare a built-in name as an array
5014 return;
5015 }
5016 if (symbol == nullptr || ! currentScope) {
5017 //
5018 // Successfully process a new definition.
5019 // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations)
5020 //
5021 symbol = new TVariable(&identifier, type);
5022 symbolTable.insert(*symbol);
John Kessenichd3f11222016-11-05 10:15:53 -06005023 if (track && symbolTable.atGlobalLevel())
John Kessenich02467d82017-01-19 15:41:47 -07005024 trackLinkage(*symbol);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005025
John Kesseniche01a9bc2016-03-12 20:11:22 -07005026 return;
5027 }
5028 if (symbol->getAsAnonMember()) {
5029 error(loc, "cannot redeclare a user-block member array", identifier.c_str(), "");
5030 symbol = nullptr;
5031 return;
5032 }
5033 }
5034
5035 //
5036 // Process a redeclaration.
5037 //
5038
5039 if (! symbol) {
5040 error(loc, "array variable name expected", identifier.c_str(), "");
5041 return;
5042 }
5043
5044 // redeclareBuiltinVariable() should have already done the copyUp()
5045 TType& existingType = symbol->getWritableType();
5046
John Kesseniche01a9bc2016-03-12 20:11:22 -07005047 if (existingType.isExplicitlySizedArray()) {
5048 // be more lenient for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size
John Kesseniche01a9bc2016-03-12 20:11:22 -07005049 return;
5050 }
5051
5052 existingType.updateArraySizes(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005053}
5054
5055void HlslParseContext::updateImplicitArraySize(const TSourceLoc& loc, TIntermNode *node, int index)
5056{
5057 // maybe there is nothing to do...
5058 TIntermTyped* typedNode = node->getAsTyped();
5059 if (typedNode->getType().getImplicitArraySize() > index)
5060 return;
5061
5062 // something to do...
5063
5064 // Figure out what symbol to lookup, as we will use its type to edit for the size change,
5065 // as that type will be shared through shallow copies for future references.
5066 TSymbol* symbol = nullptr;
5067 int blockIndex = -1;
5068 const TString* lookupName = nullptr;
5069 if (node->getAsSymbolNode())
5070 lookupName = &node->getAsSymbolNode()->getName();
5071 else if (node->getAsBinaryNode()) {
5072 const TIntermBinary* deref = node->getAsBinaryNode();
5073 // This has to be the result of a block dereference, unless it's bad shader code
5074 // If it's a uniform block, then an error will be issued elsewhere, but
5075 // return early now to avoid crashing later in this function.
5076 if (! deref->getLeft()->getAsSymbolNode() || deref->getLeft()->getBasicType() != EbtBlock ||
5077 deref->getLeft()->getType().getQualifier().storage == EvqUniform ||
5078 deref->getRight()->getAsConstantUnion() == nullptr)
5079 return;
5080
5081 blockIndex = deref->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
5082
5083 lookupName = &deref->getLeft()->getAsSymbolNode()->getName();
5084 if (IsAnonymous(*lookupName))
5085 lookupName = &(*deref->getLeft()->getType().getStruct())[blockIndex].type->getFieldName();
5086 }
5087
5088 // Lookup the symbol, should only fail if shader code is incorrect
5089 symbol = symbolTable.find(*lookupName);
5090 if (symbol == nullptr)
5091 return;
5092
5093 if (symbol->getAsFunction()) {
5094 error(loc, "array variable name expected", symbol->getName().c_str(), "");
5095 return;
5096 }
5097
5098 symbol->getWritableType().setImplicitArraySize(index + 1);
5099}
5100
5101//
John Kessenichaa6d5622016-12-30 16:42:57 -07005102// Enforce non-initializer type/qualifier rules.
5103//
John Kessenich4dc835c2017-03-28 23:43:10 -06005104void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type, TIntermTyped*& initializer)
John Kessenichaa6d5622016-12-30 16:42:57 -07005105{
5106 //
5107 // Make the qualifier make sense, given that there is an initializer.
5108 //
5109 if (initializer == nullptr) {
5110 if (type.getQualifier().storage == EvqConst ||
5111 type.getQualifier().storage == EvqConstReadOnly) {
5112 initializer = intermediate.makeAggregate(loc);
5113 warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), "");
5114 }
5115 }
5116}
5117
5118//
John Kesseniche01a9bc2016-03-12 20:11:22 -07005119// See if the identifier is a built-in symbol that can be redeclared, and if so,
5120// copy the symbol table's read-only built-in variable to the current
5121// global level, where it can be modified based on the passed in type.
5122//
5123// Returns nullptr if no redeclaration took place; meaning a normal declaration still
5124// needs to occur for it, not necessarily an error.
5125//
5126// Returns a redeclared and type-modified variable if a redeclared occurred.
5127//
John Kessenich7f349c72016-07-08 22:09:10 -06005128TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier,
5129 const TQualifier& /*qualifier*/,
John Kessenichd3f11222016-11-05 10:15:53 -06005130 const TShaderQualifiers& /*publicType*/)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005131{
5132 if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel())
5133 return nullptr;
5134
5135 return nullptr;
5136}
5137
5138//
5139// Either redeclare the requested block, or give an error message why it can't be done.
5140//
5141// TODO: functionality: explicitly sizing members of redeclared blocks is not giving them an explicit size
5142void HlslParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes)
5143{
5144 // Redeclaring a built-in block...
5145
5146 // Blocks with instance names are easy to find, lookup the instance name,
5147 // Anonymous blocks need to be found via a member.
5148 bool builtIn;
5149 TSymbol* block;
5150 if (instanceName)
5151 block = symbolTable.find(*instanceName, &builtIn);
5152 else
5153 block = symbolTable.find(newTypeList.front().type->getFieldName(), &builtIn);
5154
5155 // If the block was not found, this must be a version/profile/stage
5156 // that doesn't have it, or the instance name is wrong.
5157 const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str();
5158 if (! block) {
5159 error(loc, "no declaration found for redeclaration", errorName, "");
5160 return;
5161 }
5162 // Built-in blocks cannot be redeclared more than once, which if happened,
5163 // we'd be finding the already redeclared one here, rather than the built in.
5164 if (! builtIn) {
5165 error(loc, "can only redeclare a built-in block once, and before any use", blockName.c_str(), "");
5166 return;
5167 }
5168
5169 // Copy the block to make a writable version, to insert into the block table after editing.
5170 block = symbolTable.copyUpDeferredInsert(block);
5171
5172 if (block->getType().getBasicType() != EbtBlock) {
5173 error(loc, "cannot redeclare a non block as a block", errorName, "");
5174 return;
5175 }
5176
5177 // Edit and error check the container against the redeclaration
5178 // - remove unused members
5179 // - ensure remaining qualifiers/types match
5180 TType& type = block->getWritableType();
5181 TTypeList::iterator member = type.getWritableStruct()->begin();
5182 size_t numOriginalMembersFound = 0;
5183 while (member != type.getStruct()->end()) {
5184 // look for match
5185 bool found = false;
5186 TTypeList::const_iterator newMember;
5187 TSourceLoc memberLoc;
5188 memberLoc.init();
5189 for (newMember = newTypeList.begin(); newMember != newTypeList.end(); ++newMember) {
5190 if (member->type->getFieldName() == newMember->type->getFieldName()) {
5191 found = true;
5192 memberLoc = newMember->loc;
5193 break;
5194 }
5195 }
5196
5197 if (found) {
5198 ++numOriginalMembersFound;
5199 // - ensure match between redeclared members' types
5200 // - check for things that can't be changed
5201 // - update things that can be changed
5202 TType& oldType = *member->type;
5203 const TType& newType = *newMember->type;
5204 if (! newType.sameElementType(oldType))
5205 error(memberLoc, "cannot redeclare block member with a different type", member->type->getFieldName().c_str(), "");
5206 if (oldType.isArray() != newType.isArray())
5207 error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), "");
5208 else if (! oldType.sameArrayness(newType) && oldType.isExplicitlySizedArray())
5209 error(memberLoc, "cannot change array size of redeclared block member", member->type->getFieldName().c_str(), "");
5210 if (newType.getQualifier().isMemory())
5211 error(memberLoc, "cannot add memory qualifier to redeclared block member", member->type->getFieldName().c_str(), "");
5212 if (newType.getQualifier().hasLayout())
5213 error(memberLoc, "cannot add layout to redeclared block member", member->type->getFieldName().c_str(), "");
5214 if (newType.getQualifier().patch)
5215 error(memberLoc, "cannot add patch to redeclared block member", member->type->getFieldName().c_str(), "");
5216 oldType.getQualifier().centroid = newType.getQualifier().centroid;
5217 oldType.getQualifier().sample = newType.getQualifier().sample;
5218 oldType.getQualifier().invariant = newType.getQualifier().invariant;
John Kessenich17f07862016-05-04 12:36:14 -06005219 oldType.getQualifier().noContraction = newType.getQualifier().noContraction;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005220 oldType.getQualifier().smooth = newType.getQualifier().smooth;
5221 oldType.getQualifier().flat = newType.getQualifier().flat;
5222 oldType.getQualifier().nopersp = newType.getQualifier().nopersp;
5223
5224 // go to next member
5225 ++member;
5226 } else {
5227 // For missing members of anonymous blocks that have been redeclared,
5228 // hide the original (shared) declaration.
5229 // Instance-named blocks can just have the member removed.
5230 if (instanceName)
5231 member = type.getWritableStruct()->erase(member);
5232 else {
5233 member->type->hideMember();
5234 ++member;
5235 }
5236 }
5237 }
5238
5239 if (numOriginalMembersFound < newTypeList.size())
5240 error(loc, "block redeclaration has extra members", blockName.c_str(), "");
5241 if (type.isArray() != (arraySizes != nullptr))
5242 error(loc, "cannot change arrayness of redeclared block", blockName.c_str(), "");
5243 else if (type.isArray()) {
5244 if (type.isExplicitlySizedArray() && arraySizes->getOuterSize() == UnsizedArraySize)
5245 error(loc, "block already declared with size, can't redeclare as implicitly-sized", blockName.c_str(), "");
5246 else if (type.isExplicitlySizedArray() && type.getArraySizes() != *arraySizes)
5247 error(loc, "cannot change array size of redeclared block", blockName.c_str(), "");
5248 else if (type.isImplicitlySizedArray() && arraySizes->getOuterSize() != UnsizedArraySize)
5249 type.changeOuterArraySize(arraySizes->getOuterSize());
5250 }
5251
5252 symbolTable.insert(*block);
5253
John Kesseniche01a9bc2016-03-12 20:11:22 -07005254 // Save it in the AST for linker use.
John Kessenich02467d82017-01-19 15:41:47 -07005255 trackLinkage(*block);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005256}
5257
steve-lunargdd8287a2017-02-23 18:04:12 -07005258//
5259// Generate index to the array element in a structure buffer (SSBO)
5260//
5261TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const
5262{
5263 // Bail out if not a struct buffer
5264 if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
5265 return nullptr;
5266
5267 // Runtime sized array is always the last element.
5268 const TTypeList* bufferStruct = buffer->getType().getStruct();
5269 TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc);
5270
5271 TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc);
5272 argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type);
5273
5274 return argArray;
5275}
5276
5277//
5278// IFF type is a structuredbuffer/byteaddressbuffer type, return the content
5279// (template) type. E.g, StructuredBuffer<MyType> -> MyType. Else return nullptr.
5280//
5281TType* HlslParseContext::getStructBufferContentType(const TType& type) const
5282{
5283 if (type.getBasicType() != EbtBlock)
5284 return nullptr;
5285
Daniel Kochd9b7a852017-03-03 10:34:07 -05005286 const int memberCount = (int)type.getStruct()->size();
steve-lunargdd8287a2017-02-23 18:04:12 -07005287 assert(memberCount > 0);
5288
5289 TType* contentType = (*type.getStruct())[memberCount-1].type;
5290
5291 return contentType->isRuntimeSizedArray() ? contentType : nullptr;
5292}
5293
5294//
5295// If an existing struct buffer has a sharable type, then share it.
5296//
5297void HlslParseContext::shareStructBufferType(TType& type)
5298{
5299 // PackOffset must be equivalent to share types on a per-member basis.
5300 // Note: cannot use auto type due to recursion. Thus, this is a std::function.
5301 const std::function<bool(TType& lhs, TType& rhs)>
5302 compareQualifiers = [&](TType& lhs, TType& rhs) -> bool {
5303 if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset)
5304 return false;
5305
5306 if (lhs.isStruct() != rhs.isStruct())
5307 return false;
5308
5309 if (lhs.isStruct() && rhs.isStruct()) {
5310 if (lhs.getStruct()->size() != rhs.getStruct()->size())
5311 return false;
5312
5313 for (int i = 0; i < int(lhs.getStruct()->size()); ++i)
5314 if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type))
5315 return false;
5316 }
5317
5318 return true;
5319 };
5320
5321 // We need to compare certain qualifiers in addition to the type.
5322 const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool {
5323 if (lhs.getQualifier().readonly != rhs.getQualifier().readonly)
5324 return false;
5325
5326 // If both are structures, recursively look for packOffset equality
5327 // as well as type equality.
5328 return compareQualifiers(lhs, rhs) && lhs == rhs;
5329 };
5330
steve-lunargdd8287a2017-02-23 18:04:12 -07005331 // This is an exhaustive O(N) search, but real world shaders have
5332 // only a small number of these.
5333 for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) {
5334 // If the deep structure matches, modulo qualifiers, use it
5335 if (typeEqual(*structBufferTypes[idx], type)) {
5336 type.shallowCopy(*structBufferTypes[idx]);
5337 return;
5338 }
5339 }
5340
5341 // Otherwise, remember it:
5342 TType* typeCopy = new TType;
5343 typeCopy->shallowCopy(type);
5344 structBufferTypes.push_back(typeCopy);
steve-lunargdd8287a2017-02-23 18:04:12 -07005345}
5346
John Kessenich5aa59e22016-06-17 15:50:47 -06005347void HlslParseContext::paramFix(TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005348{
John Kessenich5aa59e22016-06-17 15:50:47 -06005349 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005350 case EvqConst:
John Kesseniche01a9bc2016-03-12 20:11:22 -07005351 type.getQualifier().storage = EvqConstReadOnly;
5352 break;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005353 case EvqGlobal:
5354 case EvqTemporary:
5355 type.getQualifier().storage = EvqIn;
5356 break;
steve-lunargdd8287a2017-02-23 18:04:12 -07005357 case EvqBuffer:
5358 {
5359 // SSBO parameter. These do not go through the declareBlock path since they are fn parameters.
5360 correctUniform(type.getQualifier());
5361 TQualifier bufferQualifier = globalBufferDefaults;
5362 mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true);
5363 bufferQualifier.storage = type.getQualifier().storage;
5364 bufferQualifier.readonly = type.getQualifier().readonly;
5365 bufferQualifier.coherent = type.getQualifier().coherent;
5366 type.getQualifier() = bufferQualifier;
5367 break;
5368 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005369 default:
John Kesseniche01a9bc2016-03-12 20:11:22 -07005370 break;
5371 }
5372}
5373
John Kesseniche01a9bc2016-03-12 20:11:22 -07005374void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
5375{
5376 if (type.containsSpecializationSize())
5377 error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
5378}
5379
5380//
5381// Layout qualifier stuff.
5382//
5383
5384// Put the id's layout qualification into the public type, for qualifiers not having a number set.
5385// This is before we know any type information for error checking.
John Kessenichb9e39122016-08-17 10:22:08 -06005386void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005387{
5388 std::transform(id.begin(), id.end(), id.begin(), ::tolower);
5389
5390 if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
John Kessenich10f7fc72016-09-25 20:25:06 -06005391 qualifier.layoutMatrix = ElmRowMajor;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005392 return;
5393 }
5394 if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
John Kessenich10f7fc72016-09-25 20:25:06 -06005395 qualifier.layoutMatrix = ElmColumnMajor;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005396 return;
5397 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005398 if (id == "push_constant") {
5399 requireVulkan(loc, "push_constant");
John Kessenichb9e39122016-08-17 10:22:08 -06005400 qualifier.layoutPushConstant = true;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005401 return;
5402 }
5403 if (language == EShLangGeometry || language == EShLangTessEvaluation) {
5404 if (id == TQualifier::getGeometryString(ElgTriangles)) {
John Kessenich927608b2017-01-06 12:34:14 -07005405 // publicType.shaderQualifiers.geometry = ElgTriangles;
John Kessenichb9e39122016-08-17 10:22:08 -06005406 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005407 return;
5408 }
5409 if (language == EShLangGeometry) {
5410 if (id == TQualifier::getGeometryString(ElgPoints)) {
John Kessenich927608b2017-01-06 12:34:14 -07005411 // publicType.shaderQualifiers.geometry = ElgPoints;
John Kessenichb9e39122016-08-17 10:22:08 -06005412 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005413 return;
5414 }
5415 if (id == TQualifier::getGeometryString(ElgLineStrip)) {
John Kessenich927608b2017-01-06 12:34:14 -07005416 // publicType.shaderQualifiers.geometry = ElgLineStrip;
John Kessenichb9e39122016-08-17 10:22:08 -06005417 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005418 return;
5419 }
5420 if (id == TQualifier::getGeometryString(ElgLines)) {
John Kessenich927608b2017-01-06 12:34:14 -07005421 // publicType.shaderQualifiers.geometry = ElgLines;
John Kessenichb9e39122016-08-17 10:22:08 -06005422 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005423 return;
5424 }
5425 if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
John Kessenich927608b2017-01-06 12:34:14 -07005426 // publicType.shaderQualifiers.geometry = ElgLinesAdjacency;
John Kessenichb9e39122016-08-17 10:22:08 -06005427 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005428 return;
5429 }
5430 if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
John Kessenich927608b2017-01-06 12:34:14 -07005431 // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency;
John Kessenichb9e39122016-08-17 10:22:08 -06005432 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005433 return;
5434 }
5435 if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
John Kessenich927608b2017-01-06 12:34:14 -07005436 // publicType.shaderQualifiers.geometry = ElgTriangleStrip;
John Kessenichb9e39122016-08-17 10:22:08 -06005437 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005438 return;
5439 }
5440 } else {
5441 assert(language == EShLangTessEvaluation);
5442
5443 // input primitive
5444 if (id == TQualifier::getGeometryString(ElgTriangles)) {
John Kessenich927608b2017-01-06 12:34:14 -07005445 // publicType.shaderQualifiers.geometry = ElgTriangles;
John Kessenichb9e39122016-08-17 10:22:08 -06005446 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005447 return;
5448 }
5449 if (id == TQualifier::getGeometryString(ElgQuads)) {
John Kessenich927608b2017-01-06 12:34:14 -07005450 // publicType.shaderQualifiers.geometry = ElgQuads;
John Kessenichb9e39122016-08-17 10:22:08 -06005451 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005452 return;
5453 }
5454 if (id == TQualifier::getGeometryString(ElgIsolines)) {
John Kessenich927608b2017-01-06 12:34:14 -07005455 // publicType.shaderQualifiers.geometry = ElgIsolines;
John Kessenichb9e39122016-08-17 10:22:08 -06005456 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005457 return;
5458 }
5459
5460 // vertex spacing
5461 if (id == TQualifier::getVertexSpacingString(EvsEqual)) {
John Kessenich927608b2017-01-06 12:34:14 -07005462 // publicType.shaderQualifiers.spacing = EvsEqual;
John Kessenichb9e39122016-08-17 10:22:08 -06005463 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005464 return;
5465 }
5466 if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) {
John Kessenich927608b2017-01-06 12:34:14 -07005467 // publicType.shaderQualifiers.spacing = EvsFractionalEven;
John Kessenichb9e39122016-08-17 10:22:08 -06005468 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005469 return;
5470 }
5471 if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) {
John Kessenich927608b2017-01-06 12:34:14 -07005472 // publicType.shaderQualifiers.spacing = EvsFractionalOdd;
John Kessenichb9e39122016-08-17 10:22:08 -06005473 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005474 return;
5475 }
5476
5477 // triangle order
5478 if (id == TQualifier::getVertexOrderString(EvoCw)) {
John Kessenich927608b2017-01-06 12:34:14 -07005479 // publicType.shaderQualifiers.order = EvoCw;
John Kessenichb9e39122016-08-17 10:22:08 -06005480 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005481 return;
5482 }
5483 if (id == TQualifier::getVertexOrderString(EvoCcw)) {
John Kessenich927608b2017-01-06 12:34:14 -07005484 // publicType.shaderQualifiers.order = EvoCcw;
John Kessenichb9e39122016-08-17 10:22:08 -06005485 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005486 return;
5487 }
5488
5489 // point mode
5490 if (id == "point_mode") {
John Kessenich927608b2017-01-06 12:34:14 -07005491 // publicType.shaderQualifiers.pointMode = true;
John Kessenichb9e39122016-08-17 10:22:08 -06005492 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005493 return;
5494 }
5495 }
5496 }
5497 if (language == EShLangFragment) {
5498 if (id == "origin_upper_left") {
John Kessenich927608b2017-01-06 12:34:14 -07005499 // publicType.shaderQualifiers.originUpperLeft = true;
John Kessenichb9e39122016-08-17 10:22:08 -06005500 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005501 return;
5502 }
5503 if (id == "pixel_center_integer") {
John Kessenich927608b2017-01-06 12:34:14 -07005504 // publicType.shaderQualifiers.pixelCenterInteger = true;
John Kessenichb9e39122016-08-17 10:22:08 -06005505 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005506 return;
5507 }
5508 if (id == "early_fragment_tests") {
John Kessenich927608b2017-01-06 12:34:14 -07005509 // publicType.shaderQualifiers.earlyFragmentTests = true;
John Kessenichb9e39122016-08-17 10:22:08 -06005510 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005511 return;
5512 }
5513 for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) {
5514 if (id == TQualifier::getLayoutDepthString(depth)) {
John Kessenich927608b2017-01-06 12:34:14 -07005515 // publicType.shaderQualifiers.layoutDepth = depth;
John Kessenichb9e39122016-08-17 10:22:08 -06005516 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005517 return;
5518 }
5519 }
5520 if (id.compare(0, 13, "blend_support") == 0) {
5521 bool found = false;
5522 for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
5523 if (id == TQualifier::getBlendEquationString(be)) {
5524 requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation");
5525 intermediate.addBlendEquation(be);
John Kessenich927608b2017-01-06 12:34:14 -07005526 // publicType.shaderQualifiers.blendEquation = true;
John Kessenichb9e39122016-08-17 10:22:08 -06005527 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005528 found = true;
5529 break;
5530 }
5531 }
5532 if (! found)
5533 error(loc, "unknown blend equation", "blend_support", "");
5534 return;
5535 }
5536 }
5537 error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
5538}
5539
5540// Put the id's layout qualifier value into the public type, for qualifiers having a number set.
5541// This is before we know any type information for error checking.
John Kessenichb9e39122016-08-17 10:22:08 -06005542void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id, const TIntermTyped* node)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005543{
5544 const char* feature = "layout-id value";
John Kessenich927608b2017-01-06 12:34:14 -07005545 // const char* nonLiteralFeature = "non-literal layout-id value";
John Kesseniche01a9bc2016-03-12 20:11:22 -07005546
5547 integerCheck(node, feature);
5548 const TIntermConstantUnion* constUnion = node->getAsConstantUnion();
5549 int value = 0;
5550 if (constUnion) {
5551 value = constUnion->getConstArray()[0].getIConst();
5552 }
5553
5554 std::transform(id.begin(), id.end(), id.begin(), ::tolower);
5555
5556 if (id == "offset") {
John Kessenichb9e39122016-08-17 10:22:08 -06005557 qualifier.layoutOffset = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005558 return;
5559 } else if (id == "align") {
5560 // "The specified alignment must be a power of 2, or a compile-time error results."
5561 if (! IsPow2(value))
5562 error(loc, "must be a power of 2", "align", "");
5563 else
John Kessenichb9e39122016-08-17 10:22:08 -06005564 qualifier.layoutAlign = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005565 return;
5566 } else if (id == "location") {
5567 if ((unsigned int)value >= TQualifier::layoutLocationEnd)
5568 error(loc, "location is too large", id.c_str(), "");
5569 else
John Kessenichb9e39122016-08-17 10:22:08 -06005570 qualifier.layoutLocation = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005571 return;
5572 } else if (id == "set") {
5573 if ((unsigned int)value >= TQualifier::layoutSetEnd)
5574 error(loc, "set is too large", id.c_str(), "");
5575 else
John Kessenichb9e39122016-08-17 10:22:08 -06005576 qualifier.layoutSet = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005577 return;
5578 } else if (id == "binding") {
5579 if ((unsigned int)value >= TQualifier::layoutBindingEnd)
5580 error(loc, "binding is too large", id.c_str(), "");
5581 else
John Kessenichb9e39122016-08-17 10:22:08 -06005582 qualifier.layoutBinding = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005583 return;
5584 } else if (id == "component") {
5585 if ((unsigned)value >= TQualifier::layoutComponentEnd)
5586 error(loc, "component is too large", id.c_str(), "");
5587 else
John Kessenichb9e39122016-08-17 10:22:08 -06005588 qualifier.layoutComponent = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005589 return;
5590 } else if (id.compare(0, 4, "xfb_") == 0) {
John Kessenichecba76f2017-01-06 00:34:48 -07005591 // "Any shader making any static use (after preprocessing) of any of these
5592 // *xfb_* qualifiers will cause the shader to be in a transform feedback
5593 // capturing mode and hence responsible for describing the transform feedback
John Kesseniche01a9bc2016-03-12 20:11:22 -07005594 // setup."
5595 intermediate.setXfbMode();
5596 if (id == "xfb_buffer") {
5597 // "It is a compile-time error to specify an *xfb_buffer* that is greater than
5598 // the implementation-dependent constant gl_MaxTransformFeedbackBuffers."
5599 if (value >= resources.maxTransformFeedbackBuffers)
5600 error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", resources.maxTransformFeedbackBuffers);
5601 if (value >= (int)TQualifier::layoutXfbBufferEnd)
5602 error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1);
5603 else
John Kessenichb9e39122016-08-17 10:22:08 -06005604 qualifier.layoutXfbBuffer = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005605 return;
5606 } else if (id == "xfb_offset") {
5607 if (value >= (int)TQualifier::layoutXfbOffsetEnd)
5608 error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1);
5609 else
John Kessenichb9e39122016-08-17 10:22:08 -06005610 qualifier.layoutXfbOffset = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005611 return;
5612 } else if (id == "xfb_stride") {
John Kessenichecba76f2017-01-06 00:34:48 -07005613 // "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 -07005614 // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
5615 if (value > 4 * resources.maxTransformFeedbackInterleavedComponents)
5616 error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", resources.maxTransformFeedbackInterleavedComponents);
5617 else if (value >= (int)TQualifier::layoutXfbStrideEnd)
5618 error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1);
5619 if (value < (int)TQualifier::layoutXfbStrideEnd)
John Kessenichb9e39122016-08-17 10:22:08 -06005620 qualifier.layoutXfbStride = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005621 return;
5622 }
5623 }
5624
5625 if (id == "input_attachment_index") {
5626 requireVulkan(loc, "input_attachment_index");
5627 if (value >= (int)TQualifier::layoutAttachmentEnd)
5628 error(loc, "attachment index is too large", id.c_str(), "");
5629 else
John Kessenichb9e39122016-08-17 10:22:08 -06005630 qualifier.layoutAttachment = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005631 return;
5632 }
5633 if (id == "constant_id") {
5634 requireSpv(loc, "constant_id");
5635 if (value >= (int)TQualifier::layoutSpecConstantIdEnd) {
5636 error(loc, "specialization-constant id is too large", id.c_str(), "");
5637 } else {
John Kessenichb9e39122016-08-17 10:22:08 -06005638 qualifier.layoutSpecConstantId = value;
5639 qualifier.specConstant = true;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005640 if (! intermediate.addUsedConstantId(value))
5641 error(loc, "specialization-constant id already used", id.c_str(), "");
5642 }
5643 return;
5644 }
5645
5646 switch (language) {
5647 case EShLangVertex:
5648 break;
5649
5650 case EShLangTessControl:
5651 if (id == "vertices") {
5652 if (value == 0)
5653 error(loc, "must be greater than 0", "vertices", "");
5654 else
John Kessenich927608b2017-01-06 12:34:14 -07005655 // publicType.shaderQualifiers.vertices = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005656 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005657 return;
5658 }
5659 break;
5660
5661 case EShLangTessEvaluation:
5662 break;
5663
5664 case EShLangGeometry:
5665 if (id == "invocations") {
5666 if (value == 0)
5667 error(loc, "must be at least 1", "invocations", "");
5668 else
John Kessenich927608b2017-01-06 12:34:14 -07005669 // publicType.shaderQualifiers.invocations = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005670 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005671 return;
5672 }
5673 if (id == "max_vertices") {
John Kessenich927608b2017-01-06 12:34:14 -07005674 // publicType.shaderQualifiers.vertices = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005675 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005676 if (value > resources.maxGeometryOutputVertices)
5677 error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", "");
5678 return;
5679 }
5680 if (id == "stream") {
John Kessenichb9e39122016-08-17 10:22:08 -06005681 qualifier.layoutStream = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005682 return;
5683 }
5684 break;
5685
5686 case EShLangFragment:
5687 if (id == "index") {
John Kessenichb9e39122016-08-17 10:22:08 -06005688 qualifier.layoutIndex = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005689 return;
5690 }
5691 break;
5692
5693 case EShLangCompute:
5694 if (id.compare(0, 11, "local_size_") == 0) {
5695 if (id == "local_size_x") {
John Kessenich927608b2017-01-06 12:34:14 -07005696 // publicType.shaderQualifiers.localSize[0] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005697 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005698 return;
5699 }
5700 if (id == "local_size_y") {
John Kessenich927608b2017-01-06 12:34:14 -07005701 // publicType.shaderQualifiers.localSize[1] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005702 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005703 return;
5704 }
5705 if (id == "local_size_z") {
John Kessenich927608b2017-01-06 12:34:14 -07005706 // publicType.shaderQualifiers.localSize[2] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005707 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005708 return;
5709 }
John Kessenichb901ade2016-06-16 20:59:42 -06005710 if (spvVersion.spv != 0) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005711 if (id == "local_size_x_id") {
John Kessenich927608b2017-01-06 12:34:14 -07005712 // publicType.shaderQualifiers.localSizeSpecId[0] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005713 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005714 return;
5715 }
5716 if (id == "local_size_y_id") {
John Kessenich927608b2017-01-06 12:34:14 -07005717 // publicType.shaderQualifiers.localSizeSpecId[1] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005718 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005719 return;
5720 }
5721 if (id == "local_size_z_id") {
John Kessenich927608b2017-01-06 12:34:14 -07005722 // publicType.shaderQualifiers.localSizeSpecId[2] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005723 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005724 return;
5725 }
5726 }
5727 }
5728 break;
5729
5730 default:
5731 break;
5732 }
5733
5734 error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
5735}
5736
5737// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
5738//
5739// "More than one layout qualifier may appear in a single declaration.
John Kessenichecba76f2017-01-06 00:34:48 -07005740// Additionally, the same layout-qualifier-name can occur multiple times
5741// within a layout qualifier or across multiple layout qualifiers in the
5742// same declaration. When the same layout-qualifier-name occurs
5743// multiple times, in a single declaration, the last occurrence overrides
5744// the former occurrence(s). Further, if such a layout-qualifier-name
5745// will effect subsequent declarations or other observable behavior, it
5746// is only the last occurrence that will have any effect, behaving as if
5747// the earlier occurrence(s) within the declaration are not present.
5748// This is also true for overriding layout-qualifier-names, where one
5749// overrides the other (e.g., row_major vs. column_major); only the last
5750// occurrence has any effect."
John Kesseniche01a9bc2016-03-12 20:11:22 -07005751//
5752void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly)
5753{
5754 if (src.hasMatrix())
5755 dst.layoutMatrix = src.layoutMatrix;
5756 if (src.hasPacking())
5757 dst.layoutPacking = src.layoutPacking;
5758
5759 if (src.hasStream())
5760 dst.layoutStream = src.layoutStream;
5761
5762 if (src.hasFormat())
5763 dst.layoutFormat = src.layoutFormat;
5764
5765 if (src.hasXfbBuffer())
5766 dst.layoutXfbBuffer = src.layoutXfbBuffer;
5767
5768 if (src.hasAlign())
5769 dst.layoutAlign = src.layoutAlign;
5770
5771 if (! inheritOnly) {
5772 if (src.hasLocation())
5773 dst.layoutLocation = src.layoutLocation;
5774 if (src.hasComponent())
5775 dst.layoutComponent = src.layoutComponent;
5776 if (src.hasIndex())
5777 dst.layoutIndex = src.layoutIndex;
5778
5779 if (src.hasOffset())
5780 dst.layoutOffset = src.layoutOffset;
5781
5782 if (src.hasSet())
5783 dst.layoutSet = src.layoutSet;
5784 if (src.layoutBinding != TQualifier::layoutBindingEnd)
5785 dst.layoutBinding = src.layoutBinding;
5786
5787 if (src.hasXfbStride())
5788 dst.layoutXfbStride = src.layoutXfbStride;
5789 if (src.hasXfbOffset())
5790 dst.layoutXfbOffset = src.layoutXfbOffset;
5791 if (src.hasAttachment())
5792 dst.layoutAttachment = src.layoutAttachment;
5793 if (src.hasSpecConstantId())
5794 dst.layoutSpecConstantId = src.layoutSpecConstantId;
5795
5796 if (src.layoutPushConstant)
5797 dst.layoutPushConstant = true;
5798 }
5799}
5800
5801//
5802// Look up a function name in the symbol table, and make sure it is a function.
5803//
John Kessenichfcc0aa32016-08-24 18:34:43 -06005804// First, look for an exact match. If there is none, use the generic selector
John Kessenichecba76f2017-01-06 00:34:48 -07005805// TParseContextBase::selectFunction() to find one, parameterized by the
John Kessenichfcc0aa32016-08-24 18:34:43 -06005806// convertible() and better() predicates defined below.
5807//
John Kesseniche01a9bc2016-03-12 20:11:22 -07005808// Return the function symbol if found, otherwise nullptr.
5809//
steve-lunarg26d31452016-12-23 18:56:57 -07005810const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn,
5811 TIntermTyped*& args)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005812{
John Kessenich7f349c72016-07-08 22:09:10 -06005813 // const TFunction* function = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005814
5815 if (symbolTable.isFunctionNameVariable(call.getName())) {
5816 error(loc, "can't use function syntax on variable", call.getName().c_str(), "");
5817 return nullptr;
5818 }
5819
5820 // first, look for an exact match
5821 TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn);
5822 if (symbol)
5823 return symbol->getAsFunction();
5824
John Kessenichfcc0aa32016-08-24 18:34:43 -06005825 // no exact match, use the generic selector, parameterized by the GLSL rules
John Kesseniche01a9bc2016-03-12 20:11:22 -07005826
John Kessenichfcc0aa32016-08-24 18:34:43 -06005827 // create list of candidates to send
John Kessenich0a04b4d2016-08-19 07:27:28 -06005828 TVector<const TFunction*> candidateList;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005829 symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
John Kessenichecba76f2017-01-06 00:34:48 -07005830
steve-lunargf49cdf42016-11-17 15:04:20 -07005831 // These builtin ops can accept any type, so we bypass the argument selection
5832 if (candidateList.size() == 1 && builtIn &&
5833 (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
5834 candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip)) {
5835
5836 return candidateList[0];
5837 }
5838
steve-lunarg05f75142016-12-06 15:50:11 -07005839 bool allowOnlyUpConversions = true;
5840
John Kessenichfcc0aa32016-08-24 18:34:43 -06005841 // can 'from' convert to 'to'?
steve-lunarg05f75142016-12-06 15:50:11 -07005842 const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool {
John Kessenichfcc0aa32016-08-24 18:34:43 -06005843 if (from == to)
5844 return true;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005845
5846 // no aggregate conversions
John Kessenichecba76f2017-01-06 00:34:48 -07005847 if (from.isArray() || to.isArray() ||
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005848 from.isStruct() || to.isStruct())
John Kessenichfcc0aa32016-08-24 18:34:43 -06005849 return false;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005850
steve-lunarg05f75142016-12-06 15:50:11 -07005851 switch (op) {
5852 case EOpInterlockedAdd:
5853 case EOpInterlockedAnd:
5854 case EOpInterlockedCompareExchange:
5855 case EOpInterlockedCompareStore:
5856 case EOpInterlockedExchange:
5857 case EOpInterlockedMax:
5858 case EOpInterlockedMin:
5859 case EOpInterlockedOr:
5860 case EOpInterlockedXor:
5861 // We do not promote the texture or image type for these ocodes. Normally that would not
5862 // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this
5863 // stage it's merely e.g, a basic integer type.
John Kessenichecba76f2017-01-06 00:34:48 -07005864 //
steve-lunarg05f75142016-12-06 15:50:11 -07005865 // Instead, we want to promote other arguments, but stay within the same family. In other
5866 // words, InterlockedAdd(RWBuffer<int>, ...) will always use the int flavor, never the uint flavor,
5867 // but it is allowed to promote its other arguments.
5868 if (arg == 0)
5869 return false;
t.jung84e59202017-01-02 17:10:27 +01005870 break;
5871 case EOpMethodSample:
5872 case EOpMethodSampleBias:
5873 case EOpMethodSampleCmp:
5874 case EOpMethodSampleCmpLevelZero:
5875 case EOpMethodSampleGrad:
5876 case EOpMethodSampleLevel:
5877 case EOpMethodLoad:
5878 case EOpMethodGetDimensions:
5879 case EOpMethodGetSamplePosition:
5880 case EOpMethodGather:
5881 case EOpMethodCalculateLevelOfDetail:
5882 case EOpMethodCalculateLevelOfDetailUnclamped:
5883 case EOpMethodGatherRed:
5884 case EOpMethodGatherGreen:
5885 case EOpMethodGatherBlue:
5886 case EOpMethodGatherAlpha:
5887 case EOpMethodGatherCmp:
5888 case EOpMethodGatherCmpRed:
5889 case EOpMethodGatherCmpGreen:
5890 case EOpMethodGatherCmpBlue:
5891 case EOpMethodGatherCmpAlpha:
5892 case EOpMethodAppend:
5893 case EOpMethodRestartStrip:
5894 // those are method calls, the object type can not be changed
5895 // they are equal if the dim and type match (is dim sufficient?)
5896 if (arg == 0)
5897 return from.getSampler().type == to.getSampler().type &&
5898 from.getSampler().arrayed == to.getSampler().arrayed &&
5899 from.getSampler().shadow == to.getSampler().shadow &&
5900 from.getSampler().ms == to.getSampler().ms &&
5901 from.getSampler().dim == to.getSampler().dim;
5902 break;
steve-lunarg05f75142016-12-06 15:50:11 -07005903 default:
5904 break;
5905 }
5906
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005907 // basic types have to be convertible
steve-lunarg05f75142016-12-06 15:50:11 -07005908 if (allowOnlyUpConversions)
5909 if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall))
5910 return false;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005911
5912 // shapes have to be convertible
steve-lunargd9cb8322016-11-11 15:37:10 -07005913 if ((from.isScalarOrVec1() && to.isScalarOrVec1()) ||
5914 (from.isScalarOrVec1() && to.isVector()) ||
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005915 (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize()))
5916 return true;
5917
5918 // TODO: what are the matrix rules? they go here
5919
5920 return false;
John Kessenichfcc0aa32016-08-24 18:34:43 -06005921 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07005922
John Kessenichfcc0aa32016-08-24 18:34:43 -06005923 // Is 'to2' a better conversion than 'to1'?
5924 // Ties should not be considered as better.
5925 // Assumes 'convertible' already said true.
baldurk54a28de2016-10-13 19:23:39 +02005926 const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool {
John Kessenich90dd70f2016-08-25 10:49:21 -06005927 // exact match is always better than mismatch
John Kessenichfcc0aa32016-08-24 18:34:43 -06005928 if (from == to2)
5929 return from != to1;
5930 if (from == to1)
5931 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005932
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005933 // shape changes are always worse
5934 if (from.isScalar() || from.isVector()) {
5935 if (from.getVectorSize() == to2.getVectorSize() &&
5936 from.getVectorSize() != to1.getVectorSize())
John Kessenichfcc0aa32016-08-24 18:34:43 -06005937 return true;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005938 if (from.getVectorSize() == to1.getVectorSize() &&
5939 from.getVectorSize() != to2.getVectorSize())
5940 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005941 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005942
steve-lunarg26d31452016-12-23 18:56:57 -07005943 // Handle sampler betterness: An exact sampler match beats a non-exact match.
5944 // (If we just looked at basic type, all EbtSamplers would look the same).
5945 // If any type is not a sampler, just use the linearize function below.
5946 if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) {
5947 // We can ignore the vector size in the comparison.
5948 TSampler to1Sampler = to1.getSampler();
5949 TSampler to2Sampler = to2.getSampler();
5950
5951 to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize;
5952
5953 if (from.getSampler() == to2Sampler)
5954 return from.getSampler() != to1Sampler;
5955 if (from.getSampler() == to1Sampler)
5956 return false;
5957 }
5958
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005959 // Might or might not be changing shape, which means basic type might
5960 // or might not match, so within that, the question is how big a
5961 // basic-type conversion is being done.
5962 //
5963 // Use a hierarchy of domains, translated to order of magnitude
5964 // in a linearized view:
5965 // - floating-point vs. integer
5966 // - 32 vs. 64 bit (or width in general)
5967 // - bool vs. non bool
5968 // - signed vs. not signed
baldurk54a28de2016-10-13 19:23:39 +02005969 const auto linearize = [](const TBasicType& basicType) -> int {
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005970 switch (basicType) {
5971 case EbtBool: return 1;
5972 case EbtInt: return 10;
5973 case EbtUint: return 11;
5974 case EbtInt64: return 20;
5975 case EbtUint64: return 21;
5976 case EbtFloat: return 100;
5977 case EbtDouble: return 110;
5978 default: return 0;
5979 }
5980 };
John Kessenich90dd70f2016-08-25 10:49:21 -06005981
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005982 return std::abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) <
5983 std::abs(linearize(to1.getBasicType()) - linearize(from.getBasicType()));
John Kessenichfcc0aa32016-08-24 18:34:43 -06005984 };
5985
5986 // for ambiguity reporting
5987 bool tie = false;
John Kessenichecba76f2017-01-06 00:34:48 -07005988
John Kessenichfcc0aa32016-08-24 18:34:43 -06005989 // send to the generic selector
5990 const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie);
5991
steve-lunargef33ec02016-11-02 12:42:34 -06005992 if (bestMatch == nullptr) {
steve-lunarg05f75142016-12-06 15:50:11 -07005993 // If there is nothing selected by allowing only up-conversions (to a larger linearize() value),
5994 // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any
5995 // upconversions possible.
5996 allowOnlyUpConversions = false;
5997 bestMatch = selectFunction(candidateList, call, convertible, better, tie);
5998 }
5999
6000 if (bestMatch == nullptr) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006001 error(loc, "no matching overloaded function found", call.getName().c_str(), "");
steve-lunargef33ec02016-11-02 12:42:34 -06006002 return nullptr;
6003 }
6004
6005 // For builtins, we can convert across the arguments. This will happen in several steps:
6006 // Step 1: If there's an exact match, use it.
6007 // Step 2a: Otherwise, get the operator from the best match and promote arguments:
6008 // Step 2b: reconstruct the TFunction based on the new arg types
6009 // Step 3: Re-select after type promotion is applied, to find proper candidate.
6010 if (builtIn) {
6011 // Step 1: If there's an exact match, use it.
6012 if (call.getMangledName() == bestMatch->getMangledName())
6013 return bestMatch;
6014
6015 // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we
6016 // are that kind of operator.
6017 if (args != nullptr) {
6018 // The arg list can be a unary node, or an aggregate. We have to handle both.
6019 // We will use the normal promote() facilities, which require an interm node.
6020 TIntermOperator* promote = nullptr;
6021
6022 if (call.getParamCount() == 1) {
6023 promote = new TIntermUnary(bestMatch->getBuiltInOp());
6024 promote->getAsUnaryNode()->setOperand(args->getAsTyped());
6025 } else {
6026 promote = new TIntermAggregate(bestMatch->getBuiltInOp());
6027 promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
6028 }
6029
6030 if (! intermediate.promote(promote))
6031 return nullptr;
6032
6033 // Obtain the promoted arg list.
6034 if (call.getParamCount() == 1) {
6035 args = promote->getAsUnaryNode()->getOperand();
6036 } else {
6037 promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
6038 }
6039 }
6040
6041 // Step 2b: reconstruct the TFunction based on the new arg types
6042 TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp());
6043
6044 if (args->getAsAggregate()) {
6045 // Handle aggregates: put all args into the new function call
6046 for (int arg=0; arg<int(args->getAsAggregate()->getSequence().size()); ++arg) {
6047 // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly.
steve-lunarg26d31452016-12-23 18:56:57 -07006048 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06006049 param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType());
6050 convertedCall.addParameter(param);
6051 }
6052 } else if (args->getAsUnaryNode()) {
6053 // Handle unaries: put all args into the new function call
steve-lunarg26d31452016-12-23 18:56:57 -07006054 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06006055 param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType());
6056 convertedCall.addParameter(param);
6057 } else if (args->getAsTyped()) {
6058 // Handle bare e.g, floats, not in an aggregate.
steve-lunarg26d31452016-12-23 18:56:57 -07006059 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06006060 param.type->shallowCopy(args->getAsTyped()->getType());
6061 convertedCall.addParameter(param);
6062 } else {
6063 assert(0); // unknown argument list.
6064 return nullptr;
6065 }
6066
6067 // Step 3: Re-select after type promotion, to find proper candidate
6068 // send to the generic selector
6069 bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie);
6070
6071 // At this point, there should be no tie.
6072 }
6073
6074 if (tie)
John Kessenichfcc0aa32016-08-24 18:34:43 -06006075 error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006076
steve-lunarg26d31452016-12-23 18:56:57 -07006077 // Append default parameter values if needed
6078 if (!tie && bestMatch != nullptr) {
6079 for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) {
6080 handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue);
6081 }
6082 }
6083
John Kessenichfcc0aa32016-08-24 18:34:43 -06006084 return bestMatch;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006085}
6086
6087//
John Kessenich5e69ec62016-07-05 00:02:40 -06006088// Do everything necessary to handle a typedef declaration, for a single symbol.
John Kessenichecba76f2017-01-06 00:34:48 -07006089//
John Kessenich5e69ec62016-07-05 00:02:40 -06006090// 'parseType' is the type part of the declaration (to the left)
6091// 'arraySizes' is the arrayness tagged on the identifier (to the right)
6092//
John Kessenich4dc835c2017-03-28 23:43:10 -06006093void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType)
John Kessenich5e69ec62016-07-05 00:02:40 -06006094{
John Kessenichdd402602017-02-08 13:59:30 -07006095 TVariable* typeSymbol = new TVariable(&identifier, parseType, true);
John Kessenich5e69ec62016-07-05 00:02:40 -06006096 if (! symbolTable.insert(*typeSymbol))
6097 error(loc, "name already defined", "typedef", identifier.c_str());
6098}
6099
John Kessenich727b3742017-02-03 17:57:55 -07006100// Do everything necessary to handle a struct declaration, including
6101// making IO aliases because HLSL allows mixed IO in a struct that specializes
6102// based on the usage (input, output, uniform, none).
6103void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type)
steve-lunarga2e75312016-12-14 15:22:25 -07006104{
John Kessenich727b3742017-02-03 17:57:55 -07006105 // If it was named, which means the type can be reused later, add
6106 // it to the symbol table. (Unless it's a block, in which
6107 // case the name is not a type.)
6108 if (type.getBasicType() == EbtBlock || structName.size() == 0)
John Kessenichabd8dca2017-02-01 18:09:17 -07006109 return;
steve-lunarg5d3023a2017-01-25 10:03:17 -07006110
John Kessenich727b3742017-02-03 17:57:55 -07006111 TVariable* userTypeDef = new TVariable(&structName, type, true);
6112 if (! symbolTable.insert(*userTypeDef)) {
6113 error(loc, "redefinition", structName.c_str(), "struct");
6114 return;
6115 }
steve-lunarg5d3023a2017-01-25 10:03:17 -07006116
John Kessenich727b3742017-02-03 17:57:55 -07006117 // See if we need IO aliases for the structure typeList
6118
John Kessenichbf472862017-02-05 20:27:30 -07006119 const auto condAlloc = [](bool pred, TTypeList*& list) {
6120 if (pred && list == nullptr)
6121 list = new TTypeList;
6122 };
6123
6124 tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found
John Kessenich727b3742017-02-03 17:57:55 -07006125 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
John Kessenichbf472862017-02-05 20:27:30 -07006126 condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform);
6127 condAlloc( hasInput(member->type->getQualifier()), newLists.input);
6128 condAlloc( hasOutput(member->type->getQualifier()), newLists.output);
6129
John Kessenich727b3742017-02-03 17:57:55 -07006130 if (member->type->isStruct()) {
John Kessenichbf472862017-02-05 20:27:30 -07006131 auto it = ioTypeMap.find(member->type->getStruct());
6132 if (it != ioTypeMap.end()) {
6133 condAlloc(it->second.uniform != nullptr, newLists.uniform);
6134 condAlloc(it->second.input != nullptr, newLists.input);
6135 condAlloc(it->second.output != nullptr, newLists.output);
John Kessenich727b3742017-02-03 17:57:55 -07006136 }
6137 }
6138 }
John Kessenichbf472862017-02-05 20:27:30 -07006139 if (newLists.uniform == nullptr &&
6140 newLists.input == nullptr &&
John Kessenich65ee2302017-02-06 18:44:52 -07006141 newLists.output == nullptr) {
6142 // Won't do any IO caching, clear up the type and get out now.
6143 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member)
6144 clearUniformInputOutput(member->type->getQualifier());
John Kessenichabd8dca2017-02-01 18:09:17 -07006145 return;
John Kessenich65ee2302017-02-06 18:44:52 -07006146 }
steve-lunarga2e75312016-12-14 15:22:25 -07006147
John Kessenich727b3742017-02-03 17:57:55 -07006148 // We have IO involved.
steve-lunarga2e75312016-12-14 15:22:25 -07006149
John Kessenich727b3742017-02-03 17:57:55 -07006150 // Make a pure typeList for the symbol table, and cache side copies of IO versions.
John Kessenich727b3742017-02-03 17:57:55 -07006151 for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
John Kessenichbf472862017-02-05 20:27:30 -07006152 const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) {
6153 if (s != nullptr) {
6154 ioMember.type = new TType;
6155 ioMember.type->shallowCopy(*member->type);
6156 ioMember.type->setStruct(s);
6157 }
6158 };
6159 const auto newMember = [&](TTypeLoc& m) {
6160 if (m.type == nullptr) {
6161 m.type = new TType;
6162 m.type->shallowCopy(*member->type);
6163 }
6164 };
6165
6166 TTypeLoc newUniformMember = { nullptr, member->loc };
6167 TTypeLoc newInputMember = { nullptr, member->loc };
6168 TTypeLoc newOutputMember = { nullptr, member->loc };
John Kessenich727b3742017-02-03 17:57:55 -07006169 if (member->type->isStruct()) {
6170 // swap in an IO child if there is one
6171 auto it = ioTypeMap.find(member->type->getStruct());
John Kessenichbf472862017-02-05 20:27:30 -07006172 if (it != ioTypeMap.end()) {
6173 inheritStruct(it->second.uniform, newUniformMember);
6174 inheritStruct(it->second.input, newInputMember);
6175 inheritStruct(it->second.output, newOutputMember);
6176 }
John Kessenich727b3742017-02-03 17:57:55 -07006177 }
John Kessenichbf472862017-02-05 20:27:30 -07006178 if (newLists.uniform) {
6179 newMember(newUniformMember);
6180 correctUniform(newUniformMember.type->getQualifier());
6181 newLists.uniform->push_back(newUniformMember);
6182 }
6183 if (newLists.input) {
6184 newMember(newInputMember);
6185 correctInput(newInputMember.type->getQualifier());
6186 newLists.input->push_back(newInputMember);
6187 }
6188 if (newLists.output) {
6189 newMember(newOutputMember);
6190 correctOutput(newOutputMember.type->getQualifier());
6191 newLists.output->push_back(newOutputMember);
6192 }
6193
6194 // make original pure
6195 clearUniformInputOutput(member->type->getQualifier());
John Kessenich727b3742017-02-03 17:57:55 -07006196 }
John Kessenichbf472862017-02-05 20:27:30 -07006197 ioTypeMap[type.getStruct()] = newLists;
steve-lunarga2e75312016-12-14 15:22:25 -07006198}
6199
John Kessenich854fe242017-03-02 14:30:59 -07006200// Lookup a user-type by name.
6201// If found, fill in the type and return the defining symbol.
6202// If not found, return nullptr.
6203TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type)
6204{
6205 TSymbol* symbol = symbolTable.find(typeName);
6206 if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
6207 type.shallowCopy(symbol->getType());
6208 return symbol;
6209 } else
6210 return nullptr;
6211}
6212
John Kessenich5e69ec62016-07-05 00:02:40 -06006213//
John Kesseniche01a9bc2016-03-12 20:11:22 -07006214// Do everything necessary to handle a variable (non-block) declaration.
6215// Either redeclaring a variable, or making a new one, updating the symbol
6216// table, and all error checking.
6217//
6218// Returns a subtree node that computes an initializer, if needed.
6219// Returns nullptr if there is no code to execute for initialization.
6220//
John Kessenich5e69ec62016-07-05 00:02:40 -06006221// 'parseType' is the type part of the declaration (to the left)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006222// 'arraySizes' is the arrayness tagged on the identifier (to the right)
6223//
John Kessenich4dc835c2017-03-28 23:43:10 -06006224TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type, TIntermTyped* initializer)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006225{
John Kesseniche01a9bc2016-03-12 20:11:22 -07006226 if (voidErrorCheck(loc, identifier, type.getBasicType()))
6227 return nullptr;
6228
John Kessenichaa6d5622016-12-30 16:42:57 -07006229 // make const and initialization consistent
6230 fixConstInit(loc, identifier, type, initializer);
6231
John Kesseniche01a9bc2016-03-12 20:11:22 -07006232 // Check for redeclaration of built-ins and/or attempting to declare a reserved name
John Kessenichd3f11222016-11-05 10:15:53 -06006233 TSymbol* symbol = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006234
6235 inheritGlobalDefaults(type.getQualifier());
6236
John Kessenich02467d82017-01-19 15:41:47 -07006237 const bool flattenVar = shouldFlattenUniform(type);
steve-lunarga2e75312016-12-14 15:22:25 -07006238
John Kessenichbf472862017-02-05 20:27:30 -07006239 // correct IO in the type
steve-lunarg5d3023a2017-01-25 10:03:17 -07006240 switch (type.getQualifier().storage) {
6241 case EvqGlobal:
6242 case EvqTemporary:
John Kessenichbf472862017-02-05 20:27:30 -07006243 clearUniformInputOutput(type.getQualifier());
6244 break;
6245 case EvqUniform:
6246 case EvqBuffer:
6247 correctUniform(type.getQualifier());
John Kessenich65ee2302017-02-06 18:44:52 -07006248 if (type.isStruct()) {
6249 auto it = ioTypeMap.find(type.getStruct());
6250 if (it != ioTypeMap.end())
6251 type.setStruct(it->second.uniform);
6252 }
steve-lunargdd8287a2017-02-23 18:04:12 -07006253
John Kessenichbf472862017-02-05 20:27:30 -07006254 break;
steve-lunarg5d3023a2017-01-25 10:03:17 -07006255 default:
6256 break;
6257 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06006258
John Kesseniche01a9bc2016-03-12 20:11:22 -07006259 // Declare the variable
John Kesseniche82061d2016-09-27 14:38:57 -06006260 if (type.isArray()) {
6261 // array case
steve-lunarg5d3023a2017-01-25 10:03:17 -07006262 declareArray(loc, identifier, type, symbol, !flattenVar);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006263 } else {
6264 // non-array case
6265 if (! symbol)
steve-lunarg5d3023a2017-01-25 10:03:17 -07006266 symbol = declareNonArray(loc, identifier, type, !flattenVar);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006267 else if (type != symbol->getType())
6268 error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
6269 }
6270
steve-lunarga2b01a02016-11-28 17:09:54 -07006271 if (flattenVar)
6272 flatten(loc, *symbol->getAsVariable());
6273
John Kesseniche01a9bc2016-03-12 20:11:22 -07006274 if (! symbol)
6275 return nullptr;
6276
6277 // Deal with initializer
6278 TIntermNode* initNode = nullptr;
6279 if (symbol && initializer) {
steve-lunarge0b9deb2016-09-16 13:26:37 -06006280 if (flattenVar)
6281 error(loc, "flattened array with initializer list unsupported", identifier.c_str(), "");
6282
John Kesseniche01a9bc2016-03-12 20:11:22 -07006283 TVariable* variable = symbol->getAsVariable();
6284 if (! variable) {
6285 error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
6286 return nullptr;
6287 }
6288 initNode = executeInitializer(loc, initializer, variable);
6289 }
6290
John Kesseniche01a9bc2016-03-12 20:11:22 -07006291 return initNode;
6292}
6293
6294// Pick up global defaults from the provide global defaults into dst.
6295void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const
6296{
6297 if (dst.storage == EvqVaryingOut) {
6298 if (! dst.hasStream() && language == EShLangGeometry)
6299 dst.layoutStream = globalOutputDefaults.layoutStream;
6300 if (! dst.hasXfbBuffer())
6301 dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
6302 }
6303}
6304
6305//
6306// Make an internal-only variable whose name is for debug purposes only
6307// and won't be searched for. Callers will only use the return value to use
6308// the variable, not the name to look it up. It is okay if the name
6309// is the same as other names; there won't be any conflict.
6310//
6311TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const
6312{
John Kesseniche48b8d72017-01-19 15:29:25 -07006313 TString* nameString = NewPoolTString(name);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006314 TVariable* variable = new TVariable(nameString, type);
6315 symbolTable.makeInternalVariable(*variable);
6316
6317 return variable;
6318}
6319
John Kessenich82460b52017-04-04 11:47:42 -06006320// Make a symbol node holding a new internal temporary variable.
6321TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name, const TType& type) const
6322{
6323 TVariable* tmpVar = makeInternalVariable(name, type);
6324 tmpVar->getWritableType().getQualifier().makeTemporary();
6325
6326 return intermediate.addSymbol(*tmpVar, loc);
6327}
6328
John Kesseniche01a9bc2016-03-12 20:11:22 -07006329//
6330// Declare a non-array variable, the main point being there is no redeclaration
6331// for resizing allowed.
6332//
6333// Return the successfully declared variable.
6334//
John Kessenich4dc835c2017-03-28 23:43:10 -06006335TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type, bool track)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006336{
6337 // make a new variable
6338 TVariable* variable = new TVariable(&identifier, type);
6339
6340 // add variable to symbol table
John Kessenichd3f11222016-11-05 10:15:53 -06006341 if (symbolTable.insert(*variable)) {
steve-lunarga2b01a02016-11-28 17:09:54 -07006342 if (track && symbolTable.atGlobalLevel())
John Kessenich02467d82017-01-19 15:41:47 -07006343 trackLinkage(*variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006344 return variable;
6345 }
John Kessenichd3f11222016-11-05 10:15:53 -06006346
6347 error(loc, "redefinition", variable->getName().c_str(), "");
6348 return nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006349}
6350
6351//
6352// Handle all types of initializers from the grammar.
6353//
6354// Returning nullptr just means there is no code to execute to handle the
6355// initializer, which will, for example, be the case for constant initializers.
6356//
6357TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
6358{
6359 //
6360 // Identifier must be of type constant, a global, or a temporary, and
6361 // starting at version 120, desktop allows uniforms to have initializers.
6362 //
6363 TStorageQualifier qualifier = variable->getType().getQualifier().storage;
6364
6365 //
6366 // If the initializer was from braces { ... }, we convert the whole subtree to a
6367 // constructor-style subtree, allowing the rest of the code to operate
6368 // identically for both kinds of initializers.
6369 //
John Kessenich085b8332017-01-05 10:28:26 -07006370 //
6371 // Type can't be deduced from the initializer list, so a skeletal type to
6372 // follow has to be passed in. Constness and specialization-constness
6373 // should be deduced bottom up, not dictated by the skeletal type.
6374 //
6375 TType skeletalType;
6376 skeletalType.shallowCopy(variable->getType());
6377 skeletalType.getQualifier().makeTemporary();
John Kessenich98ad4852016-11-27 17:39:07 -07006378 if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
John Kessenich82460b52017-04-04 11:47:42 -06006379 initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006380 if (! initializer) {
6381 // error recovery; don't leave const without constant values
6382 if (qualifier == EvqConst)
6383 variable->getWritableType().getQualifier().storage = EvqTemporary;
6384 return nullptr;
6385 }
6386
6387 // Fix outer arrayness if variable is unsized, getting size from the initializer
6388 if (initializer->getType().isExplicitlySizedArray() &&
6389 variable->getType().isImplicitlySizedArray())
6390 variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize());
6391
6392 // Inner arrayness can also get set by an initializer
6393 if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() &&
6394 initializer->getType().getArraySizes()->getNumDims() ==
6395 variable->getType().getArraySizes()->getNumDims()) {
6396 // adopt unsized sizes from the initializer's sizes
6397 for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) {
6398 if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize)
6399 variable->getWritableType().getArraySizes().setDimSize(d, initializer->getType().getArraySizes()->getDimSize(d));
6400 }
6401 }
6402
6403 // Uniform and global consts require a constant initializer
6404 if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) {
6405 error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
6406 variable->getWritableType().getQualifier().storage = EvqTemporary;
6407 return nullptr;
6408 }
6409 if (qualifier == EvqConst && symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) {
6410 error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
6411 variable->getWritableType().getQualifier().storage = EvqTemporary;
6412 return nullptr;
6413 }
6414
6415 // Const variables require a constant initializer, depending on version
6416 if (qualifier == EvqConst) {
6417 if (initializer->getType().getQualifier().storage != EvqConst) {
6418 variable->getWritableType().getQualifier().storage = EvqConstReadOnly;
6419 qualifier = EvqConstReadOnly;
6420 }
6421 }
6422
6423 if (qualifier == EvqConst || qualifier == EvqUniform) {
6424 // Compile-time tagging of the variable with its constant value...
6425
6426 initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
6427 if (! initializer || ! initializer->getAsConstantUnion() || variable->getType() != initializer->getType()) {
6428 error(loc, "non-matching or non-convertible constant type for const initializer",
6429 variable->getType().getStorageQualifierString(), "");
6430 variable->getWritableType().getQualifier().storage = EvqTemporary;
6431 return nullptr;
6432 }
6433
6434 variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
6435 } else {
6436 // normal assigning of a value to a variable...
6437 specializationCheck(loc, initializer->getType(), "initializer");
6438 TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
steve-lunarge0b9deb2016-09-16 13:26:37 -06006439 TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006440 if (! initNode)
6441 assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
6442
6443 return initNode;
6444 }
6445
6446 return nullptr;
6447}
6448
6449//
6450// Reprocess any initializer-list { ... } parts of the initializer.
6451// Need to hierarchically assign correct types and implicit
6452// conversions. Will do this mimicking the same process used for
6453// creating a constructor-style initializer, ensuring we get the
6454// same form.
6455//
John Kessenichf97f2ce2016-11-27 22:51:36 -07006456// Returns a node representing an expression for the initializer list expressed
6457// as the correct type.
6458//
6459// Returns nullptr if there is an error.
6460//
John Kessenich82460b52017-04-04 11:47:42 -06006461TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type,
6462 TIntermTyped* initializer, TIntermTyped* scalarInit)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006463{
6464 // Will operate recursively. Once a subtree is found that is constructor style,
6465 // everything below it is already good: Only the "top part" of the initializer
6466 // can be an initializer list, where "top part" can extend for several (or all) levels.
6467
6468 // see if we have bottomed out in the tree within the initializer-list part
6469 TIntermAggregate* initList = initializer->getAsAggregate();
John Kessenich98ad4852016-11-27 17:39:07 -07006470 if (! initList || initList->getOp() != EOpNull) {
6471 // We don't have a list, but if it's a scalar and the 'type' is a
6472 // composite, we need to lengthen below to make it useful.
6473 // Otherwise, this is an already formed object to initialize with.
6474 if (type.isScalar() || !initializer->getType().isScalar())
6475 return initializer;
6476 else
6477 initList = intermediate.makeAggregate(initializer);
6478 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07006479
6480 // Of the initializer-list set of nodes, need to process bottom up,
6481 // so recurse deep, then process on the way up.
6482
6483 // Go down the tree here...
6484 if (type.isArray()) {
6485 // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate.
6486 // Later on, initializer execution code will deal with array size logic.
6487 TType arrayType;
6488 arrayType.shallowCopy(type); // sharing struct stuff is fine
6489 arrayType.newArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below
6490
6491 // edit array sizes to fill in unsized dimensions
John Kessenich98ad4852016-11-27 17:39:07 -07006492 if (type.isImplicitlySizedArray())
6493 arrayType.changeOuterArraySize((int)initList->getSequence().size());
John Kessenich53864842016-12-30 15:59:28 -07006494
6495 // set unsized array dimensions that can be derived from the initializer's first element
6496 if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) {
6497 TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped();
6498 if (firstInit->getType().isArray() &&
6499 arrayType.getArraySizes().getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) {
6500 for (int d = 1; d < arrayType.getArraySizes().getNumDims(); ++d) {
6501 if (arrayType.getArraySizes().getDimSize(d) == UnsizedArraySize)
6502 arrayType.getArraySizes().setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1));
6503 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07006504 }
6505 }
6506
John Kessenich98ad4852016-11-27 17:39:07 -07006507 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06006508 lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit);
John Kessenich98ad4852016-11-27 17:39:07 -07006509
6510 // recursively process each element
John Kesseniche01a9bc2016-03-12 20:11:22 -07006511 TType elementType(arrayType, 0); // dereferenced type
John Kessenich98ad4852016-11-27 17:39:07 -07006512 for (int i = 0; i < arrayType.getOuterArraySize(); ++i) {
John Kessenich82460b52017-04-04 11:47:42 -06006513 initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped(), scalarInit);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006514 if (initList->getSequence()[i] == nullptr)
6515 return nullptr;
6516 }
6517
John Kessenicha26a5172016-07-28 15:29:35 -06006518 return addConstructor(loc, initList, arrayType);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006519 } else if (type.isStruct()) {
John Kessenich98ad4852016-11-27 17:39:07 -07006520 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06006521 lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()), scalarInit);
John Kessenich98ad4852016-11-27 17:39:07 -07006522
John Kesseniche01a9bc2016-03-12 20:11:22 -07006523 if (type.getStruct()->size() != initList->getSequence().size()) {
6524 error(loc, "wrong number of structure members", "initializer list", "");
6525 return nullptr;
6526 }
6527 for (size_t i = 0; i < type.getStruct()->size(); ++i) {
John Kessenich82460b52017-04-04 11:47:42 -06006528 initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped(), scalarInit);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006529 if (initList->getSequence()[i] == nullptr)
6530 return nullptr;
6531 }
6532 } else if (type.isMatrix()) {
steve-lunarg297ae212016-08-24 14:36:13 -06006533 if (type.computeNumComponents() == (int)initList->getSequence().size()) {
6534 // This means the matrix is initialized component-wise, rather than as
6535 // a series of rows and columns. We can just use the list directly as
6536 // a constructor; no further processing needed.
6537 } else {
John Kessenich98ad4852016-11-27 17:39:07 -07006538 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06006539 lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit);
John Kessenich98ad4852016-11-27 17:39:07 -07006540
steve-lunarg297ae212016-08-24 14:36:13 -06006541 if (type.getMatrixCols() != (int)initList->getSequence().size()) {
6542 error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
John Kesseniche01a9bc2016-03-12 20:11:22 -07006543 return nullptr;
steve-lunarg297ae212016-08-24 14:36:13 -06006544 }
6545 TType vectorType(type, 0); // dereferenced type
6546 for (int i = 0; i < type.getMatrixCols(); ++i) {
John Kessenich82460b52017-04-04 11:47:42 -06006547 initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped(), scalarInit);
steve-lunarg297ae212016-08-24 14:36:13 -06006548 if (initList->getSequence()[i] == nullptr)
6549 return nullptr;
6550 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07006551 }
6552 } else if (type.isVector()) {
John Kessenich98ad4852016-11-27 17:39:07 -07006553 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06006554 lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit);
John Kessenich98ad4852016-11-27 17:39:07 -07006555
6556 // error check; we're at bottom, so work is finished below
John Kesseniche01a9bc2016-03-12 20:11:22 -07006557 if (type.getVectorSize() != (int)initList->getSequence().size()) {
6558 error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", type.getCompleteString().c_str());
6559 return nullptr;
6560 }
steve-lunargfe5a3ff2016-07-30 10:36:09 -06006561 } else if (type.isScalar()) {
John Kessenich53864842016-12-30 15:59:28 -07006562 // lengthen list to be long enough
John Kessenich82460b52017-04-04 11:47:42 -06006563 lengthenList(loc, initList->getSequence(), 1, scalarInit);
John Kessenich53864842016-12-30 15:59:28 -07006564
steve-lunargfe5a3ff2016-07-30 10:36:09 -06006565 if ((int)initList->getSequence().size() != 1) {
6566 error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str());
6567 return nullptr;
6568 }
John Kessenich98ad4852016-11-27 17:39:07 -07006569 } else {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006570 error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
6571 return nullptr;
6572 }
6573
John Kessenichff132132016-07-29 18:22:22 -06006574 // Now that the subtree is processed, process this node as if the
6575 // initializer list is a set of arguments to a constructor.
John Kessenichc633f642017-04-03 21:48:37 -06006576 TIntermTyped* emulatedConstructorArguments;
John Kessenichff132132016-07-29 18:22:22 -06006577 if (initList->getSequence().size() == 1)
John Kessenichc633f642017-04-03 21:48:37 -06006578 emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped();
John Kessenichff132132016-07-29 18:22:22 -06006579 else
6580 emulatedConstructorArguments = initList;
John Kessenich98ad4852016-11-27 17:39:07 -07006581
John Kessenich64285c92017-01-05 10:45:32 -07006582 return addConstructor(loc, emulatedConstructorArguments, type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006583}
6584
John Kessenich98ad4852016-11-27 17:39:07 -07006585// Lengthen list to be long enough to cover any gap from the current list size
6586// to 'size'. If the list is longer, do nothing.
6587// The value to lengthen with is the default for short lists.
John Kessenich82460b52017-04-04 11:47:42 -06006588//
6589// By default, lists that are too short due to lack of initializers initialize to zero.
6590// Alternatively, it could be a scalar initializer for a structure. Both cases are handled,
6591// based on whether something is passed in as 'scalarInit'.
6592//
6593// 'scalarInit' must be safe to use each time this is called (no side effects replication).
6594//
6595void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit)
John Kessenich98ad4852016-11-27 17:39:07 -07006596{
John Kessenich82460b52017-04-04 11:47:42 -06006597 for (int c = (int)list.size(); c < size; ++c) {
6598 if (scalarInit == nullptr)
6599 list.push_back(intermediate.addConstantUnion(0, loc));
6600 else
6601 list.push_back(scalarInit);
6602 }
John Kessenich98ad4852016-11-27 17:39:07 -07006603}
6604
John Kesseniche01a9bc2016-03-12 20:11:22 -07006605//
6606// Test for the correctness of the parameters passed to various constructor functions
6607// and also convert them to the right data type, if allowed and required.
6608//
6609// Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
6610//
John Kessenichc633f642017-04-03 21:48:37 -06006611TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006612{
John Kessenichc633f642017-04-03 21:48:37 -06006613 if (node == nullptr)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006614 return nullptr;
6615
John Kessenich82460b52017-04-04 11:47:42 -06006616 // Handle the idiom "(struct type)<scalar value>"
6617 if (type.isStruct() && isScalarConstructor(node)) {
6618 // 'node' will almost always get used multiple times, so should not be used directly,
6619 // it would create a DAG instead of a tree, which might be okay (would
6620 // like to formalize that for constants and symbols), but if it has
6621 // side effects, they would get executed multiple times, which is not okay.
6622 if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) {
6623 TIntermAggregate* seq = intermediate.makeAggregate(loc);
6624 TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType());
6625 seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc));
6626 seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy));
6627 seq->setOp(EOpComma);
6628 seq->setType(type);
6629 return seq;
6630 } else
6631 return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node);
6632 }
John Kessenichf97f2ce2016-11-27 22:51:36 -07006633
John Kessenichc633f642017-04-03 21:48:37 -06006634 return addConstructor(loc, node, type);
6635}
6636
6637// Add a constructor, either from the grammar, or other programmatic reasons.
6638//
6639// Return nullptr if it can't be done.
6640//
6641TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
6642{
John Kesseniche01a9bc2016-03-12 20:11:22 -07006643 TIntermAggregate* aggrNode = node->getAsAggregate();
John Kessenicha26a5172016-07-28 15:29:35 -06006644 TOperator op = intermediate.mapTypeToConstructorOp(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006645
6646 // Combined texture-sampler constructors are completely semantic checked
6647 // in constructorTextureSamplerError()
6648 if (op == EOpConstructTextureSampler)
6649 return intermediate.setAggregateOperator(aggrNode, op, type, loc);
6650
6651 TTypeList::const_iterator memberTypes;
6652 if (op == EOpConstructStruct)
6653 memberTypes = type.getStruct()->begin();
6654
6655 TType elementType;
6656 if (type.isArray()) {
6657 TType dereferenced(type, 0);
6658 elementType.shallowCopy(dereferenced);
6659 } else
6660 elementType.shallowCopy(type);
6661
6662 bool singleArg;
6663 if (aggrNode) {
6664 if (aggrNode->getOp() != EOpNull || aggrNode->getSequence().size() == 1)
6665 singleArg = true;
6666 else
6667 singleArg = false;
6668 } else
6669 singleArg = true;
6670
6671 TIntermTyped *newNode;
6672 if (singleArg) {
6673 // If structure constructor or array constructor is being called
6674 // for only one parameter inside the structure, we need to call constructAggregate function once.
6675 if (type.isArray())
6676 newNode = constructAggregate(node, elementType, 1, node->getLoc());
6677 else if (op == EOpConstructStruct)
6678 newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
6679 else
John Kessenichc633f642017-04-03 21:48:37 -06006680 newNode = constructBuiltIn(type, op, node, node->getLoc(), false);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006681
6682 if (newNode && (type.isArray() || op == EOpConstructStruct))
6683 newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc);
6684
6685 return newNode;
6686 }
6687
6688 //
6689 // Handle list of arguments.
6690 //
6691 TIntermSequence &sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor
6692 // if the structure constructor contains more than one parameter, then construct
6693 // each parameter
6694
6695 int paramCount = 0; // keeps a track of the constructor parameter number being checked
6696
6697 // for each parameter to the constructor call, check to see if the right type is passed or convert them
6698 // to the right type if possible (and allowed).
6699 // for structure constructors, just check if the right type is passed, no conversion is allowed.
6700
6701 for (TIntermSequence::iterator p = sequenceVector.begin();
6702 p != sequenceVector.end(); p++, paramCount++) {
6703 if (type.isArray())
6704 newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc());
6705 else if (op == EOpConstructStruct)
6706 newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc());
6707 else
6708 newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
6709
6710 if (newNode)
6711 *p = newNode;
6712 else
6713 return nullptr;
6714 }
6715
6716 TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
6717
6718 return constructor;
6719}
6720
6721// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value
6722// for the parameter to the constructor (passed to this function). Essentially, it converts
6723// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a
6724// float, then float is converted to int.
6725//
6726// Returns nullptr for an error or the constructed node.
6727//
6728TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc, bool subset)
6729{
6730 TIntermTyped* newNode;
6731 TOperator basicOp;
6732
6733 //
6734 // First, convert types as needed.
6735 //
6736 switch (op) {
6737 case EOpConstructVec2:
6738 case EOpConstructVec3:
6739 case EOpConstructVec4:
6740 case EOpConstructMat2x2:
6741 case EOpConstructMat2x3:
6742 case EOpConstructMat2x4:
6743 case EOpConstructMat3x2:
6744 case EOpConstructMat3x3:
6745 case EOpConstructMat3x4:
6746 case EOpConstructMat4x2:
6747 case EOpConstructMat4x3:
6748 case EOpConstructMat4x4:
6749 case EOpConstructFloat:
6750 basicOp = EOpConstructFloat;
6751 break;
6752
6753 case EOpConstructDVec2:
6754 case EOpConstructDVec3:
6755 case EOpConstructDVec4:
6756 case EOpConstructDMat2x2:
6757 case EOpConstructDMat2x3:
6758 case EOpConstructDMat2x4:
6759 case EOpConstructDMat3x2:
6760 case EOpConstructDMat3x3:
6761 case EOpConstructDMat3x4:
6762 case EOpConstructDMat4x2:
6763 case EOpConstructDMat4x3:
6764 case EOpConstructDMat4x4:
6765 case EOpConstructDouble:
6766 basicOp = EOpConstructDouble;
6767 break;
6768
6769 case EOpConstructIVec2:
6770 case EOpConstructIVec3:
6771 case EOpConstructIVec4:
6772 case EOpConstructInt:
6773 basicOp = EOpConstructInt;
6774 break;
6775
6776 case EOpConstructUVec2:
6777 case EOpConstructUVec3:
6778 case EOpConstructUVec4:
6779 case EOpConstructUint:
6780 basicOp = EOpConstructUint;
6781 break;
6782
6783 case EOpConstructBVec2:
6784 case EOpConstructBVec3:
6785 case EOpConstructBVec4:
6786 case EOpConstructBool:
6787 basicOp = EOpConstructBool;
6788 break;
6789
6790 default:
6791 error(loc, "unsupported construction", "", "");
6792
6793 return nullptr;
6794 }
6795 newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc());
6796 if (newNode == nullptr) {
6797 error(loc, "can't convert", "constructor", "");
6798 return nullptr;
6799 }
6800
6801 //
6802 // Now, if there still isn't an operation to do the construction, and we need one, add one.
6803 //
6804
6805 // Otherwise, skip out early.
6806 if (subset || (newNode != node && newNode->getType() == type))
6807 return newNode;
6808
6809 // setAggregateOperator will insert a new node for the constructor, as needed.
6810 return intermediate.setAggregateOperator(newNode, op, type, loc);
6811}
6812
6813// This function tests for the type of the parameters to the structure or array constructor. Raises
6814// an error message if the expected type does not match the parameter passed to the constructor.
6815//
6816// Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
6817//
6818TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc)
6819{
6820 TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
6821 if (! converted || converted->getType() != type) {
6822 error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
6823 node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
6824
6825 return nullptr;
6826 }
6827
6828 return converted;
6829}
6830
6831//
6832// Do everything needed to add an interface block.
6833//
John Kessenich3d157c52016-07-25 16:05:33 -06006834void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName, TArraySizes* arraySizes)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006835{
John Kessenich3d157c52016-07-25 16:05:33 -06006836 assert(type.getWritableStruct() != nullptr);
6837
John Kessenich65ee2302017-02-06 18:44:52 -07006838 // Clean up top-level decorations that don't belong.
6839 switch (type.getQualifier().storage) {
6840 case EvqUniform:
6841 case EvqBuffer:
6842 correctUniform(type.getQualifier());
6843 break;
6844 case EvqVaryingIn:
6845 correctInput(type.getQualifier());
6846 break;
6847 case EvqVaryingOut:
6848 correctOutput(type.getQualifier());
6849 break;
6850 default:
6851 break;
6852 }
6853
John Kessenich3d157c52016-07-25 16:05:33 -06006854 TTypeList& typeList = *type.getWritableStruct();
John Kesseniche01a9bc2016-03-12 20:11:22 -07006855 // fix and check for member storage qualifiers and types that don't belong within a block
6856 for (unsigned int member = 0; member < typeList.size(); ++member) {
6857 TType& memberType = *typeList[member].type;
6858 TQualifier& memberQualifier = memberType.getQualifier();
6859 const TSourceLoc& memberLoc = typeList[member].loc;
6860 globalQualifierFix(memberLoc, memberQualifier);
John Kessenich3d157c52016-07-25 16:05:33 -06006861 memberQualifier.storage = type.getQualifier().storage;
John Kessenich65ee2302017-02-06 18:44:52 -07006862
6863 if (memberType.isStruct()) {
6864 // clean up and pick up the right set of decorations
6865 auto it = ioTypeMap.find(memberType.getStruct());
6866 switch (type.getQualifier().storage) {
6867 case EvqUniform:
6868 case EvqBuffer:
6869 correctUniform(type.getQualifier());
6870 if (it != ioTypeMap.end() && it->second.uniform)
6871 type.setStruct(it->second.uniform);
6872 break;
6873 case EvqVaryingIn:
6874 correctInput(type.getQualifier());
6875 if (it != ioTypeMap.end() && it->second.input)
6876 type.setStruct(it->second.input);
6877 break;
6878 case EvqVaryingOut:
6879 correctOutput(type.getQualifier());
6880 if (it != ioTypeMap.end() && it->second.output)
6881 type.setStruct(it->second.output);
6882 break;
6883 default:
6884 break;
6885 }
6886 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07006887 }
6888
6889 // This might be a redeclaration of a built-in block. If so, redeclareBuiltinBlock() will
6890 // do all the rest.
John Kessenich927608b2017-01-06 12:34:14 -07006891 // if (! symbolTable.atBuiltInLevel() && builtInName(*blockName)) {
John Kessenich3d157c52016-07-25 16:05:33 -06006892 // redeclareBuiltinBlock(loc, typeList, *blockName, instanceName, arraySizes);
6893 // return;
6894 //}
John Kesseniche01a9bc2016-03-12 20:11:22 -07006895
6896 // Make default block qualification, and adjust the member qualifications
6897
6898 TQualifier defaultQualification;
John Kessenich3d157c52016-07-25 16:05:33 -06006899 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006900 case EvqUniform: defaultQualification = globalUniformDefaults; break;
6901 case EvqBuffer: defaultQualification = globalBufferDefaults; break;
6902 case EvqVaryingIn: defaultQualification = globalInputDefaults; break;
6903 case EvqVaryingOut: defaultQualification = globalOutputDefaults; break;
6904 default: defaultQualification.clear(); break;
6905 }
6906
6907 // Special case for "push_constant uniform", which has a default of std430,
6908 // contrary to normal uniform defaults, and can't have a default tracked for it.
John Kessenich3d157c52016-07-25 16:05:33 -06006909 if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking())
6910 type.getQualifier().layoutPacking = ElpStd430;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006911
6912 // fix and check for member layout qualifiers
6913
John Kessenich3d157c52016-07-25 16:05:33 -06006914 mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006915
6916 bool memberWithLocation = false;
6917 bool memberWithoutLocation = false;
6918 for (unsigned int member = 0; member < typeList.size(); ++member) {
6919 TQualifier& memberQualifier = typeList[member].type->getQualifier();
6920 const TSourceLoc& memberLoc = typeList[member].loc;
6921 if (memberQualifier.hasStream()) {
6922 if (defaultQualification.layoutStream != memberQualifier.layoutStream)
6923 error(memberLoc, "member cannot contradict block", "stream", "");
6924 }
6925
John Kessenichecba76f2017-01-06 00:34:48 -07006926 // "This includes a block's inheritance of the
6927 // current global default buffer, a block member's inheritance of the block's
6928 // buffer, and the requirement that any *xfb_buffer* declared on a block
John Kesseniche01a9bc2016-03-12 20:11:22 -07006929 // member must match the buffer inherited from the block."
6930 if (memberQualifier.hasXfbBuffer()) {
6931 if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer)
6932 error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
6933 }
6934
John Kesseniche01a9bc2016-03-12 20:11:22 -07006935 if (memberQualifier.hasLocation()) {
John Kessenich3d157c52016-07-25 16:05:33 -06006936 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006937 case EvqVaryingIn:
6938 case EvqVaryingOut:
6939 memberWithLocation = true;
6940 break;
6941 default:
6942 break;
6943 }
6944 } else
6945 memberWithoutLocation = true;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006946
6947 TQualifier newMemberQualification = defaultQualification;
John Kessenich34e7ee72016-09-16 17:10:39 -06006948 mergeQualifiers(newMemberQualification, memberQualifier);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006949 memberQualifier = newMemberQualification;
6950 }
6951
6952 // Process the members
John Kessenich3d157c52016-07-25 16:05:33 -06006953 fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation);
6954 fixBlockXfbOffsets(type.getQualifier(), typeList);
6955 fixBlockUniformOffsets(type.getQualifier(), typeList);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006956
6957 // reverse merge, so that currentBlockQualifier now has all layout information
6958 // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
John Kessenich3d157c52016-07-25 16:05:33 -06006959 mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006960
6961 //
6962 // Build and add the interface block as a new type named 'blockName'
6963 //
6964
steve-lunarg8ffc36a2016-09-21 14:19:40 -06006965 // Use the instance name as the interface name if one exists, else the block name.
6966 const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName();
6967
6968 TType blockType(&typeList, interfaceName, type.getQualifier());
John Kesseniche01a9bc2016-03-12 20:11:22 -07006969 if (arraySizes)
6970 blockType.newArraySizes(*arraySizes);
6971
John Kesseniche01a9bc2016-03-12 20:11:22 -07006972 // Add the variable, as anonymous or named instanceName.
6973 // Make an anonymous variable if no name was provided.
6974 if (! instanceName)
6975 instanceName = NewPoolTString("");
6976
6977 TVariable& variable = *new TVariable(instanceName, blockType);
6978 if (! symbolTable.insert(variable)) {
6979 if (*instanceName == "")
John Kessenich3d157c52016-07-25 16:05:33 -06006980 error(loc, "nameless block contains a member that already has a name at global scope", "" /* blockName->c_str() */, "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07006981 else
6982 error(loc, "block instance name redefinition", variable.getName().c_str(), "");
6983
6984 return;
6985 }
6986
John Kesseniche01a9bc2016-03-12 20:11:22 -07006987 // Save it in the AST for linker use.
John Kessenich02467d82017-01-19 15:41:47 -07006988 trackLinkage(variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006989}
6990
6991//
John Kessenichecba76f2017-01-06 00:34:48 -07006992// "For a block, this process applies to the entire block, or until the first member
6993// is reached that has a location layout qualifier. When a block member is declared with a location
John Kesseniche01a9bc2016-03-12 20:11:22 -07006994// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level
John Kessenichecba76f2017-01-06 00:34:48 -07006995// declaration. Subsequent members are again assigned consecutive locations, based on the newest location,
6996// 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 -07006997// declared in increasing order."
6998void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation)
6999{
John Kessenichecba76f2017-01-06 00:34:48 -07007000 // "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 -07007001 // have a location layout qualifier, or a compile-time error results."
7002 if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation)
7003 error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", "");
7004 else {
7005 if (memberWithLocation) {
7006 // remove any block-level location and make it per *every* member
7007 int nextLocation = 0; // by the rule above, initial value is not relevant
7008 if (qualifier.hasAnyLocation()) {
7009 nextLocation = qualifier.layoutLocation;
7010 qualifier.layoutLocation = TQualifier::layoutLocationEnd;
7011 if (qualifier.hasComponent()) {
7012 // "It is a compile-time error to apply the *component* qualifier to a ... block"
7013 error(loc, "cannot apply to a block", "component", "");
7014 }
7015 if (qualifier.hasIndex()) {
7016 error(loc, "cannot apply to a block", "index", "");
7017 }
7018 }
7019 for (unsigned int member = 0; member < typeList.size(); ++member) {
7020 TQualifier& memberQualifier = typeList[member].type->getQualifier();
7021 const TSourceLoc& memberLoc = typeList[member].loc;
7022 if (! memberQualifier.hasLocation()) {
7023 if (nextLocation >= (int)TQualifier::layoutLocationEnd)
7024 error(memberLoc, "location is too large", "location", "");
7025 memberQualifier.layoutLocation = nextLocation;
7026 memberQualifier.layoutComponent = 0;
7027 }
7028 nextLocation = memberQualifier.layoutLocation + intermediate.computeTypeLocationSize(*typeList[member].type);
7029 }
7030 }
7031 }
7032}
7033
7034void HlslParseContext::fixBlockXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
7035{
John Kessenichecba76f2017-01-06 00:34:48 -07007036 // "If a block is qualified with xfb_offset, all its
7037 // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any
7038 // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer
John Kesseniche01a9bc2016-03-12 20:11:22 -07007039 // offsets."
7040
7041 if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset())
7042 return;
7043
7044 int nextOffset = qualifier.layoutXfbOffset;
7045 for (unsigned int member = 0; member < typeList.size(); ++member) {
7046 TQualifier& memberQualifier = typeList[member].type->getQualifier();
7047 bool containsDouble = false;
7048 int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, containsDouble);
7049 // see if we need to auto-assign an offset to this member
7050 if (! memberQualifier.hasXfbOffset()) {
7051 // "if applied to an aggregate containing a double, the offset must also be a multiple of 8"
7052 if (containsDouble)
7053 RoundToPow2(nextOffset, 8);
7054 memberQualifier.layoutXfbOffset = nextOffset;
7055 } else
7056 nextOffset = memberQualifier.layoutXfbOffset;
7057 nextOffset += memberSize;
7058 }
7059
7060 // The above gave all block members an offset, so we can take it off the block now,
7061 // which will avoid double counting the offset usage.
7062 qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd;
7063}
7064
John Kessenichecba76f2017-01-06 00:34:48 -07007065// Calculate and save the offset of each block member, using the recursively
John Kesseniche01a9bc2016-03-12 20:11:22 -07007066// defined block offset rules and the user-provided offset and align.
7067//
John Kessenichecba76f2017-01-06 00:34:48 -07007068// Also, compute and save the total size of the block. For the block's size, arrayness
John Kesseniche01a9bc2016-03-12 20:11:22 -07007069// is not taken into account, as each element is backed by a separate buffer.
7070//
John Kessenich6dbc0a72016-09-27 19:13:05 -06007071void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList)
John Kesseniche01a9bc2016-03-12 20:11:22 -07007072{
7073 if (! qualifier.isUniformOrBuffer())
7074 return;
7075 if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430)
7076 return;
7077
7078 int offset = 0;
7079 int memberSize;
7080 for (unsigned int member = 0; member < typeList.size(); ++member) {
7081 TQualifier& memberQualifier = typeList[member].type->getQualifier();
7082 const TSourceLoc& memberLoc = typeList[member].loc;
7083
7084 // "When align is applied to an array, it effects only the start of the array, not the array's internal stride."
7085
7086 // modify just the children's view of matrix layout, if there is one for this member
7087 TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
7088 int dummyStride;
John Kessenich6dbc0a72016-09-27 19:13:05 -06007089 int memberAlignment = intermediate.getBaseAlignment(*typeList[member].type, memberSize, dummyStride,
7090 qualifier.layoutPacking == ElpStd140,
7091 subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor
7092 : qualifier.layoutMatrix == ElmRowMajor);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007093 if (memberQualifier.hasOffset()) {
John Kessenichecba76f2017-01-06 00:34:48 -07007094 // "The specified offset must be a multiple
John Kesseniche01a9bc2016-03-12 20:11:22 -07007095 // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
7096 if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment))
7097 error(memberLoc, "must be a multiple of the member's alignment", "offset", "");
7098
John Kessenichecba76f2017-01-06 00:34:48 -07007099 // "The offset qualifier forces the qualified member to start at or after the specified
7100 // integral-constant expression, which will be its byte offset from the beginning of the buffer.
7101 // "The actual offset of a member is computed as
John Kesseniche01a9bc2016-03-12 20:11:22 -07007102 // follows: If offset was declared, start with that offset, otherwise start with the next available offset."
7103 offset = std::max(offset, memberQualifier.layoutOffset);
7104 }
7105
John Kessenichecba76f2017-01-06 00:34:48 -07007106 // "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 -07007107 // (e.g., std140) base alignment for the member's type."
7108 if (memberQualifier.hasAlign())
7109 memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign);
7110
7111 // "If the resulting offset is not a multiple of the actual alignment,
John Kessenichecba76f2017-01-06 00:34:48 -07007112 // increase it to the first offset that is a multiple of
John Kesseniche01a9bc2016-03-12 20:11:22 -07007113 // the actual alignment."
7114 RoundToPow2(offset, memberAlignment);
7115 typeList[member].type->getQualifier().layoutOffset = offset;
7116 offset += memberSize;
7117 }
7118}
7119
7120// For an identifier that is already declared, add more qualification to it.
7121void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
7122{
7123 TSymbol* symbol = symbolTable.find(identifier);
7124 if (! symbol) {
7125 error(loc, "identifier not previously declared", identifier.c_str(), "");
7126 return;
7127 }
7128 if (symbol->getAsFunction()) {
7129 error(loc, "cannot re-qualify a function name", identifier.c_str(), "");
7130 return;
7131 }
7132
7133 if (qualifier.isAuxiliary() ||
7134 qualifier.isMemory() ||
7135 qualifier.isInterpolation() ||
7136 qualifier.hasLayout() ||
7137 qualifier.storage != EvqTemporary ||
7138 qualifier.precision != EpqNone) {
7139 error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), "");
7140 return;
7141 }
7142
7143 // For read-only built-ins, add a new symbol for holding the modified qualifier.
7144 // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block)
7145 if (symbol->isReadOnly())
7146 symbol = symbolTable.copyUp(symbol);
7147
7148 if (qualifier.invariant) {
7149 if (intermediate.inIoAccessed(identifier))
7150 error(loc, "cannot change qualification after use", "invariant", "");
7151 symbol->getWritableType().getQualifier().invariant = true;
John Kessenich17f07862016-05-04 12:36:14 -06007152 } else if (qualifier.noContraction) {
7153 if (intermediate.inIoAccessed(identifier))
7154 error(loc, "cannot change qualification after use", "precise", "");
7155 symbol->getWritableType().getQualifier().noContraction = true;
7156 } else if (qualifier.specConstant) {
7157 symbol->getWritableType().getQualifier().makeSpecConstant();
7158 if (qualifier.hasSpecConstantId())
7159 symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId;
John Kesseniche01a9bc2016-03-12 20:11:22 -07007160 } else
7161 warn(loc, "unknown requalification", "", "");
7162}
7163
7164void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers)
7165{
7166 for (unsigned int i = 0; i < identifiers.size(); ++i)
7167 addQualifierToExisting(loc, qualifier, *identifiers[i]);
7168}
7169
7170//
steve-lunargf49cdf42016-11-17 15:04:20 -07007171// Update the intermediate for the given input geometry
7172//
7173bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
7174{
7175 switch (geometry) {
7176 case ElgPoints: // fall through
7177 case ElgLines: // ...
7178 case ElgTriangles: // ...
7179 case ElgLinesAdjacency: // ...
7180 case ElgTrianglesAdjacency: // ...
7181 if (! intermediate.setInputPrimitive(geometry)) {
7182 error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
7183 return false;
7184 }
7185 break;
7186
7187 default:
7188 error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), "");
7189 return false;
7190 }
7191
7192 return true;
7193}
7194
7195//
7196// Update the intermediate for the given output geometry
7197//
7198bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
7199{
7200 switch (geometry) {
7201 case ElgPoints:
7202 case ElgLineStrip:
7203 case ElgTriangleStrip:
7204 if (! intermediate.setOutputPrimitive(geometry)) {
7205 error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
7206 return false;
7207 }
7208 break;
7209 default:
7210 error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), "");
7211 return false;
7212 }
7213
7214 return true;
7215}
7216
7217//
John Kesseniche01a9bc2016-03-12 20:11:22 -07007218// Updating default qualifier for the case of a declaration with just a qualifier,
7219// no type, block, or identifier.
7220//
7221void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType)
7222{
7223 if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) {
7224 assert(language == EShLangTessControl || language == EShLangGeometry);
John Kessenich7f349c72016-07-08 22:09:10 -06007225 // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices";
John Kesseniche01a9bc2016-03-12 20:11:22 -07007226 }
7227 if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) {
7228 if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations))
7229 error(loc, "cannot change previously set layout value", "invocations", "");
7230 }
7231 if (publicType.shaderQualifiers.geometry != ElgNone) {
7232 if (publicType.qualifier.storage == EvqVaryingIn) {
7233 switch (publicType.shaderQualifiers.geometry) {
7234 case ElgPoints:
7235 case ElgLines:
7236 case ElgLinesAdjacency:
7237 case ElgTriangles:
7238 case ElgTrianglesAdjacency:
7239 case ElgQuads:
7240 case ElgIsolines:
John Kesseniche01a9bc2016-03-12 20:11:22 -07007241 break;
7242 default:
7243 error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
7244 }
7245 } else if (publicType.qualifier.storage == EvqVaryingOut) {
steve-lunargf49cdf42016-11-17 15:04:20 -07007246 handleOutputGeometry(loc, publicType.shaderQualifiers.geometry);
John Kesseniche01a9bc2016-03-12 20:11:22 -07007247 } else
7248 error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), GetStorageQualifierString(publicType.qualifier.storage));
7249 }
7250 if (publicType.shaderQualifiers.spacing != EvsNone)
7251 intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing);
7252 if (publicType.shaderQualifiers.order != EvoNone)
7253 intermediate.setVertexOrder(publicType.shaderQualifiers.order);
7254 if (publicType.shaderQualifiers.pointMode)
7255 intermediate.setPointMode();
7256 for (int i = 0; i < 3; ++i) {
7257 if (publicType.shaderQualifiers.localSize[i] > 1) {
7258 int max = 0;
7259 switch (i) {
7260 case 0: max = resources.maxComputeWorkGroupSizeX; break;
7261 case 1: max = resources.maxComputeWorkGroupSizeY; break;
7262 case 2: max = resources.maxComputeWorkGroupSizeZ; break;
7263 default: break;
7264 }
7265 if (intermediate.getLocalSize(i) > (unsigned int)max)
7266 error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
7267
7268 // Fix the existing constant gl_WorkGroupSize with this new information.
7269 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
7270 workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
7271 }
7272 if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
7273 intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]);
7274 // Set the workgroup built-in variable as a specialization constant
7275 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
7276 workGroupSize->getWritableType().getQualifier().specConstant = true;
7277 }
7278 }
7279 if (publicType.shaderQualifiers.earlyFragmentTests)
7280 intermediate.setEarlyFragmentTests();
7281
7282 const TQualifier& qualifier = publicType.qualifier;
7283
7284 switch (qualifier.storage) {
7285 case EvqUniform:
7286 if (qualifier.hasMatrix())
7287 globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
7288 if (qualifier.hasPacking())
7289 globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
7290 break;
7291 case EvqBuffer:
7292 if (qualifier.hasMatrix())
7293 globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
7294 if (qualifier.hasPacking())
7295 globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
7296 break;
7297 case EvqVaryingIn:
7298 break;
7299 case EvqVaryingOut:
7300 if (qualifier.hasStream())
7301 globalOutputDefaults.layoutStream = qualifier.layoutStream;
7302 if (qualifier.hasXfbBuffer())
7303 globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer;
7304 if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) {
7305 if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride))
7306 error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer);
7307 }
7308 break;
7309 default:
7310 error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
7311 return;
7312 }
7313}
7314
7315//
7316// Take the sequence of statements that has been built up since the last case/default,
7317// put it on the list of top-level nodes for the current (inner-most) switch statement,
7318// and follow that by the case/default we are on now. (See switch topology comment on
7319// TIntermSwitch.)
7320//
7321void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
7322{
7323 TIntermSequence* switchSequence = switchSequenceStack.back();
7324
7325 if (statements) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07007326 statements->setOperator(EOpSequence);
7327 switchSequence->push_back(statements);
7328 }
7329 if (branchNode) {
7330 // check all previous cases for the same label (or both are 'default')
7331 for (unsigned int s = 0; s < switchSequence->size(); ++s) {
7332 TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
7333 if (prevBranch) {
7334 TIntermTyped* prevExpression = prevBranch->getExpression();
7335 TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
7336 if (prevExpression == nullptr && newExpression == nullptr)
7337 error(branchNode->getLoc(), "duplicate label", "default", "");
7338 else if (prevExpression != nullptr &&
7339 newExpression != nullptr &&
7340 prevExpression->getAsConstantUnion() &&
7341 newExpression->getAsConstantUnion() &&
7342 prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() ==
7343 newExpression->getAsConstantUnion()->getConstArray()[0].getIConst())
7344 error(branchNode->getLoc(), "duplicated value", "case", "");
7345 }
7346 }
7347 switchSequence->push_back(branchNode);
7348 }
7349}
7350
7351//
7352// Turn the top-level node sequence built up of wrapupSwitchSubsequence
7353// into a switch node.
7354//
7355TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements)
7356{
7357 wrapupSwitchSubsequence(lastStatements, nullptr);
7358
7359 if (expression == nullptr ||
7360 (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) ||
7361 expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector())
7362 error(loc, "condition must be a scalar integer expression", "switch", "");
7363
7364 // If there is nothing to do, drop the switch but still execute the expression
7365 TIntermSequence* switchSequence = switchSequenceStack.back();
7366 if (switchSequence->size() == 0)
7367 return expression;
7368
7369 if (lastStatements == nullptr) {
7370 // emulate a break for error recovery
7371 lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc));
7372 lastStatements->setOperator(EOpSequence);
7373 switchSequence->push_back(lastStatements);
7374 }
7375
7376 TIntermAggregate* body = new TIntermAggregate(EOpSequence);
7377 body->getSequence() = *switchSequenceStack.back();
7378 body->setLoc(loc);
7379
7380 TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
7381 switchNode->setLoc(loc);
7382
7383 return switchNode;
7384}
7385
John Kessenich37789792017-03-21 23:56:40 -06007386// Make a new symbol-table level that is made out of the members of a structure.
7387// This should be done as an anonymous struct (name is "") so that the symbol table
7388// finds the members with on explicit reference to a 'this' variable.
7389void HlslParseContext::pushThisScope(const TType& thisStruct)
7390{
7391 TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct);
7392 symbolTable.pushThis(thisVariable);
7393}
7394
John Kessenichf3d88bd2017-03-19 12:24:29 -06007395// Track levels of class/struct/namespace nesting with a prefix string using
John Kessenich54ee28f2017-03-11 14:13:00 -07007396// the type names separated by the scoping operator. E.g., two levels
7397// would look like:
7398//
7399// outer::inner
7400//
7401// The string is empty when at normal global level.
7402//
John Kessenichf3d88bd2017-03-19 12:24:29 -06007403void HlslParseContext::pushNamespace(const TString& typeName)
John Kessenich54ee28f2017-03-11 14:13:00 -07007404{
7405 // make new type prefix
7406 TString newPrefix;
7407 if (currentTypePrefix.size() > 0) {
7408 newPrefix = currentTypePrefix.back();
John Kessenichf3d88bd2017-03-19 12:24:29 -06007409 newPrefix.append(scopeMangler);
John Kessenich54ee28f2017-03-11 14:13:00 -07007410 }
7411 newPrefix.append(typeName);
7412 currentTypePrefix.push_back(newPrefix);
7413}
7414
John Kessenichf3d88bd2017-03-19 12:24:29 -06007415// Opposite of pushNamespace(), see above
7416void HlslParseContext::popNamespace()
John Kessenich54ee28f2017-03-11 14:13:00 -07007417{
7418 currentTypePrefix.pop_back();
7419}
7420
7421// Use the class/struct nesting string to create a global name for
John Kessenichf3d88bd2017-03-19 12:24:29 -06007422// a member of a class/struct.
John Kessenich4dc835c2017-03-28 23:43:10 -06007423void HlslParseContext::getFullNamespaceName(const TString*& name) const
John Kessenich54ee28f2017-03-11 14:13:00 -07007424{
John Kessenich4dc835c2017-03-28 23:43:10 -06007425 if (currentTypePrefix.size() == 0)
7426 return;
John Kessenich54ee28f2017-03-11 14:13:00 -07007427
John Kessenich4dc835c2017-03-28 23:43:10 -06007428 TString* fullName = NewPoolTString(currentTypePrefix.back().c_str());
7429 fullName->append(scopeMangler);
7430 fullName->append(*name);
7431 name = fullName;
John Kessenich54ee28f2017-03-11 14:13:00 -07007432}
7433
John Kessenichf3d88bd2017-03-19 12:24:29 -06007434// Helper function to add the namespace scope mangling syntax to a string.
7435void HlslParseContext::addScopeMangler(TString& name)
7436{
7437 name.append(scopeMangler);
7438}
7439
steve-lunargf1e0c872016-10-31 15:13:43 -06007440// Potentially rename shader entry point function
John Kessenich4dc835c2017-03-28 23:43:10 -06007441void HlslParseContext::renameShaderFunction(const TString*& name) const
steve-lunargf1e0c872016-10-31 15:13:43 -06007442{
7443 // Replace the entry point name given in the shader with the real entry point name,
7444 // if there is a substitution.
7445 if (name != nullptr && *name == sourceEntryPointName)
John Kesseniche48b8d72017-01-19 15:29:25 -07007446 name = NewPoolTString(intermediate.getEntryPointName().c_str());
steve-lunargf1e0c872016-10-31 15:13:43 -06007447}
7448
John Kessenichbf472862017-02-05 20:27:30 -07007449// Return true if this has uniform-interface like decorations.
7450bool HlslParseContext::hasUniform(const TQualifier& qualifier) const
7451{
7452 return qualifier.hasUniformLayout() ||
7453 qualifier.layoutPushConstant;
7454}
7455
7456// Potentially not the opposite of hasUniform(), as if some characteristic is
7457// ever used for more than one thing (e.g., uniform or input), hasUniform() should
7458// say it exists, but clearUniform() should leave it in place.
7459void HlslParseContext::clearUniform(TQualifier& qualifier)
7460{
7461 qualifier.clearUniformLayout();
7462 qualifier.layoutPushConstant = false;
7463}
7464
7465// Return false if builtIn by itself doesn't force this qualifier to be an input qualifier.
7466bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const
7467{
7468 switch (qualifier.builtIn) {
7469 case EbvPosition:
7470 case EbvPointSize:
7471 return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment;
7472 case EbvClipDistance:
7473 case EbvCullDistance:
7474 return language != EShLangVertex && language != EShLangCompute;
7475 case EbvFragCoord:
7476 case EbvFace:
7477 case EbvHelperInvocation:
7478 case EbvLayer:
7479 case EbvPointCoord:
7480 case EbvSampleId:
7481 case EbvSampleMask:
7482 case EbvSamplePosition:
7483 case EbvViewportIndex:
7484 return language == EShLangFragment;
7485 case EbvGlobalInvocationId:
7486 case EbvLocalInvocationIndex:
7487 case EbvLocalInvocationId:
7488 case EbvNumWorkGroups:
7489 case EbvWorkGroupId:
7490 case EbvWorkGroupSize:
7491 return language == EShLangCompute;
7492 case EbvInvocationId:
7493 return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry;
7494 case EbvPatchVertices:
7495 return language == EShLangTessControl || language == EShLangTessEvaluation;
7496 case EbvInstanceId:
7497 case EbvInstanceIndex:
7498 case EbvVertexId:
7499 case EbvVertexIndex:
7500 return language == EShLangVertex;
7501 case EbvPrimitiveId:
7502 return language == EShLangGeometry || language == EShLangFragment;
7503 case EbvTessLevelInner:
7504 case EbvTessLevelOuter:
7505 return language == EShLangTessEvaluation;
steve-lunarg7afe1342017-03-18 22:24:14 -06007506 case EbvTessCoord:
7507 return language == EShLangTessEvaluation;
John Kessenichbf472862017-02-05 20:27:30 -07007508 default:
7509 return false;
7510 }
7511}
7512
John Kessenich65ee2302017-02-06 18:44:52 -07007513// Return true if there are decorations to preserve for input-like storage.
John Kessenichbf472862017-02-05 20:27:30 -07007514bool HlslParseContext::hasInput(const TQualifier& qualifier) const
7515{
7516 if (qualifier.hasAnyLocation())
7517 return true;
7518
John Kessenich65ee2302017-02-06 18:44:52 -07007519 if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample))
7520 return true;
7521
7522 if (language == EShLangTessEvaluation && qualifier.patch)
John Kessenichbf472862017-02-05 20:27:30 -07007523 return true;
7524
7525 if (isInputBuiltIn(qualifier))
7526 return true;
7527
7528 return false;
7529}
7530
7531// Return false if builtIn by itself doesn't force this qualifier to be an output qualifier.
7532bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const
7533{
7534 switch (qualifier.builtIn) {
7535 case EbvPosition:
7536 case EbvPointSize:
7537 case EbvClipVertex:
7538 case EbvClipDistance:
7539 case EbvCullDistance:
7540 return language != EShLangFragment && language != EShLangCompute;
7541 case EbvFragDepth:
John Kessenich65ee2302017-02-06 18:44:52 -07007542 case EbvFragDepthGreater:
7543 case EbvFragDepthLesser:
John Kessenichbf472862017-02-05 20:27:30 -07007544 case EbvSampleMask:
7545 return language == EShLangFragment;
7546 case EbvLayer:
7547 case EbvViewportIndex:
7548 return language == EShLangGeometry;
7549 case EbvPrimitiveId:
7550 return language == EShLangGeometry || language == EShLangTessControl || language == EShLangTessEvaluation;
7551 case EbvTessLevelInner:
7552 case EbvTessLevelOuter:
7553 return language == EShLangTessControl;
7554 default:
7555 return false;
7556 }
7557}
7558
John Kessenich65ee2302017-02-06 18:44:52 -07007559// Return true if there are decorations to preserve for output-like storage.
John Kessenichbf472862017-02-05 20:27:30 -07007560bool HlslParseContext::hasOutput(const TQualifier& qualifier) const
7561{
7562 if (qualifier.hasAnyLocation())
7563 return true;
7564
John Kessenich65ee2302017-02-06 18:44:52 -07007565 if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb())
7566 return true;
7567
7568 if (language == EShLangTessControl && qualifier.patch)
John Kessenichbf472862017-02-05 20:27:30 -07007569 return true;
7570
7571 if (language == EShLangGeometry && qualifier.hasStream())
7572 return true;
7573
7574 if (isOutputBuiltIn(qualifier))
7575 return true;
7576
7577 return false;
7578}
7579
7580// Make the IO decorations etc. be appropriate only for an input interface.
7581void HlslParseContext::correctInput(TQualifier& qualifier)
7582{
7583 clearUniform(qualifier);
7584 if (language == EShLangVertex)
7585 qualifier.clearInterstage();
John Kessenich65ee2302017-02-06 18:44:52 -07007586 if (language != EShLangTessEvaluation)
7587 qualifier.patch = false;
7588 if (language != EShLangFragment) {
7589 qualifier.clearInterpolation();
7590 qualifier.sample = false;
7591 }
7592
John Kessenichbf472862017-02-05 20:27:30 -07007593 qualifier.clearStreamLayout();
7594 qualifier.clearXfbLayout();
7595
7596 if (! isInputBuiltIn(qualifier))
7597 qualifier.builtIn = EbvNone;
7598}
7599
7600// Make the IO decorations etc. be appropriate only for an output interface.
7601void HlslParseContext::correctOutput(TQualifier& qualifier)
7602{
7603 clearUniform(qualifier);
7604 if (language == EShLangFragment)
7605 qualifier.clearInterstage();
7606 if (language != EShLangGeometry)
7607 qualifier.clearStreamLayout();
7608 if (language == EShLangFragment)
7609 qualifier.clearXfbLayout();
John Kessenich65ee2302017-02-06 18:44:52 -07007610 if (language != EShLangTessControl)
7611 qualifier.patch = false;
John Kessenichbf472862017-02-05 20:27:30 -07007612
7613 switch (qualifier.builtIn) {
7614 case EbvFragDepthGreater:
7615 intermediate.setDepth(EldGreater);
7616 qualifier.builtIn = EbvFragDepth;
7617 break;
7618 case EbvFragDepthLesser:
7619 intermediate.setDepth(EldLess);
7620 qualifier.builtIn = EbvFragDepth;
7621 break;
7622 default:
7623 break;
7624 }
7625
7626 if (! isOutputBuiltIn(qualifier))
7627 qualifier.builtIn = EbvNone;
7628}
7629
7630// Make the IO decorations etc. be appropriate only for uniform type interfaces.
7631void HlslParseContext::correctUniform(TQualifier& qualifier)
7632{
7633 qualifier.builtIn = EbvNone;
7634 qualifier.clearInterstage();
7635 qualifier.clearInterstageLayout();
7636}
7637
7638// Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface.
7639void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier)
7640{
7641 clearUniform(qualifier);
7642 correctUniform(qualifier);
7643}
7644
steve-lunarge752f462017-03-22 18:39:25 -06007645
7646// Return a symbol for the linkage variable of the given TBuiltInVariable type
7647TIntermSymbol* HlslParseContext::findLinkageSymbol(TBuiltInVariable biType) const
7648{
7649 const auto it = builtInLinkageSymbols.find(biType);
7650 if (it == builtInLinkageSymbols.end()) // if it wasn't declared by the user, return nullptr
7651 return nullptr;
7652
7653 return intermediate.addSymbol(*it->second->getAsVariable());
7654}
7655
steve-lunarg858c9282017-01-07 08:54:10 -07007656// Add patch constant function invocation
7657void HlslParseContext::addPatchConstantInvocation()
7658{
7659 TSourceLoc loc;
7660 loc.init();
7661
7662 // If there's no patch constant function, or we're not a HS, do nothing.
7663 if (patchConstantFunctionName.empty() || language != EShLangTessControl)
7664 return;
7665
7666 if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) {
7667 error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), "");
7668 return;
7669 }
7670
7671 const TString mangledName = patchConstantFunctionName + "(";
7672
7673 // create list of PCF candidates
7674 TVector<const TFunction*> candidateList;
7675 bool builtIn;
7676 symbolTable.findFunctionNameList(mangledName, candidateList, builtIn);
7677
7678 // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not
7679 // allow any disambiguation of overloads.
7680 if (candidateList.empty()) {
7681 error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), "");
7682 return;
7683 }
7684
7685 // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions,
7686 // HLSL picks the last one in shader source order. Since that isn't yet implemented here, error
7687 // out if there is more than one candidate.
7688 if (candidateList.size() > 1) {
7689 error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), "");
7690 return;
7691 }
7692
7693 // Look for builtin variables in a function's parameter list.
7694 const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) {
7695 for (int p=0; p<function.getParamCount(); ++p) {
steve-lunarg067eb9b2017-04-01 15:34:48 -06007696 TStorageQualifier storage = function[p].type->getQualifier().storage;
7697
7698 if (storage == EvqConstReadOnly) // treated identically to input
7699 storage = EvqIn;
steve-lunarg858c9282017-01-07 08:54:10 -07007700
7701 if (function[p].declaredBuiltIn != EbvNone)
baldurk5d5db802017-03-09 17:48:59 +00007702 builtIns.insert(HlslParseContext::tInterstageIoData(function[p].declaredBuiltIn, storage));
steve-lunarg858c9282017-01-07 08:54:10 -07007703 else
baldurk5d5db802017-03-09 17:48:59 +00007704 builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage));
steve-lunarg858c9282017-01-07 08:54:10 -07007705 }
7706 };
7707
7708
7709 // If we synthesize a builtin interface variable, we must add it to the linkage.
7710 const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) {
7711 if (name == nullptr) {
7712 error(loc, "unable to locate patch function parameter name", "", "");
7713 return;
7714 } else {
7715 TVariable& variable = *new TVariable(name, type);
7716 if (! symbolTable.insert(variable)) {
7717 error(loc, "unable to declare patch constant function interface variable", name->c_str(), "");
7718 return;
7719 }
7720
7721 globalQualifierFix(loc, variable.getWritableType().getQualifier());
7722
7723 if (symbolNode != nullptr)
7724 *symbolNode = intermediate.addSymbol(variable);
7725
7726 trackLinkage(variable);
7727 }
7728 };
7729
steve-lunarg067eb9b2017-04-01 15:34:48 -06007730 const auto isOutputPatch = [this](TFunction& patchConstantFunction, int param) {
7731 const TType& type = *patchConstantFunction[param].type;
7732 const TBuiltInVariable biType = patchConstantFunction[param].declaredBuiltIn;
7733
7734 return type.isArray() && !type.isRuntimeSizedArray() && biType == EbvOutputPatch;
steve-lunarg858c9282017-01-07 08:54:10 -07007735 };
7736
7737 // We will perform these steps. Each is in a scoped block for separation: they could
7738 // become separate functions to make addPatchConstantInvocation shorter.
7739 //
7740 // 1. Union the interfaces, and create builtins for anything present in the PCF and
7741 // declared as a builtin variable that isn't present in the entry point's signature.
7742 //
7743 // 2. Synthesizes a call to the patchconstfunction using builtin variables from either main,
7744 // or the ones we created. Matching is based on builtin type. We may use synthesized
7745 // variables from (1) above.
steve-lunarg9cee73e2017-03-14 17:37:10 -06007746 //
7747 // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them.
steve-lunarg858c9282017-01-07 08:54:10 -07007748 //
7749 // 3. Create a return sequence: copy the return value (if any) from the PCF to a
7750 // (non-sanitized) output variable. In case this may involve multiple copies, such as for
7751 // an arrayed variable, a temporary copy of the PCF output is created to avoid multiple
7752 // indirections into a complex R-value coming from the call to the PCF.
steve-lunarg9cee73e2017-03-14 17:37:10 -06007753 //
7754 // 4. Create a barrier.
7755 //
7756 // 5/5B. Call the PCF inside an if test for (invocation id == 0).
steve-lunarg858c9282017-01-07 08:54:10 -07007757
7758 TFunction& patchConstantFunction = const_cast<TFunction&>(*candidateList[0]);
7759 const int pcfParamCount = patchConstantFunction.getParamCount();
7760 TIntermSymbol* invocationIdSym = findLinkageSymbol(EbvInvocationId);
7761 TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence();
7762
steve-lunarg067eb9b2017-04-01 15:34:48 -06007763 int outPatchParam = -1; // -1 means there isn't one.
steve-lunarg9cee73e2017-03-14 17:37:10 -06007764
steve-lunarg858c9282017-01-07 08:54:10 -07007765 // ================ Step 1A: Union Interfaces ================
7766 // Our patch constant function.
7767 {
7768 std::set<tInterstageIoData> pcfBuiltIns; // patch constant function builtins
7769 std::set<tInterstageIoData> epfBuiltIns; // entry point function builtins
7770
7771 assert(entryPointFunction);
7772 assert(entryPointFunctionBody);
7773
7774 findBuiltIns(patchConstantFunction, pcfBuiltIns);
7775 findBuiltIns(*entryPointFunction, epfBuiltIns);
7776
steve-lunarg858c9282017-01-07 08:54:10 -07007777 // Find the set of builtins in the PCF that are not present in the entry point.
7778 std::set<tInterstageIoData> notInEntryPoint;
7779
7780 notInEntryPoint = pcfBuiltIns;
7781
baldurk5d5db802017-03-09 17:48:59 +00007782 // std::set_difference not usable on unordered containers
7783 for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi)
7784 notInEntryPoint.erase(*bi);
steve-lunarg858c9282017-01-07 08:54:10 -07007785
7786 // Now we'll add those to the entry and to the linkage.
7787 for (int p=0; p<pcfParamCount; ++p) {
steve-lunarg858c9282017-01-07 08:54:10 -07007788 const TBuiltInVariable biType = patchConstantFunction[p].declaredBuiltIn;
steve-lunarg067eb9b2017-04-01 15:34:48 -06007789 TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage;
steve-lunarg858c9282017-01-07 08:54:10 -07007790
steve-lunarg067eb9b2017-04-01 15:34:48 -06007791 // Track whether there is an output patch param
7792 if (isOutputPatch(patchConstantFunction, p)) {
7793 if (outPatchParam >= 0) {
7794 // Presently we only support one per ctrl pt input.
7795 error(loc, "unimplemented: multiple output patches in patch constant function", "", "");
steve-lunarg9cee73e2017-03-14 17:37:10 -06007796 return;
7797 }
steve-lunarg067eb9b2017-04-01 15:34:48 -06007798 outPatchParam = p;
steve-lunarg9cee73e2017-03-14 17:37:10 -06007799 }
steve-lunarg858c9282017-01-07 08:54:10 -07007800
steve-lunarg9cee73e2017-03-14 17:37:10 -06007801 if (biType != EbvNone) {
7802 TType* paramType = patchConstantFunction[p].type->clone();
steve-lunarg9cee73e2017-03-14 17:37:10 -06007803
steve-lunarg067eb9b2017-04-01 15:34:48 -06007804 if (storage == EvqConstReadOnly) // treated identically to input
7805 storage = EvqIn;
7806
7807 // Presently, the only non-builtin we support is InputPatch, which is treated as
7808 // a pseudo-builtin.
7809 if (biType == EbvInputPatch) {
7810 builtInLinkageSymbols[biType] = inputPatch;
7811 } else if (biType == EbvOutputPatch) {
7812 // Nothing...
7813 } else {
7814 // Use the original declaration type for the linkage
7815 paramType->getQualifier().builtIn = biType;
7816
7817 if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1)
7818 addToLinkage(*paramType, patchConstantFunction[p].name, nullptr);
7819 }
steve-lunarg9cee73e2017-03-14 17:37:10 -06007820 }
steve-lunarg858c9282017-01-07 08:54:10 -07007821 }
7822
7823 // If we didn't find it because the shader made one, add our own.
7824 if (invocationIdSym == nullptr) {
7825 TType invocationIdType(EbtUint, EvqIn, 1);
7826 TString* invocationIdName = NewPoolTString("InvocationId");
7827 invocationIdType.getQualifier().builtIn = EbvInvocationId;
7828 addToLinkage(invocationIdType, invocationIdName, &invocationIdSym);
7829 }
7830
7831 assert(invocationIdSym);
7832 }
7833
7834 TIntermTyped* pcfArguments = nullptr;
steve-lunarg9cee73e2017-03-14 17:37:10 -06007835 TVariable* perCtrlPtVar = nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07007836
7837 // ================ Step 1B: Argument synthesis ================
7838 // Create pcfArguments for synthesis of patchconstantfunction invocation
7839 // TODO: handle struct or array inputs
7840 {
7841 for (int p=0; p<pcfParamCount; ++p) {
steve-lunarg9cee73e2017-03-14 17:37:10 -06007842 TIntermSymbol* inputArg = nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07007843
steve-lunarg067eb9b2017-04-01 15:34:48 -06007844 if (p == outPatchParam) {
steve-lunarg9cee73e2017-03-14 17:37:10 -06007845 if (perCtrlPtVar == nullptr) {
steve-lunarg067eb9b2017-04-01 15:34:48 -06007846 perCtrlPtVar = makeInternalVariable(*patchConstantFunction[outPatchParam].name,
7847 *patchConstantFunction[outPatchParam].type);
steve-lunarg9cee73e2017-03-14 17:37:10 -06007848
7849 perCtrlPtVar->getWritableType().getQualifier().makeTemporary();
7850 }
7851 inputArg = intermediate.addSymbol(*perCtrlPtVar, loc);
7852 } else {
7853 // find which builtin it is
7854 const TBuiltInVariable biType = patchConstantFunction[p].declaredBuiltIn;
7855
7856 inputArg = findLinkageSymbol(biType);
steve-lunarg067eb9b2017-04-01 15:34:48 -06007857
steve-lunarg9cee73e2017-03-14 17:37:10 -06007858 if (inputArg == nullptr) {
7859 error(loc, "unable to find patch constant function builtin variable", "", "");
7860 return;
7861 }
steve-lunarg858c9282017-01-07 08:54:10 -07007862 }
7863
7864 if (pcfParamCount == 1)
steve-lunarg9cee73e2017-03-14 17:37:10 -06007865 pcfArguments = inputArg;
steve-lunarg858c9282017-01-07 08:54:10 -07007866 else
steve-lunarg9cee73e2017-03-14 17:37:10 -06007867 pcfArguments = intermediate.growAggregate(pcfArguments, inputArg);
steve-lunarg858c9282017-01-07 08:54:10 -07007868 }
7869 }
7870
7871 // ================ Step 2: Synthesize call to PCF ================
steve-lunarg9cee73e2017-03-14 17:37:10 -06007872 TIntermAggregate* pcfCallSequence = nullptr;
steve-lunarg858c9282017-01-07 08:54:10 -07007873 TIntermTyped* pcfCall = nullptr;
7874
7875 {
7876 // Create a function call to the patchconstantfunction
7877 if (pcfArguments)
7878 addInputArgumentConversions(patchConstantFunction, pcfArguments);
7879
7880 // Synthetic call.
7881 pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc);
7882 pcfCall->getAsAggregate()->setUserDefined();
7883 pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName());
steve-lunarg9cee73e2017-03-14 17:37:10 -06007884 intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(),
7885 patchConstantFunction.getMangledName());
steve-lunarg858c9282017-01-07 08:54:10 -07007886
7887 if (pcfCall->getAsAggregate()) {
7888 TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList();
7889 for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) {
7890 TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage;
7891 qualifierList.push_back(qual);
7892 }
7893 pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator());
7894 }
7895 }
7896
steve-lunarg9cee73e2017-03-14 17:37:10 -06007897 // ================ Step 2B: Per Control Point synthesis ================
7898 // If there is per control point data, we must either emulate that with multiple
7899 // invocations of the entry point to build up an array, or (TODO:) use a yet
7900 // unavailable extension to look across the SIMD lanes. This is the former
7901 // as a placeholder for the latter.
steve-lunarg067eb9b2017-04-01 15:34:48 -06007902 if (outPatchParam >= 0) {
steve-lunarg9cee73e2017-03-14 17:37:10 -06007903 // We must introduce a local temp variable of the type wanted by the PCF input.
steve-lunarg067eb9b2017-04-01 15:34:48 -06007904 const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize();
steve-lunarg9cee73e2017-03-14 17:37:10 -06007905
7906 if (entryPointFunction->getType().getBasicType() == EbtVoid) {
7907 error(loc, "entry point must return a value for use with patch constant function", "", "");
7908 return;
7909 }
7910
7911 // Create calls to wrapped main to fill in the array. We will substitute fixed values
7912 // of invocation ID when calling the wrapped main.
7913
7914 // This is the type of the each member of the per ctrl point array.
7915 const TType derefType(perCtrlPtVar->getType(), 0);
7916
7917 for (int cpt = 0; cpt < arraySize; ++cpt) {
7918 // TODO: improve. substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab
7919 // for this function.
7920 const TString origName = entryPointFunction->getName().substr(1);
7921 TFunction callee(&origName, TType(EbtVoid));
7922 TIntermTyped* callingArgs = nullptr;
7923
7924 for (int i = 0; i < entryPointFunction->getParamCount(); i++) {
7925 TParameter& param = (*entryPointFunction)[i];
7926 TType& paramType = *param.type;
7927
7928 if (paramType.getQualifier().isParamOutput()) {
7929 error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", "");
7930 return;
7931 }
7932
7933 if (paramType.getQualifier().isParamInput()) {
7934 TIntermTyped* arg = nullptr;
7935 if ((*entryPointFunction)[i].declaredBuiltIn == EbvInvocationId) {
7936 // substitute invocation ID with the array element ID
7937 arg = intermediate.addConstantUnion(cpt, loc);
7938 } else {
7939 TVariable* argVar = makeInternalVariable(*param.name, *param.type);
7940 argVar->getWritableType().getQualifier().makeTemporary();
7941 arg = intermediate.addSymbol(*argVar);
7942 }
7943
7944 handleFunctionArgument(&callee, callingArgs, arg);
7945 }
7946 }
7947
7948 // Call and assign to per ctrl point variable
7949 currentCaller = intermediate.getEntryPointMangledName().c_str();
7950 TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
7951 TIntermTyped* index = intermediate.addConstantUnion(cpt, loc);
7952 TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc);
7953 TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc);
7954 element->setType(derefType);
7955 element->setLoc(loc);
7956
7957 pcfCallSequence = intermediate.growAggregate(pcfCallSequence,
7958 handleAssign(loc, EOpAssign, element, callReturn));
7959 }
7960 }
7961
steve-lunarg858c9282017-01-07 08:54:10 -07007962 // ================ Step 3: Create return Sequence ================
7963 // Return sequence: copy PCF result to a temporary, then to shader output variable.
7964 if (pcfCall->getBasicType() != EbtVoid) {
7965 const TType* retType = &patchConstantFunction.getType(); // return type from the PCF
7966 TType outType; // output type that goes with the return type.
7967 outType.shallowCopy(*retType);
7968
7969 // substitute the output type
7970 const auto newLists = ioTypeMap.find(retType->getStruct());
7971 if (newLists != ioTypeMap.end())
7972 outType.setStruct(newLists->second.output);
7973
7974 // Substitute the top level type's builtin type
7975 if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone)
7976 outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType();
7977
steve-lunarg9cee73e2017-03-14 17:37:10 -06007978 outType.getQualifier().patch = true; // make it a per-patch variable
7979
steve-lunarg858c9282017-01-07 08:54:10 -07007980 TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType);
7981 pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut;
7982
7983 if (pcfOutput->getType().containsBuiltInInterstageIO(language))
7984 split(*pcfOutput);
7985
steve-lunarg9cee73e2017-03-14 17:37:10 -06007986 assignLocations(*pcfOutput);
7987
steve-lunarg858c9282017-01-07 08:54:10 -07007988 TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc);
7989
7990 // The call to the PCF is a complex R-value: we want to store it in a temp to avoid
7991 // repeated calls to the PCF:
7992 TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType);
7993 pcfCallResult->getWritableType().getQualifier().makeTemporary();
steve-lunarg858c9282017-01-07 08:54:10 -07007994
steve-lunarg9cee73e2017-03-14 17:37:10 -06007995 TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc);
7996 TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall);
steve-lunarg858c9282017-01-07 08:54:10 -07007997 TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym, intermediate.addSymbol(*pcfCallResult, loc));
7998
steve-lunarg9cee73e2017-03-14 17:37:10 -06007999 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign);
8000 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut);
8001 } else {
8002 pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall);
steve-lunarg858c9282017-01-07 08:54:10 -07008003 }
8004
8005 // ================ Step 4: Barrier ================
8006 TIntermTyped* barrier = new TIntermAggregate(EOpBarrier);
8007 barrier->setLoc(loc);
8008 barrier->setType(TType(EbtVoid));
8009 epBodySeq.insert(epBodySeq.end(), barrier);
8010
steve-lunarg9cee73e2017-03-14 17:37:10 -06008011 // ================ Step 5: Test on invocation ID ================
steve-lunarg858c9282017-01-07 08:54:10 -07008012 TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
8013 TIntermTyped* cmp = intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool));
8014
steve-lunarg9cee73e2017-03-14 17:37:10 -06008015
8016 // ================ Step 5B: Create if statement on Invocation ID == 0 ================
8017 intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc);
8018 TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr);
steve-lunarg858c9282017-01-07 08:54:10 -07008019 invocationIdTest->setLoc(loc);
8020
8021 // add our test sequence before the return.
8022 epBodySeq.insert(epBodySeq.end(), invocationIdTest);
8023}
8024
steve-lunarga2e75312016-12-14 15:22:25 -07008025// post-processing
8026void HlslParseContext::finish()
8027{
steve-lunarg858c9282017-01-07 08:54:10 -07008028 addPatchConstantInvocation();
steve-lunarga2e75312016-12-14 15:22:25 -07008029 addInterstageIoToLinkage();
8030
8031 TParseContextBase::finish();
8032}
8033
John Kesseniche01a9bc2016-03-12 20:11:22 -07008034} // end namespace glslang