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

322 lines
10 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/lang/lower_impl.h"
#include <algorithm>
#include <queue>
#include <string>
#include <unordered_set>
#include "paddle/cinn/common/common.h"
#include "paddle/cinn/common/context.h"
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/ir/ir_base.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/tensor.h"
#include "paddle/cinn/optim/ir_simplify.h"
#include "paddle/cinn/optim/replace_var_with_expr.h"
#include "paddle/cinn/optim/transform_polyfor_to_for.h"
#include "paddle/common/enforce.h"
PD_DECLARE_bool(cinn_runtime_display_debug_info);
namespace cinn {
namespace lang {
namespace detail {
void CheckNoIslCallRemains(Expr* expr) {
auto isl_calls = ir::ir_utils::CollectIRNodes(*expr, [](const Expr* expr) {
return expr->As<ir::Call>() && expr->As<ir::Call>()->is_isl_call();
});
#ifdef CINN_DEBUG
for (auto& item : isl_calls) {
LOG(ERROR) << "ISL call: " << item;
}
#endif
if (!isl_calls.empty()) {
LOG(WARNING) << "Some ISL call nodes remained, get " << isl_calls.size()
<< " isl_calls, the first one is " << *isl_calls.begin();
}
}
const char* CompuGraphNode::__type_info__ = "ComputeGraphNode";
const char* CompuGraphNode::type_info() const { return __type_info__; }
std::string CompuGraphNode::id() const {
PADDLE_ENFORCE_EQ(
tensor.defined(),
true,
::common::errors::InvalidArgument("Tensor is not defined. Please ensure "
"tensor is properly initialized."));
return tensor->name;
}
void LowerImpl::CheckArgsUnique() {
for (auto& tensor : tensor_args_) {
if (!tensor->buffer.defined()) {
LOG(ERROR) << "tensor [" << tensor->name << "] buffer is null";
continue;
}
}
}
std::vector<ir::Argument> LowerImpl::GenerateFunctionArgumentList(
Expr fn_body) {
CheckArgsUnique();
std::vector<ir::Argument> args;
auto teller = ir::ir_utils::CollectTensorNeedsWrite(&fn_body);
std::set<std::string> arg_names;
for (auto& scalar : scalar_args_) {
PADDLE_ENFORCE_EQ(
arg_names.count(scalar->name),
0,
::common::errors::InvalidArgument(
"Argument name '%s' already exists in the argument names set.",
scalar->name));
auto* scalar_node = scalar.As<ir::_Var_>();
PADDLE_ENFORCE_EQ(
scalar_node->type().valid(),
true,
::common::errors::InvalidArgument(
"The type of scalar node '%s' is not valid.", scalar->name));
arg_names.insert(scalar->name);
args.emplace_back(scalar, ir::Argument::IO::kInput);
}
for (auto& tensor : tensor_args_) {
auto* tensor_node = tensor.As<ir::_Tensor_>();
bool is_output = teller.count(tensor->name);
VLOG(1) << "tensor argument " << tensor->name << " buffer "
<< tensor->buffer->name;
// avoid duplicate
if (!tensor_node->buffer.defined()) continue;
// if a argument is already marked as kInput, mark it as kOutput and move it
// to the back.
if (arg_names.count(tensor_node->buffer->name)) {
auto it =
std::find_if(args.begin(), args.end(), [&](const ir::Argument& x) {
return x.name() == tensor_node->buffer->name;
});
PADDLE_ENFORCE_EQ(
it != args.end(),
true,
::common::errors::InvalidArgument(
"Argument with name '%s' not found in the argument list.",
tensor_node->buffer->name));
if (it->is_input()) {
args.erase(it);
} else if (it->is_output()) {
continue;
}
}
arg_names.insert(tensor_node->buffer->name);
auto io = is_output ? ir::Argument::IO::kOutput : ir::Argument::IO::kInput;
VLOG(3) << "Collect " << (is_output ? "W" : "R") << " argument "
<< tensor->buffer->name;
args.emplace_back(tensor_node->buffer, io);
}
return args;
}
// Generate Function Arguments for split kernel.
std::vector<ir::Argument> LowerImpl::GenFuncArgForSplitKernel(
Expr func_iterator, std::vector<ir::Tensor> temp_tensors) {
CheckArgsUnique();
std::vector<ir::Argument> in_args;
std::vector<ir::Argument> out_args;
auto teller = ir::ir_utils::CollectTensorNeedsWrite(&func_iterator);
std::set<std::string> arg_names;
std::set<std::string> all_tensor_names;
for (auto& scalar : scalar_args_) {
PADDLE_ENFORCE_EQ(
arg_names.count(scalar->name),
0,
::common::errors::InvalidArgument(
"Argument name '%s' already exists in the argument names set.",
scalar->name));
auto* scalar_node = scalar.As<ir::_Var_>();
PADDLE_ENFORCE_EQ(
scalar_node->type().valid(),
true,
::common::errors::InvalidArgument(
"The type of scalar node '%s' is not valid.", scalar->name));
arg_names.insert(scalar->name);
in_args.emplace_back(scalar, ir::Argument::IO::kInput);
}
auto all_tensors = ir::ir_utils::CollectIRNodes(
func_iterator, [&](const Expr* x) { return x->as_tensor(); });
auto all_vars = ir::ir_utils::CollectIRNodes(
func_iterator, [&](const Expr* x) { return x->as_var(); });
for (auto& i : all_tensors) {
auto* tensor = i.as_tensor();
all_tensor_names.insert(tensor->name);
VLOG(3) << "In all_tensors, it has : " << tensor->name;
}
for (auto& i : all_vars) {
auto* var = i.as_var();
VLOG(3) << "In all_vars, it has : " << var->name;
}
for (auto& i : scalar_args_) {
VLOG(3) << "In scalar_args_, var has : " << i->name;
}
std::set<std::string> temp_tensor_names;
for (auto& i : temp_tensors) {
VLOG(3) << "In temp_tensors, it has : " << i->name;
temp_tensor_names.insert(i->name);
}
for (auto& tensor : tensor_args_) {
VLOG(3) << "In tensor_args_, it has : " << tensor->name;
if (temp_tensor_names.count(tensor->name) > 0) continue;
if (all_tensor_names.count(tensor->name) == 0) continue;
bool is_output = teller.count(tensor->name);
VLOG(3) << "tensor argument " << tensor->name << " buffer "
<< tensor->buffer->name;
// avoid duplicate
if (!tensor->buffer.defined()) {
VLOG(3) << "tensor->buffer is not defined";
continue;
}
// if a argument is already marked as kInput, mark it as kOutput and move it
// to the back.
if (arg_names.count(tensor->buffer->name)) {
auto it = std::find_if(
in_args.begin(), in_args.end(), [&](const ir::Argument& x) {
return x.name() == tensor->buffer->name;
});
if (it != in_args.end()) {
in_args.erase(it);
} else {
continue;
}
}
arg_names.insert(tensor->buffer->name);
auto io = is_output ? ir::Argument::IO::kOutput : ir::Argument::IO::kInput;
if (io == ir::Argument::IO::kInput)
in_args.emplace_back(tensor->buffer, io);
else
out_args.emplace_back(tensor->buffer, io);
}
if (out_args.empty()) {
for (auto& i : all_tensors) {
auto* tensor = i.as_tensor();
VLOG(3) << "Tensor " << tensor->name;
if (tensor->buffer.defined() && !arg_names.count(tensor->buffer->name)) {
bool is_output =
teller.count(tensor->name) && teller.count(tensor->name);
if (is_output)
out_args.emplace_back(tensor->buffer, ir::Argument::IO::kOutput);
}
}
}
std::vector<ir::Argument> args(in_args.begin(), in_args.end());
args.insert(std::end(args), out_args.begin(), out_args.end());
return args;
}
std::vector<Tensor> LowerImpl::CollectTemporaryTensors() {
// a temporary should be in the comp_graph but not contained in the
// tensor_args.
paddle::flat_hash_map<std::string, Tensor> tensor_arg_map = GenTensorArgMap();
paddle::flat_hash_map<std::string, Tensor> temp_tensor_map;
for (auto* node : compu_graph_->nodes()) {
auto* cnode = node->safe_as<CompuGraphNode>();
PADDLE_ENFORCE_NOT_NULL(
cnode,
::common::errors::InvalidArgument(
"Node could not be safely cast to CompuGraphNode."));
if (!tensor_arg_map.count(cnode->tensor->name)) {
temp_tensor_map[cnode->tensor->name] = cnode->tensor;
}
}
std::vector<Tensor> temp_tensors;
std::transform(
temp_tensor_map.begin(),
temp_tensor_map.end(),
std::back_inserter(temp_tensors),
[&](const decltype(temp_tensor_map)::value_type& x) { return x.second; });
return temp_tensors;
}
paddle::flat_hash_map<std::string, Tensor> LowerImpl::GenTensorArgMap() {
paddle::flat_hash_map<std::string, Tensor> map;
for (auto& t : tensor_args_) {
map[t->name] = t;
}
return map;
}
paddle::flat_hash_map<std::string, Tensor> LowerImpl::GenAllTensorMap() {
paddle::flat_hash_map<std::string, Tensor> map;
for (auto& t : CollectAllTensors()) {
map[t->name] = t;
}
return map;
}
std::vector<Tensor> LowerImpl::CollectAllTensors() {
std::vector<Tensor> tensors;
auto topo_order = compu_graph_->topological_order(); // NOLINT
auto& nodes = std::get<0>(topo_order);
auto& edges = std::get<1>(topo_order);
for (auto* node : nodes) {
auto* cnode = node->safe_as<CompuGraphNode>();
PADDLE_ENFORCE_NOT_NULL(
cnode,
::common::errors::InvalidArgument(
"Node could not be safely cast to CompuGraphNode."));
tensors.push_back(cnode->tensor);
}
return tensors;
}
std::set<std::pair<std::string, std::string>>
LowerImpl::CollectExtraDependencies() const {
std::set<std::pair<std::string, std::string>> deps;
for (auto* node : compu_graph_->nodes()) {
auto* cnode = node->safe_as<CompuGraphNode>();
PADDLE_ENFORCE_NOT_NULL(
cnode,
::common::errors::InvalidArgument(
"Node could not be safely cast to CompuGraphNode."));
}
return deps;
}
} // namespace detail
} // namespace lang
} // namespace cinn