Files
paddlepaddle--paddle/paddle/cinn/common/iter_simplify.cc
T
2026-07-13 12:40:42 +08:00

710 lines
23 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.
#include "paddle/cinn/common/iter_simplify.h"
#include "paddle/cinn/common/const_fold.h"
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/ir/ir_mutator.h"
#include "paddle/cinn/ir/ir_visitor.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/utils/ir_compare.h"
#include "paddle/cinn/ir/utils/ir_copy.h"
#include "paddle/cinn/optim/simplify_util.h"
#include "paddle/common/enforce.h"
#include "paddle/common/errors.h"
namespace cinn {
namespace common {
using cinn::optim::ProveDivisible;
/*! \brief Override VisitExpr for iter expr type processing */
void IterMapToExprNormalizer::Visit(const Expr* expr, Expr* op) {
if (auto op_ = op->As<ir::IterSplit>()) {
*op = ConvertIterSplit(op_);
} else if (auto op_ = op->As<ir::IterSum>()) {
*op = ConvertIterSum(op_);
} else {
IRMutator::Visit(expr, op);
}
}
ir::IndexExpr IterMapToExprNormalizer::ConvertIterSum(ir::IterSum* expr) {
ir::IndexExpr res(0);
for (auto&& arg : expr->args) {
auto split = arg.As<ir::IterSplit>();
res = res + ConvertIterSplit(split);
}
res = res + expr->base;
return res;
}
ir::IndexExpr IterMapToExprNormalizer::ConvertIterSplit(ir::IterSplit* expr) {
ir::IndexExpr source;
ir::IterMark* mark = expr->source.As<ir::IterMark>();
if (auto opt = mark->source.As<ir::_Var_>()) {
if (IsOne(mark->extent)) return ir::IndexExpr(0);
source = ir::IndexExpr(opt);
} else if (auto opt = mark->source.As<ir::IterSum>()) {
source = ConvertIterSum(opt);
} else {
VLOG(4) << "unsupported iter expr type";
Visit(&(mark->source), &(mark->source));
source = mark->source;
}
// quick branch
if (IsZero(expr->scale) || IsOne(expr->extent))
return ir::Zero(expr->extent.type());
if (analyzer_.ProveEQ(expr->extent, mark->extent).value_or(false) &&
IsOne(expr->lower_factor)) {
return source * expr->scale;
} else if (analyzer_.ProveLE(mark->extent, expr->lower_factor * expr->extent)
.value_or(false)) {
if (IsOne(expr->extent)) {
return ir::Zero(expr->extent.type());
}
return source / expr->lower_factor * expr->scale;
} else {
return (source % (expr->lower_factor * expr->extent)) / expr->lower_factor *
expr->scale;
}
}
void IterMapRewriter::Visit(const ir::_Var_* op, Expr* expr) {
auto it = var_map_.find(op->name);
if (it != var_map_.end()) *expr = it->second;
}
void IterMapRewriter::Visit(const ir::Add* op, Expr* expr) {
auto a = op->a();
auto b = op->b();
Visit(&a);
Visit(&b);
if (auto const_res = cinn::common::TryConstFold<ir::Add>(a, b)) {
*expr = const_res.value();
return;
}
if (!IsIterExpr(a, b)) {
return;
}
Expr ret = ir::ir_utils::IRCopy(ToIterSum(a));
ir::IterSum* ret_sum = ret.As<ir::IterSum>();
if (auto b_sum = b.As<ir::IterSum>()) {
AddToLhs(ret_sum, *b_sum, 1);
} else if (auto b_split = b.As<ir::IterSplit>()) {
AddToLhs(ret_sum, *b_split, 1);
} else {
ret_sum->base = ret_sum->base + b.as_index();
}
*expr = ret;
}
void IterMapRewriter::Visit(const ir::Sub* op, Expr* expr) {
auto a = op->a();
auto b = op->b();
Visit(&a);
Visit(&b);
if (auto const_res = cinn::common::TryConstFold<ir::Sub>(a, b)) {
*expr = const_res.value();
return;
}
if (!IsIterExpr(a, b)) return;
Expr ret = ir::ir_utils::IRCopy(ToIterSum(a));
ir::IterSum* ret_sum = ret.As<ir::IterSum>();
if (auto b_sum = b.As<ir::IterSum>()) {
AddToLhs(ret_sum, *b_sum, -1);
} else if (auto* b_split = b.As<ir::IterSplit>()) {
AddToLhs(ret_sum, *b_split, -1);
} else {
ret_sum->base = ret_sum->base - b.as_index();
}
*expr = ret;
}
void IterMapRewriter::Visit(const ir::Mul* op, Expr* expr) {
auto a = op->a();
auto b = op->b();
Visit(&a);
Visit(&b);
if (auto const_res = cinn::common::TryConstFold<ir::Mul>(a, b)) {
*expr = const_res.value();
return;
}
if (!IsIterExpr(a, b)) return;
if ((a.As<ir::IterSum>() || a.As<ir::IterSplit>()) &&
(b.As<ir::IterSum>() || b.As<ir::IterSplit>())) {
PADDLE_THROW(::common::errors::InvalidArgument(
"Product of iter and iter is not supported"));
return;
}
if (!a.As<ir::IterSum>() && !a.As<ir::IterSplit>()) {
std::swap(a, b);
}
auto ret = ir::ir_utils::IRCopy(a);
if (auto a_sum = ret.As<ir::IterSum>()) {
MulToLhs(a_sum, b);
} else if (auto a_split = ret.As<ir::IterSplit>()) {
a_split->scale = a_split->scale * b.as_index();
}
*expr = ret;
}
void IterMapRewriter::Visit(const ir::Div* op, Expr* expr) {
auto a = op->a();
auto b = op->b();
Visit(&a);
Visit(&b);
if (auto const_res = cinn::common::TryConstFold<ir::Div>(a, b)) {
*expr = const_res.value();
return;
}
if (!IsIterExpr(a, b)) return;
if ((b.As<ir::IterSum>() || b.As<ir::IterSplit>())) {
PADDLE_THROW(::common::errors::InvalidArgument(
"Division of iter and iter is not supported"));
return;
}
auto ret = ir::ir_utils::IRCopy(a);
auto preprocessed = PreprocessDividend(ret);
auto preprocessed_sum = preprocessed.As<ir::IterSum>();
ret = SplitDivConst(preprocessed_sum->args[0], preprocessed_sum->base, b);
*expr = ret;
}
void IterMapRewriter::Visit(const ir::Mod* op, Expr* expr) {
auto a = op->a();
auto b = op->b();
Visit(&a);
Visit(&b);
if (auto const_res = cinn::common::TryConstFold<ir::Mod>(a, b)) {
*expr = const_res.value();
return;
}
if (!IsIterExpr(a, b)) return;
if ((b.As<ir::IterSum>() || b.As<ir::IterSplit>())) {
PADDLE_THROW(::common::errors::InvalidArgument(
"Mod of iter and iter is not supported"));
return;
}
auto ret = ir::ir_utils::IRCopy(a);
auto preprocessed = PreprocessDividend(ret);
auto preprocessed_sum = preprocessed.As<ir::IterSum>();
ret = SplitModConst(preprocessed_sum->args[0], preprocessed_sum->base, b);
*expr = ret;
}
Expr IterMapRewriter::PreprocessDividend(const Expr& dividend) {
if (dividend.As<ir::IterSplit>()) {
return ir::IterSum::Make({dividend}, ir::Zero(dividend.type()));
} else if (auto sum = dividend.As<ir::IterSum>()) {
if (sum->args.size() == 1) {
return dividend;
}
auto opt_fused = TryFuse(dividend);
if (!opt_fused) {
PADDLE_THROW(::common::errors::InvalidArgument(
"Dividend can't be written as a single fused IterSum"));
return Expr();
}
return opt_fused.value();
} else {
PADDLE_THROW(
::common::errors::InvalidArgument("Expect dividend is IterExpr."));
return Expr();
}
}
Expr IterMapRewriter::SplitDivConst(Expr lhs_expr,
ir::IndexExpr base,
ir::IndexExpr rhs) {
// (lhs_expr + base) // rhs
if (IsOne(rhs)) {
if (IsZero(base)) return lhs_expr;
return ir::IterSum::Make({lhs_expr}, base);
}
auto lhs = lhs_expr.As<ir::IterSplit>();
if (!IsOne(lhs->scale)) {
if (ProveDivisible(lhs->scale, rhs) && IsZero(base)) {
lhs->scale = lhs->scale / rhs;
return lhs;
} else if (ProveDivisible(lhs->scale, rhs) && ProveDivisible(base, rhs)) {
lhs->scale = lhs->scale / rhs;
return ir::IterSum::Make({lhs}, base / rhs);
} else if (ProveDivisible(rhs, lhs->scale) && IsZero(base)) {
rhs = rhs / lhs->scale;
lhs->scale = ir::One(rhs.type());
} else if (ProveDivisible(rhs, lhs->scale) &&
ProveDivisible(base, lhs->scale)) {
base = base / lhs->scale;
rhs = rhs / lhs->scale;
lhs->scale = ir::One(rhs.type());
} else {
PADDLE_THROW(::common::errors::InvalidArgument(
"IterExpr scale must be divisible by rhs"));
return Expr();
}
}
// TODO(liuruyan): Padding dividend to divisor later. assuming dividend canbe
// divided by divisor now.
Expr new_split;
if (!ProveDivisible(base, rhs)) {
// padding base to divisor later. Treat the whole expr as IterMark now.
return ir::IterSum::Make(
{ir::IterSplit::Make(
ir::IterMark::Make(ir::IterSum::Make({Expr(lhs)}, base),
lhs->extent + base),
rhs,
(lhs->extent + base + rhs - 1) / rhs,
ir::One(rhs.type()))},
ir::Zero(rhs.type()));
}
if (ProveDivisible(lhs->extent, rhs)) {
new_split = ir::IterSplit::Make(
lhs->source, lhs->lower_factor * rhs, lhs->extent / rhs, lhs->scale);
} else if (IsOne(lhs->lower_factor) &&
analyzer_
.ProveEQ(lhs->extent, lhs->source.As<ir::IterMark>()->extent)
.value_or(false)) {
new_split = ir::IterSplit::Make(
lhs->source, rhs, (lhs->extent + rhs - 1) / rhs, lhs->scale);
} else {
new_split = ir::IterSplit::Make(ir::IterMark::Make(lhs, lhs->extent),
rhs,
(lhs->extent + rhs - 1) / rhs,
ir::One(rhs.type()));
}
return IsZero(base / rhs) ? new_split
: ir::IterSum::Make({new_split}, base / rhs);
}
Expr IterMapRewriter::SplitModConst(ir::Expr lhs_expr,
ir::IndexExpr base,
ir::IndexExpr rhs) {
// (lhs_expr + base) % rhs
if (IsOne(rhs)) {
return ir::Zero(lhs_expr.type());
}
auto lhs = lhs_expr.As<ir::IterSplit>();
if (!IsOne(lhs->scale)) {
if (ProveDivisible(lhs->scale, rhs) && IsZero(base)) {
return ir::Zero(lhs_expr.type());
} else if (ProveDivisible(lhs->scale, rhs) && ProveDivisible(base, rhs)) {
return ir::Zero(lhs_expr.type());
} else if (ProveDivisible(rhs, lhs->scale) && IsZero(base)) {
rhs = rhs / lhs->scale;
} else if (ProveDivisible(rhs, lhs->scale) &&
ProveDivisible(base, lhs->scale)) {
base = base / lhs->scale;
rhs = rhs / lhs->scale;
} else {
PADDLE_THROW(::common::errors::InvalidArgument(
"IterExpr scale must be divisible by rhs"));
return Expr();
}
}
if (!ProveDivisible(base, rhs)) {
auto lhs_s1 = ir::IterSplit::Make(
lhs->source, lhs->lower_factor, lhs->extent, ir::One(lhs_expr.type()));
// padding base to divisor later. Treat the whole expr as IterMark now.
return ir::IterSplit::Make(
ir::IterMark::Make(ir::IterSum::Make({lhs_s1}, base),
lhs->extent + base),
ir::One(rhs.type()),
rhs,
lhs->scale);
}
// TODO(liuruyan): Padding dividend to divisor later. assuming dividend canbe
// divided by divisor now.
return ir::IterSplit::Make(lhs->source, lhs->lower_factor, rhs, lhs->scale);
}
int32_t IterMapRewriter::FindFirstPossibleUnitExtentIndex(
const ir::IterSum& expr) {
for (int32_t i = 0; i < expr.args.size(); ++i) {
if (IsOne(expr.args[i].As<ir::IterSplit>()->extent)) return i;
}
return static_cast<int32_t>(expr.args.size());
}
int32_t IterMapRewriter::FindSplitWithExactScale(
const ir::IterSum& expr,
const std::vector<bool>& skip_flag,
const ir::IndexExpr& expected_scale,
const Expr& match_source,
int32_t rbegin,
int32_t first_possible_unit_extent_pos) {
if (rbegin == -1) {
rbegin = static_cast<int32_t>(expr.args.size()) - 1;
}
int32_t matched_pos = -1;
// Use reverse search, as smallest scale usually are near the end.
for (int32_t j = rbegin; j >= 0; --j) {
if (skip_flag[j]) continue;
auto split = expr.args[j].As<ir::IterSplit>();
if (match_source.defined() && match_source != split->source) continue;
const ir::IndexExpr& cur_scale = split->scale;
if (analyzer_.ProveEQ(cur_scale, expected_scale).value_or(false)) {
if (IsOne(split->extent)) return j;
// We prefer the unit extent Iter. just search when extent != 1.
if (matched_pos == -1) {
matched_pos = j;
}
// There is no unit extent in front of first_possible_unit_extent_pos,
// so just return.
if (j <= first_possible_unit_extent_pos) return matched_pos;
}
}
return matched_pos;
}
int32_t IterMapRewriter::FindBaseSplit(const ir::IterSum& expr,
const std::vector<bool>& skip_flag,
const Expr& match_source,
int32_t rbegin) {
if (rbegin == -1) {
rbegin = static_cast<int>(expr.args.size()) - 1;
}
int32_t base_index = -1;
int64_t min_const_scale = 0;
// Compare the const scale size, use reverse search, as smallest scale usually
// are near the end.
for (int32_t i = rbegin; i >= 0; --i) {
if (skip_flag[i]) continue;
auto split = expr.args[i].As<ir::IterSplit>();
if (match_source.defined() && match_source != split->source) continue;
if (const auto* op = split->scale.As<ir::IntImm>()) {
if (base_index == -1 || op->value < min_const_scale) {
min_const_scale = op->value;
base_index = i;
} else if (op->value == min_const_scale) {
if (IsOne(split->extent) &&
!IsOne(expr.args[base_index].As<ir::IterSplit>()->extent)) {
base_index = i;
}
}
}
}
// Found! return the base index.
if (base_index != -1) return base_index;
// If not found const scale, compare the symbol length in scale.
int32_t min_reduce_size = 0;
for (int32_t i = rbegin; i >= 0; --i) {
if (skip_flag[i]) continue;
auto split = expr.args[i].As<ir::IterSplit>();
if (match_source.defined() && match_source != split->source) continue;
int32_t reduce_size = 0;
auto fcollect = [&](const ir::IndexExpr&) { ++reduce_size; };
optim::UnpackReduction<ir::Mul>(split->scale, fcollect);
if (base_index == -1 || reduce_size < min_reduce_size) {
min_reduce_size = reduce_size;
base_index = i;
}
}
return base_index;
}
std::optional<Expr> IterMapRewriter::TryFuse(const Expr& expr) {
auto iter_sum = expr.As<ir::IterSum>();
if (!iter_sum) return std::nullopt;
if (iter_sum->args.size() <= 1) return std::nullopt;
// Fuse Iter with same source. e.g. i_j_fused / 4 * 4 + i_j_fused % 4
if (auto opt = TryFuseSameSource(expr)) {
auto sum = opt.value().As<ir::IterSum>();
if (sum->args.size() <= 1) {
return opt.value();
}
}
// Select iter with smallest scale as base iter.
std::vector<bool> visited(iter_sum->args.size(), false);
int base_index = FindBaseSplit(*iter_sum, visited, Expr(), -1);
if (base_index == -1) return std::nullopt;
ir::IndexExpr base_scale =
iter_sum->args[base_index].As<ir::IterSplit>()->scale;
std::vector<Expr> grouped_iters;
ir::IndexExpr expected_scale = base_scale;
int first_possible_unit_extent_pos =
FindFirstPossibleUnitExtentIndex(*iter_sum);
// Find iter with same scale as expected_scale and update expected_scale.
// e.g. i * 32 + j * 8 + k * 1, Extent(i, j, k) = 2, 4, 8.
// first base_index = 2, expected_scale = 1. means select k as base iter.
// then matched_pos = 2, expected_scale = 8 * 1 = 8. means match k.
// then matched_pos = 1, expected_scale = 8 * 4 = 32. means match j.
// finally matched_pos = 0, expected_scale = 32 * 2 = 64. means match i.
// if match failed, indicates that expr is illegal and cannot be merged.
for (size_t i = 0; i < iter_sum->args.size(); ++i) {
ir::IndexExpr matched_scale;
int matched_pos =
i == 0 ? base_index
: FindSplitWithExactScale(*iter_sum,
visited,
expected_scale,
Expr(),
-1,
first_possible_unit_extent_pos);
// If not found iter with expected scale, search above case:
// D(i)=2, D(j)=8, Split loop from (j, 0, 8) to (-1, 32)
// (i * 8 + j) % 16 ==> (i * 8 + j0 * 32 + j1)
if (matched_pos == -1) {
matched_pos = FindBaseSplit(*iter_sum, visited, Expr(), -1);
// // If not found iter with expected scale again, return nullopt.
if (matched_pos == -1) return std::nullopt;
}
matched_scale = expected_scale;
visited[matched_pos] = true;
auto arg_copy = ir::ir_utils::IRCopy(iter_sum->args[matched_pos]);
auto arg = arg_copy.As<ir::IterSplit>();
arg->scale = arg->scale / base_scale;
grouped_iters.push_back(arg_copy);
// Update expected_scale = matched_split->scale * matched_split->extent
expected_scale =
iter_sum->args[matched_pos].As<ir::IterSplit>()->extent * matched_scale;
}
std::reverse(grouped_iters.begin(), grouped_iters.end());
Expr grouped_sum =
ir::IterSum::Make(grouped_iters, ir::Zero(iter_sum->type()));
// If the iter is already fused, return it directly.
auto it = sum_fuse_map_.find(grouped_sum);
if (it != sum_fuse_map_.end()) {
return ir::IterSum::Make({ir::IterSplit::Make(it->second, base_scale)},
iter_sum->base);
} else {
// new iter, form a new mark
auto mark = ir::IterMark::Make(grouped_sum, expected_scale / base_scale);
sum_fuse_map_[grouped_sum] = mark;
return ir::IterSum::Make({ir::IterSplit::Make(mark, base_scale)},
iter_sum->base);
}
}
std::optional<Expr> IterMapRewriter::TryFuseSameSource(const Expr& expr) {
auto iter_sum = expr.As<ir::IterSum>();
if (!iter_sum) return std::nullopt;
if (iter_sum->args.size() <= 1) return std::nullopt;
// Only for IterMark
std::unordered_map<Expr, int32_t> hit_count;
bool has_overlap = false;
// Check if the iterators have overlap, just return nullopt if not.
for (auto&& split : iter_sum->args) {
auto mark = split.As<ir::IterSplit>()->source;
auto it = hit_count.find(mark);
if (it != hit_count.end()) {
++it->second;
has_overlap = true;
} else {
hit_count[mark] = 1;
}
}
if (!has_overlap) return std::nullopt;
std::vector<bool> visited(iter_sum->args.size(), false);
// Only for IterSplit
std::vector<Expr> reverse_flattened_iters;
int first_possible_unit_extent_pos =
FindFirstPossibleUnitExtentIndex(*iter_sum);
// Start eliminating the iterators
for (int rend = static_cast<int32_t>(iter_sum->args.size()) - 1; rend >= 0;) {
auto split = iter_sum->args[rend].As<ir::IterSplit>();
if (visited[rend]) {
--rend;
continue;
}
if (hit_count.at(split->source) == 1) {
reverse_flattened_iters.push_back(iter_sum->args[rend]);
visited[rend] = true;
--rend;
continue;
}
int matched_index = FindBaseSplit(*iter_sum, visited, split->source, rend);
visited[matched_index] = true;
auto split_copy = ir::ir_utils::IRCopy(iter_sum->args[matched_index]);
auto rhs_iter = split_copy.As<ir::IterSplit>();
// Eliminate the lhs iterators when meets the following conditions:
// 1. The lhs has the same source as the rhs.
// 2. lhs->scale == rhs->extent * rhs->scale.
// 3. lhs->lower_factor == rhs->lower_factor * rhs->extent.
while (true) {
ir::IndexExpr lhs_scale = rhs_iter->extent * rhs_iter->scale;
matched_index = FindSplitWithExactScale(*iter_sum,
visited,
lhs_scale,
rhs_iter->source,
rend,
first_possible_unit_extent_pos);
if (matched_index == -1) break;
auto lhs_iter = iter_sum->args[matched_index].As<ir::IterSplit>();
ir::IndexExpr lhs_lower_factor =
rhs_iter->lower_factor * rhs_iter->extent;
if (!analyzer_.ProveEQ(lhs_iter->lower_factor, lhs_lower_factor)
.value_or(false))
break;
visited[matched_index] = true;
rhs_iter->extent = lhs_iter->extent * rhs_iter->extent;
}
reverse_flattened_iters.push_back(split_copy);
}
std::reverse(reverse_flattened_iters.begin(), reverse_flattened_iters.end());
auto simplified_sum =
ir::IterSum::Make(reverse_flattened_iters, iter_sum->base);
return simplified_sum;
}
Expr IterMapRewriter::ToIterSum(const Expr& expr) {
if (expr.As<ir::IterSum>()) {
return expr;
} else if (auto split = expr.As<ir::IterSplit>()) {
auto split_expr = ir::IterSplit::Make(
split->source, split->lower_factor, split->extent, split->scale);
return ir::IterSum::Make({split_expr}, ir::Zero(expr.type()));
} else {
return ir::IterSum::Make({}, expr);
}
}
void IterMapRewriter::AddToLhs(ir::IterSum* lhs,
const ir::IterSplit& rhs,
int sign) {
auto rhs_expr = ir::ir_utils::IRCopy(Expr(&Reference(&rhs)));
for (auto&& lvalue : lhs->args) {
if (lvalue == rhs_expr) {
auto lsplit = lvalue.As<ir::IterSplit>();
if (sign > 0) {
lsplit->scale = lsplit->scale + rhs.scale;
} else {
lsplit->scale = lsplit->scale - rhs.scale;
}
return;
}
}
if (sign > 0) {
lhs->args.push_back(rhs_expr);
} else {
rhs_expr.As<ir::IterSplit>()->scale = -rhs.scale;
lhs->args.push_back(rhs_expr);
}
}
void IterMapRewriter::AddToLhs(ir::IterSum* lhs,
const ir::IterSum& rhs,
int sign) {
for (auto&& arg : rhs.args) {
auto rhs = arg.As<ir::IterSplit>();
AddToLhs(lhs, *rhs, sign);
}
if (sign > 0) {
lhs->base = lhs->base + rhs.base;
} else {
lhs->base = lhs->base - rhs.base;
}
}
void IterMapRewriter::MulToLhs(ir::IterSum* lhs, const ir::IndexExpr& rhs) {
for (auto&& lvalue : lhs->args) {
auto lsplit = lvalue.As<ir::IterSplit>();
lsplit->scale = lsplit->scale * rhs;
}
lhs->base = lhs->base * rhs;
}
void IterMapSimplify(std::vector<Expr>& indices, // NOLINT
const std::vector<cinn::ir::Var>& input_iters,
const SymbolicExprAnalyzer& analyzer) {
IterMapRewriter rewriter(input_iters, analyzer);
IterMapToExprNormalizer converter(analyzer);
for (auto& value : indices) {
VLOG(5) << "before rewrite: " << value;
rewriter.Rewrite(&value);
VLOG(5) << "after rewrite: " << value;
converter.Convert(&value);
VLOG(5) << "after convert: " << value;
}
}
void SimplifyBlockBinding::Visit(const ir::For* op, Expr* expr) {
auto for_op = expr->As<ir::For>();
loop_var_.emplace_back(op->loop_var);
IRMutator::Visit(for_op, expr);
loop_var_.pop_back();
}
void SimplifyBlockBinding::Visit(const ir::ScheduleBlockRealize* op,
Expr* expr) {
auto sch_block_rlz_op = expr->As<ir::ScheduleBlockRealize>();
if (sch_block_rlz_op->iter_values.empty()) return;
IterMapSimplify(sch_block_rlz_op->iter_values, loop_var_, analyzer_);
}
} // namespace common
} // namespace cinn