// Copyright (c) 2024 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 #include #include "paddle/cinn/common/integer_set.h" #include "paddle/cinn/common/ir_util.h" #include "paddle/cinn/ir/ir.h" #include "paddle/cinn/ir/ir_base.h" #include "paddle/cinn/ir/ir_mutator.h" namespace cinn { namespace common { class IterMapToExprNormalizer : public ir::IRMutator<> { public: explicit IterMapToExprNormalizer(const SymbolicExprAnalyzer& analyzer) : analyzer_(analyzer) {} void Convert(Expr* expr) { Visit(expr, expr); } private: void Visit(const Expr* expr, Expr* op) override; ir::IndexExpr ConvertIterSum(ir::IterSum* expr); ir::IndexExpr ConvertIterSplit(ir::IterSplit* expr); private: common::SymbolicExprAnalyzer analyzer_; }; class IterMapRewriter : public ir::IRMutator<> { public: explicit IterMapRewriter(const std::vector& input_iters, const SymbolicExprAnalyzer& analyzer) : analyzer_(analyzer) { for (const auto& iter : input_iters) { // Remove unit extent `for` ? // if (IsOne(iter->upper_bound)) { // var_map_[iter->name] = ir::IterSum::Make({}, iter->lower_bound); // } if (IsZero(iter->lower_bound)) { auto tmp = ir::IterMark::Make(ir::IndexExpr(iter.ptr()), iter->upper_bound); auto mark = tmp.As(); var_map_[iter->name] = ir::IterSplit::Make(tmp); input_marks_.push_back(*mark); } else { PADDLE_THROW(::common::errors::InvalidArgument( "iter should start from 0, but got %d", iter->lower_bound)); } } } void Visit(Expr* expr) { ir::IRMutator<>::Visit(expr, expr); } void Rewrite(Expr* expr) { IRMutator::Visit(expr, expr); *expr = ToIterSum(*expr); } void Visit(const ir::_Var_* op, Expr* expr) override; void Visit(const ir::Add* op, Expr* expr) override; void Visit(const ir::Sub* op, Expr* expr) override; void Visit(const ir::Mul* op, Expr* expr) override; void Visit(const ir::Div* op, Expr* expr) override; void Visit(const ir::Mod* op, Expr* expr) override; private: static Expr ToIterSum(const Expr& expr); static void AddToLhs(ir::IterSum* lhs, const ir::IterSplit& rhs, int sign); static void AddToLhs(ir::IterSum* lhs, const ir::IterSum& rhs, int sign); static void MulToLhs(ir::IterSum* lhs, const ir::IndexExpr& rhs); Expr PreprocessDividend(const Expr& dividend); Expr SplitDivConst(Expr lhs, ir::IndexExpr base, ir::IndexExpr rhs); Expr SplitModConst(Expr lhs, ir::IndexExpr base, ir::IndexExpr rhs); /*! * \brief This function will find the iter which has the expected scale. * For example: * expr: * IterSum(IterSplit(IterMark(i), scale = 32), * IterSplit(IterMark(j), scale = 8), * base = 0) // i * 32 + j * 8 * ret: `1` when expected_scale = 8 * ret: `0` when expected_scale = 32 * * \param expr the input IterSum to search. * \param skip_flag the flag to indicate whether a Iter should be skipped. * \param match_source Whether to only match the same source. * \param rbegin the last position in reverse searching. -1 means the last. * \param first_possible_unit_extent_pos the first possible position of the * unit extent. * \return the index of the Iter with expected scale. return -1 * if not found. */ int32_t FindSplitWithExactScale(const ir::IterSum& expr, const std::vector& skip_flag, const ir::IndexExpr& expected_scale, const Expr& match_source, int32_t rbegin = -1, int32_t first_possible_unit_extent_pos = 0); /*! * \brief Find the first possible position where IterSplit->extent = 1. * \param expr the input IterSum to search. * \return the index of the first IterSplit with extent = 1, * return IterSum.args.size if not found. */ int32_t FindFirstPossibleUnitExtentIndex(const ir::IterSum& expr); /*! * \brief This function will find the base Iter which has the smallest scale. * * For example: * expr: * IterSum(IterSplit(IterMark(i), scale = 32), * IterSplit(IterMark(j), scale = 8), * base = 0) // i * 32 + j * 8 * * ret: `1` when match_source = nullptr * ret: `0` when match_source = IterMark(i) * * \param expr the input IterSum to search. * \param skip_flag the flag to indicate whether a Iter should be skipped. * \param match_source Whether to only match the same source. * \param rbegin the last position in reverse searching. -1 means the last. * \return the index of the base Iter. return -1 if not found. */ int32_t FindBaseSplit(const ir::IterSum& expr, const std::vector& skip_flag, const Expr& match_source, int32_t rbegin = -1); /*! * \brief TryFuse will create new IterMark and returns an aggregated IterSum * that only has one IterSplit with the new IterMark. * * For example: * expr: * IterSum(IterSplit(IterMark(i), scale = 32), * IterSplit(IterMark(j), scale = 8), * base = 0) // i * 32 + j * 8 * ret: * IterSum(IterSplit(IterMark(i * 4 + j), scale = 8), * base = 0) // Treat `i * 4 + j` as a IterMark * * \param expr the input IterSum. * \return the IterSum after fused. */ std::optional TryFuse(const Expr& expr); /*! * \brief TryFuseSameSource will simplify the IterSum by fusing IterSplits * with same source. same source means the IterSplits have same IterMark. * * For example: * expr: * IterSum(IterSplit(IterMark(f), lower = 4, ext = 8 scale = 4), * IterSplit(IterMark(f), lower = 1, ext = 4 scale = 1), * base = 0) // f /4 * 4 + f % 4 * ret: * IterSum(IterSplit(IterMark(f), scale = 1), * base = 0) // f * * \param expr the input IterSum * \return the IterSum after fused. */ std::optional TryFuseSameSource(const Expr& expr); std::unordered_map var_map_; std::vector input_marks_; std::unordered_map sum_fuse_map_; common::SymbolicExprAnalyzer analyzer_; }; class SimplifyBlockBinding : public ir::IRMutator<> { public: explicit SimplifyBlockBinding(const std::vector& loop_var, const SymbolicExprAnalyzer& analyzer) : loop_var_(loop_var), analyzer_(analyzer) {} static void SimplifyBindings(ir::Expr expr, const std::vector& loop_srefs, const SymbolicExprAnalyzer& analyzer) { std::vector loop_var; for (const ir::Expr& sref : loop_srefs) { const ir::For* loop = sref.As(); loop_var.emplace_back(loop->loop_var); } SimplifyBlockBinding(loop_var, analyzer)(&expr); } void operator()(Expr* expr) { IRMutator::Visit(expr, expr); } private: void Visit(const ir::For* op, Expr* expr) override; void Visit(const ir::ScheduleBlockRealize* op, Expr* expr) override; std::vector loop_var_; common::SymbolicExprAnalyzer analyzer_; }; void IterMapSimplify(std::vector& indices, // NOLINT const std::vector& input_iters, const SymbolicExprAnalyzer& analyzer); } // namespace common } // namespace cinn