summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/tree_ops/d3d')
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.cpp61
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.h24
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.cpp82
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.h23
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.cpp76
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.h23
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.cpp233
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.h27
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.cpp110
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.h25
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.cpp152
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.h34
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.cpp385
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.h28
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.cpp270
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.h27
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.cpp183
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.h42
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.cpp123
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.h27
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp420
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.h37
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.cpp117
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.h23
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.cpp83
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.h25
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.cpp89
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.h32
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.cpp138
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.h28
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.cpp200
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.h30
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.cpp126
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.h25
34 files changed, 3328 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.cpp
new file mode 100644
index 0000000000..8790c4f5f2
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.cpp
@@ -0,0 +1,61 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AddDefaultReturnStatements.cpp: Add default return statements to functions that do not end in a
+// return.
+//
+
+#include "compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.h"
+
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+bool NeedsReturnStatement(TIntermFunctionDefinition *node, TType *returnType)
+{
+ *returnType = node->getFunctionPrototype()->getType();
+ if (returnType->getBasicType() == EbtVoid)
+ {
+ return false;
+ }
+
+ TIntermBlock *bodyNode = node->getBody();
+ TIntermBranch *returnNode = bodyNode->getSequence()->back()->getAsBranchNode();
+ if (returnNode != nullptr && returnNode->getFlowOp() == EOpReturn)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+} // anonymous namespace
+
+bool AddDefaultReturnStatements(TCompiler *compiler, TIntermBlock *root)
+{
+ TType returnType;
+ for (TIntermNode *node : *root->getSequence())
+ {
+ TIntermFunctionDefinition *definition = node->getAsFunctionDefinition();
+ if (definition != nullptr && NeedsReturnStatement(definition, &returnType))
+ {
+ TIntermBranch *branch = new TIntermBranch(EOpReturn, CreateZeroNode(returnType));
+
+ TIntermBlock *bodyNode = definition->getBody();
+ bodyNode->getSequence()->push_back(branch);
+ }
+ }
+
+ return compiler->validateAST(root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.h
new file mode 100644
index 0000000000..52b601514c
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.h
@@ -0,0 +1,24 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AddDefaultReturnStatements.h: Add default return statements to functions that do not end in a
+// return.
+//
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_ADDDEFAULTRETURNSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_ADDDEFAULTRETURNSTATEMENTS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermBlock;
+
+[[nodiscard]] bool AddDefaultReturnStatements(TCompiler *compiler, TIntermBlock *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_ADDDEFAULTRETURNSTATEMENTS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.cpp
new file mode 100644
index 0000000000..d9a0c9bebc
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.cpp
@@ -0,0 +1,82 @@
+//
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.h"
+
+#include "compiler/translator/StaticType.h"
+#include "compiler/translator/Symbol.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class AggregateAssignArraysInSSBOsTraverser : public TIntermTraverser
+{
+ public:
+ AggregateAssignArraysInSSBOsTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable)
+ {}
+
+ protected:
+ bool visitBinary(Visit visit, TIntermBinary *node) override
+ {
+ // Replace all aggregate assignments to arrays in SSBOs with element-by-element assignments.
+ // TODO(anglebug.com/7363): this implementation only works for the simple case (assignment
+ // statement), not more complex cases such as assignment-as-expression or functions with
+ // side effects in the RHS.
+
+ if (node->getOp() != EOpAssign)
+ {
+ return true;
+ }
+ else if (!node->getLeft()->getType().isArray())
+ {
+ return true;
+ }
+ else if (!IsInShaderStorageBlock(node->getLeft()))
+ {
+ return true;
+ }
+ const TType *mediumpIndexType = StaticType::Get<EbtInt, EbpMedium, EvqTemporary, 1, 1>();
+ auto *indexVariable = CreateTempVariable(mSymbolTable, mediumpIndexType);
+ auto *indexInit =
+ CreateTempInitDeclarationNode(indexVariable, CreateZeroNode(indexVariable->getType()));
+ auto *arraySizeNode = CreateIndexNode(node->getOutermostArraySize());
+ auto *indexSymbolNode = CreateTempSymbolNode(indexVariable);
+ auto *cond = new TIntermBinary(EOpLessThan, indexSymbolNode->deepCopy(), arraySizeNode);
+ auto *indexIncrement =
+ new TIntermUnary(EOpPreIncrement, indexSymbolNode->deepCopy(), nullptr);
+ auto *forLoopBody = new TIntermBlock();
+ auto *indexedLeft =
+ new TIntermBinary(EOpIndexDirect, node->getLeft(), indexSymbolNode->deepCopy());
+ auto *indexedRight =
+ new TIntermBinary(EOpIndexDirect, node->getRight(), indexSymbolNode->deepCopy());
+ auto *assign = new TIntermBinary(TOperator::EOpAssign, indexedLeft, indexedRight);
+ forLoopBody->appendStatement(assign);
+ auto *forLoop =
+ new TIntermLoop(ELoopFor, indexInit, cond, indexIncrement, EnsureBlock(forLoopBody));
+ queueReplacement(forLoop, OriginalNode::IS_DROPPED);
+ return false;
+ }
+};
+
+} // namespace
+
+bool AggregateAssignArraysInSSBOs(TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable)
+{
+ AggregateAssignArraysInSSBOsTraverser traverser(symbolTable);
+ root->traverse(&traverser);
+ return traverser.updateTree(compiler, root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.h
new file mode 100644
index 0000000000..ce965bb002
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignArraysInSSBOs.h
@@ -0,0 +1,23 @@
+//
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_AGGREGATEASSIGNARRAYSINSSBOS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_AGGREGATEASSIGNARRAYSINSSBOS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermBlock;
+class TSymbolTable;
+
+[[nodiscard]] bool AggregateAssignArraysInSSBOs(TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_AGGREGATEASSIGNARRAYSINSSBOS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.cpp
new file mode 100644
index 0000000000..4293f4c4da
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.cpp
@@ -0,0 +1,76 @@
+//
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.h"
+
+#include "compiler/translator/StaticType.h"
+#include "compiler/translator/Symbol.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class AggregateAssignStructsInSSBOsTraverser : public TIntermTraverser
+{
+ public:
+ AggregateAssignStructsInSSBOsTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable)
+ {}
+
+ protected:
+ bool visitBinary(Visit visit, TIntermBinary *node) override
+ {
+ // Replace all assignments to structs in SSBOs with field-by-field asignments.
+ // TODO(anglebug.com/7362): this implementation only works for the simple case (assignment
+ // statement), not more complex cases such as assignment-as-expression or functions with
+ // side effects in the RHS.
+ const TStructure *s;
+ if (node->getOp() != EOpAssign)
+ {
+ return true;
+ }
+ else if (!IsInShaderStorageBlock(node->getLeft()))
+ {
+ return true;
+ }
+ else if (!(s = node->getLeft()->getType().getStruct()))
+ {
+ return true;
+ }
+ ASSERT(node->getRight()->getType().getStruct() == s);
+ auto *block = new TIntermBlock();
+ for (int i = 0; i < static_cast<int>(s->fields().size()); ++i)
+ {
+ auto *left = new TIntermBinary(EOpIndexDirectStruct, node->getLeft()->deepCopy(),
+ CreateIndexNode(i));
+ auto *right = new TIntermBinary(EOpIndexDirectStruct, node->getRight()->deepCopy(),
+ CreateIndexNode(i));
+ auto *assign = new TIntermBinary(TOperator::EOpAssign, left, right);
+ block->appendStatement(assign);
+ }
+
+ queueReplacement(block, OriginalNode::IS_DROPPED);
+ return false;
+ }
+};
+
+} // namespace
+
+bool AggregateAssignStructsInSSBOs(TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable)
+{
+ AggregateAssignStructsInSSBOsTraverser traverser(symbolTable);
+ root->traverse(&traverser);
+ return traverser.updateTree(compiler, root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.h
new file mode 100644
index 0000000000..910345902b
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/AggregateAssignStructsInSSBOs.h
@@ -0,0 +1,23 @@
+//
+// Copyright 2022 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_AGGREGATEASSIGNSTRUCTSINSSBOS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_AGGREGATEASSIGNSTRUCTSINSSBOS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermBlock;
+class TSymbolTable;
+
+[[nodiscard]] bool AggregateAssignStructsInSSBOs(TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_AGGREGATEASSIGNSTRUCTSINSSBOS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.cpp
new file mode 100644
index 0000000000..54d8fc0808
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.cpp
@@ -0,0 +1,233 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The ArrayReturnValueToOutParameter function changes return values of an array type to out
+// parameters in function definitions, prototypes, and call sites.
+
+#include "compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.h"
+
+#include <map>
+
+#include "compiler/translator/StaticType.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+constexpr const ImmutableString kReturnValueVariableName("angle_return");
+
+class ArrayReturnValueToOutParameterTraverser : private TIntermTraverser
+{
+ public:
+ [[nodiscard]] static bool apply(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable);
+
+ private:
+ ArrayReturnValueToOutParameterTraverser(TSymbolTable *symbolTable);
+
+ void visitFunctionPrototype(TIntermFunctionPrototype *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitBranch(Visit visit, TIntermBranch *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+
+ TIntermAggregate *createReplacementCall(TIntermAggregate *originalCall,
+ TIntermTyped *returnValueTarget);
+
+ // Set when traversal is inside a function with array return value.
+ TIntermFunctionDefinition *mFunctionWithArrayReturnValue;
+
+ struct ChangedFunction
+ {
+ const TVariable *returnValueVariable;
+ const TFunction *func;
+ };
+
+ // Map from function symbol ids to the changed function.
+ std::map<int, ChangedFunction> mChangedFunctions;
+};
+
+TIntermAggregate *ArrayReturnValueToOutParameterTraverser::createReplacementCall(
+ TIntermAggregate *originalCall,
+ TIntermTyped *returnValueTarget)
+{
+ TIntermSequence replacementArguments;
+ TIntermSequence *originalArguments = originalCall->getSequence();
+ for (auto &arg : *originalArguments)
+ {
+ replacementArguments.push_back(arg);
+ }
+ replacementArguments.push_back(returnValueTarget);
+ ASSERT(originalCall->getFunction());
+ const TSymbolUniqueId &originalId = originalCall->getFunction()->uniqueId();
+ TIntermAggregate *replacementCall = TIntermAggregate::CreateFunctionCall(
+ *mChangedFunctions[originalId.get()].func, &replacementArguments);
+ replacementCall->setLine(originalCall->getLine());
+ return replacementCall;
+}
+
+bool ArrayReturnValueToOutParameterTraverser::apply(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable)
+{
+ ArrayReturnValueToOutParameterTraverser arrayReturnValueToOutParam(symbolTable);
+ root->traverse(&arrayReturnValueToOutParam);
+ return arrayReturnValueToOutParam.updateTree(compiler, root);
+}
+
+ArrayReturnValueToOutParameterTraverser::ArrayReturnValueToOutParameterTraverser(
+ TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, true, symbolTable), mFunctionWithArrayReturnValue(nullptr)
+{}
+
+bool ArrayReturnValueToOutParameterTraverser::visitFunctionDefinition(
+ Visit visit,
+ TIntermFunctionDefinition *node)
+{
+ if (node->getFunctionPrototype()->isArray() && visit == PreVisit)
+ {
+ // Replacing the function header is done on visitFunctionPrototype().
+ mFunctionWithArrayReturnValue = node;
+ }
+ if (visit == PostVisit)
+ {
+ mFunctionWithArrayReturnValue = nullptr;
+ }
+ return true;
+}
+
+void ArrayReturnValueToOutParameterTraverser::visitFunctionPrototype(TIntermFunctionPrototype *node)
+{
+ if (node->isArray())
+ {
+ // Replace the whole prototype node with another node that has the out parameter
+ // added. Also set the function to return void.
+ const TSymbolUniqueId &functionId = node->getFunction()->uniqueId();
+ if (mChangedFunctions.find(functionId.get()) == mChangedFunctions.end())
+ {
+ TType *returnValueVariableType = new TType(node->getType());
+ returnValueVariableType->setQualifier(EvqParamOut);
+ ChangedFunction changedFunction;
+ changedFunction.returnValueVariable =
+ new TVariable(mSymbolTable, kReturnValueVariableName, returnValueVariableType,
+ SymbolType::AngleInternal);
+ TFunction *func = new TFunction(mSymbolTable, node->getFunction()->name(),
+ node->getFunction()->symbolType(),
+ StaticType::GetBasic<EbtVoid, EbpUndefined>(), false);
+ for (size_t i = 0; i < node->getFunction()->getParamCount(); ++i)
+ {
+ func->addParameter(node->getFunction()->getParam(i));
+ }
+ func->addParameter(changedFunction.returnValueVariable);
+ changedFunction.func = func;
+ mChangedFunctions[functionId.get()] = changedFunction;
+ }
+ TIntermFunctionPrototype *replacement =
+ new TIntermFunctionPrototype(mChangedFunctions[functionId.get()].func);
+ replacement->setLine(node->getLine());
+
+ queueReplacement(replacement, OriginalNode::IS_DROPPED);
+ }
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ ASSERT(!node->isArray() || node->getOp() != EOpCallInternalRawFunction);
+ if (visit == PreVisit && node->isArray() && node->getOp() == EOpCallFunctionInAST)
+ {
+ // Handle call sites where the returned array is not assigned.
+ // Examples where f() is a function returning an array:
+ // 1. f();
+ // 2. another_array == f();
+ // 3. another_function(f());
+ // 4. return f();
+ // Cases 2 to 4 are already converted to simpler cases by
+ // SeparateExpressionsReturningArrays, so we only need to worry about the case where a
+ // function call returning an array forms an expression by itself.
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ if (parentBlock)
+ {
+ // replace
+ // f();
+ // with
+ // type s0[size]; f(s0);
+ TIntermSequence replacements;
+
+ // type s0[size];
+ TIntermDeclaration *returnValueDeclaration = nullptr;
+ TVariable *returnValue = DeclareTempVariable(mSymbolTable, new TType(node->getType()),
+ EvqTemporary, &returnValueDeclaration);
+ replacements.push_back(returnValueDeclaration);
+
+ // f(s0);
+ TIntermSymbol *returnValueSymbol = CreateTempSymbolNode(returnValue);
+ replacements.push_back(createReplacementCall(node, returnValueSymbol));
+ mMultiReplacements.emplace_back(parentBlock, node, std::move(replacements));
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBranch *node)
+{
+ if (mFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn)
+ {
+ // Instead of returning a value, assign to the out parameter and then return.
+ TIntermSequence replacements;
+
+ TIntermTyped *expression = node->getExpression();
+ ASSERT(expression != nullptr);
+ const TSymbolUniqueId &functionId =
+ mFunctionWithArrayReturnValue->getFunction()->uniqueId();
+ ASSERT(mChangedFunctions.find(functionId.get()) != mChangedFunctions.end());
+ TIntermSymbol *returnValueSymbol =
+ new TIntermSymbol(mChangedFunctions[functionId.get()].returnValueVariable);
+ TIntermBinary *replacementAssignment =
+ new TIntermBinary(EOpAssign, returnValueSymbol, expression);
+ replacementAssignment->setLine(expression->getLine());
+ replacements.push_back(replacementAssignment);
+
+ TIntermBranch *replacementBranch = new TIntermBranch(EOpReturn, nullptr);
+ replacementBranch->setLine(node->getLine());
+ replacements.push_back(replacementBranch);
+
+ mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node,
+ std::move(replacements));
+ }
+ return false;
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (node->getOp() == EOpAssign && node->getLeft()->isArray())
+ {
+ TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
+ ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpCallInternalRawFunction);
+ if (rightAgg != nullptr && rightAgg->getOp() == EOpCallFunctionInAST)
+ {
+ TIntermAggregate *replacementCall = createReplacementCall(rightAgg, node->getLeft());
+ queueReplacement(replacementCall, OriginalNode::IS_DROPPED);
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+bool ArrayReturnValueToOutParameter(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable)
+{
+ return ArrayReturnValueToOutParameterTraverser::apply(compiler, root, symbolTable);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.h
new file mode 100644
index 0000000000..ae8d04ae9a
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.h
@@ -0,0 +1,27 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The ArrayReturnValueToOutParameter function changes return values of an array type to out
+// parameters in function definitions, prototypes and call sites.
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_ARRAYRETURNVALUETOOUTPARAMETER_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_ARRAYRETURNVALUETOOUTPARAMETER_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+
+class TCompiler;
+class TIntermNode;
+class TSymbolTable;
+
+[[nodiscard]] bool ArrayReturnValueToOutParameter(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_ARRAYRETURNVALUETOOUTPARAMETER_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.cpp
new file mode 100644
index 0000000000..908da7a8c2
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.cpp
@@ -0,0 +1,110 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+// may record a variable as aliasing another. Sometimes the alias information gets garbled
+// so we work around this issue by breaking the aliasing chain in inner loops.
+
+#include "compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.h"
+
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+// A HLSL compiler developer gave us more details on the root cause and the workaround needed:
+// The root problem is that if the HLSL compiler is applying aliasing information even on
+// incomplete simulations (in this case, a single pass). The bug is triggered by an assignment
+// that comes from a series of assignments, possibly with swizzled or ternary operators with
+// known conditionals, where the source is before the loop.
+// So, a workaround is to add a +0 term to variables the first time they are assigned to in
+// an inner loop (if they are declared in an outside scope, otherwise there is no need).
+// This will break the aliasing chain.
+
+// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because
+// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure
+// assignment don't need a workaround.
+
+namespace sh
+{
+
+namespace
+{
+
+class AliasingBreaker : public TIntermTraverser
+{
+ public:
+ AliasingBreaker() : TIntermTraverser(true, false, true) {}
+
+ protected:
+ bool visitBinary(Visit visit, TIntermBinary *binary) override
+ {
+ if (visit != PreVisit)
+ {
+ return false;
+ }
+
+ if (mLoopLevel < 2 || !binary->isAssignment())
+ {
+ return true;
+ }
+
+ TIntermTyped *B = binary->getRight();
+ TType type = B->getType();
+
+ if (!type.isScalar() && !type.isVector() && !type.isMatrix())
+ {
+ return true;
+ }
+
+ if (type.isArray() || IsSampler(type.getBasicType()))
+ {
+ return true;
+ }
+
+ // We have a scalar / vector / matrix assignment with loop depth 2.
+ // Transform it from
+ // A = B
+ // to
+ // A = (B + typeof<B>(0));
+
+ TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, CreateZeroNode(type));
+ bPlusZero->setLine(B->getLine());
+
+ binary->replaceChildNode(B, bPlusZero);
+
+ return true;
+ }
+
+ bool visitLoop(Visit visit, TIntermLoop *loop) override
+ {
+ if (visit == PreVisit)
+ {
+ mLoopLevel++;
+ }
+ else
+ {
+ ASSERT(mLoopLevel > 0);
+ mLoopLevel--;
+ }
+
+ return true;
+ }
+
+ private:
+ int mLoopLevel = 0;
+};
+
+} // anonymous namespace
+
+bool BreakVariableAliasingInInnerLoops(TCompiler *compiler, TIntermNode *root)
+{
+ AliasingBreaker breaker;
+ root->traverse(&breaker);
+
+ return compiler->validateAST(root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.h
new file mode 100644
index 0000000000..455c7c322d
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.h
@@ -0,0 +1,25 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+// may record a variable as aliasing another. Sometimes the alias information gets garbled
+// so we work around this issue by breaking the aliasing chain in inner loops.
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_BREAKVARIABLEALIASINGININNERLOOPS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_BREAKVARIABLEALIASINGININNERLOOPS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermNode;
+
+[[nodiscard]] bool BreakVariableAliasingInInnerLoops(TCompiler *compiler, TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_BREAKVARIABLEALIASINGININNERLOOPS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.cpp
new file mode 100644
index 0000000000..e873db56cd
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.cpp
@@ -0,0 +1,152 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of the integer pow expressions HLSL bug workaround.
+// See header for more info.
+
+#include "compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.h"
+
+#include <cmath>
+#include <cstdlib>
+
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ [[nodiscard]] static bool Apply(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable);
+
+ private:
+ Traverser(TSymbolTable *symbolTable);
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ void nextIteration();
+
+ bool mFound = false;
+};
+
+// static
+bool Traverser::Apply(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable)
+{
+ Traverser traverser(symbolTable);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ if (!traverser.updateTree(compiler, root))
+ {
+ return false;
+ }
+ }
+ } while (traverser.mFound);
+
+ return true;
+}
+
+Traverser::Traverser(TSymbolTable *symbolTable) : TIntermTraverser(true, false, false, symbolTable)
+{}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+}
+
+bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Test 0: skip non-pow operators.
+ if (node->getOp() != EOpPow)
+ {
+ return true;
+ }
+
+ const TIntermSequence *sequence = node->getSequence();
+ ASSERT(sequence->size() == 2u);
+ const TIntermConstantUnion *constantExponent = sequence->at(1)->getAsConstantUnion();
+
+ // Test 1: check for a single constant.
+ if (!constantExponent || constantExponent->getNominalSize() != 1)
+ {
+ return true;
+ }
+
+ float exponentValue = constantExponent->getConstantValue()->getFConst();
+
+ // Test 2: exponentValue is in the problematic range.
+ if (exponentValue < -5.0f || exponentValue > 9.0f)
+ {
+ return true;
+ }
+
+ // Test 3: exponentValue is integer or pretty close to an integer.
+ if (std::abs(exponentValue - std::round(exponentValue)) > 0.0001f)
+ {
+ return true;
+ }
+
+ // Test 4: skip -1, 0, and 1
+ int exponent = static_cast<int>(std::round(exponentValue));
+ int n = std::abs(exponent);
+ if (n < 2)
+ {
+ return true;
+ }
+
+ // Potential problem case detected, apply workaround.
+
+ TIntermTyped *lhs = sequence->at(0)->getAsTyped();
+ ASSERT(lhs);
+
+ TIntermDeclaration *lhsVariableDeclaration = nullptr;
+ TVariable *lhsVariable =
+ DeclareTempVariable(mSymbolTable, lhs, EvqTemporary, &lhsVariableDeclaration);
+ insertStatementInParentBlock(lhsVariableDeclaration);
+
+ // Create a chain of n-1 multiples.
+ TIntermTyped *current = CreateTempSymbolNode(lhsVariable);
+ for (int i = 1; i < n; ++i)
+ {
+ TIntermBinary *mul = new TIntermBinary(EOpMul, current, CreateTempSymbolNode(lhsVariable));
+ mul->setLine(node->getLine());
+ current = mul;
+ }
+
+ // For negative pow, compute the reciprocal of the positive pow.
+ if (exponent < 0)
+ {
+ TConstantUnion *oneVal = new TConstantUnion();
+ oneVal->setFConst(1.0f);
+ TIntermConstantUnion *oneNode = new TIntermConstantUnion(oneVal, node->getType());
+ TIntermBinary *div = new TIntermBinary(EOpDiv, oneNode, current);
+ current = div;
+ }
+
+ queueReplacement(current, OriginalNode::IS_DROPPED);
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+bool ExpandIntegerPowExpressions(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable)
+{
+ return Traverser::Apply(compiler, root, symbolTable);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.h
new file mode 100644
index 0000000000..2404fb08ac
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.h
@@ -0,0 +1,34 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This mutating tree traversal works around a bug in the HLSL compiler optimizer with "pow" that
+// manifests under the following conditions:
+//
+// - If pow() has a literal exponent value
+// - ... and this value is integer or within 10e-6 of an integer
+// - ... and it is in {-4, -3, -2, 2, 3, 4, 5, 6, 7, 8}
+//
+// The workaround is to replace the pow with a series of multiplies.
+// See http://anglebug.com/851
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_EXPANDINTEGERPOWEXPRESSIONS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_EXPANDINTEGERPOWEXPRESSIONS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+
+class TCompiler;
+class TIntermNode;
+class TSymbolTable;
+
+[[nodiscard]] bool ExpandIntegerPowExpressions(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_EXPANDINTEGERPOWEXPRESSIONS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.cpp
new file mode 100644
index 0000000000..18d49814ac
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.cpp
@@ -0,0 +1,385 @@
+//
+// Copyright 2020 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RecordUniformBlocksWithLargeArrayMember.h:
+// Collect all uniform blocks which have one or more large array members,
+// and the array sizes are greater than or equal to 50. If some of them
+// satify some conditions, we will translate them to StructuredBuffers
+// on Direct3D backend.
+//
+
+#include "compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.h"
+
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+// Only when a uniform block member's array size is greater than or equal to
+// kMinArraySizeUseStructuredBuffer, then we may translate the uniform block
+// to a StructuredBuffer on Direct3D backend.
+const unsigned int kMinArraySizeUseStructuredBuffer = 50u;
+
+// There is a maximum of D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT(128) slots that are
+// available for shader resources on Direct3D 11. When shader version is 300, we only use
+// D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT(16) slots for texture units. We allow StructuredBuffer
+// to use the maximum of 60 slots, that is enough here.
+const unsigned int kMaxAllowToUseRegisterCount = 60u;
+
+// Traverser that all uniform blocks which have one or more large array members, and the array
+// sizes are greater than or equal to 50.
+class UniformBlocksWithLargeArrayMemberTraverser : public TIntermTraverser
+{
+ public:
+ UniformBlocksWithLargeArrayMemberTraverser();
+
+ void visitSymbol(TIntermSymbol *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ std::map<int, const TInterfaceBlock *> &getUniformBlockMayTranslation()
+ {
+ return mUniformBlockMayTranslation;
+ }
+ std::map<int, const TInterfaceBlock *> &getUniformBlockNotAllowTranslation()
+ {
+ return mUniformBlockNotAllowTranslation;
+ }
+ std::map<int, unsigned int> &getUniformBlockUsedRegisterCount()
+ {
+ return mUniformBlockUsedRegisterCount;
+ }
+ std::map<int, const TInterfaceBlock *> &getUniformBlockWithLargeArrayMember()
+ {
+ return mUniformBlockWithLargeArrayMember;
+ }
+
+ private:
+ std::map<int, const TInterfaceBlock *> mUniformBlockMayTranslation;
+ std::map<int, const TInterfaceBlock *> mUniformBlockNotAllowTranslation;
+ std::map<int, unsigned int> mUniformBlockUsedRegisterCount;
+ std::map<int, const TInterfaceBlock *> mUniformBlockWithLargeArrayMember;
+};
+
+UniformBlocksWithLargeArrayMemberTraverser::UniformBlocksWithLargeArrayMemberTraverser()
+ : TIntermTraverser(true, true, false)
+{}
+
+static bool IsSupportedTypeForStructuredBuffer(const TType &type)
+{
+ const TStructure *structure = type.getStruct();
+ const TLayoutMatrixPacking matrixPacking = type.getLayoutQualifier().matrixPacking;
+ if (structure)
+ {
+ const TFieldList &fields = structure->fields();
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ const TType &fieldType = *fields[i]->type();
+ // Do not allow the structure's member is array or structure.
+ if (!fieldType.isArray() && !fieldType.getStruct() &&
+ (fieldType.isScalar() || fieldType.isVector() ||
+ (fieldType.isMatrix() &&
+ ((matrixPacking != EmpRowMajor && fieldType.getRows() == 4) ||
+ (matrixPacking == EmpRowMajor && fieldType.getCols() == 4)))))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ else if (type.isMatrix())
+ {
+ // Only supports the matrix types that we do not need to pad in a structure or an array
+ // explicitly.
+ return (matrixPacking != EmpRowMajor && type.getRows() == 4) ||
+ (matrixPacking == EmpRowMajor && type.getCols() == 4);
+ }
+ else
+ {
+ // Supports vector and scalar types in a structure or an array.
+ return true;
+ }
+}
+
+static bool CanTranslateUniformBlockToStructuredBuffer(const TInterfaceBlock &interfaceBlock)
+{
+ const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
+
+ if (blockStorage == EbsStd140 && interfaceBlock.fields().size() == 1u)
+ {
+ const TType &fieldType = *interfaceBlock.fields()[0]->type();
+ if (fieldType.getNumArraySizes() == 1u &&
+ fieldType.getOutermostArraySize() >= kMinArraySizeUseStructuredBuffer)
+ {
+ return IsSupportedTypeForStructuredBuffer(fieldType);
+ }
+ }
+
+ return false;
+}
+
+static bool FieldIsOrHasLargeArrayField(const TField &field)
+{
+ const TType *type = field.type();
+ if (type->getArraySizeProduct() >= kMinArraySizeUseStructuredBuffer)
+ {
+ return true;
+ }
+
+ const TStructure *structure = type->getStruct();
+ if (structure)
+ {
+ const TFieldList &fields = structure->fields();
+ bool hasLargeArrayField = false;
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ hasLargeArrayField = FieldIsOrHasLargeArrayField(*fields[i]);
+ if (hasLargeArrayField)
+ {
+ break;
+ }
+ }
+ return hasLargeArrayField;
+ }
+
+ return false;
+}
+
+static bool IsInterfaceBlockWithLargeArrayField(const TInterfaceBlock &interfaceBlock)
+{
+ const TFieldList &fields = interfaceBlock.fields();
+ bool isLargeArrayField = false;
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ isLargeArrayField = FieldIsOrHasLargeArrayField(*fields[i]);
+ if (isLargeArrayField)
+ {
+ break;
+ }
+ }
+
+ return isLargeArrayField;
+}
+
+void UniformBlocksWithLargeArrayMemberTraverser::visitSymbol(TIntermSymbol *node)
+{
+ const TVariable &variable = node->variable();
+ const TType &variableType = variable.getType();
+ TQualifier qualifier = variable.getType().getQualifier();
+
+ if (qualifier == EvqUniform)
+ {
+ const TInterfaceBlock *interfaceBlock = variableType.getInterfaceBlock();
+ if (interfaceBlock)
+ {
+ if (CanTranslateUniformBlockToStructuredBuffer(*interfaceBlock))
+ {
+ if (mUniformBlockMayTranslation.count(interfaceBlock->uniqueId().get()) == 0)
+ {
+ mUniformBlockMayTranslation[interfaceBlock->uniqueId().get()] = interfaceBlock;
+ }
+
+ if (!variableType.isInterfaceBlock())
+ {
+ TIntermNode *accessor = getAncestorNode(0);
+ TIntermBinary *accessorAsBinary = accessor->getAsBinaryNode();
+ // The uniform block variable is array type, only indexing operator is allowed
+ // to operate on the variable, otherwise do not translate the uniform block to
+ // HLSL StructuredBuffer.
+ if (!accessorAsBinary ||
+ !(accessorAsBinary && (accessorAsBinary->getOp() == EOpIndexDirect ||
+ accessorAsBinary->getOp() == EOpIndexIndirect)))
+ {
+ if (mUniformBlockNotAllowTranslation.count(
+ interfaceBlock->uniqueId().get()) == 0)
+ {
+ mUniformBlockNotAllowTranslation[interfaceBlock->uniqueId().get()] =
+ interfaceBlock;
+ }
+ }
+ else
+ {
+ if (mUniformBlockUsedRegisterCount.count(
+ interfaceBlock->uniqueId().get()) == 0)
+ {
+ // The uniform block is not an instanced one, so it only uses one
+ // register.
+ mUniformBlockUsedRegisterCount[interfaceBlock->uniqueId().get()] = 1;
+ }
+ }
+ }
+ else
+ {
+ if (mUniformBlockUsedRegisterCount.count(interfaceBlock->uniqueId().get()) == 0)
+ {
+ // The uniform block is an instanced one, the count of used registers
+ // depends on the array size of variable.
+ mUniformBlockUsedRegisterCount[interfaceBlock->uniqueId().get()] =
+ variableType.isArray() ? variableType.getOutermostArraySize() : 1;
+ }
+ }
+ }
+
+ if (interfaceBlock->blockStorage() == EbsStd140 &&
+ IsInterfaceBlockWithLargeArrayField(*interfaceBlock))
+ {
+ if (!variableType.isInterfaceBlock())
+ {
+ TIntermNode *accessor = getAncestorNode(0);
+ TIntermBinary *accessorAsBinary = accessor->getAsBinaryNode();
+ if (accessorAsBinary && (accessorAsBinary->getOp() == EOpIndexDirect ||
+ accessorAsBinary->getOp() == EOpIndexIndirect))
+ {
+ if (mUniformBlockWithLargeArrayMember.count(
+ interfaceBlock->uniqueId().get()) == 0)
+ {
+ mUniformBlockWithLargeArrayMember[interfaceBlock->uniqueId().get()] =
+ interfaceBlock;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+bool UniformBlocksWithLargeArrayMemberTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ switch (node->getOp())
+ {
+ case EOpIndexDirect:
+ {
+ if (visit == PreVisit)
+ {
+ const TType &leftType = node->getLeft()->getType();
+ if (leftType.isInterfaceBlock())
+ {
+ const TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
+ if (CanTranslateUniformBlockToStructuredBuffer(*interfaceBlock) &&
+ mUniformBlockMayTranslation.count(interfaceBlock->uniqueId().get()) == 0)
+ {
+ mUniformBlockMayTranslation[interfaceBlock->uniqueId().get()] =
+ interfaceBlock;
+ if (mUniformBlockUsedRegisterCount.count(
+ interfaceBlock->uniqueId().get()) == 0)
+ {
+ // The uniform block is an instanced one, the count of used registers
+ // depends on the array size of variable.
+ mUniformBlockUsedRegisterCount[interfaceBlock->uniqueId().get()] =
+ leftType.isArray() ? leftType.getOutermostArraySize() : 1;
+ }
+ return false;
+ }
+
+ if (interfaceBlock->blockStorage() == EbsStd140 &&
+ IsInterfaceBlockWithLargeArrayField(*interfaceBlock))
+ {
+ if (mUniformBlockWithLargeArrayMember.count(
+ interfaceBlock->uniqueId().get()) == 0)
+ {
+ mUniformBlockWithLargeArrayMember[interfaceBlock->uniqueId().get()] =
+ interfaceBlock;
+ }
+ }
+ }
+ }
+ break;
+ }
+ case EOpIndexDirectInterfaceBlock:
+ {
+ if (visit == InVisit)
+ {
+ const TInterfaceBlock *interfaceBlock =
+ node->getLeft()->getType().getInterfaceBlock();
+ if (CanTranslateUniformBlockToStructuredBuffer(*interfaceBlock))
+ {
+ TIntermNode *accessor = getAncestorNode(0);
+ TIntermBinary *accessorAsBinary = accessor->getAsBinaryNode();
+ // The uniform block variable is array type, only indexing operator is allowed
+ // to operate on the variable, otherwise do not translate the uniform block to
+ // HLSL StructuredBuffer.
+ if ((!accessorAsBinary ||
+ !(accessorAsBinary && (accessorAsBinary->getOp() == EOpIndexDirect ||
+ accessorAsBinary->getOp() == EOpIndexIndirect))) &&
+ mUniformBlockNotAllowTranslation.count(interfaceBlock->uniqueId().get()) ==
+ 0)
+ {
+ mUniformBlockNotAllowTranslation[interfaceBlock->uniqueId().get()] =
+ interfaceBlock;
+ return false;
+ }
+ }
+
+ if (interfaceBlock->blockStorage() == EbsStd140 &&
+ IsInterfaceBlockWithLargeArrayField(*interfaceBlock))
+ {
+ TIntermNode *accessor = getAncestorNode(0);
+ TIntermBinary *accessorAsBinary = accessor->getAsBinaryNode();
+ if (accessorAsBinary && (accessorAsBinary->getOp() == EOpIndexDirect ||
+ accessorAsBinary->getOp() == EOpIndexIndirect))
+ {
+ if (mUniformBlockWithLargeArrayMember.count(
+ interfaceBlock->uniqueId().get()) == 0)
+ {
+ mUniformBlockWithLargeArrayMember[interfaceBlock->uniqueId().get()] =
+ interfaceBlock;
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+} // namespace
+
+bool RecordUniformBlocksWithLargeArrayMember(
+ TIntermNode *root,
+ std::map<int, const TInterfaceBlock *> &uniformBlockOptimizedMap,
+ std::set<std::string> &slowCompilingUniformBlockSet)
+{
+ UniformBlocksWithLargeArrayMemberTraverser traverser;
+ root->traverse(&traverser);
+ std::map<int, const TInterfaceBlock *> &uniformBlockMayTranslation =
+ traverser.getUniformBlockMayTranslation();
+ std::map<int, const TInterfaceBlock *> &uniformBlockNotAllowTranslation =
+ traverser.getUniformBlockNotAllowTranslation();
+ std::map<int, unsigned int> &uniformBlockUsedRegisterCount =
+ traverser.getUniformBlockUsedRegisterCount();
+ std::map<int, const TInterfaceBlock *> &uniformBlockWithLargeArrayMember =
+ traverser.getUniformBlockWithLargeArrayMember();
+
+ unsigned int usedRegisterCount = 0;
+ for (auto &uniformBlock : uniformBlockMayTranslation)
+ {
+ if (uniformBlockNotAllowTranslation.count(uniformBlock.first) == 0)
+ {
+ usedRegisterCount += uniformBlockUsedRegisterCount[uniformBlock.first];
+ if (usedRegisterCount > kMaxAllowToUseRegisterCount)
+ {
+ break;
+ }
+ uniformBlockOptimizedMap[uniformBlock.first] = uniformBlock.second;
+ }
+ }
+
+ for (auto &uniformBlock : uniformBlockWithLargeArrayMember)
+ {
+ if (uniformBlockOptimizedMap.count(uniformBlock.first) == 0)
+ {
+ slowCompilingUniformBlockSet.insert(uniformBlock.second->name().data());
+ }
+ }
+
+ return true;
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.h
new file mode 100644
index 0000000000..d7825c28cb
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.h
@@ -0,0 +1,28 @@
+//
+// Copyright 2020 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RecordUniformBlocksWithLargeArrayMember.h:
+// Collect all uniform blocks which have one or more large array members,
+// and the array sizes are greater than or equal to 50. If some of them
+// satify some conditions, we will translate them to StructuredBuffers
+// on Direct3D backend.
+//
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_RECORDUNIFORMBLOCKSWITHLARGEARRAYMEMBER_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_RECORDUNIFORMBLOCKSWITHLARGEARRAYMEMBER_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+class TIntermNode;
+
+[[nodiscard]] bool RecordUniformBlocksWithLargeArrayMember(
+ TIntermNode *root,
+ std::map<int, const TInterfaceBlock *> &uniformBlockOptimizedMap,
+ std::set<std::string> &slowCompilingUniformBlockSet);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_RECORDUNIFORMBLOCKSWITHLARGEARRAYMEMBER_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.cpp
new file mode 100644
index 0000000000..6360f1083c
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.cpp
@@ -0,0 +1,270 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveSwitchFallThrough.cpp: Remove fall-through from switch statements.
+// Note that it is unsafe to do further AST transformations on the AST generated
+// by this function. It leaves duplicate nodes in the AST making replacements
+// unreliable.
+
+#include "compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.h"
+
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class RemoveSwitchFallThroughTraverser : public TIntermTraverser
+{
+ public:
+ static TIntermBlock *removeFallThrough(TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics);
+
+ private:
+ RemoveSwitchFallThroughTraverser(TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics);
+
+ void visitSymbol(TIntermSymbol *node) override;
+ void visitConstantUnion(TIntermConstantUnion *node) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+ bool visitBinary(Visit, TIntermBinary *node) override;
+ bool visitUnary(Visit, TIntermUnary *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+ bool visitSwizzle(Visit, TIntermSwizzle *node) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override;
+ bool visitSwitch(Visit, TIntermSwitch *node) override;
+ bool visitCase(Visit, TIntermCase *node) override;
+ bool visitAggregate(Visit, TIntermAggregate *node) override;
+ bool visitBlock(Visit, TIntermBlock *node) override;
+ bool visitLoop(Visit, TIntermLoop *node) override;
+ bool visitBranch(Visit, TIntermBranch *node) override;
+
+ void outputSequence(TIntermSequence *sequence, size_t startIndex);
+ void handlePreviousCase();
+
+ TIntermBlock *mStatementList;
+ TIntermBlock *mStatementListOut;
+ bool mLastStatementWasBreak;
+ TIntermBlock *mPreviousCase;
+ std::vector<TIntermBlock *> mCasesSharingBreak;
+ PerformanceDiagnostics *mPerfDiagnostics;
+};
+
+TIntermBlock *RemoveSwitchFallThroughTraverser::removeFallThrough(
+ TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics)
+{
+ RemoveSwitchFallThroughTraverser rm(statementList, perfDiagnostics);
+ ASSERT(statementList);
+ statementList->traverse(&rm);
+ ASSERT(rm.mPreviousCase || statementList->getSequence()->empty());
+ if (!rm.mLastStatementWasBreak && rm.mPreviousCase)
+ {
+ // Make sure that there's a branch at the end of the final case inside the switch statement.
+ // This also ensures that any cases that fall through to the final case will get the break.
+ TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
+ rm.mPreviousCase->getSequence()->push_back(finalBreak);
+ rm.mLastStatementWasBreak = true;
+ }
+ rm.handlePreviousCase();
+ return rm.mStatementListOut;
+}
+
+RemoveSwitchFallThroughTraverser::RemoveSwitchFallThroughTraverser(
+ TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics)
+ : TIntermTraverser(true, false, false),
+ mStatementList(statementList),
+ mLastStatementWasBreak(false),
+ mPreviousCase(nullptr),
+ mPerfDiagnostics(perfDiagnostics)
+{
+ mStatementListOut = new TIntermBlock();
+}
+
+void RemoveSwitchFallThroughTraverser::visitSymbol(TIntermSymbol *node)
+{
+ // Note that this assumes that switch statements which don't begin by a case statement
+ // have already been weeded out in validation.
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+}
+
+void RemoveSwitchFallThroughTraverser::visitConstantUnion(TIntermConstantUnion *node)
+{
+ // Conditions of case labels are not traversed, so this is a constant statement like "0;".
+ // These are no-ops so there's no need to add them back to the statement list. Should have
+ // already been pruned out of the AST, in fact.
+ UNREACHABLE();
+}
+
+bool RemoveSwitchFallThroughTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitBinary(Visit, TIntermBinary *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitUnary(Visit, TIntermUnary *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitTernary(Visit, TIntermTernary *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitSwizzle(Visit, TIntermSwizzle *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitIfElse(Visit, TIntermIfElse *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitSwitch(Visit, TIntermSwitch *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ // Don't go into nested switch statements
+ return false;
+}
+
+void RemoveSwitchFallThroughTraverser::outputSequence(TIntermSequence *sequence, size_t startIndex)
+{
+ for (size_t i = startIndex; i < sequence->size(); ++i)
+ {
+ mStatementListOut->getSequence()->push_back(sequence->at(i));
+ }
+}
+
+void RemoveSwitchFallThroughTraverser::handlePreviousCase()
+{
+ if (mPreviousCase)
+ mCasesSharingBreak.push_back(mPreviousCase);
+ if (mLastStatementWasBreak)
+ {
+ for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
+ {
+ ASSERT(!mCasesSharingBreak.at(i)->getSequence()->empty());
+ if (mCasesSharingBreak.at(i)->getSequence()->size() == 1)
+ {
+ // Fall-through is allowed in case the label has no statements.
+ outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
+ }
+ else
+ {
+ // Include all the statements that this case can fall through under the same label.
+ if (mCasesSharingBreak.size() > i + 1u)
+ {
+ mPerfDiagnostics->warning(mCasesSharingBreak.at(i)->getLine(),
+ "Performance: non-empty fall-through cases in "
+ "switch statements generate extra code.",
+ "switch");
+ }
+ for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
+ {
+ size_t startIndex =
+ j > i ? 1 : 0; // Add the label only from the first sequence.
+ outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
+ }
+ }
+ }
+ mCasesSharingBreak.clear();
+ }
+ mLastStatementWasBreak = false;
+ mPreviousCase = nullptr;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitCase(Visit, TIntermCase *node)
+{
+ handlePreviousCase();
+ mPreviousCase = new TIntermBlock();
+ mPreviousCase->getSequence()->push_back(node);
+ mPreviousCase->setLine(node->getLine());
+ // Don't traverse the condition of the case statement
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitAggregate(Visit, TIntermAggregate *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool DoesBlockAlwaysBreak(TIntermBlock *node)
+{
+ if (node->getSequence()->empty())
+ {
+ return false;
+ }
+
+ TIntermBlock *lastStatementAsBlock = node->getSequence()->back()->getAsBlock();
+ if (lastStatementAsBlock)
+ {
+ return DoesBlockAlwaysBreak(lastStatementAsBlock);
+ }
+
+ TIntermBranch *lastStatementAsBranch = node->getSequence()->back()->getAsBranchNode();
+ return lastStatementAsBranch != nullptr;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitBlock(Visit, TIntermBlock *node)
+{
+ if (node != mStatementList)
+ {
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = DoesBlockAlwaysBreak(node);
+ return false;
+ }
+ return true;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitLoop(Visit, TIntermLoop *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThroughTraverser::visitBranch(Visit, TIntermBranch *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ // TODO: Verify that accepting return or continue statements here doesn't cause problems.
+ mLastStatementWasBreak = true;
+ return false;
+}
+
+} // anonymous namespace
+
+TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics)
+{
+ return RemoveSwitchFallThroughTraverser::removeFallThrough(statementList, perfDiagnostics);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.h
new file mode 100644
index 0000000000..b92e7e5f6d
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.h
@@ -0,0 +1,27 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveSwitchFallThrough.h: Remove fall-through from switch statements.
+// Note that it is unsafe to do further AST transformations on the AST generated
+// by this function. It leaves duplicate nodes in the AST making replacements
+// unreliable.
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_REMOVESWITCHFALLTHROUGH_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_REMOVESWITCHFALLTHROUGH_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class PerformanceDiagnostics;
+
+// When given a statementList from a switch AST node, return an updated
+// statementList that has fall-through removed.
+TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList,
+ PerformanceDiagnostics *perfDiagnostics);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_REMOVESWITCHFALLTHROUGH_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.cpp
new file mode 100644
index 0000000000..b2de6079b7
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.cpp
@@ -0,0 +1,183 @@
+//
+// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of the function RewriteAtomicFunctionExpressions.
+// See the header for more details.
+
+#include "compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.h"
+
+#include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+namespace
+{
+// Traverser that simplifies all the atomic function expressions into the ones that can be directly
+// translated into HLSL.
+//
+// case 1 (only for atomicExchange and atomicCompSwap):
+// original:
+// atomicExchange(counter, newValue);
+// new:
+// tempValue = atomicExchange(counter, newValue);
+//
+// case 2 (atomic function, temporary variable required):
+// original:
+// value = atomicAdd(counter, 1) * otherValue;
+// someArray[atomicAdd(counter, 1)] = someOtherValue;
+// new:
+// value = ((tempValue = atomicAdd(counter, 1)), tempValue) * otherValue;
+// someArray[((tempValue = atomicAdd(counter, 1)), tempValue)] = someOtherValue;
+//
+// case 3 (atomic function used directly initialize a variable):
+// original:
+// int value = atomicAdd(counter, 1);
+// new:
+// tempValue = atomicAdd(counter, 1);
+// int value = tempValue;
+//
+class RewriteAtomicFunctionExpressionsTraverser : public TIntermTraverser
+{
+ public:
+ RewriteAtomicFunctionExpressionsTraverser(TSymbolTable *symbolTable, int shaderVersion);
+
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitBlock(Visit visit, TIntermBlock *node) override;
+
+ private:
+ static bool IsAtomicExchangeOrCompSwapNoReturnValue(TIntermAggregate *node,
+ TIntermNode *parentNode);
+ static bool IsAtomicFunctionInsideExpression(TIntermAggregate *node, TIntermNode *parentNode);
+
+ void rewriteAtomicFunctionCallNode(TIntermAggregate *oldAtomicFunctionNode);
+
+ const TVariable *getTempVariable(const TType *type);
+
+ int mShaderVersion;
+ TIntermSequence mTempVariables;
+};
+
+RewriteAtomicFunctionExpressionsTraverser::RewriteAtomicFunctionExpressionsTraverser(
+ TSymbolTable *symbolTable,
+ int shaderVersion)
+ : TIntermTraverser(false, false, true, symbolTable), mShaderVersion(shaderVersion)
+{}
+
+void RewriteAtomicFunctionExpressionsTraverser::rewriteAtomicFunctionCallNode(
+ TIntermAggregate *oldAtomicFunctionNode)
+{
+ ASSERT(oldAtomicFunctionNode);
+
+ const TVariable *returnVariable = getTempVariable(&oldAtomicFunctionNode->getType());
+
+ TIntermBinary *rewrittenNode = new TIntermBinary(
+ TOperator::EOpAssign, CreateTempSymbolNode(returnVariable), oldAtomicFunctionNode);
+
+ auto *parentNode = getParentNode();
+
+ auto *parentBinary = parentNode->getAsBinaryNode();
+ if (parentBinary && parentBinary->getOp() == EOpInitialize)
+ {
+ insertStatementInParentBlock(rewrittenNode);
+ queueReplacement(CreateTempSymbolNode(returnVariable), OriginalNode::IS_DROPPED);
+ }
+ else
+ {
+ // As all atomic function assignment will be converted to the last argument of an
+ // interlocked function, if we need the return value, assignment needs to be wrapped with
+ // the comma operator and the temporary variables.
+ if (!parentNode->getAsBlock())
+ {
+ rewrittenNode = TIntermBinary::CreateComma(
+ rewrittenNode, new TIntermSymbol(returnVariable), mShaderVersion);
+ }
+
+ queueReplacement(rewrittenNode, OriginalNode::IS_DROPPED);
+ }
+}
+
+const TVariable *RewriteAtomicFunctionExpressionsTraverser::getTempVariable(const TType *type)
+{
+ TIntermDeclaration *variableDeclaration;
+ TVariable *returnVariable =
+ DeclareTempVariable(mSymbolTable, type, EvqTemporary, &variableDeclaration);
+ mTempVariables.push_back(variableDeclaration);
+ return returnVariable;
+}
+
+bool RewriteAtomicFunctionExpressionsTraverser::IsAtomicExchangeOrCompSwapNoReturnValue(
+ TIntermAggregate *node,
+ TIntermNode *parentNode)
+{
+ ASSERT(node);
+ return (node->getOp() == EOpAtomicExchange || node->getOp() == EOpAtomicCompSwap) &&
+ parentNode && parentNode->getAsBlock();
+}
+
+bool RewriteAtomicFunctionExpressionsTraverser::IsAtomicFunctionInsideExpression(
+ TIntermAggregate *node,
+ TIntermNode *parentNode)
+{
+ ASSERT(node);
+ // We only need to handle atomic functions with a parent that it is not block nodes. If the
+ // parent node is block, it means that the atomic function is not inside an expression.
+ if (!BuiltInGroup::IsAtomicMemory(node->getOp()) || parentNode->getAsBlock())
+ {
+ return false;
+ }
+
+ auto *parentAsBinary = parentNode->getAsBinaryNode();
+ // Assignments are handled in OutputHLSL
+ return !parentAsBinary || parentAsBinary->getOp() != EOpAssign;
+}
+
+bool RewriteAtomicFunctionExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ ASSERT(visit == PostVisit);
+ // Skip atomic memory functions for SSBO. They will be processed in the OutputHLSL traverser.
+ if (BuiltInGroup::IsAtomicMemory(node->getOp()) &&
+ IsInShaderStorageBlock((*node->getSequence())[0]->getAsTyped()))
+ {
+ return false;
+ }
+
+ TIntermNode *parentNode = getParentNode();
+ if (IsAtomicExchangeOrCompSwapNoReturnValue(node, parentNode) ||
+ IsAtomicFunctionInsideExpression(node, parentNode))
+ {
+ rewriteAtomicFunctionCallNode(node);
+ }
+
+ return true;
+}
+
+bool RewriteAtomicFunctionExpressionsTraverser::visitBlock(Visit visit, TIntermBlock *node)
+{
+ ASSERT(visit == PostVisit);
+
+ if (!mTempVariables.empty() && getParentNode()->getAsFunctionDefinition())
+ {
+ insertStatementsInBlockAtPosition(node, 0, mTempVariables, TIntermSequence());
+ mTempVariables.clear();
+ }
+
+ return true;
+}
+
+} // anonymous namespace
+
+bool RewriteAtomicFunctionExpressions(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable,
+ int shaderVersion)
+{
+ RewriteAtomicFunctionExpressionsTraverser traverser(symbolTable, shaderVersion);
+ traverser.traverse(root);
+ return traverser.updateTree(compiler, root);
+}
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.h
new file mode 100644
index 0000000000..60e6ffa2d1
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.h
@@ -0,0 +1,42 @@
+//
+// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RewriteAtomicFunctionExpressions rewrites the expressions that contain
+// atomic function calls and cannot be directly translated into HLSL into
+// several simple ones that can be easily handled in the HLSL translator.
+//
+// We need to rewite these expressions because:
+// 1. All GLSL atomic functions have return values, which all represent the
+// original value of the shared or ssbo variable; while all HLSL atomic
+// functions don't, and the original value can be stored in the last
+// parameter of the function call.
+// 2. For HLSL atomic functions, the last parameter that stores the original
+// value is optional except for InterlockedExchange and
+// InterlockedCompareExchange. Missing original_value in the call of
+// InterlockedExchange or InterlockedCompareExchange results in a compile
+// error from HLSL compiler.
+//
+// RewriteAtomicFunctionExpressions is a function that can modify the AST
+// to ensure all the expressions that contain atomic function calls can be
+// directly translated into HLSL expressions.
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITE_ATOMIC_FUNCTION_EXPRESSIONS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITE_ATOMIC_FUNCTION_EXPRESSIONS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermNode;
+class TSymbolTable;
+
+[[nodiscard]] bool RewriteAtomicFunctionExpressions(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable,
+ int shaderVersion);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITE_ATOMIC_FUNCTION_EXPRESSIONS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.cpp
new file mode 100644
index 0000000000..10647d1bf1
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.cpp
@@ -0,0 +1,123 @@
+//
+// Copyright 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RewriteElseBlocks.cpp: Implementation for tree transform to change
+// all if-else blocks to if-if blocks.
+//
+
+#include "compiler/translator/tree_ops/d3d/RewriteElseBlocks.h"
+
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/NodeSearch.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class ElseBlockRewriter : public TIntermTraverser
+{
+ public:
+ ElseBlockRewriter(TSymbolTable *symbolTable);
+
+ protected:
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *aggregate) override;
+ bool visitBlock(Visit visit, TIntermBlock *block) override;
+
+ private:
+ TIntermNode *rewriteIfElse(TIntermIfElse *ifElse);
+
+ const TType *mFunctionType;
+};
+
+ElseBlockRewriter::ElseBlockRewriter(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, true, symbolTable), mFunctionType(nullptr)
+{}
+
+bool ElseBlockRewriter::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ // Store the current function context (see comment below)
+ mFunctionType = ((visit == PreVisit) ? &node->getFunctionPrototype()->getType() : nullptr);
+ return true;
+}
+
+bool ElseBlockRewriter::visitBlock(Visit visit, TIntermBlock *node)
+{
+ if (visit == PostVisit)
+ {
+ for (size_t statementIndex = 0; statementIndex != node->getSequence()->size();
+ statementIndex++)
+ {
+ TIntermNode *statement = (*node->getSequence())[statementIndex];
+ TIntermIfElse *ifElse = statement->getAsIfElseNode();
+ if (ifElse && ifElse->getFalseBlock() != nullptr)
+ {
+ (*node->getSequence())[statementIndex] = rewriteIfElse(ifElse);
+ }
+ }
+ }
+ return true;
+}
+
+TIntermNode *ElseBlockRewriter::rewriteIfElse(TIntermIfElse *ifElse)
+{
+ ASSERT(ifElse != nullptr);
+
+ TIntermDeclaration *storeCondition = nullptr;
+ TVariable *conditionVariable =
+ DeclareTempVariable(mSymbolTable, ifElse->getCondition(), EvqTemporary, &storeCondition);
+
+ TIntermBlock *falseBlock = nullptr;
+
+ TType boolType(EbtBool, EbpUndefined, EvqTemporary);
+
+ if (ifElse->getFalseBlock())
+ {
+ TIntermBlock *negatedElse = nullptr;
+ // crbug.com/346463
+ // D3D generates error messages claiming a function has no return value, when rewriting
+ // an if-else clause that returns something non-void in a function. By appending mock
+ // returns (that are unreachable) we can silence this compile error.
+ if (mFunctionType && mFunctionType->getBasicType() != EbtVoid)
+ {
+ TIntermNode *returnNode = new TIntermBranch(EOpReturn, CreateZeroNode(*mFunctionType));
+ negatedElse = new TIntermBlock();
+ negatedElse->appendStatement(returnNode);
+ }
+
+ TIntermSymbol *conditionSymbolElse = CreateTempSymbolNode(conditionVariable);
+ TIntermUnary *negatedCondition =
+ new TIntermUnary(EOpLogicalNot, conditionSymbolElse, nullptr);
+ TIntermIfElse *falseIfElse =
+ new TIntermIfElse(negatedCondition, ifElse->getFalseBlock(), negatedElse);
+ falseBlock = EnsureBlock(falseIfElse);
+ }
+
+ TIntermSymbol *conditionSymbolSel = CreateTempSymbolNode(conditionVariable);
+ TIntermIfElse *newIfElse =
+ new TIntermIfElse(conditionSymbolSel, ifElse->getTrueBlock(), falseBlock);
+
+ TIntermBlock *block = new TIntermBlock();
+ block->getSequence()->push_back(storeCondition);
+ block->getSequence()->push_back(newIfElse);
+
+ return block;
+}
+
+} // anonymous namespace
+
+bool RewriteElseBlocks(TCompiler *compiler, TIntermNode *node, TSymbolTable *symbolTable)
+{
+ ElseBlockRewriter rewriter(symbolTable);
+ node->traverse(&rewriter);
+
+ return compiler->validateAST(node);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.h
new file mode 100644
index 0000000000..cab0f7090c
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.h
@@ -0,0 +1,27 @@
+//
+// Copyright 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RewriteElseBlocks.h: Prototype for tree transform to change
+// all if-else blocks to if-if blocks.
+//
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITEELSEBLOCKS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITEELSEBLOCKS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+
+class TCompiler;
+class TIntermNode;
+class TSymbolTable;
+
+[[nodiscard]] bool RewriteElseBlocks(TCompiler *compiler,
+ TIntermNode *node,
+ TSymbolTable *symbolTable);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITEELSEBLOCKS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp
new file mode 100644
index 0000000000..709f394878
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp
@@ -0,0 +1,420 @@
+//
+// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RewriteExpressionsWithShaderStorageBlock rewrites the expressions that contain shader storage
+// block calls into several simple ones that can be easily handled in the HLSL translator. After the
+// AST pass, all ssbo related blocks will be like below:
+// ssbo_access_chain = ssbo_access_chain;
+// ssbo_access_chain = expr_no_ssbo;
+// lvalue_no_ssbo = ssbo_access_chain;
+//
+
+#include "compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.h"
+
+#include "compiler/translator/Symbol.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+namespace
+{
+
+bool IsIncrementOrDecrementOperator(TOperator op)
+{
+ switch (op)
+ {
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsCompoundAssignment(TOperator op)
+{
+ switch (op)
+ {
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ case EOpDivAssign:
+ case EOpIModAssign:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// EOpIndexDirect, EOpIndexIndirect, EOpIndexDirectStruct, EOpIndexDirectInterfaceBlock belong to
+// operators in SSBO access chain.
+bool IsReadonlyBinaryOperatorNotInSSBOAccessChain(TOperator op)
+{
+ switch (op)
+ {
+ case EOpComma:
+ case EOpAdd:
+ case EOpSub:
+ case EOpMul:
+ case EOpDiv:
+ case EOpIMod:
+ case EOpBitShiftLeft:
+ case EOpBitShiftRight:
+ case EOpBitwiseAnd:
+ case EOpBitwiseXor:
+ case EOpBitwiseOr:
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ case EOpVectorTimesScalar:
+ case EOpMatrixTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesMatrix:
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ case EOpLogicalAnd:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool HasSSBOAsFunctionArgument(TIntermSequence *arguments)
+{
+ for (TIntermNode *arg : *arguments)
+ {
+ TIntermTyped *typedArg = arg->getAsTyped();
+ if (IsInShaderStorageBlock(typedArg))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+class RewriteExpressionsWithShaderStorageBlockTraverser : public TIntermTraverser
+{
+ public:
+ RewriteExpressionsWithShaderStorageBlockTraverser(TSymbolTable *symbolTable);
+ void nextIteration();
+ bool foundSSBO() const { return mFoundSSBO; }
+
+ private:
+ bool visitBinary(Visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+
+ TIntermSymbol *insertInitStatementAndReturnTempSymbol(TIntermTyped *node,
+ TIntermSequence *insertions);
+
+ bool mFoundSSBO;
+};
+
+RewriteExpressionsWithShaderStorageBlockTraverser::
+ RewriteExpressionsWithShaderStorageBlockTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, true, false, symbolTable), mFoundSSBO(false)
+{}
+
+TIntermSymbol *
+RewriteExpressionsWithShaderStorageBlockTraverser::insertInitStatementAndReturnTempSymbol(
+ TIntermTyped *node,
+ TIntermSequence *insertions)
+{
+ TIntermDeclaration *variableDeclaration;
+ TVariable *tempVariable =
+ DeclareTempVariable(mSymbolTable, node, EvqTemporary, &variableDeclaration);
+
+ insertions->push_back(variableDeclaration);
+ return CreateTempSymbolNode(tempVariable);
+}
+
+bool RewriteExpressionsWithShaderStorageBlockTraverser::visitBinary(Visit visit,
+ TIntermBinary *node)
+{
+ // Make sure that the expression is caculated from left to right.
+ if (visit != InVisit)
+ {
+ return true;
+ }
+
+ if (mFoundSSBO)
+ {
+ return false;
+ }
+
+ bool rightSSBO = IsInShaderStorageBlock(node->getRight());
+ bool leftSSBO = IsInShaderStorageBlock(node->getLeft());
+ if (!leftSSBO && !rightSSBO)
+ {
+ return true;
+ }
+
+ // case 1: Compound assigment operator
+ // original:
+ // lssbo += expr;
+ // new:
+ // var rvalue = expr;
+ // var temp = lssbo;
+ // temp += rvalue;
+ // lssbo = temp;
+ //
+ // original:
+ // lvalue_no_ssbo += rssbo;
+ // new:
+ // var rvalue = rssbo;
+ // lvalue_no_ssbo += rvalue;
+ if (IsCompoundAssignment(node->getOp()))
+ {
+ mFoundSSBO = true;
+ TIntermSequence insertions;
+ TIntermTyped *rightNode =
+ insertInitStatementAndReturnTempSymbol(node->getRight(), &insertions);
+ if (leftSSBO)
+ {
+ TIntermSymbol *tempSymbol =
+ insertInitStatementAndReturnTempSymbol(node->getLeft()->deepCopy(), &insertions);
+ TIntermBinary *tempCompoundOperate =
+ new TIntermBinary(node->getOp(), tempSymbol->deepCopy(), rightNode->deepCopy());
+ insertions.push_back(tempCompoundOperate);
+ insertStatementsInParentBlock(insertions);
+
+ TIntermBinary *assignTempValueToSSBO =
+ new TIntermBinary(EOpAssign, node->getLeft(), tempSymbol->deepCopy());
+ queueReplacement(assignTempValueToSSBO, OriginalNode::IS_DROPPED);
+ }
+ else
+ {
+ insertStatementsInParentBlock(insertions);
+ TIntermBinary *compoundAssignRValueToLValue =
+ new TIntermBinary(node->getOp(), node->getLeft(), rightNode->deepCopy());
+ queueReplacement(compoundAssignRValueToLValue, OriginalNode::IS_DROPPED);
+ }
+ }
+ // case 2: Readonly binary operator
+ // original:
+ // ssbo0 + ssbo1 + ssbo2;
+ // new:
+ // var temp0 = ssbo0;
+ // var temp1 = ssbo1;
+ // var temp2 = ssbo2;
+ // temp0 + temp1 + temp2;
+ else if (IsReadonlyBinaryOperatorNotInSSBOAccessChain(node->getOp()) && (leftSSBO || rightSSBO))
+ {
+ mFoundSSBO = true;
+ TIntermTyped *rightNode = node->getRight();
+ TIntermTyped *leftNode = node->getLeft();
+ TIntermSequence insertions;
+ if (rightSSBO)
+ {
+ rightNode = insertInitStatementAndReturnTempSymbol(node->getRight(), &insertions);
+ }
+ if (leftSSBO)
+ {
+ leftNode = insertInitStatementAndReturnTempSymbol(node->getLeft(), &insertions);
+ }
+
+ insertStatementsInParentBlock(insertions);
+ TIntermBinary *newExpr =
+ new TIntermBinary(node->getOp(), leftNode->deepCopy(), rightNode->deepCopy());
+ queueReplacement(newExpr, OriginalNode::IS_DROPPED);
+ }
+ return !mFoundSSBO;
+}
+
+// case 3: ssbo as the argument of aggregate type
+// original:
+// foo(ssbo);
+// new:
+// var tempArg = ssbo;
+// foo(tempArg);
+// ssbo = tempArg; (Optional based on whether ssbo is an out|input argument)
+//
+// original:
+// foo(ssbo) * expr;
+// new:
+// var tempArg = ssbo;
+// var tempReturn = foo(tempArg);
+// ssbo = tempArg; (Optional based on whether ssbo is an out|input argument)
+// tempReturn * expr;
+bool RewriteExpressionsWithShaderStorageBlockTraverser::visitAggregate(Visit visit,
+ TIntermAggregate *node)
+{
+ // Make sure that visitAggregate is only executed once for same node.
+ if (visit != PreVisit)
+ {
+ return true;
+ }
+
+ if (mFoundSSBO)
+ {
+ return false;
+ }
+
+ // We still need to process the ssbo as the non-first argument of atomic memory functions.
+ if (BuiltInGroup::IsAtomicMemory(node->getOp()) &&
+ IsInShaderStorageBlock((*node->getSequence())[0]->getAsTyped()))
+ {
+ return true;
+ }
+
+ if (!HasSSBOAsFunctionArgument(node->getSequence()))
+ {
+ return true;
+ }
+
+ mFoundSSBO = true;
+ TIntermSequence insertions;
+ TIntermSequence readBackToSSBOs;
+ TIntermSequence *originalArguments = node->getSequence();
+ for (size_t i = 0; i < node->getChildCount(); ++i)
+ {
+ TIntermTyped *ssboArgument = (*originalArguments)[i]->getAsTyped();
+ if (IsInShaderStorageBlock(ssboArgument))
+ {
+ TIntermSymbol *argumentCopy =
+ insertInitStatementAndReturnTempSymbol(ssboArgument, &insertions);
+ if (node->getFunction() != nullptr)
+ {
+ TQualifier qual = node->getFunction()->getParam(i)->getType().getQualifier();
+ if (qual == EvqParamInOut || qual == EvqParamOut)
+ {
+ TIntermBinary *readBackToSSBO = new TIntermBinary(
+ EOpAssign, ssboArgument->deepCopy(), argumentCopy->deepCopy());
+ readBackToSSBOs.push_back(readBackToSSBO);
+ }
+ }
+ node->replaceChildNode(ssboArgument, argumentCopy);
+ }
+ }
+
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ if (parentBlock)
+ {
+ // Aggregate node is as a single sentence.
+ insertions.push_back(node);
+ if (!readBackToSSBOs.empty())
+ {
+ insertions.insert(insertions.end(), readBackToSSBOs.begin(), readBackToSSBOs.end());
+ }
+ mMultiReplacements.emplace_back(parentBlock, node, std::move(insertions));
+ }
+ else
+ {
+ // Aggregate node is inside an expression.
+ TIntermSymbol *tempSymbol = insertInitStatementAndReturnTempSymbol(node, &insertions);
+ if (!readBackToSSBOs.empty())
+ {
+ insertions.insert(insertions.end(), readBackToSSBOs.begin(), readBackToSSBOs.end());
+ }
+ insertStatementsInParentBlock(insertions);
+ queueReplacement(tempSymbol->deepCopy(), OriginalNode::IS_DROPPED);
+ }
+
+ return false;
+}
+
+bool RewriteExpressionsWithShaderStorageBlockTraverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (mFoundSSBO)
+ {
+ return false;
+ }
+
+ if (!IsInShaderStorageBlock(node->getOperand()))
+ {
+ return true;
+ }
+
+ // .length() is processed in OutputHLSL.
+ if (node->getOp() == EOpArrayLength)
+ {
+ return true;
+ }
+
+ mFoundSSBO = true;
+
+ // case 4: ssbo as the operand of ++/--
+ // original:
+ // ++ssbo * expr;
+ // new:
+ // var temp1 = ssbo;
+ // var temp2 = ++temp1;
+ // ssbo = temp1;
+ // temp2 * expr;
+ if (IsIncrementOrDecrementOperator(node->getOp()))
+ {
+ TIntermSequence insertions;
+ TIntermSymbol *temp1 =
+ insertInitStatementAndReturnTempSymbol(node->getOperand(), &insertions);
+ TIntermUnary *newUnary = new TIntermUnary(node->getOp(), temp1->deepCopy(), nullptr);
+ TIntermSymbol *temp2 = insertInitStatementAndReturnTempSymbol(newUnary, &insertions);
+ TIntermBinary *readBackToSSBO =
+ new TIntermBinary(EOpAssign, node->getOperand()->deepCopy(), temp1->deepCopy());
+ insertions.push_back(readBackToSSBO);
+ insertStatementsInParentBlock(insertions);
+ queueReplacement(temp2->deepCopy(), OriginalNode::IS_DROPPED);
+ }
+ // case 5: ssbo as the operand of readonly unary operator
+ // original:
+ // ~ssbo * expr;
+ // new:
+ // var temp = ssbo;
+ // ~temp * expr;
+ else
+ {
+ TIntermSequence insertions;
+ TIntermSymbol *temp =
+ insertInitStatementAndReturnTempSymbol(node->getOperand(), &insertions);
+ insertStatementsInParentBlock(insertions);
+ node->replaceChildNode(node->getOperand(), temp->deepCopy());
+ }
+ return false;
+}
+
+void RewriteExpressionsWithShaderStorageBlockTraverser::nextIteration()
+{
+ mFoundSSBO = false;
+}
+
+} // anonymous namespace
+
+bool RewriteExpressionsWithShaderStorageBlock(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable)
+{
+ RewriteExpressionsWithShaderStorageBlockTraverser traverser(symbolTable);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundSSBO())
+ {
+ if (!traverser.updateTree(compiler, root))
+ {
+ return false;
+ }
+ }
+ } while (traverser.foundSSBO());
+
+ return true;
+}
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.h
new file mode 100644
index 0000000000..bf824c4a23
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.h
@@ -0,0 +1,37 @@
+//
+// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RewriteExpressionsWithShaderStorageBlock rewrites the expressions that contain shader storage
+// block calls into several simple ones that can be easily handled in the HLSL translator. After the
+// AST pass, all ssbo related blocks will be like below:
+// ssbo_access_chain = ssbo_access_chain;
+// ssbo_access_chain = expr_no_ssbo;
+// lvalue_no_ssbo = ssbo_access_chain;
+//
+// Below situations are needed to be rewritten (Details can be found in .cpp file).
+// SSBO as the operand of compound assignment operators.
+// SSBO as the operand of ++/--.
+// SSBO as the operand of repeated assignment.
+// SSBO as the operand of readonly unary/binary/ternary operators.
+// SSBO as the argument of aggregate type.
+// SSBO as the condition of if/switch/while/do-while/for
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITE_EXPRESSIONS_WITH_SHADER_STORAGE_BLOCK_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITE_EXPRESSIONS_WITH_SHADER_STORAGE_BLOCK_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermNode;
+class TSymbolTable;
+
+[[nodiscard]] bool RewriteExpressionsWithShaderStorageBlock(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITE_EXPRESSIONS_WITH_SHADER_STORAGE_BLOCK_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.cpp
new file mode 100644
index 0000000000..5d8cdb657d
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.cpp
@@ -0,0 +1,117 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of evaluating unary integer variable bug workaround.
+// See header for more info.
+
+#include "compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.h"
+
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ [[nodiscard]] static bool Apply(TCompiler *compiler, TIntermNode *root);
+
+ private:
+ Traverser();
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ void nextIteration();
+
+ bool mFound = false;
+};
+
+// static
+bool Traverser::Apply(TCompiler *compiler, TIntermNode *root)
+{
+ Traverser traverser;
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ if (!traverser.updateTree(compiler, root))
+ {
+ return false;
+ }
+ }
+ } while (traverser.mFound);
+
+ return true;
+}
+
+Traverser::Traverser() : TIntermTraverser(true, false, false) {}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+}
+
+bool Traverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Decide if the current unary operator is unary minus.
+ if (node->getOp() != EOpNegative)
+ {
+ return true;
+ }
+
+ // Decide if the current operand is an integer variable.
+ TIntermTyped *opr = node->getOperand();
+ if (!opr->getType().isScalarInt())
+ {
+ return true;
+ }
+
+ // Potential problem case detected, apply workaround: -(int) -> ~(int) + 1.
+ // ~(int)
+ TIntermUnary *bitwiseNot = new TIntermUnary(EOpBitwiseNot, opr, nullptr);
+ bitwiseNot->setLine(opr->getLine());
+
+ // Constant 1 (or 1u)
+ TConstantUnion *one = new TConstantUnion();
+ if (opr->getType().getBasicType() == EbtInt)
+ {
+ one->setIConst(1);
+ }
+ else
+ {
+ one->setUConst(1u);
+ }
+ TType *oneType = new TType(opr->getType());
+ oneType->setQualifier(EvqConst);
+
+ TIntermConstantUnion *oneNode = new TIntermConstantUnion(one, *oneType);
+ oneNode->setLine(opr->getLine());
+
+ // ~(int) + 1
+ TIntermBinary *add = new TIntermBinary(EOpAdd, bitwiseNot, oneNode);
+ add->setLine(opr->getLine());
+
+ queueReplacement(add, OriginalNode::IS_DROPPED);
+
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+bool RewriteUnaryMinusOperatorInt(TCompiler *compiler, TIntermNode *root)
+{
+ return Traverser::Apply(compiler, root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.h
new file mode 100644
index 0000000000..cc5ac86456
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.h
@@ -0,0 +1,23 @@
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This mutating tree traversal works around a bug on evaluating unary
+// integer variable on Intel D3D driver. It works by rewriting -(int) to
+// ~(int) + 1 when evaluating unary integer variables.
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITEUNARYMINUSOPERATORINT_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITEUNARYMINUSOPERATORINT_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermNode;
+
+[[nodiscard]] bool RewriteUnaryMinusOperatorInt(TCompiler *compiler, TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_REWRITEUNARYMINUSOPERATORINT_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.cpp
new file mode 100644
index 0000000000..170b29acd7
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.cpp
@@ -0,0 +1,83 @@
+//
+// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SeparateArrayConstructorStatements splits statements that are array constructors and drops all of
+// their constant arguments. For example, a statement like:
+// int[2](0, i++);
+// Will be changed to:
+// i++;
+
+#include "compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.h"
+
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void SplitConstructorArgs(const TIntermSequence &originalArgs, TIntermSequence *argsOut)
+{
+ for (TIntermNode *arg : originalArgs)
+ {
+ TIntermTyped *argTyped = arg->getAsTyped();
+ if (argTyped->hasSideEffects())
+ {
+ TIntermAggregate *argAggregate = argTyped->getAsAggregate();
+ if (argTyped->isArray() && argAggregate && argAggregate->isConstructor())
+ {
+ SplitConstructorArgs(*argAggregate->getSequence(), argsOut);
+ }
+ else
+ {
+ argsOut->push_back(argTyped);
+ }
+ }
+ }
+}
+
+class SeparateArrayConstructorStatementsTraverser : public TIntermTraverser
+{
+ public:
+ SeparateArrayConstructorStatementsTraverser();
+
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+};
+
+SeparateArrayConstructorStatementsTraverser::SeparateArrayConstructorStatementsTraverser()
+ : TIntermTraverser(true, false, false)
+{}
+
+bool SeparateArrayConstructorStatementsTraverser::visitAggregate(Visit visit,
+ TIntermAggregate *node)
+{
+ TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
+ if (!parentAsBlock)
+ {
+ return false;
+ }
+ if (!node->isArray() || !node->isConstructor())
+ {
+ return false;
+ }
+
+ TIntermSequence constructorArgs;
+ SplitConstructorArgs(*node->getSequence(), &constructorArgs);
+ mMultiReplacements.emplace_back(parentAsBlock, node, std::move(constructorArgs));
+
+ return false;
+}
+
+} // namespace
+
+bool SeparateArrayConstructorStatements(TCompiler *compiler, TIntermBlock *root)
+{
+ SeparateArrayConstructorStatementsTraverser traverser;
+ root->traverse(&traverser);
+ return traverser.updateTree(compiler, root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.h
new file mode 100644
index 0000000000..225a4651da
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.h
@@ -0,0 +1,25 @@
+//
+// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SeparateArrayConstructorStatements splits statements that are array constructors and drops all of
+// their constant arguments. For example, a statement like:
+// int[2](0, i++);
+// Will be changed to:
+// i++;
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEARRAYCONSTRUCTORSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEARRAYCONSTRUCTORSTATEMENTS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermBlock;
+
+[[nodiscard]] bool SeparateArrayConstructorStatements(TCompiler *compiler, TIntermBlock *root);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEARRAYCONSTRUCTORSTATEMENTS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.cpp
new file mode 100644
index 0000000000..de5c393cf6
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.cpp
@@ -0,0 +1,89 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The SeparateArrayInitialization function splits each array initialization into a declaration and
+// an assignment.
+// Example:
+// type[n] a = initializer;
+// will effectively become
+// type[n] a;
+// a = initializer;
+//
+// Note that if the array is declared as const, the initialization may still be split, making the
+// AST technically invalid. Because of that this transformation should only be used when subsequent
+// stages don't care about const qualifiers. However, the initialization will not be split if the
+// initializer can be written as a HLSL literal.
+
+#include "compiler/translator/tree_ops/d3d/SeparateArrayInitialization.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/OutputHLSL.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class SeparateArrayInitTraverser : private TIntermTraverser
+{
+ public:
+ [[nodiscard]] static bool apply(TCompiler *compiler, TIntermNode *root);
+
+ private:
+ SeparateArrayInitTraverser();
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+};
+
+bool SeparateArrayInitTraverser::apply(TCompiler *compiler, TIntermNode *root)
+{
+ SeparateArrayInitTraverser separateInit;
+ root->traverse(&separateInit);
+ return separateInit.updateTree(compiler, root);
+}
+
+SeparateArrayInitTraverser::SeparateArrayInitTraverser() : TIntermTraverser(true, false, false) {}
+
+bool SeparateArrayInitTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ TIntermSequence *sequence = node->getSequence();
+ TIntermBinary *initNode = sequence->back()->getAsBinaryNode();
+ if (initNode != nullptr && initNode->getOp() == EOpInitialize)
+ {
+ TIntermTyped *initializer = initNode->getRight();
+ if (initializer->isArray() && !initializer->hasConstantValue())
+ {
+ // We rely on that array declarations have been isolated to single declarations.
+ ASSERT(sequence->size() == 1);
+ TIntermTyped *symbol = initNode->getLeft();
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ ASSERT(parentBlock != nullptr);
+
+ TIntermSequence replacements;
+
+ TIntermDeclaration *replacementDeclaration = new TIntermDeclaration();
+ replacementDeclaration->appendDeclarator(symbol);
+ replacementDeclaration->setLine(symbol->getLine());
+ replacements.push_back(replacementDeclaration);
+
+ TIntermBinary *replacementAssignment =
+ new TIntermBinary(EOpAssign, symbol, initializer);
+ replacementAssignment->setLine(symbol->getLine());
+ replacements.push_back(replacementAssignment);
+
+ mMultiReplacements.emplace_back(parentBlock, node, std::move(replacements));
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+bool SeparateArrayInitialization(TCompiler *compiler, TIntermNode *root)
+{
+ return SeparateArrayInitTraverser::apply(compiler, root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.h
new file mode 100644
index 0000000000..e83554186a
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.h
@@ -0,0 +1,32 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The SeparateArrayInitialization function splits each array initialization into a declaration and
+// an assignment.
+// Example:
+// type[n] a = initializer;
+// will effectively become
+// type[n] a;
+// a = initializer;
+//
+// Note that if the array is declared as const, the initialization may still be split, making the
+// AST technically invalid. Because of that this transformation should only be used when subsequent
+// stages don't care about const qualifiers. However, the initialization will not be split if the
+// initializer can be written as a HLSL literal.
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEARRAYINITIALIZATION_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEARRAYINITIALIZATION_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermNode;
+
+[[nodiscard]] bool SeparateArrayInitialization(TCompiler *compiler, TIntermNode *root);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEARRAYINITIALIZATION_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.cpp
new file mode 100644
index 0000000000..58d8a0a9be
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.cpp
@@ -0,0 +1,138 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names
+// from more complex expressions, assigning them to a temporary variable a#.
+// Examples where a, b and c are all arrays:
+// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2;
+// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i];
+
+#include "compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.h"
+
+#include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// Traverser that separates one array expression into a statement at a time.
+class SeparateExpressionsTraverser : public TIntermTraverser
+{
+ public:
+ SeparateExpressionsTraverser(TSymbolTable *symbolTable);
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+ void nextIteration();
+ bool foundArrayExpression() const { return mFoundArrayExpression; }
+
+ protected:
+ // Marked to true once an operation that needs to be hoisted out of the expression has been
+ // found. After that, no more AST updates are performed on that traversal.
+ bool mFoundArrayExpression;
+
+ IntermNodePatternMatcher mPatternToSeparateMatcher;
+};
+
+SeparateExpressionsTraverser::SeparateExpressionsTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable),
+ mFoundArrayExpression(false),
+ mPatternToSeparateMatcher(IntermNodePatternMatcher::kExpressionReturningArray)
+{}
+
+// Performs a shallow copy of an assignment node.
+// These shallow copies are useful when a node gets inserted into an aggregate node
+// and also needs to be replaced in its original location by a different node.
+TIntermBinary *CopyAssignmentNode(TIntermBinary *node)
+{
+ return new TIntermBinary(node->getOp(), node->getLeft(), node->getRight());
+}
+
+bool SeparateExpressionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (mFoundArrayExpression)
+ return false;
+
+ // Return if the expression is not an array or if we're not inside a complex expression.
+ if (!mPatternToSeparateMatcher.match(node, getParentNode()))
+ return true;
+
+ ASSERT(node->getOp() == EOpAssign);
+
+ mFoundArrayExpression = true;
+
+ TIntermSequence insertions;
+ insertions.push_back(CopyAssignmentNode(node));
+ // TODO(oetuaho): In some cases it would be more optimal to not add the temporary node, but just
+ // use the original target of the assignment. Care must be taken so that this doesn't happen
+ // when the same array symbol is a target of assignment more than once in one expression.
+ TIntermDeclaration *arrayVariableDeclaration;
+ TVariable *arrayVariable =
+ DeclareTempVariable(mSymbolTable, node->getLeft(), EvqTemporary, &arrayVariableDeclaration);
+ insertions.push_back(arrayVariableDeclaration);
+ insertStatementsInParentBlock(insertions);
+
+ queueReplacement(CreateTempSymbolNode(arrayVariable), OriginalNode::IS_DROPPED);
+
+ return false;
+}
+
+bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFoundArrayExpression)
+ return false; // No need to traverse further
+
+ if (!mPatternToSeparateMatcher.match(node, getParentNode()))
+ return true;
+
+ ASSERT(node->isConstructor() || node->getOp() == EOpCallFunctionInAST);
+
+ mFoundArrayExpression = true;
+
+ TIntermDeclaration *arrayVariableDeclaration;
+ TVariable *arrayVariable = DeclareTempVariable(mSymbolTable, node->shallowCopy(), EvqTemporary,
+ &arrayVariableDeclaration);
+ insertStatementInParentBlock(arrayVariableDeclaration);
+
+ queueReplacement(CreateTempSymbolNode(arrayVariable), OriginalNode::IS_DROPPED);
+
+ return false;
+}
+
+void SeparateExpressionsTraverser::nextIteration()
+{
+ mFoundArrayExpression = false;
+}
+
+} // namespace
+
+bool SeparateExpressionsReturningArrays(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable)
+{
+ SeparateExpressionsTraverser traverser(symbolTable);
+ // Separate one expression at a time, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundArrayExpression())
+ {
+ if (!traverser.updateTree(compiler, root))
+ {
+ return false;
+ }
+ }
+ } while (traverser.foundArrayExpression());
+
+ return true;
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.h
new file mode 100644
index 0000000000..6a9350d89a
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.h
@@ -0,0 +1,28 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names
+// from more complex expressions, assigning them to a temporary variable a#.
+// Examples where a, b and c are all arrays:
+// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2;
+// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i];
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+class TCompiler;
+class TIntermNode;
+class TSymbolTable;
+
+[[nodiscard]] bool SeparateExpressionsReturningArrays(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.cpp
new file mode 100644
index 0000000000..5b104c4889
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.cpp
@@ -0,0 +1,200 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else
+// statements.
+// The results are assigned to s# temporaries, which are used by the main translator instead of
+// the original expression.
+//
+
+#include "compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.h"
+
+#include "compiler/translator/StaticType.h"
+#include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// Traverser that unfolds one short-circuiting operation at a time.
+class UnfoldShortCircuitTraverser : public TIntermTraverser
+{
+ public:
+ UnfoldShortCircuitTraverser(TSymbolTable *symbolTable);
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+
+ void nextIteration();
+ bool foundShortCircuit() const { return mFoundShortCircuit; }
+
+ protected:
+ // Marked to true once an operation that needs to be unfolded has been found.
+ // After that, no more unfolding is performed on that traversal.
+ bool mFoundShortCircuit;
+
+ IntermNodePatternMatcher mPatternToUnfoldMatcher;
+};
+
+UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, true, symbolTable),
+ mFoundShortCircuit(false),
+ mPatternToUnfoldMatcher(IntermNodePatternMatcher::kUnfoldedShortCircuitExpression)
+{}
+
+bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (mFoundShortCircuit)
+ return false;
+
+ if (visit != PreVisit)
+ return true;
+
+ if (!mPatternToUnfoldMatcher.match(node, getParentNode()))
+ return true;
+
+ // If our right node doesn't have side effects, we know we don't need to unfold this
+ // expression: there will be no short-circuiting side effects to avoid
+ // (note: unfolding doesn't depend on the left node -- it will always be evaluated)
+ ASSERT(node->getRight()->hasSideEffects());
+
+ mFoundShortCircuit = true;
+
+ switch (node->getOp())
+ {
+ case EOpLogicalOr:
+ {
+ // "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true;
+ // else s = y;",
+ // and then further simplifies down to "bool s = x; if(!s) s = y;".
+
+ TIntermSequence insertions;
+ const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>();
+ TVariable *resultVariable = CreateTempVariable(mSymbolTable, boolType);
+
+ ASSERT(node->getLeft()->getType() == *boolType);
+ insertions.push_back(CreateTempInitDeclarationNode(resultVariable, node->getLeft()));
+
+ TIntermBlock *assignRightBlock = new TIntermBlock();
+ ASSERT(node->getRight()->getType() == *boolType);
+ assignRightBlock->getSequence()->push_back(
+ CreateTempAssignmentNode(resultVariable, node->getRight()));
+
+ TIntermUnary *notTempSymbol =
+ new TIntermUnary(EOpLogicalNot, CreateTempSymbolNode(resultVariable), nullptr);
+ TIntermIfElse *ifNode = new TIntermIfElse(notTempSymbol, assignRightBlock, nullptr);
+ insertions.push_back(ifNode);
+
+ insertStatementsInParentBlock(insertions);
+
+ queueReplacement(CreateTempSymbolNode(resultVariable), OriginalNode::IS_DROPPED);
+ return false;
+ }
+ case EOpLogicalAnd:
+ {
+ // "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y;
+ // else s = false;",
+ // and then further simplifies down to "bool s = x; if(s) s = y;".
+ TIntermSequence insertions;
+ const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>();
+ TVariable *resultVariable = CreateTempVariable(mSymbolTable, boolType);
+
+ ASSERT(node->getLeft()->getType() == *boolType);
+ insertions.push_back(CreateTempInitDeclarationNode(resultVariable, node->getLeft()));
+
+ TIntermBlock *assignRightBlock = new TIntermBlock();
+ ASSERT(node->getRight()->getType() == *boolType);
+ assignRightBlock->getSequence()->push_back(
+ CreateTempAssignmentNode(resultVariable, node->getRight()));
+
+ TIntermIfElse *ifNode =
+ new TIntermIfElse(CreateTempSymbolNode(resultVariable), assignRightBlock, nullptr);
+ insertions.push_back(ifNode);
+
+ insertStatementsInParentBlock(insertions);
+
+ queueReplacement(CreateTempSymbolNode(resultVariable), OriginalNode::IS_DROPPED);
+ return false;
+ }
+ default:
+ UNREACHABLE();
+ return true;
+ }
+}
+
+bool UnfoldShortCircuitTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+ if (mFoundShortCircuit)
+ return false;
+
+ if (visit != PreVisit)
+ return true;
+
+ if (!mPatternToUnfoldMatcher.match(node))
+ return true;
+
+ mFoundShortCircuit = true;
+
+ // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
+ TIntermSequence insertions;
+ TIntermDeclaration *tempDeclaration = nullptr;
+ TVariable *resultVariable = DeclareTempVariable(mSymbolTable, new TType(node->getType()),
+ EvqTemporary, &tempDeclaration);
+ insertions.push_back(tempDeclaration);
+
+ TIntermBlock *trueBlock = new TIntermBlock();
+ TIntermBinary *trueAssignment =
+ CreateTempAssignmentNode(resultVariable, node->getTrueExpression());
+ trueBlock->getSequence()->push_back(trueAssignment);
+
+ TIntermBlock *falseBlock = new TIntermBlock();
+ TIntermBinary *falseAssignment =
+ CreateTempAssignmentNode(resultVariable, node->getFalseExpression());
+ falseBlock->getSequence()->push_back(falseAssignment);
+
+ TIntermIfElse *ifNode =
+ new TIntermIfElse(node->getCondition()->getAsTyped(), trueBlock, falseBlock);
+ insertions.push_back(ifNode);
+
+ insertStatementsInParentBlock(insertions);
+
+ TIntermSymbol *ternaryResult = CreateTempSymbolNode(resultVariable);
+ queueReplacement(ternaryResult, OriginalNode::IS_DROPPED);
+
+ return false;
+}
+
+void UnfoldShortCircuitTraverser::nextIteration()
+{
+ mFoundShortCircuit = false;
+}
+
+} // namespace
+
+bool UnfoldShortCircuitToIf(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable)
+{
+ UnfoldShortCircuitTraverser traverser(symbolTable);
+ // Unfold one operator at a time, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundShortCircuit())
+ {
+ if (!traverser.updateTree(compiler, root))
+ {
+ return false;
+ }
+ }
+ } while (traverser.foundShortCircuit());
+
+ return true;
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.h
new file mode 100644
index 0000000000..97587e4d38
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.h
@@ -0,0 +1,30 @@
+//
+// Copyright 2002 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else
+// statements.
+// The results are assigned to s# temporaries, which are used by the main translator instead of
+// the original expression.
+//
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_UNFOLDSHORTCIRCUIT_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_UNFOLDSHORTCIRCUIT_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+
+class TCompiler;
+class TIntermNode;
+class TSymbolTable;
+
+[[nodiscard]] bool UnfoldShortCircuitToIf(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_UNFOLDSHORTCIRCUIT_H_
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.cpp
new file mode 100644
index 0000000000..58a941da0e
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.cpp
@@ -0,0 +1,126 @@
+//
+// Copyright 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// WrapSwitchStatementsInBlocks.cpp: Wrap switch statements in blocks and declare all switch-scoped
+// variables there to make the AST compatible with HLSL output.
+//
+// switch (init)
+// {
+// case 0:
+// float f;
+// default:
+// f = 1.0;
+// }
+//
+// becomes
+//
+// {
+// float f;
+// switch (init)
+// {
+// case 0:
+// default:
+// f = 1.0;
+// }
+// }
+
+#include "compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class WrapSwitchStatementsInBlocksTraverser : public TIntermTraverser
+{
+ public:
+ WrapSwitchStatementsInBlocksTraverser() : TIntermTraverser(true, false, false) {}
+
+ bool visitSwitch(Visit visit, TIntermSwitch *node) override;
+};
+
+bool WrapSwitchStatementsInBlocksTraverser::visitSwitch(Visit, TIntermSwitch *node)
+{
+ std::vector<TIntermDeclaration *> declarations;
+ TIntermSequence *statementList = node->getStatementList()->getSequence();
+ for (TIntermNode *statement : *statementList)
+ {
+ TIntermDeclaration *asDeclaration = statement->getAsDeclarationNode();
+ if (asDeclaration)
+ {
+ declarations.push_back(asDeclaration);
+ }
+ }
+ if (declarations.empty())
+ {
+ // We don't need to wrap the switch if it doesn't contain declarations as its direct
+ // descendants.
+ return true;
+ }
+
+ TIntermBlock *wrapperBlock = new TIntermBlock();
+ for (TIntermDeclaration *declaration : declarations)
+ {
+ // SeparateDeclarations should have already been run.
+ ASSERT(declaration->getSequence()->size() == 1);
+
+ TIntermDeclaration *declarationInBlock = new TIntermDeclaration();
+ TIntermSymbol *declaratorAsSymbol = declaration->getSequence()->at(0)->getAsSymbolNode();
+ if (declaratorAsSymbol)
+ {
+ // This is a simple declaration like: "float f;"
+ // Remove the declaration from inside the switch and put it in the wrapping block.
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.emplace_back(node->getStatementList(), declaration,
+ std::move(emptyReplacement));
+
+ declarationInBlock->appendDeclarator(declaratorAsSymbol->deepCopy());
+ // The declaration can't be the last statement inside the switch since unused variables
+ // should already have been pruned.
+ ASSERT(declaration != statementList->back());
+ }
+ else
+ {
+ // This is an init declaration like: "float f = 0.0;"
+ // Change the init declaration inside the switch into an assignment and put a plain
+ // declaration in the wrapping block.
+ TIntermBinary *declaratorAsBinary =
+ declaration->getSequence()->at(0)->getAsBinaryNode();
+ ASSERT(declaratorAsBinary);
+
+ TIntermBinary *initAssignment = new TIntermBinary(
+ EOpAssign, declaratorAsBinary->getLeft(), declaratorAsBinary->getRight());
+
+ queueReplacementWithParent(node->getStatementList(), declaration, initAssignment,
+ OriginalNode::IS_DROPPED);
+
+ declarationInBlock->appendDeclarator(declaratorAsBinary->getLeft()->deepCopy());
+ }
+ wrapperBlock->appendStatement(declarationInBlock);
+ }
+
+ wrapperBlock->appendStatement(node);
+ queueReplacement(wrapperBlock, OriginalNode::BECOMES_CHILD);
+
+ // Should be fine to process multiple switch statements, even nesting ones in the same
+ // traversal.
+ return true;
+}
+
+} // anonymous namespace
+
+// Wrap switch statements in the AST into blocks when needed.
+bool WrapSwitchStatementsInBlocks(TCompiler *compiler, TIntermBlock *root)
+{
+ WrapSwitchStatementsInBlocksTraverser traverser;
+ root->traverse(&traverser);
+ return traverser.updateTree(compiler, root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.h b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.h
new file mode 100644
index 0000000000..0c765c306a
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.h
@@ -0,0 +1,25 @@
+//
+// Copyright 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// WrapSwitchStatementsInBlocks.h: Wrap switch statements in blocks and declare all switch-scoped
+// variables there to make the AST compatible with HLSL output.
+
+#ifndef COMPILER_TRANSLATOR_TREEOPS_D3D_WRAPSWITCHSTATEMENTSINBLOCKS_H_
+#define COMPILER_TRANSLATOR_TREEOPS_D3D_WRAPSWITCHSTATEMENTSINBLOCKS_H_
+
+#include "common/angleutils.h"
+
+namespace sh
+{
+
+class TCompiler;
+class TIntermBlock;
+
+// Wrap switch statements in the AST into blocks when needed. Returns true if the AST was changed.
+[[nodiscard]] bool WrapSwitchStatementsInBlocks(TCompiler *compiler, TIntermBlock *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TREEOPS_D3D_WRAPSWITCHSTATEMENTSINBLOCKS_H_