|  | // Copyright (c) 2018 Google LLC. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #ifndef SOURCE_OPT_SCALAR_ANALYSIS_H_ | 
|  | #define SOURCE_OPT_SCALAR_ANALYSIS_H_ | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cstdint> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <unordered_set> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "source/opt/basic_block.h" | 
|  | #include "source/opt/instruction.h" | 
|  | #include "source/opt/scalar_analysis_nodes.h" | 
|  |  | 
|  | namespace spvtools { | 
|  | namespace opt { | 
|  |  | 
|  | class IRContext; | 
|  | class Loop; | 
|  |  | 
|  | // Manager for the Scalar Evolution analysis. Creates and maintains a DAG of | 
|  | // scalar operations generated from analysing the use def graph from incoming | 
|  | // instructions. Each node is hashed as it is added so like node (for instance, | 
|  | // two induction variables i=0,i++ and j=0,j++) become the same node. After | 
|  | // creating a DAG with AnalyzeInstruction it can the be simplified into a more | 
|  | // usable form with SimplifyExpression. | 
|  | class ScalarEvolutionAnalysis { | 
|  | public: | 
|  | explicit ScalarEvolutionAnalysis(IRContext* context); | 
|  |  | 
|  | // Create a unary negative node on |operand|. | 
|  | SENode* CreateNegation(SENode* operand); | 
|  |  | 
|  | // Creates a subtraction between the two operands by adding |operand_1| to the | 
|  | // negation of |operand_2|. | 
|  | SENode* CreateSubtraction(SENode* operand_1, SENode* operand_2); | 
|  |  | 
|  | // Create an addition node between two operands. The |simplify| when set will | 
|  | // allow the function to return an SEConstant instead of an addition if the | 
|  | // two input operands are also constant. | 
|  | SENode* CreateAddNode(SENode* operand_1, SENode* operand_2); | 
|  |  | 
|  | // Create a multiply node between two operands. | 
|  | SENode* CreateMultiplyNode(SENode* operand_1, SENode* operand_2); | 
|  |  | 
|  | // Create a node representing a constant integer. | 
|  | SENode* CreateConstant(int64_t integer); | 
|  |  | 
|  | // Create a value unknown node, such as a load. | 
|  | SENode* CreateValueUnknownNode(const Instruction* inst); | 
|  |  | 
|  | // Create a CantComputeNode. Used to exit out of analysis. | 
|  | SENode* CreateCantComputeNode(); | 
|  |  | 
|  | // Create a new recurrent node with |offset| and |coefficient|, with respect | 
|  | // to |loop|. | 
|  | SENode* CreateRecurrentExpression(const Loop* loop, SENode* offset, | 
|  | SENode* coefficient); | 
|  |  | 
|  | // Construct the DAG by traversing use def chain of |inst|. | 
|  | SENode* AnalyzeInstruction(const Instruction* inst); | 
|  |  | 
|  | // Simplify the |node| by grouping like terms or if contains a recurrent | 
|  | // expression, rewrite the graph so the whole DAG (from |node| down) is in | 
|  | // terms of that recurrent expression. | 
|  | // | 
|  | // For example. | 
|  | // Induction variable i=0, i++ would produce Rec(0,1) so i+1 could be | 
|  | // transformed into Rec(1,1). | 
|  | // | 
|  | // X+X*2+Y-Y+34-17 would be transformed into 3*X + 17, where X and Y are | 
|  | // ValueUnknown nodes (such as a load instruction). | 
|  | SENode* SimplifyExpression(SENode* node); | 
|  |  | 
|  | // Add |prospective_node| into the cache and return a raw pointer to it. If | 
|  | // |prospective_node| is already in the cache just return the raw pointer. | 
|  | SENode* GetCachedOrAdd(std::unique_ptr<SENode> prospective_node); | 
|  |  | 
|  | // Checks that the graph starting from |node| is invariant to the |loop|. | 
|  | bool IsLoopInvariant(const Loop* loop, const SENode* node) const; | 
|  |  | 
|  | // Sets |is_gt_zero| to true if |node| represent a value always strictly | 
|  | // greater than 0. The result of |is_gt_zero| is valid only if the function | 
|  | // returns true. | 
|  | bool IsAlwaysGreaterThanZero(SENode* node, bool* is_gt_zero) const; | 
|  |  | 
|  | // Sets |is_ge_zero| to true if |node| represent a value greater or equals to | 
|  | // 0. The result of |is_ge_zero| is valid only if the function returns true. | 
|  | bool IsAlwaysGreaterOrEqualToZero(SENode* node, bool* is_ge_zero) const; | 
|  |  | 
|  | // Find the recurrent term belonging to |loop| in the graph starting from | 
|  | // |node| and return the coefficient of that recurrent term. Constant zero | 
|  | // will be returned if no recurrent could be found. |node| should be in | 
|  | // simplest form. | 
|  | SENode* GetCoefficientFromRecurrentTerm(SENode* node, const Loop* loop); | 
|  |  | 
|  | // Return a rebuilt graph starting from |node| with the recurrent expression | 
|  | // belonging to |loop| being zeroed out. Returned node will be simplified. | 
|  | SENode* BuildGraphWithoutRecurrentTerm(SENode* node, const Loop* loop); | 
|  |  | 
|  | // Return the recurrent term belonging to |loop| if it appears in the graph | 
|  | // starting at |node| or null if it doesn't. | 
|  | SERecurrentNode* GetRecurrentTerm(SENode* node, const Loop* loop); | 
|  |  | 
|  | SENode* UpdateChildNode(SENode* parent, SENode* child, SENode* new_child); | 
|  |  | 
|  | // The loops in |loop_pair| will be considered the same when constructing | 
|  | // SERecurrentNode objects. This enables analysing dependencies that will be | 
|  | // created during loop fusion. | 
|  | void AddLoopsToPretendAreTheSame( | 
|  | const std::pair<const Loop*, const Loop*>& loop_pair) { | 
|  | pretend_equal_[std::get<1>(loop_pair)] = std::get<0>(loop_pair); | 
|  | } | 
|  |  | 
|  | private: | 
|  | SENode* AnalyzeConstant(const Instruction* inst); | 
|  |  | 
|  | // Handles both addition and subtraction. If the |instruction| is OpISub | 
|  | // then the resulting node will be op1+(-op2) otherwise if it is OpIAdd then | 
|  | // the result will be op1+op2. |instruction| must be OpIAdd or OpISub. | 
|  | SENode* AnalyzeAddOp(const Instruction* instruction); | 
|  |  | 
|  | SENode* AnalyzeMultiplyOp(const Instruction* multiply); | 
|  |  | 
|  | SENode* AnalyzePhiInstruction(const Instruction* phi); | 
|  |  | 
|  | IRContext* context_; | 
|  |  | 
|  | // A map of instructions to SENodes. This is used to track recurrent | 
|  | // expressions as they are added when analyzing instructions. Recurrent | 
|  | // expressions come from phi nodes which by nature can include recursion so we | 
|  | // check if nodes have already been built when analyzing instructions. | 
|  | std::map<const Instruction*, SENode*> recurrent_node_map_; | 
|  |  | 
|  | // On creation we create and cache the CantCompute node so we not need to | 
|  | // perform a needless create step. | 
|  | SENode* cached_cant_compute_; | 
|  |  | 
|  | // Helper functor to allow two unique_ptr to nodes to be compare. Only | 
|  | // needed | 
|  | // for the unordered_set implementation. | 
|  | struct NodePointersEquality { | 
|  | bool operator()(const std::unique_ptr<SENode>& lhs, | 
|  | const std::unique_ptr<SENode>& rhs) const { | 
|  | return *lhs == *rhs; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Cache of nodes. All pointers to the nodes are references to the memory | 
|  | // managed by they set. | 
|  | std::unordered_set<std::unique_ptr<SENode>, SENodeHash, NodePointersEquality> | 
|  | node_cache_; | 
|  |  | 
|  | // Loops that should be considered the same for performing analysis for loop | 
|  | // fusion. | 
|  | std::map<const Loop*, const Loop*> pretend_equal_; | 
|  | }; | 
|  |  | 
|  | // Wrapping class to manipulate SENode pointer using + - * / operators. | 
|  | class SExpression { | 
|  | public: | 
|  | // Implicit on purpose ! | 
|  | SExpression(SENode* node) | 
|  | : node_(node->GetParentAnalysis()->SimplifyExpression(node)), | 
|  | scev_(node->GetParentAnalysis()) {} | 
|  |  | 
|  | inline operator SENode*() const { return node_; } | 
|  | inline SENode* operator->() const { return node_; } | 
|  | const SENode& operator*() const { return *node_; } | 
|  |  | 
|  | inline ScalarEvolutionAnalysis* GetScalarEvolutionAnalysis() const { | 
|  | return scev_; | 
|  | } | 
|  |  | 
|  | inline SExpression operator+(SENode* rhs) const; | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type = 0> | 
|  | inline SExpression operator+(T integer) const; | 
|  | inline SExpression operator+(SExpression rhs) const; | 
|  |  | 
|  | inline SExpression operator-() const; | 
|  | inline SExpression operator-(SENode* rhs) const; | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type = 0> | 
|  | inline SExpression operator-(T integer) const; | 
|  | inline SExpression operator-(SExpression rhs) const; | 
|  |  | 
|  | inline SExpression operator*(SENode* rhs) const; | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type = 0> | 
|  | inline SExpression operator*(T integer) const; | 
|  | inline SExpression operator*(SExpression rhs) const; | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type = 0> | 
|  | inline std::pair<SExpression, int64_t> operator/(T integer) const; | 
|  | // Try to perform a division. Returns the pair <this.node_ / rhs, division | 
|  | // remainder>. If it fails to simplify it, the function returns a | 
|  | // CanNotCompute node. | 
|  | std::pair<SExpression, int64_t> operator/(SExpression rhs) const; | 
|  |  | 
|  | private: | 
|  | SENode* node_; | 
|  | ScalarEvolutionAnalysis* scev_; | 
|  | }; | 
|  |  | 
|  | inline SExpression SExpression::operator+(SENode* rhs) const { | 
|  | return scev_->CreateAddNode(node_, rhs); | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type> | 
|  | inline SExpression SExpression::operator+(T integer) const { | 
|  | return *this + scev_->CreateConstant(integer); | 
|  | } | 
|  |  | 
|  | inline SExpression SExpression::operator+(SExpression rhs) const { | 
|  | return *this + rhs.node_; | 
|  | } | 
|  |  | 
|  | inline SExpression SExpression::operator-() const { | 
|  | return scev_->CreateNegation(node_); | 
|  | } | 
|  |  | 
|  | inline SExpression SExpression::operator-(SENode* rhs) const { | 
|  | return *this + scev_->CreateNegation(rhs); | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type> | 
|  | inline SExpression SExpression::operator-(T integer) const { | 
|  | return *this - scev_->CreateConstant(integer); | 
|  | } | 
|  |  | 
|  | inline SExpression SExpression::operator-(SExpression rhs) const { | 
|  | return *this - rhs.node_; | 
|  | } | 
|  |  | 
|  | inline SExpression SExpression::operator*(SENode* rhs) const { | 
|  | return scev_->CreateMultiplyNode(node_, rhs); | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type> | 
|  | inline SExpression SExpression::operator*(T integer) const { | 
|  | return *this * scev_->CreateConstant(integer); | 
|  | } | 
|  |  | 
|  | inline SExpression SExpression::operator*(SExpression rhs) const { | 
|  | return *this * rhs.node_; | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type> | 
|  | inline std::pair<SExpression, int64_t> SExpression::operator/(T integer) const { | 
|  | return *this / scev_->CreateConstant(integer); | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type> | 
|  | inline SExpression operator+(T lhs, SExpression rhs) { | 
|  | return rhs + lhs; | 
|  | } | 
|  | inline SExpression operator+(SENode* lhs, SExpression rhs) { return rhs + lhs; } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type> | 
|  | inline SExpression operator-(T lhs, SExpression rhs) { | 
|  | // NOLINTNEXTLINE(whitespace/braces) | 
|  | return SExpression{rhs.GetScalarEvolutionAnalysis()->CreateConstant(lhs)} - | 
|  | rhs; | 
|  | } | 
|  | inline SExpression operator-(SENode* lhs, SExpression rhs) { | 
|  | // NOLINTNEXTLINE(whitespace/braces) | 
|  | return SExpression{lhs} - rhs; | 
|  | } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type> | 
|  | inline SExpression operator*(T lhs, SExpression rhs) { | 
|  | return rhs * lhs; | 
|  | } | 
|  | inline SExpression operator*(SENode* lhs, SExpression rhs) { return rhs * lhs; } | 
|  |  | 
|  | template <typename T, | 
|  | typename std::enable_if<std::is_integral<T>::value, int>::type> | 
|  | inline std::pair<SExpression, int64_t> operator/(T lhs, SExpression rhs) { | 
|  | // NOLINTNEXTLINE(whitespace/braces) | 
|  | return SExpression{rhs.GetScalarEvolutionAnalysis()->CreateConstant(lhs)} / | 
|  | rhs; | 
|  | } | 
|  | inline std::pair<SExpression, int64_t> operator/(SENode* lhs, SExpression rhs) { | 
|  | // NOLINTNEXTLINE(whitespace/braces) | 
|  | return SExpression{lhs} / rhs; | 
|  | } | 
|  |  | 
|  | }  // namespace opt | 
|  | }  // namespace spvtools | 
|  | #endif  // SOURCE_OPT_SCALAR_ANALYSIS_H_ |