Files
paddlepaddle--paddle/paddle/cinn/ir/schedule_block_graph.h
T
2026-07-13 12:40:42 +08:00

201 lines
6.0 KiB
C++

// 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 <list>
#include <stack>
#include "paddle/cinn/common/graph_utils.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_mutator.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
namespace cinn {
namespace ir {
// Node in units of ScheduleBlock.
class ScheduleBlockNode : public cinn::common::GraphNode {
public:
ScheduleBlockNode(Expr block, const IRSchedule& ir_sch);
// Get the id of this node, which is same as the name of ScheduleBlock.
std::string id() const { return id_; }
// Get the ScheduleBlockRealize expr
Expr Block() const;
// Get all control stmts containing the schedule_block, now only the For node
// is being considered.
std::vector<Expr> GetLoops() const;
// Get all the upstream nodes that this node depends on.
std::unordered_set<std::string> UpstreamNodes() const {
return upstream_nodes_;
}
// Get all downstream nodes that depend on this node.
std::unordered_set<std::string> DownstreamNodes() const {
return downstream_nodes_;
}
// Get the producer node that this node directly depends on
std::vector<ScheduleBlockNode*> Producers() const;
// Get consumer nodes that directly depend on this node.
std::vector<ScheduleBlockNode*> Consumers() const;
void AddUpstreamNode(const std::string& node_id) {
upstream_nodes_.insert(node_id);
}
void AddDownstreamNode(const std::string& node_id) {
downstream_nodes_.insert(node_id);
}
private:
std::vector<cinn::common::Shared<cinn::common::GraphEdge>> OrderedInLinks()
const;
std::vector<cinn::common::Shared<cinn::common::GraphEdge>> OrderedOutLinks()
const;
private:
std::string id_;
std::unordered_set<std::string> upstream_nodes_;
std::unordered_set<std::string> downstream_nodes_;
const IRSchedule& ir_sch_;
};
// Graph in units of ScheduleBlockNode, each node corresponds to a ScheduleBlock
// in IR.
class ScheduleBlockGraph : public cinn::common::Graph {
public:
explicit ScheduleBlockGraph(const IRSchedule& ir_sch);
// Update graph information according to the new IRSchedule.
void Update(const IRSchedule& ir_sch);
// Retrieve a node in the graph by id, the id is same as the name of
// ScheduleBlock.
ScheduleBlockNode* RetrieveNode(const std::string& id) {
return dynamic_cast<ScheduleBlockNode*>(
cinn::common::Graph::RetrieveNode(id));
}
// Get all block name in order,
// this sequence may become invalid after some schedule operations,
// and an Update() operation is required.
std::list<std::string> BlockIdsInOrder() const { return block_ids_in_order_; }
// Get all nodes without input node.
std::vector<ScheduleBlockNode*> StartPoints();
// Get all nodes without output node.
std::vector<ScheduleBlockNode*> EndPoints();
// Function used to define the operations to be performed on each node.
using NodeHandlerType = std::function<void(ScheduleBlockNode*)>;
// Walk through each node
// and perform some operations defined by NodeHandler on it.
void NodesWalk(const NodeHandlerType& NodeHandler);
// Walk through each node topological dfs topo order
// and perform some operations defined by NodeHandler on it.
void DFSTopoWalk(const NodeHandlerType& NodeHandler, bool is_reverse = true);
private:
std::list<std::string> block_ids_in_order_;
};
/**
* The mutator used to construct the order of blocks and their control
* statements
*
* Example:
* for0:
* for1:
* block0
* block1
* block2
* for2:
* block3
* block4
*
* the result is:
* [0]: for0
* [0, 0]: for1
* [0, 0, 0]: block0
* [0, 0, 1]: block1
* [0, 1]: block2
* [0, 2]: for2
* [0, 2, 0]: block3
* [0, 2, 1]: block4
*/
struct BlockOrderConstructor : public IRMutator<Expr*> {
std::map<std::vector<int>, Expr> operator()(ir::Expr* expr) {
IRMutator::Visit(expr, expr);
return block_order_with_ctrl_structure_;
}
private:
void Visit(const For* x, Expr* op) {
if (global_idx_.empty() ||
block_order_with_ctrl_structure_.rbegin()->first.size() ==
global_idx_.size()) {
cur_idx_ = -1;
}
global_idx_.push_back(++cur_idx_);
block_order_with_ctrl_structure_.insert(std::make_pair(global_idx_, *op));
IRMutator<Expr*>::Visit(x, op);
cur_idx_ = global_idx_.back();
global_idx_.pop_back();
}
void Visit(const ScheduleBlockRealize* x, Expr* op) {
if (global_idx_.empty() ||
block_order_with_ctrl_structure_.rbegin()->first.size() ==
global_idx_.size()) {
cur_idx_ = -1;
}
global_idx_.push_back(++cur_idx_);
block_order_with_ctrl_structure_.insert(std::make_pair(global_idx_, *op));
if (x->schedule_block.As<ScheduleBlock>()->name.substr(0, 4) == "root") {
IRMutator<Expr*>::Visit(x, op);
}
global_idx_.pop_back();
}
void Visit(const IfThenElse* x, Expr* op) {
if (global_idx_.empty() ||
block_order_with_ctrl_structure_.rbegin()->first.size() ==
global_idx_.size()) {
cur_idx_ = -1;
}
global_idx_.push_back(++cur_idx_);
block_order_with_ctrl_structure_.insert(std::make_pair(global_idx_, *op));
IRMutator<Expr*>::Visit(x, op);
cur_idx_ = global_idx_.back();
global_idx_.pop_back();
}
private:
int cur_idx_;
std::vector<int> global_idx_;
std::map<std::vector<int>, Expr> block_order_with_ctrl_structure_;
};
} // namespace ir
} // namespace cinn