// // Copyright 2021 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. // // FindPreciseNodes.cpp: Propagates |precise| to AST nodes. // // The high level algorithm is as follows. For every node that "assigns" to a precise object, // subobject (a precise struct whose field is being assigned) or superobject (a struct with a // precise field), two things happen: // // - The operation is marked precise if it's an arithmetic operation // - The right hand side of the assignment is made precise. If only a subobject is precise, only // the corresponding subobject of the right hand side is made precise. // #include "compiler/translator/tree_util/FindPreciseNodes.h" #include "common/hash_containers.h" #include "common/hash_utils.h" #include "compiler/translator/Compiler.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/Symbol.h" #include "compiler/translator/tree_util/IntermTraverse.h" namespace sh { namespace { // An access chain applied to a variable. The |precise|-ness of a node does not change when // indexing arrays, selecting matrix columns or swizzle vectors. This access chain thus only // includes block field selections. The access chain is used to identify the part of an object // that is or should be |precise|. If both a.b.c and a.b are precise, only a.b is every considered. class AccessChain { … }; bool IsIndexOp(TOperator op) { … } const TVariable *AccessChain::build(TIntermTyped *lvalue) { … } void AccessChain::pop_front(size_t n) { … } bool AccessChain::removePrefix(const AccessChain &other) { … } AccessChain GetAssignmentAccessChain(TIntermOperator *node) { … } template <typename Traverser> void TraverseIndexNodesOnly(TIntermNode *node, Traverser *traverser) { … } // An object, which could be a sub-object of a variable. struct ObjectAndAccessChain { … }; bool operator==(const ObjectAndAccessChain &a, const ObjectAndAccessChain &b) { … } struct ObjectAndAccessChainHash { … }; // A map from variables to AST nodes that modify them (i.e. nodes where IsAssignment(op)). VariableToAssignmentNodeMap; // A set of |return| nodes from functions with a |precise| return value. PreciseReturnNodes; // A set of precise objects that need processing, or have been processed. PreciseObjectSet; struct ASTInfo { … }; int GetObjectPreciseSubChainLength(const ObjectAndAccessChain &object) { … } void AddPreciseObject(ASTInfo *info, const ObjectAndAccessChain &object) { … } void AddPreciseSubObjects(ASTInfo *info, const ObjectAndAccessChain &object); void AddObjectIfPrecise(ASTInfo *info, const ObjectAndAccessChain &object) { … } void AddPreciseSubObjects(ASTInfo *info, const ObjectAndAccessChain &object) { … } bool IsArithmeticOp(TOperator op) { … } // A traverser that gathers the following information, used to kick off processing: // // - For each variable, the AST nodes that modify it. // - The set of |precise| return AST node. // - The set of |precise| access chains assigned to. // class InfoGatherTraverser : public TIntermTraverser { … }; // A traverser that, given an access chain, traverses an expression and marks parts of it |precise|. // For example, in the expression |Struct1(a, Struct2(b, c), d)|: // // - Given access chain [1], both |b| and |c| are marked precise. // - Given access chain [1, 0], only |b| is marked precise. // // When access chain is empty, arithmetic nodes are marked |precise| and any access chains found in // their children is recursively added for processing. // // The access chain given to the traverser is derived from the left hand side of an assignment, // while the traverser is run on the right hand side. class PropagatePreciseTraverser : public TIntermTraverser { … }; } // anonymous namespace void FindPreciseNodes(TCompiler *compiler, TIntermBlock *root) { … } } // namespace sh