Files
2026-07-13 12:40:42 +08:00

234 lines
8.1 KiB
C++

// 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 <optional>
#include <unordered_map>
#include <vector>
#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<ir::Var>& 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<ir::IterMark>();
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<bool>& 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<bool>& 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<Expr> 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<Expr> TryFuseSameSource(const Expr& expr);
std::unordered_map<std::string, Expr> var_map_;
std::vector<ir::IterMark> input_marks_;
std::unordered_map<Expr, Expr> sum_fuse_map_;
common::SymbolicExprAnalyzer analyzer_;
};
class SimplifyBlockBinding : public ir::IRMutator<> {
public:
explicit SimplifyBlockBinding(const std::vector<ir::Var>& loop_var,
const SymbolicExprAnalyzer& analyzer)
: loop_var_(loop_var), analyzer_(analyzer) {}
static void SimplifyBindings(ir::Expr expr,
const std::vector<ir::Expr>& loop_srefs,
const SymbolicExprAnalyzer& analyzer) {
std::vector<ir::Var> loop_var;
for (const ir::Expr& sref : loop_srefs) {
const ir::For* loop = sref.As<ir::For>();
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<ir::Var> loop_var_;
common::SymbolicExprAnalyzer analyzer_;
};
void IterMapSimplify(std::vector<Expr>& indices, // NOLINT
const std::vector<cinn::ir::Var>& input_iters,
const SymbolicExprAnalyzer& analyzer);
} // namespace common
} // namespace cinn