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

124 lines
3.8 KiB
C++

// Copyright (c) 2021 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/optim/unroll_loops.h"
#include <utility>
#include <vector>
#include "paddle/cinn/ir/ir_mutator.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/utils/ir_copy.h"
#include "paddle/cinn/ir/utils/ir_replace.h"
namespace cinn {
namespace optim {
namespace {
struct UnrollMutator : public ir::IRMutator<Expr*> {
void operator()(Expr* expr) { ir::IRMutator<>::Visit(expr, expr); }
private:
// update auto_max_step_ from the specific attribute of ScheduleBlock
void Visit(const ir::ScheduleBlock* op, Expr* expr) override {
auto attr_it = op->attrs.find(ir::attr::auto_unroll_max_step);
if (attr_it != op->attrs.end()) {
const int* attr_v = std::get_if<int>(&attr_it->second);
if (attr_v) {
int value = *attr_v;
std::swap(auto_max_step_, value);
VLOG(5) << "auto_max_step is updated:" << auto_max_step_;
ir::IRMutator<>::Visit(op, expr);
std::swap(auto_max_step_, value);
return;
} else {
LOG(WARNING) << "Get invalid value of attr:"
<< ir::attr::auto_unroll_max_step;
}
}
ir::IRMutator<>::Visit(op, expr);
}
// count a Store node as plain statement
void Visit(const ir::Store* op, Expr* expr) override {
IRMutator<>::Visit(op, expr);
++flat_step_;
}
// predicate whether a for-loop can be unrolled and do it
void Visit(const ir::For* op, Expr* expr) override {
IRMutator<>::Visit(op, expr);
if (op->extent.As<ir::IntImm>() == nullptr) {
VLOG(5) << "loop to be unrolled should have a constant extent";
return;
}
int64_t extent = op->extent.as_int64();
// predicate this for-loop can be unrolled by auto-unroll conditions
bool unrollable =
(op->is_serial() && extent >= 0 && not_unrolled_depth_ == 0 &&
extent * flat_step_ <= auto_max_step_);
// predicate this for-loop can be unrolled by the unrolled tag
unrollable =
(unrollable || op->is_unrolled()) && extent <= max_unroll_extent_;
if (unrollable) {
Unroll(op, expr);
flat_step_ *= extent;
} else {
++not_unrolled_depth_;
}
}
//! Unroll a forloop.
void Unroll(const ir::For* op, Expr* expr) {
std::vector<Expr> body;
auto* min = op->min.As<ir::IntImm>();
auto* extent = op->extent.As<ir::IntImm>();
if (!(min && extent)) return;
for (int i = min->value; i < extent->value; i++) {
Expr start = op->min + i;
body.push_back(
ir::ir_utils::IRCopy(op->body, /* copy_buffer_node = */ false));
cinn::ir::ir_utils::IrReplaceVarBroadcast(
&body.back(), op->loop_var, start);
}
*expr = ir::Block::Make(body);
}
private:
// max permitted steps to be automatically unrolled in total
int auto_max_step_ = 0;
// max permitted extent of a loop to be unrolled
int max_unroll_extent_ = 50;
// the number of steps that have been unrolled or plain statement
int64_t flat_step_ = 0;
// the number of nested loops not to be unrolled
int not_unrolled_depth_ = 0;
};
} // namespace
void UnrollLoop(Expr* expr) { UnrollMutator()(expr); }
} // namespace optim
} // namespace cinn