blob: b4b22f2e68c456be8f940c949139c4cb88b066dd [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>
John Kesseniche01a9bc2016-03-12 20:11:22 -070051
52namespace glslang {
53
John Kessenichd3f11222016-11-05 10:15:53 -060054HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins,
John Kessenichb901ade2016-06-16 20:59:42 -060055 int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink,
steve-lunargf1e0c872016-10-31 15:13:43 -060056 const TString sourceEntryPointName,
John Kesseniche01a9bc2016-03-12 20:11:22 -070057 bool forwardCompatible, EShMessages messages) :
John Kessenichd3f11222016-11-05 10:15:53 -060058 TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink, forwardCompatible, messages),
John Kessenicha1e2d492016-09-20 13:22:58 -060059 contextPragma(true, false),
60 loopNestingLevel(0), annotationNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0),
John Kessenich517fe7a2016-11-26 13:31:47 -070061 postEntryPointReturn(false),
John Kesseniche01a9bc2016-03-12 20:11:22 -070062 limits(resources.limits),
steve-lunarg132d3312016-12-19 15:48:01 -070063 builtInIoIndex(nullptr),
Alex Szpakowski5f316d92017-01-08 18:21:17 -040064 builtInIoBase(nullptr),
65 nextInLocation(0), nextOutLocation(0),
66 sourceEntryPointName(sourceEntryPointName)
John Kesseniche01a9bc2016-03-12 20:11:22 -070067{
John Kesseniche01a9bc2016-03-12 20:11:22 -070068 globalUniformDefaults.clear();
John Kessenich10f7fc72016-09-25 20:25:06 -060069 globalUniformDefaults.layoutMatrix = ElmRowMajor;
John Kessenichb901ade2016-06-16 20:59:42 -060070 globalUniformDefaults.layoutPacking = ElpStd140;
John Kesseniche01a9bc2016-03-12 20:11:22 -070071
72 globalBufferDefaults.clear();
John Kessenich10f7fc72016-09-25 20:25:06 -060073 globalBufferDefaults.layoutMatrix = ElmRowMajor;
John Kessenichb901ade2016-06-16 20:59:42 -060074 globalBufferDefaults.layoutPacking = ElpStd430;
John Kesseniche01a9bc2016-03-12 20:11:22 -070075
76 globalInputDefaults.clear();
77 globalOutputDefaults.clear();
78
John Kessenichecba76f2017-01-06 00:34:48 -070079 // "Shaders in the transform
John Kesseniche01a9bc2016-03-12 20:11:22 -070080 // feedback capturing mode have an initial global default of
81 // layout(xfb_buffer = 0) out;"
82 if (language == EShLangVertex ||
83 language == EShLangTessControl ||
84 language == EShLangTessEvaluation ||
85 language == EShLangGeometry)
86 globalOutputDefaults.layoutXfbBuffer = 0;
87
88 if (language == EShLangGeometry)
89 globalOutputDefaults.layoutStream = 0;
John Kessenichd4032292016-09-09 11:43:11 -060090
91 if (spvVersion.spv == 0 || spvVersion.vulkan == 0)
92 infoSink.info << "ERROR: HLSL currently only supported when requesting SPIR-V for Vulkan.\n";
John Kesseniche01a9bc2016-03-12 20:11:22 -070093}
94
95HlslParseContext::~HlslParseContext()
96{
97}
98
LoopDawg62561462016-07-22 20:46:03 -060099void HlslParseContext::initializeExtensionBehavior()
100{
101 TParseContextBase::initializeExtensionBehavior();
102
103 // HLSL allows #line by default.
104 extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable;
105}
106
John Kesseniche01a9bc2016-03-12 20:11:22 -0700107void HlslParseContext::setLimits(const TBuiltInResource& r)
108{
109 resources = r;
110 intermediate.setLimits(resources);
111}
112
113//
114// Parse an array of strings using the parser in HlslRules.
115//
116// Returns true for successful acceptance of the shader, false if any errors.
117//
118bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError)
119{
120 currentScanner = &input;
121 ppContext.setInput(input, versionWillBeError);
122
John Kesseniche01a9bc2016-03-12 20:11:22 -0700123 HlslScanContext scanContext(*this, ppContext);
124 HlslGrammar grammar(scanContext, *this);
John Kessenich7f702122016-09-15 22:49:31 -0600125 if (!grammar.parse()) {
John Kessenich219b0252016-08-23 17:51:13 -0600126 // Print a message formated such that if you click on the message it will take you right to
127 // the line through most UIs.
dankbakerafe6e9c2016-08-21 12:29:08 -0400128 const glslang::TSourceLoc& sourceLoc = input.getSourceLoc();
John Kessenich142785f2016-09-19 14:56:55 -0600129 infoSink.info << sourceLoc.name << "(" << sourceLoc.line << "): error at column " << sourceLoc.column << ", HLSL parsing failed.\n";
130 ++numErrors;
John Kessenich7f702122016-09-15 22:49:31 -0600131 return false;
dankbakerafe6e9c2016-08-21 12:29:08 -0400132 }
John Kessenich7f702122016-09-15 22:49:31 -0600133
John Kessenichd3f11222016-11-05 10:15:53 -0600134 finish();
135
John Kesseniche01a9bc2016-03-12 20:11:22 -0700136 return numErrors == 0;
137}
138
steve-lunarg07830e82016-10-10 10:00:14 -0600139//
140// Return true if this l-value node should be converted in some manner.
141// For instance: turning a load aggregate into a store in an l-value.
142//
steve-lunarg90707962016-10-07 19:35:40 -0600143bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const
144{
145 if (node == nullptr)
146 return false;
147
148 const TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
steve-lunargcd6829b2016-12-28 10:03:58 -0700149 const TIntermBinary* lhsAsBinary = node->getAsBinaryNode();
150
151 // If it's a swizzled/indexed aggregate, look at the left node instead.
152 if (lhsAsBinary != nullptr &&
153 (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect))
154 lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
steve-lunarg0de16da2016-10-08 10:54:52 -0600155
steve-lunarg90707962016-10-07 19:35:40 -0600156 if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad)
157 return true;
158
159 return false;
160}
161
steve-lunarg0de16da2016-10-08 10:54:52 -0600162//
steve-lunarg4f2da272016-10-10 15:24:57 -0600163// Return a TLayoutFormat corresponding to the given texture type.
164//
165TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType)
166{
167 const int components = txType.getVectorSize();
168
baldurkca735702016-10-28 17:57:25 +0200169 const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat {
steve-lunargcce8d482016-10-14 18:36:42 -0600170 if (intermediate.getNoStorageFormat())
171 return ElfNone;
172
steve-lunarg4f2da272016-10-10 15:24:57 -0600173 return components == 1 ? v1 :
174 components == 2 ? v2 : v4;
175 };
176
177 switch (txType.getBasicType()) {
steve-lunarg8b0227c2016-10-14 16:40:32 -0600178 case EbtFloat: return selectFormat(ElfR32f, ElfRg32f, ElfRgba32f);
179 case EbtInt: return selectFormat(ElfR32i, ElfRg32i, ElfRgba32i);
180 case EbtUint: return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui);
steve-lunarg4f2da272016-10-10 15:24:57 -0600181 default:
182 error(loc, "unknown basic type in image format", "", "");
183 return ElfNone;
184 }
185}
186
187//
steve-lunarg0de16da2016-10-08 10:54:52 -0600188// Both test and if necessary, spit out an error, to see if the node is really
189// an l-value that can be operated on this way.
190//
191// Returns true if there was an error.
192//
193bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
194{
195 if (shouldConvertLValue(node)) {
196 // if we're writing to a texture, it must be an RW form.
197
198 TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
199 TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -0700200
steve-lunarg0de16da2016-10-08 10:54:52 -0600201 if (!object->getType().getSampler().isImage()) {
202 error(loc, "operator[] on a non-RW texture must be an r-value", "", "");
203 return true;
204 }
205 }
206
207 // Let the base class check errors
208 return TParseContextBase::lValueErrorCheck(loc, op, node);
209}
steve-lunarg90707962016-10-07 19:35:40 -0600210
211//
212// This function handles l-value conversions and verifications. It uses, but is not synonymous
213// with lValueErrorCheck. That function accepts an l-value directly, while this one must be
214// given the surrounding tree - e.g, with an assignment, so we can convert the assign into a
215// series of other image operations.
216//
217// Most things are passed through unmodified, except for error checking.
John Kessenichecba76f2017-01-06 00:34:48 -0700218//
steve-lunarg90707962016-10-07 19:35:40 -0600219TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped* node)
220{
steve-lunarg07830e82016-10-10 10:00:14 -0600221 if (node == nullptr)
222 return nullptr;
223
steve-lunarg90707962016-10-07 19:35:40 -0600224 TIntermBinary* nodeAsBinary = node->getAsBinaryNode();
225 TIntermUnary* nodeAsUnary = node->getAsUnaryNode();
226 TIntermAggregate* sequence = nullptr;
227
228 TIntermTyped* lhs = nodeAsUnary ? nodeAsUnary->getOperand() :
229 nodeAsBinary ? nodeAsBinary->getLeft() :
230 nullptr;
231
steve-lunarg90707962016-10-07 19:35:40 -0600232 // Early bail out if there is no conversion to apply
233 if (!shouldConvertLValue(lhs)) {
steve-lunarg0de16da2016-10-08 10:54:52 -0600234 if (lhs != nullptr)
235 if (lValueErrorCheck(loc, op, lhs))
236 return nullptr;
steve-lunarg90707962016-10-07 19:35:40 -0600237 return node;
238 }
239
240 // *** If we get here, we're going to apply some conversion to an l-value.
241
242 // Helper to create a load.
243 const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) {
244 TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad);
245 loadOp->setLoc(loc);
246 loadOp->getSequence().push_back(object);
247 loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode()));
248 loadOp->setType(derefType);
249
250 sequence = intermediate.growAggregate(sequence,
251 intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc),
252 loc);
253 };
254
255 // Helper to create a store.
256 const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) {
257 TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore);
258 storeOp->getSequence().push_back(object);
259 storeOp->getSequence().push_back(coord);
260 storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp));
261 storeOp->setLoc(loc);
262 storeOp->setType(TType(EbtVoid));
263
264 sequence = intermediate.growAggregate(sequence, storeOp);
265 };
266
267 // Helper to create an assign.
steve-lunargb3da8a92016-10-12 12:38:12 -0600268 const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) {
steve-lunarg90707962016-10-07 19:35:40 -0600269 sequence = intermediate.growAggregate(sequence,
steve-lunargb3da8a92016-10-12 12:38:12 -0600270 intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()),
steve-lunarg90707962016-10-07 19:35:40 -0600271 loc);
272 };
273
274 // Helper to complete sequence by adding trailing variable, so we evaluate to the right value.
baldurkca735702016-10-28 17:57:25 +0200275 const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* {
steve-lunarg90707962016-10-07 19:35:40 -0600276 // Add a trailing use of the temp, so the sequence returns the proper value.
277 sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp));
278 sequence->setOperator(EOpSequence);
279 sequence->setLoc(loc);
280 sequence->setType(derefType);
281
282 return sequence;
283 };
284
285 // Helper to add unary op
steve-lunargb3da8a92016-10-12 12:38:12 -0600286 const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) {
steve-lunarg90707962016-10-07 19:35:40 -0600287 sequence = intermediate.growAggregate(sequence,
steve-lunargb3da8a92016-10-12 12:38:12 -0600288 intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc,
289 rhsTmp->getType()),
steve-lunarg90707962016-10-07 19:35:40 -0600290 loc);
291 };
292
steve-lunargcd6829b2016-12-28 10:03:58 -0700293 // Return true if swizzle or index writes all components of the given variable.
294 const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool {
295 if (swizzle == nullptr) // not a swizzle or index
296 return true;
297
298 // Track which components are being set.
299 std::array<bool, 4> compIsSet;
300 compIsSet.fill(false);
301
302 const TIntermConstantUnion* asConst = swizzle->getRight()->getAsConstantUnion();
303 const TIntermAggregate* asAggregate = swizzle->getRight()->getAsAggregate();
304
305 // This could be either a direct index, or a swizzle.
306 if (asConst) {
307 compIsSet[asConst->getConstArray()[0].getIConst()] = true;
308 } else if (asAggregate) {
309 const TIntermSequence& seq = asAggregate->getSequence();
310 for (int comp=0; comp<int(seq.size()); ++comp)
311 compIsSet[seq[comp]->getAsConstantUnion()->getConstArray()[0].getIConst()] = true;
312 } else {
313 assert(0);
314 }
315
316 // Return true if all components are being set by the index or swizzle
317 return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(),
318 [](bool isSet) { return isSet; } );
319 };
320
steve-lunarg90707962016-10-07 19:35:40 -0600321 // helper to create a temporary variable
baldurkca735702016-10-28 17:57:25 +0200322 const auto addTmpVar = [&](const char* name, const TType& derefType) -> TIntermSymbol* {
steve-lunarg90707962016-10-07 19:35:40 -0600323 TVariable* tmpVar = makeInternalVariable(name, derefType);
324 tmpVar->getWritableType().getQualifier().makeTemporary();
325 return intermediate.addSymbol(*tmpVar, loc);
326 };
steve-lunargcd6829b2016-12-28 10:03:58 -0700327
328 // Create swizzle matching input swizzle
329 const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* {
330 if (swizzle)
331 return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType());
332 else
333 return var;
334 };
335
336 TIntermBinary* lhsAsBinary = lhs->getAsBinaryNode();
steve-lunarg90707962016-10-07 19:35:40 -0600337 TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate();
steve-lunargcd6829b2016-12-28 10:03:58 -0700338 bool lhsIsSwizzle = false;
339
340 // If it's a swizzled L-value, remember the swizzle, and use the LHS.
341 if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) {
342 lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
343 lhsIsSwizzle = true;
344 }
John Kessenichecba76f2017-01-06 00:34:48 -0700345
steve-lunarg90707962016-10-07 19:35:40 -0600346 TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
347 TIntermTyped* coord = lhsAsAggregate->getSequence()[1]->getAsTyped();
348
steve-lunarg8b0227c2016-10-14 16:40:32 -0600349 const TSampler& texSampler = object->getType().getSampler();
350
steve-lunarg8b0227c2016-10-14 16:40:32 -0600351 const TType objDerefType(texSampler.type, EvqTemporary, texSampler.vectorSize);
steve-lunarg90707962016-10-07 19:35:40 -0600352
353 if (nodeAsBinary) {
354 TIntermTyped* rhs = nodeAsBinary->getRight();
355 const TOperator assignOp = nodeAsBinary->getOp();
356
357 bool isModifyOp = false;
358
359 switch (assignOp) {
360 case EOpAddAssign:
361 case EOpSubAssign:
362 case EOpMulAssign:
363 case EOpVectorTimesMatrixAssign:
364 case EOpVectorTimesScalarAssign:
365 case EOpMatrixTimesScalarAssign:
366 case EOpMatrixTimesMatrixAssign:
367 case EOpDivAssign:
368 case EOpModAssign:
369 case EOpAndAssign:
370 case EOpInclusiveOrAssign:
371 case EOpExclusiveOrAssign:
372 case EOpLeftShiftAssign:
373 case EOpRightShiftAssign:
374 isModifyOp = true;
375 // fall through...
376 case EOpAssign:
377 {
378 // Since this is an lvalue, we'll convert an image load to a sequence like this (to still provide the value):
379 // OpSequence
380 // OpImageStore(object, lhs, rhs)
381 // rhs
382 // 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
383 // instead to this:
384 // OpSequence
385 // rhsTmp = rhs
386 // OpImageStore(object, coord, rhsTmp)
387 // rhsTmp
388 // If this is a read-modify-write op, like +=, we issue:
389 // OpSequence
390 // coordtmp = load's param1
391 // rhsTmp = OpImageLoad(object, coordTmp)
John Kessenich64285c92017-01-05 10:45:32 -0700392 // rhsTmp op= rhs
steve-lunarg90707962016-10-07 19:35:40 -0600393 // OpImageStore(object, coordTmp, rhsTmp)
394 // rhsTmp
steve-lunargcd6829b2016-12-28 10:03:58 -0700395 //
396 // If the lvalue is swizzled, we apply that when writing the temp variable, like so:
397 // ...
398 // rhsTmp.some_swizzle = ...
399 // For partial writes, an error is generated.
steve-lunarg90707962016-10-07 19:35:40 -0600400
401 TIntermSymbol* rhsTmp = rhs->getAsSymbolNode();
402 TIntermTyped* coordTmp = coord;
John Kessenichecba76f2017-01-06 00:34:48 -0700403
steve-lunargcd6829b2016-12-28 10:03:58 -0700404 if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) {
steve-lunarg90707962016-10-07 19:35:40 -0600405 rhsTmp = addTmpVar("storeTemp", objDerefType);
406
steve-lunargcd6829b2016-12-28 10:03:58 -0700407 // Partial updates not yet supported
408 if (!writesAllComponents(rhsTmp, lhsAsBinary)) {
409 error(loc, "unimplemented: partial image updates", "", "");
410 }
411
steve-lunarg90707962016-10-07 19:35:40 -0600412 // Assign storeTemp = rhs
413 if (isModifyOp) {
414 // We have to make a temp var for the coordinate, to avoid evaluating it twice.
415 coordTmp = addTmpVar("coordTemp", coord->getType());
steve-lunargb3da8a92016-10-12 12:38:12 -0600416 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600417 makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
418 }
419
420 // rhsTmp op= rhs.
steve-lunargcd6829b2016-12-28 10:03:58 -0700421 makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs);
steve-lunarg90707962016-10-07 19:35:40 -0600422 }
John Kessenichecba76f2017-01-06 00:34:48 -0700423
steve-lunarg90707962016-10-07 19:35:40 -0600424 makeStore(object, coordTmp, rhsTmp); // add a store
425 return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
426 }
427
428 default:
429 break;
430 }
431 }
432
433 if (nodeAsUnary) {
434 const TOperator assignOp = nodeAsUnary->getOp();
435
436 switch (assignOp) {
437 case EOpPreIncrement:
438 case EOpPreDecrement:
439 {
440 // We turn this into:
441 // OpSequence
442 // coordtmp = load's param1
443 // rhsTmp = OpImageLoad(object, coordTmp)
444 // rhsTmp op
445 // OpImageStore(object, coordTmp, rhsTmp)
446 // rhsTmp
John Kessenichecba76f2017-01-06 00:34:48 -0700447
steve-lunarg90707962016-10-07 19:35:40 -0600448 TIntermSymbol* rhsTmp = addTmpVar("storeTemp", objDerefType);
449 TIntermTyped* coordTmp = addTmpVar("coordTemp", coord->getType());
John Kessenichecba76f2017-01-06 00:34:48 -0700450
steve-lunargb3da8a92016-10-12 12:38:12 -0600451 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600452 makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
steve-lunargb3da8a92016-10-12 12:38:12 -0600453 makeUnary(assignOp, rhsTmp); // op rhsTmp
steve-lunarg90707962016-10-07 19:35:40 -0600454 makeStore(object, coordTmp, rhsTmp); // OpImageStore(object, coordTmp, rhsTmp)
455 return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
456 }
457
458 case EOpPostIncrement:
459 case EOpPostDecrement:
460 {
461 // We turn this into:
462 // OpSequence
463 // coordtmp = load's param1
464 // rhsTmp1 = OpImageLoad(object, coordTmp)
465 // rhsTmp2 = rhsTmp1
466 // rhsTmp2 op
467 // OpImageStore(object, coordTmp, rhsTmp2)
468 // rhsTmp1 (pre-op value)
469 TIntermSymbol* rhsTmp1 = addTmpVar("storeTempPre", objDerefType);
470 TIntermSymbol* rhsTmp2 = addTmpVar("storeTempPost", objDerefType);
471 TIntermTyped* coordTmp = addTmpVar("coordTemp", coord->getType());
472
steve-lunargb3da8a92016-10-12 12:38:12 -0600473 makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
steve-lunarg90707962016-10-07 19:35:40 -0600474 makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp)
steve-lunargb3da8a92016-10-12 12:38:12 -0600475 makeBinary(EOpAssign, rhsTmp2, rhsTmp1); // rhsTmp2 = rhsTmp1
476 makeUnary(assignOp, rhsTmp2); // rhsTmp op
steve-lunarg90707962016-10-07 19:35:40 -0600477 makeStore(object, coordTmp, rhsTmp2); // OpImageStore(object, coordTmp, rhsTmp2)
478 return finishSequence(rhsTmp1, objDerefType); // return rhsTmp from sequence
steve-lunarg90707962016-10-07 19:35:40 -0600479 }
John Kessenichecba76f2017-01-06 00:34:48 -0700480
steve-lunarg90707962016-10-07 19:35:40 -0600481 default:
482 break;
483 }
484 }
485
steve-lunarg0de16da2016-10-08 10:54:52 -0600486 if (lhs)
487 if (lValueErrorCheck(loc, op, lhs))
488 return nullptr;
John Kessenichecba76f2017-01-06 00:34:48 -0700489
steve-lunarg90707962016-10-07 19:35:40 -0600490 return node;
491}
492
John Kesseniche01a9bc2016-03-12 20:11:22 -0700493void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
494{
495 if (pragmaCallback)
496 pragmaCallback(loc.line, tokens);
497
498 if (tokens.size() == 0)
499 return;
500}
501
502//
John Kessenichc142c882017-01-13 19:34:22 -0700503// Look at a '.' matrix selector string and change it into components
John Kessenich001dfa12017-01-12 16:51:18 -0700504// for a matrix. There are two types:
505//
506// _21 second row, first column (one based)
507// _m21 third row, second column (zero based)
508//
509// Returns true if there is no error.
510//
John Kessenichc142c882017-01-13 19:34:22 -0700511bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows,
512 TSwizzleSelectors<TMatrixSelector>& components)
John Kessenich001dfa12017-01-12 16:51:18 -0700513{
John Kessenich33dadd12017-01-13 20:22:00 -0700514 int startPos[MaxSwizzleSelectors];
John Kessenich001dfa12017-01-12 16:51:18 -0700515 int numComps = 0;
516 TString compString = fields;
517
518 // Find where each component starts,
519 // recording the first character position after the '_'.
520 for (size_t c = 0; c < compString.size(); ++c) {
521 if (compString[c] == '_') {
John Kessenich33dadd12017-01-13 20:22:00 -0700522 if (numComps >= MaxSwizzleSelectors) {
John Kessenich001dfa12017-01-12 16:51:18 -0700523 error(loc, "matrix component swizzle has too many components", compString.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -0700524 return false;
525 }
John Kessenich001dfa12017-01-12 16:51:18 -0700526 if (c > compString.size() - 3 ||
527 ((compString[c+1] == 'm' || compString[c+1] == 'M') && c > compString.size() - 4)) {
528 error(loc, "matrix component swizzle missing", compString.c_str(), "");
529 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700530 }
John Kessenich001dfa12017-01-12 16:51:18 -0700531 startPos[numComps++] = c + 1;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700532 }
John Kessenich001dfa12017-01-12 16:51:18 -0700533 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700534
John Kessenich001dfa12017-01-12 16:51:18 -0700535 // Process each component
536 for (int i = 0; i < numComps; ++i) {
537 int pos = startPos[i];
538 int bias = -1;
539 if (compString[pos] == 'm' || compString[pos] == 'M') {
540 bias = 0;
541 ++pos;
542 }
John Kessenichc142c882017-01-13 19:34:22 -0700543 TMatrixSelector comp;
John Kessenich001dfa12017-01-12 16:51:18 -0700544 comp.coord1 = compString[pos+0] - '0' + bias;
545 comp.coord2 = compString[pos+1] - '0' + bias;
546 if (comp.coord1 < 0 || comp.coord1 >= cols) {
547 error(loc, "matrix row component out of range", compString.c_str(), "");
548 return false;
549 }
550 if (comp.coord2 < 0 || comp.coord2 >= rows) {
551 error(loc, "matrix column component out of range", compString.c_str(), "");
552 return false;
553 }
554 components.push_back(comp);
555 }
556
557 return true;
558}
559
560// If the 'comps' express a column of a matrix,
561// return the column. Column means the first coords all match.
562//
563// Otherwise, return -1.
564//
John Kessenichc142c882017-01-13 19:34:22 -0700565int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>& selector)
John Kessenich001dfa12017-01-12 16:51:18 -0700566{
567 int col = -1;
568
569 // right number of comps?
John Kessenichc142c882017-01-13 19:34:22 -0700570 if (selector.size() != rows)
John Kessenich001dfa12017-01-12 16:51:18 -0700571 return -1;
572
573 // all comps in the same column?
574 // rows in order?
John Kessenichc142c882017-01-13 19:34:22 -0700575 col = selector[0].coord1;
John Kessenich001dfa12017-01-12 16:51:18 -0700576 for (int i = 0; i < rows; ++i) {
John Kessenichc142c882017-01-13 19:34:22 -0700577 if (col != selector[i].coord1)
John Kessenich001dfa12017-01-12 16:51:18 -0700578 return -1;
John Kessenichc142c882017-01-13 19:34:22 -0700579 if (i != selector[i].coord2)
John Kessenich001dfa12017-01-12 16:51:18 -0700580 return -1;
581 }
582
583 return col;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700584}
585
586//
John Kesseniche01a9bc2016-03-12 20:11:22 -0700587// Handle seeing a variable identifier in the grammar.
588//
John Kesseniche6e74942016-06-11 16:43:14 -0600589TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symbol, const TString* string)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700590{
John Kesseniche6e74942016-06-11 16:43:14 -0600591 if (symbol == nullptr)
592 symbol = symbolTable.find(*string);
593 if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
594 error(loc, "expected symbol, not user-defined type", string->c_str(), "");
595 return nullptr;
596 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700597
598 // Error check for requiring specific extensions present.
599 if (symbol && symbol->getNumExtensions())
600 requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str());
601
John Kesseniche01a9bc2016-03-12 20:11:22 -0700602 const TVariable* variable;
603 const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr;
John Kesseniche6e74942016-06-11 16:43:14 -0600604 TIntermTyped* node = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700605 if (anon) {
606 // It was a member of an anonymous container.
607
608 // Create a subtree for its dereference.
609 variable = anon->getAnonContainer().getAsVariable();
610 TIntermTyped* container = intermediate.addSymbol(*variable, loc);
611 TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc);
612 node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc);
613
614 node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
615 if (node->getType().hiddenMember())
616 error(loc, "member of nameless block was not redeclared", string->c_str(), "");
617 } else {
618 // Not a member of an anonymous container.
619
620 // The symbol table search was done in the lexical phase.
621 // See if it was a variable.
622 variable = symbol ? symbol->getAsVariable() : nullptr;
623 if (variable) {
624 if ((variable->getType().getBasicType() == EbtBlock ||
625 variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) {
626 error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), "");
627 variable = nullptr;
628 }
629 } else {
630 if (symbol)
631 error(loc, "variable name expected", string->c_str(), "");
632 }
633
634 // Recovery, if it wasn't found or was not a variable.
John Kessenich1e275c82016-12-14 17:02:32 -0700635 if (! variable) {
636 error(loc, "unknown variable", string->c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -0700637 variable = new TVariable(string, TType(EbtVoid));
John Kessenich1e275c82016-12-14 17:02:32 -0700638 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700639
640 if (variable->getType().getQualifier().isFrontEndConstant())
641 node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
642 else
643 node = intermediate.addSymbol(*variable, loc);
644 }
645
646 if (variable->getType().getQualifier().isIo())
647 intermediate.addIoAccessed(*string);
648
649 return node;
650}
651
652//
steve-lunarg6b43d272016-10-06 20:12:24 -0600653// Handle operator[] on any objects it applies to. Currently:
654// Textures
655// Buffers
656//
657TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
658{
659 // handle r-value operator[] on textures and images. l-values will be processed later.
660 if (base->getType().getBasicType() == EbtSampler && !base->isArray()) {
661 const TSampler& sampler = base->getType().getSampler();
662 if (sampler.isImage() || sampler.isTexture()) {
steve-lunarg07830e82016-10-10 10:00:14 -0600663 TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch);
steve-lunarg6b43d272016-10-06 20:12:24 -0600664
steve-lunarg8b0227c2016-10-14 16:40:32 -0600665 load->setType(TType(sampler.type, EvqTemporary, sampler.vectorSize));
steve-lunarg6b43d272016-10-06 20:12:24 -0600666 load->setLoc(loc);
667 load->getSequence().push_back(base);
668 load->getSequence().push_back(index);
steve-lunarg07830e82016-10-10 10:00:14 -0600669
670 // Textures need a MIP. First indirection is always to mip 0. If there's another, we'll add it
671 // later.
672 if (sampler.isTexture())
673 load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true));
674
steve-lunarg6b43d272016-10-06 20:12:24 -0600675 return load;
676 }
677 }
678
679 return nullptr;
680}
681
682//
John Kesseniche01a9bc2016-03-12 20:11:22 -0700683// Handle seeing a base[index] dereference in the grammar.
684//
685TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
686{
steve-lunarg6b43d272016-10-06 20:12:24 -0600687 TIntermTyped* result = handleBracketOperator(loc, base, index);
688
689 if (result != nullptr)
690 return result; // it was handled as an operator[]
John Kesseniche01a9bc2016-03-12 20:11:22 -0700691
steve-lunargcf43e662016-09-22 14:35:23 -0600692 bool flattened = false;
John Kesseniche01a9bc2016-03-12 20:11:22 -0700693 int indexValue = 0;
694 if (index->getQualifier().storage == EvqConst) {
695 indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
696 checkIndex(loc, base->getType(), indexValue);
697 }
698
699 variableCheck(base);
700 if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) {
701 if (base->getAsSymbolNode())
702 error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), "");
703 else
704 error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
705 } else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
706 return intermediate.foldDereference(base, indexValue, loc);
707 else {
708 // at least one of base and index is variable...
709
John Kessenich02467d82017-01-19 15:41:47 -0700710 if (base->getAsSymbolNode() && (wasFlattened(base) || shouldFlattenUniform(base->getType()))) {
steve-lunarge0b9deb2016-09-16 13:26:37 -0600711 if (index->getQualifier().storage != EvqConst)
steve-lunarg132d3312016-12-19 15:48:01 -0700712 error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), "");
steve-lunarge0b9deb2016-09-16 13:26:37 -0600713
steve-lunarga2e75312016-12-14 15:22:25 -0700714 result = flattenAccess(base, indexValue);
steve-lunargcf43e662016-09-22 14:35:23 -0600715 flattened = (result != base);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700716 } else {
steve-lunarg132d3312016-12-19 15:48:01 -0700717 splitAccessArray(loc, base, index);
718
steve-lunarge0b9deb2016-09-16 13:26:37 -0600719 if (index->getQualifier().storage == EvqConst) {
720 if (base->getType().isImplicitlySizedArray())
721 updateImplicitArraySize(loc, base, indexValue);
722 result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
723 } else {
724 result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
725 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700726 }
727 }
728
729 if (result == nullptr) {
730 // Insert dummy error-recovery result
731 result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
732 } else {
steve-lunargcf43e662016-09-22 14:35:23 -0600733 // If the array reference was flattened, it has the correct type. E.g, if it was
734 // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps.
735 // In that case, we preserve the qualifiers.
736 if (!flattened) {
737 // Insert valid dereferenced result
738 TType newType(base->getType(), 0); // dereferenced type
739 if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
740 newType.getQualifier().storage = EvqConst;
741 else
742 newType.getQualifier().storage = EvqTemporary;
743 result->setType(newType);
744 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700745 }
746
747 return result;
748}
749
John Kessenich7f349c72016-07-08 22:09:10 -0600750void HlslParseContext::checkIndex(const TSourceLoc& /*loc*/, const TType& /*type*/, int& /*index*/)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700751{
752 // HLSL todo: any rules for index fixups?
753}
754
John Kesseniche01a9bc2016-03-12 20:11:22 -0700755// Handle seeing a binary node with a math operation.
756TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right)
757{
758 TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
759 if (! result)
760 binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
761
762 return result;
763}
764
765// Handle seeing a unary node with a math operation.
766TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode)
767{
768 TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc);
769
770 if (result)
771 return result;
772 else
773 unaryOpError(loc, str, childNode->getCompleteString());
774
775 return childNode;
776}
777
778//
779// Handle seeing a base.field dereference in the grammar.
780//
781TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field)
782{
783 variableCheck(base);
784
785 //
LoopDawg4886f692016-06-29 10:58:58 -0600786 // methods can't be resolved until we later see the function-calling syntax.
John Kessenichecba76f2017-01-06 00:34:48 -0700787 // Save away the name in the AST for now. Processing is completed in
LoopDawg4886f692016-06-29 10:58:58 -0600788 // handleLengthMethod(), etc.
John Kesseniche01a9bc2016-03-12 20:11:22 -0700789 //
790 if (field == "length") {
791 return intermediate.addMethod(base, TType(EbtInt), &field, loc);
LoopDawg4886f692016-06-29 10:58:58 -0600792 } else if (field == "CalculateLevelOfDetail" ||
793 field == "CalculateLevelOfDetailUnclamped" ||
794 field == "Gather" ||
steve-lunarg7dfcf4d2016-07-31 10:37:02 -0600795 field == "GatherRed" ||
796 field == "GatherGreen" ||
797 field == "GatherBlue" ||
798 field == "GatherAlpha" ||
799 field == "GatherCmp" ||
800 field == "GatherCmpRed" ||
801 field == "GatherCmpGreen" ||
802 field == "GatherCmpBlue" ||
803 field == "GatherCmpAlpha" ||
LoopDawg4886f692016-06-29 10:58:58 -0600804 field == "GetDimensions" ||
805 field == "GetSamplePosition" ||
806 field == "Load" ||
807 field == "Sample" ||
808 field == "SampleBias" ||
809 field == "SampleCmp" ||
810 field == "SampleCmpLevelZero" ||
811 field == "SampleGrad" ||
812 field == "SampleLevel") {
813 // If it's not a method on a sampler object, we fall through in case it is a struct member.
814 if (base->getType().getBasicType() == EbtSampler) {
steve-lunarg6b43d272016-10-06 20:12:24 -0600815 const TSampler& sampler = base->getType().getSampler();
816 if (! sampler.isPureSampler()) {
817 const int vecSize = sampler.isShadow() ? 1 : 4; // TODO: handle arbitrary sample return sizes
818 return intermediate.addMethod(base, TType(sampler.type, EvqTemporary, vecSize), &field, loc);
LoopDawg4886f692016-06-29 10:58:58 -0600819 }
820 }
steve-lunargf49cdf42016-11-17 15:04:20 -0700821 } else if (field == "Append" ||
822 field == "RestartStrip") {
steve-lunarg132d3312016-12-19 15:48:01 -0700823 // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but
824 // the code is around in the shader source.
825 return intermediate.addMethod(base, TType(EbtVoid), &field, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700826 }
827
828 // It's not .length() if we get to here.
829
830 if (base->isArray()) {
831 error(loc, "cannot apply to an array:", ".", field.c_str());
832
833 return base;
834 }
835
836 // It's neither an array nor .length() if we get here,
837 // leaving swizzles and struct/block dereferences.
838
839 TIntermTyped* result = base;
840 if (base->isVector() || base->isScalar()) {
John Kessenichc142c882017-01-13 19:34:22 -0700841 TSwizzleSelectors<TVectorSelector> selectors;
842 parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700843
844 if (base->isScalar()) {
John Kessenichc142c882017-01-13 19:34:22 -0700845 if (selectors.size() == 1)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700846 return result;
847 else {
John Kessenichc142c882017-01-13 19:34:22 -0700848 TType type(base->getBasicType(), EvqTemporary, selectors.size());
John Kessenicha26a5172016-07-28 15:29:35 -0600849 return addConstructor(loc, base, type);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700850 }
851 }
John Kessenich7d01bd62016-09-02 22:21:25 -0600852 if (base->getVectorSize() == 1) {
853 TType scalarType(base->getBasicType(), EvqTemporary, 1);
John Kessenichc142c882017-01-13 19:34:22 -0700854 if (selectors.size() == 1)
John Kessenich7d01bd62016-09-02 22:21:25 -0600855 return addConstructor(loc, base, scalarType);
856 else {
John Kessenichc142c882017-01-13 19:34:22 -0700857 TType vectorType(base->getBasicType(), EvqTemporary, selectors.size());
John Kessenich7d01bd62016-09-02 22:21:25 -0600858 return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType);
859 }
860 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700861
862 if (base->getType().getQualifier().isFrontEndConstant())
John Kessenichc142c882017-01-13 19:34:22 -0700863 result = intermediate.foldSwizzle(base, selectors, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700864 else {
John Kessenichc142c882017-01-13 19:34:22 -0700865 if (selectors.size() == 1) {
866 TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700867 result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
John Kessenichf6640762016-08-01 19:44:00 -0600868 result->setType(TType(base->getBasicType(), EvqTemporary));
John Kesseniche01a9bc2016-03-12 20:11:22 -0700869 } else {
John Kessenichc142c882017-01-13 19:34:22 -0700870 TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700871 result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
John Kessenichc142c882017-01-13 19:34:22 -0700872 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, selectors.size()));
John Kesseniche01a9bc2016-03-12 20:11:22 -0700873 }
874 }
John Kessenich001dfa12017-01-12 16:51:18 -0700875 } else if (base->isMatrix()) {
John Kessenichc142c882017-01-13 19:34:22 -0700876 TSwizzleSelectors<TMatrixSelector> selectors;
877 if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors))
John Kessenich001dfa12017-01-12 16:51:18 -0700878 return result;
879
John Kessenichc142c882017-01-13 19:34:22 -0700880 if (selectors.size() == 1) {
John Kessenich001dfa12017-01-12 16:51:18 -0700881 // Representable by m[c][r]
John Kessenichfdf63472017-01-13 12:27:52 -0700882 if (base->getType().getQualifier().isFrontEndConstant()) {
John Kessenichc142c882017-01-13 19:34:22 -0700883 result = intermediate.foldDereference(base, selectors[0].coord1, loc);
884 result = intermediate.foldDereference(result, selectors[0].coord2, loc);
John Kessenich001dfa12017-01-12 16:51:18 -0700885 } else {
John Kessenichc142c882017-01-13 19:34:22 -0700886 result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(selectors[0].coord1, loc), loc);
John Kessenich001dfa12017-01-12 16:51:18 -0700887 TType dereferencedCol(base->getType(), 0);
888 result->setType(dereferencedCol);
John Kessenichc142c882017-01-13 19:34:22 -0700889 result = intermediate.addIndex(EOpIndexDirect, result, intermediate.addConstantUnion(selectors[0].coord2, loc), loc);
John Kessenich001dfa12017-01-12 16:51:18 -0700890 TType dereferenced(dereferencedCol, 0);
891 result->setType(dereferenced);
892 }
893 } else {
John Kessenichc142c882017-01-13 19:34:22 -0700894 int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors);
John Kessenich001dfa12017-01-12 16:51:18 -0700895 if (column >= 0) {
896 // Representable by m[c]
John Kessenichfdf63472017-01-13 12:27:52 -0700897 if (base->getType().getQualifier().isFrontEndConstant())
John Kessenich001dfa12017-01-12 16:51:18 -0700898 result = intermediate.foldDereference(base, column, loc);
899 else {
900 result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc), loc);
901 TType dereferenced(base->getType(), 0);
902 result->setType(dereferenced);
903 }
904 } else {
905 // general case, not a column, not a single component
John Kessenichc142c882017-01-13 19:34:22 -0700906 TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
John Kessenichfdf63472017-01-13 12:27:52 -0700907 result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc);
John Kessenichc142c882017-01-13 19:34:22 -0700908 result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, selectors.size()));
John Kessenichfdf63472017-01-13 12:27:52 -0700909 }
John Kessenich001dfa12017-01-12 16:51:18 -0700910 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700911 } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
912 const TTypeList* fields = base->getType().getStruct();
913 bool fieldFound = false;
914 int member;
915 for (member = 0; member < (int)fields->size(); ++member) {
916 if ((*fields)[member].type->getFieldName() == field) {
917 fieldFound = true;
918 break;
919 }
920 }
921 if (fieldFound) {
John Kessenich02467d82017-01-19 15:41:47 -0700922 if (base->getAsSymbolNode() && (wasFlattened(base) || shouldFlattenUniform(base->getType()))) {
steve-lunarga2e75312016-12-14 15:22:25 -0700923 result = flattenAccess(base, member);
924 } else {
925 // Update the base and member to access if this was a split structure.
steve-lunarg132d3312016-12-19 15:48:01 -0700926 result = splitAccessStruct(loc, base, member);
steve-lunarga2e75312016-12-14 15:22:25 -0700927 fields = base->getType().getStruct();
928
929 if (result == nullptr) {
930 if (base->getType().getQualifier().storage == EvqConst)
931 result = intermediate.foldDereference(base, member, loc);
932 else {
933 TIntermTyped* index = intermediate.addConstantUnion(member, loc);
934 result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc);
935 result->setType(*(*fields)[member].type);
936 }
John Kessenichcd0a78a2016-09-09 16:32:09 -0600937 }
John Kesseniche01a9bc2016-03-12 20:11:22 -0700938 }
939 } else
940 error(loc, "no such field in structure", field.c_str(), "");
941 } else
942 error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str());
943
944 return result;
945}
946
steve-lunarg132d3312016-12-19 15:48:01 -0700947// Split the type of the given node into two structs:
steve-lunarga2e75312016-12-14 15:22:25 -0700948// 1. interstage IO
949// 2. everything else
950// IO members are put into the ioStruct. The type is modified to remove them.
951void HlslParseContext::split(TIntermTyped* node)
952{
953 if (node == nullptr)
954 return;
955
956 TIntermSymbol* symNode = node->getAsSymbolNode();
957
958 if (symNode == nullptr)
959 return;
960
961 // Create a new variable:
steve-lunarg132d3312016-12-19 15:48:01 -0700962 TType& splitType = split(*symNode->getType().clone(), symNode->getName());
963
964 splitIoVars[symNode->getId()] = makeInternalVariable(symNode->getName(), splitType);
steve-lunarga2e75312016-12-14 15:22:25 -0700965}
966
steve-lunarg132d3312016-12-19 15:48:01 -0700967// Split the type of the given variable into two structs:
steve-lunarga2e75312016-12-14 15:22:25 -0700968void HlslParseContext::split(const TVariable& variable)
969{
970 const TType& type = variable.getType();
971
John Kessenich02467d82017-01-19 15:41:47 -0700972 TString name = variable.getName();
steve-lunarg132d3312016-12-19 15:48:01 -0700973
steve-lunarga2e75312016-12-14 15:22:25 -0700974 // Create a new variable:
steve-lunarg132d3312016-12-19 15:48:01 -0700975 TType& splitType = split(*type.clone(), name);
976
977 splitIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType);
steve-lunarga2e75312016-12-14 15:22:25 -0700978}
979
980// Recursive implementation of split(const TVariable& variable).
981// Returns reference to the modified type.
steve-lunarg132d3312016-12-19 15:48:01 -0700982TType& HlslParseContext::split(TType& type, TString name, const TType* outerStructType)
steve-lunarga2e75312016-12-14 15:22:25 -0700983{
steve-lunarg132d3312016-12-19 15:48:01 -0700984 const TArraySizes* arraySizes = nullptr;
985
986 // At the outer-most scope, remember the struct type so we can examine its storage class
987 // at deeper levels.
988 if (outerStructType == nullptr)
989 outerStructType = &type;
990
991 if (type.isArray())
992 arraySizes = &type.getArraySizes();
993
steve-lunarga2e75312016-12-14 15:22:25 -0700994 // We can ignore arrayness: it's uninvolved.
995 if (type.isStruct()) {
996 TTypeList* userStructure = type.getWritableStruct();
John Kessenichecba76f2017-01-06 00:34:48 -0700997
John Kessenich02467d82017-01-19 15:41:47 -0700998 // Get iterator to (now at end) set of builtin interstage IO members
steve-lunarga2e75312016-12-14 15:22:25 -0700999 const auto firstIo = std::stable_partition(userStructure->begin(), userStructure->end(),
steve-lunarg46d54282017-01-07 09:07:14 -07001000 [this](const TTypeLoc& t) {return !t.type->isBuiltInInterstageIO(language);});
steve-lunarga2e75312016-12-14 15:22:25 -07001001
steve-lunarg132d3312016-12-19 15:48:01 -07001002 // Move those to the builtin IO. However, we also propagate arrayness (just one level is handled
1003 // now) to this variable.
steve-lunarga2e75312016-12-14 15:22:25 -07001004 for (auto ioType = firstIo; ioType != userStructure->end(); ++ioType) {
1005 const TType& memberType = *ioType->type;
steve-lunarg46d54282017-01-07 09:07:14 -07001006 TVariable* ioVar = makeInternalVariable(name + (name.empty() ? "" : "_") + memberType.getFieldName(), memberType);
steve-lunarg132d3312016-12-19 15:48:01 -07001007
1008 if (arraySizes)
1009 ioVar->getWritableType().newArraySizes(*arraySizes);
1010
1011 interstageBuiltInIo[tInterstageIoData(memberType, *outerStructType)] = ioVar;
steve-lunarga2e75312016-12-14 15:22:25 -07001012
1013 // Merge qualifier from the user structure
steve-lunarg132d3312016-12-19 15:48:01 -07001014 mergeQualifiers(ioVar->getWritableType().getQualifier(), outerStructType->getQualifier());
steve-lunarga2e75312016-12-14 15:22:25 -07001015 }
1016
1017 // Erase the IO vars from the user structure.
1018 userStructure->erase(firstIo, userStructure->end());
1019
1020 // Recurse further into the members.
1021 for (unsigned int i = 0; i < userStructure->size(); ++i)
steve-lunarg132d3312016-12-19 15:48:01 -07001022 split(*(*userStructure)[i].type,
steve-lunarg46d54282017-01-07 09:07:14 -07001023 name + (name.empty() ? "" : "_") + (*userStructure)[i].type->getFieldName(),
steve-lunarg132d3312016-12-19 15:48:01 -07001024 outerStructType);
steve-lunarga2e75312016-12-14 15:22:25 -07001025 }
1026
1027 return type;
1028}
1029
steve-lunarge0b9deb2016-09-16 13:26:37 -06001030// Is this a uniform array which should be flattened?
1031bool HlslParseContext::shouldFlattenUniform(const TType& type) const
1032{
1033 const TStorageQualifier qualifier = type.getQualifier().storage;
1034
John Kessenich02467d82017-01-19 15:41:47 -07001035 return qualifier == EvqUniform &&
1036 ((type.isArray() && intermediate.getFlattenUniformArrays()) || type.isStruct()) &&
steve-lunarga2b01a02016-11-28 17:09:54 -07001037 type.containsOpaque();
steve-lunarge0b9deb2016-09-16 13:26:37 -06001038}
1039
steve-lunarga2b01a02016-11-28 17:09:54 -07001040// Top level variable flattening: construct data
steve-lunarge0b9deb2016-09-16 13:26:37 -06001041void HlslParseContext::flatten(const TSourceLoc& loc, const TVariable& variable)
1042{
1043 const TType& type = variable.getType();
1044
rdba2f0e0e2017-01-20 14:46:39 +01001045 auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(),
1046 TFlattenData(type.getQualifier().layoutBinding)));
steve-lunarga2e75312016-12-14 15:22:25 -07001047
rdba2f0e0e2017-01-20 14:46:39 +01001048 // the item is a map pair, so first->second is the TFlattenData itself.
steve-lunarga2b01a02016-11-28 17:09:54 -07001049 flatten(loc, variable, type, entry.first->second, "");
1050}
John Kessenichecba76f2017-01-06 00:34:48 -07001051
steve-lunarga2b01a02016-11-28 17:09:54 -07001052// Recursively flatten the given variable at the provided type, building the flattenData as we go.
1053//
1054// This is mutually recursive with flattenStruct and flattenArray.
1055// We are going to flatten an arbitrarily nested composite structure into a linear sequence of
1056// members, and later on, we want to turn a path through the tree structure into a final
1057// location in this linear sequence.
1058//
1059// If the tree was N-ary, that can be directly calculated. However, we are dealing with
John Kessenich64285c92017-01-05 10:45:32 -07001060// arbitrary numbers - perhaps a struct of 7 members containing an array of 3. Thus, we must
steve-lunarga2b01a02016-11-28 17:09:54 -07001061// build a data structure to allow the sequence of bracket and dot operators on arrays and
1062// structs to arrive at the proper member.
1063//
1064// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers.
1065// The leaves are the indexes into the flattened member array.
1066// Each level will have the next location for the Nth item stored sequentially, so for instance:
1067//
1068// struct { float2 a[2]; int b; float4 c[3] };
1069//
1070// This will produce the following flattened tree:
1071// Pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
1072// (3, 7, 8, 5, 6, 0, 1, 2, 11, 12, 13, 3, 4, 5}
1073//
1074// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse:
1075// (0+2) = 8 --> (8+1) = 12 --> 12 = 4
1076//
1077// so the 4th flattened member in traversal order is ours.
1078//
1079int HlslParseContext::flatten(const TSourceLoc& loc, const TVariable& variable, const TType& type,
1080 TFlattenData& flattenData, TString name)
1081{
steve-lunarga2b01a02016-11-28 17:09:54 -07001082 // If something is an arrayed struct, the array flattener will recursively call flatten()
1083 // to then flatten the struct, so this is an "if else": we don't do both.
steve-lunarge0b9deb2016-09-16 13:26:37 -06001084 if (type.isArray())
steve-lunarga2b01a02016-11-28 17:09:54 -07001085 return flattenArray(loc, variable, type, flattenData, name);
1086 else if (type.isStruct())
1087 return flattenStruct(loc, variable, type, flattenData, name);
1088 else {
1089 assert(0); // should never happen
1090 return -1;
1091 }
1092}
1093
1094// Add a single flattened member to the flattened data being tracked for the composite
1095// Returns true for the final flattening level.
John Kessenichecba76f2017-01-06 00:34:48 -07001096int HlslParseContext::addFlattenedMember(const TSourceLoc& loc,
John Kessenichabd8dca2017-02-01 18:09:17 -07001097 const TVariable& variable, const TType& type, TFlattenData& flattenData,
1098 const TString& memberName, bool track)
steve-lunarga2b01a02016-11-28 17:09:54 -07001099{
1100 if (isFinalFlattening(type)) {
1101 // This is as far as we flatten. Insert the variable.
steve-lunarga2e75312016-12-14 15:22:25 -07001102 TVariable* memberVariable = makeInternalVariable(memberName, type);
steve-lunarga2b01a02016-11-28 17:09:54 -07001103 mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier());
1104
1105 if (flattenData.nextBinding != TQualifier::layoutBindingEnd)
1106 memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++;
1107
Jamie Madill3ec327c2016-12-13 17:30:58 -05001108 flattenData.offsets.push_back(static_cast<int>(flattenData.members.size()));
steve-lunarga2b01a02016-11-28 17:09:54 -07001109 flattenData.members.push_back(memberVariable);
1110
1111 if (track)
John Kessenich02467d82017-01-19 15:41:47 -07001112 trackLinkage(*memberVariable);
steve-lunarga2b01a02016-11-28 17:09:54 -07001113
Jamie Madill3ec327c2016-12-13 17:30:58 -05001114 return static_cast<int>(flattenData.offsets.size())-1; // location of the member reference
steve-lunarga2b01a02016-11-28 17:09:54 -07001115 } else {
1116 // Further recursion required
1117 return flatten(loc, variable, type, flattenData, memberName);
1118 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06001119}
1120
John Kessenichf9115002016-09-18 23:10:22 -06001121// Figure out the mapping between an aggregate's top members and an
John Kessenichcd0a78a2016-09-09 16:32:09 -06001122// equivalent set of individual variables.
1123//
1124// Assumes shouldFlatten() or equivalent was called first.
John Kessenichecba76f2017-01-06 00:34:48 -07001125int HlslParseContext::flattenStruct(const TSourceLoc& loc, const TVariable& variable, const TType& type,
John Kessenichabd8dca2017-02-01 18:09:17 -07001126 TFlattenData& flattenData, TString name)
John Kessenichcd0a78a2016-09-09 16:32:09 -06001127{
steve-lunarga2b01a02016-11-28 17:09:54 -07001128 assert(type.isStruct());
John Kessenichcd0a78a2016-09-09 16:32:09 -06001129
steve-lunarga2b01a02016-11-28 17:09:54 -07001130 auto members = *type.getStruct();
1131
1132 // Reserve space for this tree level.
Jamie Madill3ec327c2016-12-13 17:30:58 -05001133 int start = static_cast<int>(flattenData.offsets.size());
steve-lunarga2b01a02016-11-28 17:09:54 -07001134 int pos = start;
1135 flattenData.offsets.resize(int(pos + members.size()), -1);
1136
John Kessenichcd0a78a2016-09-09 16:32:09 -06001137 for (int member = 0; member < (int)members.size(); ++member) {
steve-lunarga2b01a02016-11-28 17:09:54 -07001138 TType& dereferencedType = *members[member].type;
1139 const TString memberName = name + (name.empty() ? "" : ".") + dereferencedType.getFieldName();
1140
1141 const int mpos = addFlattenedMember(loc, variable, dereferencedType, flattenData, memberName, false);
1142 flattenData.offsets[pos++] = mpos;
John Kessenichcd0a78a2016-09-09 16:32:09 -06001143 }
1144
steve-lunarga2b01a02016-11-28 17:09:54 -07001145 return start;
John Kessenichcd0a78a2016-09-09 16:32:09 -06001146}
1147
steve-lunarge0b9deb2016-09-16 13:26:37 -06001148// Figure out mapping between an array's members and an
1149// equivalent set of individual variables.
1150//
1151// Assumes shouldFlatten() or equivalent was called first.
John Kessenichecba76f2017-01-06 00:34:48 -07001152int HlslParseContext::flattenArray(const TSourceLoc& loc, const TVariable& variable, const TType& type,
steve-lunarga2b01a02016-11-28 17:09:54 -07001153 TFlattenData& flattenData, TString name)
steve-lunarge0b9deb2016-09-16 13:26:37 -06001154{
steve-lunarge0b9deb2016-09-16 13:26:37 -06001155 assert(type.isArray());
1156
1157 if (type.isImplicitlySizedArray())
1158 error(loc, "cannot flatten implicitly sized array", variable.getName().c_str(), "");
1159
steve-lunarga2b01a02016-11-28 17:09:54 -07001160 const int size = type.getOuterArraySize();
steve-lunarge0b9deb2016-09-16 13:26:37 -06001161 const TType dereferencedType(type, 0);
steve-lunarge0b9deb2016-09-16 13:26:37 -06001162
steve-lunarga2b01a02016-11-28 17:09:54 -07001163 if (name.empty())
1164 name = variable.getName();
steve-lunarge0b9deb2016-09-16 13:26:37 -06001165
steve-lunarga2b01a02016-11-28 17:09:54 -07001166 // Reserve space for this tree level.
Jamie Madill3ec327c2016-12-13 17:30:58 -05001167 int start = static_cast<int>(flattenData.offsets.size());
steve-lunarga2b01a02016-11-28 17:09:54 -07001168 int pos = start;
1169 flattenData.offsets.resize(int(pos + size), -1);
1170
John Kessenichecba76f2017-01-06 00:34:48 -07001171 for (int element=0; element < size; ++element) {
steve-lunarge0b9deb2016-09-16 13:26:37 -06001172 char elementNumBuf[20]; // sufficient for MAXINT
1173 snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element);
steve-lunarga2b01a02016-11-28 17:09:54 -07001174 const int mpos = addFlattenedMember(loc, variable, dereferencedType, flattenData,
1175 name + elementNumBuf, true);
steve-lunarge0b9deb2016-09-16 13:26:37 -06001176
steve-lunarga2b01a02016-11-28 17:09:54 -07001177 flattenData.offsets[pos++] = mpos;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001178 }
John Kessenichd3f11222016-11-05 10:15:53 -06001179
steve-lunarga2b01a02016-11-28 17:09:54 -07001180 return start;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001181}
1182
steve-lunarga2b01a02016-11-28 17:09:54 -07001183// Return true if we have flattened this node.
1184bool HlslParseContext::wasFlattened(const TIntermTyped* node) const
1185{
steve-lunarga2e75312016-12-14 15:22:25 -07001186 return node != nullptr && node->getAsSymbolNode() != nullptr &&
steve-lunarga2b01a02016-11-28 17:09:54 -07001187 wasFlattened(node->getAsSymbolNode()->getId());
1188}
1189
steve-lunarga2e75312016-12-14 15:22:25 -07001190// Return true if we have split this structure
1191bool HlslParseContext::wasSplit(const TIntermTyped* node) const
1192{
1193 return node != nullptr && node->getAsSymbolNode() != nullptr &&
1194 wasSplit(node->getAsSymbolNode()->getId());
1195}
1196
steve-lunarge0b9deb2016-09-16 13:26:37 -06001197// Turn an access into an aggregate that was flattened to instead be
1198// an access to the individual variable the member was flattened to.
John Kessenichcd0a78a2016-09-09 16:32:09 -06001199// Assumes shouldFlatten() or equivalent was called first.
steve-lunarga2e75312016-12-14 15:22:25 -07001200TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
John Kessenichcd0a78a2016-09-09 16:32:09 -06001201{
steve-lunarga2b01a02016-11-28 17:09:54 -07001202 const TType dereferencedType(base->getType(), member); // dereferenced type
1203
John Kessenichcd0a78a2016-09-09 16:32:09 -06001204 const TIntermSymbol& symbolNode = *base->getAsSymbolNode();
1205
steve-lunarga2b01a02016-11-28 17:09:54 -07001206 const auto flattenData = flattenMap.find(symbolNode.getId());
1207
1208 if (flattenData == flattenMap.end())
John Kessenichcd0a78a2016-09-09 16:32:09 -06001209 return base;
1210
steve-lunarga2b01a02016-11-28 17:09:54 -07001211 // Calculate new cumulative offset from the packed tree
1212 flattenOffset.back() = flattenData->second.offsets[flattenOffset.back() + member];
1213
1214 if (isFinalFlattening(dereferencedType)) {
1215 // Finished flattening: create symbol for variable
1216 member = flattenData->second.offsets[flattenOffset.back()];
1217 const TVariable* memberVariable = flattenData->second.members[member];
1218 return intermediate.addSymbol(*memberVariable);
1219 } else {
1220 // If this is not the final flattening, accumulate the position and return
1221 // an object of the partially dereferenced type.
1222 return new TIntermSymbol(symbolNode.getId(), "flattenShadow", dereferencedType);
1223 }
John Kessenichcd0a78a2016-09-09 16:32:09 -06001224}
1225
steve-lunarga2e75312016-12-14 15:22:25 -07001226// Find and return the split IO TVariable for id, or nullptr if none.
1227TVariable* HlslParseContext::getSplitIoVar(int id) const
1228{
1229 const auto splitIoVar = splitIoVars.find(id);
1230
1231 if (splitIoVar == splitIoVars.end())
1232 return nullptr;
1233
1234 return splitIoVar->second;
1235}
1236
1237// Find and return the split IO TVariable for variable, or nullptr if none.
1238TVariable* HlslParseContext::getSplitIoVar(const TVariable* var) const
1239{
1240 if (var == nullptr)
1241 return nullptr;
1242
1243 return getSplitIoVar(var->getUniqueId());
1244}
1245
1246// Find and return the split IO TVariable for symbol in this node, or nullptr if none.
1247TVariable* HlslParseContext::getSplitIoVar(const TIntermTyped* node) const
1248{
1249 if (node == nullptr)
1250 return nullptr;
1251
1252 const TIntermSymbol* symbolNode = node->getAsSymbolNode();
1253
1254 if (symbolNode == nullptr)
1255 return nullptr;
1256
1257 return getSplitIoVar(symbolNode->getId());
1258}
1259
steve-lunarg132d3312016-12-19 15:48:01 -07001260// Remember the index used to dereference into this structure, in case it has to be moved to a
1261// split-off builtin IO member.
1262void HlslParseContext::splitAccessArray(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
1263{
1264 const TVariable* splitIoVar = getSplitIoVar(base);
steve-lunarga2e75312016-12-14 15:22:25 -07001265
steve-lunarg132d3312016-12-19 15:48:01 -07001266 // Not a split structure
1267 if (splitIoVar == nullptr)
1268 return;
1269
1270 if (builtInIoBase) {
1271 error(loc, "only one array dimension supported for builtIn IO variable", "", "");
1272 return;
1273 }
1274
1275 builtInIoBase = base;
1276 builtInIoIndex = index;
1277}
1278
steve-lunarg132d3312016-12-19 15:48:01 -07001279// Turn an access into an struct that was split to instead be an
1280// access to either the modified structure, or a direct reference to
1281// one of the split member variables.
1282TIntermTyped* HlslParseContext::splitAccessStruct(const TSourceLoc& loc, TIntermTyped*& base, int& member)
steve-lunarga2e75312016-12-14 15:22:25 -07001283{
1284 // nothing to do
steve-lunarg132d3312016-12-19 15:48:01 -07001285 if (base == nullptr)
steve-lunarga2e75312016-12-14 15:22:25 -07001286 return nullptr;
1287
steve-lunarg132d3312016-12-19 15:48:01 -07001288 // We have a pending bracket reference to an outer struct that we may want to move to an inner member.
1289 if (builtInIoBase)
1290 base = builtInIoBase;
1291
steve-lunarga2e75312016-12-14 15:22:25 -07001292 const TVariable* splitIoVar = getSplitIoVar(base);
1293
1294 if (splitIoVar == nullptr)
1295 return nullptr;
1296
1297 const TTypeList& members = *base->getType().getStruct();
1298
steve-lunarg132d3312016-12-19 15:48:01 -07001299 const TType& memberType = *members[member].type;
1300
steve-lunarg46d54282017-01-07 09:07:14 -07001301 if (memberType.isBuiltInInterstageIO(language)) {
steve-lunarg132d3312016-12-19 15:48:01 -07001302 // It's one of the interstage IO variables we split off.
1303 TIntermTyped* builtIn = intermediate.addSymbol(*interstageBuiltInIo[tInterstageIoData(memberType, base->getType())], loc);
1304
1305 // If there's an array reference to an outer split struct, we re-apply it here.
1306 if (builtInIoIndex != nullptr) {
1307 if (builtInIoIndex->getQualifier().storage == EvqConst)
1308 builtIn = intermediate.addIndex(EOpIndexDirect, builtIn, builtInIoIndex, loc);
1309 else
1310 builtIn = intermediate.addIndex(EOpIndexIndirect, builtIn, builtInIoIndex, loc);
1311
1312 builtIn->setType(memberType);
1313
1314 builtInIoIndex = nullptr;
1315 builtInIoBase = nullptr;
1316 }
1317
1318 return builtIn;
steve-lunarga2e75312016-12-14 15:22:25 -07001319 } else {
1320 // It's not an IO variable. Find the equivalent index into the new variable.
1321 base = intermediate.addSymbol(*splitIoVar, loc);
1322
1323 int newMember = 0;
1324 for (int m=0; m<member; ++m)
steve-lunarg46d54282017-01-07 09:07:14 -07001325 if (!members[m].type->isBuiltInInterstageIO(language))
steve-lunarga2e75312016-12-14 15:22:25 -07001326 ++newMember;
1327
1328 member = newMember;
1329
1330 return nullptr;
1331 }
1332}
1333
John Kessenich7dc630f2016-09-16 01:44:43 -06001334// Variables that correspond to the user-interface in and out of a stage
1335// (not the built-in interface) are assigned locations and
1336// registered as a linkage node (part of the stage's external interface).
1337//
1338// Assumes it is called in the order in which locations should be assigned.
1339void HlslParseContext::assignLocations(TVariable& variable)
1340{
1341 const auto assignLocation = [&](TVariable& variable) {
1342 const TQualifier& qualifier = variable.getType().getQualifier();
1343 if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
1344 if (qualifier.builtIn == EbvNone) {
1345 if (qualifier.storage == EvqVaryingIn) {
1346 variable.getWritableType().getQualifier().layoutLocation = nextInLocation;
1347 nextInLocation += intermediate.computeTypeLocationSize(variable.getType());
1348 } else {
1349 variable.getWritableType().getQualifier().layoutLocation = nextOutLocation;
1350 nextOutLocation += intermediate.computeTypeLocationSize(variable.getType());
1351 }
1352 }
John Kessenichd3f11222016-11-05 10:15:53 -06001353 trackLinkage(variable);
John Kessenich7dc630f2016-09-16 01:44:43 -06001354 }
1355 };
1356
steve-lunarga2b01a02016-11-28 17:09:54 -07001357 if (wasFlattened(variable.getUniqueId())) {
1358 auto& memberList = flattenMap[variable.getUniqueId()].members;
John Kessenich7dc630f2016-09-16 01:44:43 -06001359 for (auto member = memberList.begin(); member != memberList.end(); ++member)
1360 assignLocation(**member);
steve-lunarga2e75312016-12-14 15:22:25 -07001361 } else if (wasSplit(variable.getUniqueId())) {
steve-lunarg132d3312016-12-19 15:48:01 -07001362 TVariable* splitIoVar = getSplitIoVar(&variable);
1363 const TTypeList* structure = splitIoVar->getType().getStruct();
1364 // Struct splitting can produce empty structures if the only members of the
1365 // struct were builtin interstage IO types. Only assign locations if it
1366 // isn't a struct, or is a non-empty struct.
1367 if (structure == nullptr || !structure->empty())
1368 assignLocation(*splitIoVar);
steve-lunarga2e75312016-12-14 15:22:25 -07001369 } else {
John Kessenich7dc630f2016-09-16 01:44:43 -06001370 assignLocation(variable);
steve-lunarga2e75312016-12-14 15:22:25 -07001371 }
John Kessenich7dc630f2016-09-16 01:44:43 -06001372}
1373
John Kesseniche01a9bc2016-03-12 20:11:22 -07001374//
1375// Handle seeing a function declarator in the grammar. This is the precursor
1376// to recognizing a function prototype or function definition.
1377//
John Kessenicha3051662016-09-02 19:13:36 -06001378TFunction& HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype)
John Kesseniche01a9bc2016-03-12 20:11:22 -07001379{
1380 //
1381 // Multiple declarations of the same function name are allowed.
1382 //
1383 // If this is a definition, the definition production code will check for redefinitions
1384 // (we don't know at this point if it's a definition or not).
1385 //
John Kesseniche01a9bc2016-03-12 20:11:22 -07001386 bool builtIn;
1387 TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
1388 const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
1389
1390 if (prototype) {
1391 // All built-in functions are defined, even though they don't have a body.
1392 // Count their prototype as a definition instead.
1393 if (symbolTable.atBuiltInLevel())
1394 function.setDefined();
1395 else {
1396 if (prevDec && ! builtIn)
1397 symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const
1398 function.setPrototyped();
1399 }
1400 }
1401
1402 // This insert won't actually insert it if it's a duplicate signature, but it will still check for
1403 // other forms of name collisions.
1404 if (! symbolTable.insert(function))
1405 error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
1406
1407 //
1408 // If this is a redeclaration, it could also be a definition,
1409 // in which case, we need to use the parameter names from this one, and not the one that's
1410 // being redeclared. So, pass back this declaration, not the one in the symbol table.
1411 //
John Kessenicha3051662016-09-02 19:13:36 -06001412 return function;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001413}
1414
steve-lunarga2e75312016-12-14 15:22:25 -07001415// Add interstage IO variables to the linkage in canonical order.
1416void HlslParseContext::addInterstageIoToLinkage()
1417{
steve-lunarg46d54282017-01-07 09:07:14 -07001418 TSourceLoc loc;
1419 loc.init();
1420
steve-lunarg132d3312016-12-19 15:48:01 -07001421 std::vector<tInterstageIoData> io;
1422 io.reserve(interstageBuiltInIo.size());
steve-lunarga2e75312016-12-14 15:22:25 -07001423
steve-lunarg132d3312016-12-19 15:48:01 -07001424 for (auto ioVar = interstageBuiltInIo.begin(); ioVar != interstageBuiltInIo.end(); ++ioVar)
1425 io.push_back(ioVar->first);
steve-lunarga2e75312016-12-14 15:22:25 -07001426
steve-lunarg132d3312016-12-19 15:48:01 -07001427 // Our canonical order is the TBuiltInVariable numeric order.
1428 std::sort(io.begin(), io.end());
steve-lunarga2e75312016-12-14 15:22:25 -07001429
steve-lunarg46d54282017-01-07 09:07:14 -07001430 // We have to (potentially) track two IO blocks, one in, one out. E.g, a GS may have a
1431 // PerVertex block in both directions, possibly with different members.
1432 static const TStorageQualifier ioType[2] = { EvqVaryingIn, EvqVaryingOut };
1433 static const char* blockName[2] = { "PerVertex_in", "PerVertex_out" };
1434
1435 TTypeList* ioBlockTypes[2] = { nullptr, nullptr };
1436 TArraySizes* ioBlockArray[2] = { nullptr, nullptr };
1437
1438 for (int idx = 0; idx < int(io.size()); ++idx) {
1439 TVariable* var = interstageBuiltInIo[io[idx]];
1440
1441 // Add the loose interstage IO to the linkage
1442 if (var->getType().isLooseAndBuiltIn(language))
John Kessenich02467d82017-01-19 15:41:47 -07001443 trackLinkage(*var);
steve-lunarg46d54282017-01-07 09:07:14 -07001444
1445 // Add the PerVertex interstage IO to the IO block
1446 if (var->getType().isPerVertexAndBuiltIn(language)) {
1447 int blockId = 0;
1448 switch (var->getType().getQualifier().storage) {
1449 case EvqVaryingIn: blockId = 0; break;
1450 case EvqVaryingOut: blockId = 1; break;
1451 default: assert(0 && "Invalid storage qualifier");
1452 }
1453
1454 // Lazy creation of type list only if we end up needing it.
1455 if (ioBlockTypes[blockId] == nullptr)
1456 ioBlockTypes[blockId] = new TTypeList();
1457
1458 TTypeLoc member = { new TType(EbtVoid), loc };
1459 member.type->shallowCopy(var->getType());
1460 member.type->setFieldName(var->getName());
1461
1462 // We may have collected these from different parts of different structures. If their
1463 // array dimensions are not the same, we don't know what to do, so issue an error.
1464 if (member.type->isArray()) {
1465 if (ioBlockArray[blockId] == nullptr) {
1466 ioBlockArray[blockId] = &member.type->getArraySizes();
1467 } else {
1468 if (*ioBlockArray[blockId] != member.type->getArraySizes())
1469 error(loc, "PerVertex block array dimension mismatch", "", "");
1470 }
1471 member.type->clearArraySizes();
1472 }
1473
1474 ioBlockTypes[blockId]->push_back(member);
1475 }
1476 }
1477
1478 // If there were PerVertex items, add the block to the linkage. Handle in and out separately.
1479 for (int blockId = 0; blockId <= 1; ++blockId) {
1480 if (ioBlockTypes[blockId] != nullptr) {
1481 const TString* instanceName = NewPoolTString(blockName[blockId]);
1482 TQualifier blockQualifier;
1483
1484 blockQualifier.clear();
1485 blockQualifier.storage = ioType[blockId];
1486
1487 TType blockType(ioBlockTypes[blockId], *instanceName, blockQualifier);
1488
1489 if (ioBlockArray[blockId] != nullptr)
1490 blockType.newArraySizes(*ioBlockArray[blockId]);
1491
1492 TVariable* ioBlock = new TVariable(instanceName, blockType);
1493 if (!symbolTable.insert(*ioBlock))
1494 error(loc, "block instance name redefinition", ioBlock->getName().c_str(), "");
1495 else
John Kessenich02467d82017-01-19 15:41:47 -07001496 trackLinkage(*ioBlock);
steve-lunarg46d54282017-01-07 09:07:14 -07001497 }
1498 }
steve-lunarga2e75312016-12-14 15:22:25 -07001499}
1500
John Kesseniche01a9bc2016-03-12 20:11:22 -07001501//
John Kessenichecba76f2017-01-06 00:34:48 -07001502// Handle seeing the function prototype in front of a function definition in the grammar.
John Kesseniche01a9bc2016-03-12 20:11:22 -07001503// The body is handled after this function returns.
1504//
John Kessenichecba76f2017-01-06 00:34:48 -07001505TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
John Kessenich02467d82017-01-19 15:41:47 -07001506 const TAttributeMap& attributes, TIntermNode*& entryPointTree)
John Kesseniche01a9bc2016-03-12 20:11:22 -07001507{
1508 currentCaller = function.getMangledName();
1509 TSymbol* symbol = symbolTable.find(function.getMangledName());
1510 TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
1511
1512 if (! prevDec)
1513 error(loc, "can't find function", function.getName().c_str(), "");
1514 // Note: 'prevDec' could be 'function' if this is the first time we've seen function
1515 // as it would have just been put in the symbol table. Otherwise, we're looking up
1516 // an earlier occurrence.
1517
1518 if (prevDec && prevDec->isDefined()) {
1519 // Then this function already has a body.
1520 error(loc, "function already has a body", function.getName().c_str(), "");
1521 }
1522 if (prevDec && ! prevDec->isDefined()) {
1523 prevDec->setDefined();
1524
1525 // Remember the return type for later checking for RETURN statements.
1526 currentFunctionType = &(prevDec->getType());
1527 } else
1528 currentFunctionType = new TType(EbtVoid);
1529 functionReturnsValue = false;
1530
John Kessenich94dfb7a2017-01-18 16:45:02 -07001531 // Entry points need different I/O and other handling, transform it so the
1532 // rest of this function doesn't care.
John Kessenich02467d82017-01-19 15:41:47 -07001533 entryPointTree = transformEntryPoint(loc, function, attributes);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001534
John Kessenich6dbc0a72016-09-27 19:13:05 -06001535 // Insert the $Global constant buffer.
1536 // TODO: this design fails if new members are declared between function definitions.
1537 if (! insertGlobalUniformBlock())
1538 error(loc, "failed to insert the global constant buffer", "uniform", "");
1539
John Kesseniche01a9bc2016-03-12 20:11:22 -07001540 //
1541 // New symbol table scope for body of function plus its arguments
1542 //
John Kessenich077e0522016-06-09 02:02:17 -06001543 pushScope();
John Kesseniche01a9bc2016-03-12 20:11:22 -07001544
1545 //
1546 // Insert parameters into the symbol table.
1547 // If the parameter has no name, it's not an error, just don't insert it
1548 // (could be used for unused args).
1549 //
John Kessenich7dc630f2016-09-16 01:44:43 -06001550 // Also, accumulate the list of parameters into the AST, so lower level code
John Kesseniche01a9bc2016-03-12 20:11:22 -07001551 // knows where to find parameters.
1552 //
1553 TIntermAggregate* paramNodes = new TIntermAggregate;
1554 for (int i = 0; i < function.getParamCount(); i++) {
1555 TParameter& param = function[i];
1556 if (param.name != nullptr) {
John Kessenich02467d82017-01-19 15:41:47 -07001557 TVariable *variable = new TVariable(param.name, *param.type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001558
1559 // Insert the parameters with name in the symbol table.
1560 if (! symbolTable.insert(*variable))
1561 error(loc, "redefinition", variable->getName().c_str(), "");
1562 else {
1563 // Transfer ownership of name pointer to symbol table.
1564 param.name = nullptr;
1565
John Kessenich7dc630f2016-09-16 01:44:43 -06001566 // Add the parameter to the AST
John Kesseniche01a9bc2016-03-12 20:11:22 -07001567 paramNodes = intermediate.growAggregate(paramNodes,
John Kessenich7dc630f2016-09-16 01:44:43 -06001568 intermediate.addSymbol(*variable, loc),
1569 loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001570 }
1571 } else
John Kessenich1c7e7072016-04-03 20:36:48 -06001572 paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07001573 }
steve-lunarga2e75312016-12-14 15:22:25 -07001574
John Kesseniche01a9bc2016-03-12 20:11:22 -07001575 intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
1576 loopNestingLevel = 0;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001577 controlFlowNestingLevel = 0;
John Kessenich517fe7a2016-11-26 13:31:47 -07001578 postEntryPointReturn = false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001579
John Kessenich94dfb7a2017-01-18 16:45:02 -07001580 return paramNodes;
1581}
John Kessenichecba76f2017-01-06 00:34:48 -07001582
John Kessenich94dfb7a2017-01-18 16:45:02 -07001583//
John Kessenich02467d82017-01-19 15:41:47 -07001584// Do all special handling for the entry point, including wrapping
1585// the shader's entry point with the official entry point that will call it.
John Kessenich94dfb7a2017-01-18 16:45:02 -07001586//
John Kessenich02467d82017-01-19 15:41:47 -07001587// The following:
1588//
1589// retType shaderEntryPoint(args...) // shader declared entry point
1590// { body }
1591//
1592// Becomes
1593//
1594// out retType ret;
1595// in iargs<that are input>...;
1596// out oargs<that are output> ...;
1597//
1598// void shaderEntryPoint() // synthesized, but official, entry point
1599// {
1600// args<that are input> = iargs...;
1601// ret = @shaderEntryPoint(args...);
1602// oargs = args<that are output>...;
1603// }
1604//
1605// The symbol table will still map the original entry point name to the
1606// the modified function and it's new name:
1607//
1608// symbol table: shaderEntryPoint -> @shaderEntryPoint
1609//
1610// Returns nullptr if no entry-point tree was built, otherwise, returns
1611// a subtree that creates the entry point.
1612//
1613TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction, const TAttributeMap& attributes)
John Kessenich94dfb7a2017-01-18 16:45:02 -07001614{
John Kessenich02467d82017-01-19 15:41:47 -07001615 // if we aren't in the entry point, fix the IO as such and exit
1616 if (userFunction.getName().compare(intermediate.getEntryPointName().c_str()) != 0) {
1617 remapNonEntryPointIO(userFunction);
1618 return nullptr;
steve-lunarg1868b142016-10-20 13:07:10 -06001619 }
1620
John Kessenich94dfb7a2017-01-18 16:45:02 -07001621 // entry point logic...
1622
John Kessenich02467d82017-01-19 15:41:47 -07001623 // Handle entry-point function attributes
John Kessenich94dfb7a2017-01-18 16:45:02 -07001624 const TIntermAggregate* numThreads = attributes[EatNumThreads];
1625 if (numThreads != nullptr) {
1626 const TIntermSequence& sequence = numThreads->getSequence();
1627
1628 for (int lid = 0; lid < int(sequence.size()); ++lid)
1629 intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
1630 }
1631 const TIntermAggregate* maxVertexCount = attributes[EatMaxVertexCount];
1632 if (maxVertexCount != nullptr)
1633 intermediate.setVertices(maxVertexCount->getSequence()[0]->getAsConstantUnion()->getConstArray()[0].getIConst());
John Kessenich02467d82017-01-19 15:41:47 -07001634
1635 // Move parameters and return value to shader in/out
1636 TVariable* entryPointOutput; // gets created in remapEntryPointIO
1637 TVector<TVariable*> inputs;
1638 TVector<TVariable*> outputs;
1639 remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs);
John Kessenichabd8dca2017-02-01 18:09:17 -07001640 // Once the parameters are moved to shader I/O, they should be non-I/O
1641 remapNonEntryPointIO(userFunction);
John Kessenich02467d82017-01-19 15:41:47 -07001642
1643 // Further this return/in/out transform by flattening, splitting, and assigning locations
1644 const auto makeVariableInOut = [&](TVariable& variable) {
1645 if (variable.getType().isStruct()) {
1646 const TStorageQualifier qualifier = variable.getType().getQualifier().storage;
1647 // struct inputs to the vertex stage and outputs from the fragment stage must be flattened
1648 if ((language == EShLangVertex && qualifier == EvqVaryingIn) ||
1649 (language == EShLangFragment && qualifier == EvqVaryingOut))
1650 flatten(loc, variable);
1651 // Mixture of IO and non-IO must be split
1652 else if (variable.getType().containsBuiltInInterstageIO(language))
1653 split(variable);
1654 }
1655 assignLocations(variable);
1656 };
1657 if (entryPointOutput)
1658 makeVariableInOut(*entryPointOutput);
1659 for (auto it = inputs.begin(); it != inputs.end(); ++it)
1660 makeVariableInOut(*(*it));
1661 for (auto it = outputs.begin(); it != outputs.end(); ++it)
1662 makeVariableInOut(*(*it));
1663
1664 // Synthesize the call
1665
1666 pushScope(); // matches the one in handleFunctionBody()
1667
1668 // new signature
1669 TType voidType(EbtVoid);
1670 TFunction synthEntryPoint(&userFunction.getName(), voidType);
1671 TIntermAggregate* synthParams = new TIntermAggregate();
1672 intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc);
1673 intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str());
1674 intermediate.incrementEntryPointCount();
1675 TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table
1676
1677 // change original name
1678 userFunction.addPrefix("@"); // change the name in the function, but not in the symbol table
1679
1680 // Copy inputs (shader-in -> calling arg), while building up the call node
1681 TVector<TVariable*> argVars;
1682 TIntermAggregate* synthBody = new TIntermAggregate();
1683 auto inputIt = inputs.begin();
1684 TIntermTyped* callingArgs = nullptr;
1685 for (int i = 0; i < userFunction.getParamCount(); i++) {
1686 TParameter& param = userFunction[i];
1687 argVars.push_back(makeInternalVariable(*param.name, *param.type));
1688 argVars.back()->getWritableType().getQualifier().makeTemporary();
1689 TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
1690 handleFunctionArgument(&callee, callingArgs, arg);
1691 if (param.type->getQualifier().isParamInput()) {
1692 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
1693 intermediate.addSymbol(**inputIt)));
1694 inputIt++;
1695 }
1696 }
1697
1698 // Call
1699 currentCaller = synthEntryPoint.getMangledName();
1700 TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
1701 currentCaller = userFunction.getMangledName();
1702
1703 // Return value
1704 if (entryPointOutput)
1705 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign,
1706 intermediate.addSymbol(*entryPointOutput), callReturn));
1707 else
1708 intermediate.growAggregate(synthBody, callReturn);
1709
1710 // Output copies
1711 auto outputIt = outputs.begin();
1712 for (int i = 0; i < userFunction.getParamCount(); i++) {
1713 TParameter& param = userFunction[i];
1714 if (param.type->getQualifier().isParamOutput()) {
1715 intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign,
1716 intermediate.addSymbol(**outputIt),
1717 intermediate.addSymbol(*argVars[i])));
1718 outputIt++;
1719 }
1720 }
1721
1722 // Put the pieces together to form a full function subtree
1723 // for the synthesized entry point.
1724 synthBody->setOperator(EOpSequence);
1725 TIntermNode* synthFunctionDef = synthParams;
1726 handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef);
1727
1728 return synthFunctionDef;
John Kesseniche01a9bc2016-03-12 20:11:22 -07001729}
1730
John Kessenicha3051662016-09-02 19:13:36 -06001731void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody, TIntermNode*& node)
1732{
1733 node = intermediate.growAggregate(node, functionBody);
1734 intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc);
1735 node->getAsAggregate()->setName(function.getMangledName().c_str());
1736
1737 popScope();
1738
1739 if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue)
1740 error(loc, "function does not return a value:", "", function.getName().c_str());
1741}
1742
John Kessenich830b0cc2016-08-29 18:10:47 -06001743// AST I/O is done through shader globals declared in the 'in' or 'out'
1744// storage class. An HLSL entry point has a return value, input parameters
1745// and output parameters. These need to get remapped to the AST I/O.
John Kessenich02467d82017-01-19 15:41:47 -07001746void HlslParseContext::remapEntryPointIO(const TFunction& function, TVariable*& returnValue,
1747 TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
John Kessenich830b0cc2016-08-29 18:10:47 -06001748{
John Kessenich7dc630f2016-09-16 01:44:43 -06001749 const auto remapType = [&](TType& type) {
1750 const auto remapBuiltInType = [&](TType& type) {
1751 switch (type.getQualifier().builtIn) {
1752 case EbvFragDepthGreater:
1753 intermediate.setDepth(EldGreater);
1754 type.getQualifier().builtIn = EbvFragDepth;
1755 break;
1756 case EbvFragDepthLesser:
1757 intermediate.setDepth(EldLess);
1758 type.getQualifier().builtIn = EbvFragDepth;
1759 break;
1760 default:
1761 break;
1762 }
1763 };
1764 remapBuiltInType(type);
1765 if (type.isStruct()) {
1766 auto members = *type.getStruct();
1767 for (auto member = members.begin(); member != members.end(); ++member)
John Kessenich02467d82017-01-19 15:41:47 -07001768 remapBuiltInType(*member->type); // TODO: lack-of-recursion structure depth problem
John Kessenich9e079532016-09-02 20:05:19 -06001769 }
1770 };
1771
John Kessenich830b0cc2016-08-29 18:10:47 -06001772 // return value is actually a shader-scoped output (out)
John Kessenich02467d82017-01-19 15:41:47 -07001773 if (function.getType().getBasicType() == EbtVoid)
1774 returnValue = nullptr;
1775 else {
1776 returnValue = makeInternalVariable("@entryPointOutput", function.getType());
1777 returnValue->getWritableType().getQualifier().storage = EvqVaryingOut;
1778 remapType(returnValue->getWritableType());
John Kessenich830b0cc2016-08-29 18:10:47 -06001779 }
1780
1781 // parameters are actually shader-scoped inputs and outputs (in or out)
1782 for (int i = 0; i < function.getParamCount(); i++) {
John Kessenichcd0a78a2016-09-09 16:32:09 -06001783 TType& paramType = *function[i].type;
John Kessenich02467d82017-01-19 15:41:47 -07001784 if (paramType.getQualifier().isParamInput()) {
1785 TVariable* argAsGlobal = makeInternalVariable(*function[i].name, paramType);
1786 argAsGlobal->getWritableType().getQualifier().storage = EvqVaryingIn;
1787 inputs.push_back(argAsGlobal);
1788 }
1789 if (paramType.getQualifier().isParamOutput()) {
1790 TVariable* argAsGlobal = makeInternalVariable(*function[i].name, paramType);
1791 argAsGlobal->getWritableType().getQualifier().storage = EvqVaryingOut;
1792 outputs.push_back(argAsGlobal);
1793 remapType(argAsGlobal->getWritableType());
1794 }
John Kessenich830b0cc2016-08-29 18:10:47 -06001795 }
1796}
1797
John Kessenich07350f32016-09-02 20:23:27 -06001798// An HLSL function that looks like an entry point, but is not,
1799// declares entry point IO built-ins, but these have to be undone.
John Kessenich6fccb3c2016-09-19 16:01:41 -06001800void HlslParseContext::remapNonEntryPointIO(TFunction& function)
John Kessenich07350f32016-09-02 20:23:27 -06001801{
John Kessenich07350f32016-09-02 20:23:27 -06001802 // return value
1803 if (function.getType().getBasicType() != EbtVoid)
John Kessenichabd8dca2017-02-01 18:09:17 -07001804 makeTypeNonIo(&function.getWritableType());
John Kessenich07350f32016-09-02 20:23:27 -06001805
1806 // parameters
1807 for (int i = 0; i < function.getParamCount(); i++)
John Kessenichabd8dca2017-02-01 18:09:17 -07001808 makeTypeNonIo(function[i].type);
John Kessenich07350f32016-09-02 20:23:27 -06001809}
1810
steve-lunargc4a13072016-08-09 11:28:03 -06001811// Handle function returns, including type conversions to the function return type
1812// if necessary.
1813TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
1814{
John Kessenicha3051662016-09-02 19:13:36 -06001815 functionReturnsValue = true;
John Kessenich6a70eb72016-08-28 15:00:23 -06001816
steve-lunargc4a13072016-08-09 11:28:03 -06001817 if (currentFunctionType->getBasicType() == EbtVoid) {
1818 error(loc, "void function cannot return a value", "return", "");
1819 return intermediate.addBranch(EOpReturn, loc);
1820 } else if (*currentFunctionType != value->getType()) {
John Kessenich087a4542016-10-06 16:56:54 -06001821 value = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
1822 if (value && *currentFunctionType != value->getType())
1823 value = intermediate.addShapeConversion(EOpReturn, *currentFunctionType, value);
1824 if (value == nullptr) {
steve-lunargc4a13072016-08-09 11:28:03 -06001825 error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
John Kessenich087a4542016-10-06 16:56:54 -06001826 return value;
steve-lunargc4a13072016-08-09 11:28:03 -06001827 }
John Kessenich6a70eb72016-08-28 15:00:23 -06001828 }
1829
John Kessenich02467d82017-01-19 15:41:47 -07001830 return intermediate.addBranch(EOpReturn, value, loc);
steve-lunargc4a13072016-08-09 11:28:03 -06001831}
1832
steve-lunarga2e75312016-12-14 15:22:25 -07001833void HlslParseContext::handleFunctionArgument(TFunction* function,
1834 TIntermTyped*& arguments, TIntermTyped* newArg)
John Kessenichd016be12016-03-13 11:24:20 -06001835{
steve-lunarg26d31452016-12-23 18:56:57 -07001836 TParameter param = { 0, new TType, nullptr };
John Kessenich4678ca92016-05-13 09:33:42 -06001837 param.type->shallowCopy(newArg->getType());
steve-lunarga2e75312016-12-14 15:22:25 -07001838
John Kessenichd016be12016-03-13 11:24:20 -06001839 function->addParameter(param);
John Kessenich4678ca92016-05-13 09:33:42 -06001840 if (arguments)
1841 arguments = intermediate.growAggregate(arguments, newArg);
1842 else
1843 arguments = newArg;
John Kessenichd016be12016-03-13 11:24:20 -06001844}
1845
John Kessenichd21baed2016-09-16 03:05:12 -06001846// Some simple source assignments need to be flattened to a sequence
John Kessenichfdf63472017-01-13 12:27:52 -07001847// of AST assignments. Catch these and flatten, otherwise, pass through
John Kessenichd21baed2016-09-16 03:05:12 -06001848// to intermediate.addAssign().
John Kessenichfdf63472017-01-13 12:27:52 -07001849//
1850// Also, assignment to matrix swizzles requires multiple component assignments,
1851// intercept those as well.
1852TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right)
John Kessenichd21baed2016-09-16 03:05:12 -06001853{
steve-lunarg90707962016-10-07 19:35:40 -06001854 if (left == nullptr || right == nullptr)
1855 return nullptr;
1856
John Kessenichfdf63472017-01-13 12:27:52 -07001857 if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle)
1858 return handleAssignToMatrixSwizzle(loc, op, left, right);
1859
steve-lunarg132d3312016-12-19 15:48:01 -07001860 const bool isSplitLeft = wasSplit(left);
1861 const bool isSplitRight = wasSplit(right);
John Kessenichd2ce8382016-09-16 19:44:00 -06001862
steve-lunarg132d3312016-12-19 15:48:01 -07001863 const bool isFlattenLeft = wasFlattened(left);
1864 const bool isFlattenRight = wasFlattened(right);
steve-lunarga2e75312016-12-14 15:22:25 -07001865
1866 // 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 -07001867 // isn't, we fall back to a member-wise copy.
steve-lunarg132d3312016-12-19 15:48:01 -07001868 if (! isFlattenLeft && ! isFlattenRight && !isSplitLeft && !isSplitRight)
John Kessenichd21baed2016-09-16 03:05:12 -06001869 return intermediate.addAssign(op, left, right, loc);
1870
steve-lunarge0b9deb2016-09-16 13:26:37 -06001871 TIntermAggregate* assignList = nullptr;
John Kessenichf9115002016-09-18 23:10:22 -06001872 const TVector<TVariable*>* leftVariables = nullptr;
1873 const TVector<TVariable*>* rightVariables = nullptr;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001874
steve-lunarg2199c242016-10-02 22:13:22 -06001875 // A temporary to store the right node's value, so we don't keep indirecting into it
1876 // if it's not a simple symbol.
1877 TVariable* rhsTempVar = nullptr;
1878
1879 // If the RHS is a simple symbol node, we'll copy it for each member.
1880 TIntermSymbol* cloneSymNode = nullptr;
1881
steve-lunarg2199c242016-10-02 22:13:22 -06001882 int memberCount = 0;
1883
1884 // Track how many items there are to copy.
1885 if (left->getType().isStruct())
Alex Szpakowski49ad2b72016-10-08 22:07:20 -03001886 memberCount = (int)left->getType().getStruct()->size();
steve-lunarg2199c242016-10-02 22:13:22 -06001887 if (left->getType().isArray())
1888 memberCount = left->getType().getCumulativeArraySize();
1889
steve-lunarg132d3312016-12-19 15:48:01 -07001890 if (isFlattenLeft)
steve-lunarga2b01a02016-11-28 17:09:54 -07001891 leftVariables = &flattenMap.find(left->getAsSymbolNode()->getId())->second.members;
steve-lunarg2199c242016-10-02 22:13:22 -06001892
steve-lunarg132d3312016-12-19 15:48:01 -07001893 if (isFlattenRight) {
steve-lunarga2b01a02016-11-28 17:09:54 -07001894 rightVariables = &flattenMap.find(right->getAsSymbolNode()->getId())->second.members;
steve-lunarg2199c242016-10-02 22:13:22 -06001895 } else {
1896 // The RHS is not flattened. There are several cases:
1897 // 1. 1 item to copy: Use the RHS directly.
1898 // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp.
1899 // 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 -07001900
steve-lunarg2199c242016-10-02 22:13:22 -06001901 if (memberCount <= 1) {
1902 // case 1: we'll use the symbol directly below. Nothing to do.
1903 } else {
1904 if (right->getAsSymbolNode() != nullptr) {
1905 // case 2: we'll copy the symbol per iteration below.
1906 cloneSymNode = right->getAsSymbolNode();
1907 } else {
1908 // case 3: assign to a temp, and indirect into that.
1909 rhsTempVar = makeInternalVariable("flattenTemp", right->getType());
1910 rhsTempVar->getWritableType().getQualifier().makeTemporary();
1911 TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc);
1912
1913 // Add this to the aggregate being built.
1914 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, noFlattenRHS, right, loc), loc);
1915 }
1916 }
1917 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06001918
steve-lunarga2b01a02016-11-28 17:09:54 -07001919 int memberIdx = 0;
1920
steve-lunarg132d3312016-12-19 15:48:01 -07001921 // We track the outer-most aggregate, so that we can use its storage class later.
1922 const TIntermTyped* outerLeft = left;
1923 const TIntermTyped* outerRight = right;
1924
1925 const auto getMember = [&](bool isLeft, TIntermTyped* node, int member, TIntermTyped* splitNode, int splitMember) -> TIntermTyped * {
steve-lunarge0b9deb2016-09-16 13:26:37 -06001926 TIntermTyped* subTree;
steve-lunarga2e75312016-12-14 15:22:25 -07001927
steve-lunarg132d3312016-12-19 15:48:01 -07001928 const bool flattened = isLeft ? isFlattenLeft : isFlattenRight;
1929 const bool split = isLeft ? isSplitLeft : isSplitRight;
1930 const TIntermTyped* outer = isLeft ? outerLeft : outerRight;
1931 const TVector<TVariable*>& flatVariables = isLeft ? *leftVariables : *rightVariables;
1932 const TOperator op = node->getType().isArray() ? EOpIndexDirect : EOpIndexDirectStruct;
1933 const TType derefType(node->getType(), member);
1934
steve-lunarg46d54282017-01-07 09:07:14 -07001935 if (split && derefType.isBuiltInInterstageIO(language)) {
steve-lunarg132d3312016-12-19 15:48:01 -07001936 // copy from interstage IO builtin if needed
rdba2f0e0e2017-01-20 14:46:39 +01001937 subTree = intermediate.addSymbol(*interstageBuiltInIo[tInterstageIoData(derefType, outer->getType())]);
steve-lunarg132d3312016-12-19 15:48:01 -07001938 } else if (flattened && isFinalFlattening(derefType)) {
1939 subTree = intermediate.addSymbol(*flatVariables[memberIdx++]);
steve-lunarga2b01a02016-11-28 17:09:54 -07001940 } else {
steve-lunarg132d3312016-12-19 15:48:01 -07001941 const TType splitDerefType(splitNode->getType(), splitMember);
1942
1943 subTree = intermediate.addIndex(op, splitNode, intermediate.addConstantUnion(splitMember, loc), loc);
1944 subTree->setType(splitDerefType);
steve-lunarge0b9deb2016-09-16 13:26:37 -06001945 }
1946
1947 return subTree;
1948 };
1949
steve-lunarga2b01a02016-11-28 17:09:54 -07001950 // Use the proper RHS node: a new symbol from a TVariable, copy
1951 // of an TIntermSymbol node, or sometimes the right node directly.
1952 right = rhsTempVar ? intermediate.addSymbol(*rhsTempVar, loc) :
1953 cloneSymNode ? intermediate.addSymbol(*cloneSymNode) :
1954 right;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001955
John Kessenichd21baed2016-09-16 03:05:12 -06001956 // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the
1957 // whole thing. So, we'll resort to an explicit type via std::function.
steve-lunarg132d3312016-12-19 15:48:01 -07001958 const std::function<void(TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight)>
1959 traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight) -> void {
John Kessenichd21baed2016-09-16 03:05:12 -06001960 // If we get here, we are assigning to or from a whole array or struct that must be
1961 // flattened, so have to do member-by-member assignment:
1962
John Kessenichd2ce8382016-09-16 19:44:00 -06001963 if (left->getType().isArray()) {
John Kessenichd21baed2016-09-16 03:05:12 -06001964 const TType dereferencedType(left->getType(), 0);
John Kessenichd2ce8382016-09-16 19:44:00 -06001965
steve-lunarg132d3312016-12-19 15:48:01 -07001966 // array case
John Kessenichd2ce8382016-09-16 19:44:00 -06001967 for (int element=0; element < left->getType().getOuterArraySize(); ++element) {
1968 // Add a new AST symbol node if we have a temp variable holding a complex RHS.
steve-lunarg132d3312016-12-19 15:48:01 -07001969 TIntermTyped* subLeft = getMember(true, left, element, left, element);
1970 TIntermTyped* subRight = getMember(false, right, element, right, element);
John Kessenichd2ce8382016-09-16 19:44:00 -06001971
steve-lunarg65cdff92017-01-19 15:18:00 -07001972 TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left, element, splitLeft, element) : subLeft;
1973 TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right, element, splitRight, element) : subRight;
1974
John Kessenichd2ce8382016-09-16 19:44:00 -06001975 if (isFinalFlattening(dereferencedType))
1976 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, subLeft, subRight, loc), loc);
1977 else
steve-lunarg65cdff92017-01-19 15:18:00 -07001978 traverse(subLeft, subRight, subSplitLeft, subSplitRight);
John Kessenichd2ce8382016-09-16 19:44:00 -06001979 }
John Kessenichfcea3022016-09-16 21:16:04 -06001980 } else if (left->getType().isStruct()) {
1981 // struct case
steve-lunarga2e75312016-12-14 15:22:25 -07001982 const auto& membersL = *left->getType().getStruct();
1983 const auto& membersR = *right->getType().getStruct();
John Kessenichfcea3022016-09-16 21:16:04 -06001984
steve-lunarg132d3312016-12-19 15:48:01 -07001985 // These track the members in the split structures corresponding to the same in the unsplit structures,
1986 // which we traverse in parallel.
steve-lunarga2e75312016-12-14 15:22:25 -07001987 int memberL = 0;
1988 int memberR = 0;
steve-lunarge0b9deb2016-09-16 13:26:37 -06001989
steve-lunarga2e75312016-12-14 15:22:25 -07001990 for (int member = 0; member < int(membersL.size()); ++member) {
1991 const TType& typeL = *membersL[member].type;
1992 const TType& typeR = *membersR[member].type;
1993
steve-lunarg132d3312016-12-19 15:48:01 -07001994 TIntermTyped* subLeft = getMember(true, left, member, left, member);
1995 TIntermTyped* subRight = getMember(false, right, member, right, member);
steve-lunarga2e75312016-12-14 15:22:25 -07001996
steve-lunarg132d3312016-12-19 15:48:01 -07001997 // If there is no splitting, use the same values to avoid inefficiency.
1998 TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left, member, splitLeft, memberL) : subLeft;
1999 TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right, member, splitRight, memberR) : subRight;
2000
2001 // If this is the final flattening (no nested types below to flatten) we'll copy the member, else
2002 // recurse into the type hierarchy. However, if splitting the struct, that means we can copy a whole
2003 // subtree here IFF it does not itself contain any interstage built-in IO variables, so we only have to
2004 // recurse into it if there's something for splitting to do. That can save a lot of AST verbosity for
2005 // a bunch of memberwise copies.
John Kessenichecba76f2017-01-06 00:34:48 -07002006 if (isFinalFlattening(typeL) || (!isFlattenLeft && !isFlattenRight &&
steve-lunarg46d54282017-01-07 09:07:14 -07002007 !typeL.containsBuiltInInterstageIO(language) && !typeR.containsBuiltInInterstageIO(language))) {
steve-lunarg132d3312016-12-19 15:48:01 -07002008 assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, subSplitLeft, subSplitRight, loc), loc);
steve-lunarga2e75312016-12-14 15:22:25 -07002009 } else {
steve-lunarg132d3312016-12-19 15:48:01 -07002010 traverse(subLeft, subRight, subSplitLeft, subSplitRight);
steve-lunarga2e75312016-12-14 15:22:25 -07002011 }
2012
steve-lunarg46d54282017-01-07 09:07:14 -07002013 memberL += (typeL.isBuiltInInterstageIO(language) ? 0 : 1);
2014 memberR += (typeR.isBuiltInInterstageIO(language) ? 0 : 1);
steve-lunarge0b9deb2016-09-16 13:26:37 -06002015 }
2016 } else {
2017 assert(0); // we should never be called on a non-flattenable thing, because
2018 // that case bails out above to a simple copy.
2019 }
2020
2021 };
2022
steve-lunarg132d3312016-12-19 15:48:01 -07002023 TIntermTyped* splitLeft = left;
2024 TIntermTyped* splitRight = right;
2025
2026 // If either left or right was a split structure, we must read or write it, but still have to
2027 // parallel-recurse through the unsplit structure to identify the builtin IO vars.
2028 if (isSplitLeft)
2029 splitLeft = intermediate.addSymbol(*getSplitIoVar(left), loc);
2030
2031 if (isSplitRight)
2032 splitRight = intermediate.addSymbol(*getSplitIoVar(right), loc);
2033
steve-lunarga2b01a02016-11-28 17:09:54 -07002034 // This makes the whole assignment, recursing through subtypes as needed.
steve-lunarg132d3312016-12-19 15:48:01 -07002035 traverse(left, right, splitLeft, splitRight);
steve-lunarge0b9deb2016-09-16 13:26:37 -06002036
2037 assert(assignList != nullptr);
John Kessenichd21baed2016-09-16 03:05:12 -06002038 assignList->setOperator(EOpSequence);
2039
2040 return assignList;
2041}
2042
John Kessenichfdf63472017-01-13 12:27:52 -07002043// An assignment to matrix swizzle must be decomposed into individual assignments.
2044// These must be selected component-wise from the RHS and stored component-wise
2045// into the LHS.
2046TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right)
2047{
2048 assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle);
2049
2050 if (op != EOpAssign)
2051 error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", "");
2052
2053 // isolate the matrix and swizzle nodes
2054 TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped();
2055 const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence();
2056
2057 // if the RHS isn't already a simple vector, let's store into one
2058 TIntermSymbol* vector = right->getAsSymbolNode();
2059 TIntermTyped* vectorAssign = nullptr;
2060 if (vector == nullptr) {
2061 // create a new intermediate vector variable to assign to
2062 TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, swizzle.size()/2);
2063 vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc);
2064
2065 // assign the right to the new vector
2066 vectorAssign = handleAssign(loc, op, vector, right);
2067 }
2068
2069 // Assign the vector components to the matrix components.
2070 // Store this as a sequence, so a single aggregate node represents this
2071 // entire operation.
2072 TIntermAggregate* result = intermediate.makeAggregate(vectorAssign);
2073 TType columnType(matrix->getType(), 0);
2074 TType componentType(columnType, 0);
2075 TType indexType(EbtInt);
2076 for (int i = 0; i < (int)swizzle.size(); i += 2) {
2077 // the right component, single index into the RHS vector
2078 TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector,
2079 intermediate.addConstantUnion(i/2, loc), loc);
2080
2081 // the left component, double index into the LHS matrix
2082 TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix,
2083 intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(),
2084 indexType, loc),
2085 loc);
2086 leftComp->setType(columnType);
2087 leftComp = intermediate.addIndex(EOpIndexDirect, leftComp,
2088 intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(),
2089 indexType, loc),
2090 loc);
2091 leftComp->setType(componentType);
2092
2093 // Add the assignment to the aggregate
2094 result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc));
2095 }
2096
2097 result->setOp(EOpSequence);
2098
2099 return result;
2100}
2101
LoopDawg58910702016-06-13 09:22:28 -06002102//
2103// HLSL atomic operations have slightly different arguments than
2104// GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic.
2105// This provides the post-decomposition equivalent opcode.
2106//
2107TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage)
2108{
2109 switch (op) {
steve-lunarg22322362016-10-19 10:15:25 -06002110 case EOpInterlockedAdd: return isImage ? EOpImageAtomicAdd : EOpAtomicAdd;
2111 case EOpInterlockedAnd: return isImage ? EOpImageAtomicAnd : EOpAtomicAnd;
LoopDawg58910702016-06-13 09:22:28 -06002112 case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap;
steve-lunarg22322362016-10-19 10:15:25 -06002113 case EOpInterlockedMax: return isImage ? EOpImageAtomicMax : EOpAtomicMax;
2114 case EOpInterlockedMin: return isImage ? EOpImageAtomicMin : EOpAtomicMin;
2115 case EOpInterlockedOr: return isImage ? EOpImageAtomicOr : EOpAtomicOr;
2116 case EOpInterlockedXor: return isImage ? EOpImageAtomicXor : EOpAtomicXor;
LoopDawg58910702016-06-13 09:22:28 -06002117 case EOpInterlockedExchange: return isImage ? EOpImageAtomicExchange : EOpAtomicExchange;
John Kessenichecba76f2017-01-06 00:34:48 -07002118 case EOpInterlockedCompareStore: // TODO: ...
LoopDawg58910702016-06-13 09:22:28 -06002119 default:
2120 error(loc, "unknown atomic operation", "unknown op", "");
2121 return EOpNull;
2122 }
2123}
2124
LoopDawg4624a022016-06-20 13:26:59 -06002125//
LoopDawga2b79912016-07-14 14:45:14 -06002126// Create a combined sampler/texture from separate sampler and texture.
LoopDawg4624a022016-06-20 13:26:59 -06002127//
LoopDawga2b79912016-07-14 14:45:14 -06002128TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, TIntermTyped* argSampler)
2129{
2130 TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler);
2131
2132 txcombine->getSequence().push_back(argTex);
2133 txcombine->getSequence().push_back(argSampler);
2134
2135 TSampler samplerType = argTex->getType().getSampler();
2136 samplerType.combined = true;
LoopDawga78b0292016-07-19 14:28:05 -06002137 samplerType.shadow = argSampler->getType().getSampler().shadow;
LoopDawga2b79912016-07-14 14:45:14 -06002138
2139 txcombine->setType(TType(samplerType, EvqTemporary));
2140 txcombine->setLoc(loc);
2141
2142 return txcombine;
2143}
2144
2145//
2146// Decompose DX9 and DX10 sample intrinsics & object methods into AST
2147//
2148void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
LoopDawg4624a022016-06-20 13:26:59 -06002149{
2150 if (!node || !node->getAsOperator())
2151 return;
2152
baldurkca735702016-10-28 17:57:25 +02002153 const auto clampReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
steve-lunarg8b0227c2016-10-14 16:40:32 -06002154 // Sampler return must always be a vec4, but we can construct a shorter vector
2155 result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize()));
2156
John Kessenich4a346792016-10-16 11:50:46 -06002157 if (sampler.vectorSize < (unsigned)node->getVectorSize()) {
steve-lunarg8b0227c2016-10-14 16:40:32 -06002158 // Too many components. Construct shorter vector from it.
2159 const TType clampedType(result->getType().getBasicType(), EvqTemporary, sampler.vectorSize);
2160
steve-lunargcce8d482016-10-14 18:36:42 -06002161 const TOperator op = intermediate.mapTypeToConstructorOp(clampedType);
steve-lunarg8b0227c2016-10-14 16:40:32 -06002162
2163 result = constructBuiltIn(clampedType, op, result, loc, false);
2164 }
2165
2166 result->setLoc(loc);
2167 return result;
2168 };
2169
LoopDawg4624a022016-06-20 13:26:59 -06002170 const TOperator op = node->getAsOperator()->getOp();
2171 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
2172
2173 switch (op) {
LoopDawga2b79912016-07-14 14:45:14 -06002174 // **** DX9 intrinsics: ****
LoopDawg4624a022016-06-20 13:26:59 -06002175 case EOpTexture:
2176 {
LoopDawga2b79912016-07-14 14:45:14 -06002177 // Texture with ddx & ddy is really gradient form in HLSL
steve-lunarg8b0227c2016-10-14 16:40:32 -06002178 if (argAggregate->getSequence().size() == 4)
LoopDawg4624a022016-06-20 13:26:59 -06002179 node->getAsAggregate()->setOperator(EOpTextureGrad);
LoopDawg4624a022016-06-20 13:26:59 -06002180
2181 break;
2182 }
2183
2184 case EOpTextureBias:
2185 {
2186 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // sampler
2187 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // coord
2188
2189 // HLSL puts bias in W component of coordinate. We extract it and add it to
2190 // the argument list, instead
2191 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
2192 TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
2193
2194 TOperator constructOp = EOpNull;
steve-lunarg8b0227c2016-10-14 16:40:32 -06002195 const TSampler& sampler = arg0->getType().getSampler();
2196
2197 switch (sampler.dim) {
LoopDawg4624a022016-06-20 13:26:59 -06002198 case Esd1D: constructOp = EOpConstructFloat; break; // 1D
2199 case Esd2D: constructOp = EOpConstructVec2; break; // 2D
2200 case Esd3D: constructOp = EOpConstructVec3; break; // 3D
2201 case EsdCube: constructOp = EOpConstructVec3; break; // also 3D
2202 default: break;
2203 }
steve-lunarg8b0227c2016-10-14 16:40:32 -06002204
LoopDawg4624a022016-06-20 13:26:59 -06002205 TIntermAggregate* constructCoord = new TIntermAggregate(constructOp);
2206 constructCoord->getSequence().push_back(arg1);
2207 constructCoord->setLoc(loc);
2208
steve-lunarg6b596682016-10-20 14:50:12 -06002209 // The input vector should never be less than 2, since there's always a bias.
2210 // The max is for safety, and should be a no-op.
2211 constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0)));
2212
LoopDawg4624a022016-06-20 13:26:59 -06002213 TIntermAggregate* tex = new TIntermAggregate(EOpTexture);
2214 tex->getSequence().push_back(arg0); // sampler
2215 tex->getSequence().push_back(constructCoord); // coordinate
2216 tex->getSequence().push_back(bias); // bias
John Kessenichecba76f2017-01-06 00:34:48 -07002217
steve-lunarg8b0227c2016-10-14 16:40:32 -06002218 node = clampReturn(tex, sampler);
LoopDawg4624a022016-06-20 13:26:59 -06002219
2220 break;
2221 }
2222
LoopDawga2b79912016-07-14 14:45:14 -06002223 // **** DX10 methods: ****
2224 case EOpMethodSample: // fall through
2225 case EOpMethodSampleBias: // ...
2226 {
2227 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2228 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2229 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2230 TIntermTyped* argBias = nullptr;
2231 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06002232 const TSampler& sampler = argTex->getType().getSampler();
LoopDawga2b79912016-07-14 14:45:14 -06002233
2234 int nextArg = 3;
2235
2236 if (op == EOpMethodSampleBias) // SampleBias has a bias arg
2237 argBias = argAggregate->getSequence()[nextArg++]->getAsTyped();
2238
2239 TOperator textureOp = EOpTexture;
2240
John Kesseniche4821e42016-07-16 10:19:43 -06002241 if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form
LoopDawga2b79912016-07-14 14:45:14 -06002242 textureOp = EOpTextureOffset;
2243 argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped();
2244 }
2245
2246 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2247
2248 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
2249 txsample->getSequence().push_back(txcombine);
2250 txsample->getSequence().push_back(argCoord);
2251
2252 if (argBias != nullptr)
2253 txsample->getSequence().push_back(argBias);
2254
2255 if (argOffset != nullptr)
2256 txsample->getSequence().push_back(argOffset);
2257
steve-lunarg8b0227c2016-10-14 16:40:32 -06002258 node = clampReturn(txsample, sampler);
LoopDawga2b79912016-07-14 14:45:14 -06002259
2260 break;
2261 }
John Kessenichecba76f2017-01-06 00:34:48 -07002262
LoopDawga2b79912016-07-14 14:45:14 -06002263 case EOpMethodSampleGrad: // ...
2264 {
2265 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2266 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2267 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2268 TIntermTyped* argDDX = argAggregate->getSequence()[3]->getAsTyped();
2269 TIntermTyped* argDDY = argAggregate->getSequence()[4]->getAsTyped();
2270 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06002271 const TSampler& sampler = argTex->getType().getSampler();
LoopDawga2b79912016-07-14 14:45:14 -06002272
2273 TOperator textureOp = EOpTextureGrad;
2274
2275 if (argAggregate->getSequence().size() == 6) { // last parameter is offset form
2276 textureOp = EOpTextureGradOffset;
2277 argOffset = argAggregate->getSequence()[5]->getAsTyped();
2278 }
2279
2280 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2281
2282 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
2283 txsample->getSequence().push_back(txcombine);
2284 txsample->getSequence().push_back(argCoord);
2285 txsample->getSequence().push_back(argDDX);
2286 txsample->getSequence().push_back(argDDY);
2287
2288 if (argOffset != nullptr)
2289 txsample->getSequence().push_back(argOffset);
2290
steve-lunarg8b0227c2016-10-14 16:40:32 -06002291 node = clampReturn(txsample, sampler);
LoopDawga2b79912016-07-14 14:45:14 -06002292
2293 break;
2294 }
2295
LoopDawg5d58fae2016-07-15 11:22:24 -06002296 case EOpMethodGetDimensions:
2297 {
2298 // AST returns a vector of results, which we break apart component-wise into
2299 // separate values to assign to the HLSL method's outputs, ala:
2300 // tx . GetDimensions(width, height);
2301 // float2 sizeQueryTemp = EOpTextureQuerySize
2302 // width = sizeQueryTemp.X;
2303 // height = sizeQueryTemp.Y;
2304
2305 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2306 const TType& texType = argTex->getType();
2307
2308 assert(texType.getBasicType() == EbtSampler);
2309
steve-lunarg8b0227c2016-10-14 16:40:32 -06002310 const TSampler& sampler = texType.getSampler();
2311 const TSamplerDim dim = sampler.dim;
2312 const bool isImage = sampler.isImage();
John Kessenich267590d2016-08-05 17:34:34 -06002313 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawg5d58fae2016-07-15 11:22:24 -06002314
2315 int numDims = 0;
2316
2317 switch (dim) {
steve-lunargbb0183f2016-10-04 16:58:14 -06002318 case Esd1D: numDims = 1; break; // W
2319 case Esd2D: numDims = 2; break; // W, H
2320 case Esd3D: numDims = 3; break; // W, H, D
2321 case EsdCube: numDims = 2; break; // W, H (cube)
steve-lunarg6b43d272016-10-06 20:12:24 -06002322 case EsdBuffer: numDims = 1; break; // W (buffers)
LoopDawg5d58fae2016-07-15 11:22:24 -06002323 default:
2324 assert(0 && "unhandled texture dimension");
2325 }
2326
2327 // Arrayed adds another dimension for the number of array elements
steve-lunarg8b0227c2016-10-14 16:40:32 -06002328 if (sampler.isArrayed())
LoopDawg5d58fae2016-07-15 11:22:24 -06002329 ++numDims;
2330
2331 // Establish whether we're querying mip levels
steve-lunarg8b0227c2016-10-14 16:40:32 -06002332 const bool mipQuery = (numArgs > (numDims + 1)) && (!sampler.isMultiSample());
LoopDawg5d58fae2016-07-15 11:22:24 -06002333
2334 // AST assumes integer return. Will be converted to float if required.
steve-lunargbb0183f2016-10-04 16:58:14 -06002335 TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize);
LoopDawg5d58fae2016-07-15 11:22:24 -06002336 sizeQuery->getSequence().push_back(argTex);
2337 // If we're querying an explicit LOD, add the LOD, which is always arg #1
2338 if (mipQuery) {
2339 TIntermTyped* queryLod = argAggregate->getSequence()[1]->getAsTyped();
2340 sizeQuery->getSequence().push_back(queryLod);
2341 }
2342 sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims));
2343 sizeQuery->setLoc(loc);
2344
2345 // Return value from size query
2346 TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType());
2347 tempArg->getWritableType().getQualifier().makeTemporary();
steve-lunarg2199c242016-10-02 22:13:22 -06002348 TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign,
2349 intermediate.addSymbol(*tempArg, loc),
2350 sizeQuery, loc);
LoopDawg5d58fae2016-07-15 11:22:24 -06002351
2352 // Compound statement for assigning outputs
2353 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc);
2354 // Index of first output parameter
2355 const int outParamBase = mipQuery ? 2 : 1;
2356
2357 for (int compNum = 0; compNum < numDims; ++compNum) {
2358 TIntermTyped* indexedOut = nullptr;
steve-lunarg2199c242016-10-02 22:13:22 -06002359 TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc);
LoopDawg5d58fae2016-07-15 11:22:24 -06002360
2361 if (numDims > 1) {
2362 TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true);
2363 indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc);
2364 indexedOut->setType(TType(EbtUint, EvqTemporary, 1));
2365 indexedOut->setLoc(loc);
2366 } else {
2367 indexedOut = sizeQueryReturn;
2368 }
John Kessenichecba76f2017-01-06 00:34:48 -07002369
LoopDawg5d58fae2016-07-15 11:22:24 -06002370 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped();
2371 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc);
2372
2373 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
2374 }
2375
2376 // handle mip level parameter
2377 if (mipQuery) {
2378 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
2379
2380 TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels);
2381 levelsQuery->getSequence().push_back(argTex);
2382 levelsQuery->setType(TType(EbtUint, EvqTemporary, 1));
2383 levelsQuery->setLoc(loc);
2384
2385 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc);
2386 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
2387 }
2388
steve-lunarg1e19d902016-07-26 15:19:28 -06002389 // 2DMS formats query # samples, which needs a different query op
steve-lunarg8b0227c2016-10-14 16:40:32 -06002390 if (sampler.isMultiSample()) {
steve-lunarg1e19d902016-07-26 15:19:28 -06002391 TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
2392
2393 TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
2394 samplesQuery->getSequence().push_back(argTex);
2395 samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
2396 samplesQuery->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07002397
steve-lunarg1e19d902016-07-26 15:19:28 -06002398 TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc);
2399 compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
2400 }
2401
LoopDawg5d58fae2016-07-15 11:22:24 -06002402 compoundStatement->setOperator(EOpSequence);
2403 compoundStatement->setLoc(loc);
2404 compoundStatement->setType(TType(EbtVoid));
2405
2406 node = compoundStatement;
2407
2408 break;
2409 }
2410
LoopDawga78b0292016-07-19 14:28:05 -06002411 case EOpMethodSampleCmp: // fall through...
2412 case EOpMethodSampleCmpLevelZero:
2413 {
2414 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2415 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2416 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2417 TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped();
2418 TIntermTyped* argOffset = nullptr;
2419
2420 // optional offset value
2421 if (argAggregate->getSequence().size() > 4)
2422 argOffset = argAggregate->getSequence()[4]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -07002423
LoopDawga78b0292016-07-19 14:28:05 -06002424 const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp
2425
2426 // AST wants comparison value as one of the texture coordinates
2427 TOperator constructOp = EOpNull;
2428 switch (coordDimWithCmpVal) {
2429 // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val
2430 case 2: constructOp = EOpConstructVec2; break;
2431 case 3: constructOp = EOpConstructVec3; break;
2432 case 4: constructOp = EOpConstructVec4; break;
2433 case 5: constructOp = EOpConstructVec4; break; // cubeArrayShadow, cmp value is separate arg.
2434 default: assert(0); break;
2435 }
2436
2437 TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp);
2438 coordWithCmp->getSequence().push_back(argCoord);
2439 if (coordDimWithCmpVal != 5) // cube array shadow is special.
2440 coordWithCmp->getSequence().push_back(argCmpVal);
2441 coordWithCmp->setLoc(loc);
steve-lunarg6b596682016-10-20 14:50:12 -06002442 coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4)));
LoopDawga78b0292016-07-19 14:28:05 -06002443
2444 TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture);
2445 if (argOffset != nullptr)
2446 textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset);
2447
2448 // Create combined sampler & texture op
2449 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2450 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
2451 txsample->getSequence().push_back(txcombine);
2452 txsample->getSequence().push_back(coordWithCmp);
2453
2454 if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord.
2455 txsample->getSequence().push_back(argCmpVal);
2456
2457 // the LevelZero form uses 0 as an explicit LOD
2458 if (op == EOpMethodSampleCmpLevelZero)
2459 txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true));
2460
2461 // Add offset if present
2462 if (argOffset != nullptr)
2463 txsample->getSequence().push_back(argOffset);
2464
2465 txsample->setType(node->getType());
2466 txsample->setLoc(loc);
2467 node = txsample;
2468
2469 break;
2470 }
2471
LoopDawgf2451012016-07-20 16:34:44 -06002472 case EOpMethodLoad:
2473 {
2474 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2475 TIntermTyped* argCoord = argAggregate->getSequence()[1]->getAsTyped();
2476 TIntermTyped* argOffset = nullptr;
2477 TIntermTyped* lodComponent = nullptr;
2478 TIntermTyped* coordSwizzle = nullptr;
2479
steve-lunarg8b0227c2016-10-14 16:40:32 -06002480 const TSampler& sampler = argTex->getType().getSampler();
2481 const bool isMS = sampler.isMultiSample();
2482 const bool isBuffer = sampler.dim == EsdBuffer;
2483 const bool isImage = sampler.isImage();
LoopDawgf2451012016-07-20 16:34:44 -06002484 const TBasicType coordBaseType = argCoord->getType().getBasicType();
2485
2486 // Last component of coordinate is the mip level, for non-MS. we separate them here:
steve-lunargbb0183f2016-10-04 16:58:14 -06002487 if (isMS || isBuffer || isImage) {
2488 // MS, Buffer, and Image have no LOD
steve-lunarg1e19d902016-07-26 15:19:28 -06002489 coordSwizzle = argCoord;
LoopDawgf2451012016-07-20 16:34:44 -06002490 } else {
2491 // Extract coordinate
John Kessenichc142c882017-01-13 19:34:22 -07002492 int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1);
2493 TSwizzleSelectors<TVectorSelector> coordFields;
2494 for (int i = 0; i < swizzleSize; ++i)
2495 coordFields.push_back(i);
LoopDawgf2451012016-07-20 16:34:44 -06002496 TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc);
2497 coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc);
John Kessenichc142c882017-01-13 19:34:22 -07002498 coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size()));
LoopDawgf2451012016-07-20 16:34:44 -06002499
2500 // Extract LOD
John Kessenichc142c882017-01-13 19:34:22 -07002501 TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true);
LoopDawgf2451012016-07-20 16:34:44 -06002502 lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc);
2503 lodComponent->setType(TType(coordBaseType, EvqTemporary, 1));
2504 }
2505
John Kessenich267590d2016-08-05 17:34:34 -06002506 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawgf2451012016-07-20 16:34:44 -06002507 const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4));
2508
LoopDawgf2451012016-07-20 16:34:44 -06002509 // Create texel fetch
steve-lunargbb0183f2016-10-04 16:58:14 -06002510 const TOperator fetchOp = (isImage ? EOpImageLoad :
2511 hasOffset ? EOpTextureFetchOffset :
2512 EOpTextureFetch);
LoopDawgf2451012016-07-20 16:34:44 -06002513 TIntermAggregate* txfetch = new TIntermAggregate(fetchOp);
2514
LoopDawgf2451012016-07-20 16:34:44 -06002515 // Build up the fetch
2516 txfetch->getSequence().push_back(argTex);
2517 txfetch->getSequence().push_back(coordSwizzle);
2518
steve-lunarg1e19d902016-07-26 15:19:28 -06002519 if (isMS) {
2520 // add 2DMS sample index
2521 TIntermTyped* argSampleIdx = argAggregate->getSequence()[2]->getAsTyped();
2522 txfetch->getSequence().push_back(argSampleIdx);
steve-lunargd53f7172016-07-27 15:46:48 -06002523 } else if (isBuffer) {
2524 // Nothing else to do for buffers.
steve-lunargbb0183f2016-10-04 16:58:14 -06002525 } else if (isImage) {
2526 // Nothing else to do for images.
steve-lunarg1e19d902016-07-26 15:19:28 -06002527 } else {
steve-lunargd53f7172016-07-27 15:46:48 -06002528 // 2DMS and buffer have no LOD, but everything else does.
LoopDawgf2451012016-07-20 16:34:44 -06002529 txfetch->getSequence().push_back(lodComponent);
steve-lunarg1e19d902016-07-26 15:19:28 -06002530 }
LoopDawgf2451012016-07-20 16:34:44 -06002531
steve-lunarg1e19d902016-07-26 15:19:28 -06002532 // Obtain offset arg, if there is one.
2533 if (hasOffset) {
2534 const int offsetPos = (isMS ? 3 : 2);
2535 argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped();
LoopDawgf2451012016-07-20 16:34:44 -06002536 txfetch->getSequence().push_back(argOffset);
steve-lunarg1e19d902016-07-26 15:19:28 -06002537 }
LoopDawgf2451012016-07-20 16:34:44 -06002538
steve-lunarg8b0227c2016-10-14 16:40:32 -06002539 node = clampReturn(txfetch, sampler);
LoopDawgf2451012016-07-20 16:34:44 -06002540
2541 break;
2542 }
2543
LoopDawg3ef78522016-07-21 15:02:16 -06002544 case EOpMethodSampleLevel:
2545 {
2546 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2547 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2548 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2549 TIntermTyped* argLod = argAggregate->getSequence()[3]->getAsTyped();
2550 TIntermTyped* argOffset = nullptr;
steve-lunarg8b0227c2016-10-14 16:40:32 -06002551 const TSampler& sampler = argTex->getType().getSampler();
John Kessenichecba76f2017-01-06 00:34:48 -07002552
John Kessenich267590d2016-08-05 17:34:34 -06002553 const int numArgs = (int)argAggregate->getSequence().size();
LoopDawg3ef78522016-07-21 15:02:16 -06002554
2555 if (numArgs == 5) // offset, if present
2556 argOffset = argAggregate->getSequence()[4]->getAsTyped();
John Kessenichecba76f2017-01-06 00:34:48 -07002557
LoopDawg3ef78522016-07-21 15:02:16 -06002558 const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset);
2559 TIntermAggregate* txsample = new TIntermAggregate(textureOp);
2560
2561 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2562
2563 txsample->getSequence().push_back(txcombine);
2564 txsample->getSequence().push_back(argCoord);
2565 txsample->getSequence().push_back(argLod);
2566
2567 if (argOffset != nullptr)
2568 txsample->getSequence().push_back(argOffset);
2569
steve-lunarg8b0227c2016-10-14 16:40:32 -06002570 node = clampReturn(txsample, sampler);
LoopDawg3ef78522016-07-21 15:02:16 -06002571
2572 break;
2573 }
2574
LoopDawga2f3d282016-07-22 08:28:11 -06002575 case EOpMethodGather:
2576 {
2577 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2578 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2579 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2580 TIntermTyped* argOffset = nullptr;
2581
2582 // Offset is optional
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06002583 if (argAggregate->getSequence().size() > 3)
LoopDawga2f3d282016-07-22 08:28:11 -06002584 argOffset = argAggregate->getSequence()[3]->getAsTyped();
2585
2586 const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset);
2587 TIntermAggregate* txgather = new TIntermAggregate(textureOp);
2588
2589 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2590
2591 txgather->getSequence().push_back(txcombine);
2592 txgather->getSequence().push_back(argCoord);
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06002593 // Offset if not given is implicitly channel 0 (red)
LoopDawga2f3d282016-07-22 08:28:11 -06002594
2595 if (argOffset != nullptr)
2596 txgather->getSequence().push_back(argOffset);
2597
2598 txgather->setType(node->getType());
2599 txgather->setLoc(loc);
2600 node = txgather;
2601
2602 break;
2603 }
John Kessenichecba76f2017-01-06 00:34:48 -07002604
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06002605 case EOpMethodGatherRed: // fall through...
2606 case EOpMethodGatherGreen: // ...
2607 case EOpMethodGatherBlue: // ...
2608 case EOpMethodGatherAlpha: // ...
2609 case EOpMethodGatherCmpRed: // ...
2610 case EOpMethodGatherCmpGreen: // ...
2611 case EOpMethodGatherCmpBlue: // ...
2612 case EOpMethodGatherCmpAlpha: // ...
2613 {
2614 int channel = 0; // the channel we are gathering
2615 int cmpValues = 0; // 1 if there is a compare value (handier than a bool below)
2616
2617 switch (op) {
2618 case EOpMethodGatherCmpRed: cmpValues = 1; // fall through
2619 case EOpMethodGatherRed: channel = 0; break;
2620 case EOpMethodGatherCmpGreen: cmpValues = 1; // fall through
2621 case EOpMethodGatherGreen: channel = 1; break;
2622 case EOpMethodGatherCmpBlue: cmpValues = 1; // fall through
2623 case EOpMethodGatherBlue: channel = 2; break;
2624 case EOpMethodGatherCmpAlpha: cmpValues = 1; // fall through
2625 case EOpMethodGatherAlpha: channel = 3; break;
2626 default: assert(0); break;
2627 }
2628
2629 // For now, we have nothing to map the component-wise comparison forms
2630 // to, because neither GLSL nor SPIR-V has such an opcode. Issue an
2631 // unimplemented error instead. Most of the machinery is here if that
2632 // should ever become available.
2633 if (cmpValues) {
2634 error(loc, "unimplemented: component-level gather compare", "", "");
2635 return;
2636 }
2637
2638 int arg = 0;
2639
2640 TIntermTyped* argTex = argAggregate->getSequence()[arg++]->getAsTyped();
2641 TIntermTyped* argSamp = argAggregate->getSequence()[arg++]->getAsTyped();
2642 TIntermTyped* argCoord = argAggregate->getSequence()[arg++]->getAsTyped();
2643 TIntermTyped* argOffset = nullptr;
2644 TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr };
2645 // TIntermTyped* argStatus = nullptr; // TODO: residency
2646 TIntermTyped* argCmp = nullptr;
2647
2648 const TSamplerDim dim = argTex->getType().getSampler().dim;
2649
baldurk1eb1c112016-08-15 18:01:15 +02002650 const int argSize = (int)argAggregate->getSequence().size();
steve-lunarg7dfcf4d2016-07-31 10:37:02 -06002651 bool hasStatus = (argSize == (5+cmpValues) || argSize == (8+cmpValues));
2652 bool hasOffset1 = false;
2653 bool hasOffset4 = false;
2654
2655 // Only 2D forms can have offsets. Discover if we have 0, 1 or 4 offsets.
2656 if (dim == Esd2D) {
2657 hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues));
2658 hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues));
2659 }
2660
2661 assert(!(hasOffset1 && hasOffset4));
2662
2663 TOperator textureOp = EOpTextureGather;
2664
2665 // Compare forms have compare value
2666 if (cmpValues != 0)
2667 argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
2668
2669 // Some forms have single offset
2670 if (hasOffset1) {
2671 textureOp = EOpTextureGatherOffset; // single offset form
2672 argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
2673 }
2674
2675 // Some forms have 4 gather offsets
2676 if (hasOffset4) {
2677 textureOp = EOpTextureGatherOffsets; // note plural, for 4 offset form
2678 for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
2679 argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped();
2680 }
2681
2682 // Residency status
2683 if (hasStatus) {
2684 // argStatus = argAggregate->getSequence()[arg++]->getAsTyped();
2685 error(loc, "unimplemented: residency status", "", "");
2686 return;
2687 }
2688
2689 TIntermAggregate* txgather = new TIntermAggregate(textureOp);
2690 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2691
2692 TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true);
2693
2694 txgather->getSequence().push_back(txcombine);
2695 txgather->getSequence().push_back(argCoord);
2696
2697 // AST wants an array of 4 offsets, where HLSL has separate args. Here
2698 // we construct an array from the separate args.
2699 if (hasOffset4) {
2700 TType arrayType(EbtInt, EvqTemporary, 2);
2701 TArraySizes arraySizes;
2702 arraySizes.addInnerSize(4);
2703 arrayType.newArraySizes(arraySizes);
2704
2705 TIntermAggregate* initList = new TIntermAggregate(EOpNull);
2706
2707 for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
2708 initList->getSequence().push_back(argOffsets[offsetNum]);
2709
2710 argOffset = addConstructor(loc, initList, arrayType);
2711 }
2712
2713 // Add comparison value if we have one
2714 if (argTex->getType().getSampler().isShadow())
2715 txgather->getSequence().push_back(argCmp);
2716
2717 // Add offset (either 1, or an array of 4) if we have one
2718 if (argOffset != nullptr)
2719 txgather->getSequence().push_back(argOffset);
2720
2721 txgather->getSequence().push_back(argChannel);
2722
2723 txgather->setType(node->getType());
2724 txgather->setLoc(loc);
2725 node = txgather;
2726
2727 break;
2728 }
2729
steve-lunarg68f2c142016-07-26 08:57:53 -06002730 case EOpMethodCalculateLevelOfDetail:
2731 case EOpMethodCalculateLevelOfDetailUnclamped:
2732 {
2733 TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
2734 TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
2735 TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
2736
2737 TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod);
2738
2739 TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
2740 txquerylod->getSequence().push_back(txcombine);
2741 txquerylod->getSequence().push_back(argCoord);
2742
2743 TIntermTyped* lodComponent = intermediate.addConstantUnion(0, loc, true);
2744 TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc);
2745 lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1));
2746
2747 node = lodComponentIdx;
2748
2749 // We cannot currently obtain the unclamped LOD
2750 if (op == EOpMethodCalculateLevelOfDetailUnclamped)
2751 error(loc, "unimplemented: CalculateLevelOfDetailUnclamped", "", "");
2752
2753 break;
2754 }
2755
2756 case EOpMethodGetSamplePosition:
2757 {
2758 error(loc, "unimplemented: GetSamplePosition", "", "");
2759 break;
2760 }
2761
LoopDawg4624a022016-06-20 13:26:59 -06002762 default:
2763 break; // most pass through unchanged
2764 }
2765}
2766
2767//
steve-lunargf49cdf42016-11-17 15:04:20 -07002768// Decompose geometry shader methods
2769//
2770void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
2771{
2772 if (!node || !node->getAsOperator())
2773 return;
2774
2775 const TOperator op = node->getAsOperator()->getOp();
2776 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
2777
2778 switch (op) {
2779 case EOpMethodAppend:
2780 if (argAggregate) {
2781 TIntermAggregate* sequence = nullptr;
2782 TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex);
2783
2784 emit->setLoc(loc);
2785 emit->setType(TType(EbtVoid));
2786
2787 sequence = intermediate.growAggregate(sequence,
John Kessenichecba76f2017-01-06 00:34:48 -07002788 handleAssign(loc, EOpAssign,
steve-lunarga2e75312016-12-14 15:22:25 -07002789 argAggregate->getSequence()[0]->getAsTyped(),
2790 argAggregate->getSequence()[1]->getAsTyped()),
steve-lunargf49cdf42016-11-17 15:04:20 -07002791 loc);
2792
2793 sequence = intermediate.growAggregate(sequence, emit);
2794
2795 sequence->setOperator(EOpSequence);
2796 sequence->setLoc(loc);
2797 sequence->setType(TType(EbtVoid));
2798 node = sequence;
2799 }
2800 break;
2801
2802 case EOpMethodRestartStrip:
2803 {
2804 TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive);
2805 cut->setLoc(loc);
2806 cut->setType(TType(EbtVoid));
2807 node = cut;
2808 }
2809 break;
2810
2811 default:
2812 break; // most pass through unchanged
2813 }
2814}
2815
2816//
LoopDawg592860c2016-06-09 08:57:35 -06002817// Optionally decompose intrinsics to AST opcodes.
2818//
2819void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
2820{
steve-lunarg22322362016-10-19 10:15:25 -06002821 // Helper to find image data for image atomics:
2822 // OpImageLoad(image[idx])
2823 // We take the image load apart and add its params to the atomic op aggregate node
2824 const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) {
2825 TIntermAggregate* loadOp = load->getAsAggregate();
2826 if (loadOp == nullptr) {
2827 error(loc, "unknown image type in atomic operation", "", "");
2828 node = nullptr;
2829 return;
2830 }
2831
2832 atomic->getSequence().push_back(loadOp->getSequence()[0]);
2833 atomic->getSequence().push_back(loadOp->getSequence()[1]);
2834 };
2835
2836 // Return true if this is an imageLoad, which we will change to an image atomic.
baldurkca735702016-10-28 17:57:25 +02002837 const auto isImageParam = [](TIntermTyped* image) -> bool {
steve-lunarg22322362016-10-19 10:15:25 -06002838 TIntermAggregate* imageAggregate = image->getAsAggregate();
2839 return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad;
2840 };
2841
LoopDawg592860c2016-06-09 08:57:35 -06002842 // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST
2843 // opcodes for compatibility with existing software stacks.
2844 static const bool decomposeHlslIntrinsics = true;
2845
2846 if (!decomposeHlslIntrinsics || !node || !node->getAsOperator())
2847 return;
John Kessenichecba76f2017-01-06 00:34:48 -07002848
LoopDawg592860c2016-06-09 08:57:35 -06002849 const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
2850 TIntermUnary* fnUnary = node->getAsUnaryNode();
2851 const TOperator op = node->getAsOperator()->getOp();
2852
2853 switch (op) {
2854 case EOpGenMul:
2855 {
2856 // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul
steve-lunarg297ae212016-08-24 14:36:13 -06002857 // Since we are treating HLSL rows like GLSL columns (the first matrix indirection),
2858 // we must reverse the operand order here. Hence, arg0 gets sequence[1], etc.
2859 TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped();
2860 TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped();
LoopDawg592860c2016-06-09 08:57:35 -06002861
2862 if (arg0->isVector() && arg1->isVector()) { // vec * vec
2863 node->getAsAggregate()->setOperator(EOpDot);
2864 } else {
2865 node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1);
2866 }
2867
2868 break;
2869 }
2870
2871 case EOpRcp:
2872 {
2873 // rcp(a) -> 1 / a
2874 TIntermTyped* arg0 = fnUnary->getOperand();
2875 TBasicType type0 = arg0->getBasicType();
2876 TIntermTyped* one = intermediate.addConstantUnion(1, type0, loc, true);
2877 node = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0);
2878
2879 break;
2880 }
2881
2882 case EOpSaturate:
2883 {
2884 // saturate(a) -> clamp(a,0,1)
2885 TIntermTyped* arg0 = fnUnary->getOperand();
2886 TBasicType type0 = arg0->getBasicType();
2887 TIntermAggregate* clamp = new TIntermAggregate(EOpClamp);
2888
2889 clamp->getSequence().push_back(arg0);
2890 clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true));
2891 clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true));
2892 clamp->setLoc(loc);
2893 clamp->setType(node->getType());
LoopDawg58910702016-06-13 09:22:28 -06002894 clamp->getWritableType().getQualifier().makeTemporary();
LoopDawg592860c2016-06-09 08:57:35 -06002895 node = clamp;
2896
2897 break;
2898 }
2899
2900 case EOpSinCos:
2901 {
2902 // sincos(a,b,c) -> b = sin(a), c = cos(a)
2903 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
2904 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
2905 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();
2906
2907 TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0);
2908 TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0);
2909 TIntermTyped* sinAssign = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc);
2910 TIntermTyped* cosAssign = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc);
2911
2912 TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc);
2913 compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign);
2914 compoundStatement->setOperator(EOpSequence);
2915 compoundStatement->setLoc(loc);
LoopDawg4624a022016-06-20 13:26:59 -06002916 compoundStatement->setType(TType(EbtVoid));
LoopDawg592860c2016-06-09 08:57:35 -06002917
2918 node = compoundStatement;
2919
2920 break;
2921 }
2922
2923 case EOpClip:
2924 {
2925 // clip(a) -> if (any(a<0)) discard;
2926 TIntermTyped* arg0 = fnUnary->getOperand();
2927 TBasicType type0 = arg0->getBasicType();
2928 TIntermTyped* compareNode = nullptr;
2929
2930 // For non-scalars: per experiment with FXC compiler, discard if any component < 0.
2931 if (!arg0->isScalar()) {
2932 // component-wise compare: a < 0
2933 TIntermAggregate* less = new TIntermAggregate(EOpLessThan);
2934 less->getSequence().push_back(arg0);
2935 less->setLoc(loc);
2936
2937 // make vec or mat of bool matching dimensions of input
2938 less->setType(TType(EbtBool, EvqTemporary,
2939 arg0->getType().getVectorSize(),
2940 arg0->getType().getMatrixCols(),
2941 arg0->getType().getMatrixRows(),
2942 arg0->getType().isVector()));
2943
2944 // calculate # of components for comparison const
John Kessenichecba76f2017-01-06 00:34:48 -07002945 const int constComponentCount =
LoopDawg592860c2016-06-09 08:57:35 -06002946 std::max(arg0->getType().getVectorSize(), 1) *
2947 std::max(arg0->getType().getMatrixCols(), 1) *
2948 std::max(arg0->getType().getMatrixRows(), 1);
2949
2950 TConstUnion zero;
2951 zero.setDConst(0.0);
2952 TConstUnionArray zeros(constComponentCount, zero);
2953
2954 less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true));
2955
2956 compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool));
2957 } else {
2958 TIntermTyped* zero = intermediate.addConstantUnion(0, type0, loc, true);
2959 compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero);
2960 }
John Kessenichecba76f2017-01-06 00:34:48 -07002961
LoopDawg592860c2016-06-09 08:57:35 -06002962 TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc);
2963
2964 node = new TIntermSelection(compareNode, killNode, nullptr);
2965 node->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07002966
LoopDawg592860c2016-06-09 08:57:35 -06002967 break;
2968 }
2969
2970 case EOpLog10:
2971 {
2972 // log10(a) -> log2(a) * 0.301029995663981 (== 1/log2(10))
2973 TIntermTyped* arg0 = fnUnary->getOperand();
2974 TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0);
2975 TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true);
2976
2977 node = handleBinaryMath(loc, "mul", EOpMul, log2, base);
2978
2979 break;
2980 }
2981
2982 case EOpDst:
2983 {
2984 // dest.x = 1;
2985 // dest.y = src0.y * src1.y;
2986 // dest.z = src0.z;
2987 // dest.w = src1.w;
2988
2989 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
2990 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
LoopDawg592860c2016-06-09 08:57:35 -06002991
LoopDawg592860c2016-06-09 08:57:35 -06002992 TIntermTyped* y = intermediate.addConstantUnion(1, loc, true);
2993 TIntermTyped* z = intermediate.addConstantUnion(2, loc, true);
2994 TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
2995
2996 TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc);
2997 TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc);
2998 TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc);
2999 TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
3000
3001 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
3002
3003 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
3004 dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y));
3005 dst->getSequence().push_back(src0z);
3006 dst->getSequence().push_back(src1w);
LoopDawg6e72fdd2016-06-15 09:50:24 -06003007 dst->setType(TType(EbtFloat, EvqTemporary, 4));
LoopDawg592860c2016-06-09 08:57:35 -06003008 dst->setLoc(loc);
3009 node = dst;
3010
3011 break;
3012 }
3013
LoopDawg58910702016-06-13 09:22:28 -06003014 case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value
3015 case EOpInterlockedMin: // ...
3016 case EOpInterlockedMax: // ...
3017 case EOpInterlockedAnd: // ...
3018 case EOpInterlockedOr: // ...
3019 case EOpInterlockedXor: // ...
3020 case EOpInterlockedExchange: // always has output arg
3021 {
steve-lunarg22322362016-10-19 10:15:25 -06003022 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest
3023 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // value
3024 TIntermTyped* arg2 = nullptr;
LoopDawg58910702016-06-13 09:22:28 -06003025
steve-lunarg22322362016-10-19 10:15:25 -06003026 if (argAggregate->getSequence().size() > 2)
3027 arg2 = argAggregate->getSequence()[2]->getAsTyped();
3028
3029 const bool isImage = isImageParam(arg0);
LoopDawg58910702016-06-13 09:22:28 -06003030 const TOperator atomicOp = mapAtomicOp(loc, op, isImage);
steve-lunarg22322362016-10-19 10:15:25 -06003031 TIntermAggregate* atomic = new TIntermAggregate(atomicOp);
3032 atomic->setType(arg0->getType());
3033 atomic->getWritableType().getQualifier().makeTemporary();
3034 atomic->setLoc(loc);
John Kessenichecba76f2017-01-06 00:34:48 -07003035
steve-lunarg22322362016-10-19 10:15:25 -06003036 if (isImage) {
3037 // orig_value = imageAtomicOp(image, loc, data)
3038 imageAtomicParams(atomic, arg0);
LoopDawg58910702016-06-13 09:22:28 -06003039 atomic->getSequence().push_back(arg1);
LoopDawg58910702016-06-13 09:22:28 -06003040
steve-lunarg22322362016-10-19 10:15:25 -06003041 if (argAggregate->getSequence().size() > 2) {
3042 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
3043 } else {
3044 node = atomic; // no assignment needed, as there was no out var.
3045 }
LoopDawg58910702016-06-13 09:22:28 -06003046 } else {
steve-lunarg22322362016-10-19 10:15:25 -06003047 // Normal memory variable:
3048 // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value
3049 if (argAggregate->getSequence().size() > 2) {
3050 // optional output param is present. return value goes to arg2.
3051 atomic->getSequence().push_back(arg0);
3052 atomic->getSequence().push_back(arg1);
3053
3054 node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
3055 } else {
3056 // Set the matching operator. Since output is absent, this is all we need to do.
3057 node->getAsAggregate()->setOperator(atomicOp);
3058 }
LoopDawg58910702016-06-13 09:22:28 -06003059 }
3060
3061 break;
3062 }
3063
3064 case EOpInterlockedCompareExchange:
3065 {
3066 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest
3067 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // cmp
3068 TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); // value
3069 TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped(); // orig
3070
steve-lunarg22322362016-10-19 10:15:25 -06003071 const bool isImage = isImageParam(arg0);
LoopDawg58910702016-06-13 09:22:28 -06003072 TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage));
LoopDawg58910702016-06-13 09:22:28 -06003073 atomic->setLoc(loc);
3074 atomic->setType(arg2->getType());
3075 atomic->getWritableType().getQualifier().makeTemporary();
3076
steve-lunarg22322362016-10-19 10:15:25 -06003077 if (isImage) {
3078 imageAtomicParams(atomic, arg0);
3079 } else {
3080 atomic->getSequence().push_back(arg0);
3081 }
3082
3083 atomic->getSequence().push_back(arg1);
3084 atomic->getSequence().push_back(arg2);
LoopDawg58910702016-06-13 09:22:28 -06003085 node = intermediate.addAssign(EOpAssign, arg3, atomic, loc);
John Kessenichecba76f2017-01-06 00:34:48 -07003086
LoopDawg58910702016-06-13 09:22:28 -06003087 break;
3088 }
3089
LoopDawg6e72fdd2016-06-15 09:50:24 -06003090 case EOpEvaluateAttributeSnapped:
3091 {
3092 // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels
3093 // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y:
3094 // iU = (iU<<28)>>28
3095 // fU = ((float)iU)/16
3096 // Targets might handle this natively, in which case they can disable
3097 // decompositions.
3098
3099 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // value
3100 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // offset
3101
3102 TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true);
3103 TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift,
3104 handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28),
3105 i28);
3106
3107 TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true);
3108 TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul,
3109 intermediate.addConversion(EOpConstructFloat,
3110 TType(EbtFloat, EvqTemporary, 2), iU),
3111 recip16);
John Kessenichecba76f2017-01-06 00:34:48 -07003112
LoopDawg6e72fdd2016-06-15 09:50:24 -06003113 TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset);
3114 interp->getSequence().push_back(arg0);
3115 interp->getSequence().push_back(floatOffset);
3116 interp->setLoc(loc);
3117 interp->setType(arg0->getType());
3118 interp->getWritableType().getQualifier().makeTemporary();
3119
3120 node = interp;
3121
3122 break;
3123 }
3124
3125 case EOpLit:
3126 {
3127 TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped();
3128 TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped();
3129 TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped();
3130
3131 TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
3132
3133 // Ambient
3134 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
3135
3136 // Diffuse:
3137 TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
3138 TIntermAggregate* diffuse = new TIntermAggregate(EOpMax);
3139 diffuse->getSequence().push_back(n_dot_l);
3140 diffuse->getSequence().push_back(zero);
3141 diffuse->setLoc(loc);
3142 diffuse->setType(TType(EbtFloat));
3143 dst->getSequence().push_back(diffuse);
3144
3145 // Specular:
3146 TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin);
3147 min_ndot->getSequence().push_back(n_dot_l);
3148 min_ndot->getSequence().push_back(n_dot_h);
3149 min_ndot->setLoc(loc);
3150 min_ndot->setType(TType(EbtFloat));
3151
3152 TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero);
3153 TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m); // n_dot_h * m
3154
3155 dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc));
John Kessenichecba76f2017-01-06 00:34:48 -07003156
LoopDawg6e72fdd2016-06-15 09:50:24 -06003157 // One:
3158 dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
3159
3160 dst->setLoc(loc);
3161 dst->setType(TType(EbtFloat, EvqTemporary, 4));
3162 node = dst;
3163 break;
3164 }
3165
LoopDawg1b7fd0f2016-06-22 15:20:14 -06003166 case EOpAsDouble:
3167 {
3168 // asdouble accepts two 32 bit ints. we can use EOpUint64BitsToDouble, but must
3169 // first construct a uint64.
3170 TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
3171 TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
3172
3173 if (arg0->getType().isVector()) { // TODO: ...
3174 error(loc, "double2 conversion not implemented", "asdouble", "");
3175 break;
3176 }
3177
3178 TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2);
3179
3180 uint64->getSequence().push_back(arg0);
3181 uint64->getSequence().push_back(arg1);
3182 uint64->setType(TType(EbtUint, EvqTemporary, 2)); // convert 2 uints to a uint2
3183 uint64->setLoc(loc);
3184
3185 // bitcast uint2 to a double
3186 TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble);
3187 convert->getAsUnaryNode()->setOperand(uint64);
3188 convert->setLoc(loc);
3189 convert->setType(TType(EbtDouble, EvqTemporary));
3190 node = convert;
John Kessenichecba76f2017-01-06 00:34:48 -07003191
LoopDawg1b7fd0f2016-06-22 15:20:14 -06003192 break;
3193 }
John Kessenichecba76f2017-01-06 00:34:48 -07003194
LoopDawg6e72fdd2016-06-15 09:50:24 -06003195 case EOpF16tof32:
3196 case EOpF32tof16:
3197 {
3198 // Temporary until decomposition is available.
3199 error(loc, "unimplemented intrinsic: handle natively", "f32tof16", "");
3200 break;
3201 }
3202
steve-lunarg7ea7ff42017-01-03 14:42:18 -07003203 case EOpD3DCOLORtoUBYTE4:
3204 {
3205 // ivec4 ( x.zyxw * 255.001953 );
3206 TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
John Kessenichc142c882017-01-13 19:34:22 -07003207 TSwizzleSelectors<TVectorSelector> selectors;
3208 selectors.push_back(2);
3209 selectors.push_back(1);
3210 selectors.push_back(0);
3211 selectors.push_back(3);
3212 TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc);
steve-lunarg7ea7ff42017-01-03 14:42:18 -07003213 TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc);
3214 swizzled->setType(arg0->getType());
3215 swizzled->getWritableType().getQualifier().makeTemporary();
3216
3217 TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true);
3218 TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled);
3219 rangeConverted->setType(arg0->getType());
3220 rangeConverted->getWritableType().getQualifier().makeTemporary();
3221
3222 node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted);
3223 node->setLoc(loc);
3224 node->setType(TType(EbtInt, EvqTemporary, 4));
3225 break;
3226 }
3227
LoopDawg592860c2016-06-09 08:57:35 -06003228 default:
3229 break; // most pass through unchanged
3230 }
3231}
3232
John Kesseniche01a9bc2016-03-12 20:11:22 -07003233//
3234// Handle seeing function call syntax in the grammar, which could be any of
3235// - .length() method
3236// - constructor
3237// - a call to a built-in function mapped to an operator
3238// - a call to a built-in function that will remain a function call (e.g., texturing)
3239// - user function
3240// - subroutine call (not implemented yet)
3241//
steve-lunarg26d31452016-12-23 18:56:57 -07003242TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments)
John Kesseniche01a9bc2016-03-12 20:11:22 -07003243{
3244 TIntermTyped* result = nullptr;
3245
3246 TOperator op = function->getBuiltInOp();
3247 if (op == EOpArrayLength)
3248 result = handleLengthMethod(loc, function, arguments);
3249 else if (op != EOpNull) {
3250 //
3251 // Then this should be a constructor.
3252 // Don't go through the symbol table for constructors.
3253 // Their parameters will be verified algorithmically.
3254 //
3255 TType type(EbtVoid); // use this to get the type back
3256 if (! constructorError(loc, arguments, *function, op, type)) {
3257 //
3258 // It's a constructor, of type 'type'.
3259 //
John Kessenicha26a5172016-07-28 15:29:35 -06003260 result = addConstructor(loc, arguments, type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003261 if (result == nullptr)
3262 error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), "");
3263 }
3264 } else {
3265 //
3266 // Find it in the symbol table.
3267 //
3268 const TFunction* fnCandidate;
3269 bool builtIn;
steve-lunargef33ec02016-11-02 12:42:34 -06003270 fnCandidate = findFunction(loc, *function, builtIn, arguments);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003271 if (fnCandidate) {
3272 // This is a declared function that might map to
3273 // - a built-in operator,
3274 // - a built-in function not mapped to an operator, or
3275 // - a user function.
3276
3277 // Error check for a function requiring specific extensions present.
3278 if (builtIn && fnCandidate->getNumExtensions())
3279 requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str());
3280
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003281 // Convert 'in' arguments
3282 if (arguments)
3283 addInputArgumentConversions(*fnCandidate, arguments);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003284
3285 op = fnCandidate->getBuiltInOp();
3286 if (builtIn && op != EOpNull) {
3287 // A function call mapped to a built-in operation.
3288 result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments, fnCandidate->getType());
3289 if (result == nullptr) {
3290 error(arguments->getLoc(), " wrong operand type", "Internal Error",
3291 "built in unary operator function. Type: %s",
3292 static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str());
3293 } else if (result->getAsOperator()) {
3294 builtInOpCheck(loc, *fnCandidate, *result->getAsOperator());
3295 }
3296 } else {
3297 // This is a function call not mapped to built-in operator.
3298 // It could still be a built-in function, but only if PureOperatorBuiltins == false.
3299 result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc);
3300 TIntermAggregate* call = result->getAsAggregate();
3301 call->setName(fnCandidate->getMangledName());
3302
3303 // this is how we know whether the given function is a built-in function or a user-defined function
3304 // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also
3305 // if builtIn == true, it's definitely a built-in function with EOpNull
3306 if (! builtIn) {
3307 call->setUserDefined();
3308 intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName());
3309 }
3310 }
3311
steve-lunargef33ec02016-11-02 12:42:34 -06003312 // for decompositions, since we want to operate on the function node, not the aggregate holding
3313 // output conversions.
John Kessenichecba76f2017-01-06 00:34:48 -07003314 const TIntermTyped* fnNode = result;
steve-lunargef33ec02016-11-02 12:42:34 -06003315
3316 decomposeIntrinsic(loc, result, arguments); // HLSL->AST intrinsic decompositions
3317 decomposeSampleMethods(loc, result, arguments); // HLSL->AST sample method decompositions
3318 decomposeGeometryMethods(loc, result, arguments); // HLSL->AST geometry method decompositions
3319
John Kesseniche01a9bc2016-03-12 20:11:22 -07003320 // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore.
3321 // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output.
3322 // Also, build the qualifier list for user function calls, which are always called with an aggregate.
steve-lunargef33ec02016-11-02 12:42:34 -06003323 // We don't do this is if there has been a decomposition, which will have added its own conversions
3324 // for output parameters.
3325 if (result == fnNode && result->getAsAggregate()) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07003326 TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList();
3327 for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
3328 TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage;
3329 qualifierList.push_back(qual);
3330 }
steve-lunargef33ec02016-11-02 12:42:34 -06003331 result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator());
John Kesseniche01a9bc2016-03-12 20:11:22 -07003332 }
3333 }
3334 }
3335
3336 // generic error recovery
3337 // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to reduce cascades
3338 if (result == nullptr)
3339 result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
3340
3341 return result;
3342}
3343
3344// Finish processing object.length(). This started earlier in handleDotDereference(), where
John Kessenichecba76f2017-01-06 00:34:48 -07003345// the ".length" part was recognized and semantically checked, and finished here where the
John Kesseniche01a9bc2016-03-12 20:11:22 -07003346// function syntax "()" is recognized.
3347//
3348// Return resulting tree node.
3349TIntermTyped* HlslParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction* function, TIntermNode* intermNode)
3350{
3351 int length = 0;
3352
3353 if (function->getParamCount() > 0)
3354 error(loc, "method does not accept any arguments", function->getName().c_str(), "");
3355 else {
3356 const TType& type = intermNode->getAsTyped()->getType();
3357 if (type.isArray()) {
3358 if (type.isRuntimeSizedArray()) {
3359 // Create a unary op and let the back end handle it
3360 return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt));
John Kesseniche01a9bc2016-03-12 20:11:22 -07003361 } else
3362 length = type.getOuterArraySize();
3363 } else if (type.isMatrix())
3364 length = type.getMatrixCols();
3365 else if (type.isVector())
3366 length = type.getVectorSize();
3367 else {
3368 // we should not get here, because earlier semantic checking should have prevented this path
3369 error(loc, ".length()", "unexpected use of .length()", "");
3370 }
3371 }
3372
3373 if (length == 0)
3374 length = 1;
3375
3376 return intermediate.addConstantUnion(length, loc);
3377}
3378
3379//
3380// Add any needed implicit conversions for function-call arguments to input parameters.
3381//
steve-lunarg26d31452016-12-23 18:56:57 -07003382void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments)
John Kesseniche01a9bc2016-03-12 20:11:22 -07003383{
3384 TIntermAggregate* aggregate = arguments->getAsAggregate();
steve-lunarg26d31452016-12-23 18:56:57 -07003385 const auto setArg = [&](int argNum, TIntermTyped* arg) {
John Kessenich5159d4f2016-09-19 00:06:19 -06003386 if (function.getParamCount() == 1)
3387 arguments = arg;
3388 else {
3389 if (aggregate)
3390 aggregate->getSequence()[argNum] = arg;
3391 else
3392 arguments = arg;
3393 }
3394 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07003395
3396 // Process each argument's conversion
3397 for (int i = 0; i < function.getParamCount(); ++i) {
John Kessenichc86d38b2016-10-01 13:30:37 -06003398 if (! function[i].type->getQualifier().isParamInput())
3399 continue;
3400
John Kesseniche01a9bc2016-03-12 20:11:22 -07003401 // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
3402 // is the single argument itself or its children are the arguments. Only one argument
3403 // means take 'arguments' itself as the one argument.
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003404 TIntermTyped* arg = function.getParamCount() == 1
3405 ? arguments->getAsTyped()
3406 : (aggregate ? aggregate->getSequence()[i]->getAsTyped() : arguments->getAsTyped());
John Kesseniche01a9bc2016-03-12 20:11:22 -07003407 if (*function[i].type != arg->getType()) {
John Kessenichc86d38b2016-10-01 13:30:37 -06003408 // In-qualified arguments just need an extra node added above the argument to
3409 // convert to the correct type.
John Kessenich1e275c82016-12-14 17:02:32 -07003410 TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[i].type, arg);
3411 if (convArg != nullptr)
3412 convArg = intermediate.addShapeConversion(EOpFunctionCall, *function[i].type, convArg);
3413 if (convArg != nullptr)
3414 setArg(i, convArg);
3415 else
3416 error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", i);
John Kessenich5159d4f2016-09-19 00:06:19 -06003417 } else {
steve-lunarga2e75312016-12-14 15:22:25 -07003418 if (wasFlattened(arg) || wasSplit(arg)) {
John Kessenich6714bcc2016-09-21 17:50:12 -06003419 // Will make a two-level subtree.
3420 // The deepest will copy member-by-member to build the structure to pass.
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003421 // The level above that will be a two-operand EOpComma sequence that follows the copy by the
John Kessenich6714bcc2016-09-21 17:50:12 -06003422 // object itself.
John Kessenich5159d4f2016-09-19 00:06:19 -06003423 TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[i].type);
John Kessenich28b28142016-09-19 00:19:49 -06003424 internalAggregate->getWritableType().getQualifier().makeTemporary();
John Kessenichecba76f2017-01-06 00:34:48 -07003425 TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(),
John Kessenich5159d4f2016-09-19 00:06:19 -06003426 internalAggregate->getName(),
3427 internalAggregate->getType());
John Kessenicha08c9292016-10-01 17:17:55 -06003428 internalSymbolNode->setLoc(arg->getLoc());
John Kessenich6714bcc2016-09-21 17:50:12 -06003429 // This makes the deepest level, the member-wise copy
John Kessenicha08c9292016-10-01 17:17:55 -06003430 TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign, internalSymbolNode, arg)->getAsAggregate();
John Kessenich6714bcc2016-09-21 17:50:12 -06003431
3432 // Now, pair that with the resulting aggregate.
John Kessenicha08c9292016-10-01 17:17:55 -06003433 assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc());
John Kessenich5159d4f2016-09-19 00:06:19 -06003434 assignAgg->setOperator(EOpComma);
John Kessenich6714bcc2016-09-21 17:50:12 -06003435 assignAgg->setType(internalAggregate->getType());
John Kessenich5159d4f2016-09-19 00:06:19 -06003436 setArg(i, assignAgg);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003437 }
3438 }
3439 }
3440}
3441
3442//
3443// Add any needed implicit output conversions for function-call arguments. This
3444// can require a new tree topology, complicated further by whether the function
3445// has a return value.
3446//
3447// Returns a node of a subtree that evaluates to the return value of the function.
3448//
steve-lunargef33ec02016-11-02 12:42:34 -06003449TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode)
John Kesseniche01a9bc2016-03-12 20:11:22 -07003450{
steve-lunargef33ec02016-11-02 12:42:34 -06003451 assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr);
3452
3453 const TSourceLoc& loc = intermNode.getLoc();
3454
3455 TIntermSequence argSequence; // temp sequence for unary node args
3456
3457 if (intermNode.getAsUnaryNode())
3458 argSequence.push_back(intermNode.getAsUnaryNode()->getOperand());
3459
3460 TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence;
3461
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003462 const auto needsConversion = [&](int argNum) {
3463 return function[argNum].type->getQualifier().isParamOutput() &&
3464 (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() ||
steve-lunarg90707962016-10-07 19:35:40 -06003465 shouldConvertLValue(arguments[argNum]) ||
steve-lunarga2b01a02016-11-28 17:09:54 -07003466 wasFlattened(arguments[argNum]->getAsTyped()));
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003467 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07003468
3469 // Will there be any output conversions?
3470 bool outputConversions = false;
3471 for (int i = 0; i < function.getParamCount(); ++i) {
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003472 if (needsConversion(i)) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07003473 outputConversions = true;
3474 break;
3475 }
3476 }
3477
3478 if (! outputConversions)
3479 return &intermNode;
3480
3481 // Setup for the new tree, if needed:
3482 //
3483 // Output conversions need a different tree topology.
3484 // Out-qualified arguments need a temporary of the correct type, with the call
3485 // followed by an assignment of the temporary to the original argument:
3486 // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...)
3487 // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet)
3488 // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment.
3489 TIntermTyped* conversionTree = nullptr;
3490 TVariable* tempRet = nullptr;
3491 if (intermNode.getBasicType() != EbtVoid) {
3492 // do the "tempRet = function(...), " bit from above
3493 tempRet = makeInternalVariable("tempReturn", intermNode.getType());
steve-lunargef33ec02016-11-02 12:42:34 -06003494 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
3495 conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003496 } else
3497 conversionTree = &intermNode;
3498
3499 conversionTree = intermediate.makeAggregate(conversionTree);
3500
3501 // Process each argument's conversion
3502 for (int i = 0; i < function.getParamCount(); ++i) {
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003503 if (needsConversion(i)) {
3504 // Out-qualified arguments needing conversion need to use the topology setup above.
3505 // Do the " ...(tempArg, ...), arg = tempArg" bit from above.
3506
3507 // Make a temporary for what the function expects the argument to look like.
3508 TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type);
3509 tempArg->getWritableType().getQualifier().makeTemporary();
steve-lunargef33ec02016-11-02 12:42:34 -06003510 TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc);
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003511
3512 // This makes the deepest level, the member-wise copy
steve-lunarg90707962016-10-07 19:35:40 -06003513 TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(), tempArgNode);
3514 tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign);
John Kessenichd8fe2ca2016-10-01 17:11:21 -06003515 conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc());
3516
3517 // replace the argument with another node for the same tempArg variable
steve-lunargef33ec02016-11-02 12:42:34 -06003518 arguments[i] = intermediate.addSymbol(*tempArg, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003519 }
3520 }
3521
3522 // Finalize the tree topology (see bigger comment above).
3523 if (tempRet) {
3524 // do the "..., tempRet" bit from above
steve-lunargef33ec02016-11-02 12:42:34 -06003525 TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
3526 conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003527 }
steve-lunargef33ec02016-11-02 12:42:34 -06003528
3529 conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003530
3531 return conversionTree;
3532}
3533
3534//
3535// Do additional checking of built-in function calls that is not caught
3536// by normal semantic checks on argument type, extension tagging, etc.
3537//
3538// Assumes there has been a semantically correct match to a built-in function prototype.
3539//
3540void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode)
3541{
3542 // Set up convenience accessors to the argument(s). There is almost always
3543 // multiple arguments for the cases below, but when there might be one,
3544 // check the unaryArg first.
3545 const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference
3546 const TIntermTyped* unaryArg = nullptr;
3547 const TIntermTyped* arg0 = nullptr;
3548 if (callNode.getAsAggregate()) {
3549 argp = &callNode.getAsAggregate()->getSequence();
3550 if (argp->size() > 0)
3551 arg0 = (*argp)[0]->getAsTyped();
3552 } else {
3553 assert(callNode.getAsUnaryNode());
3554 unaryArg = callNode.getAsUnaryNode()->getOperand();
3555 arg0 = unaryArg;
3556 }
3557 const TIntermSequence& aggArgs = *argp; // only valid when unaryArg is nullptr
3558
John Kesseniche01a9bc2016-03-12 20:11:22 -07003559 switch (callNode.getOp()) {
3560 case EOpTextureGather:
3561 case EOpTextureGatherOffset:
3562 case EOpTextureGatherOffsets:
3563 {
3564 // Figure out which variants are allowed by what extensions,
3565 // and what arguments must be constant for which situations.
3566
3567 TString featureString = fnCandidate.getName() + "(...)";
3568 const char* feature = featureString.c_str();
3569 int compArg = -1; // track which argument, if any, is the constant component argument
3570 switch (callNode.getOp()) {
3571 case EOpTextureGather:
3572 // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
3573 // otherwise, need GL_ARB_texture_gather.
3574 if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) {
3575 if (! fnCandidate[0].type->getSampler().shadow)
3576 compArg = 2;
3577 }
3578 break;
3579 case EOpTextureGatherOffset:
3580 // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
3581 if (! fnCandidate[0].type->getSampler().shadow)
3582 compArg = 3;
3583 break;
3584 case EOpTextureGatherOffsets:
3585 if (! fnCandidate[0].type->getSampler().shadow)
3586 compArg = 3;
3587 break;
3588 default:
3589 break;
3590 }
3591
3592 if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
3593 if (aggArgs[compArg]->getAsConstantUnion()) {
3594 int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
3595 if (value < 0 || value > 3)
3596 error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
3597 } else
3598 error(loc, "must be a compile-time constant:", feature, "component argument");
3599 }
3600
3601 break;
3602 }
3603
3604 case EOpTextureOffset:
3605 case EOpTextureFetchOffset:
3606 case EOpTextureProjOffset:
3607 case EOpTextureLodOffset:
3608 case EOpTextureProjLodOffset:
3609 case EOpTextureGradOffset:
3610 case EOpTextureProjGradOffset:
3611 {
3612 // Handle texture-offset limits checking
3613 // Pick which argument has to hold constant offsets
3614 int arg = -1;
3615 switch (callNode.getOp()) {
3616 case EOpTextureOffset: arg = 2; break;
3617 case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break;
3618 case EOpTextureProjOffset: arg = 2; break;
3619 case EOpTextureLodOffset: arg = 3; break;
3620 case EOpTextureProjLodOffset: arg = 3; break;
3621 case EOpTextureGradOffset: arg = 4; break;
3622 case EOpTextureProjGradOffset: arg = 4; break;
3623 default:
3624 assert(0);
3625 break;
3626 }
3627
3628 if (arg > 0) {
3629 if (! aggArgs[arg]->getAsConstantUnion())
3630 error(loc, "argument must be compile-time constant", "texel offset", "");
3631 else {
3632 const TType& type = aggArgs[arg]->getAsTyped()->getType();
3633 for (int c = 0; c < type.getVectorSize(); ++c) {
3634 int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
3635 if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
3636 error(loc, "value is out of range:", "texel offset", "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
3637 }
3638 }
3639 }
3640
3641 break;
3642 }
3643
3644 case EOpTextureQuerySamples:
3645 case EOpImageQuerySamples:
3646 break;
3647
3648 case EOpImageAtomicAdd:
3649 case EOpImageAtomicMin:
3650 case EOpImageAtomicMax:
3651 case EOpImageAtomicAnd:
3652 case EOpImageAtomicOr:
3653 case EOpImageAtomicXor:
3654 case EOpImageAtomicExchange:
3655 case EOpImageAtomicCompSwap:
3656 break;
3657
3658 case EOpInterpolateAtCentroid:
3659 case EOpInterpolateAtSample:
3660 case EOpInterpolateAtOffset:
John Kesseniche01a9bc2016-03-12 20:11:22 -07003661 // Make sure the first argument is an interpolant, or an array element of an interpolant
3662 if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
3663 // It might still be an array element.
3664 //
3665 // We could check more, but the semantics of the first argument are already met; the
3666 // only way to turn an array into a float/vec* is array dereference and swizzle.
3667 //
3668 // ES and desktop 4.3 and earlier: swizzles may not be used
3669 // desktop 4.4 and later: swizzles may be used
3670 const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true);
3671 if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
3672 error(loc, "first argument must be an interpolant, or interpolant-array element", fnCandidate.getName().c_str(), "");
3673 }
3674 break;
3675
3676 default:
3677 break;
3678 }
3679}
3680
3681//
3682// Handle seeing a built-in constructor in a grammar production.
3683//
John Kessenichd016be12016-03-13 11:24:20 -06003684TFunction* HlslParseContext::handleConstructorCall(const TSourceLoc& loc, const TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07003685{
John Kessenicha26a5172016-07-28 15:29:35 -06003686 TOperator op = intermediate.mapTypeToConstructorOp(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07003687
3688 if (op == EOpNull) {
3689 error(loc, "cannot construct this type", type.getBasicString(), "");
John Kessenichd016be12016-03-13 11:24:20 -06003690 return nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07003691 }
3692
3693 TString empty("");
3694
3695 return new TFunction(&empty, type, op);
3696}
3697
3698//
John Kessenich630dd7d2016-06-12 23:52:12 -06003699// Handle seeing a "COLON semantic" at the end of a type declaration,
3700// by updating the type according to the semantic.
3701//
John Kessenich7735b942016-09-05 12:40:06 -06003702void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, const TString& semantic)
John Kessenich630dd7d2016-06-12 23:52:12 -06003703{
3704 // TODO: need to know if it's an input or an output
3705 // The following sketches what needs to be done, but can't be right
3706 // without taking into account stage and input/output.
Dan Baker26aa8a42016-08-25 17:13:25 -04003707
3708 TString semanticUpperCase = semantic;
3709 std::transform(semanticUpperCase.begin(), semanticUpperCase.end(), semanticUpperCase.begin(), ::toupper);
Dan Bakerdeec03c2016-08-25 11:59:17 -04003710 // in DX9, all outputs had to have a semantic associated with them, that was either consumed
3711 // by the system or was a specific register assignment
3712 // in DX10+, only semantics with the SV_ prefix have any meaning beyond decoration
3713 // Fxc will only accept DX9 style semantics in compat mode
3714 // Also, in DX10 if a SV value is present as the input of a stage, but isn't appropriate for that
3715 // stage, it would just be ignored as it is likely there as part of an output struct from one stage
3716 // to the next
John Kessenichecba76f2017-01-06 00:34:48 -07003717
Dan Baker26aa8a42016-08-25 17:13:25 -04003718 bool bParseDX9 = false;
John Kessenich81d47142016-08-29 16:07:29 -06003719 if (bParseDX9) {
Dan Baker26aa8a42016-08-25 17:13:25 -04003720 if (semanticUpperCase == "PSIZE")
John Kessenich7735b942016-09-05 12:40:06 -06003721 qualifier.builtIn = EbvPointSize;
Dan Bakerdeec03c2016-08-25 11:59:17 -04003722 else if (semantic == "FOG")
John Kessenich7735b942016-09-05 12:40:06 -06003723 qualifier.builtIn = EbvFogFragCoord;
Dan Baker26aa8a42016-08-25 17:13:25 -04003724 else if (semanticUpperCase == "DEPTH")
John Kessenich7735b942016-09-05 12:40:06 -06003725 qualifier.builtIn = EbvFragDepth;
Dan Baker26aa8a42016-08-25 17:13:25 -04003726 else if (semanticUpperCase == "VFACE")
John Kessenich7735b942016-09-05 12:40:06 -06003727 qualifier.builtIn = EbvFace;
Dan Baker26aa8a42016-08-25 17:13:25 -04003728 else if (semanticUpperCase == "VPOS")
John Kessenich7735b942016-09-05 12:40:06 -06003729 qualifier.builtIn = EbvFragCoord;
Dan Bakerdeec03c2016-08-25 11:59:17 -04003730 }
Dan Bakerdeec03c2016-08-25 11:59:17 -04003731
John Kessenich927608b2017-01-06 12:34:14 -07003732 // SV Position has a different meaning in vertex vs fragment
John Kessenich81d47142016-08-29 16:07:29 -06003733 if (semanticUpperCase == "SV_POSITION" && language != EShLangFragment)
John Kessenich7735b942016-09-05 12:40:06 -06003734 qualifier.builtIn = EbvPosition;
John Kessenich81d47142016-08-29 16:07:29 -06003735 else if (semanticUpperCase == "SV_POSITION" && language == EShLangFragment)
John Kessenich7735b942016-09-05 12:40:06 -06003736 qualifier.builtIn = EbvFragCoord;
John Kessenich81d47142016-08-29 16:07:29 -06003737 else if (semanticUpperCase == "SV_CLIPDISTANCE")
John Kessenich7735b942016-09-05 12:40:06 -06003738 qualifier.builtIn = EbvClipDistance;
John Kessenich81d47142016-08-29 16:07:29 -06003739 else if (semanticUpperCase == "SV_CULLDISTANCE")
John Kessenich7735b942016-09-05 12:40:06 -06003740 qualifier.builtIn = EbvCullDistance;
John Kessenich81d47142016-08-29 16:07:29 -06003741 else if (semanticUpperCase == "SV_VERTEXID")
John Kessenich7735b942016-09-05 12:40:06 -06003742 qualifier.builtIn = EbvVertexIndex;
John Kessenich81d47142016-08-29 16:07:29 -06003743 else if (semanticUpperCase == "SV_VIEWPORTARRAYINDEX")
John Kessenich7735b942016-09-05 12:40:06 -06003744 qualifier.builtIn = EbvViewportIndex;
John Kessenich81d47142016-08-29 16:07:29 -06003745 else if (semanticUpperCase == "SV_TESSFACTOR")
John Kessenich7735b942016-09-05 12:40:06 -06003746 qualifier.builtIn = EbvTessLevelOuter;
Dan Bakerdeec03c2016-08-25 11:59:17 -04003747
John Kessenich927608b2017-01-06 12:34:14 -07003748 // Targets are defined 0-7
John Kessenich81d47142016-08-29 16:07:29 -06003749 else if (semanticUpperCase == "SV_TARGET") {
John Kessenich7735b942016-09-05 12:40:06 -06003750 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003751 // qualifier.layoutLocation = 0;
John Kessenich81d47142016-08-29 16:07:29 -06003752 } else if (semanticUpperCase == "SV_TARGET0") {
John Kessenich7735b942016-09-05 12:40:06 -06003753 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003754 // qualifier.layoutLocation = 0;
John Kessenich81d47142016-08-29 16:07:29 -06003755 } else if (semanticUpperCase == "SV_TARGET1") {
John Kessenich7735b942016-09-05 12:40:06 -06003756 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003757 // qualifier.layoutLocation = 1;
John Kessenich81d47142016-08-29 16:07:29 -06003758 } else if (semanticUpperCase == "SV_TARGET2") {
John Kessenich7735b942016-09-05 12:40:06 -06003759 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003760 // qualifier.layoutLocation = 2;
John Kessenich81d47142016-08-29 16:07:29 -06003761 } else if (semanticUpperCase == "SV_TARGET3") {
John Kessenich7735b942016-09-05 12:40:06 -06003762 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003763 // qualifier.layoutLocation = 3;
John Kessenich81d47142016-08-29 16:07:29 -06003764 } else if (semanticUpperCase == "SV_TARGET4") {
John Kessenich7735b942016-09-05 12:40:06 -06003765 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003766 // qualifier.layoutLocation = 4;
John Kessenich81d47142016-08-29 16:07:29 -06003767 } else if (semanticUpperCase == "SV_TARGET5") {
John Kessenich7735b942016-09-05 12:40:06 -06003768 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003769 // qualifier.layoutLocation = 5;
John Kessenich81d47142016-08-29 16:07:29 -06003770 } else if (semanticUpperCase == "SV_TARGET6") {
John Kessenich7735b942016-09-05 12:40:06 -06003771 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003772 // qualifier.layoutLocation = 6;
John Kessenich81d47142016-08-29 16:07:29 -06003773 } else if (semanticUpperCase == "SV_TARGET7") {
John Kessenich7735b942016-09-05 12:40:06 -06003774 qualifier.builtIn = EbvNone;
John Kessenich927608b2017-01-06 12:34:14 -07003775 // qualifier.layoutLocation = 7;
John Kessenich81d47142016-08-29 16:07:29 -06003776 } else if (semanticUpperCase == "SV_SAMPLEINDEX")
John Kessenich7735b942016-09-05 12:40:06 -06003777 qualifier.builtIn = EbvSampleId;
John Kessenich81d47142016-08-29 16:07:29 -06003778 else if (semanticUpperCase == "SV_RENDERTARGETARRAYINDEX")
John Kessenich7735b942016-09-05 12:40:06 -06003779 qualifier.builtIn = EbvLayer;
John Kessenich81d47142016-08-29 16:07:29 -06003780 else if (semanticUpperCase == "SV_PRIMITIVEID")
John Kessenich7735b942016-09-05 12:40:06 -06003781 qualifier.builtIn = EbvPrimitiveId;
John Kessenich81d47142016-08-29 16:07:29 -06003782 else if (semanticUpperCase == "SV_OUTPUTCONTROLPOINTID")
John Kessenich7735b942016-09-05 12:40:06 -06003783 qualifier.builtIn = EbvInvocationId;
John Kessenich81d47142016-08-29 16:07:29 -06003784 else if (semanticUpperCase == "SV_ISFRONTFACE")
John Kessenich7735b942016-09-05 12:40:06 -06003785 qualifier.builtIn = EbvFace;
John Kessenich81d47142016-08-29 16:07:29 -06003786 else if (semanticUpperCase == "SV_INSTANCEID")
John Kessenich7735b942016-09-05 12:40:06 -06003787 qualifier.builtIn = EbvInstanceIndex;
John Kessenich81d47142016-08-29 16:07:29 -06003788 else if (semanticUpperCase == "SV_INSIDETESSFACTOR")
John Kessenich7735b942016-09-05 12:40:06 -06003789 qualifier.builtIn = EbvTessLevelInner;
John Kessenich81d47142016-08-29 16:07:29 -06003790 else if (semanticUpperCase == "SV_GSINSTANCEID")
John Kessenich7735b942016-09-05 12:40:06 -06003791 qualifier.builtIn = EbvInvocationId;
John Kessenichb50fd172016-10-16 12:12:11 -06003792 else if (semanticUpperCase == "SV_DISPATCHTHREADID")
John Kessenichf37f4d22017-01-02 14:59:19 -07003793 qualifier.builtIn = EbvGlobalInvocationId;
John Kessenich81d47142016-08-29 16:07:29 -06003794 else if (semanticUpperCase == "SV_GROUPTHREADID")
John Kessenich7735b942016-09-05 12:40:06 -06003795 qualifier.builtIn = EbvLocalInvocationId;
John Kessenich81d47142016-08-29 16:07:29 -06003796 else if (semanticUpperCase == "SV_GROUPID")
John Kessenich7735b942016-09-05 12:40:06 -06003797 qualifier.builtIn = EbvWorkGroupId;
John Kessenich81d47142016-08-29 16:07:29 -06003798 else if (semanticUpperCase == "SV_DOMAINLOCATION")
John Kessenich7735b942016-09-05 12:40:06 -06003799 qualifier.builtIn = EbvTessCoord;
John Kessenich81d47142016-08-29 16:07:29 -06003800 else if (semanticUpperCase == "SV_DEPTH")
John Kessenich7735b942016-09-05 12:40:06 -06003801 qualifier.builtIn = EbvFragDepth;
John Kessenichb50fd172016-10-16 12:12:11 -06003802 else if( semanticUpperCase == "SV_COVERAGE")
3803 qualifier.builtIn = EbvSampleMask;
Dan Bakerdeec03c2016-08-25 11:59:17 -04003804
John Kessenich927608b2017-01-06 12:34:14 -07003805 // TODO, these need to get refined to be more specific
John Kessenich9e079532016-09-02 20:05:19 -06003806 else if( semanticUpperCase == "SV_DEPTHGREATEREQUAL")
John Kessenich7735b942016-09-05 12:40:06 -06003807 qualifier.builtIn = EbvFragDepthGreater;
John Kessenich9e079532016-09-02 20:05:19 -06003808 else if( semanticUpperCase == "SV_DEPTHLESSEQUAL")
John Kessenich7735b942016-09-05 12:40:06 -06003809 qualifier.builtIn = EbvFragDepthLesser;
John Kessenich9e079532016-09-02 20:05:19 -06003810 else if( semanticUpperCase == "SV_STENCILREF")
John Kessenichb50fd172016-10-16 12:12:11 -06003811 error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", "");
3812 else if( semanticUpperCase == "SV_GROUPINDEX")
3813 error(loc, "unimplemented", "SV_GROUPINDEX", "");
John Kessenich630dd7d2016-06-12 23:52:12 -06003814}
3815
3816//
John Kessenich96e9f472016-07-29 14:28:39 -06003817// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN"
John Kessenich82d6baf2016-07-29 13:03:05 -06003818//
3819// 'location' has the "c[Subcomponent]" part.
3820// 'component' points to the "component" part, or nullptr if not present.
3821//
John Kessenich7735b942016-09-05 12:40:06 -06003822void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location,
3823 const glslang::TString* component)
John Kessenich82d6baf2016-07-29 13:03:05 -06003824{
3825 if (location.size() == 0 || location[0] != 'c') {
3826 error(loc, "expected 'c'", "packoffset", "");
3827 return;
3828 }
3829 if (location.size() == 1)
3830 return;
3831 if (! isdigit(location[1])) {
3832 error(loc, "expected number after 'c'", "packoffset", "");
3833 return;
3834 }
3835
John Kessenich7735b942016-09-05 12:40:06 -06003836 qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str());
John Kessenich96e9f472016-07-29 14:28:39 -06003837 if (component != nullptr) {
John Kessenich82d6baf2016-07-29 13:03:05 -06003838 int componentOffset = 0;
3839 switch ((*component)[0]) {
3840 case 'x': componentOffset = 0; break;
3841 case 'y': componentOffset = 4; break;
3842 case 'z': componentOffset = 8; break;
3843 case 'w': componentOffset = 12; break;
3844 default:
3845 componentOffset = -1;
3846 break;
3847 }
3848 if (componentOffset < 0 || component->size() > 1) {
3849 error(loc, "expected {x, y, z, w} for component", "packoffset", "");
3850 return;
3851 }
John Kessenich7735b942016-09-05 12:40:06 -06003852 qualifier.layoutOffset += componentOffset;
John Kessenich82d6baf2016-07-29 13:03:05 -06003853 }
3854}
3855
3856//
John Kessenich96e9f472016-07-29 14:28:39 -06003857// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN"
3858//
3859// 'profile' points to the shader_profile part, or nullptr if not present.
3860// 'desc' is the type# part.
3861//
John Kessenich7735b942016-09-05 12:40:06 -06003862void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile,
John Kessenichcfd7ce82016-09-05 16:03:12 -06003863 const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc)
John Kessenich96e9f472016-07-29 14:28:39 -06003864{
3865 if (profile != nullptr)
3866 warn(loc, "ignoring shader_profile", "register", "");
3867
John Kessenichb38f0712016-07-30 10:29:54 -06003868 if (desc.size() < 1) {
3869 error(loc, "expected register type", "register", "");
John Kessenich96e9f472016-07-29 14:28:39 -06003870 return;
3871 }
3872
John Kessenichb38f0712016-07-30 10:29:54 -06003873 int regNumber = 0;
3874 if (desc.size() > 1) {
3875 if (isdigit(desc[1]))
3876 regNumber = atoi(desc.substr(1, desc.size()).c_str());
3877 else {
3878 error(loc, "expected register number after register type", "register", "");
3879 return;
3880 }
John Kessenich96e9f472016-07-29 14:28:39 -06003881 }
3882
John Kessenichb38f0712016-07-30 10:29:54 -06003883 // TODO: learn what all these really mean and how they interact with regNumber and subComponent
steve-lunarg9088be42016-11-01 10:31:42 -06003884 switch (std::tolower(desc[0])) {
John Kessenich96e9f472016-07-29 14:28:39 -06003885 case 'b':
3886 case 't':
3887 case 'c':
3888 case 's':
steve-lunarg9088be42016-11-01 10:31:42 -06003889 case 'u':
John Kessenich7735b942016-09-05 12:40:06 -06003890 qualifier.layoutBinding = regNumber + subComponent;
John Kessenich96e9f472016-07-29 14:28:39 -06003891 break;
3892 default:
3893 warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]);
3894 break;
3895 }
John Kessenichcfd7ce82016-09-05 16:03:12 -06003896
3897 // space
3898 unsigned int setNumber;
baldurk54a28de2016-10-13 19:23:39 +02003899 const auto crackSpace = [&]() -> bool {
John Kessenichcfd7ce82016-09-05 16:03:12 -06003900 const int spaceLen = 5;
3901 if (spaceDesc->size() < spaceLen + 1)
3902 return false;
3903 if (spaceDesc->compare(0, spaceLen, "space") != 0)
3904 return false;
3905 if (! isdigit((*spaceDesc)[spaceLen]))
3906 return false;
3907 setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str());
3908 return true;
3909 };
3910
3911 if (spaceDesc) {
3912 if (! crackSpace()) {
3913 error(loc, "expected spaceN", "register", "");
3914 return;
3915 }
3916 qualifier.layoutSet = setNumber;
3917 }
John Kessenich96e9f472016-07-29 14:28:39 -06003918}
3919
3920//
John Kesseniche01a9bc2016-03-12 20:11:22 -07003921// Same error message for all places assignments don't work.
3922//
3923void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right)
3924{
3925 error(loc, "", op, "cannot convert from '%s' to '%s'",
3926 right.c_str(), left.c_str());
3927}
3928
3929//
3930// Same error message for all places unary operations don't work.
3931//
3932void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand)
3933{
3934 error(loc, " wrong operand type", op,
3935 "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)",
3936 op, operand.c_str());
3937}
3938
3939//
3940// Same error message for all binary operations don't work.
3941//
3942void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right)
3943{
3944 error(loc, " wrong operand types:", op,
3945 "no operation '%s' exists that takes a left-hand operand of type '%s' and "
3946 "a right operand of type '%s' (or there is no acceptable conversion)",
3947 op, left.c_str(), right.c_str());
3948}
3949
3950//
3951// A basic type of EbtVoid is a key that the name string was seen in the source, but
3952// it was not found as a variable in the symbol table. If so, give the error
3953// message and insert a dummy variable in the symbol table to prevent future errors.
3954//
3955void HlslParseContext::variableCheck(TIntermTyped*& nodePtr)
3956{
3957 TIntermSymbol* symbol = nodePtr->getAsSymbolNode();
3958 if (! symbol)
3959 return;
3960
3961 if (symbol->getType().getBasicType() == EbtVoid) {
3962 error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), "");
3963
3964 // Add to symbol table to prevent future error messages on the same name
3965 if (symbol->getName().size() > 0) {
3966 TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat));
3967 symbolTable.insert(*fakeVariable);
3968
3969 // substitute a symbol node for this new variable
3970 nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc());
3971 }
3972 }
3973}
3974
3975//
3976// Both test, and if necessary spit out an error, to see if the node is really
3977// a constant.
3978//
3979void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token)
3980{
3981 if (node->getQualifier().storage != EvqConst)
3982 error(node->getLoc(), "constant expression required", token, "");
3983}
3984
3985//
3986// Both test, and if necessary spit out an error, to see if the node is really
3987// an integer.
3988//
3989void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token)
3990{
3991 if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar())
3992 return;
3993
3994 error(node->getLoc(), "scalar integer expression required", token, "");
3995}
3996
3997//
3998// Both test, and if necessary spit out an error, to see if we are currently
3999// globally scoped.
4000//
4001void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token)
4002{
4003 if (! symbolTable.atGlobalLevel())
4004 error(loc, "not allowed in nested scope", token, "");
4005}
4006
John Kessenich7f349c72016-07-08 22:09:10 -06004007bool HlslParseContext::builtInName(const TString& /*identifier*/)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004008{
4009 return false;
4010}
4011
4012//
4013// Make sure there is enough data and not too many arguments provided to the
4014// constructor to build something of the type of the constructor. Also returns
4015// the type of the constructor.
4016//
4017// Returns true if there was an error in construction.
4018//
John Kessenichf97f2ce2016-11-27 22:51:36 -07004019bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function,
John Kessenich7f349c72016-07-08 22:09:10 -06004020 TOperator op, TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004021{
4022 type.shallowCopy(function.getType());
4023
4024 bool constructingMatrix = false;
4025 switch (op) {
4026 case EOpConstructTextureSampler:
4027 return constructorTextureSamplerError(loc, function);
4028 case EOpConstructMat2x2:
4029 case EOpConstructMat2x3:
4030 case EOpConstructMat2x4:
4031 case EOpConstructMat3x2:
4032 case EOpConstructMat3x3:
4033 case EOpConstructMat3x4:
4034 case EOpConstructMat4x2:
4035 case EOpConstructMat4x3:
4036 case EOpConstructMat4x4:
4037 case EOpConstructDMat2x2:
4038 case EOpConstructDMat2x3:
4039 case EOpConstructDMat2x4:
4040 case EOpConstructDMat3x2:
4041 case EOpConstructDMat3x3:
4042 case EOpConstructDMat3x4:
4043 case EOpConstructDMat4x2:
4044 case EOpConstructDMat4x3:
4045 case EOpConstructDMat4x4:
4046 constructingMatrix = true;
4047 break;
4048 default:
4049 break;
4050 }
4051
4052 //
4053 // Walk the arguments for first-pass checks and collection of information.
4054 //
4055
4056 int size = 0;
4057 bool constType = true;
4058 bool full = false;
4059 bool overFull = false;
4060 bool matrixInMatrix = false;
4061 bool arrayArg = false;
4062 for (int arg = 0; arg < function.getParamCount(); ++arg) {
4063 if (function[arg].type->isArray()) {
4064 if (! function[arg].type->isExplicitlySizedArray()) {
4065 // Can't construct from an unsized array.
4066 error(loc, "array argument must be sized", "constructor", "");
4067 return true;
4068 }
4069 arrayArg = true;
4070 }
4071 if (constructingMatrix && function[arg].type->isMatrix())
4072 matrixInMatrix = true;
4073
4074 // 'full' will go to true when enough args have been seen. If we loop
4075 // again, there is an extra argument.
4076 if (full) {
4077 // For vectors and matrices, it's okay to have too many components
4078 // available, but not okay to have unused arguments.
4079 overFull = true;
4080 }
4081
4082 size += function[arg].type->computeNumComponents();
4083 if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
4084 full = true;
4085
4086 if (function[arg].type->getQualifier().storage != EvqConst)
4087 constType = false;
4088 }
4089
4090 if (constType)
4091 type.getQualifier().storage = EvqConst;
4092
4093 if (type.isArray()) {
4094 if (function.getParamCount() == 0) {
4095 error(loc, "array constructor must have at least one argument", "constructor", "");
4096 return true;
4097 }
4098
4099 if (type.isImplicitlySizedArray()) {
4100 // auto adapt the constructor type to the number of arguments
4101 type.changeOuterArraySize(function.getParamCount());
4102 } else if (type.getOuterArraySize() != function.getParamCount()) {
4103 error(loc, "array constructor needs one argument per array element", "constructor", "");
4104 return true;
4105 }
4106
4107 if (type.isArrayOfArrays()) {
4108 // Types have to match, but we're still making the type.
4109 // Finish making the type, and the comparison is done later
4110 // when checking for conversion.
4111 TArraySizes& arraySizes = type.getArraySizes();
4112
4113 // At least the dimensionalities have to match.
4114 if (! function[0].type->isArray() || arraySizes.getNumDims() != function[0].type->getArraySizes().getNumDims() + 1) {
John Kessenich509c4212016-11-27 17:26:21 -07004115 error(loc, "array constructor argument not correct type to construct array element", "constructor", "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004116 return true;
4117 }
4118
4119 if (arraySizes.isInnerImplicit()) {
4120 // "Arrays of arrays ..., and the size for any dimension is optional"
4121 // That means we need to adopt (from the first argument) the other array sizes into the type.
4122 for (int d = 1; d < arraySizes.getNumDims(); ++d) {
4123 if (arraySizes.getDimSize(d) == UnsizedArraySize) {
4124 arraySizes.setDimSize(d, function[0].type->getArraySizes().getDimSize(d - 1));
4125 }
4126 }
4127 }
4128 }
4129 }
4130
4131 if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
4132 error(loc, "constructing non-array constituent from array argument", "constructor", "");
4133 return true;
4134 }
4135
4136 if (matrixInMatrix && ! type.isArray()) {
4137 return false;
4138 }
4139
4140 if (overFull) {
4141 error(loc, "too many arguments", "constructor", "");
4142 return true;
4143 }
4144
John Kessenichf97f2ce2016-11-27 22:51:36 -07004145 if (op == EOpConstructStruct && ! type.isArray() && isZeroConstructor(node))
4146 return false;
4147
John Kesseniche01a9bc2016-03-12 20:11:22 -07004148 if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) {
4149 error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", "");
4150 return true;
4151 }
4152
4153 if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) ||
4154 (op == EOpConstructStruct && size < type.computeNumComponents())) {
4155 error(loc, "not enough data provided for construction", "constructor", "");
4156 return true;
4157 }
4158
John Kesseniche01a9bc2016-03-12 20:11:22 -07004159 return false;
4160}
4161
John Kessenichf97f2ce2016-11-27 22:51:36 -07004162bool HlslParseContext::isZeroConstructor(const TIntermNode* node)
4163{
4164 return node->getAsTyped()->isScalar() && node->getAsConstantUnion() &&
4165 node->getAsConstantUnion()->getConstArray()[0].getIConst() == 0;
4166}
4167
John Kesseniche01a9bc2016-03-12 20:11:22 -07004168// Verify all the correct semantics for constructing a combined texture/sampler.
4169// Return true if the semantics are incorrect.
4170bool HlslParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function)
4171{
4172 TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change
4173 const char* token = constructorName.c_str();
4174
4175 // exactly two arguments needed
4176 if (function.getParamCount() != 2) {
4177 error(loc, "sampler-constructor requires two arguments", token, "");
4178 return true;
4179 }
4180
4181 // For now, not allowing arrayed constructors, the rest of this function
4182 // is set up to allow them, if this test is removed:
4183 if (function.getType().isArray()) {
4184 error(loc, "sampler-constructor cannot make an array of samplers", token, "");
4185 return true;
4186 }
4187
4188 // first argument
4189 // * the constructor's first argument must be a texture type
4190 // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array)
4191 // of the texture type must match that of the constructed sampler type
4192 // (that is, the suffixes of the type of the first argument and the
4193 // type of the constructor will be spelled the same way)
4194 if (function[0].type->getBasicType() != EbtSampler ||
4195 ! function[0].type->getSampler().isTexture() ||
4196 function[0].type->isArray()) {
4197 error(loc, "sampler-constructor first argument must be a scalar textureXXX type", token, "");
4198 return true;
4199 }
4200 // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=()
4201 TSampler texture = function.getType().getSampler();
4202 texture.combined = false;
4203 texture.shadow = false;
4204 if (texture != function[0].type->getSampler()) {
4205 error(loc, "sampler-constructor first argument must match type and dimensionality of constructor type", token, "");
4206 return true;
4207 }
4208
4209 // second argument
4210 // * the constructor's second argument must be a scalar of type
4211 // *sampler* or *samplerShadow*
4212 // * the presence or absence of depth comparison (Shadow) must match
4213 // between the constructed sampler type and the type of the second argument
4214 if (function[1].type->getBasicType() != EbtSampler ||
4215 ! function[1].type->getSampler().isPureSampler() ||
4216 function[1].type->isArray()) {
4217 error(loc, "sampler-constructor second argument must be a scalar type 'sampler'", token, "");
4218 return true;
4219 }
4220 if (function.getType().getSampler().shadow != function[1].type->getSampler().shadow) {
4221 error(loc, "sampler-constructor second argument presence of shadow must match constructor presence of shadow", token, "");
4222 return true;
4223 }
4224
4225 return false;
4226}
4227
4228// Checks to see if a void variable has been declared and raise an error message for such a case
4229//
4230// returns true in case of an error
4231//
4232bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType)
4233{
4234 if (basicType == EbtVoid) {
4235 error(loc, "illegal use of type 'void'", identifier.c_str(), "");
4236 return true;
4237 }
4238
4239 return false;
4240}
4241
4242// Checks to see if the node (for the expression) contains a scalar boolean expression or not
4243void HlslParseContext::boolCheck(const TSourceLoc& loc, const TIntermTyped* type)
4244{
4245 if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector())
4246 error(loc, "boolean expression expected", "", "");
4247}
4248
John Kesseniche01a9bc2016-03-12 20:11:22 -07004249//
4250// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
4251//
John Kessenich7f349c72016-07-08 22:09:10 -06004252void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004253{
4254 // move from parameter/unknown qualifiers to pipeline in/out qualifiers
4255 switch (qualifier.storage) {
4256 case EvqIn:
4257 qualifier.storage = EvqVaryingIn;
4258 break;
4259 case EvqOut:
4260 qualifier.storage = EvqVaryingOut;
4261 break;
4262 default:
4263 break;
4264 }
4265}
4266
4267//
4268// Merge characteristics of the 'src' qualifier into the 'dst'.
4269// If there is duplication, issue error messages, unless 'force'
4270// is specified, which means to just override default settings.
4271//
4272// Also, when force is false, it will be assumed that 'src' follows
4273// 'dst', for the purpose of error checking order for versions
4274// that require specific orderings of qualifiers.
4275//
John Kessenich34e7ee72016-09-16 17:10:39 -06004276void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004277{
4278 // Storage qualification
4279 if (dst.storage == EvqTemporary || dst.storage == EvqGlobal)
4280 dst.storage = src.storage;
4281 else if ((dst.storage == EvqIn && src.storage == EvqOut) ||
John Kessenich3d157c52016-07-25 16:05:33 -06004282 (dst.storage == EvqOut && src.storage == EvqIn))
John Kesseniche01a9bc2016-03-12 20:11:22 -07004283 dst.storage = EvqInOut;
4284 else if ((dst.storage == EvqIn && src.storage == EvqConst) ||
John Kessenich3d157c52016-07-25 16:05:33 -06004285 (dst.storage == EvqConst && src.storage == EvqIn))
John Kesseniche01a9bc2016-03-12 20:11:22 -07004286 dst.storage = EvqConstReadOnly;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004287
John Kesseniche01a9bc2016-03-12 20:11:22 -07004288 // Layout qualifiers
4289 mergeObjectLayoutQualifiers(dst, src, false);
4290
4291 // individual qualifiers
4292 bool repeated = false;
4293#define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field;
4294 MERGE_SINGLETON(invariant);
John Kessenich17f07862016-05-04 12:36:14 -06004295 MERGE_SINGLETON(noContraction);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004296 MERGE_SINGLETON(centroid);
4297 MERGE_SINGLETON(smooth);
4298 MERGE_SINGLETON(flat);
4299 MERGE_SINGLETON(nopersp);
4300 MERGE_SINGLETON(patch);
4301 MERGE_SINGLETON(sample);
4302 MERGE_SINGLETON(coherent);
4303 MERGE_SINGLETON(volatil);
4304 MERGE_SINGLETON(restrict);
4305 MERGE_SINGLETON(readonly);
4306 MERGE_SINGLETON(writeonly);
4307 MERGE_SINGLETON(specConstant);
4308}
4309
4310// used to flatten the sampler type space into a single dimension
4311// correlates with the declaration of defaultSamplerPrecision[]
4312int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler)
4313{
4314 int arrayIndex = sampler.arrayed ? 1 : 0;
4315 int shadowIndex = sampler.shadow ? 1 : 0;
4316 int externalIndex = sampler.external ? 1 : 0;
4317
4318 return EsdNumDims * (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim;
4319}
4320
4321//
4322// Do size checking for an array type's size.
4323//
4324void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair)
4325{
4326 bool isConst = false;
4327 sizePair.size = 1;
4328 sizePair.node = nullptr;
4329
4330 TIntermConstantUnion* constant = expr->getAsConstantUnion();
4331 if (constant) {
4332 // handle true (non-specialization) constant
4333 sizePair.size = constant->getConstArray()[0].getIConst();
4334 isConst = true;
4335 } else {
4336 // see if it's a specialization constant instead
4337 if (expr->getQualifier().isSpecConstant()) {
4338 isConst = true;
4339 sizePair.node = expr;
4340 TIntermSymbol* symbol = expr->getAsSymbolNode();
4341 if (symbol && symbol->getConstArray().size() > 0)
4342 sizePair.size = symbol->getConstArray()[0].getIConst();
4343 }
4344 }
4345
4346 if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
4347 error(loc, "array size must be a constant integer expression", "", "");
4348 return;
4349 }
4350
4351 if (sizePair.size <= 0) {
4352 error(loc, "array size must be a positive integer", "", "");
4353 return;
4354 }
4355}
4356
4357//
4358// Require array to be completely sized
4359//
4360void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes)
4361{
4362 if (arraySizes.isImplicit())
4363 error(loc, "array size required", "", "");
4364}
4365
4366void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type)
4367{
4368 const TTypeList& structure = *type.getStruct();
4369 for (int m = 0; m < (int)structure.size(); ++m) {
4370 const TType& member = *structure[m].type;
4371 if (member.isArray())
4372 arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes());
4373 }
4374}
4375
4376// Merge array dimensions listed in 'sizes' onto the type's array dimensions.
4377//
4378// From the spec: "vec4[2] a[3]; // size-3 array of size-2 array of vec4"
4379//
4380// That means, the 'sizes' go in front of the 'type' as outermost sizes.
4381// 'type' is the type part of the declaration (to the left)
4382// 'sizes' is the arrayness tagged on the identifier (to the right)
4383//
4384void HlslParseContext::arrayDimMerge(TType& type, const TArraySizes* sizes)
4385{
4386 if (sizes)
4387 type.addArrayOuterSizes(*sizes);
4388}
4389
4390//
4391// Do all the semantic checking for declaring or redeclaring an array, with and
4392// without a size, and make the right changes to the symbol table.
4393//
John Kessenichd3f11222016-11-05 10:15:53 -06004394void HlslParseContext::declareArray(const TSourceLoc& loc, TString& identifier, const TType& type, TSymbol*& symbol, bool track)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004395{
4396 if (! symbol) {
4397 bool currentScope;
4398 symbol = symbolTable.find(identifier, nullptr, &currentScope);
4399
4400 if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) {
4401 // bad shader (errors already reported) trying to redeclare a built-in name as an array
4402 return;
4403 }
4404 if (symbol == nullptr || ! currentScope) {
4405 //
4406 // Successfully process a new definition.
4407 // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations)
4408 //
4409 symbol = new TVariable(&identifier, type);
4410 symbolTable.insert(*symbol);
John Kessenichd3f11222016-11-05 10:15:53 -06004411 if (track && symbolTable.atGlobalLevel())
John Kessenich02467d82017-01-19 15:41:47 -07004412 trackLinkage(*symbol);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004413
John Kesseniche01a9bc2016-03-12 20:11:22 -07004414 return;
4415 }
4416 if (symbol->getAsAnonMember()) {
4417 error(loc, "cannot redeclare a user-block member array", identifier.c_str(), "");
4418 symbol = nullptr;
4419 return;
4420 }
4421 }
4422
4423 //
4424 // Process a redeclaration.
4425 //
4426
4427 if (! symbol) {
4428 error(loc, "array variable name expected", identifier.c_str(), "");
4429 return;
4430 }
4431
4432 // redeclareBuiltinVariable() should have already done the copyUp()
4433 TType& existingType = symbol->getWritableType();
4434
John Kesseniche01a9bc2016-03-12 20:11:22 -07004435 if (existingType.isExplicitlySizedArray()) {
4436 // 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 -07004437 return;
4438 }
4439
4440 existingType.updateArraySizes(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004441}
4442
4443void HlslParseContext::updateImplicitArraySize(const TSourceLoc& loc, TIntermNode *node, int index)
4444{
4445 // maybe there is nothing to do...
4446 TIntermTyped* typedNode = node->getAsTyped();
4447 if (typedNode->getType().getImplicitArraySize() > index)
4448 return;
4449
4450 // something to do...
4451
4452 // Figure out what symbol to lookup, as we will use its type to edit for the size change,
4453 // as that type will be shared through shallow copies for future references.
4454 TSymbol* symbol = nullptr;
4455 int blockIndex = -1;
4456 const TString* lookupName = nullptr;
4457 if (node->getAsSymbolNode())
4458 lookupName = &node->getAsSymbolNode()->getName();
4459 else if (node->getAsBinaryNode()) {
4460 const TIntermBinary* deref = node->getAsBinaryNode();
4461 // This has to be the result of a block dereference, unless it's bad shader code
4462 // If it's a uniform block, then an error will be issued elsewhere, but
4463 // return early now to avoid crashing later in this function.
4464 if (! deref->getLeft()->getAsSymbolNode() || deref->getLeft()->getBasicType() != EbtBlock ||
4465 deref->getLeft()->getType().getQualifier().storage == EvqUniform ||
4466 deref->getRight()->getAsConstantUnion() == nullptr)
4467 return;
4468
4469 blockIndex = deref->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
4470
4471 lookupName = &deref->getLeft()->getAsSymbolNode()->getName();
4472 if (IsAnonymous(*lookupName))
4473 lookupName = &(*deref->getLeft()->getType().getStruct())[blockIndex].type->getFieldName();
4474 }
4475
4476 // Lookup the symbol, should only fail if shader code is incorrect
4477 symbol = symbolTable.find(*lookupName);
4478 if (symbol == nullptr)
4479 return;
4480
4481 if (symbol->getAsFunction()) {
4482 error(loc, "array variable name expected", symbol->getName().c_str(), "");
4483 return;
4484 }
4485
4486 symbol->getWritableType().setImplicitArraySize(index + 1);
4487}
4488
4489//
John Kessenichaa6d5622016-12-30 16:42:57 -07004490// Enforce non-initializer type/qualifier rules.
4491//
4492void HlslParseContext::fixConstInit(const TSourceLoc& loc, TString& identifier, TType& type, TIntermTyped*& initializer)
4493{
4494 //
4495 // Make the qualifier make sense, given that there is an initializer.
4496 //
4497 if (initializer == nullptr) {
4498 if (type.getQualifier().storage == EvqConst ||
4499 type.getQualifier().storage == EvqConstReadOnly) {
4500 initializer = intermediate.makeAggregate(loc);
4501 warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), "");
4502 }
4503 }
4504}
4505
4506//
John Kesseniche01a9bc2016-03-12 20:11:22 -07004507// See if the identifier is a built-in symbol that can be redeclared, and if so,
4508// copy the symbol table's read-only built-in variable to the current
4509// global level, where it can be modified based on the passed in type.
4510//
4511// Returns nullptr if no redeclaration took place; meaning a normal declaration still
4512// needs to occur for it, not necessarily an error.
4513//
4514// Returns a redeclared and type-modified variable if a redeclared occurred.
4515//
John Kessenich7f349c72016-07-08 22:09:10 -06004516TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier,
4517 const TQualifier& /*qualifier*/,
John Kessenichd3f11222016-11-05 10:15:53 -06004518 const TShaderQualifiers& /*publicType*/)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004519{
4520 if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel())
4521 return nullptr;
4522
4523 return nullptr;
4524}
4525
4526//
4527// Either redeclare the requested block, or give an error message why it can't be done.
4528//
4529// TODO: functionality: explicitly sizing members of redeclared blocks is not giving them an explicit size
4530void HlslParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes)
4531{
4532 // Redeclaring a built-in block...
4533
4534 // Blocks with instance names are easy to find, lookup the instance name,
4535 // Anonymous blocks need to be found via a member.
4536 bool builtIn;
4537 TSymbol* block;
4538 if (instanceName)
4539 block = symbolTable.find(*instanceName, &builtIn);
4540 else
4541 block = symbolTable.find(newTypeList.front().type->getFieldName(), &builtIn);
4542
4543 // If the block was not found, this must be a version/profile/stage
4544 // that doesn't have it, or the instance name is wrong.
4545 const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str();
4546 if (! block) {
4547 error(loc, "no declaration found for redeclaration", errorName, "");
4548 return;
4549 }
4550 // Built-in blocks cannot be redeclared more than once, which if happened,
4551 // we'd be finding the already redeclared one here, rather than the built in.
4552 if (! builtIn) {
4553 error(loc, "can only redeclare a built-in block once, and before any use", blockName.c_str(), "");
4554 return;
4555 }
4556
4557 // Copy the block to make a writable version, to insert into the block table after editing.
4558 block = symbolTable.copyUpDeferredInsert(block);
4559
4560 if (block->getType().getBasicType() != EbtBlock) {
4561 error(loc, "cannot redeclare a non block as a block", errorName, "");
4562 return;
4563 }
4564
4565 // Edit and error check the container against the redeclaration
4566 // - remove unused members
4567 // - ensure remaining qualifiers/types match
4568 TType& type = block->getWritableType();
4569 TTypeList::iterator member = type.getWritableStruct()->begin();
4570 size_t numOriginalMembersFound = 0;
4571 while (member != type.getStruct()->end()) {
4572 // look for match
4573 bool found = false;
4574 TTypeList::const_iterator newMember;
4575 TSourceLoc memberLoc;
4576 memberLoc.init();
4577 for (newMember = newTypeList.begin(); newMember != newTypeList.end(); ++newMember) {
4578 if (member->type->getFieldName() == newMember->type->getFieldName()) {
4579 found = true;
4580 memberLoc = newMember->loc;
4581 break;
4582 }
4583 }
4584
4585 if (found) {
4586 ++numOriginalMembersFound;
4587 // - ensure match between redeclared members' types
4588 // - check for things that can't be changed
4589 // - update things that can be changed
4590 TType& oldType = *member->type;
4591 const TType& newType = *newMember->type;
4592 if (! newType.sameElementType(oldType))
4593 error(memberLoc, "cannot redeclare block member with a different type", member->type->getFieldName().c_str(), "");
4594 if (oldType.isArray() != newType.isArray())
4595 error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), "");
4596 else if (! oldType.sameArrayness(newType) && oldType.isExplicitlySizedArray())
4597 error(memberLoc, "cannot change array size of redeclared block member", member->type->getFieldName().c_str(), "");
4598 if (newType.getQualifier().isMemory())
4599 error(memberLoc, "cannot add memory qualifier to redeclared block member", member->type->getFieldName().c_str(), "");
4600 if (newType.getQualifier().hasLayout())
4601 error(memberLoc, "cannot add layout to redeclared block member", member->type->getFieldName().c_str(), "");
4602 if (newType.getQualifier().patch)
4603 error(memberLoc, "cannot add patch to redeclared block member", member->type->getFieldName().c_str(), "");
4604 oldType.getQualifier().centroid = newType.getQualifier().centroid;
4605 oldType.getQualifier().sample = newType.getQualifier().sample;
4606 oldType.getQualifier().invariant = newType.getQualifier().invariant;
John Kessenich17f07862016-05-04 12:36:14 -06004607 oldType.getQualifier().noContraction = newType.getQualifier().noContraction;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004608 oldType.getQualifier().smooth = newType.getQualifier().smooth;
4609 oldType.getQualifier().flat = newType.getQualifier().flat;
4610 oldType.getQualifier().nopersp = newType.getQualifier().nopersp;
4611
4612 // go to next member
4613 ++member;
4614 } else {
4615 // For missing members of anonymous blocks that have been redeclared,
4616 // hide the original (shared) declaration.
4617 // Instance-named blocks can just have the member removed.
4618 if (instanceName)
4619 member = type.getWritableStruct()->erase(member);
4620 else {
4621 member->type->hideMember();
4622 ++member;
4623 }
4624 }
4625 }
4626
4627 if (numOriginalMembersFound < newTypeList.size())
4628 error(loc, "block redeclaration has extra members", blockName.c_str(), "");
4629 if (type.isArray() != (arraySizes != nullptr))
4630 error(loc, "cannot change arrayness of redeclared block", blockName.c_str(), "");
4631 else if (type.isArray()) {
4632 if (type.isExplicitlySizedArray() && arraySizes->getOuterSize() == UnsizedArraySize)
4633 error(loc, "block already declared with size, can't redeclare as implicitly-sized", blockName.c_str(), "");
4634 else if (type.isExplicitlySizedArray() && type.getArraySizes() != *arraySizes)
4635 error(loc, "cannot change array size of redeclared block", blockName.c_str(), "");
4636 else if (type.isImplicitlySizedArray() && arraySizes->getOuterSize() != UnsizedArraySize)
4637 type.changeOuterArraySize(arraySizes->getOuterSize());
4638 }
4639
4640 symbolTable.insert(*block);
4641
John Kesseniche01a9bc2016-03-12 20:11:22 -07004642 // Save it in the AST for linker use.
John Kessenich02467d82017-01-19 15:41:47 -07004643 trackLinkage(*block);
John Kesseniche01a9bc2016-03-12 20:11:22 -07004644}
4645
John Kessenich5aa59e22016-06-17 15:50:47 -06004646void HlslParseContext::paramFix(TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004647{
John Kessenich5aa59e22016-06-17 15:50:47 -06004648 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07004649 case EvqConst:
John Kesseniche01a9bc2016-03-12 20:11:22 -07004650 type.getQualifier().storage = EvqConstReadOnly;
4651 break;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004652 case EvqGlobal:
4653 case EvqTemporary:
4654 type.getQualifier().storage = EvqIn;
4655 break;
4656 default:
John Kesseniche01a9bc2016-03-12 20:11:22 -07004657 break;
4658 }
4659}
4660
John Kesseniche01a9bc2016-03-12 20:11:22 -07004661void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
4662{
4663 if (type.containsSpecializationSize())
4664 error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
4665}
4666
4667//
4668// Layout qualifier stuff.
4669//
4670
4671// Put the id's layout qualification into the public type, for qualifiers not having a number set.
4672// This is before we know any type information for error checking.
John Kessenichb9e39122016-08-17 10:22:08 -06004673void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004674{
4675 std::transform(id.begin(), id.end(), id.begin(), ::tolower);
4676
4677 if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
John Kessenich10f7fc72016-09-25 20:25:06 -06004678 qualifier.layoutMatrix = ElmRowMajor;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004679 return;
4680 }
4681 if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
John Kessenich10f7fc72016-09-25 20:25:06 -06004682 qualifier.layoutMatrix = ElmColumnMajor;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004683 return;
4684 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07004685 if (id == "push_constant") {
4686 requireVulkan(loc, "push_constant");
John Kessenichb9e39122016-08-17 10:22:08 -06004687 qualifier.layoutPushConstant = true;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004688 return;
4689 }
4690 if (language == EShLangGeometry || language == EShLangTessEvaluation) {
4691 if (id == TQualifier::getGeometryString(ElgTriangles)) {
John Kessenich927608b2017-01-06 12:34:14 -07004692 // publicType.shaderQualifiers.geometry = ElgTriangles;
John Kessenichb9e39122016-08-17 10:22:08 -06004693 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004694 return;
4695 }
4696 if (language == EShLangGeometry) {
4697 if (id == TQualifier::getGeometryString(ElgPoints)) {
John Kessenich927608b2017-01-06 12:34:14 -07004698 // publicType.shaderQualifiers.geometry = ElgPoints;
John Kessenichb9e39122016-08-17 10:22:08 -06004699 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004700 return;
4701 }
4702 if (id == TQualifier::getGeometryString(ElgLineStrip)) {
John Kessenich927608b2017-01-06 12:34:14 -07004703 // publicType.shaderQualifiers.geometry = ElgLineStrip;
John Kessenichb9e39122016-08-17 10:22:08 -06004704 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004705 return;
4706 }
4707 if (id == TQualifier::getGeometryString(ElgLines)) {
John Kessenich927608b2017-01-06 12:34:14 -07004708 // publicType.shaderQualifiers.geometry = ElgLines;
John Kessenichb9e39122016-08-17 10:22:08 -06004709 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004710 return;
4711 }
4712 if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
John Kessenich927608b2017-01-06 12:34:14 -07004713 // publicType.shaderQualifiers.geometry = ElgLinesAdjacency;
John Kessenichb9e39122016-08-17 10:22:08 -06004714 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004715 return;
4716 }
4717 if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
John Kessenich927608b2017-01-06 12:34:14 -07004718 // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency;
John Kessenichb9e39122016-08-17 10:22:08 -06004719 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004720 return;
4721 }
4722 if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
John Kessenich927608b2017-01-06 12:34:14 -07004723 // publicType.shaderQualifiers.geometry = ElgTriangleStrip;
John Kessenichb9e39122016-08-17 10:22:08 -06004724 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004725 return;
4726 }
4727 } else {
4728 assert(language == EShLangTessEvaluation);
4729
4730 // input primitive
4731 if (id == TQualifier::getGeometryString(ElgTriangles)) {
John Kessenich927608b2017-01-06 12:34:14 -07004732 // publicType.shaderQualifiers.geometry = ElgTriangles;
John Kessenichb9e39122016-08-17 10:22:08 -06004733 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004734 return;
4735 }
4736 if (id == TQualifier::getGeometryString(ElgQuads)) {
John Kessenich927608b2017-01-06 12:34:14 -07004737 // publicType.shaderQualifiers.geometry = ElgQuads;
John Kessenichb9e39122016-08-17 10:22:08 -06004738 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004739 return;
4740 }
4741 if (id == TQualifier::getGeometryString(ElgIsolines)) {
John Kessenich927608b2017-01-06 12:34:14 -07004742 // publicType.shaderQualifiers.geometry = ElgIsolines;
John Kessenichb9e39122016-08-17 10:22:08 -06004743 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004744 return;
4745 }
4746
4747 // vertex spacing
4748 if (id == TQualifier::getVertexSpacingString(EvsEqual)) {
John Kessenich927608b2017-01-06 12:34:14 -07004749 // publicType.shaderQualifiers.spacing = EvsEqual;
John Kessenichb9e39122016-08-17 10:22:08 -06004750 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004751 return;
4752 }
4753 if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) {
John Kessenich927608b2017-01-06 12:34:14 -07004754 // publicType.shaderQualifiers.spacing = EvsFractionalEven;
John Kessenichb9e39122016-08-17 10:22:08 -06004755 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004756 return;
4757 }
4758 if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) {
John Kessenich927608b2017-01-06 12:34:14 -07004759 // publicType.shaderQualifiers.spacing = EvsFractionalOdd;
John Kessenichb9e39122016-08-17 10:22:08 -06004760 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004761 return;
4762 }
4763
4764 // triangle order
4765 if (id == TQualifier::getVertexOrderString(EvoCw)) {
John Kessenich927608b2017-01-06 12:34:14 -07004766 // publicType.shaderQualifiers.order = EvoCw;
John Kessenichb9e39122016-08-17 10:22:08 -06004767 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004768 return;
4769 }
4770 if (id == TQualifier::getVertexOrderString(EvoCcw)) {
John Kessenich927608b2017-01-06 12:34:14 -07004771 // publicType.shaderQualifiers.order = EvoCcw;
John Kessenichb9e39122016-08-17 10:22:08 -06004772 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004773 return;
4774 }
4775
4776 // point mode
4777 if (id == "point_mode") {
John Kessenich927608b2017-01-06 12:34:14 -07004778 // publicType.shaderQualifiers.pointMode = true;
John Kessenichb9e39122016-08-17 10:22:08 -06004779 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004780 return;
4781 }
4782 }
4783 }
4784 if (language == EShLangFragment) {
4785 if (id == "origin_upper_left") {
John Kessenich927608b2017-01-06 12:34:14 -07004786 // publicType.shaderQualifiers.originUpperLeft = true;
John Kessenichb9e39122016-08-17 10:22:08 -06004787 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004788 return;
4789 }
4790 if (id == "pixel_center_integer") {
John Kessenich927608b2017-01-06 12:34:14 -07004791 // publicType.shaderQualifiers.pixelCenterInteger = true;
John Kessenichb9e39122016-08-17 10:22:08 -06004792 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004793 return;
4794 }
4795 if (id == "early_fragment_tests") {
John Kessenich927608b2017-01-06 12:34:14 -07004796 // publicType.shaderQualifiers.earlyFragmentTests = true;
John Kessenichb9e39122016-08-17 10:22:08 -06004797 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004798 return;
4799 }
4800 for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) {
4801 if (id == TQualifier::getLayoutDepthString(depth)) {
John Kessenich927608b2017-01-06 12:34:14 -07004802 // publicType.shaderQualifiers.layoutDepth = depth;
John Kessenichb9e39122016-08-17 10:22:08 -06004803 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004804 return;
4805 }
4806 }
4807 if (id.compare(0, 13, "blend_support") == 0) {
4808 bool found = false;
4809 for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
4810 if (id == TQualifier::getBlendEquationString(be)) {
4811 requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation");
4812 intermediate.addBlendEquation(be);
John Kessenich927608b2017-01-06 12:34:14 -07004813 // publicType.shaderQualifiers.blendEquation = true;
John Kessenichb9e39122016-08-17 10:22:08 -06004814 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004815 found = true;
4816 break;
4817 }
4818 }
4819 if (! found)
4820 error(loc, "unknown blend equation", "blend_support", "");
4821 return;
4822 }
4823 }
4824 error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
4825}
4826
4827// Put the id's layout qualifier value into the public type, for qualifiers having a number set.
4828// This is before we know any type information for error checking.
John Kessenichb9e39122016-08-17 10:22:08 -06004829void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id, const TIntermTyped* node)
John Kesseniche01a9bc2016-03-12 20:11:22 -07004830{
4831 const char* feature = "layout-id value";
John Kessenich927608b2017-01-06 12:34:14 -07004832 // const char* nonLiteralFeature = "non-literal layout-id value";
John Kesseniche01a9bc2016-03-12 20:11:22 -07004833
4834 integerCheck(node, feature);
4835 const TIntermConstantUnion* constUnion = node->getAsConstantUnion();
4836 int value = 0;
4837 if (constUnion) {
4838 value = constUnion->getConstArray()[0].getIConst();
4839 }
4840
4841 std::transform(id.begin(), id.end(), id.begin(), ::tolower);
4842
4843 if (id == "offset") {
John Kessenichb9e39122016-08-17 10:22:08 -06004844 qualifier.layoutOffset = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004845 return;
4846 } else if (id == "align") {
4847 // "The specified alignment must be a power of 2, or a compile-time error results."
4848 if (! IsPow2(value))
4849 error(loc, "must be a power of 2", "align", "");
4850 else
John Kessenichb9e39122016-08-17 10:22:08 -06004851 qualifier.layoutAlign = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004852 return;
4853 } else if (id == "location") {
4854 if ((unsigned int)value >= TQualifier::layoutLocationEnd)
4855 error(loc, "location is too large", id.c_str(), "");
4856 else
John Kessenichb9e39122016-08-17 10:22:08 -06004857 qualifier.layoutLocation = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004858 return;
4859 } else if (id == "set") {
4860 if ((unsigned int)value >= TQualifier::layoutSetEnd)
4861 error(loc, "set is too large", id.c_str(), "");
4862 else
John Kessenichb9e39122016-08-17 10:22:08 -06004863 qualifier.layoutSet = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004864 return;
4865 } else if (id == "binding") {
4866 if ((unsigned int)value >= TQualifier::layoutBindingEnd)
4867 error(loc, "binding is too large", id.c_str(), "");
4868 else
John Kessenichb9e39122016-08-17 10:22:08 -06004869 qualifier.layoutBinding = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004870 return;
4871 } else if (id == "component") {
4872 if ((unsigned)value >= TQualifier::layoutComponentEnd)
4873 error(loc, "component is too large", id.c_str(), "");
4874 else
John Kessenichb9e39122016-08-17 10:22:08 -06004875 qualifier.layoutComponent = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004876 return;
4877 } else if (id.compare(0, 4, "xfb_") == 0) {
John Kessenichecba76f2017-01-06 00:34:48 -07004878 // "Any shader making any static use (after preprocessing) of any of these
4879 // *xfb_* qualifiers will cause the shader to be in a transform feedback
4880 // capturing mode and hence responsible for describing the transform feedback
John Kesseniche01a9bc2016-03-12 20:11:22 -07004881 // setup."
4882 intermediate.setXfbMode();
4883 if (id == "xfb_buffer") {
4884 // "It is a compile-time error to specify an *xfb_buffer* that is greater than
4885 // the implementation-dependent constant gl_MaxTransformFeedbackBuffers."
4886 if (value >= resources.maxTransformFeedbackBuffers)
4887 error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", resources.maxTransformFeedbackBuffers);
4888 if (value >= (int)TQualifier::layoutXfbBufferEnd)
4889 error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1);
4890 else
John Kessenichb9e39122016-08-17 10:22:08 -06004891 qualifier.layoutXfbBuffer = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004892 return;
4893 } else if (id == "xfb_offset") {
4894 if (value >= (int)TQualifier::layoutXfbOffsetEnd)
4895 error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1);
4896 else
John Kessenichb9e39122016-08-17 10:22:08 -06004897 qualifier.layoutXfbOffset = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004898 return;
4899 } else if (id == "xfb_stride") {
John Kessenichecba76f2017-01-06 00:34:48 -07004900 // "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 -07004901 // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
4902 if (value > 4 * resources.maxTransformFeedbackInterleavedComponents)
4903 error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", resources.maxTransformFeedbackInterleavedComponents);
4904 else if (value >= (int)TQualifier::layoutXfbStrideEnd)
4905 error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1);
4906 if (value < (int)TQualifier::layoutXfbStrideEnd)
John Kessenichb9e39122016-08-17 10:22:08 -06004907 qualifier.layoutXfbStride = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004908 return;
4909 }
4910 }
4911
4912 if (id == "input_attachment_index") {
4913 requireVulkan(loc, "input_attachment_index");
4914 if (value >= (int)TQualifier::layoutAttachmentEnd)
4915 error(loc, "attachment index is too large", id.c_str(), "");
4916 else
John Kessenichb9e39122016-08-17 10:22:08 -06004917 qualifier.layoutAttachment = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004918 return;
4919 }
4920 if (id == "constant_id") {
4921 requireSpv(loc, "constant_id");
4922 if (value >= (int)TQualifier::layoutSpecConstantIdEnd) {
4923 error(loc, "specialization-constant id is too large", id.c_str(), "");
4924 } else {
John Kessenichb9e39122016-08-17 10:22:08 -06004925 qualifier.layoutSpecConstantId = value;
4926 qualifier.specConstant = true;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004927 if (! intermediate.addUsedConstantId(value))
4928 error(loc, "specialization-constant id already used", id.c_str(), "");
4929 }
4930 return;
4931 }
4932
4933 switch (language) {
4934 case EShLangVertex:
4935 break;
4936
4937 case EShLangTessControl:
4938 if (id == "vertices") {
4939 if (value == 0)
4940 error(loc, "must be greater than 0", "vertices", "");
4941 else
John Kessenich927608b2017-01-06 12:34:14 -07004942 // publicType.shaderQualifiers.vertices = value;
John Kessenichb9e39122016-08-17 10:22:08 -06004943 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004944 return;
4945 }
4946 break;
4947
4948 case EShLangTessEvaluation:
4949 break;
4950
4951 case EShLangGeometry:
4952 if (id == "invocations") {
4953 if (value == 0)
4954 error(loc, "must be at least 1", "invocations", "");
4955 else
John Kessenich927608b2017-01-06 12:34:14 -07004956 // publicType.shaderQualifiers.invocations = value;
John Kessenichb9e39122016-08-17 10:22:08 -06004957 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004958 return;
4959 }
4960 if (id == "max_vertices") {
John Kessenich927608b2017-01-06 12:34:14 -07004961 // publicType.shaderQualifiers.vertices = value;
John Kessenichb9e39122016-08-17 10:22:08 -06004962 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004963 if (value > resources.maxGeometryOutputVertices)
4964 error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", "");
4965 return;
4966 }
4967 if (id == "stream") {
John Kessenichb9e39122016-08-17 10:22:08 -06004968 qualifier.layoutStream = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004969 return;
4970 }
4971 break;
4972
4973 case EShLangFragment:
4974 if (id == "index") {
John Kessenichb9e39122016-08-17 10:22:08 -06004975 qualifier.layoutIndex = value;
John Kesseniche01a9bc2016-03-12 20:11:22 -07004976 return;
4977 }
4978 break;
4979
4980 case EShLangCompute:
4981 if (id.compare(0, 11, "local_size_") == 0) {
4982 if (id == "local_size_x") {
John Kessenich927608b2017-01-06 12:34:14 -07004983 // publicType.shaderQualifiers.localSize[0] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06004984 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004985 return;
4986 }
4987 if (id == "local_size_y") {
John Kessenich927608b2017-01-06 12:34:14 -07004988 // publicType.shaderQualifiers.localSize[1] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06004989 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004990 return;
4991 }
4992 if (id == "local_size_z") {
John Kessenich927608b2017-01-06 12:34:14 -07004993 // publicType.shaderQualifiers.localSize[2] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06004994 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07004995 return;
4996 }
John Kessenichb901ade2016-06-16 20:59:42 -06004997 if (spvVersion.spv != 0) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07004998 if (id == "local_size_x_id") {
John Kessenich927608b2017-01-06 12:34:14 -07004999 // publicType.shaderQualifiers.localSizeSpecId[0] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005000 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005001 return;
5002 }
5003 if (id == "local_size_y_id") {
John Kessenich927608b2017-01-06 12:34:14 -07005004 // publicType.shaderQualifiers.localSizeSpecId[1] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005005 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005006 return;
5007 }
5008 if (id == "local_size_z_id") {
John Kessenich927608b2017-01-06 12:34:14 -07005009 // publicType.shaderQualifiers.localSizeSpecId[2] = value;
John Kessenichb9e39122016-08-17 10:22:08 -06005010 warn(loc, "ignored", id.c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005011 return;
5012 }
5013 }
5014 }
5015 break;
5016
5017 default:
5018 break;
5019 }
5020
5021 error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
5022}
5023
5024// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
5025//
5026// "More than one layout qualifier may appear in a single declaration.
John Kessenichecba76f2017-01-06 00:34:48 -07005027// Additionally, the same layout-qualifier-name can occur multiple times
5028// within a layout qualifier or across multiple layout qualifiers in the
5029// same declaration. When the same layout-qualifier-name occurs
5030// multiple times, in a single declaration, the last occurrence overrides
5031// the former occurrence(s). Further, if such a layout-qualifier-name
5032// will effect subsequent declarations or other observable behavior, it
5033// is only the last occurrence that will have any effect, behaving as if
5034// the earlier occurrence(s) within the declaration are not present.
5035// This is also true for overriding layout-qualifier-names, where one
5036// overrides the other (e.g., row_major vs. column_major); only the last
5037// occurrence has any effect."
John Kesseniche01a9bc2016-03-12 20:11:22 -07005038//
5039void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly)
5040{
5041 if (src.hasMatrix())
5042 dst.layoutMatrix = src.layoutMatrix;
5043 if (src.hasPacking())
5044 dst.layoutPacking = src.layoutPacking;
5045
5046 if (src.hasStream())
5047 dst.layoutStream = src.layoutStream;
5048
5049 if (src.hasFormat())
5050 dst.layoutFormat = src.layoutFormat;
5051
5052 if (src.hasXfbBuffer())
5053 dst.layoutXfbBuffer = src.layoutXfbBuffer;
5054
5055 if (src.hasAlign())
5056 dst.layoutAlign = src.layoutAlign;
5057
5058 if (! inheritOnly) {
5059 if (src.hasLocation())
5060 dst.layoutLocation = src.layoutLocation;
5061 if (src.hasComponent())
5062 dst.layoutComponent = src.layoutComponent;
5063 if (src.hasIndex())
5064 dst.layoutIndex = src.layoutIndex;
5065
5066 if (src.hasOffset())
5067 dst.layoutOffset = src.layoutOffset;
5068
5069 if (src.hasSet())
5070 dst.layoutSet = src.layoutSet;
5071 if (src.layoutBinding != TQualifier::layoutBindingEnd)
5072 dst.layoutBinding = src.layoutBinding;
5073
5074 if (src.hasXfbStride())
5075 dst.layoutXfbStride = src.layoutXfbStride;
5076 if (src.hasXfbOffset())
5077 dst.layoutXfbOffset = src.layoutXfbOffset;
5078 if (src.hasAttachment())
5079 dst.layoutAttachment = src.layoutAttachment;
5080 if (src.hasSpecConstantId())
5081 dst.layoutSpecConstantId = src.layoutSpecConstantId;
5082
5083 if (src.layoutPushConstant)
5084 dst.layoutPushConstant = true;
5085 }
5086}
5087
5088//
5089// Look up a function name in the symbol table, and make sure it is a function.
5090//
John Kessenichfcc0aa32016-08-24 18:34:43 -06005091// First, look for an exact match. If there is none, use the generic selector
John Kessenichecba76f2017-01-06 00:34:48 -07005092// TParseContextBase::selectFunction() to find one, parameterized by the
John Kessenichfcc0aa32016-08-24 18:34:43 -06005093// convertible() and better() predicates defined below.
5094//
John Kesseniche01a9bc2016-03-12 20:11:22 -07005095// Return the function symbol if found, otherwise nullptr.
5096//
steve-lunarg26d31452016-12-23 18:56:57 -07005097const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn,
5098 TIntermTyped*& args)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005099{
John Kessenich7f349c72016-07-08 22:09:10 -06005100 // const TFunction* function = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005101
5102 if (symbolTable.isFunctionNameVariable(call.getName())) {
5103 error(loc, "can't use function syntax on variable", call.getName().c_str(), "");
5104 return nullptr;
5105 }
5106
5107 // first, look for an exact match
5108 TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn);
5109 if (symbol)
5110 return symbol->getAsFunction();
5111
John Kessenichfcc0aa32016-08-24 18:34:43 -06005112 // no exact match, use the generic selector, parameterized by the GLSL rules
John Kesseniche01a9bc2016-03-12 20:11:22 -07005113
John Kessenichfcc0aa32016-08-24 18:34:43 -06005114 // create list of candidates to send
John Kessenich0a04b4d2016-08-19 07:27:28 -06005115 TVector<const TFunction*> candidateList;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005116 symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
John Kessenichecba76f2017-01-06 00:34:48 -07005117
steve-lunargf49cdf42016-11-17 15:04:20 -07005118 // These builtin ops can accept any type, so we bypass the argument selection
5119 if (candidateList.size() == 1 && builtIn &&
5120 (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
5121 candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip)) {
5122
5123 return candidateList[0];
5124 }
5125
steve-lunarg05f75142016-12-06 15:50:11 -07005126 bool allowOnlyUpConversions = true;
5127
John Kessenichfcc0aa32016-08-24 18:34:43 -06005128 // can 'from' convert to 'to'?
steve-lunarg05f75142016-12-06 15:50:11 -07005129 const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool {
John Kessenichfcc0aa32016-08-24 18:34:43 -06005130 if (from == to)
5131 return true;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005132
5133 // no aggregate conversions
John Kessenichecba76f2017-01-06 00:34:48 -07005134 if (from.isArray() || to.isArray() ||
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005135 from.isStruct() || to.isStruct())
John Kessenichfcc0aa32016-08-24 18:34:43 -06005136 return false;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005137
steve-lunarg05f75142016-12-06 15:50:11 -07005138 switch (op) {
5139 case EOpInterlockedAdd:
5140 case EOpInterlockedAnd:
5141 case EOpInterlockedCompareExchange:
5142 case EOpInterlockedCompareStore:
5143 case EOpInterlockedExchange:
5144 case EOpInterlockedMax:
5145 case EOpInterlockedMin:
5146 case EOpInterlockedOr:
5147 case EOpInterlockedXor:
5148 // We do not promote the texture or image type for these ocodes. Normally that would not
5149 // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this
5150 // stage it's merely e.g, a basic integer type.
John Kessenichecba76f2017-01-06 00:34:48 -07005151 //
steve-lunarg05f75142016-12-06 15:50:11 -07005152 // Instead, we want to promote other arguments, but stay within the same family. In other
5153 // words, InterlockedAdd(RWBuffer<int>, ...) will always use the int flavor, never the uint flavor,
5154 // but it is allowed to promote its other arguments.
5155 if (arg == 0)
5156 return false;
t.jung84e59202017-01-02 17:10:27 +01005157 break;
5158 case EOpMethodSample:
5159 case EOpMethodSampleBias:
5160 case EOpMethodSampleCmp:
5161 case EOpMethodSampleCmpLevelZero:
5162 case EOpMethodSampleGrad:
5163 case EOpMethodSampleLevel:
5164 case EOpMethodLoad:
5165 case EOpMethodGetDimensions:
5166 case EOpMethodGetSamplePosition:
5167 case EOpMethodGather:
5168 case EOpMethodCalculateLevelOfDetail:
5169 case EOpMethodCalculateLevelOfDetailUnclamped:
5170 case EOpMethodGatherRed:
5171 case EOpMethodGatherGreen:
5172 case EOpMethodGatherBlue:
5173 case EOpMethodGatherAlpha:
5174 case EOpMethodGatherCmp:
5175 case EOpMethodGatherCmpRed:
5176 case EOpMethodGatherCmpGreen:
5177 case EOpMethodGatherCmpBlue:
5178 case EOpMethodGatherCmpAlpha:
5179 case EOpMethodAppend:
5180 case EOpMethodRestartStrip:
5181 // those are method calls, the object type can not be changed
5182 // they are equal if the dim and type match (is dim sufficient?)
5183 if (arg == 0)
5184 return from.getSampler().type == to.getSampler().type &&
5185 from.getSampler().arrayed == to.getSampler().arrayed &&
5186 from.getSampler().shadow == to.getSampler().shadow &&
5187 from.getSampler().ms == to.getSampler().ms &&
5188 from.getSampler().dim == to.getSampler().dim;
5189 break;
steve-lunarg05f75142016-12-06 15:50:11 -07005190 default:
5191 break;
5192 }
5193
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005194 // basic types have to be convertible
steve-lunarg05f75142016-12-06 15:50:11 -07005195 if (allowOnlyUpConversions)
5196 if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall))
5197 return false;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005198
5199 // shapes have to be convertible
steve-lunargd9cb8322016-11-11 15:37:10 -07005200 if ((from.isScalarOrVec1() && to.isScalarOrVec1()) ||
5201 (from.isScalarOrVec1() && to.isVector()) ||
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005202 (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize()))
5203 return true;
5204
5205 // TODO: what are the matrix rules? they go here
5206
5207 return false;
John Kessenichfcc0aa32016-08-24 18:34:43 -06005208 };
John Kesseniche01a9bc2016-03-12 20:11:22 -07005209
John Kessenichfcc0aa32016-08-24 18:34:43 -06005210 // Is 'to2' a better conversion than 'to1'?
5211 // Ties should not be considered as better.
5212 // Assumes 'convertible' already said true.
baldurk54a28de2016-10-13 19:23:39 +02005213 const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool {
John Kessenich90dd70f2016-08-25 10:49:21 -06005214 // exact match is always better than mismatch
John Kessenichfcc0aa32016-08-24 18:34:43 -06005215 if (from == to2)
5216 return from != to1;
5217 if (from == to1)
5218 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005219
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005220 // shape changes are always worse
5221 if (from.isScalar() || from.isVector()) {
5222 if (from.getVectorSize() == to2.getVectorSize() &&
5223 from.getVectorSize() != to1.getVectorSize())
John Kessenichfcc0aa32016-08-24 18:34:43 -06005224 return true;
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005225 if (from.getVectorSize() == to1.getVectorSize() &&
5226 from.getVectorSize() != to2.getVectorSize())
5227 return false;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005228 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005229
steve-lunarg26d31452016-12-23 18:56:57 -07005230 // Handle sampler betterness: An exact sampler match beats a non-exact match.
5231 // (If we just looked at basic type, all EbtSamplers would look the same).
5232 // If any type is not a sampler, just use the linearize function below.
5233 if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) {
5234 // We can ignore the vector size in the comparison.
5235 TSampler to1Sampler = to1.getSampler();
5236 TSampler to2Sampler = to2.getSampler();
5237
5238 to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize;
5239
5240 if (from.getSampler() == to2Sampler)
5241 return from.getSampler() != to1Sampler;
5242 if (from.getSampler() == to1Sampler)
5243 return false;
5244 }
5245
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005246 // Might or might not be changing shape, which means basic type might
5247 // or might not match, so within that, the question is how big a
5248 // basic-type conversion is being done.
5249 //
5250 // Use a hierarchy of domains, translated to order of magnitude
5251 // in a linearized view:
5252 // - floating-point vs. integer
5253 // - 32 vs. 64 bit (or width in general)
5254 // - bool vs. non bool
5255 // - signed vs. not signed
baldurk54a28de2016-10-13 19:23:39 +02005256 const auto linearize = [](const TBasicType& basicType) -> int {
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005257 switch (basicType) {
5258 case EbtBool: return 1;
5259 case EbtInt: return 10;
5260 case EbtUint: return 11;
5261 case EbtInt64: return 20;
5262 case EbtUint64: return 21;
5263 case EbtFloat: return 100;
5264 case EbtDouble: return 110;
5265 default: return 0;
5266 }
5267 };
John Kessenich90dd70f2016-08-25 10:49:21 -06005268
John Kesseniche3f2c8f2016-08-25 15:57:56 -06005269 return std::abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) <
5270 std::abs(linearize(to1.getBasicType()) - linearize(from.getBasicType()));
John Kessenichfcc0aa32016-08-24 18:34:43 -06005271 };
5272
5273 // for ambiguity reporting
5274 bool tie = false;
John Kessenichecba76f2017-01-06 00:34:48 -07005275
John Kessenichfcc0aa32016-08-24 18:34:43 -06005276 // send to the generic selector
5277 const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie);
5278
steve-lunargef33ec02016-11-02 12:42:34 -06005279 if (bestMatch == nullptr) {
steve-lunarg05f75142016-12-06 15:50:11 -07005280 // If there is nothing selected by allowing only up-conversions (to a larger linearize() value),
5281 // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any
5282 // upconversions possible.
5283 allowOnlyUpConversions = false;
5284 bestMatch = selectFunction(candidateList, call, convertible, better, tie);
5285 }
5286
5287 if (bestMatch == nullptr) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005288 error(loc, "no matching overloaded function found", call.getName().c_str(), "");
steve-lunargef33ec02016-11-02 12:42:34 -06005289 return nullptr;
5290 }
5291
5292 // For builtins, we can convert across the arguments. This will happen in several steps:
5293 // Step 1: If there's an exact match, use it.
5294 // Step 2a: Otherwise, get the operator from the best match and promote arguments:
5295 // Step 2b: reconstruct the TFunction based on the new arg types
5296 // Step 3: Re-select after type promotion is applied, to find proper candidate.
5297 if (builtIn) {
5298 // Step 1: If there's an exact match, use it.
5299 if (call.getMangledName() == bestMatch->getMangledName())
5300 return bestMatch;
5301
5302 // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we
5303 // are that kind of operator.
5304 if (args != nullptr) {
5305 // The arg list can be a unary node, or an aggregate. We have to handle both.
5306 // We will use the normal promote() facilities, which require an interm node.
5307 TIntermOperator* promote = nullptr;
5308
5309 if (call.getParamCount() == 1) {
5310 promote = new TIntermUnary(bestMatch->getBuiltInOp());
5311 promote->getAsUnaryNode()->setOperand(args->getAsTyped());
5312 } else {
5313 promote = new TIntermAggregate(bestMatch->getBuiltInOp());
5314 promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
5315 }
5316
5317 if (! intermediate.promote(promote))
5318 return nullptr;
5319
5320 // Obtain the promoted arg list.
5321 if (call.getParamCount() == 1) {
5322 args = promote->getAsUnaryNode()->getOperand();
5323 } else {
5324 promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
5325 }
5326 }
5327
5328 // Step 2b: reconstruct the TFunction based on the new arg types
5329 TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp());
5330
5331 if (args->getAsAggregate()) {
5332 // Handle aggregates: put all args into the new function call
5333 for (int arg=0; arg<int(args->getAsAggregate()->getSequence().size()); ++arg) {
5334 // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly.
steve-lunarg26d31452016-12-23 18:56:57 -07005335 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06005336 param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType());
5337 convertedCall.addParameter(param);
5338 }
5339 } else if (args->getAsUnaryNode()) {
5340 // Handle unaries: put all args into the new function call
steve-lunarg26d31452016-12-23 18:56:57 -07005341 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06005342 param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType());
5343 convertedCall.addParameter(param);
5344 } else if (args->getAsTyped()) {
5345 // Handle bare e.g, floats, not in an aggregate.
steve-lunarg26d31452016-12-23 18:56:57 -07005346 TParameter param = { 0, new TType, nullptr };
steve-lunargef33ec02016-11-02 12:42:34 -06005347 param.type->shallowCopy(args->getAsTyped()->getType());
5348 convertedCall.addParameter(param);
5349 } else {
5350 assert(0); // unknown argument list.
5351 return nullptr;
5352 }
5353
5354 // Step 3: Re-select after type promotion, to find proper candidate
5355 // send to the generic selector
5356 bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie);
5357
5358 // At this point, there should be no tie.
5359 }
5360
5361 if (tie)
John Kessenichfcc0aa32016-08-24 18:34:43 -06005362 error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
John Kesseniche01a9bc2016-03-12 20:11:22 -07005363
steve-lunarg26d31452016-12-23 18:56:57 -07005364 // Append default parameter values if needed
5365 if (!tie && bestMatch != nullptr) {
5366 for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) {
5367 handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue);
5368 }
5369 }
5370
John Kessenichfcc0aa32016-08-24 18:34:43 -06005371 return bestMatch;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005372}
5373
5374//
John Kessenich5e69ec62016-07-05 00:02:40 -06005375// Do everything necessary to handle a typedef declaration, for a single symbol.
John Kessenichecba76f2017-01-06 00:34:48 -07005376//
John Kessenich5e69ec62016-07-05 00:02:40 -06005377// 'parseType' is the type part of the declaration (to the left)
5378// 'arraySizes' is the arrayness tagged on the identifier (to the right)
5379//
Alex Szpakowski49ad2b72016-10-08 22:07:20 -03005380void HlslParseContext::declareTypedef(const TSourceLoc& loc, TString& identifier, const TType& parseType, TArraySizes* /*arraySizes*/)
John Kessenich5e69ec62016-07-05 00:02:40 -06005381{
5382 TType type;
5383 type.deepCopy(parseType);
5384
John Kessenich5e69ec62016-07-05 00:02:40 -06005385 TVariable* typeSymbol = new TVariable(&identifier, type, true);
5386 if (! symbolTable.insert(*typeSymbol))
5387 error(loc, "name already defined", "typedef", identifier.c_str());
5388}
5389
John Kessenichabd8dca2017-02-01 18:09:17 -07005390// Create a non-IO type from an IO type. If there is no IO data,
5391// the input type is unmodified. Otherwise, it modifies the type
5392// in place.
5393void HlslParseContext::makeTypeNonIo(TType* type)
steve-lunarga2e75312016-12-14 15:22:25 -07005394{
steve-lunarg5d3023a2017-01-25 10:03:17 -07005395 // early out if there's nothing to do: prevents introduction of unneeded types.
5396 if (!type->hasIoData())
John Kessenichabd8dca2017-02-01 18:09:17 -07005397 return;
steve-lunarg5d3023a2017-01-25 10:03:17 -07005398
5399 type->getQualifier().makeNonIo(); // Sanitize the qualifier.
5400
5401 // Nothing more to do if there is no deep structure.
steve-lunarga2e75312016-12-14 15:22:25 -07005402 if (!type->isStruct())
John Kessenichabd8dca2017-02-01 18:09:17 -07005403 return;
steve-lunarga2e75312016-12-14 15:22:25 -07005404
steve-lunarg5d3023a2017-01-25 10:03:17 -07005405 const auto typeIter = nonIoTypeMap.find(type->getStruct());
steve-lunarga2e75312016-12-14 15:22:25 -07005406
steve-lunarg5d3023a2017-01-25 10:03:17 -07005407 if (typeIter != nonIoTypeMap.end()) {
5408 // reuse deep structure if we have sanitized it before, but we must preserve
5409 // our unique shallow structure, which may not be shared with other users of
5410 // the deep copy. Create a new type with the sanitized qualifier, and the
5411 // shared deep structure
5412 type->setStruct(typeIter->second); // share already sanitized deep structure.
steve-lunarga2e75312016-12-14 15:22:25 -07005413 } else {
steve-lunarg5d3023a2017-01-25 10:03:17 -07005414 // The type contains interstage IO, but we've never encountered it before.
5415 // Copy it, scrub data we don't want for an non-IO type, and remember it in the nonIoTypeMap
5416
5417 TType nonIoType;
5418 nonIoType.deepCopy(*type);
5419 nonIoType.makeNonIo();
5420
5421 // remember the new deep structure in a map, so we can share it in the future.
5422 nonIoTypeMap[type->getStruct()] = nonIoType.getWritableStruct();
5423 type->shallowCopy(nonIoType); // we modify the input type in place
5424 }
steve-lunarga2e75312016-12-14 15:22:25 -07005425}
5426
John Kessenich5e69ec62016-07-05 00:02:40 -06005427//
John Kesseniche01a9bc2016-03-12 20:11:22 -07005428// Do everything necessary to handle a variable (non-block) declaration.
5429// Either redeclaring a variable, or making a new one, updating the symbol
5430// table, and all error checking.
5431//
5432// Returns a subtree node that computes an initializer, if needed.
5433// Returns nullptr if there is no code to execute for initialization.
5434//
John Kessenich5e69ec62016-07-05 00:02:40 -06005435// 'parseType' is the type part of the declaration (to the left)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005436// 'arraySizes' is the arrayness tagged on the identifier (to the right)
5437//
John Kesseniche82061d2016-09-27 14:38:57 -06005438TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& identifier, TType& type, TIntermTyped* initializer)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005439{
John Kesseniche01a9bc2016-03-12 20:11:22 -07005440 if (voidErrorCheck(loc, identifier, type.getBasicType()))
5441 return nullptr;
5442
John Kessenichaa6d5622016-12-30 16:42:57 -07005443 // make const and initialization consistent
5444 fixConstInit(loc, identifier, type, initializer);
5445
John Kesseniche01a9bc2016-03-12 20:11:22 -07005446 // Check for redeclaration of built-ins and/or attempting to declare a reserved name
John Kessenichd3f11222016-11-05 10:15:53 -06005447 TSymbol* symbol = nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005448
5449 inheritGlobalDefaults(type.getQualifier());
5450
John Kessenich02467d82017-01-19 15:41:47 -07005451 const bool flattenVar = shouldFlattenUniform(type);
steve-lunarga2e75312016-12-14 15:22:25 -07005452
steve-lunarg5d3023a2017-01-25 10:03:17 -07005453 // make non-IO version of type
5454 switch (type.getQualifier().storage) {
5455 case EvqGlobal:
5456 case EvqTemporary:
John Kessenichabd8dca2017-02-01 18:09:17 -07005457 makeTypeNonIo(&type);
steve-lunarg5d3023a2017-01-25 10:03:17 -07005458 default:
5459 break;
5460 }
steve-lunarge0b9deb2016-09-16 13:26:37 -06005461
John Kesseniche01a9bc2016-03-12 20:11:22 -07005462 // Declare the variable
John Kesseniche82061d2016-09-27 14:38:57 -06005463 if (type.isArray()) {
5464 // array case
steve-lunarg5d3023a2017-01-25 10:03:17 -07005465 declareArray(loc, identifier, type, symbol, !flattenVar);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005466 } else {
5467 // non-array case
5468 if (! symbol)
steve-lunarg5d3023a2017-01-25 10:03:17 -07005469 symbol = declareNonArray(loc, identifier, type, !flattenVar);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005470 else if (type != symbol->getType())
5471 error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
5472 }
5473
steve-lunarga2b01a02016-11-28 17:09:54 -07005474 if (flattenVar)
5475 flatten(loc, *symbol->getAsVariable());
5476
John Kesseniche01a9bc2016-03-12 20:11:22 -07005477 if (! symbol)
5478 return nullptr;
5479
5480 // Deal with initializer
5481 TIntermNode* initNode = nullptr;
5482 if (symbol && initializer) {
steve-lunarge0b9deb2016-09-16 13:26:37 -06005483 if (flattenVar)
5484 error(loc, "flattened array with initializer list unsupported", identifier.c_str(), "");
5485
John Kesseniche01a9bc2016-03-12 20:11:22 -07005486 TVariable* variable = symbol->getAsVariable();
5487 if (! variable) {
5488 error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
5489 return nullptr;
5490 }
5491 initNode = executeInitializer(loc, initializer, variable);
5492 }
5493
John Kesseniche01a9bc2016-03-12 20:11:22 -07005494 return initNode;
5495}
5496
5497// Pick up global defaults from the provide global defaults into dst.
5498void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const
5499{
5500 if (dst.storage == EvqVaryingOut) {
5501 if (! dst.hasStream() && language == EShLangGeometry)
5502 dst.layoutStream = globalOutputDefaults.layoutStream;
5503 if (! dst.hasXfbBuffer())
5504 dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
5505 }
5506}
5507
5508//
5509// Make an internal-only variable whose name is for debug purposes only
5510// and won't be searched for. Callers will only use the return value to use
5511// the variable, not the name to look it up. It is okay if the name
5512// is the same as other names; there won't be any conflict.
5513//
5514TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const
5515{
John Kesseniche48b8d72017-01-19 15:29:25 -07005516 TString* nameString = NewPoolTString(name);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005517 TVariable* variable = new TVariable(nameString, type);
5518 symbolTable.makeInternalVariable(*variable);
5519
5520 return variable;
5521}
5522
5523//
5524// Declare a non-array variable, the main point being there is no redeclaration
5525// for resizing allowed.
5526//
5527// Return the successfully declared variable.
5528//
steve-lunarga2b01a02016-11-28 17:09:54 -07005529TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, TString& identifier, TType& type, bool track)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005530{
5531 // make a new variable
5532 TVariable* variable = new TVariable(&identifier, type);
5533
5534 // add variable to symbol table
John Kessenichd3f11222016-11-05 10:15:53 -06005535 if (symbolTable.insert(*variable)) {
steve-lunarga2b01a02016-11-28 17:09:54 -07005536 if (track && symbolTable.atGlobalLevel())
John Kessenich02467d82017-01-19 15:41:47 -07005537 trackLinkage(*variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005538 return variable;
5539 }
John Kessenichd3f11222016-11-05 10:15:53 -06005540
5541 error(loc, "redefinition", variable->getName().c_str(), "");
5542 return nullptr;
John Kesseniche01a9bc2016-03-12 20:11:22 -07005543}
5544
5545//
5546// Handle all types of initializers from the grammar.
5547//
5548// Returning nullptr just means there is no code to execute to handle the
5549// initializer, which will, for example, be the case for constant initializers.
5550//
5551TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
5552{
5553 //
5554 // Identifier must be of type constant, a global, or a temporary, and
5555 // starting at version 120, desktop allows uniforms to have initializers.
5556 //
5557 TStorageQualifier qualifier = variable->getType().getQualifier().storage;
5558
5559 //
5560 // If the initializer was from braces { ... }, we convert the whole subtree to a
5561 // constructor-style subtree, allowing the rest of the code to operate
5562 // identically for both kinds of initializers.
5563 //
John Kessenich085b8332017-01-05 10:28:26 -07005564 //
5565 // Type can't be deduced from the initializer list, so a skeletal type to
5566 // follow has to be passed in. Constness and specialization-constness
5567 // should be deduced bottom up, not dictated by the skeletal type.
5568 //
5569 TType skeletalType;
5570 skeletalType.shallowCopy(variable->getType());
5571 skeletalType.getQualifier().makeTemporary();
John Kessenich98ad4852016-11-27 17:39:07 -07005572 if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
John Kessenich085b8332017-01-05 10:28:26 -07005573 initializer = convertInitializerList(loc, skeletalType, initializer);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005574 if (! initializer) {
5575 // error recovery; don't leave const without constant values
5576 if (qualifier == EvqConst)
5577 variable->getWritableType().getQualifier().storage = EvqTemporary;
5578 return nullptr;
5579 }
5580
5581 // Fix outer arrayness if variable is unsized, getting size from the initializer
5582 if (initializer->getType().isExplicitlySizedArray() &&
5583 variable->getType().isImplicitlySizedArray())
5584 variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize());
5585
5586 // Inner arrayness can also get set by an initializer
5587 if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() &&
5588 initializer->getType().getArraySizes()->getNumDims() ==
5589 variable->getType().getArraySizes()->getNumDims()) {
5590 // adopt unsized sizes from the initializer's sizes
5591 for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) {
5592 if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize)
5593 variable->getWritableType().getArraySizes().setDimSize(d, initializer->getType().getArraySizes()->getDimSize(d));
5594 }
5595 }
5596
5597 // Uniform and global consts require a constant initializer
5598 if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) {
5599 error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
5600 variable->getWritableType().getQualifier().storage = EvqTemporary;
5601 return nullptr;
5602 }
5603 if (qualifier == EvqConst && symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) {
5604 error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
5605 variable->getWritableType().getQualifier().storage = EvqTemporary;
5606 return nullptr;
5607 }
5608
5609 // Const variables require a constant initializer, depending on version
5610 if (qualifier == EvqConst) {
5611 if (initializer->getType().getQualifier().storage != EvqConst) {
5612 variable->getWritableType().getQualifier().storage = EvqConstReadOnly;
5613 qualifier = EvqConstReadOnly;
5614 }
5615 }
5616
5617 if (qualifier == EvqConst || qualifier == EvqUniform) {
5618 // Compile-time tagging of the variable with its constant value...
5619
5620 initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
5621 if (! initializer || ! initializer->getAsConstantUnion() || variable->getType() != initializer->getType()) {
5622 error(loc, "non-matching or non-convertible constant type for const initializer",
5623 variable->getType().getStorageQualifierString(), "");
5624 variable->getWritableType().getQualifier().storage = EvqTemporary;
5625 return nullptr;
5626 }
5627
5628 variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
5629 } else {
5630 // normal assigning of a value to a variable...
5631 specializationCheck(loc, initializer->getType(), "initializer");
5632 TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
steve-lunarge0b9deb2016-09-16 13:26:37 -06005633 TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005634 if (! initNode)
5635 assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
5636
5637 return initNode;
5638 }
5639
5640 return nullptr;
5641}
5642
5643//
5644// Reprocess any initializer-list { ... } parts of the initializer.
5645// Need to hierarchically assign correct types and implicit
5646// conversions. Will do this mimicking the same process used for
5647// creating a constructor-style initializer, ensuring we get the
5648// same form.
5649//
John Kessenichf97f2ce2016-11-27 22:51:36 -07005650// Returns a node representing an expression for the initializer list expressed
5651// as the correct type.
5652//
5653// Returns nullptr if there is an error.
5654//
John Kesseniche01a9bc2016-03-12 20:11:22 -07005655TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer)
5656{
5657 // Will operate recursively. Once a subtree is found that is constructor style,
5658 // everything below it is already good: Only the "top part" of the initializer
5659 // can be an initializer list, where "top part" can extend for several (or all) levels.
5660
5661 // see if we have bottomed out in the tree within the initializer-list part
5662 TIntermAggregate* initList = initializer->getAsAggregate();
John Kessenich98ad4852016-11-27 17:39:07 -07005663 if (! initList || initList->getOp() != EOpNull) {
5664 // We don't have a list, but if it's a scalar and the 'type' is a
5665 // composite, we need to lengthen below to make it useful.
5666 // Otherwise, this is an already formed object to initialize with.
5667 if (type.isScalar() || !initializer->getType().isScalar())
5668 return initializer;
5669 else
5670 initList = intermediate.makeAggregate(initializer);
5671 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005672
5673 // Of the initializer-list set of nodes, need to process bottom up,
5674 // so recurse deep, then process on the way up.
5675
5676 // Go down the tree here...
5677 if (type.isArray()) {
5678 // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate.
5679 // Later on, initializer execution code will deal with array size logic.
5680 TType arrayType;
5681 arrayType.shallowCopy(type); // sharing struct stuff is fine
5682 arrayType.newArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below
5683
5684 // edit array sizes to fill in unsized dimensions
John Kessenich98ad4852016-11-27 17:39:07 -07005685 if (type.isImplicitlySizedArray())
5686 arrayType.changeOuterArraySize((int)initList->getSequence().size());
John Kessenich53864842016-12-30 15:59:28 -07005687
5688 // set unsized array dimensions that can be derived from the initializer's first element
5689 if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) {
5690 TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped();
5691 if (firstInit->getType().isArray() &&
5692 arrayType.getArraySizes().getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) {
5693 for (int d = 1; d < arrayType.getArraySizes().getNumDims(); ++d) {
5694 if (arrayType.getArraySizes().getDimSize(d) == UnsizedArraySize)
5695 arrayType.getArraySizes().setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1));
5696 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005697 }
5698 }
5699
John Kessenich98ad4852016-11-27 17:39:07 -07005700 // lengthen list to be long enough
5701 lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize());
5702
5703 // recursively process each element
John Kesseniche01a9bc2016-03-12 20:11:22 -07005704 TType elementType(arrayType, 0); // dereferenced type
John Kessenich98ad4852016-11-27 17:39:07 -07005705 for (int i = 0; i < arrayType.getOuterArraySize(); ++i) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005706 initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped());
5707 if (initList->getSequence()[i] == nullptr)
5708 return nullptr;
5709 }
5710
John Kessenicha26a5172016-07-28 15:29:35 -06005711 return addConstructor(loc, initList, arrayType);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005712 } else if (type.isStruct()) {
John Kessenich98ad4852016-11-27 17:39:07 -07005713 // lengthen list to be long enough
Jamie Madill3ec327c2016-12-13 17:30:58 -05005714 lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()));
John Kessenich98ad4852016-11-27 17:39:07 -07005715
John Kesseniche01a9bc2016-03-12 20:11:22 -07005716 if (type.getStruct()->size() != initList->getSequence().size()) {
5717 error(loc, "wrong number of structure members", "initializer list", "");
5718 return nullptr;
5719 }
5720 for (size_t i = 0; i < type.getStruct()->size(); ++i) {
5721 initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped());
5722 if (initList->getSequence()[i] == nullptr)
5723 return nullptr;
5724 }
5725 } else if (type.isMatrix()) {
steve-lunarg297ae212016-08-24 14:36:13 -06005726 if (type.computeNumComponents() == (int)initList->getSequence().size()) {
5727 // This means the matrix is initialized component-wise, rather than as
5728 // a series of rows and columns. We can just use the list directly as
5729 // a constructor; no further processing needed.
5730 } else {
John Kessenich98ad4852016-11-27 17:39:07 -07005731 // lengthen list to be long enough
5732 lengthenList(loc, initList->getSequence(), type.getMatrixCols());
5733
steve-lunarg297ae212016-08-24 14:36:13 -06005734 if (type.getMatrixCols() != (int)initList->getSequence().size()) {
5735 error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
John Kesseniche01a9bc2016-03-12 20:11:22 -07005736 return nullptr;
steve-lunarg297ae212016-08-24 14:36:13 -06005737 }
5738 TType vectorType(type, 0); // dereferenced type
5739 for (int i = 0; i < type.getMatrixCols(); ++i) {
5740 initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped());
5741 if (initList->getSequence()[i] == nullptr)
5742 return nullptr;
5743 }
John Kesseniche01a9bc2016-03-12 20:11:22 -07005744 }
5745 } else if (type.isVector()) {
John Kessenich98ad4852016-11-27 17:39:07 -07005746 // lengthen list to be long enough
5747 lengthenList(loc, initList->getSequence(), type.getVectorSize());
5748
5749 // error check; we're at bottom, so work is finished below
John Kesseniche01a9bc2016-03-12 20:11:22 -07005750 if (type.getVectorSize() != (int)initList->getSequence().size()) {
5751 error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", type.getCompleteString().c_str());
5752 return nullptr;
5753 }
steve-lunargfe5a3ff2016-07-30 10:36:09 -06005754 } else if (type.isScalar()) {
John Kessenich53864842016-12-30 15:59:28 -07005755 // lengthen list to be long enough
5756 lengthenList(loc, initList->getSequence(), 1);
5757
steve-lunargfe5a3ff2016-07-30 10:36:09 -06005758 if ((int)initList->getSequence().size() != 1) {
5759 error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str());
5760 return nullptr;
5761 }
John Kessenich98ad4852016-11-27 17:39:07 -07005762 } else {
John Kesseniche01a9bc2016-03-12 20:11:22 -07005763 error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
5764 return nullptr;
5765 }
5766
John Kessenichff132132016-07-29 18:22:22 -06005767 // Now that the subtree is processed, process this node as if the
5768 // initializer list is a set of arguments to a constructor.
5769 TIntermNode* emulatedConstructorArguments;
5770 if (initList->getSequence().size() == 1)
5771 emulatedConstructorArguments = initList->getSequence()[0];
5772 else
5773 emulatedConstructorArguments = initList;
John Kessenich98ad4852016-11-27 17:39:07 -07005774
John Kessenich64285c92017-01-05 10:45:32 -07005775 return addConstructor(loc, emulatedConstructorArguments, type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005776}
5777
John Kessenich98ad4852016-11-27 17:39:07 -07005778// Lengthen list to be long enough to cover any gap from the current list size
5779// to 'size'. If the list is longer, do nothing.
5780// The value to lengthen with is the default for short lists.
5781void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size)
5782{
5783 for (int c = (int)list.size(); c < size; ++c)
5784 list.push_back(intermediate.addConstantUnion(0, loc));
5785}
5786
John Kesseniche01a9bc2016-03-12 20:11:22 -07005787//
5788// Test for the correctness of the parameters passed to various constructor functions
5789// and also convert them to the right data type, if allowed and required.
5790//
5791// Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
5792//
John Kessenicha26a5172016-07-28 15:29:35 -06005793TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005794{
5795 if (node == nullptr || node->getAsTyped() == nullptr)
5796 return nullptr;
5797
John Kessenichf97f2ce2016-11-27 22:51:36 -07005798 // Handle the idiom "(struct type)0"
5799 if (type.isStruct() && isZeroConstructor(node))
5800 return convertInitializerList(loc, type, intermediate.makeAggregate(loc));
5801
John Kesseniche01a9bc2016-03-12 20:11:22 -07005802 TIntermAggregate* aggrNode = node->getAsAggregate();
John Kessenicha26a5172016-07-28 15:29:35 -06005803 TOperator op = intermediate.mapTypeToConstructorOp(type);
John Kesseniche01a9bc2016-03-12 20:11:22 -07005804
5805 // Combined texture-sampler constructors are completely semantic checked
5806 // in constructorTextureSamplerError()
5807 if (op == EOpConstructTextureSampler)
5808 return intermediate.setAggregateOperator(aggrNode, op, type, loc);
5809
5810 TTypeList::const_iterator memberTypes;
5811 if (op == EOpConstructStruct)
5812 memberTypes = type.getStruct()->begin();
5813
5814 TType elementType;
5815 if (type.isArray()) {
5816 TType dereferenced(type, 0);
5817 elementType.shallowCopy(dereferenced);
5818 } else
5819 elementType.shallowCopy(type);
5820
5821 bool singleArg;
5822 if (aggrNode) {
5823 if (aggrNode->getOp() != EOpNull || aggrNode->getSequence().size() == 1)
5824 singleArg = true;
5825 else
5826 singleArg = false;
5827 } else
5828 singleArg = true;
5829
5830 TIntermTyped *newNode;
5831 if (singleArg) {
5832 // If structure constructor or array constructor is being called
5833 // for only one parameter inside the structure, we need to call constructAggregate function once.
5834 if (type.isArray())
5835 newNode = constructAggregate(node, elementType, 1, node->getLoc());
5836 else if (op == EOpConstructStruct)
5837 newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
5838 else
5839 newNode = constructBuiltIn(type, op, node->getAsTyped(), node->getLoc(), false);
5840
5841 if (newNode && (type.isArray() || op == EOpConstructStruct))
5842 newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc);
5843
5844 return newNode;
5845 }
5846
5847 //
5848 // Handle list of arguments.
5849 //
5850 TIntermSequence &sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor
5851 // if the structure constructor contains more than one parameter, then construct
5852 // each parameter
5853
5854 int paramCount = 0; // keeps a track of the constructor parameter number being checked
5855
5856 // for each parameter to the constructor call, check to see if the right type is passed or convert them
5857 // to the right type if possible (and allowed).
5858 // for structure constructors, just check if the right type is passed, no conversion is allowed.
5859
5860 for (TIntermSequence::iterator p = sequenceVector.begin();
5861 p != sequenceVector.end(); p++, paramCount++) {
5862 if (type.isArray())
5863 newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc());
5864 else if (op == EOpConstructStruct)
5865 newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc());
5866 else
5867 newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
5868
5869 if (newNode)
5870 *p = newNode;
5871 else
5872 return nullptr;
5873 }
5874
5875 TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
5876
5877 return constructor;
5878}
5879
5880// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value
5881// for the parameter to the constructor (passed to this function). Essentially, it converts
5882// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a
5883// float, then float is converted to int.
5884//
5885// Returns nullptr for an error or the constructed node.
5886//
5887TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc, bool subset)
5888{
5889 TIntermTyped* newNode;
5890 TOperator basicOp;
5891
5892 //
5893 // First, convert types as needed.
5894 //
5895 switch (op) {
5896 case EOpConstructVec2:
5897 case EOpConstructVec3:
5898 case EOpConstructVec4:
5899 case EOpConstructMat2x2:
5900 case EOpConstructMat2x3:
5901 case EOpConstructMat2x4:
5902 case EOpConstructMat3x2:
5903 case EOpConstructMat3x3:
5904 case EOpConstructMat3x4:
5905 case EOpConstructMat4x2:
5906 case EOpConstructMat4x3:
5907 case EOpConstructMat4x4:
5908 case EOpConstructFloat:
5909 basicOp = EOpConstructFloat;
5910 break;
5911
5912 case EOpConstructDVec2:
5913 case EOpConstructDVec3:
5914 case EOpConstructDVec4:
5915 case EOpConstructDMat2x2:
5916 case EOpConstructDMat2x3:
5917 case EOpConstructDMat2x4:
5918 case EOpConstructDMat3x2:
5919 case EOpConstructDMat3x3:
5920 case EOpConstructDMat3x4:
5921 case EOpConstructDMat4x2:
5922 case EOpConstructDMat4x3:
5923 case EOpConstructDMat4x4:
5924 case EOpConstructDouble:
5925 basicOp = EOpConstructDouble;
5926 break;
5927
5928 case EOpConstructIVec2:
5929 case EOpConstructIVec3:
5930 case EOpConstructIVec4:
5931 case EOpConstructInt:
5932 basicOp = EOpConstructInt;
5933 break;
5934
5935 case EOpConstructUVec2:
5936 case EOpConstructUVec3:
5937 case EOpConstructUVec4:
5938 case EOpConstructUint:
5939 basicOp = EOpConstructUint;
5940 break;
5941
5942 case EOpConstructBVec2:
5943 case EOpConstructBVec3:
5944 case EOpConstructBVec4:
5945 case EOpConstructBool:
5946 basicOp = EOpConstructBool;
5947 break;
5948
5949 default:
5950 error(loc, "unsupported construction", "", "");
5951
5952 return nullptr;
5953 }
5954 newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc());
5955 if (newNode == nullptr) {
5956 error(loc, "can't convert", "constructor", "");
5957 return nullptr;
5958 }
5959
5960 //
5961 // Now, if there still isn't an operation to do the construction, and we need one, add one.
5962 //
5963
5964 // Otherwise, skip out early.
5965 if (subset || (newNode != node && newNode->getType() == type))
5966 return newNode;
5967
5968 // setAggregateOperator will insert a new node for the constructor, as needed.
5969 return intermediate.setAggregateOperator(newNode, op, type, loc);
5970}
5971
5972// This function tests for the type of the parameters to the structure or array constructor. Raises
5973// an error message if the expected type does not match the parameter passed to the constructor.
5974//
5975// Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
5976//
5977TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc)
5978{
5979 TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
5980 if (! converted || converted->getType() != type) {
5981 error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
5982 node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
5983
5984 return nullptr;
5985 }
5986
5987 return converted;
5988}
5989
5990//
5991// Do everything needed to add an interface block.
5992//
John Kessenich3d157c52016-07-25 16:05:33 -06005993void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName, TArraySizes* arraySizes)
John Kesseniche01a9bc2016-03-12 20:11:22 -07005994{
John Kessenich3d157c52016-07-25 16:05:33 -06005995 assert(type.getWritableStruct() != nullptr);
5996
5997 TTypeList& typeList = *type.getWritableStruct();
John Kesseniche01a9bc2016-03-12 20:11:22 -07005998 // fix and check for member storage qualifiers and types that don't belong within a block
5999 for (unsigned int member = 0; member < typeList.size(); ++member) {
6000 TType& memberType = *typeList[member].type;
6001 TQualifier& memberQualifier = memberType.getQualifier();
6002 const TSourceLoc& memberLoc = typeList[member].loc;
6003 globalQualifierFix(memberLoc, memberQualifier);
John Kessenich3d157c52016-07-25 16:05:33 -06006004 memberQualifier.storage = type.getQualifier().storage;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006005 }
6006
6007 // This might be a redeclaration of a built-in block. If so, redeclareBuiltinBlock() will
6008 // do all the rest.
John Kessenich927608b2017-01-06 12:34:14 -07006009 // if (! symbolTable.atBuiltInLevel() && builtInName(*blockName)) {
John Kessenich3d157c52016-07-25 16:05:33 -06006010 // redeclareBuiltinBlock(loc, typeList, *blockName, instanceName, arraySizes);
6011 // return;
6012 //}
John Kesseniche01a9bc2016-03-12 20:11:22 -07006013
6014 // Make default block qualification, and adjust the member qualifications
6015
6016 TQualifier defaultQualification;
John Kessenich3d157c52016-07-25 16:05:33 -06006017 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006018 case EvqUniform: defaultQualification = globalUniformDefaults; break;
6019 case EvqBuffer: defaultQualification = globalBufferDefaults; break;
6020 case EvqVaryingIn: defaultQualification = globalInputDefaults; break;
6021 case EvqVaryingOut: defaultQualification = globalOutputDefaults; break;
6022 default: defaultQualification.clear(); break;
6023 }
6024
6025 // Special case for "push_constant uniform", which has a default of std430,
6026 // contrary to normal uniform defaults, and can't have a default tracked for it.
John Kessenich3d157c52016-07-25 16:05:33 -06006027 if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking())
6028 type.getQualifier().layoutPacking = ElpStd430;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006029
6030 // fix and check for member layout qualifiers
6031
John Kessenich3d157c52016-07-25 16:05:33 -06006032 mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006033
6034 bool memberWithLocation = false;
6035 bool memberWithoutLocation = false;
6036 for (unsigned int member = 0; member < typeList.size(); ++member) {
6037 TQualifier& memberQualifier = typeList[member].type->getQualifier();
6038 const TSourceLoc& memberLoc = typeList[member].loc;
6039 if (memberQualifier.hasStream()) {
6040 if (defaultQualification.layoutStream != memberQualifier.layoutStream)
6041 error(memberLoc, "member cannot contradict block", "stream", "");
6042 }
6043
John Kessenichecba76f2017-01-06 00:34:48 -07006044 // "This includes a block's inheritance of the
6045 // current global default buffer, a block member's inheritance of the block's
6046 // buffer, and the requirement that any *xfb_buffer* declared on a block
John Kesseniche01a9bc2016-03-12 20:11:22 -07006047 // member must match the buffer inherited from the block."
6048 if (memberQualifier.hasXfbBuffer()) {
6049 if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer)
6050 error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
6051 }
6052
6053 if (memberQualifier.hasPacking())
6054 error(memberLoc, "member of block cannot have a packing layout qualifier", typeList[member].type->getFieldName().c_str(), "");
6055 if (memberQualifier.hasLocation()) {
John Kessenich3d157c52016-07-25 16:05:33 -06006056 switch (type.getQualifier().storage) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006057 case EvqVaryingIn:
6058 case EvqVaryingOut:
6059 memberWithLocation = true;
6060 break;
6061 default:
6062 break;
6063 }
6064 } else
6065 memberWithoutLocation = true;
6066 if (memberQualifier.hasAlign()) {
6067 if (defaultQualification.layoutPacking != ElpStd140 && defaultQualification.layoutPacking != ElpStd430)
6068 error(memberLoc, "can only be used with std140 or std430 layout packing", "align", "");
6069 }
6070
6071 TQualifier newMemberQualification = defaultQualification;
John Kessenich34e7ee72016-09-16 17:10:39 -06006072 mergeQualifiers(newMemberQualification, memberQualifier);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006073 memberQualifier = newMemberQualification;
6074 }
6075
6076 // Process the members
John Kessenich3d157c52016-07-25 16:05:33 -06006077 fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation);
6078 fixBlockXfbOffsets(type.getQualifier(), typeList);
6079 fixBlockUniformOffsets(type.getQualifier(), typeList);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006080
6081 // reverse merge, so that currentBlockQualifier now has all layout information
6082 // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
John Kessenich3d157c52016-07-25 16:05:33 -06006083 mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006084
6085 //
6086 // Build and add the interface block as a new type named 'blockName'
6087 //
6088
steve-lunarg8ffc36a2016-09-21 14:19:40 -06006089 // Use the instance name as the interface name if one exists, else the block name.
6090 const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName();
6091
6092 TType blockType(&typeList, interfaceName, type.getQualifier());
John Kesseniche01a9bc2016-03-12 20:11:22 -07006093 if (arraySizes)
6094 blockType.newArraySizes(*arraySizes);
6095
John Kesseniche01a9bc2016-03-12 20:11:22 -07006096 // Add the variable, as anonymous or named instanceName.
6097 // Make an anonymous variable if no name was provided.
6098 if (! instanceName)
6099 instanceName = NewPoolTString("");
6100
6101 TVariable& variable = *new TVariable(instanceName, blockType);
6102 if (! symbolTable.insert(variable)) {
6103 if (*instanceName == "")
John Kessenich3d157c52016-07-25 16:05:33 -06006104 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 -07006105 else
6106 error(loc, "block instance name redefinition", variable.getName().c_str(), "");
6107
6108 return;
6109 }
6110
John Kesseniche01a9bc2016-03-12 20:11:22 -07006111 // Save it in the AST for linker use.
John Kessenich02467d82017-01-19 15:41:47 -07006112 trackLinkage(variable);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006113}
6114
John Kessenich6dbc0a72016-09-27 19:13:05 -06006115void HlslParseContext::finalizeGlobalUniformBlockLayout(TVariable& block)
6116{
6117 block.getWritableType().getQualifier().layoutPacking = ElpStd140;
6118 block.getWritableType().getQualifier().layoutMatrix = ElmRowMajor;
6119 fixBlockUniformOffsets(block.getType().getQualifier(), *block.getWritableType().getWritableStruct());
6120}
6121
John Kesseniche01a9bc2016-03-12 20:11:22 -07006122//
John Kessenichecba76f2017-01-06 00:34:48 -07006123// "For a block, this process applies to the entire block, or until the first member
6124// is reached that has a location layout qualifier. When a block member is declared with a location
John Kesseniche01a9bc2016-03-12 20:11:22 -07006125// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level
John Kessenichecba76f2017-01-06 00:34:48 -07006126// declaration. Subsequent members are again assigned consecutive locations, based on the newest location,
6127// 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 -07006128// declared in increasing order."
6129void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation)
6130{
John Kessenichecba76f2017-01-06 00:34:48 -07006131 // "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 -07006132 // have a location layout qualifier, or a compile-time error results."
6133 if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation)
6134 error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", "");
6135 else {
6136 if (memberWithLocation) {
6137 // remove any block-level location and make it per *every* member
6138 int nextLocation = 0; // by the rule above, initial value is not relevant
6139 if (qualifier.hasAnyLocation()) {
6140 nextLocation = qualifier.layoutLocation;
6141 qualifier.layoutLocation = TQualifier::layoutLocationEnd;
6142 if (qualifier.hasComponent()) {
6143 // "It is a compile-time error to apply the *component* qualifier to a ... block"
6144 error(loc, "cannot apply to a block", "component", "");
6145 }
6146 if (qualifier.hasIndex()) {
6147 error(loc, "cannot apply to a block", "index", "");
6148 }
6149 }
6150 for (unsigned int member = 0; member < typeList.size(); ++member) {
6151 TQualifier& memberQualifier = typeList[member].type->getQualifier();
6152 const TSourceLoc& memberLoc = typeList[member].loc;
6153 if (! memberQualifier.hasLocation()) {
6154 if (nextLocation >= (int)TQualifier::layoutLocationEnd)
6155 error(memberLoc, "location is too large", "location", "");
6156 memberQualifier.layoutLocation = nextLocation;
6157 memberQualifier.layoutComponent = 0;
6158 }
6159 nextLocation = memberQualifier.layoutLocation + intermediate.computeTypeLocationSize(*typeList[member].type);
6160 }
6161 }
6162 }
6163}
6164
6165void HlslParseContext::fixBlockXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
6166{
John Kessenichecba76f2017-01-06 00:34:48 -07006167 // "If a block is qualified with xfb_offset, all its
6168 // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any
6169 // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer
John Kesseniche01a9bc2016-03-12 20:11:22 -07006170 // offsets."
6171
6172 if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset())
6173 return;
6174
6175 int nextOffset = qualifier.layoutXfbOffset;
6176 for (unsigned int member = 0; member < typeList.size(); ++member) {
6177 TQualifier& memberQualifier = typeList[member].type->getQualifier();
6178 bool containsDouble = false;
6179 int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, containsDouble);
6180 // see if we need to auto-assign an offset to this member
6181 if (! memberQualifier.hasXfbOffset()) {
6182 // "if applied to an aggregate containing a double, the offset must also be a multiple of 8"
6183 if (containsDouble)
6184 RoundToPow2(nextOffset, 8);
6185 memberQualifier.layoutXfbOffset = nextOffset;
6186 } else
6187 nextOffset = memberQualifier.layoutXfbOffset;
6188 nextOffset += memberSize;
6189 }
6190
6191 // The above gave all block members an offset, so we can take it off the block now,
6192 // which will avoid double counting the offset usage.
6193 qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd;
6194}
6195
John Kessenichecba76f2017-01-06 00:34:48 -07006196// Calculate and save the offset of each block member, using the recursively
John Kesseniche01a9bc2016-03-12 20:11:22 -07006197// defined block offset rules and the user-provided offset and align.
6198//
John Kessenichecba76f2017-01-06 00:34:48 -07006199// Also, compute and save the total size of the block. For the block's size, arrayness
John Kesseniche01a9bc2016-03-12 20:11:22 -07006200// is not taken into account, as each element is backed by a separate buffer.
6201//
John Kessenich6dbc0a72016-09-27 19:13:05 -06006202void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList)
John Kesseniche01a9bc2016-03-12 20:11:22 -07006203{
6204 if (! qualifier.isUniformOrBuffer())
6205 return;
6206 if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430)
6207 return;
6208
6209 int offset = 0;
6210 int memberSize;
6211 for (unsigned int member = 0; member < typeList.size(); ++member) {
6212 TQualifier& memberQualifier = typeList[member].type->getQualifier();
6213 const TSourceLoc& memberLoc = typeList[member].loc;
6214
6215 // "When align is applied to an array, it effects only the start of the array, not the array's internal stride."
6216
6217 // modify just the children's view of matrix layout, if there is one for this member
6218 TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
6219 int dummyStride;
John Kessenich6dbc0a72016-09-27 19:13:05 -06006220 int memberAlignment = intermediate.getBaseAlignment(*typeList[member].type, memberSize, dummyStride,
6221 qualifier.layoutPacking == ElpStd140,
6222 subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor
6223 : qualifier.layoutMatrix == ElmRowMajor);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006224 if (memberQualifier.hasOffset()) {
John Kessenichecba76f2017-01-06 00:34:48 -07006225 // "The specified offset must be a multiple
John Kesseniche01a9bc2016-03-12 20:11:22 -07006226 // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
6227 if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment))
6228 error(memberLoc, "must be a multiple of the member's alignment", "offset", "");
6229
John Kessenichecba76f2017-01-06 00:34:48 -07006230 // "The offset qualifier forces the qualified member to start at or after the specified
6231 // integral-constant expression, which will be its byte offset from the beginning of the buffer.
6232 // "The actual offset of a member is computed as
John Kesseniche01a9bc2016-03-12 20:11:22 -07006233 // follows: If offset was declared, start with that offset, otherwise start with the next available offset."
6234 offset = std::max(offset, memberQualifier.layoutOffset);
6235 }
6236
John Kessenichecba76f2017-01-06 00:34:48 -07006237 // "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 -07006238 // (e.g., std140) base alignment for the member's type."
6239 if (memberQualifier.hasAlign())
6240 memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign);
6241
6242 // "If the resulting offset is not a multiple of the actual alignment,
John Kessenichecba76f2017-01-06 00:34:48 -07006243 // increase it to the first offset that is a multiple of
John Kesseniche01a9bc2016-03-12 20:11:22 -07006244 // the actual alignment."
6245 RoundToPow2(offset, memberAlignment);
6246 typeList[member].type->getQualifier().layoutOffset = offset;
6247 offset += memberSize;
6248 }
6249}
6250
6251// For an identifier that is already declared, add more qualification to it.
6252void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
6253{
6254 TSymbol* symbol = symbolTable.find(identifier);
6255 if (! symbol) {
6256 error(loc, "identifier not previously declared", identifier.c_str(), "");
6257 return;
6258 }
6259 if (symbol->getAsFunction()) {
6260 error(loc, "cannot re-qualify a function name", identifier.c_str(), "");
6261 return;
6262 }
6263
6264 if (qualifier.isAuxiliary() ||
6265 qualifier.isMemory() ||
6266 qualifier.isInterpolation() ||
6267 qualifier.hasLayout() ||
6268 qualifier.storage != EvqTemporary ||
6269 qualifier.precision != EpqNone) {
6270 error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), "");
6271 return;
6272 }
6273
6274 // For read-only built-ins, add a new symbol for holding the modified qualifier.
6275 // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block)
6276 if (symbol->isReadOnly())
6277 symbol = symbolTable.copyUp(symbol);
6278
6279 if (qualifier.invariant) {
6280 if (intermediate.inIoAccessed(identifier))
6281 error(loc, "cannot change qualification after use", "invariant", "");
6282 symbol->getWritableType().getQualifier().invariant = true;
John Kessenich17f07862016-05-04 12:36:14 -06006283 } else if (qualifier.noContraction) {
6284 if (intermediate.inIoAccessed(identifier))
6285 error(loc, "cannot change qualification after use", "precise", "");
6286 symbol->getWritableType().getQualifier().noContraction = true;
6287 } else if (qualifier.specConstant) {
6288 symbol->getWritableType().getQualifier().makeSpecConstant();
6289 if (qualifier.hasSpecConstantId())
6290 symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId;
John Kesseniche01a9bc2016-03-12 20:11:22 -07006291 } else
6292 warn(loc, "unknown requalification", "", "");
6293}
6294
6295void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers)
6296{
6297 for (unsigned int i = 0; i < identifiers.size(); ++i)
6298 addQualifierToExisting(loc, qualifier, *identifiers[i]);
6299}
6300
6301//
steve-lunargf49cdf42016-11-17 15:04:20 -07006302// Update the intermediate for the given input geometry
6303//
6304bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
6305{
6306 switch (geometry) {
6307 case ElgPoints: // fall through
6308 case ElgLines: // ...
6309 case ElgTriangles: // ...
6310 case ElgLinesAdjacency: // ...
6311 case ElgTrianglesAdjacency: // ...
6312 if (! intermediate.setInputPrimitive(geometry)) {
6313 error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
6314 return false;
6315 }
6316 break;
6317
6318 default:
6319 error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), "");
6320 return false;
6321 }
6322
6323 return true;
6324}
6325
6326//
6327// Update the intermediate for the given output geometry
6328//
6329bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
6330{
6331 switch (geometry) {
6332 case ElgPoints:
6333 case ElgLineStrip:
6334 case ElgTriangleStrip:
6335 if (! intermediate.setOutputPrimitive(geometry)) {
6336 error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
6337 return false;
6338 }
6339 break;
6340 default:
6341 error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), "");
6342 return false;
6343 }
6344
6345 return true;
6346}
6347
6348//
John Kesseniche01a9bc2016-03-12 20:11:22 -07006349// Updating default qualifier for the case of a declaration with just a qualifier,
6350// no type, block, or identifier.
6351//
6352void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType)
6353{
6354 if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) {
6355 assert(language == EShLangTessControl || language == EShLangGeometry);
John Kessenich7f349c72016-07-08 22:09:10 -06006356 // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices";
John Kesseniche01a9bc2016-03-12 20:11:22 -07006357 }
6358 if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) {
6359 if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations))
6360 error(loc, "cannot change previously set layout value", "invocations", "");
6361 }
6362 if (publicType.shaderQualifiers.geometry != ElgNone) {
6363 if (publicType.qualifier.storage == EvqVaryingIn) {
6364 switch (publicType.shaderQualifiers.geometry) {
6365 case ElgPoints:
6366 case ElgLines:
6367 case ElgLinesAdjacency:
6368 case ElgTriangles:
6369 case ElgTrianglesAdjacency:
6370 case ElgQuads:
6371 case ElgIsolines:
John Kesseniche01a9bc2016-03-12 20:11:22 -07006372 break;
6373 default:
6374 error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
6375 }
6376 } else if (publicType.qualifier.storage == EvqVaryingOut) {
steve-lunargf49cdf42016-11-17 15:04:20 -07006377 handleOutputGeometry(loc, publicType.shaderQualifiers.geometry);
John Kesseniche01a9bc2016-03-12 20:11:22 -07006378 } else
6379 error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), GetStorageQualifierString(publicType.qualifier.storage));
6380 }
6381 if (publicType.shaderQualifiers.spacing != EvsNone)
6382 intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing);
6383 if (publicType.shaderQualifiers.order != EvoNone)
6384 intermediate.setVertexOrder(publicType.shaderQualifiers.order);
6385 if (publicType.shaderQualifiers.pointMode)
6386 intermediate.setPointMode();
6387 for (int i = 0; i < 3; ++i) {
6388 if (publicType.shaderQualifiers.localSize[i] > 1) {
6389 int max = 0;
6390 switch (i) {
6391 case 0: max = resources.maxComputeWorkGroupSizeX; break;
6392 case 1: max = resources.maxComputeWorkGroupSizeY; break;
6393 case 2: max = resources.maxComputeWorkGroupSizeZ; break;
6394 default: break;
6395 }
6396 if (intermediate.getLocalSize(i) > (unsigned int)max)
6397 error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
6398
6399 // Fix the existing constant gl_WorkGroupSize with this new information.
6400 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
6401 workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
6402 }
6403 if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
6404 intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]);
6405 // Set the workgroup built-in variable as a specialization constant
6406 TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
6407 workGroupSize->getWritableType().getQualifier().specConstant = true;
6408 }
6409 }
6410 if (publicType.shaderQualifiers.earlyFragmentTests)
6411 intermediate.setEarlyFragmentTests();
6412
6413 const TQualifier& qualifier = publicType.qualifier;
6414
6415 switch (qualifier.storage) {
6416 case EvqUniform:
6417 if (qualifier.hasMatrix())
6418 globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
6419 if (qualifier.hasPacking())
6420 globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
6421 break;
6422 case EvqBuffer:
6423 if (qualifier.hasMatrix())
6424 globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
6425 if (qualifier.hasPacking())
6426 globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
6427 break;
6428 case EvqVaryingIn:
6429 break;
6430 case EvqVaryingOut:
6431 if (qualifier.hasStream())
6432 globalOutputDefaults.layoutStream = qualifier.layoutStream;
6433 if (qualifier.hasXfbBuffer())
6434 globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer;
6435 if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) {
6436 if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride))
6437 error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer);
6438 }
6439 break;
6440 default:
6441 error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
6442 return;
6443 }
6444}
6445
6446//
6447// Take the sequence of statements that has been built up since the last case/default,
6448// put it on the list of top-level nodes for the current (inner-most) switch statement,
6449// and follow that by the case/default we are on now. (See switch topology comment on
6450// TIntermSwitch.)
6451//
6452void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
6453{
6454 TIntermSequence* switchSequence = switchSequenceStack.back();
6455
6456 if (statements) {
John Kesseniche01a9bc2016-03-12 20:11:22 -07006457 statements->setOperator(EOpSequence);
6458 switchSequence->push_back(statements);
6459 }
6460 if (branchNode) {
6461 // check all previous cases for the same label (or both are 'default')
6462 for (unsigned int s = 0; s < switchSequence->size(); ++s) {
6463 TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
6464 if (prevBranch) {
6465 TIntermTyped* prevExpression = prevBranch->getExpression();
6466 TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
6467 if (prevExpression == nullptr && newExpression == nullptr)
6468 error(branchNode->getLoc(), "duplicate label", "default", "");
6469 else if (prevExpression != nullptr &&
6470 newExpression != nullptr &&
6471 prevExpression->getAsConstantUnion() &&
6472 newExpression->getAsConstantUnion() &&
6473 prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() ==
6474 newExpression->getAsConstantUnion()->getConstArray()[0].getIConst())
6475 error(branchNode->getLoc(), "duplicated value", "case", "");
6476 }
6477 }
6478 switchSequence->push_back(branchNode);
6479 }
6480}
6481
6482//
6483// Turn the top-level node sequence built up of wrapupSwitchSubsequence
6484// into a switch node.
6485//
6486TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements)
6487{
6488 wrapupSwitchSubsequence(lastStatements, nullptr);
6489
6490 if (expression == nullptr ||
6491 (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) ||
6492 expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector())
6493 error(loc, "condition must be a scalar integer expression", "switch", "");
6494
6495 // If there is nothing to do, drop the switch but still execute the expression
6496 TIntermSequence* switchSequence = switchSequenceStack.back();
6497 if (switchSequence->size() == 0)
6498 return expression;
6499
6500 if (lastStatements == nullptr) {
6501 // emulate a break for error recovery
6502 lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc));
6503 lastStatements->setOperator(EOpSequence);
6504 switchSequence->push_back(lastStatements);
6505 }
6506
6507 TIntermAggregate* body = new TIntermAggregate(EOpSequence);
6508 body->getSequence() = *switchSequenceStack.back();
6509 body->setLoc(loc);
6510
6511 TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
6512 switchNode->setLoc(loc);
6513
6514 return switchNode;
6515}
6516
steve-lunargf1e0c872016-10-31 15:13:43 -06006517// Potentially rename shader entry point function
6518void HlslParseContext::renameShaderFunction(TString*& name) const
6519{
6520 // Replace the entry point name given in the shader with the real entry point name,
6521 // if there is a substitution.
6522 if (name != nullptr && *name == sourceEntryPointName)
John Kesseniche48b8d72017-01-19 15:29:25 -07006523 name = NewPoolTString(intermediate.getEntryPointName().c_str());
steve-lunargf1e0c872016-10-31 15:13:43 -06006524}
6525
steve-lunarga2e75312016-12-14 15:22:25 -07006526// post-processing
6527void HlslParseContext::finish()
6528{
6529 addInterstageIoToLinkage();
6530
6531 TParseContextBase::finish();
6532}
6533
John Kesseniche01a9bc2016-03-12 20:11:22 -07006534} // end namespace glslang