315 lines
12 KiB
C++
315 lines
12 KiB
C++
// 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_
|