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

672 lines
22 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/ir/tensor.h"
#include <cstring>
#include "paddle/cinn/ast_gen_ius/tensor_group.h"
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/common/axis.h"
#include "paddle/cinn/common/common.h"
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/ir/buffer.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/ir_utils.h"
#include "paddle/cinn/ir/ir_visitor.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/operation.h"
#include "paddle/cinn/lang/compute.h"
#include "paddle/cinn/optim/ir_simplify.h"
#include "paddle/common/enforce.h"
namespace cinn {
namespace ir {
Tensor _Tensor_::Make(const std::string &name,
Type dtype,
const std::vector<Expr> &shape,
const std::vector<Expr> &domain,
FunctionRef fn,
const std::vector<Var> &reduce_axis) {
PADDLE_ENFORCE_EQ(name.empty(),
false,
::common::errors::InvalidArgument(
"Required tensor name shall not be empty."));
auto n = make_shared<_Tensor_>();
n->name = name;
n->shape = utils::GetCompatibleShape(shape);
n->domain = domain;
n->reduce_axis = reduce_axis;
n->set_type(dtype);
n->operation = fn;
n->InitAxis();
return Tensor(n);
}
Tensor _Tensor_::Make(const std::string &name,
Type dtype,
const std::vector<Expr> &shape,
const std::vector<Expr> &domain,
const std::vector<Var> &reduce_axis) {
PADDLE_ENFORCE_EQ(name.empty(),
false,
::common::errors::InvalidArgument(
"Required tensor name shall not be empty."));
auto n = make_shared<_Tensor_>();
n->name = name;
n->shape = utils::GetCompatibleShape(shape);
n->domain = domain;
n->reduce_axis = reduce_axis;
n->operation = PlaceholderOp::Make(n->name, n->shape, Float(32));
n->set_type(dtype);
n->InitAxis();
return Tensor(n);
}
Tensor _Tensor_::Make(const std::string &name,
Type dtype,
const std::vector<Dim> &sym_shape,
const std::vector<Dim> &sym_domain,
FunctionRef fn,
const std::vector<Var> &reduce_axis) {
PADDLE_ENFORCE_EQ(name.empty(),
false,
::common::errors::InvalidArgument(
"Required tensor name shall not be empty."));
PADDLE_ENFORCE_EQ(sym_shape.empty(),
false,
::common::errors::InvalidArgument(
"Required tensor sym_shape shall not be empty."));
auto n = make_shared<_Tensor_>();
n->name = name;
n->sym_shape = sym_shape;
for (int i = 0; i < sym_shape.size(); i++) {
n->shape.emplace_back(sym_shape[i]->dim_expr);
}
n->sym_domain = sym_domain;
for (int i = 0; i < sym_domain.size(); i++) {
n->domain.emplace_back(sym_domain[i]->dim_expr);
}
n->reduce_axis = reduce_axis;
n->set_type(dtype);
n->operation = fn;
n->InitAxis();
return Tensor(n);
}
Tensor _Tensor_::Make(const std::string &name,
Type dtype,
const std::vector<Dim> &sym_shape,
const std::vector<Dim> &sym_domain,
const std::vector<Var> &reduce_axis) {
PADDLE_ENFORCE_EQ(name.empty(),
false,
::common::errors::InvalidArgument(
"Required tensor name shall not be empty."));
PADDLE_ENFORCE_EQ(sym_shape.empty(),
false,
::common::errors::InvalidArgument(
"Required tensor sym_shape shall not be empty."));
auto n = make_shared<_Tensor_>();
n->name = name;
n->sym_shape = sym_shape;
for (int i = 0; i < sym_shape.size(); i++) {
n->shape.emplace_back(sym_shape[i]->dim_expr);
}
n->sym_domain = sym_domain;
for (int i = 0; i < sym_domain.size(); i++) {
n->domain.emplace_back(sym_domain[i]->dim_expr);
}
n->reduce_axis = reduce_axis;
n->operation = PlaceholderOp::Make(n->name, n->shape, Float(32));
n->set_type(dtype);
n->InitAxis();
return Tensor(n);
}
size_t Tensor::ndims() const { return operator->()->shape.size(); }
std::set<std::string> _Tensor_::GetDependTensorNames() const {
std::set<std::string> names;
auto add_depend_tensors_from_expr = [&](Expr expr) {
auto tensors = ir::ir_utils::CollectIRNodes(expr, [&](const Expr *x) {
return x->as_tensor() && x->as_tensor()->name != this->name;
});
for (auto &e : tensors) {
names.insert(e.as_tensor()->name);
}
};
if (is_compute_node()) {
add_depend_tensors_from_expr(body());
} else if (is_call_node()) {
add_depend_tensors_from_expr(body());
} else if (is_extern_call_node()) {
add_depend_tensors_from_expr(body());
} else if (is_placeholder_node()) {
return names;
} else {
CINN_NOT_IMPLEMENTED
}
return names;
}
Expr Tensor::operator()(const std::vector<Expr> &indices) const {
PADDLE_ENFORCE_EQ(self()->is_tuple(),
false,
::common::errors::PreconditionNotMet(
"Required tensor shall not be tuple type."));
auto *node = operator->();
const auto compatible_indices =
utils::GetCompatibleStoreLoadIndices(*this, indices);
PADDLE_ENFORCE_EQ(compatible_indices.size(),
ndims(),
::common::errors::PreconditionNotMet(
"number of indices not match the dimension"));
return Load::Make(*this, compatible_indices);
}
Expr _Tensor_::inline_expanded(const std::vector<Expr> &indices) {
PADDLE_ENFORCE_EQ(is_compute_node(),
true,
::common::errors::PreconditionNotMet(
"Required tensor shall be compute node."));
return get_compute_op()->producer_fn(indices);
}
const char *_Tensor_::operation_type() const {
if (!operation.defined()) return "";
return operation->as<ir::_Operation_>()->func_type();
}
bool _Tensor_::is_compute_node() const {
return std::strcmp(operation_type(), ir::ComputeOp::__func_type__) == 0;
}
bool _Tensor_::is_placeholder_node() const {
return std::strcmp(operation_type(), ir::PlaceholderOp::__func_type__) == 0;
}
bool _Tensor_::is_call_node() const {
return std::strcmp(operation_type(), ir::CallOp::__func_type__) == 0;
}
bool _Tensor_::is_extern_call_node() const {
if (std::strcmp(operation_type(), ir::CallOp::__func_type__) == 0) {
auto *op = operation->as<ir::CallOp>();
auto *call = op->call_expr.As<ir::Call>();
if (call) {
return call->is_extern_call();
}
}
return false;
}
bool _Tensor_::is_buffer_shared_node() const {
return std::strcmp(operation_type(), ir::BufferShareOp::__func_type__) == 0;
}
bool _Tensor_::is_preceding_view_node() const {
return std::strcmp(operation_type(), ir::PrecedingViewOp::__func_type__) == 0;
}
ComputeOp *_Tensor_::get_compute_op() const {
if (!is_compute_node()) return nullptr;
return operation->as<ComputeOp>();
}
PlaceholderOp *_Tensor_::get_placeholder_op() const {
if (!is_placeholder_node()) return nullptr;
return operation->as<PlaceholderOp>();
}
void _Tensor_::InitAxis() const {
axis_ = cinn::common::GenDefaultAxis(domain_without_reduce_axis().size());
}
bool _Tensor_::has_expression() const {
return (!is_placeholder_node()) && (!is_tuple_get()) &&
(!is_buffer_shared_node());
}
std::vector<Expr *> _Tensor_::expr_fields() {
std::vector<Expr *> res;
const char *func_type = operation->as<ir::_Operation_>()->func_type();
if (operation.defined()) {
if (is_compute_node()) {
auto *op = operation->as<ir::ComputeOp>();
for (auto &expr : op->body) res.push_back(&expr);
} else if (is_placeholder_node()) {
auto *op = operation->as<ir::PlaceholderOp>();
} else if (is_call_node()) {
auto *op = operation->as<ir::CallOp>();
for (auto &expr : op->read_args()) res.push_back(&expr);
} else if (is_buffer_shared_node()) {
} else {
CINN_NOT_IMPLEMENTED
}
}
for (auto &e : shape) {
res.push_back(&e);
}
for (auto &e : domain) {
res.push_back(&e);
}
return res;
}
std::vector<const Expr *> _Tensor_::expr_fields() const {
std::vector<const Expr *> res;
const char *func_type = operation->as<ir::_Operation_>()->func_type();
if (operation.defined()) {
if (is_compute_node()) {
auto *op = operation->as<ir::ComputeOp>();
for (auto &expr : op->body) res.push_back(&expr);
} else if (is_placeholder_node()) {
auto *op = operation->as<ir::PlaceholderOp>();
} else if (is_call_node()) {
auto *op = operation->as<ir::CallOp>();
for (auto &expr : op->read_args()) res.push_back(&expr);
} else if (is_buffer_shared_node()) {
} else {
LOG(ERROR) << "func_type: " << func_type;
CINN_NOT_IMPLEMENTED
}
}
for (auto &e : shape) {
res.push_back(&e);
}
for (auto &e : domain) {
res.push_back(&e);
}
return res;
}
_Tensor_::~_Tensor_() {}
Expr _Tensor_::body() const {
if (is_placeholder_node()) return Expr();
if (is_buffer_shared_node()) return Expr();
if (is_compute_node()) return operation->as<ir::ComputeOp>()->body.front();
if (is_call_node()) return operation->as<ir::CallOp>()->call_expr;
CINN_NOT_IMPLEMENTED;
}
Expr *_Tensor_::mutable_body() {
if (is_placeholder_node()) return nullptr;
if (is_buffer_shared_node()) return nullptr;
if (is_compute_node()) return &operation->as<ir::ComputeOp>()->body.front();
if (is_call_node()) return &operation->as<ir::CallOp>()->call_expr;
CINN_NOT_IMPLEMENTED
}
Expr _Tensor_::tensor_store_expanded_body() {
PADDLE_ENFORCE_EQ(is_placeholder_node(),
false,
::common::errors::PreconditionNotMet(
"Placeholder should not expand store."));
Expr final_body = body();
if (shape.empty()) return final_body;
std::vector<Expr> g_axis = cinn::common::GenDefaultAxisAsExpr(shape.size());
if (!new_indices.empty()) {
g_axis = new_indices;
}
auto *reduce_node = body().As<ir::Reduce>();
if (reduce_node) {
final_body = reduce_node->body;
switch (reduce_node->reduce_type) {
case ir::Reduce::kSum:
final_body = Tensor(this)(g_axis) + final_body;
break;
case ir::Reduce::kMul:
final_body = Tensor(this)(g_axis) * final_body;
break;
case ir::Reduce::kMax:
final_body = Max::Make(Tensor(this)(g_axis), final_body);
break;
case ir::Reduce::kMin:
final_body = Min::Make(Tensor(this)(g_axis), final_body);
break;
case ir::Reduce::kAll:
final_body = Tensor(this)(g_axis) && final_body;
break;
case ir::Reduce::kAny:
final_body = Tensor(this)(g_axis) || final_body;
break;
default:
CINN_NOT_IMPLEMENTED
}
}
if (is_tuple()) return final_body;
return ir::Store::Make(Expr(Buffer(this)), final_body, g_axis);
}
void _Tensor_::Bind(lang::Buffer &buffer) {
PADDLE_ENFORCE_EQ(buffer->type().is_void(),
false,
::common::errors::PreconditionNotMet(
"Required buffer type shall not be void()."));
if (this->buffer.defined()) {
// remove the old buffer
if (this->buffer == buffer.buffer()) return;
this->buffer->Unbind(this);
}
// Extract the tensors those has binded to this buffer.
buffer_depended_tensor_names_ = buffer.buffer()->binded_tensor_names();
buffer.buffer()->BindTo(this);
PADDLE_ENFORCE_EQ(buffer->binded_tensor_names().empty(),
false,
::common::errors::PreconditionNotMet(
"Required binded_tensor_names shall not be empty."));
this->buffer = buffer.buffer();
PADDLE_ENFORCE_EQ(this->buffer.defined(),
true,
::common::errors::PreconditionNotMet(
"Required buffer shall be defined."));
}
void _Tensor_::Bind(const Buffer &buffer) {
lang::Buffer buf(buffer);
Bind(buf);
}
void _Tensor_::WithBuffer(const Type &type) {
Type buf_type = type.is_void() ? type_ : type;
lang::Buffer buf(buf_type);
buf->target = cinn::common::DefaultHostTarget();
Bind(buf);
}
void _Tensor_::WithBuffer(const std::string &memory_type,
const std::string &buffer_name,
const Type &type) {
Type buf_type = type.is_void() ? type_ : type;
if (this->buffer.defined()) {
this->buffer->dtype = buf_type;
this->buffer->name = buffer_name;
if (memory_type == "shared") {
this->buffer->memory_type = MemoryType::GPUShared;
} else if (memory_type == "local") {
this->buffer->memory_type = MemoryType::GPULocal;
} else if (memory_type == "global") {
this->buffer->memory_type = MemoryType::Heap;
} else {
std::stringstream ss;
ss << "Not supported memory type " << memory_type;
PADDLE_THROW(::common::errors::InvalidArgument(ss.str()));
}
} else {
lang::Buffer buf(buf_type, buffer_name);
buf->target = cinn::common::DefaultHostTarget();
Bind(buf);
if (memory_type == "shared") {
buf->memory_type = MemoryType::GPUShared;
} else if (memory_type == "local") {
buf->memory_type = MemoryType::GPULocal;
} else if (memory_type == "global") {
buf->memory_type = MemoryType::Heap;
} else {
std::stringstream ss;
ss << "Not supported memory type " << memory_type;
PADDLE_THROW(::common::errors::InvalidArgument(ss.str()));
}
}
}
bool _Tensor_::HasSameShapeWith(const Tensor &other) const {
if (shape.size() != other->shape.size()) return false;
for (int i = 0; i < shape.size(); i++) {
Expr dim0 = optim::ArithSimplify(shape[i]);
Expr dim1 = optim::ArithSimplify(other->shape[i]);
if (dim0 != dim1) return false;
}
return true;
}
Tensor _Tensor_::TupleGet(int offset) const {
PADDLE_ENFORCE_EQ(is_tuple(),
true,
::common::errors::PreconditionNotMet(
"Required Tensor shall be tuple type."));
auto *call = body().As<ir::Call>();
PADDLE_ENFORCE_LT(
offset,
call->write_args.size(),
::common::errors::PreconditionNotMet(
"Required offset shall be less than call->write_args.size()."));
auto tensor = call->write_args[offset].as_tensor_ref();
tensor->WithBuffer();
return tensor;
}
bool _Tensor_::is_tuple() const {
if (!has_expression()) return false;
auto *call = body().As<ir::Call>();
if (call && call->is_extern_call() && !call->write_args.empty()) return true;
return false;
}
std::vector<Expr> _Tensor_::domain_with_reduce_axis() const {
if (reduce_axis.empty()) return domain;
auto res = domain;
for (const Var &axis : reduce_axis) {
PADDLE_ENFORCE_EQ(axis->upper_bound.type().is_int(32) ||
axis->upper_bound.type().is_int(64),
true,
::common::errors::PreconditionNotMet(
"Required upper_bound shall be int32 or int64."));
res.push_back(axis->upper_bound);
}
return res;
}
bool operator<(const Tensor &a, const Tensor &b) { return a->name < b->name; }
Tensor::Tensor(const std::string &name,
Type dtype,
const std::vector<Expr> &shape,
const std::vector<Expr> &domain,
FunctionRef fn,
const std::vector<Var> &reduce_axis)
: IrNodeRef(
_Tensor_::Make(name, dtype, shape, domain, fn, reduce_axis).self()) {}
Tensor::Tensor(const std::string &name,
Type dtype,
const std::vector<Dim> &sym_shape,
const std::vector<Dim> &sym_domain,
FunctionRef fn,
const std::vector<Var> &reduce_axis)
: IrNodeRef(
_Tensor_::Make(name, dtype, sym_shape, sym_domain, fn, reduce_axis)
.self()) {}
bool _Tensor_::is_tuple_get() const {
return is_call_node() && operation.defined() &&
operation->as<ir::_Operation_>()->func_type() ==
ir::CallOp::__func_type__ &&
operation->as<ir::CallOp>()->is_tuple_get;
}
bool _Tensor_::IsDependOnStatement(std::string_view statement) {
if (!is_compute_node()) {
return false;
}
auto depend_tensors = DependingTensorNames();
for (const auto &x : depend_tensors) {
if (x == statement) return true;
}
return false;
}
std::set<std::string> _Tensor_::DependingTensorNames() {
std::set<std::string> res;
if (body().defined()) {
auto depend_tensors = ir::ir_utils::CollectIRNodes(
body(), [](const Expr *x) -> bool { return x->as_tensor(); });
for (const auto &x : depend_tensors) {
if (x.get() != this) {
res.insert(x.as_tensor()->name);
}
}
}
return res;
}
const std::vector<Var> &_Tensor_::axis() const {
PADDLE_ENFORCE_EQ(axis_.size(),
domain_without_reduce_axis().size(),
::common::errors::PreconditionNotMet(
"Required axis_ shall have same size with "
"domain_without_reduce_axis."));
return axis_;
}
std::vector<Var> _Tensor_::axis_with_reduce() const {
auto axis = axis_;
axis.insert(axis.end(), reduce_axis.begin(), reduce_axis.end());
return axis;
}
bool _Tensor_::Uses(const Tensor &other) const {
auto loads = ir::ir_utils::CollectIRNodes(body(), [&](const Expr *x) {
auto *loadn = x->As<ir::Load>();
if (!loadn) return false;
return loadn->tensor.as_tensor()->name == other->name;
});
return !loads.empty();
}
ir::Tensor _Tensor_::Reshape(const std::vector<Expr> &shape) const {
auto op = BufferShareOp::Make();
auto n = make_shared<_Tensor_>();
auto selft = Tensor(const_cast<ir::_Tensor_ *>(this));
{
int32_t this_num_elements = 1;
for (auto &e : this->shape) {
this_num_elements = this_num_elements * e.as_int32();
}
int32_t num_elements = 1;
for (auto &e : shape) {
num_elements = num_elements * e.as_int32();
}
PADDLE_ENFORCE_EQ(
this_num_elements,
num_elements,
::common::errors::PreconditionNotMet(
"Required this_num_elements shall be equal to num_elements."));
}
n->name = Context::Global().NewName(name + "_reshape");
n->shape = shape;
n->domain = shape;
n->set_type(type());
n->operation = op;
n->InitAxis();
auto t = Tensor(n);
return t;
}
ir::Tensor _Tensor_::ReshapeCopied(const std::vector<Expr> &shape) const {
auto t = ir::Tensor(const_cast<ir::_Tensor_ *>(this));
auto copied = Compute(
domain,
[=](const std::vector<Expr> &axis) { return t(axis); },
Context::Global().NewName(this->name + "_copied"));
auto res = copied->Reshape(shape);
return res;
}
static constexpr char kReduceInitSuffix[] = "__reduce_init";
std::string GenReduceInitTensorNameOf(const std::string &tensor_name) {
return tensor_name + kReduceInitSuffix;
}
bool IsReduceInitTensorName(const std::string &tensor_name) {
std::string reduce_init_suffix(kReduceInitSuffix);
return tensor_name.length() > reduce_init_suffix.size() &&
tensor_name.substr(tensor_name.length() - reduce_init_suffix.size(),
reduce_init_suffix.size()) == reduce_init_suffix;
}
bool IsSplitTransformTensorName(const std::string &tensor_name) {
return tensor_name.find("_split_transform") != std::string::npos;
}
std::string GetOriginalReduceTensorName(const std::string &tensor_name) {
std::string reduce_init_suffix(kReduceInitSuffix);
if (IsReduceInitTensorName(tensor_name)) {
return tensor_name.substr(0,
tensor_name.length() - reduce_init_suffix.size());
}
return tensor_name;
}
bool _Tensor_::is_reduce_sum() const {
if (!contains_reduce_axis()) return false;
return body().As<ir::Reduce>() &&
body().As<ir::Reduce>()->reduce_type == ir::Reduce::ReduceType::kSum;
}
bool _Tensor_::is_reduce_mul() const {
if (!contains_reduce_axis()) return false;
return body().As<ir::Reduce>() &&
body().As<ir::Reduce>()->reduce_type == ir::Reduce::ReduceType::kMul;
}
Expr _Tensor_::GetReduceInitVal() const {
PADDLE_ENFORCE_EQ(is_reduce_tensor(),
true,
::common::errors::PreconditionNotMet(
"Required tensor is a reduce type."));
return body().As<ir::Reduce>()->init;
}
void _Tensor_::Verify() const {
PADDLE_ENFORCE_EQ(shape.empty(),
false,
::common::errors::PreconditionNotMet(
"Required shape shall not be empty."));
PADDLE_ENFORCE_EQ(domain.empty(),
false,
::common::errors::PreconditionNotMet(
"Required domain shall not be empty."));
PADDLE_ENFORCE_EQ(name.empty(),
false,
::common::errors::PreconditionNotMet(
"Required name shall not be empty."));
}
} // namespace ir
} // namespace cinn