// Copyright (c) 2023 CINN Authors. All Rights Reserved. // // 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. #pragma once #include #include "paddle/cinn/ir/ir.h" #include "paddle/cinn/ir/ir_printer.h" #include "paddle/cinn/optim/ir_simplify.h" namespace cinn { namespace common { /** * Interval of a _Var_. */ struct CasInterval { template CasInterval(T l, T r) : l(l), r(r) { PADDLE_ENFORCE_LE(l, r, ::common::errors::InvalidArgument( "left should not be larger than right")); } /** * @brief When iterator's upper_bound is an ir::Min of a constant value and a * inconstant value, choose the constant value. When iterator's lower_bound is * an ir::Max of a constant value and a inconstant value, choose the constant * value. E.g: expr_l = max(x, 1) and expr_r = min(y,5): max(x, 1) <= * iterator_i <= min(y,5) * * the bounds will be simplified to e_l = 1 and e_r = 5: * 1 <= iterator_i <= 5 */ CasInterval(ir::Expr expr_l, ir::Expr expr_r); ir::Expr ReplaceMinToConstant(ir::Expr expr); ir::Expr ReplaceMaxToConstant(ir::Expr expr); int l, r; // Note: not verify l <= r and (e_l, e_r) has higher priority than (l, r) ir::Expr e_l, e_r; friend std::ostream& operator<<(std::ostream& os, const CasInterval& i); }; using cas_intervals_t = paddle::flat_hash_map; cas_intervals_t CollectVarIntervalsOfExprs(const std::vector& exprs, bool is_lower_bound_zero = true); // A naive implementation of Symbolic Expression Analyzer class SymbolicExprAnalyzer { public: explicit SymbolicExprAnalyzer(const cas_intervals_t& var_intervals) : var_intervals_(var_intervals) {} SymbolicExprAnalyzer(const SymbolicExprAnalyzer&) = default; SymbolicExprAnalyzer(SymbolicExprAnalyzer&&) = default; SymbolicExprAnalyzer& operator=(const SymbolicExprAnalyzer&) = delete; SymbolicExprAnalyzer& operator=(SymbolicExprAnalyzer&&) = delete; // Try to prove the relationship of 2 symbolic expressions, // with the return value being optional. // If proven, return true. If falsified, return false. // If unable to prove or falsify, return nullopt. std::optional Prove(const ir::Expr& condition) const; std::optional ProveEQ(const ir::Expr& lhs, const ir::Expr& rhs) const; std::optional ProveNE(const ir::Expr& lhs, const ir::Expr& rhs) const; std::optional ProveGE(const ir::Expr& lhs, const ir::Expr& rhs) const; std::optional ProveLE(const ir::Expr& lhs, const ir::Expr& rhs) const; std::optional ProveGT(const ir::Expr& lhs, const ir::Expr& rhs) const; std::optional ProveLT(const ir::Expr& lhs, const ir::Expr& rhs) const; std::optional ProveDivisible(const ir::Expr& lhs, const ir::Expr& rhs) const; ir::Expr LowerBound(const ir::Expr& expr) const; ir::Expr UpperBound(const ir::Expr& expr) const; private: const cas_intervals_t& var_intervals_; }; // A helper struct to represent the positive infinity and negative infinity struct SymbolicExprLimit { static ir::Expr positive_inf; static ir::Expr negative_inf; }; // The set consisting of all integers in the interval from min to max class SingleIntervalIntSet { public: explicit SingleIntervalIntSet( const ir::Expr& min = SymbolicExprLimit::positive_inf, const ir::Expr& max = SymbolicExprLimit::negative_inf, cas_intervals_t var_intervals = {}); SingleIntervalIntSet(const SingleIntervalIntSet& set) = default; SingleIntervalIntSet(SingleIntervalIntSet&& set) = default; SingleIntervalIntSet& operator=(const SingleIntervalIntSet& set) = default; SingleIntervalIntSet& operator=(SingleIntervalIntSet&& set) = default; // Try to prove or construct the relationship between two symbolic integer // sets, if unable to determine or construct, return nullopt. std::optional ProveEmpty() const; std::optional ProveAll() const; std::optional ProvePoint() const; std::optional ProveSubSet(const SingleIntervalIntSet& other) const; std::optional ProveSuperSet(const SingleIntervalIntSet& other) const; friend std::optional ProveEQ(const SingleIntervalIntSet& lhs, const SingleIntervalIntSet& rhs); friend std::optional ProvedUnion( const SingleIntervalIntSet& a, const SingleIntervalIntSet& b); friend std::optional ProvedIntersect( const SingleIntervalIntSet& a, const SingleIntervalIntSet& b); friend cas_intervals_t MergeVarIntervals(const SingleIntervalIntSet& a, const SingleIntervalIntSet& b); inline ir::Expr Min() const { return min_; } inline ir::Expr Max() const { return max_; } private: ir::Expr min_ = SymbolicExprLimit::positive_inf; ir::Expr max_ = SymbolicExprLimit::negative_inf; cas_intervals_t var_intervals_; }; std::optional ProveEQ(const SingleIntervalIntSet& lhs, const SingleIntervalIntSet& rhs); std::optional ProvedUnion(const SingleIntervalIntSet& a, const SingleIntervalIntSet& b); std::optional ProvedIntersect( const SingleIntervalIntSet& a, const SingleIntervalIntSet& b); cas_intervals_t MergeVarIntervals(const SingleIntervalIntSet& a, const SingleIntervalIntSet& b); ir::Expr EnhancedSimplifyModExpr( ir::Expr e, const paddle::flat_hash_map& var_intervals); } // namespace common } // namespace cinn