Implement swizzles containing 0 and 1 to represent numbers not indexes.
diff --git a/glslang/MachineIndependent/ParseContextBase.cpp b/glslang/MachineIndependent/ParseContextBase.cpp
index b464009..0852862 100644
--- a/glslang/MachineIndependent/ParseContextBase.cpp
+++ b/glslang/MachineIndependent/ParseContextBase.cpp
@@ -481,13 +481,32 @@
// Look at a '.' field selector string and change it into numerical selectors
// for a vector or scalar.
//
+// These are returned as indexes in selector.
+// E.g. ".zy" will become selector = {2, 1}.
+//
// Always return some form of swizzle, so the result is always usable.
//
+// '0' and '1' in the field will mean to use the numeric values of 0 and 1
+// rather than the result of an index into the vector.
+// These are represented by:
+// '0': MaxSwizzleSelectors
+// '1': MaxSwizzleSelectors + 1
+// E.g., ".z01" will become selector = {2, 4, 5} (if MasSwizzleSelectors == 4).
+//
+// A leading underscore (prefix) will get ignored.
+//
void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize,
- TSwizzleSelectors<TVectorSelector>& selector)
+ TSwizzleSelectors<TVectorSelector>& selector, bool& numeric)
{
+ // a swizzle does not contain numerics unless there are actually numbers
+ // in it, independent of whether there is a prefix
+ numeric = false;
+
+ // If the field uses prefix syntax, normalize it.
+ const int firstChar = compString[0] == '_';
+
// Too long?
- if (compString.size() > MaxSwizzleSelectors)
+ if (compString.size() - firstChar > MaxSwizzleSelectors)
error(loc, "vector swizzle too long", compString.c_str(), "");
// Use this to test that all swizzle characters are from the same swizzle-namespace-set
@@ -495,12 +514,13 @@
exyzw,
ergba,
estpq,
- } fieldSet[MaxSwizzleSelectors];
+ enumeric,
+ } fieldSet[MaxSwizzleSelectors + 1];
// Decode the swizzle string.
- int size = std::min(MaxSwizzleSelectors, (int)compString.size());
+ const int size = std::min(MaxSwizzleSelectors, (int)compString.size() - firstChar);
for (int i = 0; i < size; ++i) {
- switch (compString[i]) {
+ switch (compString[i + firstChar]) {
case 'x':
selector.push_back(0);
fieldSet[i] = exyzw;
@@ -553,6 +573,17 @@
fieldSet[i] = estpq;
break;
+ case '0':
+ selector.push_back(MaxSwizzleSelectors);
+ fieldSet[i] = enumeric;
+ numeric = true;
+ break;
+ case '1':
+ selector.push_back(MaxSwizzleSelectors + 1);
+ fieldSet[i] = enumeric;
+ numeric = true;
+ break;
+
default:
error(loc, "unknown swizzle selection", compString.c_str(), "");
break;
@@ -561,13 +592,14 @@
// Additional error checking.
for (int i = 0; i < selector.size(); ++i) {
- if (selector[i] >= vecSize) {
+ if (selector[i] < MaxSwizzleSelectors && selector[i] >= vecSize) {
error(loc, "vector swizzle selection out of range", compString.c_str(), "");
selector.resize(i);
break;
}
- if (i > 0 && fieldSet[i] != fieldSet[i-1]) {
+ if (i > 0 && fieldSet[i] != enumeric && fieldSet[i-1] != enumeric &&
+ fieldSet[i] != fieldSet[i-1]) {
error(loc, "vector swizzle selectors not from the same set", compString.c_str(), "");
selector.resize(i);
break;
@@ -579,6 +611,24 @@
selector.push_back(0);
}
+void TParseContextBase::replicateRValue(TIntermTyped* node, int num, TVector<TIntermTyped*>& replicates)
+{
+ if (num == 0)
+ return;
+ if (num == 1) {
+ replicates.push_back(node);
+ return;
+ }
+ if (node->getAsSymbolNode()) {
+ replicates.push_back(node);
+ for (int i = 1; i < num; ++i)
+ replicates.push_back(intermediate.addSymbol(*node->getAsSymbolNode()));
+ }
+
+ // WIP: a complex expression needs to be evaluated exactly once, and then
+ // copies of the result put into the replicates.
+}
+
#ifdef ENABLE_HLSL
//
// Make the passed-in variable information become a member of the
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 117c164..fa83ef2 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -886,7 +886,8 @@
}
TSwizzleSelectors<TVectorSelector> selectors;
- parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
+ bool numeric = false;
+ parseSwizzleSelector(loc, field, base->getVectorSize(), selectors, numeric);
if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitFloat())
requireFloat16Arithmetic(loc, ".", "can't swizzle types containing float16");
@@ -895,6 +896,9 @@
if (base->isVector() && selectors.size() != 1 && base->getType().contains8BitInt())
requireInt8Arithmetic(loc, ".", "can't swizzle types containing (u)int8");
+ if (numeric)
+ return handleNumericDotSwizzle(loc, base, selectors);
+
if (base->isScalar()) {
if (selectors.size() == 1)
return result;
@@ -927,6 +931,93 @@
return result;
}
+// Handle a swizzle operation where at least one selector is numeric.
+//
+// Can return
+// - a scalar constant (e.g. for ._1), but converted to the right type
+// and constant folded
+// - a vector constructor
+// - a sequence containing
+// 1. evaluation of 'base'
+// 2. a scalar constant, converted, folded
+// - a sequence containing
+// 1. evaluation of 'base'
+// 2. a vector constructor
+//
+// Note that none of the above include swizzle operations.
+//
+// Note: A vector constructor might require copies of the rvalue being swizzled,
+// to avoid the tree accidentally becoming a DAG when there are multiple
+// letter swizzles present needing multiple operations to get the
+// components. This is quite unlike how swizzles are handled, or any
+// other native GLSL operation.
+//
+TIntermTyped* TParseContext::handleNumericDotSwizzle(const TSourceLoc& loc, TIntermTyped* base,
+ const TSwizzleSelectors<TVectorSelector>& selectors)
+{
+ const auto isLetter = [](int selector) { return selector < MaxSwizzleSelectors; };
+ const auto isNumber = [isLetter](int selector) { return !isLetter(selector); };
+ const auto getNumber = [](int selector) { return selector - MaxSwizzleSelectors; };
+
+ // The type of the result has the 'base' component type,
+ // but the component-count of 'selectors'.
+ TType type(base->getBasicType(), EvqTemporary, selectors.size());
+
+ // If only one selector, the result is a scalar.
+ // But, its type might be changing, so add a constructor.
+ // This will always result in an already folded scalar front-end constant.
+ if (selectors.size() == 1) {
+ assert(isNumber(selectors[0]));
+ return addConstructor(loc, intermediate.addConstantUnion(getNumber(selectors[0]), loc), type);
+
+ // WIP: this is incorrect if 'base' had side effects, it still needs to
+ // be evaluated as part of a sequence operation, unless the
+ // specification for this operation says those side effects are ignored.
+ }
+
+ // Otherwise, the result is like making a vector constructor,
+ // where we know we have more than one argument.
+
+ // Collect the arguments.
+ // This is complicated by the presence of more than one letter selector,
+ // because we need to reuse the r-value for each one, so it is a rare
+ // situation of needing to replicate the r-value.
+
+ // count the letter selectors (unless the base is a constant))
+ int letterCount = 0;
+ if (!base->getType().getQualifier().isFrontEndConstant()) {
+ for (int s = 0; s < selectors.size(); ++s)
+ letterCount += isLetter(selectors[s]) ? 1 : 0;
+ }
+ // get the replicates
+ TVector<TIntermTyped*> replicates;
+ replicateRValue(base, letterCount, replicates);
+
+ // process all the selectors to make the vector
+ TIntermAggregate* args = nullptr;
+ for (int s = 0; s < (int)selectors.size(); ++s) {
+ if (isNumber(selectors[s])) {
+ args = intermediate.growAggregate(args, intermediate.addConstantUnion(getNumber(selectors[s]), loc));
+ } else {
+ // traditional swizzle selector, which needs to consume the replicates
+ // (unless base is a constant)
+ TIntermTyped* arg;
+ if (base->getType().getQualifier().isFrontEndConstant()) {
+ arg = intermediate.foldDereference(base, selectors[s], loc);
+ } else {
+ TIntermTyped* rep = replicates.back();
+ replicates.pop_back();
+ TIntermTyped* index = intermediate.addConstantUnion(selectors[s], loc);
+ arg = intermediate.addIndex(EOpIndexDirect, rep, index, loc);
+ }
+ args = intermediate.growAggregate(args, arg);
+ }
+ }
+
+ // form the constructor
+ return addConstructor(loc, args, type)->getAsAggregate();
+}
+
void TParseContext::blockMemberExtensionCheck(const TSourceLoc& loc, const TIntermTyped* base, int member, const TString& memberName)
{
// a block that needs extension checking is either 'base', or if arrayed,
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index 9c5fdc7..eb50dad 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -217,7 +217,8 @@
/* output */ bool& tie);
virtual void parseSwizzleSelector(const TSourceLoc&, const TString&, int size,
- TSwizzleSelectors<TVectorSelector>&);
+ TSwizzleSelectors<TVectorSelector>&, bool& numeric);
+ virtual void replicateRValue(TIntermTyped* node, int n, TVector<TIntermTyped*>& replicates);
// Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
TVariable* globalUniformBlock; // the actual block, inserted into the symbol table
@@ -316,6 +317,8 @@
TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode);
TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
TIntermTyped* handleDotSwizzle(const TSourceLoc&, TIntermTyped* base, const TString& field);
+ TIntermTyped* handleNumericDotSwizzle(const TSourceLoc&, TIntermTyped* base,
+ const TSwizzleSelectors<TVectorSelector>&);
void blockMemberExtensionCheck(const TSourceLoc&, const TIntermTyped* base, int member, const TString& memberName);
TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&);
diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp
index 2dc173f..32ace82 100755
--- a/hlsl/hlslParseHelper.cpp
+++ b/hlsl/hlslParseHelper.cpp
@@ -952,7 +952,8 @@
}
} else if (base->isVector() || base->isScalar()) {
TSwizzleSelectors<TVectorSelector> selectors;
- parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
+ bool numeric = false;
+ parseSwizzleSelector(loc, field, base->getVectorSize(), selectors, numeric);
if (base->isScalar()) {
if (selectors.size() == 1)