chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
add_subdirectory(tools)
add_subdirectory(custom_engine)
add_subdirectory(core)
add_subdirectory(pass)
add_subdirectory(pattern_rewrite)
add_subdirectory(kernel_dialect)
add_subdirectory(cinn)
add_subdirectory(control_flow_dialect)
add_subdirectory(shape_dialect)
add_subdirectory(sub_graph)
add_subdirectory(distributed)
add_subdirectory(operator)
add_subdirectory(serialize_deserialize)
+79
View File
@@ -0,0 +1,79 @@
add_subdirectory(adt)
if(WITH_TESTING AND WITH_CINN)
paddle_test(test_dialect_convert SRCS dialect_convert_test.cc)
paddle_test(test_add_broadcast_to_elementwise SRCS
add_broadcast_to_elementwise_test.cc)
paddle_test(test_sub_graph_extract SRCS sub_graph_extract_test.cc DEPS
pir_transforms)
paddle_test(test_ir_op_cluster SRCS ir_op_cluster_test.cc DEPS pir_transforms
cinn_transforms)
paddle_test(test_pir_all_path SRCS pir_all_path_test.cc DEPS cinn_transforms)
paddle_test(test_symbolic_lower SRCS symbolic_lower_test.cc)
paddle_test(test_pir_build_cinn_pass SRCS build_cinn_pass_test.cc)
paddle_test(test_compilation_task SRCS compilation_task_test.cc)
paddle_test(test_generate_shape_util_test SRCS generate_shape_util_test.cc
DEPS cinn_op_dialect)
paddle_test(merge_parallel_matmul_pass_test SRCS
merge_parallel_matmul_pass_test.cc)
paddle_test(test_tile_config_searcher SRCS tile_config_searcher_test.cc DEPS
schedule_config_search)
paddle_test(
test_tile_config_searcher_pure_spatial SRCS
tile_config_searcher_pure_spatial_test.cc DEPS schedule_config_search)
paddle_test(test_tile_config_performance SRCS tile_config_performance_test.cc
DEPS schedule_config_search)
paddle_test(test_file_tile_config SRCS file_tile_config_test.cc)
paddle_test(replace_cross_block_reduction_test SRCS
replace_cross_block_reduction_test.cc)
paddle_test(ir_simplify_test SRCS ir_simplify_test.cc)
paddle_test(eliminate_common_factor_of_local_index_test SRCS
eliminate_common_factor_of_local_index_test.cc)
paddle_test(ir_simplify_select_test SRCS ir_simplify_select_test.cc)
paddle_test(ir_simplify_bound_test SRCS ir_simplify_bound_test.cc)
# DO NOT forget add test name here, otherwise it will not be executed in
# CINN CI.
set(cinn_unit_tests
test_dialect_convert
test_add_broadcast_to_elementwise
test_sub_graph_extract
test_pir_all_path
test_pir_build_cinn_pass
test_compilation_task
test_generate_shape_util_test
merge_parallel_matmul_pass_test
test_tile_config_searcher
test_tile_config_searcher_pure_spatial
test_file_tile_config
replace_cross_block_reduction_test)
foreach(test_name ${cinn_unit_tests})
get_property(
env
TEST ${test_name}
PROPERTY ENVIRONMENT)
set_property(TEST ${test_name} PROPERTY ENVIRONMENT ${env})
set_tests_properties(${test_name} PROPERTIES LABELS "RUN_TYPE=CINN")
endforeach()
endif()
@@ -0,0 +1,251 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/manual_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/add_broadcast_to_elementwise_pass.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
std::vector<pir::Type> CreateDenseTensorTypes(const phi::DDim &dims) {
pir::IrContext *ctx = ::pir::IrContext::Instance();
pir::Type fp32_dtype = ::pir::Float32Type::get(ctx);
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {};
size_t offset = 0;
std::vector<::pir::Type> op_output_types = {::pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset)};
return op_output_types;
}
void BuildProgram(pir::Builder &builder) { // NOLINT
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({4, 3, 16})));
builder.SetInsertionPointToBlockEnd(group_op.block());
paddle::dialect::FullOp full_input_x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_input_y = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{16}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
auto add_op = builder.Build<paddle::dialect::AddOp>(full_input_x.result(0),
full_input_y.result(0));
auto relu_op = builder.Build<paddle::dialect::ReluOp>(add_op.result(0));
builder.Build<pir::YieldOp>(std::vector<pir::Value>{relu_op.out()});
}
void BuildProgramBoth(pir::Builder &builder) { // NOLINT
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({10, 10})));
builder.SetInsertionPointToBlockEnd(group_op.block());
paddle::dialect::FullOp full_input_x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{10, 1},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_input_y =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1, 10},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
auto add_op = builder.Build<paddle::dialect::AddOp>(full_input_x.result(0),
full_input_y.result(0));
auto relu_op = builder.Build<paddle::dialect::ReluOp>(add_op.result(0));
builder.Build<pir::YieldOp>(std::vector<pir::Value>{relu_op.out()});
}
void BuildProgramSubBoth(pir::Builder &builder) { // NOLINT
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({10, 10})));
builder.SetInsertionPointToBlockEnd(group_op.block());
paddle::dialect::FullOp full_input_x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{10, 1},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_input_y =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1, 10},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
auto sub_op = builder.Build<paddle::dialect::SubtractOp>(
full_input_x.result(0), full_input_y.result(0));
auto relu_op = builder.Build<paddle::dialect::ReluOp>(sub_op.result(0));
builder.Build<pir::YieldOp>(std::vector<pir::Value>{relu_op.out()});
}
TEST(PatternRewrite, broadcast_elementwise) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
pm.Run(&program);
auto it = program.block()
->begin()
->dyn_cast<cinn::dialect::GroupOp>()
.block()
->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::AddOp>(),
true,
common::errors::PreconditionNotMet(
"Expected AddOp but found different operation type: " +
std::string(it->name())));
}
TEST(PatternRewrite, broadcast_elementwise_both) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgramBoth(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
pm.Run(&program);
auto it = program.block()
->begin()
->dyn_cast<cinn::dialect::GroupOp>()
.block()
->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::AddOp>(),
true,
common::errors::PreconditionNotMet(
"Expected AddOp but found different operation type: " +
std::string(it->name())));
}
TEST(PatternRewrite, broadcast_elementwise_sub_both) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgramSubBoth(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
pm.Run(&program);
auto it = program.block()
->begin()
->dyn_cast<cinn::dialect::GroupOp>()
.block()
->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(
it->isa<paddle::dialect::SubtractOp>(),
true,
common::errors::PreconditionNotMet(
"Expected SubtractOp but found different operation type: " +
std::string(it->name())));
}
+8
View File
@@ -0,0 +1,8 @@
if(WITH_TESTING AND WITH_CINN)
paddle_test(map_expr_test SRCS map_expr_test.cc)
set_tests_properties(map_expr_test PROPERTIES LABELS "RUN_TYPE=CINN")
paddle_test(test_data_dependency_graph SRCS data_dependency_graph_test.cc)
paddle_test(test_index_expr SRCS index_expr_test.cc)
paddle_test(test_iter_simplify SRCS iter_simplify_test.cc)
paddle_test(merge_block_utils_test SRCS merge_block_utils_test.cc)
endif()
@@ -0,0 +1,124 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sstream>
#include "paddle/cinn/ir/ir_analyzer/data_dependency_graph.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/stmt.h"
namespace cinn {
namespace ir {
namespace {
using ir::analyzer::DataDependencyGraph;
using ir::analyzer::DepKind;
class TestDataDependencyGraph : public ::testing::Test {
public:
void SetUp() override {
const std::vector<ir::Expr> shape = {};
tensor_a =
ir::_Tensor_::Make(common::UniqName("A"), common::Bool(), shape, shape);
tensor_b =
ir::_Tensor_::Make(common::UniqName("B"), common::Bool(), shape, shape);
tensor_c =
ir::_Tensor_::Make(common::UniqName("C"), common::Bool(), shape, shape);
tensor_d =
ir::_Tensor_::Make(common::UniqName("D"), common::Bool(), shape, shape);
tensor_a->WithBuffer("global", "_" + tensor_a->name + "_temp_buffer");
tensor_b->WithBuffer("global", "_" + tensor_b->name + "_temp_buffer");
tensor_c->WithBuffer("global", "_" + tensor_c->name + "_temp_buffer");
tensor_d->WithBuffer("global", "_" + tensor_d->name + "_temp_buffer");
var_x = ir::Var(ir::Expr(1), ir::Expr(INT32_MAX), "x");
load_a = ir::Load::Make(ir::Expr(tensor_a), {});
load_b = ir::Load::Make(ir::Expr(tensor_b), {});
load_c = ir::Load::Make(ir::Expr(tensor_c), {});
};
ir::Var var_x;
ir::Tensor tensor_a, tensor_b, tensor_c, tensor_d;
ir::Expr load_a, load_b, load_c;
};
TEST_F(TestDataDependencyGraph, TensorDep) {
// A[0] = B[0]
auto store_a_b = ir::stmt::Store(ir::Expr(tensor_a), load_b, {});
// B[0] = C[0]
auto store_b_c = ir::stmt::Store(ir::Expr(tensor_b), load_c, {});
// C[0] = A[0]
auto store_c_a = ir::stmt::Store(ir::Expr(tensor_c), load_a, {});
const std::vector<ir::stmt::StmtRef> stmts = {
store_a_b, store_b_c, store_c_a};
const auto &dep_graph = DataDependencyGraph(stmts);
dep_graph.Print();
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[1]), DepKind::DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[2]), DepKind::DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[1], stmts[2]), DepKind::DEP);
}
TEST_F(TestDataDependencyGraph, VarDep) {
// x = A[0]
auto let_x_a = ir::stmt::Let(ir::Expr(var_x), load_a);
// B[0] = x
auto store_b_x = ir::stmt::Store(ir::Expr(tensor_b), ir::Expr(var_x), {});
const std::vector<ir::stmt::StmtRef> stmts = {let_x_a, store_b_x};
const auto &dep_graph = DataDependencyGraph(stmts);
dep_graph.Print();
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[1]), DepKind::DEP);
}
TEST_F(TestDataDependencyGraph, TensorNoDep) {
// A[0] = B[0]
auto store_a_b = ir::stmt::Store(ir::Expr(tensor_a), load_b, {});
// C[0] = B[0]
auto store_c_b = ir::stmt::Store(ir::Expr(tensor_c), load_b, {});
// D[0] = B[0]
auto store_d_b = ir::stmt::Store(ir::Expr(tensor_d), load_b, {});
const std::vector<ir::stmt::StmtRef> stmts = {
store_a_b, store_c_b, store_d_b};
const auto &dep_graph = DataDependencyGraph(stmts);
dep_graph.Print();
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[1]), DepKind::NO_DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[2]), DepKind::NO_DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[1], stmts[2]), DepKind::NO_DEP);
}
TEST_F(TestDataDependencyGraph, VarNoDep) {
// x = A[0]
auto let_x_a = ir::stmt::Let(ir::Expr(var_x), load_a);
// B[0] = x
auto store_b_x = ir::stmt::Store(ir::Expr(tensor_b), ir::Expr(var_x), {});
// C[0] = x
auto store_c_x = ir::stmt::Store(ir::Expr(tensor_c), ir::Expr(var_x), {});
// D[0] = x
auto store_d_x = ir::stmt::Store(ir::Expr(tensor_d), ir::Expr(var_x), {});
const std::vector<ir::stmt::StmtRef> stmts = {
let_x_a, store_b_x, store_c_x, store_d_x};
const auto &dep_graph = DataDependencyGraph(stmts);
dep_graph.Print();
EXPECT_EQ(dep_graph.HasDependency(stmts[1], stmts[2]), DepKind::NO_DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[1], stmts[3]), DepKind::NO_DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[2], stmts[3]), DepKind::NO_DEP);
}
} // namespace
} // namespace ir
} // namespace cinn
+688
View File
@@ -0,0 +1,688 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include "paddle/cinn/common/integer_set.h"
#include "paddle/cinn/common/simplify_special_pattern.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_base.h"
#include "paddle/cinn/ir/ir_mutator.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/optim/simplify_util.h"
namespace cinn {
namespace common {
using optim::ChangeSeqOfDivMod;
using optim::CheckPattern;
using optim::ConstructIndexExprByNodeType;
using optim::MatchPattern;
using optim::ParseExpressionFromString;
class TestIndexExpr : public ::testing::Test {
public:
void SetUp() override {
S4 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S4")
.set_index(true);
S5 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S5")
.set_index(true);
S6 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S6")
.set_index(true);
S7 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S7")
.set_index(true);
S8 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S8")
.set_index(true);
S9 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S9")
.set_index(true);
f = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "f");
};
ir::Var S4, S5, S6, S7, S8, S9, f;
};
TEST_F(TestIndexExpr, IndexExpr_0) {
ir::IndexExpr a(14);
ir::IndexExpr b(7);
Expr d(6);
ir::Expr c0 = a + b;
ir::Expr c1 = a - b;
ir::Expr c2 = a * b;
ir::Expr c3 = a / b;
ir::Expr c4 = a % b;
ir::Expr c5 = a / d.as_index();
ir::Expr c6 = a % d.as_index();
EXPECT_EQ(c0, Expr(21));
EXPECT_EQ(c1, Expr(7));
EXPECT_EQ(c2, Expr(98));
EXPECT_EQ(c3, Expr(2));
EXPECT_EQ(c4, Expr(0));
EXPECT_EQ(c5, Expr(2));
EXPECT_EQ(c6, Expr(2));
}
TEST_F(TestIndexExpr, IndexExpr_1) {
auto test = S6 * S7;
ir::IndexExpr e1 = (S5 * ((S4 * (S5 * (S6 * S7))) / S5));
ir::IndexExpr e2 = (S4 * (S5 * (S6 * S7))) / S5;
ir::IndexExpr e3 = (S4 * S5) / S5;
ir::IndexExpr e4 = (S4 * (S5 * (S6 * S7)) + S5) / S5;
ir::IndexExpr e5 = (S4 * (S5 * (S6 * S7)) + 2 * S5) / S5;
ir::IndexExpr e6 = (S4 * (S5 * (S6 * S7)) + S5 / S6) / S5;
ir::IndexExpr e7 = (S4 * (S5 * (S6 * S7)) + 2 * S5 / S6) / S5;
EXPECT_EQ(e1.Normalize(), ir::IndexExpr((S6 * S7) * S4 * S5));
EXPECT_EQ(e2.Normalize(), ir::IndexExpr((S6 * S7) * S4));
EXPECT_EQ(e3.Normalize(), ir::IndexExpr(S4));
EXPECT_EQ(e4.Normalize(), ir::IndexExpr(((S6 * S7) * S4) + 1));
EXPECT_EQ(e5.Normalize(), ir::IndexExpr(((S6 * S7) * S4) + 2));
EXPECT_EQ(e6.Normalize(), ir::IndexExpr(((S6 * S7) * S4) + (1 / S6)));
EXPECT_EQ(e7.Normalize(), ir::IndexExpr(((S6 * S7) * S4) + (2 / S6)));
}
TEST_F(TestIndexExpr, IndexExpr_2) {
ir::Expr q1 = S4;
ir::Expr q2 = S4;
ir::Expr q3 = S4 + S5;
ir::Expr q4 = S5 + S4;
ir::Expr q5 = S4 * 2 + S5 / 4;
ir::Expr q6 = S5 / 4 + S4 * 2;
ir::Expr q7 = S4 + S5 + S6;
ir::Expr q8 = S5 + (S4 + S6);
ir::Expr q9 = S4 + (S5 + S7 / 4 + S6 * 2);
ir::Expr q10 = S5 + (S4 + S6 * 2 + S7 / 4);
ir::Expr q11 = (S7 + S5) + (S4 + S6);
ir::Expr q12 = (S4 + S5) + (S6 + S7);
ir::Expr q13 = (S4 + S5) * 3 + (S6 / 2 + S7) * 2;
ir::Expr q14 = (S6 / 2 + S7) * 2 + (S4 + S5) * 3;
ir::Expr q15 = (S4 + S5 * 2) * 3 + (S6 / 2 + S7) * 2;
ir::Expr q16 = (S6 / 2 + S7) * 2 + (S4 + S5 * 2) * 3;
ir::Expr q17 = (S4 + S5 * 2) * 3 + (S6 / 2 + S7) * 2 + S4;
ir::Expr q18 = (S6 / 2 + S7) * 2 + (S4 + S5 * 2) * 3 + S4;
ir::Expr q19 = (S4 + S5 * 2) * 3 + (S6 / 2 + S7) * 2 + S4;
ir::Expr q20 = (S6 / 2 + S7) * 2 + (S4 + S5 * 2) * 3 + S5;
EXPECT_EQ(q1.as_index().Normalize(), q2.as_index().Normalize());
EXPECT_EQ(q3.as_index().Normalize(), q4.as_index().Normalize());
EXPECT_EQ(q5.as_index().Normalize(), q6.as_index().Normalize());
EXPECT_EQ(q7.as_index().Normalize(), q8.as_index().Normalize());
EXPECT_EQ(q9.as_index().Normalize(), q10.as_index().Normalize());
EXPECT_EQ(q11.as_index().Normalize(), q12.as_index().Normalize());
EXPECT_EQ(q13.as_index().Normalize(), q14.as_index().Normalize());
EXPECT_EQ(q15.as_index().Normalize(), q16.as_index().Normalize());
EXPECT_EQ(q17.as_index().Normalize(), q18.as_index().Normalize());
EXPECT_NE(q19.as_index().Normalize(), q20.as_index().Normalize());
}
TEST_F(TestIndexExpr, IndexExpr_3) {
// `Add` corner cases
ir::Expr q1 = S4 / S5 * S5 + S4 % S5;
ir::Expr q2 = (S4 + S5) / S6 * S6 + (S4 + S5) % S6;
ir::Expr q3 = S4 / (S5 + S6) * (S5 + S6) + S4 % (S5 + S6);
ir::Expr q4 = (S4 + S5) / (S6 + S7) * (S6 + S7) + (S4 + S5) % (S6 + S7);
ir::Expr q5 = (S4 + S5) / 5 * 5 + (S4 + S5) * 11 % 5;
ir::Expr q14 = (S4 + S5) / (S6 * S7) * S6 * S7 + (S4 + S5) % (S6 * S7);
ir::Expr q15 =
(S4 * 256 + S5 + S6 * 1024) % 25088 / 512 * 512 + (S4 * 256 + S5) % 512;
ir::Expr q16 =
((S4 * 256 + S5) / S6 / S7 * S7 + (S4 * 256 + S5) / S6 % S7) * S6 +
(S4 * 256 + S5) % S6;
ir::Expr q17 = S4 / (S5 * S6) * S6 + S4 % (S5 * S6) / S5;
ir::Expr q18 = (S4 * 1024 + S5 * 256 + S6) / 2097152 * 32 +
(S4 * 1024 + S5 * 256 + S6) % 2097152 / 65536;
// `Div` corner cases
ir::Expr q6 = (S4 % S5 - S4) / S5;
ir::Expr q7 = (S4 - S4 % S5) / S5;
ir::Expr q8 = ((S4 + S5) % S6 - S4 - S5) / S6;
ir::Expr q9 = (S4 + S5 - (S4 + S5) % S6) / S6;
// `Mod` corner cases
ir::Expr q10 = (S4 % S5 - S4) % S5;
ir::Expr q11 = (S4 - S4 % S5) % S5;
ir::Expr q12 = ((S4 + S5) % S6 - S4 - S5) % S6;
ir::Expr q13 = (S4 + S5 - (S4 + S5) % S6) % S6;
EXPECT_EQ(q1.as_index().Normalize(), ir::IndexExpr(S4));
EXPECT_EQ(q2.as_index().Normalize(), ir::IndexExpr(S4 + S5));
EXPECT_EQ(q3.as_index().Normalize(), ir::IndexExpr(S4));
EXPECT_EQ(q4.as_index().Normalize(), ir::IndexExpr(S4 + S5));
EXPECT_EQ(q5.as_index().Normalize(), ir::IndexExpr(S4 + S5));
EXPECT_EQ(q6.as_index().Normalize(), ir::IndexExpr((S4 / S5) * (-1)));
EXPECT_EQ(q7.as_index().Normalize(), ir::IndexExpr(S4 / S5));
EXPECT_EQ(q8.as_index().Normalize(), ir::IndexExpr(((S4 + S5) / S6) * (-1)));
EXPECT_EQ(q9.as_index().Normalize(), ir::IndexExpr((S4 + S5) / S6));
EXPECT_EQ(q10.as_index().Normalize(), ir::IndexExpr(0));
EXPECT_EQ(q11.as_index().Normalize(), ir::IndexExpr(0));
EXPECT_EQ(q12.as_index().Normalize(), ir::IndexExpr(0));
EXPECT_EQ(q13.as_index().Normalize(), ir::IndexExpr(0));
EXPECT_EQ(q14.as_index().Normalize(), ir::IndexExpr(S4 + S5));
EXPECT_EQ(q15.as_index().Normalize(),
ir::IndexExpr((S4 * 256 + S5 + S6 * 1024)) % 25088);
EXPECT_EQ(q16.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
ir::IndexExpr(S4 * 256 + S5));
EXPECT_EQ(q17.as_index().Normalize(), ir::IndexExpr(S4 / S5));
EXPECT_EQ(q18.as_index().Normalize(),
ir::IndexExpr((S4 * 1024 + S5 * 256 + S6) / 65536));
}
TEST_F(TestIndexExpr, Change_Seq_Of_Div_Mod) {
ir::Expr q1 = S4 / S5;
ir::Expr q2 = S4 % S5;
ir::Expr q3 = S4 / S5 % S6;
ir::Expr q4 = S4 / S5 % S6;
EXPECT_EQ(ChangeSeqOfDivMod(q1.as_index()), q1);
EXPECT_EQ(ChangeSeqOfDivMod(q2.as_index()), q2);
EXPECT_EQ(ChangeSeqOfDivMod(q3.as_index()), S4 % (S5 * S6) / S5);
}
TEST_F(TestIndexExpr, Test_ConstructIndexExprByNodeType) {
ir::Expr result_add = ConstructIndexExprByNodeType(
ir::IrNodeTy::Add, S4.as_index(), S5.as_index(), true);
ir::Expr result_sub = ConstructIndexExprByNodeType(
ir::IrNodeTy::Sub, S4.as_index(), S5.as_index(), false);
ir::Expr result_mul = ConstructIndexExprByNodeType(
ir::IrNodeTy::Mul, S4.as_index(), S5.as_index(), true);
ir::Expr result_div = ConstructIndexExprByNodeType(
ir::IrNodeTy::Div, S4.as_index(), S5.as_index(), true);
ir::Expr result_mod = ConstructIndexExprByNodeType(
ir::IrNodeTy::Mod, S4.as_index(), S5.as_index(), true);
ir::Expr result_min = ConstructIndexExprByNodeType(
ir::IrNodeTy::Min, S4.as_index(), S5.as_index(), false);
ir::Expr result_max = ConstructIndexExprByNodeType(
ir::IrNodeTy::Max, S4.as_index(), S5.as_index(), false);
EXPECT_EQ(result_add, S4 + S5);
EXPECT_EQ(result_sub, S4 - S5);
EXPECT_EQ(result_mul, S4 * S5);
EXPECT_EQ(result_div, S4 / S5);
EXPECT_EQ(result_mod, S4 % S5);
EXPECT_EQ(result_min, ir::Min::Make(S4, S5));
EXPECT_EQ(result_max, ir::Max::Make(S4, S5));
}
TEST_F(TestIndexExpr, Test_dynamic) {
ir::Expr q =
((((((((((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) % S5) * S6) +
((((S7 * 1024) + S8) + (S9 * 4096)) % S6)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) % 640) %
S4) *
S6) *
S5)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) / 640) *
S5) *
S6) *
S4)) /
((S5 * S6) * S4)) *
S4) +
(((((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) % S5) * S6) +
((((S7 * 1024) + S8) + (S9 * 4096)) % S6)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) % 640) %
S4) *
S6) *
S5)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) / 640) *
S5) *
S6) *
S4)) /
(S5 * S6)) %
S4)) *
S5) +
(((((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) % S5) * S6) +
((((S7 * 1024) + S8) + (S9 * 4096)) % S6)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) % 640) % S4) *
S6) *
S5)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) / 640) * S5) *
S6) *
S4)) /
S6) %
S5)) *
S6) +
((((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) % S5) * S6) +
((((S7 * 1024) + S8) + (S9 * 4096)) % S6)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) % 640) % S4) *
S6) *
S5)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) / 640) * S5) *
S6) *
S4)) %
S6));
ir::Expr q1 =
((((((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)) / (S5 * S6)) * S6) *
S5) +
(f % (S5 * S6)));
ir::Expr q2 = ((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)) % (S5 * S6);
ir::Expr q3 = (S5 * S6) * S4 / (S5 * S6);
ir::Expr q4 = (S5 * S6) * S4 % (S5 * S6);
ir::Expr q5 =
(((((((((((((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)) / (S5 * S6)) +
((f / ((S5 * S6) * 640)) * S4)) *
S5) *
S6) +
(f % (S5 * S6))) %
((S5 * S6) * S4)) /
(S5 * S6)) +
(((((((((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)) / (S5 * S6)) +
((f / ((S5 * S6) * 640)) * S4)) *
S5) *
S6) +
(f % (S5 * S6))) /
((S5 * S6) * S4)) *
S4)) *
S5) *
S6) +
(f % (S5 * S6)));
EXPECT_EQ(
q.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
((((((((S7 * 1024) + S8) + (S9 * 4096)) / ((S5 * S6) * 640)) * S5) * S6) *
S4) +
(((((S7 * 1024) + S8) + (S9 * 4096)) % ((S5 * S6) * 640)) %
((S5 * S6) * S4))));
EXPECT_EQ(q1.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)));
EXPECT_EQ(q2.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
f % (S5 * S6));
EXPECT_EQ(q3.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
Expr(S4));
EXPECT_EQ(q4.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2), Expr(0));
EXPECT_EQ(q5.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
(((((f / ((S5 * S6) * 640)) * S4) * S5) * S6) +
((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4))));
}
TEST_F(TestIndexExpr, CommonFactor) {
ir::Var S0 = ir::Var("S0");
ir::Var S1 = ir::Var("S1");
ir::Var S2 = ir::Var("S2");
ir::Var S3 = ir::Var("S3");
ir::Var S4 = ir::Var("S4");
ir::Var S5 = ir::Var("S5");
ir::Var S6 = ir::Var("S6");
ir::Var S7 = ir::Var("S7");
ir::Var S8 = ir::Var("S8");
ir::Var S9 = ir::Var("S9");
ir::Var S13 = ir::Var("S13");
ir::Var S17 = ir::Var("S17");
ir::Var S21 = ir::Var("S21");
ir::Var tx = ir::Var("tx");
ir::Var bx = ir::Var("bx");
ir::Expr q = ((((((((S1 + S13) + S17) + S21) + S5) + S9)) * S2) * S3);
ir::Expr q1 = (((((((S3 * S5) * S2) + ((S3 * S9) * S2)) + ((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1));
ir::Expr q2 =
(((((((((f * 1024) + tx) + (bx * 4096)) %
((((((((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S3) *
S5) *
S2) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S3) *
S9) *
S2)) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S3) *
S21) *
S2)) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S2) *
S3) *
S17)) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S2) *
S3) *
S13)) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S2) *
S3) *
S1))) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) + ((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
(((((S1 + S13) + S17) + S21) + S5) + S9)) *
S2) *
S3) +
((((f * 1024) + tx) + (bx * 4096)) %
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) + ((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))));
EXPECT_EQ(q.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
(((((((S1 + S13) + S17) + S21) + S5) + S9) * S2) * S3));
EXPECT_EQ(q1.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
(((((((S5 + S9) + S21) + S17) + S13) + S1) * S2) * S3));
EXPECT_EQ(
q2.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
((((f * 1024) + tx) + (bx * 4096)) %
((((((((((((((((S5 + S9) + S21) + S17) + S13) + S1) * S2) * S3) * S0) /
4096) *
4096) +
(((((((S5 + S9) + S21) + S17) + S13) + S1) * S2) * S3)) +
4095) /
(((((((S5 + S9) + S21) + S17) + S13) + S1) * S2) * S3)) *
S3) *
S2) *
(((((S5 + S9) + S21) + S17) + S13) + S1))));
}
TEST_F(TestIndexExpr, TestCheckPattern) {
ir::Var a = ir::Var("a");
ir::Var b = ir::Var("b");
ir::Var f = ir::Var("f");
ir::Var S0 = ir::Var("S0");
ir::Var S1 = ir::Var("S1");
ir::Var S2 = ir::Var("S2");
ir::Var S3 = ir::Var("S3");
ir::Var S4 = ir::Var("S4");
ir::Var S5 = ir::Var("S5");
ir::Var S6 = ir::Var("S6");
ir::Var S7 = ir::Var("S7");
ir::Var S8 = ir::Var("S8");
ir::Var S9 = ir::Var("S9");
ir::IndexExpr pattern = f / (a * b) * b + f % (a * b) / a;
ir::IndexExpr pattern1 = f / (a * b) * a + f % (a * b) / b;
ir::IndexExpr e = (S0 * (S1 + S2) + S1 * S2 + S2) / (S4 * S5) * S5 +
(S0 * (S1 + S2) + S1 * S2 + S2) % (S4 * S5) / S4;
ir::IndexExpr e1 = (S0 * (S1 + S2) + S1 * S2 + S2) / (S4 * S5) * S4 +
(S0 * (S1 + S2) + S1 * S2 + S2) % (S4 * S5) / S5;
std::unordered_map<std::string, ir::IndexExpr> map;
EXPECT_TRUE(CheckPattern(e, pattern, &map));
map.clear();
EXPECT_FALSE(CheckPattern(e, pattern1, &map));
map.clear();
EXPECT_FALSE(CheckPattern(e1, pattern, &map));
map.clear();
EXPECT_TRUE(CheckPattern(e1, pattern1, &map));
}
TEST_F(TestIndexExpr, ParseExpression) {
ir::Var a = ir::Var("a");
ir::Var b = ir::Var("b");
ir::Var a1 = ir::Var("a_1");
ir::Var b2 = ir::Var("b2");
ir::Expr e1 = a + b;
ir::Expr e2 = a - b;
ir::Expr e3 = a * b;
ir::Expr e4 = a / b;
ir::Expr e5 = a % b;
ir::Expr e6 = a + ir::Expr(20);
ir::Expr e7 = a - ir::Expr(10);
ir::Expr e8 = ir::Expr(5) * b;
ir::Expr e9 = ir::Expr(20) / b;
ir::Expr e10 = a % ir::Expr(3) + b;
ir::Expr e11 = (a + b) * (a - b);
ir::Expr e12 = (a + (b * a)) - (b / a);
ir::Expr e13 = (a + b) * (a - b) + (a / b) - (b % a);
ir::Expr e14 = a1 + b2;
ir::Expr e15 = a + b;
EXPECT_EQ(e1, ParseExpressionFromString("a + b"));
EXPECT_EQ(e2, ParseExpressionFromString("a - b"));
EXPECT_EQ(e3, ParseExpressionFromString("a * b"));
EXPECT_EQ(e4, ParseExpressionFromString("a / b"));
EXPECT_EQ(e5, ParseExpressionFromString("a % b"));
EXPECT_EQ(e6, ParseExpressionFromString("a + 20"));
EXPECT_EQ(e7, ParseExpressionFromString("a - 10"));
EXPECT_EQ(e8, ParseExpressionFromString("5 * b"));
EXPECT_EQ(e9, ParseExpressionFromString("20 / b"));
EXPECT_EQ(e10, ParseExpressionFromString("a % 3 + b"));
EXPECT_EQ(e11, ParseExpressionFromString("(a + b) * (a - b)"));
EXPECT_EQ(e12, ParseExpressionFromString("(a + (b * a)) - (b / a)"));
EXPECT_EQ(e13,
ParseExpressionFromString("(a + b) * (a - b) + (a / b) - (b % a)"));
EXPECT_EQ(e14, ParseExpressionFromString("a_1 + b2"));
EXPECT_EQ(e15, ParseExpressionFromString(" a + b "));
EXPECT_ANY_THROW(ParseExpressionFromString("a + #"));
EXPECT_ANY_THROW(ParseExpressionFromString("(a + b"));
EXPECT_ANY_THROW(ParseExpressionFromString(""));
}
TEST_F(TestIndexExpr, MatchPattern) {
ir::Var a = ir::Var("a");
ir::Var b = ir::Var("b");
ir::Var x = ir::Var("x");
ir::Var y = ir::Var("y");
ir::IndexExpr expr1 = a + b;
ir::IndexExpr expr2 = a * b;
ir::IndexExpr expr3 = a + (b * 10);
ir::IndexExpr expr4 = (a + b) * 10;
ir::IndexExpr expr5 = x + y;
ir::IndexExpr expr6 = x * y;
auto result1 = MatchPattern(expr1, "a + b", nullptr);
EXPECT_TRUE(result1.has_value());
EXPECT_EQ(result1->at("a"), a);
EXPECT_EQ(result1->at("b"), b);
auto result2 = MatchPattern(expr3, "a + (b * 10)", nullptr);
EXPECT_TRUE(result2.has_value());
EXPECT_EQ(result2->at("a"), a);
EXPECT_EQ(result2->at("b"), b);
auto result3 = MatchPattern(expr1, "a * b", nullptr);
EXPECT_FALSE(result3.has_value());
auto result4 = MatchPattern(expr3, "a + (b * 20)", nullptr);
EXPECT_FALSE(result4.has_value());
auto condition =
[](const std::unordered_map<std::string, ir::IndexExpr> &map) {
return map.at("a") == Expr(ir::Var("a")) &&
map.at("b") == Expr(ir::Var("b"));
};
auto result5 = MatchPattern(expr1, "a + b", condition);
EXPECT_TRUE(result5.has_value());
auto condition2 =
[](const std::unordered_map<std::string, ir::IndexExpr> &map) {
return map.at("a") == ir::Var("x") && map.at("b") == ir::Var("y");
};
auto result6 = MatchPattern(expr1, "a + b", condition2);
EXPECT_FALSE(result6.has_value());
auto result7 = MatchPattern(expr4, "(a + b) * 10", nullptr);
EXPECT_TRUE(result7.has_value());
EXPECT_EQ(result7->at("a"), a);
EXPECT_EQ(result7->at("b"), b);
auto result8 = MatchPattern(expr1, "x + y", nullptr);
EXPECT_TRUE(result8.has_value());
EXPECT_EQ(result8->at("x"), a);
EXPECT_EQ(result8->at("y"), b);
auto result9 = MatchPattern(expr6, "x * y", nullptr);
EXPECT_TRUE(result9.has_value());
EXPECT_EQ(result9->at("x"), x);
EXPECT_EQ(result9->at("y"), y);
}
TEST_F(TestIndexExpr, BoundSimplify) {
ir::Var S0 = ir::Var("S0");
ir::Var i = ir::Var(ir::Expr(0), ir::Expr(5), "i"); // i ∈ [0, 5)
ir::Var j = ir::Var(ir::Expr(0), S0, "j"); // j ∈ [0, S0)
ir::Expr q0 = i / Expr(5);
ir::Expr q1 = i / Expr(4);
ir::Expr q2 = i / Expr(6);
ir::Expr q3 = j / S0;
ir::Expr q4 = j / (S0 - 1);
ir::Expr q5 = j / (S0 + 1);
ir::Expr q6 = i % Expr(5);
ir::Expr q7 = i % Expr(4);
ir::Expr q8 = i % Expr(6);
ir::Expr q9 = j % S0;
ir::Expr q10 = j % (S0 - 1);
ir::Expr q11 = j % (S0 + 1);
EXPECT_EQ(q0.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
ir::Expr(0));
EXPECT_EQ(q1.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
i / Expr(4));
EXPECT_EQ(q2.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
ir::Expr(0));
EXPECT_EQ(q3.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
ir::Expr(0));
EXPECT_EQ(q4.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
j / (S0 + ir::Expr(-1)));
EXPECT_EQ(q5.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
ir::Expr(0));
EXPECT_EQ(q6.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3), i);
EXPECT_EQ(q7.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
i % Expr(4));
EXPECT_EQ(q8.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3), i);
EXPECT_EQ(q9.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3), j);
EXPECT_EQ(q10.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
j % (S0 + ir::Expr(-1)));
EXPECT_EQ(q11.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3), j);
}
} // namespace common
} // namespace cinn
+518
View File
@@ -0,0 +1,518 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include "paddle/cinn/common/integer_set.h"
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/common/simplify_special_pattern.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/schedule/schedule_base.h"
namespace cinn {
namespace common {
#define ITER_MARK_VAR(var) \
ir::IterMark::Make(ir::IndexExpr(var.ptr()), var->upper_bound)
#define ITER_MARK_SUM(sum, ext) ir::IterMark::Make(sum, ext)
#define ITER_SPLIT(mark, ...) ir::IterSplit::Make(mark, ##__VA_ARGS__)
#define ITER_SUM(...) ir::IterSum::Make({__VA_ARGS__}, ir::IndexExpr(0))
#define ITER_SUM_WITH_BASE(base, ...) ir::IterSum::Make({__VA_ARGS__}, base)
#define TEST_EXPR(expr, expected, expr_norm) \
rewriter.Rewrite(&expr); \
EXPECT_EQ(expr, Expr(expected)); \
normalizer.Convert(&expr); \
EXPECT_EQ(expr, expr_norm);
class TestIterSimplify : public ::testing::Test {
public:
void SetUp() override {
i = ir::Var(ir::Expr(0), ir::Expr(2), "i").set_index(1);
j = ir::Var(ir::Expr(0), ir::Expr(4), "j").set_index(1);
k = ir::Var(ir::Expr(0), ir::Expr(8), "k").set_index(1);
i_j_k_fused =
ir::Var(ir::Expr(0), ir::Expr(64), "i_j_k_fused").set_index(1);
var_intervals = {
{"i", CasInterval(i->lower_bound, i->upper_bound - ir::Expr(1))},
{"j", CasInterval(j->lower_bound, j->upper_bound - ir::Expr(1))},
{"k", CasInterval(k->lower_bound, k->upper_bound - ir::Expr(1))},
{"i_j_k_fused",
CasInterval(i_j_k_fused->lower_bound,
i_j_k_fused->upper_bound - ir::Expr(1))}};
};
ir::Var i;
ir::Var j;
ir::Var k;
ir::Var i_j_k_fused;
cas_intervals_t var_intervals;
SymbolicExprAnalyzer analyzer{var_intervals};
};
TEST_F(TestIterSimplify, IterExprMake) {
// IterMark Make func.
auto mark_expr = ITER_MARK_VAR(i);
auto mark_expr_ = ITER_MARK_VAR(j);
// IterSplit Make func.
auto split_0_expr = ITER_SPLIT(mark_expr);
auto split_1_expr = ITER_SPLIT(mark_expr, ir::IndexExpr(1));
auto split_2_expr = ITER_SPLIT(
mark_expr, ir::IndexExpr(1), ir::IndexExpr(2), ir::IndexExpr(1));
auto split_3_expr = ITER_SPLIT(
mark_expr, ir::IndexExpr(2), ir::IndexExpr(2), ir::IndexExpr(1));
auto split_4_expr = ITER_SPLIT(
mark_expr_, ir::IndexExpr(1), ir::IndexExpr(2), ir::IndexExpr(1));
// IterSum Make func.
auto sum_expr = ITER_SUM(split_0_expr, split_1_expr, split_2_expr);
auto mark = mark_expr.As<ir::IterMark>();
auto split_0 = split_0_expr.As<ir::IterSplit>();
auto split_1 = split_1_expr.As<ir::IterSplit>();
auto split_2 = split_2_expr.As<ir::IterSplit>();
auto sum = sum_expr.As<ir::IterSum>();
EXPECT_EQ(mark->source, ir::IndexExpr(i.ptr()));
EXPECT_EQ(mark->extent, ir::IndexExpr(2));
EXPECT_EQ(split_0->source, mark_expr);
EXPECT_EQ(split_0->lower_factor, ir::IndexExpr(1));
EXPECT_EQ(split_0->extent, ir::IndexExpr(2));
EXPECT_EQ(split_0->scale, ir::IndexExpr(1));
EXPECT_EQ(split_1->source, mark_expr);
EXPECT_EQ(split_1->lower_factor, ir::IndexExpr(1));
EXPECT_EQ(split_1->extent, ir::IndexExpr(2));
EXPECT_EQ(split_1->scale, ir::IndexExpr(1));
EXPECT_EQ(split_2->source, mark_expr);
EXPECT_EQ(split_2->lower_factor, ir::IndexExpr(1));
EXPECT_EQ(split_2->extent, ir::IndexExpr(2));
EXPECT_EQ(split_2->scale, ir::IndexExpr(1));
EXPECT_EQ(sum->args.size(), 3);
EXPECT_EQ(sum->base, Expr(0));
EXPECT_NE(mark_expr, mark_expr_);
EXPECT_EQ(split_0_expr, split_1_expr);
EXPECT_EQ(split_1_expr, split_2_expr);
EXPECT_NE(split_2_expr, split_3_expr);
}
TEST_F(TestIterSimplify, conversion) {
IterMapRewriter rewriter{{i}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
ir::Expr e1 = i;
auto gt = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i)));
TEST_EXPR(e1, gt, e1);
}
TEST_F(TestIterSimplify, add) {
IterMapRewriter rewriter{{i, j, k}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 =
ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i)), ITER_SPLIT(ITER_MARK_VAR(j)));
auto gt2 = ITER_SUM_WITH_BASE(ir::IndexExpr(5),
ITER_SPLIT(ITER_MARK_VAR(i)),
ITER_SPLIT(ITER_MARK_VAR(j)),
ITER_SPLIT(ITER_MARK_VAR(k)));
auto gt3 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(2)));
auto gt4 = ITER_SUM_WITH_BASE(ir::IndexExpr(12));
ir::Expr e1 = i + j;
ir::Expr e2 = i + j + k + 5;
ir::Expr e3 = i + i;
ir::Expr e4 = Expr(7) + Expr(5);
TEST_EXPR(e1, gt1, i + j);
TEST_EXPR(e2, gt2, i + j + k + 5);
TEST_EXPR(e3, gt3, i * 2);
TEST_EXPR(e4, gt4, Expr(12));
}
TEST_F(TestIterSimplify, sub) {
IterMapRewriter rewriter{{i, j, k}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i)),
ITER_SPLIT(ITER_MARK_VAR(j), ir::IndexExpr(-1)));
auto gt2 =
ITER_SUM_WITH_BASE(ir::IndexExpr(5),
ITER_SPLIT(ITER_MARK_VAR(i)),
ITER_SPLIT(ITER_MARK_VAR(j)),
ITER_SPLIT(ITER_MARK_VAR(k), ir::IndexExpr(-1)));
auto gt3 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(0)));
auto gt4 = ITER_SUM_WITH_BASE(ir::IndexExpr(2));
ir::Expr e1 = i - j;
ir::Expr e2 = i + j - k + 5;
ir::Expr e3 = i - i;
ir::Expr e4 = Expr(7) - Expr(5);
TEST_EXPR(e1, gt1, (j * -1) + i);
TEST_EXPR(e2, gt2, i + j + (k * -1) + 5);
TEST_EXPR(e3, gt3, Expr(0));
TEST_EXPR(e4, gt4, Expr(2));
}
TEST_F(TestIterSimplify, mul) {
IterMapRewriter rewriter{{i, j, k}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(j)));
auto gt2 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(j), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(k)));
auto gt3 = ITER_SUM_WITH_BASE(ir::IndexExpr(10),
ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(j), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(k)));
auto gt4 = ITER_SUM_WITH_BASE(ir::IndexExpr(35));
ir::Expr e1 = i * 2 + j;
ir::Expr e2 = (i + j) * 2 + k;
ir::Expr e3 = (i + j + 5) * 2 + k;
ir::Expr e4 = Expr(7) * Expr(5);
TEST_EXPR(e1, gt1, i * 2 + j);
TEST_EXPR(e2, gt2, (i + j) * 2 + k);
TEST_EXPR(e3, gt3, (i + j) * 2 + k + 10);
TEST_EXPR(e4, gt4, Expr(35));
}
TEST_F(TestIterSimplify, div) {
IterMapRewriter rewriter{{i, j, k, i_j_k_fused}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(8),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt2 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(32),
ir::IndexExpr(2),
ir::IndexExpr(1)));
auto gt3 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused)));
auto gt4 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused), ir::IndexExpr(2)));
auto gt5 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(2),
ir::IndexExpr(32),
ir::IndexExpr(1)));
auto gt6 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(8),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(72)),
ir::IndexExpr(16),
ir::IndexExpr(5),
ir::IndexExpr(1)));
auto gt7 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(1),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(65)),
ir::IndexExpr(2),
ir::IndexExpr(33),
ir::IndexExpr(1)));
auto gt8 = ITER_SUM_WITH_BASE(ir::IndexExpr(2),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(8),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt9 = ITER_SUM_WITH_BASE(
ir::IndexExpr(2),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused), ir::IndexExpr(2)));
auto gt10 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(1),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(65)),
ir::IndexExpr(8),
ir::IndexExpr(9),
ir::IndexExpr(1)));
auto gt11 = ITER_SUM_WITH_BASE(ir::IndexExpr(3));
auto gt12 = ITER_SUM_WITH_BASE(ir::IndexExpr(3));
auto gt13 = ITER_SUM_WITH_BASE(ir::IndexExpr(15));
auto gt14 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
ir::Expr e1 = i_j_k_fused / 8;
ir::Expr e2 = i_j_k_fused / 8 / 4;
ir::Expr e3 = i_j_k_fused / 1;
ir::Expr e4 = i_j_k_fused * 16 / 8;
ir::Expr e5 = i_j_k_fused * 8 / 16;
ir::Expr e6 = (i_j_k_fused + 8) / 16;
ir::Expr e7 = (i_j_k_fused * 8 + 8) / 16;
ir::Expr e8 = (i_j_k_fused + 16) / 8;
ir::Expr e9 = (i_j_k_fused * 16 + 16) / 8;
ir::Expr e10 = (i_j_k_fused + 1) / 8;
ir::Expr e11 = Expr(15) / Expr(5);
ir::Expr e12 = Expr(15) / Expr(4);
ir::Expr e13 = Expr(15) / Expr(1);
ir::Expr e14 = Expr(0) / Expr(4);
TEST_EXPR(e1, gt1, i_j_k_fused / 8);
TEST_EXPR(e2, gt2, i_j_k_fused / 32);
TEST_EXPR(e3, gt3, i_j_k_fused);
TEST_EXPR(e4, gt4, i_j_k_fused * 2);
TEST_EXPR(e5, gt5, i_j_k_fused / 2);
TEST_EXPR(e6, gt6, (i_j_k_fused + 8) / 16);
TEST_EXPR(e7, gt7, (i_j_k_fused + 1) / 2);
TEST_EXPR(e8, gt8, i_j_k_fused / 8 + 2);
TEST_EXPR(e9, gt9, i_j_k_fused * 2 + 2);
TEST_EXPR(e10, gt10, (i_j_k_fused + 1) / 8);
TEST_EXPR(e11, gt11, Expr(3));
TEST_EXPR(e12, gt12, Expr(3));
TEST_EXPR(e13, gt13, Expr(15));
TEST_EXPR(e14, gt14, Expr(0));
}
TEST_F(TestIterSimplify, mod) {
IterMapRewriter rewriter{{i, j, k, i_j_k_fused}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt2 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(8),
ir::IndexExpr(4),
ir::IndexExpr(1)));
auto gt3 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt4 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt5 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(2),
ir::IndexExpr(8)));
auto gt6 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(8),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(72)),
ir::IndexExpr(1),
ir::IndexExpr(16),
ir::IndexExpr(1)));
auto gt7 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(1),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(64),
ir::IndexExpr(1))),
ir::IndexExpr(65)),
ir::IndexExpr(1),
ir::IndexExpr(2),
ir::IndexExpr(8)));
auto gt8 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt9 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt10 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(1),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(65)),
ir::IndexExpr(1),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt11 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt12 = ITER_SUM_WITH_BASE(ir::IndexExpr(3));
auto gt13 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt14 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
ir::Expr e1 = i_j_k_fused % 8;
ir::Expr e2 = i_j_k_fused / 8 % 4;
ir::Expr e3 = i_j_k_fused % 1;
ir::Expr e4 = i_j_k_fused * 16 % 8;
ir::Expr e5 = i_j_k_fused * 8 % 16;
ir::Expr e6 = (i_j_k_fused + 8) % 16;
ir::Expr e7 = (i_j_k_fused * 8 + 8) % 16;
ir::Expr e8 = (i_j_k_fused + 16) % 8;
ir::Expr e9 = (i_j_k_fused * 16 + 16) % 8;
ir::Expr e10 = (i_j_k_fused + 1) % 8;
ir::Expr e11 = Expr(15) % Expr(5);
ir::Expr e12 = Expr(15) % Expr(4);
ir::Expr e13 = Expr(15) % Expr(1);
ir::Expr e14 = Expr(0) % Expr(4);
TEST_EXPR(e1, gt1, i_j_k_fused % 8);
TEST_EXPR(e2, gt2, i_j_k_fused % 32 / 8);
TEST_EXPR(e3, gt3, Expr(0));
TEST_EXPR(e4, gt4, Expr(0));
TEST_EXPR(e5, gt5, i_j_k_fused % 2 * 8);
TEST_EXPR(e6, gt6, (i_j_k_fused + 8) % 16);
TEST_EXPR(e7, gt7, (i_j_k_fused + 1) % 2 * 8);
TEST_EXPR(e8, gt8, i_j_k_fused % 8);
TEST_EXPR(e9, gt9, Expr(0));
TEST_EXPR(e10, gt10, (i_j_k_fused + 1) % 8);
TEST_EXPR(e11, gt11, Expr(0));
TEST_EXPR(e12, gt12, Expr(3));
TEST_EXPR(e13, gt13, Expr(0));
TEST_EXPR(e14, gt14, Expr(0));
}
TEST_F(TestIterSimplify, fuse_not_same_source) {
IterMapRewriter rewriter{{i, j, k, i_j_k_fused}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(32)),
ITER_SPLIT(ITER_MARK_VAR(j), ir::IndexExpr(8)),
ITER_SPLIT(ITER_MARK_VAR(k), ir::IndexExpr(1))),
ir::IndexExpr(64)),
ir::IndexExpr(8),
ir::IndexExpr(8),
ir::IndexExpr(1)));
ir::Expr e1 = (i * 32 + j * 8 + k) / 8;
ir::Expr e2 = (i * 32 + j * 7) / 8;
TEST_EXPR(e1, gt1, (i * 32 + j * 8 + k) / 8);
EXPECT_ANY_THROW(rewriter.Rewrite(&e2));
}
TEST_F(TestIterSimplify, fuse_same_source) {
IterMapRewriter rewriter{{i, j, k, i_j_k_fused}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(32),
ir::IndexExpr(2),
ir::IndexExpr(1)));
auto gt2 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(8),
ir::IndexExpr(4),
ir::IndexExpr(1)));
auto gt3 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt4 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(32),
ir::IndexExpr(2),
ir::IndexExpr(1)),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(32),
ir::IndexExpr(1),
ir::IndexExpr(1)));
ir::Expr e1 = (i_j_k_fused / 16 / 2 * 32 + i_j_k_fused / 16 % 2 * 16 +
i_j_k_fused % 16) /
8 / 4;
ir::Expr e2 =
(i_j_k_fused / 32 * 32 + i_j_k_fused / 16 % 2 * 16 + i_j_k_fused % 16) /
8 % 4;
ir::Expr e3 =
(i_j_k_fused / 32 * 32 + i_j_k_fused / 16 % 2 * 16 + i_j_k_fused % 16) %
8;
ir::Expr e4 =
((i_j_k_fused / 16) / 2) +
((((i_j_k_fused % 16) / 8) + (2 * ((i_j_k_fused / 16) % 2))) / 4);
ir::Expr e5 = (((i_j_k_fused % 16) / 8) + ((4 * ((i_j_k_fused / 16) / 2)) +
(2 * ((i_j_k_fused / 16) % 2)))) %
4;
ir::Expr e6 = ((i_j_k_fused % 16) + ((32 * ((i_j_k_fused / 16) / 2)) +
(16 * ((i_j_k_fused / 16) % 2)))) %
8;
TEST_EXPR(e1, gt1, i_j_k_fused / 32);
TEST_EXPR(e2, gt2, i_j_k_fused % 32 / 8);
TEST_EXPR(e3, gt3, i_j_k_fused % 8);
TEST_EXPR(e4, gt4, i_j_k_fused / 32);
TEST_EXPR(e5, gt2, i_j_k_fused % 32 / 8);
TEST_EXPR(e6, gt3, i_j_k_fused % 8);
}
TEST_F(TestIterSimplify, SimplifyBindings) {
std::vector<ir::Var> block_vars;
std::vector<ir::Expr> iter_values;
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
// Create block vars and axis vars
for (int i = 0; i < shape.size(); ++i) {
block_vars.push_back(ir::Var(Expr(0),
shape[i],
cinn::UniqName("b" + std::to_string(i)),
false,
false)
.set_index(1));
axis_vars[i]->is_reduce_axis = false;
iter_values.push_back(axis_vars[i]);
}
// Create ScheduleBlock body
ir::Expr body_ = ir::ScheduleBlockRealize::Make(
iter_values,
ir::ScheduleBlock::Make(block_vars, {}, {}, "Test", Expr(0)));
// Create For loops
auto body = body_;
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
// Create outer ScheduleBlockRealize
ir::Expr body_outer = ir::ScheduleBlockRealize::Make(
{}, ir::ScheduleBlock::Make({}, {}, {}, "test1", body));
// Create ir schedule
ir::ModuleExpr mod_expr({ir::Block::Make({body_outer})});
ir::IRSchedule ir_sch(mod_expr);
std::vector<ir::Expr> loops = ir_sch.GetLoops(body_);
// Apply Fuse and Split
ir::Expr loop_fuse = ir_sch.Fuse(loops);
std::vector<ir::Expr> loops_split = ir_sch.Split(loop_fuse, {2, 2, 16});
ir::Expr loop_fuse_2 = ir_sch.Fuse(loops_split);
// Apply SimplifyBindings
SimplifyBlockBinding::SimplifyBindings(loop_fuse_2, {}, analyzer);
// Check result
auto for_op = loop_fuse_2.As<ir::For>();
auto simplified_values = for_op->body.As<ir::Block>()
->stmts[0]
.As<ir::ScheduleBlockRealize>()
->iter_values;
auto f = for_op->loop_var;
EXPECT_EQ(simplified_values[0], f / 32);
EXPECT_EQ(simplified_values[1], f % 32 / 8);
EXPECT_EQ(simplified_values[2], f % 8);
}
TEST_F(TestIterSimplify, MergeMulMod) {
auto S0 = ir::Var(ir::Expr(0), ir::Expr(4), "S0").set_index(1);
auto S1 = ir::Var(ir::Expr(0), ir::Expr(256), "S1").set_index(1);
auto S2 = ir::Var(ir::Expr(0), ir::Expr(13), "S2").set_index(1);
auto e1 = ((((((((S0 * 256) + S1) + (S2 * 1024)) / 2500) * 50) +
(((((S0 * 256) + S1) + (S2 * 1024)) % 2500) / 50)) *
50) +
((((S0 * 256) + S1) + (S2 * 1024)) % 50));
auto e2 = ((((((S0 * 256) + S1) + (S2 * 1024)) / 2500) + -4) * 2500) +
((((S0 * 256) + S1) + (S2 * 1024)) % 2500);
auto e3 = (S1 / 784 * 28 + S1 % 784 / 28) * 28 + S1 % 28;
EXPECT_EQ(MergeMulMod(e1), (((S0 * 256) + S1) + (S2 * 1024)));
EXPECT_EQ(MergeMulMod(e2), ((((S0 * 256) + S1) + (S2 * 1024)) + -10000));
EXPECT_EQ(MergeMulMod(e3), S1);
}
} // namespace common
} // namespace cinn
+119
View File
@@ -0,0 +1,119 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sstream>
#include "paddle/cinn/adt/generate_map_expr.h"
#include "paddle/cinn/adt/map_expr_ctx.h"
#include "paddle/cinn/adt/print.h"
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/manual_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/runtime/flags.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/shape/ir/shape_dialect.h"
#include "paddle/pir/include/dialect/shape/ir/shape_op.h"
#include "paddle/pir/include/dialect/shape/transforms/shape_optimization_pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "test/cpp/pir/tools/test_pir_utils.h"
PD_DECLARE_bool(cinn_enable_map_expr);
PD_DECLARE_bool(cinn_enable_map_expr_dynamic_shape);
PD_DECLARE_bool(cinn_enable_map_expr_index_detail);
namespace {
std::string Trim(const std::string& doc) {
std::stringstream oss{doc};
std::stringstream ret{};
std::string str;
while (oss >> str) {
std::size_t size = str.size();
for (; size > 0 && str[size - 1] == ' '; --size) {
}
ret << str.substr(0, size) << std::endl;
}
return ret.str();
}
} // namespace
TEST(MapExpr, ElementWise_Fusion_0) {
cinn::adt::UniqueId::ResetSeqNumber(0);
::pir::IrContext* ctx = ::pir::IrContext::Instance();
::pir::Program program(ctx);
ctx->GetOrRegisterDialect<pir::shape::ShapeDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
phi::DDim dims_D_2 = {-1, 1};
pir::Value value1 =
test::CreateDenseTensorOp(ctx, dims_D_2, {"op1_attr"}, {"op1_name"})
->result(0);
pir::Value value2 =
test::CreateDenseTensorOp(ctx, dims_D_2, {"op2_attr"}, {"op2_name"})
->result(0);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
builder.Build<paddle::dialect::SubtractOp>(
value1, builder.Build<paddle::dialect::ExpOp>(value2).result(0));
::pir::PassManager pass_manager(ctx);
// TODO(@jiahy0825): use CreateShapeOptimizationPass() instead of
// CreateInferSymbolicShapePass() which is a fake pass
/*
pass_manager.AddPass(::pir::CreateInferSymbolicShapePass(shape_analysis));
pass_manager.Run(&program);
std::vector<pir::Operation*> vec_op;
for (auto& op : *program.block()) {
vec_op.push_back(&op);
}
auto res = cinn::dialect::ir::OpFusionPassInternal(vec_op);
ASSERT_EQ(res.size(), 1u);
ASSERT_EQ(res[0]->ops.size(), program.block()->size());
auto group_list = cinn::dialect::ir::GeneralFusionMergePassInternal(res);
ASSERT_EQ(group_list.size(), 1u);
FLAGS_cinn_enable_map_expr = true;
FLAGS_cinn_enable_map_expr_dynamic_shape = true;
FLAGS_cinn_enable_map_expr_index_detail = true;
auto group = group_list.at(0);
group->shape_analysis = shape_analysis;
cinn::adt::TryGenerateMapExprFromGroup(group);
std::string map_expr_str =
cinn::adt::ToTxtString(group->map_expr_ctx().map_expr(), "MapExprTest");
std::string target_str = R"TEST(
MapExprTest(t_var_2, t_var_1) {
AnchoredMapStmt(t_var_0) {
MapStmt([i_59, i_60]) {
exp(
&t_var[IndexDot([BI(i_59, sym_17), 0], [sym_17, 1])],
t_var_1[IndexDot([BI(i_59, sym_17), 0], [sym_17, 1])]);
subtract(
&t_var_0[IndexDot([i_59, i_60], [sym_17, 1])],
t_var_2[IndexDot([BI(i_59, sym_17), 0], [sym_17, 1])],
t_var[IndexDot([BI(i_59, sym_17), 0], [sym_17, 1])]);
}
}
}
)TEST";
ASSERT_EQ(Trim(map_expr_str), Trim(target_str));
*/
}
@@ -0,0 +1,144 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sstream>
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/optim/ir_simplify.h"
#include "paddle/cinn/optim/merge_block_utils.h"
namespace cinn {
namespace optim {
namespace {
bool IsBlockForAllEqual(const ForTreeNode& first, const ForTreeNode& second) {
auto ForVarExtentEqual = [&](const ForTreeNode& first,
const ForTreeNode& second) -> bool {
const ir::Expr lhs = first.val->extent();
const ir::Expr rhs = second.val->extent();
if (lhs != rhs) {
return false;
}
return true;
};
if (!ForVarExtentEqual(first, second)) return false;
if (first.children.size() != second.children.size()) return false;
for (size_t i = 0; i < first.children.size(); ++i) {
if (!IsBlockForAllEqual(first.children[i], second.children[i])) {
return false;
}
}
return true;
}
ir::stmt::For MakeForLoops(const std::vector<int> extents, int index) {
ir::stmt::StmtRef body_stmt;
if (index == extents.size() - 1) {
body_stmt = ir::stmt::Schedule(
std::vector<Var>(),
std::vector<Expr>(),
std::vector<Expr>(),
std::vector<Expr>(),
"block",
ir::stmt::BlockRef(std::vector<ir::stmt::StmtRef>()));
} else {
body_stmt = MakeForLoops(extents, index + 1);
}
std::vector<ir::stmt::StmtRef> body = {body_stmt};
return ir::stmt::For(ir::Var("i"),
ir::Expr(0),
ir::Expr(extents[index]),
ir::ForType::Serial,
ir::DeviceAPI::CUDA,
ir::stmt::BlockRef(body),
ir::VectorizeInfo(),
ir::BindInfo());
}
void TestHelper(const std::vector<int>& extents1,
const std::vector<int>& extents2,
bool is_same) {
auto for_loop1 = MakeForLoops(extents1, 0);
auto for_loop2 = MakeForLoops(extents2, 0);
if (is_same) {
EXPECT_TRUE(CanMergeBlocks(for_loop1, for_loop2, IsBlockForAllEqual));
} else {
EXPECT_FALSE(CanMergeBlocks(for_loop1, for_loop2, IsBlockForAllEqual));
}
}
void TestHelper2(const std::vector<std::vector<int>>& extents1,
const std::vector<std::vector<int>>& extents2,
bool is_same) {
auto MakeNestLoops =
[&](const std::vector<std::vector<int>>& extents) -> ir::stmt::For {
std::vector<ir::stmt::StmtRef> for_loops;
for (size_t i = 0; i < extents.size(); ++i) {
for_loops.push_back(MakeForLoops(extents[i], 0));
}
ir::stmt::BlockRef block(for_loops);
ir::stmt::For for_stmt = ir::stmt::For(ir::Var("i"),
ir::Expr(0),
ir::Expr(1),
ir::ForType::Serial,
ir::DeviceAPI::CUDA,
block,
ir::VectorizeInfo(),
ir::BindInfo());
return for_stmt;
};
auto for_stmt1 = MakeNestLoops(extents1);
auto for_stmt2 = MakeNestLoops(extents2);
if (is_same) {
EXPECT_TRUE(CanMergeBlocks(for_stmt1, for_stmt2, IsBlockForAllEqual));
} else {
EXPECT_FALSE(CanMergeBlocks(for_stmt1, for_stmt2, IsBlockForAllEqual));
}
}
TEST(ForInfo, ForInfoEqual) {
TestHelper({10}, {10}, true);
TestHelper({10, 5}, {10, 5}, true);
TestHelper({10, 5, 3}, {10, 5, 3}, true);
TestHelper2({{10}, {10}}, {{10}, {10}}, true);
TestHelper2({{10, 5}, {4, 7}}, {{10, 5}, {4, 7}}, true);
TestHelper2(
{{10, 5, 3}, {4, 7, 9}, {2, 8}}, {{10, 5, 3}, {4, 7, 9}, {2, 8}}, true);
}
TEST(ForInfo, ForInfoNotEqual) {
TestHelper({10}, {9}, false);
TestHelper({10, 5}, {10, 4}, false);
TestHelper({10, 5, 3}, {10, 5, 2}, false);
TestHelper2({{10}, {10}}, {{10}, {9}}, false);
TestHelper2({{10, 5}, {4, 7}}, {{10, 5}, {4, 3}}, false);
TestHelper2(
{{10, 5, 3}, {4, 7, 9}, {2, 8}}, {{10, 5, 3}, {4, 7, 9}, {2, 7}}, false);
}
} // namespace
} // namespace optim
} // namespace cinn
+121
View File
@@ -0,0 +1,121 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_attribute.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/framework/pir/compilation_task.h"
#include "paddle/cinn/hlir/framework/pir/utils.h"
#include "paddle/cinn/hlir/framework/pir_compiler.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
using cinn::hlir::framework::pir::CompatibleInfo;
using cinn::hlir::framework::pir::OpLoweringGroup;
using cinn::hlir::framework::pir::OpLoweringGroupPtr;
using ProgramInfo = std::tuple<std::shared_ptr<::pir::Program>,
std::vector<OpLoweringGroupPtr>>;
ProgramInfo BuildProgram(std::vector<int64_t> input_shape) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const float value_one = 1.0;
auto full_op_x = builder.Build<paddle::dialect::FullOp>(
input_shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace());
std::vector<OpLoweringGroupPtr> groups;
const std::string fn_name = CompatibleInfo::GroupOpsName(
std::initializer_list<::pir::Operation*>({full_op_x.operation()}));
groups.emplace_back(std::make_shared<OpLoweringGroup>(
std::initializer_list<::pir::Operation*>({full_op_x.operation()}),
fn_name));
groups.back()->mut_output_ops().insert(full_op_x.operation());
return {program, groups};
}
// TODO(LiuYang): This test is temporarily
// TEST(CompilationTask, Basic) {
// auto prog_info = BuildProgram({4096, 128});
// std::shared_ptr<::pir::Program> program = std::get<0>(prog_info);
// LOG(INFO) << program->block()->size();
// EXPECT_EQ(program->block()->size(), 1u);
// std::stringstream ss;
// program->Print(ss);
// LOG(INFO) << ss.str();
// auto target = cinn::common::DefaultNVGPUTarget();
// auto scope = std::make_shared<cinn::hlir::framework::Scope>();
// std::vector<GroupPtr> groups = std::get<1>(prog_info);
// CHECK_EQ(groups.size(), 1);
// cinn::hlir::framework::GroupCompilationContext compilation_context(
// target, groups[0], scope);
// cinn::hlir::framework::CompilationTask
// compilation_task(&compilation_context); compilation_task.Lowering();
// LOG(INFO) << compilation_context.PrintPredicate2Funcs();
// compilation_task.CodegenAndJit();
// auto instruction = compilation_task.BuildInstruction();
// }
// TEST(CompilationTask, CompileGroup) {
// // Step 1: Construct pir::Program
// int M = 4096, N = 128;
// auto prog_info = BuildProgram({M, N});
// std::shared_ptr<::pir::Program> program = std::get<0>(prog_info);
// LOG(INFO) << program->block()->size();
// EXPECT_EQ(program->block()->size(), 1u);
// std::stringstream ss;
// program->Print(ss);
// LOG(INFO) << ss.str();
// auto target = cinn::common::DefaultNVGPUTarget();
// auto scope = std::make_shared<cinn::hlir::framework::Scope>();
// std::vector<GroupPtr> groups = std::get<1>(prog_info);
// CHECK_EQ(groups.size(), 1);
// cinn::hlir::framework::PirCompiler ir_compiler(*program, target, scope);
// auto runtime_program = ir_compiler.Build(groups);
// // Step 3: Execute Runtime Instruction and check Scope.
// ASSERT_NO_THROW(runtime_program->Execute());
// for (auto& var_name : scope->var_names()) {
// std::string name = {var_name.begin(), var_name.end()};
// int64_t numel = scope->GetTensor(name)->shape().numel();
// ASSERT_EQ(numel, M * N);
// std::vector<float> data =
// cinn::GetTensorData<float>(scope->GetTensor(name), target);
// for (int i = 0; i < numel; ++i) {
// ASSERT_EQ(data[i], 1.0);
// }
// }
// }
+137
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/pd_to_cinn_pass.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/drr/include/drr_pattern_base.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
auto sum_op =
builder.Build<paddle::dialect::SumOp>(full_input_op.result(0),
std::vector<int64_t>({-1}),
phi::DataType::FLOAT32,
true);
auto relu_op = builder.Build<paddle::dialect::ReluOp>(sum_op.result(0));
auto exp_op = builder.Build<paddle::dialect::ExpOp>(sum_op.result(0));
}
void BuildProgramMax(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
auto max_op = builder.Build<paddle::dialect::MaxOp>(
full_input_op.result(0), std::vector<int64_t>({-1}), true);
auto relu_op = builder.Build<paddle::dialect::ReluOp>(max_op.result(0));
auto exp_op = builder.Build<paddle::dialect::ExpOp>(max_op.result(0));
}
TEST(DrrTest, reduce_sum) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreatePdOpToCinnOpPass());
pm.Run(&program);
auto it = program.block()->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::FullOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<cinn::dialect::ReduceSumOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"cinn::dialect::ReduceSumOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::ReluOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::ReluOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::ExpOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::ExpOp, but it is not."));
}
TEST(DrrTest, reduce_max) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgramMax(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreatePdOpToCinnOpPass());
pm.Run(&program);
auto it = program.block()->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::FullOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<cinn::dialect::ReduceMaxOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"cinn::dialect::ReduceMaxOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::ReluOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::ReluOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::ExpOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::ExpOp, but it is not."));
}
@@ -0,0 +1,192 @@
// Copyright (c) 2025 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/eliminate_common_factor_of_local_index.h"
#include <gtest/gtest.h>
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/utils/ir_nodes_collector.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace optim {
/*
{
serial for (i_0, 0, 32) {
serial for (j_0, 0, 4) {
var_local[((i_0 * 3) + j_0), ((j_0 * 32) / 128)] =
var_global_in[i_0, ((j_0 * 32) / 128)]
var_global_out[((i_0 * 3) + j_0), j_0] =
var_local[((i_0 * 3) + j_0), ((j_0 * 32) / 128)]
}
}
}
*/
TEST(EliminateCommonFactorOfLocalIndex, SimplifyLocalIndex) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape = {ir::Expr(128), ir::Expr(128)};
// const std::vector<ir::Expr> indices = {ir::Expr(0)};
ir::Tensor var_global_in =
ir::_Tensor_::Make("var_global_in", ir::Float(32), shape, shape);
var_global_in->WithBuffer("global", "var_global_in_buffer");
ir::Tensor var_local_tensor =
ir::_Tensor_::Make("var_local", ir::Float(32), shape, shape);
var_local_tensor->WithBuffer("local", "var_local_buffer");
ir::Tensor var_global_out =
ir::_Tensor_::Make("var_global_out", ir::Float(32), shape, shape);
var_global_out->WithBuffer("global", "var_global_out_buffer");
ir::Var var_i_0 = ir::Var(ir::Expr(0), ir::Expr(32), "i_0");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(4), "j_0");
std::vector<ir::Expr> block_contents = {
ir::Store::Make(
var_local_tensor,
ir::Load::Make(var_global_in, {var_i_0, (var_j_0 * 32) / 128}),
{var_i_0 * 3 + var_j_0, (var_j_0 * 32) / 128}),
ir::Store::Make(
var_global_out,
ir::Load::Make(var_local_tensor,
{var_i_0 * 3 + var_j_0, (var_j_0 * 32) / 128}),
{var_i_0 * 3 + var_j_0, var_j_0})};
ir::Expr inner_loop_body = ir::Block::Make(block_contents);
std::vector<ir::Expr> j_loop_contents = {inner_loop_body};
ir::Expr j_loop = ir::For::Make(var_j_0,
ir::Expr(0),
ir::Expr(4),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make(j_loop_contents));
std::vector<ir::Expr> i_loop_contents = {j_loop};
ir::Expr loop_body = ir::For::Make(var_i_0,
ir::Expr(0),
ir::Expr(32),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make(i_loop_contents));
std::vector<ir::Expr> ij_loop_contents = {loop_body};
ir::Expr expr = ir::Block::Make(ij_loop_contents);
ir::stmt::BlockRef block = ir::ConvertExprBlockToStmtBlock(expr);
VLOG(6) << "Before EliminateCommonFactorOfLocalIndex: " << block;
EliminateCommonFactorOfLocalIndex(block);
VLOG(6) << "After EliminateCommonFactorOfLocalIndex: " << block;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i_0, 0, 32) {
serial for (j_0, 0, 4) {
var_local[i_0, j_0] = var_global_in[i_0, ((j_0 * 32) / 128)]
var_global_out[((i_0 * 3) + j_0), j_0] = var_local[i_0, j_0]
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(block), utils::Trim(expected_ir));
}
/*
{
serial for (i_0, 0, 32) {
serial for (j_0, 0, 4) {
var_local[(i_0 * 3), ((j_0 * 32) / 128)] =
var_global_in[i_0, ((j_0 * 32) / 128)]
var_global_out[((i_0 * 3) + j_0), j_0] =
var_local[(i_0 * 3), ((j_0 * 32) / 128)]
}
}
}
*/
TEST(EliminateCommonFactorOfLocalIndex, SimplifyLocalIndexWithZeroIndex) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape = {ir::Expr(128), ir::Expr(128)};
// const std::vector<ir::Expr> indices = {ir::Expr(0)};
ir::Tensor var_global_in =
ir::_Tensor_::Make("var_global_in", ir::Float(32), shape, shape);
var_global_in->WithBuffer("global", "var_global_in_buffer");
ir::Tensor var_local_tensor =
ir::_Tensor_::Make("var_local", ir::Float(32), shape, shape);
var_local_tensor->WithBuffer("local", "var_local_buffer");
ir::Tensor var_global_out =
ir::_Tensor_::Make("var_global_out", ir::Float(32), shape, shape);
var_global_out->WithBuffer("global", "var_global_out_buffer");
ir::Var var_i_0 = ir::Var(ir::Expr(0), ir::Expr(32), "i_0");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(4), "j_0");
std::vector<ir::Expr> block_contents = {
ir::Store::Make(
var_local_tensor,
ir::Load::Make(var_global_in, {var_i_0, var_j_0 * 32 / 128}),
{var_i_0 * 3, var_j_0 * 32 / 128}),
ir::Store::Make(
var_global_out,
ir::Load::Make(var_local_tensor, {var_i_0 * 3, var_j_0 * 32 / 128}),
{var_i_0 * 3 + var_j_0, var_j_0})};
ir::Expr inner_loop_body = ir::Block::Make(block_contents);
std::vector<ir::Expr> j_loop_contents = {inner_loop_body};
ir::Expr j_loop = ir::For::Make(var_j_0,
ir::Expr(0),
ir::Expr(4),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make(j_loop_contents));
std::vector<ir::Expr> i_loop_contents = {j_loop};
ir::Expr loop_body = ir::For::Make(var_i_0,
ir::Expr(0),
ir::Expr(32),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make(i_loop_contents));
std::vector<ir::Expr> ij_loop_contents = {loop_body};
ir::Expr expr = ir::Block::Make(ij_loop_contents);
ir::stmt::BlockRef block = ir::ConvertExprBlockToStmtBlock(expr);
VLOG(6) << "Before EliminateCommonFactorOfLocalIndex: " << block;
EliminateCommonFactorOfLocalIndex(block);
VLOG(6) << "After EliminateCommonFactorOfLocalIndex: " << block;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i_0, 0, 32) {
serial for (j_0, 0, 4) {
var_local[0, i_0] = var_global_in[i_0, ((j_0 * 32) / 128)]
var_global_out[((i_0 * 3) + j_0), j_0] = var_local[0, i_0]
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(block), utils::Trim(expected_ir));
}
} // namespace optim
} // namespace cinn
+170
View File
@@ -0,0 +1,170 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/ir/group_schedule/config/database.h"
#include "paddle/cinn/ir/group_schedule/config/file_database.h"
#include "paddle/cinn/ir/group_schedule/config/group_tile_config.h"
#include "paddle/cinn/ir/group_schedule/search/config_searcher.h"
#include "paddle/cinn/ir/group_schedule/search/measurer.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/performance_statistician.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
COMMON_DECLARE_bool(print_ir);
PD_DECLARE_string(cinn_tile_config_filename_label);
#define MKDIR(path) mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
bool PathExists(const std::string& path) {
struct stat statbuf;
if (stat(path.c_str(), &statbuf) != -1) {
if (S_ISDIR(statbuf.st_mode)) {
return true;
}
}
return false;
}
void RemoveDir(const cinn::common::Target target,
const cinn::ir::IterSpaceType& iter_space_type) {
std::string dirname = "";
std::string filename = "";
for (auto i : iter_space_type) {
dirname += i.first;
dirname += "_";
filename += i.first + i.second;
filename += "_";
}
const std::string kDirSuffix = "_EREBE";
dirname = dirname.substr(0, dirname.size() - 1) + kDirSuffix;
filename = filename.substr(0, filename.size() - 1);
auto removedir = [](const std::string& test_path,
const std::string& file_name) {
if (PathExists(test_path)) {
std::string full_test_name = test_path + file_name;
std::remove(full_test_name.c_str());
LOG(INFO) << "File exist.";
} else {
LOG(INFO) << "File doesn't exist.";
}
};
std::string root_path = FLAGS_cinn_tile_config_filename_label;
const std::string kTestFileDir = "./tile_file_test/";
if (root_path == "") {
root_path = kTestFileDir;
}
std::string target_str = target.arch_str() + "_" + target.device_name_str();
std::string file_name = "/" + filename + ".json";
removedir(root_path + target_str + "/" + dirname, file_name);
LOG(INFO) << "Dump_path "
<< root_path + target_str + "/" + dirname + " has been removed";
}
TEST(ConfigSearcher, TestReduceDemo) {
constexpr int kThreadsPerWarp = 32;
constexpr int kMaxThreadsPerBlock = 1024;
// Step 1: Construct iter space and tile config.
cinn::ir::BucketInfo bucket_info;
int s_dimension_lower = 13;
int s_dimension_upper = 13;
auto s_dimension_type = "S";
auto s_dimension_is_dynamic = true;
int r_dimension_lower = 4096;
int r_dimension_upper = 4096;
auto r_dimension_type = "R";
auto r_dimension_is_dynamic = true;
bucket_info.space.push_back(
cinn::ir::BucketInfo::Dimension{s_dimension_lower,
s_dimension_upper,
s_dimension_type,
s_dimension_is_dynamic});
bucket_info.space.push_back(
cinn::ir::BucketInfo::Dimension{r_dimension_lower,
r_dimension_upper,
r_dimension_type,
r_dimension_is_dynamic});
cinn::ir::ScheduleConfig::TileConfig tile_config;
tile_config.spatial_inner_num = 9;
tile_config.warp_num = 14;
tile_config.tree_reduce_num = 512;
// Use kTestFileDir in this test.
const std::string prev_flag = FLAGS_cinn_tile_config_filename_label;
const std::string kTestFileDir = "./tile_file_test/";
FLAGS_cinn_tile_config_filename_label = kTestFileDir;
std::vector<std::pair<std::string, std::string>> iter_space_type = {
std::make_pair(s_dimension_type,
s_dimension_is_dynamic == true ? "dynamic" : "static"),
std::make_pair(r_dimension_type,
r_dimension_is_dynamic == true ? "dynamic" : "static")};
// If test file has been created, remove it.
RemoveDir(cinn::common::DefaultTarget(), iter_space_type);
// Step 2: Add to json / Read from json
cinn::ir::FileTileConfigDatabase file_database;
file_database.AddConfig(
cinn::common::DefaultTarget(), bucket_info, tile_config, 2);
cinn::ir::TileConfigMap tile_config_map =
file_database.GetConfigs(cinn::common::DefaultTarget(), iter_space_type);
// Delete the file
RemoveDir(cinn::common::DefaultTarget(), iter_space_type);
// Check the correctness
for (auto& it : tile_config_map) {
LOG(INFO) << "bucket info is: ";
auto dims = it.first.space.size();
for (int i = 0; i < dims; i++) {
LOG(INFO) << "Dimension " << i
<< " 's lower_bound is: " << it.first.space[i].lower_bound;
LOG(INFO) << "Dimension " << i
<< " 's upper_bound is: " << it.first.space[i].upper_bound;
auto dimension_lower = i == 0 ? s_dimension_lower : r_dimension_lower;
auto dimension_upper = i == 0 ? s_dimension_upper : r_dimension_upper;
PADDLE_ENFORCE_EQ(it.first.space[i].lower_bound,
dimension_lower,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong dimension_lower"));
PADDLE_ENFORCE_EQ(it.first.space[i].upper_bound,
dimension_upper,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong dimension_upper"));
}
LOG(INFO) << "tile config is " << it.second.spatial_inner_num << " "
<< it.second.warp_num << " " << it.second.tree_reduce_num;
PADDLE_ENFORCE_EQ(it.second.spatial_inner_num,
tile_config.spatial_inner_num,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong spatial_inner_num"));
PADDLE_ENFORCE_EQ(it.second.warp_num,
tile_config.warp_num,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong warp_num"));
PADDLE_ENFORCE_EQ(it.second.tree_reduce_num,
tile_config.tree_reduce_num,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong tree_reduce_num"));
}
// Restore the previous flag
FLAGS_cinn_tile_config_filename_label = prev_flag;
}
@@ -0,0 +1,93 @@
// Copyright (c) 2023 PaddlePaddle 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 "gtest/gtest.h"
#include "paddle/cinn/hlir/dialect/operator/ir/generate_shape_util.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_builder.h"
#include "test/cpp/pir/tools/test_pir_utils.h"
namespace cinn::dialect {
using namespace symbol; // NOLINT
namespace {
DimExpr CreateExampleDimExpr() {
DimExprBuilder dim_expr_builder;
DimExpr sym0 = DimExpr("S0");
DimExpr sym1 = DimExpr("S1");
DimExpr constant = DimExpr(2);
DimExpr expr1 = (sym0 - sym1) * constant / sym0;
DimExpr expr2 = dim_expr_builder.Max(expr1, sym0);
DimExpr output = dim_expr_builder.Min(expr2, sym1);
return output;
}
} // namespace
TEST(DimExprUtil, Convert) {
pir::IrContext* ctx = pir::IrContext::Instance();
DimExpr dim_expr = CreateExampleDimExpr();
::pir::Attribute attr = ConvertDimExprToAttribute(ctx, dim_expr);
std::optional<DimExpr> opt_expr = ConvertAttributeToDimExpr(attr);
ASSERT_TRUE(opt_expr.has_value());
ASSERT_EQ(opt_expr.value(), dim_expr);
}
TEST(DimExprUtil, Substitute) {
DimExpr dim_expr = CreateExampleDimExpr();
const auto& substituted_expr = SubstituteDimExpr(
dim_expr, [](const std::string& str) -> std::optional<DimExpr> {
if (str == "S0") {
return DimExpr("symbol0");
} else if (str == "S1") {
return DimExpr("symbol1");
} else {
return std::nullopt;
}
});
const auto& ret_expr = SubstituteDimExpr(
substituted_expr, [](const std::string& str) -> std::optional<DimExpr> {
if (str == "symbol0") {
return DimExpr("S0");
} else if (str == "symbol1") {
return DimExpr("S1");
} else {
return std::nullopt;
}
});
ASSERT_EQ(ret_expr, dim_expr);
}
TEST(DimExprUtil, MakeGetterDimExpr4SymbolName) {
cinn::dialect::GenerateShapeOp::SymbolBindings symbol_bindings{};
using ShapeSymbolBinding = cinn::dialect::GenerateShapeOp::ShapeSymbolBinding;
symbol_bindings.emplace_back(ShapeSymbolBinding{"Symbol", 0, 0});
const auto& dim_expr = CreateExampleDimExpr();
const auto& shape_or_data_dim_exprs = symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({dim_expr}));
const auto& DimExpr4SymbolName = MakeGetterDimExpr4SymbolName(
symbol_bindings,
[&](int in_tensor_idx) -> const symbol::ShapeOrDataDimExprs& {
return shape_or_data_dim_exprs;
});
const auto& opt_dim_expr = DimExpr4SymbolName("Symbol");
ASSERT_TRUE(opt_dim_expr.has_value());
ASSERT_EQ(opt_dim_expr.value(), dim_expr);
}
} // namespace cinn::dialect
+197
View File
@@ -0,0 +1,197 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/manual_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_attribute.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/cinn_group_cluster_pass.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/build_cinn_pass.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
std::vector<pir::Value> BuildInput(
::pir::Builder* builder,
const std::vector<std::vector<int64_t>>& vec_shapes) {
std::vector<pir::Value> vec_res;
for (size_t i = 0; i < vec_shapes.size(); ++i) {
auto op = builder->Build<paddle::dialect::FullOp>(
vec_shapes[i], 1.0, phi::DataType::FLOAT32, phi::CPUPlace());
vec_res.push_back(op.result(0));
}
return vec_res;
}
TEST(IROpFusionPass, ElementWise_Fusion_0) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
::pir::Program program(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
int h = 32, w = 32;
auto inputs = BuildInput(&builder, {{h, w}, {h, w}, {h, w}});
auto e =
builder.Build<paddle::dialect::AddOp>(inputs[0], inputs[1]).result(0);
auto f = builder.Build<paddle::dialect::AddOp>(e, inputs[2]).result(0);
auto out1 = builder.Build<paddle::dialect::AddOp>(f, inputs[2]).result(0);
builder.Build<paddle::dialect::FetchOp>(out1, "out1", 0);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Fatal("Pass manager run failed."));
PADDLE_ENFORCE_EQ(program.block()->size(),
2u,
common::errors::PreconditionNotMet(
"The number of FusionOp does not meet expectations。"));
}
// Real test 0
TEST(IROpFusionPass, Broadcast_Test_0) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
::pir::Program program(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
int h = 32, w = 32;
auto inputs = BuildInput(&builder, {{w}, {w}, {h, w}, {h, w}});
auto e =
builder.Build<paddle::dialect::AddOp>(inputs[0], inputs[1]).result(0);
auto f =
builder.Build<paddle::dialect::AddOp>(inputs[2], inputs[3]).result(0);
std::vector<int64_t> axes{1};
std::vector<int64_t> out_shape{h, w};
auto e1 =
builder.Build<cinn::dialect::BroadcastOp>(e, axes, out_shape).result(0);
auto out1 = builder.Build<paddle::dialect::AddOp>(e1, f).result(0);
builder.Build<paddle::dialect::FetchOp>(out1, "out1", 0);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Fatal("Pass manager run failed."));
PADDLE_ENFORCE_EQ(program.block()->size(),
2u,
common::errors::PreconditionNotMet(
"The number of FusionOp does not meet expectations。"));
}
// Real test 1
TEST(IROpFusionPass, Broadcast_Test_1) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
::pir::Program program(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
int h = 32, w = 32;
auto inputs = BuildInput(&builder, {{w}, {w}, {w}, {h, w}});
auto e =
builder.Build<paddle::dialect::AddOp>(inputs[0], inputs[1]).result(0);
auto out1 = builder.Build<paddle::dialect::AddOp>(inputs[2], e).result(0);
std::vector<int64_t> axes{1};
std::vector<int64_t> out_shape{h, w};
auto e1 =
builder.Build<cinn::dialect::BroadcastOp>(e, axes, out_shape).result(0);
auto out2 = builder.Build<paddle::dialect::AddOp>(inputs[3], e1).result(0);
builder.Build<paddle::dialect::FetchOp>(out1, "out1", 0);
builder.Build<paddle::dialect::FetchOp>(out2, "out2", 1);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Fatal("Pass manager run failed."));
PADDLE_ENFORCE_EQ(program.block()->size(),
3u,
common::errors::PreconditionNotMet(
"The number of FusionOp does not meet expectations。"));
}
TEST(IROpFusionPass, softmax) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
::pir::Program program(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
auto inputs = BuildInput(&builder, {{128, 128, 768}});
std::vector<int64_t> axes{-1};
auto x = inputs[0];
auto max = builder.Build<cinn::dialect::ReduceMaxOp>(x, axes, true).result(0);
auto broadcast_1 = builder
.Build<cinn::dialect::BroadcastOp>(
max,
std::vector<int64_t>({0, 1, 2}),
std::vector<int64_t>({128, 128, 768}))
.result(0);
auto sub =
builder.Build<paddle::dialect::SubtractOp>(x, broadcast_1).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder.Build<cinn::dialect::ReduceSumOp>(exp, axes, true).result(0);
auto broadcast_2 = builder
.Build<cinn::dialect::BroadcastOp>(
sum,
std::vector<int64_t>({0, 1, 2}),
std::vector<int64_t>({128, 128, 768}))
.result(0);
auto divide =
builder.Build<paddle::dialect::DivideOp>(exp, broadcast_2).result(0);
builder.Build<paddle::dialect::FetchOp>(divide, "out1", 0);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Fatal("Pass manager run failed."));
PADDLE_ENFORCE_EQ(program.block()->size(),
2u,
common::errors::PreconditionNotMet(
"The number of FusionOp does not meet expectations。"));
}
+191
View File
@@ -0,0 +1,191 @@
// Copyright (c) 2025 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/ir_simplify.h"
#include <gtest/gtest.h>
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_base.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/utils/ir_nodes_collector.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace optim {
/*
i_j_fused: [0ll, 524288ll)
j_0: [0, 128)
Before Normalize:
(j_0 % 128)
After Normalize:
j_0
*/
TEST(IRSimplifyBound, SimplifyMod) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variable
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Final expression
ir::Expr expr = ir::Mod::Make(var_j_0, ir::Expr(128));
VLOG(6) << "Before Simplify: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Simplify: " << res;
// Expected output verification
std::string expected_ir = R"ROC(j_0)ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
/*
i_j_fused: [0ll, 524288ll)
j_0: [0, 128)
Before Normalize:
(j_0 / 128)
After Normalize:
0
*/
TEST(IRSimplifyBound, SimplifyDiv) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variable
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Final expression
ir::Expr expr = ir::Div::Make(var_j_0, ir::Expr(128));
VLOG(6) << "Before Normalize: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Normalize: " << res;
// Expected output verification
std::string expected_ir = R"ROC(0)ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
/*
i_j_fused: [0ll, 524288ll)
j_0: [0, 128)
Before Normalize:
((((i_j_fused % 16) * 128) + j_0) / 128)
After Normalize:
(i_j_fused % 16)
*/
TEST(IRSimplifyBound, SimplifyLinearDiv) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variables
ir::Var var_i_j_fused = ir::Var(ir::Expr(0), ir::Expr(524288), "i_j_fused");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Final expression
ir::Expr expr = ir::Div::Make(
ir::Add::Make(ir::Mul::Make(ir::Mod::Make(var_i_j_fused, ir::Expr(16)),
ir::Expr(128)),
var_j_0),
ir::Expr(128));
VLOG(6) << "Before Normalize: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Normalize: " << res;
// Expected output verification
std::string expected_ir = R"ROC((i_j_fused % 16))ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
/*
i_j_fused: [0ll, 524288ll)
j_0: [0, 128)
Before Normalize:
((((i_j_fused % 16) * 128) + j_0) % 128)
After Normalize:
j_0
*/
TEST(IRSimplifyBound, SimplifyLinearMod) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variables
ir::Var var_i_j_fused = ir::Var(ir::Expr(0), ir::Expr(524288), "i_j_fused");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Final expression
ir::Expr expr = ir::Mod::Make(
ir::Add::Make(ir::Mul::Make(ir::Mod::Make(var_i_j_fused, ir::Expr(16)),
ir::Expr(128)),
var_j_0),
ir::Expr(128));
VLOG(6) << "Before Normalize: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Normalize: " << res;
// Expected output verification
std::string expected_ir = R"ROC(j_0)ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
/*
loop_var_2: [0, 32)
loop_var_3: [0, 4)
Before Normalize:
(((loop_var_3 * 32ll) + loop_var_2) / 128ll)
After Normalize:
0
*/
TEST(IRSimplifyBound, SimplifyLinearDiv2) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variables
ir::Var loop_var_2 = ir::Var(ir::Expr(0), ir::Expr(32), "loop_var_2");
ir::Var loop_var_3 = ir::Var(ir::Expr(0), ir::Expr(4), "loop_var_3");
// Final expression
ir::Expr expr = ir::Div::Make(
ir::Add::Make(ir::Mul::Make(loop_var_3, ir::Expr(32)), loop_var_2),
ir::Expr(128));
VLOG(6) << "Before Normalize: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Normalize: " << res;
// Expected output verification
std::string expected_ir = R"ROC(0)ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
} // namespace optim
} // namespace cinn
@@ -0,0 +1,336 @@
// Copyright (c) 2025 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/ir_simplify.h"
#include <gtest/gtest.h>
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/utils/ir_nodes_collector.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace optim {
/*
serial for (i, 0ll, 32768ll) {
serial for (j, 0ll, 16ll) {
serial for (reduce_k_0, 0ll, 128ll) {
var_18[i, j] = select((var_18[i, j] > var_17[i, j, reduce_k_0]),
var_18[i, j], var_17[i, j, reduce_k_0])
}
}
}
}
*/
TEST(IRSimplifySelect, SimplifySelectToMax) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape_2d = {ir::Expr(32768), ir::Expr(16)};
const std::vector<ir::Expr> shape_3d = {
ir::Expr(32768), ir::Expr(16), ir::Expr(128)};
ir::Tensor var_17 =
ir::_Tensor_::Make("var_17", ir::Float(32), shape_3d, shape_3d);
var_17->WithBuffer("global", "var_17_buffer");
ir::Tensor var_18 =
ir::_Tensor_::Make("var_18", ir::Float(32), shape_2d, shape_2d);
var_18->WithBuffer("global", "var_18_buffer");
// Define loop variables
ir::Var var_i = ir::Var(ir::Expr(0), ir::Expr(32768), "i");
ir::Var var_j = ir::Var(ir::Expr(0), ir::Expr(16), "j");
ir::Var var_reduce_k_0 = ir::Var(ir::Expr(0), ir::Expr(128), "reduce_k_0");
// Create innermost reduction loop body
ir::Expr reduce_body = ir::Store::Make(
var_18,
ir::Select::Make(
ir::GT::Make(ir::Load::Make(var_18, {var_i, var_j}),
ir::Load::Make(var_17, {var_i, var_j, var_reduce_k_0})),
ir::Load::Make(var_18, {var_i, var_j}),
ir::Load::Make(var_17, {var_i, var_j, var_reduce_k_0})),
{var_i, var_j});
// Create reduction loop
ir::Expr reduce_loop = ir::For::Make(var_reduce_k_0,
ir::Expr(0),
ir::Expr(128),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({reduce_body}));
// Create j loop
ir::Expr j_loop = ir::For::Make(var_j,
ir::Expr(0),
ir::Expr(16),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({reduce_loop}));
// Create i loop
ir::Expr i_loop = ir::For::Make(var_i,
ir::Expr(0),
ir::Expr(32768),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_loop}));
// Final expression
ir::Expr expr = ir::Block::Make({i_loop});
VLOG(6) << "Before Simplify: " << expr;
Simplify(&expr);
VLOG(6) << "After Simplify: " << expr;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i, 0, 32768)
{
serial for (j, 0, 16)
{
serial for (reduce_k_0, 0, 128)
{
var_18[i, j] = cinn_max(var_17[i, j, reduce_k_0], var_18[i, j])
}
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(expr), utils::Trim(expected_ir));
}
/*
serial for (i, 0ll, 32768ll) {
serial for (j, 0ll, 16ll) {
serial for (reduce_k_0, 0ll, 128ll) {
var_18[i, j] = select((var_18[i, j] < var_17[i, j, reduce_k_0]),
var_18[i, j], var_17[i, j, reduce_k_0])
}
}
}
}
*/
TEST(IRSimplifySelect, SimplifySelectToMin) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape_2d = {ir::Expr(32768), ir::Expr(16)};
const std::vector<ir::Expr> shape_3d = {
ir::Expr(32768), ir::Expr(16), ir::Expr(128)};
ir::Tensor var_17 =
ir::_Tensor_::Make("var_17", ir::Float(32), shape_3d, shape_3d);
var_17->WithBuffer("global", "var_17_buffer");
ir::Tensor var_18 =
ir::_Tensor_::Make("var_18", ir::Float(32), shape_2d, shape_2d);
var_18->WithBuffer("global", "var_18_buffer");
// Define loop variables
ir::Var var_i = ir::Var(ir::Expr(0), ir::Expr(32768), "i");
ir::Var var_j = ir::Var(ir::Expr(0), ir::Expr(16), "j");
ir::Var var_reduce_k_0 = ir::Var(ir::Expr(0), ir::Expr(128), "reduce_k_0");
// Create innermost reduction loop body
ir::Expr reduce_body = ir::Store::Make(
var_18,
ir::Select::Make(
ir::LT::Make(ir::Load::Make(var_18, {var_i, var_j}),
ir::Load::Make(var_17, {var_i, var_j, var_reduce_k_0})),
ir::Load::Make(var_18, {var_i, var_j}),
ir::Load::Make(var_17, {var_i, var_j, var_reduce_k_0})),
{var_i, var_j});
// Create reduction loop
ir::Expr reduce_loop = ir::For::Make(var_reduce_k_0,
ir::Expr(0),
ir::Expr(128),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({reduce_body}));
// Create j loop
ir::Expr j_loop = ir::For::Make(var_j,
ir::Expr(0),
ir::Expr(16),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({reduce_loop}));
// Create i loop
ir::Expr i_loop = ir::For::Make(var_i,
ir::Expr(0),
ir::Expr(32768),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_loop}));
// Final expression
ir::Expr expr = ir::Block::Make({i_loop});
VLOG(6) << "Before Simplify: " << expr;
Simplify(&expr);
VLOG(6) << "After Simplify: " << expr;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i, 0, 32768)
{
serial for (j, 0, 16)
{
serial for (reduce_k_0, 0, 128)
{
var_18[i, j] = cinn_min(var_18[i, j], var_17[i, j, reduce_k_0])
}
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(expr), utils::Trim(expected_ir));
}
/*
serial for (i, 0ll, 32768ll)
{
serial for (j, 0, 16)
{
serial for (j_0, 0, 128)
{
var_45[i, j, j_0)] = select(
(var_18[i, ((((j * 128ll) + j_0) / 128ll) + 0ll)] <=
float32(3.4028234663852886e+38)),
select(
(var_18[i, ((((j * 128ll) + j_0) / 128ll) + 0ll)] >=
float32(9.9999997473787516e-05)),
var_18[i, ((((j * 128ll) + j_0) / 128ll) + 0ll)],
float32(9.9999997473787516e-05)
),
float32(3.4028234663852886e+38)
)
}
}
}
*/
TEST(IRSimplifySelect, SimplifySelectToMinMax) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape_2d = {ir::Expr(32768), ir::Expr(16)};
const std::vector<ir::Expr> shape_3d = {
ir::Expr(32768), ir::Expr(16), ir::Expr(128)};
ir::Tensor var_18 =
ir::_Tensor_::Make("var_18", ir::Float(32), shape_2d, shape_2d);
var_18->WithBuffer("global", "var_18_buffer");
ir::Tensor var_45 =
ir::_Tensor_::Make("var_45", ir::Float(32), shape_3d, shape_3d);
var_45->WithBuffer("global", "var_45_buffer");
// Define loop variables
ir::Var var_i = ir::Var(ir::Expr(0), ir::Expr(32768), "i");
ir::Var var_j = ir::Var(ir::Expr(0), ir::Expr(16), "j");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Create innermost loop body
ir::Expr body = ir::Store::Make(
var_45,
ir::Select::Make(
ir::LE::Make(
ir::Load::Make(
var_18,
{var_i,
ir::Div::Make(
ir::Add::Make(ir::Mul::Make(var_j, ir::Expr(128)),
var_j_0),
ir::Expr(128))}),
ir::Expr(3.4028234663852886e+38f)),
ir::Select::Make(
ir::GE::Make(
ir::Load::Make(
var_18,
{var_i,
ir::Div::Make(
ir::Add::Make(ir::Mul::Make(var_j, ir::Expr(128)),
var_j_0),
ir::Expr(128))}),
ir::Expr(9.9999997473787516e-05f)),
ir::Load::Make(
var_18,
{var_i,
ir::Div::Make(
ir::Add::Make(ir::Mul::Make(var_j, ir::Expr(128)),
var_j_0),
ir::Expr(128))}),
ir::Expr(9.9999997473787516e-05f)),
ir::Expr(3.4028234663852886e+38f)),
{var_i, var_j, var_j_0});
// Create j_0 loop
ir::Expr j_0_loop = ir::For::Make(var_j_0,
ir::Expr(0),
ir::Expr(128),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
// Create j loop
ir::Expr j_loop = ir::For::Make(var_j,
ir::Expr(0),
ir::Expr(16),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_0_loop}));
// Create i loop
ir::Expr i_loop = ir::For::Make(var_i,
ir::Expr(0),
ir::Expr(32768),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_loop}));
// Final expression
ir::Expr expr = ir::Block::Make({i_loop});
VLOG(6) << "Before Simplify: " << expr;
Simplify(&expr);
VLOG(6) << "After Simplify: " << expr;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i, 0, 32768)
{
serial for (j, 0, 16)
{
serial for (j_0, 0, 128)
{
var_45[i, j, j_0] = cinn_min(cinn_max(var_18[i, (((j * 128) + j_0) / 128)], 9.99999975e-05f), 3.40282347e+38f)
}
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(expr), utils::Trim(expected_ir));
}
} // namespace optim
} // namespace cinn
+576
View File
@@ -0,0 +1,576 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/schedule_base.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/optim/if_fold_pass.h"
#include "paddle/cinn/pass/pass_manager.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace common {
#define MAKE_FUNC(body) \
std::vector<ir::Argument> args{ \
ir::Argument(ir::Var("A"), ir::Argument::IO::kInput), \
ir::Argument(ir::Var("B"), ir::Argument::IO::kOutput)}; \
auto new_func = \
ir::_LoweredFunc_::Make("test_func", args, ir::Block::Make({body}), {}); \
optim::StmtPassManager pass_manager; \
pass_manager.AddPass(optim::CreateIfFoldPass()); \
pass_manager.Run(new_func);
/*
* serial for (i, 0, 2)
* {
* serial for (j, 0, 4)
* {
* serial for (k, 0, 8)
* {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* }
* }
* }
* }
* }
* }
*/
TEST(IRSimplify, if_fold_correct_0) {
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))}))));
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
serial for (i, 0, 2)
{
serial for (j, 0, 4)
{
serial for (k, 0, 8)
{
if (((((i * 1024) + k) + (j * 256)) == 0)) {
int32 a = 1
}
}
}
}
}
)ROC"));
}
/*
* serial for (i, 0, 2)
* {
* serial for (j, 0, 4)
* {
* serial for (k, 0, 8)
* {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* int32 b = 1
* }
* }
* }
* }
* }
* }
*/
TEST(IRSimplify, if_fold_correct_1) {
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1)),
ir::Let::Make(ir::Var("b"), Expr(1))}))));
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
serial for (i, 0, 2)
{
serial for (j, 0, 4)
{
serial for (k, 0, 8)
{
if (((((i * 1024) + k) + (j * 256)) == 0)) {
int32 a = 1
int32 b = 1
}
}
}
}
}
)ROC"));
}
/*
* serial for (i, 0, 2)
* {
* serial for (j, 0, 4)
* {
* serial for (k, 0, 8)
* {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* int32 b = 1
* } else {
* int32 c = 1
* }
* }
* }
* }
* }
* }
*/
TEST(IRSimplify, if_fold_correct_2) {
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1)),
ir::Let::Make(ir::Var("b"), Expr(1))}),
ir::Block::Make({ir::Let::Make(ir::Var("c"), Expr(1))}))));
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
serial for (i, 0, 2)
{
serial for (j, 0, 4)
{
serial for (k, 0, 8)
{
if (((((i * 1024) + k) + (j * 256)) == 0)) {
int32 a = 1
int32 b = 1
} else {
int32 c = 1
}
}
}
}
}
)ROC"));
}
/*
* serial for (i, 0, 2)
* {
* serial for (j, 0, 4)
* {
* serial for (k, 0, 8)
* {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) <= 0)) {
* int32 a = 1
* }
* }
* }
* }
* }
* }
* }
*/
TEST(IRSimplify, if_fold_correct_3) {
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::IfThenElse::Make(
ir::LE::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))})))));
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
serial for (i, 0, 2)
{
serial for (j, 0, 4)
{
serial for (k, 0, 8)
{
if (((((i * 1024) + k) + (j * 256)) == 0)) {
if (((((256 * j) + ((1024 * i) + k)) % 56) <= 0)) {
int32 a = 1
}
}
}
}
}
}
)ROC"));
}
/*
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* }
* } else {
* int32 b = 1
* }
* }
*/
TEST(IRSimplify, if_fold_has_false_brh) {
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))})),
ir::Block::Make({ir::Let::Make(ir::Var("b"), Expr(1))})));
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
int32 a = 1
}
} else {
int32 b = 1
}
}
}
)ROC"));
}
/*
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) <= 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* }
* }
* }
*/
TEST(IRSimplify, if_fold_LE) {
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::LE::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))}))));
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) <= 0)) {
if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
int32 a = 1
}
}
}
}
)ROC"));
}
/*
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 2)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* }
* }
* }
*/
TEST(IRSimplify, if_fold_EQ_2) {
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(2)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))}))));
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 2)) {
if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
int32 a = 1
}
}
}
}
)ROC"));
}
/*
serial for (i_j_fused, 0ll, 524288ll)
{
serial for (j_0, 0, 128)
{
var_45[(i_j_fused / 16), (((i_j_fused % 16) * 128) + j_0)] =
pow(2.0f, ceil(log2((0.00223214296f * var_31[0]))))
}
}
*/
TEST(IRSimplifyPowerCeilLog2BitOpLdexpf, Base) {
Context::Global().ResetNameId();
/// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape_2d = {ir::Expr(32768), ir::Expr(16)};
const std::vector<ir::Expr> shape_3d = {ir::Expr(32768), ir::Expr(16)};
ir::Tensor var_31 =
ir::_Tensor_::Make("var_31", ir::Float(32), shape_2d, shape_2d);
var_31->WithBuffer("global", "var_31_buffer");
ir::Tensor var_45 =
ir::_Tensor_::Make("var_45", ir::Float(32), shape_3d, shape_3d);
var_45->WithBuffer("global", "var_45_buffer");
// Define loop variables
ir::Var var_i_j_fused = ir::Var(ir::Expr(0), ir::Expr(524288), "i_j_fused");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Create innermost loop body
ir::Expr body = ir::Store::Make(
var_45,
ir::Call::Make(
ir::Float(32), // Return type
"pow", // Intrinsic function name
{ir::Expr(2.0f),
ir::Call::Make(
ir::Float(32),
"ceil",
{ir::Call::Make(
ir::Float(32),
"log2",
{ir::Mul::Make(ir::Expr(0.00223214296f),
ir::Load::Make(var_31, {ir::Expr(0)}))},
{},
ir::CallType::Intrinsic)},
{},
ir::CallType::Intrinsic)},
{},
ir::CallType::Intrinsic),
{ir::Div::Make(var_i_j_fused, ir::Expr(16)),
ir::Add::Make(ir::Mul::Make(ir::Mod::Make(var_i_j_fused, ir::Expr(16)),
ir::Expr(128)),
var_j_0)});
// Create j_0 loop
ir::Expr j_0_loop = ir::For::Make(var_j_0,
ir::Expr(0),
ir::Expr(128),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
// Create i_j_fused loop
ir::Expr i_j_fused_loop = ir::For::Make(var_i_j_fused,
ir::Expr(0),
ir::Expr(524288),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_0_loop}));
// Final expression
ir::Expr expr = ir::Block::Make({i_j_fused_loop});
VLOG(6) << "Before Simplify: " << expr;
cinn::optim::Simplify(&expr);
VLOG(6) << "After Simplify: " << expr;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i_j_fused, 0, 524288)
{
serial for (j_0, 0, 128)
{
var_45[(i_j_fused / 16), (((i_j_fused % 16) * 128) + j_0)] = ldexpf(1.00000000f, ((bitwise_and(right_shift(__float_as_uint((0.00223214296f * var_31[0])), 23), 255) - 127) + select((((bitwise_and(right_shift(__float_as_uint((0.00223214296f * var_31[0])), 23), 255) - 127) != -127) and (bitwise_and(__float_as_uint((0.00223214296f * var_31[0])), 8388607) != 0)), 1, 0)))
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(expr), utils::Trim(expected_ir));
}
} // namespace common
} // namespace cinn
@@ -0,0 +1,111 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/fuse_parallel_matmul_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/pd_to_cinn_pass.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 0.5);
paddle::dialect::FullOp weight_1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 0.5);
paddle::dialect::FullOp weight_2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 64}, 0.5);
paddle::dialect::FullOp weight_3 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{32, 128}, 0.5);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_1.out());
paddle::dialect::MatmulOp matmul_op2 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_2.out());
paddle::dialect::MatmulOp matmul_op3 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_3.out());
builder.Build<paddle::dialect::FetchOp>(matmul_op1.out(), "x", 0);
builder.Build<paddle::dialect::FetchOp>(matmul_op2.out(), "y", 1);
builder.Build<paddle::dialect::FetchOp>(matmul_op3.out(), "z", 1);
}
TEST(Cinn, FuseMatmul) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
ASSERT_EQ((program.block()->size()), 10u);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateFuseParallelMatmulPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
ASSERT_EQ((pm.Run(&program)), true);
ASSERT_EQ((program.block()->size()), 20u);
}
// [64, 32] * [16, 32, 32] => [16, 64, 32]
void BuildBatchProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 32}, 0.5);
paddle::dialect::FullOp weight_1 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{16, 32, 32}, 0.5);
paddle::dialect::FullOp weight_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{16, 32, 64}, 0.5);
paddle::dialect::FullOp weight_3 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{16, 32, 128}, 0.5);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_1.out());
paddle::dialect::MatmulOp matmul_op2 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_2.out());
paddle::dialect::MatmulOp matmul_op3 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_3.out());
builder.Build<paddle::dialect::FetchOp>(matmul_op1.out(), "x", 0);
builder.Build<paddle::dialect::FetchOp>(matmul_op2.out(), "y", 1);
builder.Build<paddle::dialect::FetchOp>(matmul_op3.out(), "z", 1);
}
TEST(Cinn, FuseBatchMatmul) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildBatchProgram(builder);
ASSERT_EQ((program.block()->size()), 10u);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateFuseParallelMatmulPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
ASSERT_EQ((pm.Run(&program)), true);
ASSERT_EQ((program.block()->size()), 20u);
}
+726
View File
@@ -0,0 +1,726 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/manual_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/add_broadcast_to_elementwise_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/cinn_group_cluster_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/lowering_pass/lower_cinn_fusion_op_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/merge_reshape_with_broadcast_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/pd_to_cinn_pass.h"
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/build_cinn_pass.h"
#include "paddle/fluid/pir/transforms/general/dead_code_elimination_pass.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
bool simple_cmp(float a, float b) { return std::abs((a - b) / a) < 1e-5; }
std::vector<::pir::Type> CreateDenseTensorTypes(const phi::DDim& dims) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
::pir::Type fp32_dtype = ::pir::Float32Type::get(ctx);
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {};
size_t offset = 0;
std::vector<::pir::Type> op_output_types = {::pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset)};
return op_output_types;
}
static void RunAndCheckResult(::pir::Program* program,
const bool check_result = true,
const float gt_val = 2.0) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::PassManager stage_1_pm(ctx);
stage_1_pm.AddPass(cinn::dialect::ir::CreatePdOpToCinnOpPass());
stage_1_pm.AddPass(
std::make_unique<cinn::dialect::ir::MergeReshapeWithBroadcastPass>());
stage_1_pm.AddPass(pir::CreateDeadCodeEliminationPass());
stage_1_pm.AddPass(pir::CreateBuildCinnPass());
stage_1_pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
PADDLE_ENFORCE_EQ(
stage_1_pm.Run(program),
true,
common::errors::Unavailable("stage_1_pm fail to run program"));
pir::PassManager stage_2_pm(ctx);
stage_2_pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
stage_2_pm.AddPass(pir::CreateDeadCodeEliminationPass());
stage_2_pm.AddPass(cinn::dialect::ir::CreateLowerCinnFusionOpPass());
PADDLE_ENFORCE_EQ(
stage_2_pm.Run(program),
true,
common::errors::Unavailable("stage_2_pm fail to run program"));
phi::Place place = phi::GPUPlace(0);
auto kernel_program = pir::PdOpLowerToKernelPass(program, place);
paddle::framework::Scope exe_scope;
paddle::framework::InterpreterCore executor(
place, {"out@fetch"}, kernel_program->block(), &exe_scope);
executor.Run({}, true);
auto out_tensor =
executor.local_scope()->FindVar("out@fetch")->Get<phi::DenseTensor>();
if (check_result) {
bool res0 = simple_cmp(out_tensor.data<float>()[0], gt_val);
EXPECT_EQ(res0, true);
}
}
std::shared_ptr<::pir::Program> BuildGroupProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 1.0;
const std::vector<int64_t> shape = {128, 128, 768};
auto x = builder
.Build<paddle::dialect::FullOp>(
shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto max =
builder.Build<paddle::dialect::MaxOp>(x, std::vector<int64_t>{-1}, true)
.result(0);
auto sub = builder.Build<paddle::dialect::SubtractOp>(x, max).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(
exp, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto out = builder.Build<paddle::dialect::DivideOp>(exp, sum).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuild) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildGroupProgram();
RunAndCheckResult(program.get(), true, 1.0 / 768);
}
std::shared_ptr<::pir::Program> BuildLayerNormProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
std::vector<int64_t> axes{-1};
auto x =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({128, 128, 768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto bias = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto scale = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto num = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1},
768.0,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto eps = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1},
1e-5,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(x, axes, phi::DataType::FLOAT32, true)
.result(0);
auto mean = builder.Build<paddle::dialect::DivideOp>(sum, num).result(0);
auto power = builder.Build<paddle::dialect::MultiplyOp>(x, x).result(0);
auto power_sum = builder
.Build<paddle::dialect::SumOp>(
power, axes, phi::DataType::FLOAT32, true)
.result(0);
auto mean2 =
builder.Build<paddle::dialect::DivideOp>(power_sum, num).result(0);
auto power_mean =
builder.Build<paddle::dialect::MultiplyOp>(mean, mean).result(0);
auto var =
builder.Build<paddle::dialect::SubtractOp>(mean2, power_mean).result(0);
auto sub = builder.Build<paddle::dialect::SubtractOp>(x, mean).result(0);
auto t1 = builder.Build<paddle::dialect::AddOp>(var, eps).result(0);
auto t2 = builder.Build<paddle::dialect::SqrtOp>(t1).result(0);
auto t3 = builder.Build<paddle::dialect::DivideOp>(sub, t2).result(0);
auto t5 = builder.Build<paddle::dialect::MultiplyOp>(t3, scale).result(0);
auto out = builder.Build<paddle::dialect::MultiplyOp>(t5, bias).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildLayerNorm) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildLayerNormProgram();
RunAndCheckResult(program.get(), false);
}
std::shared_ptr<::pir::Program> BuildDropOutProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({128, 128, 768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto prob = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({1}),
0.5,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto random = builder
.Build<paddle::dialect::UniformOp>(
std::vector<int64_t>({128, 128, 768}),
phi::DataType::FLOAT32,
0.0,
1.0,
0,
phi::GPUPlace())
.result(0);
auto mask =
builder.Build<paddle::dialect::GreaterThanOp>(random, prob).result(0);
auto mask1 =
builder.Build<paddle::dialect::CastOp>(mask, phi::DataType::FLOAT32)
.result(0);
auto mul = builder.Build<paddle::dialect::MultiplyOp>(x, mask1).result(0);
auto neg_prob = prob =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({1}),
0.5,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto out = builder.Build<paddle::dialect::DivideOp>(mul, neg_prob).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildDropout) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildDropOutProgram();
RunAndCheckResult(program.get(), false);
}
std::shared_ptr<::pir::Program> BuildScaleGroupProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 1.0;
const std::vector<int64_t> shape = {16, 16};
auto x = builder
.Build<paddle::dialect::FullOp>(
shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto out =
builder.Build<paddle::dialect::ScaleOp>(x, 0.5, 0.0, false).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildScale) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildScaleGroupProgram();
RunAndCheckResult(program.get(), true, 0.5);
}
std::shared_ptr<::pir::Program> BuildScaleTensorGroupProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 0.5;
const std::vector<int64_t> shape = {16, 16};
auto x = builder
.Build<paddle::dialect::FullOp>(
shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto scale = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({1}),
0.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto factor = builder.Build<paddle::dialect::CosOp>(scale).result(0);
auto out =
builder.Build<paddle::dialect::ScaleOp>(x, factor, 0.0, false).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildScaleTensor) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildScaleTensorGroupProgram();
RunAndCheckResult(program.get(), true, 0.5);
}
std::shared_ptr<::pir::Program> BuildPowerProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto factor =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto power1 =
builder.Build<paddle::dialect::ElementwisePowOp>(x, factor).result(0);
auto power2 = builder.Build<paddle::dialect::PowOp>(power1, 2.0).result(0);
auto out =
builder
.Build<paddle::dialect::ReshapeOp>(power2, std::vector<int64_t>({-1}))
.result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildPower) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildPowerProgram();
RunAndCheckResult(program.get(), true, 16.0);
}
std::shared_ptr<::pir::Program> BuildLayerNorm2Program() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
std::vector<int64_t> axes{-1};
auto x =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({128, 128, 768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto bias = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto scale = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto num =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{128, 128, 1},
768.0,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(x, axes, phi::DataType::FLOAT32, true)
.result(0);
auto mean = builder.Build<paddle::dialect::DivideOp>(sum, num).result(0);
auto diff = builder.Build<paddle::dialect::SubtractOp>(x, mean).result(0);
auto power = builder.Build<paddle::dialect::MultiplyOp>(diff, diff).result(0);
auto power_sum = builder
.Build<paddle::dialect::SumOp>(
power, axes, phi::DataType::FLOAT32, true)
.result(0);
auto num2 =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{128, 128, 1},
768.0,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto var2 =
builder.Build<paddle::dialect::DivideOp>(power_sum, num2).result(0);
auto t1 = builder.Build<paddle::dialect::ScaleOp>(var2, 1.0, 1e-5).result(0);
auto factor = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1},
-0.5,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto t2 =
builder.Build<paddle::dialect::ElementwisePowOp>(t1, factor).result(0);
// auto t2 = builder.Build<paddle::dialect::RsqrtOp>(t1).result(0);
auto t3 = builder.Build<paddle::dialect::MultiplyOp>(diff, t2).result(0);
auto t5 = builder.Build<paddle::dialect::MultiplyOp>(t3, scale).result(0);
auto out = builder.Build<paddle::dialect::AddOp>(t5, bias).result(0);
auto mean_out =
builder
.Build<paddle::dialect::ReshapeOp>(mean, std::vector<int64_t>({-1}))
.result(0);
auto mean2_out =
builder
.Build<paddle::dialect::ReshapeOp>(var2, std::vector<int64_t>({-1}))
.result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
builder.Build<paddle::dialect::FetchOp>(mean_out, "mean", 0);
builder.Build<paddle::dialect::FetchOp>(mean2_out, "var", 0);
return program;
}
TEST(GroupOp, TestBuildLayerNorm2) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildLayerNorm2Program();
RunAndCheckResult(program.get(), false);
}
std::shared_ptr<::pir::Program> BuildSum2GroupProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
0.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto cos = builder.Build<paddle::dialect::CosOp>(x).result(0);
auto y = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({8, 8}),
0.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto sin = builder.Build<paddle::dialect::SinOp>(y).result(0);
builder.Build<paddle::dialect::FetchOp>(cos, "out", 0);
builder.Build<paddle::dialect::FetchOp>(sin, "out2", 0);
return program;
}
TEST(GroupOp, TestBuildSum2Group) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildSum2GroupProgram();
RunAndCheckResult(program.get(), true, 1.0);
}
std::shared_ptr<::pir::Program> BuildConcatProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto t1 =
builder.Build<pir::CombineOp>(std::vector<pir::Value>({x, y})).result(0);
auto out = builder.Build<paddle::dialect::ConcatOp>(t1, 1).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildConcat) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildConcatProgram();
RunAndCheckResult(program.get(), true, 2.0);
}
std::shared_ptr<::pir::Program> BuildSliceProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto out = builder
.Build<paddle::dialect::SliceOp>(x,
std::vector<int64_t>({1}),
std::vector<int64_t>({0}),
std::vector<int64_t>({2}),
std::vector<int64_t>({}),
std::vector<int64_t>({}))
.result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildSlice) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildSliceProgram();
RunAndCheckResult(program.get(), true, 2.0);
}
std::shared_ptr<::pir::Program> BuildSplitProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto out_arr =
builder.Build<paddle::dialect::SplitWithNumOp>(x, 4, 1).result(0);
auto out = builder.Build<pir::SliceOp>(out_arr, 0).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildSplit) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildSplitProgram();
RunAndCheckResult(program.get(), true, 2.0);
}
std::shared_ptr<::pir::Program> BuildAddNProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto z = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto t1 = builder.Build<pir::CombineOp>(std::vector<pir::Value>({x, y, z}))
.result(0);
auto out = builder.Build<paddle::dialect::AddNOp>(t1).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildAddN) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildAddNProgram();
RunAndCheckResult(program.get(), true, 6.0);
}
std::shared_ptr<::pir::Program> BuildSplitSectionProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto split_arr = builder
.Build<paddle::dialect::SplitOp>(
x, std::vector<int64_t>({3, 5, 8}), -1)
.out();
auto out = builder.Build<pir::SliceOp>(split_arr, 0).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildSplitSection) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildSplitSectionProgram();
RunAndCheckResult(program.get(), 2.0);
}
std::shared_ptr<::pir::Program> BuildReshapeSumProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({128 * 128, 768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto sum = builder
.Build<paddle::dialect::SumOp>(
x, std::vector<int64_t>{0}, phi::DataType::FLOAT32, true)
.result(0);
builder.Build<paddle::dialect::FetchOp>(sum, "out", 0);
return program;
}
TEST(GroupOp, TestBuildReshapeSum) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildReshapeSumProgram();
RunAndCheckResult(program.get(), true, 128 * 128);
}
+262
View File
@@ -0,0 +1,262 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_attribute.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/runtime/ir/jit_kernel_op.h"
#include "paddle/cinn/hlir/dialect/runtime/ir/runtime_dialect.h"
#include "paddle/cinn/hlir/framework/pir/utils.h"
#include "paddle/cinn/hlir/framework/pir_compiler.h"
#include "paddle/cinn/utils/data_util.h"
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/api_builder.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
using cinn::hlir::framework::pir::CompatibleInfo;
using cinn::hlir::framework::pir::OpLoweringGroup;
using cinn::hlir::framework::pir::OpLoweringGroupPtr;
bool simple_cmp(float a, float b) { return std::abs((a - b) / a) < 1e-5; }
using ProgramInfo = std::tuple<std::shared_ptr<::pir::Program>,
std::vector<OpLoweringGroupPtr>>;
ProgramInfo BuildProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const float value_one = 1.0; // relu(tan(1.)) = 1.5;
const float value_two = 2.0; // relu(tan(2.)) = 0.
auto full_op_x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 128},
value_one,
phi::DataType::FLOAT32,
phi::GPUPlace());
auto full_op_y =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 128},
value_two,
phi::DataType::FLOAT32,
phi::GPUPlace());
auto tan_op_x = builder.Build<paddle::dialect::TanOp>(full_op_x->result(0));
auto relu_op_x = builder.Build<paddle::dialect::ReluOp>(tan_op_x->result(0));
auto tan_op_y = builder.Build<paddle::dialect::TanOp>(relu_op_x->result(0));
auto relu_op_y = builder.Build<paddle::dialect::ReluOp>(tan_op_y->result(0));
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_x.result(0)});
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_y.result(0)});
builder.Build<pir::YieldOp>(std::vector<pir::Value>{relu_op_y.result(0)});
std::vector<OpLoweringGroupPtr> groups;
const auto full_op_x_ops =
std::initializer_list<::pir::Operation*>({full_op_x.operation()});
groups.emplace_back(std::make_shared<OpLoweringGroup>(
full_op_x_ops,
CompatibleInfo::GroupOpsName(full_op_x_ops))); // For coverage
groups[0]->mut_output_values().push_back(groups[0]->ops().back()->result(0));
const auto full_op_y_ops =
std::initializer_list<::pir::Operation*>({full_op_x.operation()});
groups.emplace_back(std::make_shared<OpLoweringGroup>(
full_op_y_ops, CompatibleInfo::GroupOpsName(full_op_y_ops)));
groups[1]->mut_output_values().push_back(groups[1]->ops().back()->result(0));
const auto vector_ops =
std::vector<::pir::Operation*>({tan_op_x.operation(),
relu_op_x.operation(),
tan_op_y.operation(),
relu_op_y.operation()});
groups.emplace_back(std::make_shared<OpLoweringGroup>(
vector_ops, CompatibleInfo::GroupOpsName(vector_ops)));
groups[2]->mut_output_values().push_back(groups[2]->ops().back()->result(0));
return {program, groups};
}
ProgramInfo BuildSoftmax() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
std::vector<int64_t> axes{-1};
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace(0))
.result(0);
auto max = builder.Build<cinn::dialect::ReduceMaxOp>(x, axes, true).result(0);
auto broadcast_1 =
builder
.Build<cinn::dialect::BroadcastOp>(
max, std::vector<int64_t>({0, 1}), std::vector<int64_t>({16, 16}))
.result(0);
auto sub =
builder.Build<paddle::dialect::SubtractOp>(x, broadcast_1).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder.Build<cinn::dialect::ReduceSumOp>(exp, axes, true).result(0);
auto broadcast_2 =
builder
.Build<cinn::dialect::BroadcastOp>(
sum, std::vector<int64_t>({0, 1}), std::vector<int64_t>({16, 16}))
.result(0);
auto divide =
builder.Build<paddle::dialect::DivideOp>(exp, broadcast_2).result(0);
auto yield_op = builder.Build<pir::YieldOp>(std::vector<pir::Value>{divide});
std::vector<OpLoweringGroupPtr> groups;
const auto vector_ops =
std::initializer_list<::pir::Operation*>({max.defining_op(),
broadcast_1.defining_op(),
sub.defining_op(),
exp.defining_op(),
sum.defining_op(),
broadcast_2.defining_op(),
divide.defining_op()});
groups.emplace_back(std::make_shared<OpLoweringGroup>(
vector_ops, CompatibleInfo::GroupOpsName(vector_ops)));
groups[0]->mut_output_values().push_back(groups[0]->ops().back()->result(0));
groups[0]->set_op_pattern_kind(cinn::hlir::framework::kReduction);
return {program, groups};
}
// TEST(PirCompiler, CompileSoftmax) {
// // Step 1: Construct pir::Program
// ::pir::IrContext* ctx = ::pir::IrContext::Instance();
// ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
// ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
// ctx->GetOrRegisterDialect<cinn::dialect::RuntimeDialect>();
// ctx->GetOrRegisterDialect<paddle::dialect::KernelDialect>();
// auto new_program = std::make_shared<::pir::Program>(ctx);
// auto prog_info = BuildSoftmax();
// std::shared_ptr<::pir::Program> program = std::get<0>(prog_info);
// std::vector<GroupPtr> groups = std::get<1>(prog_info);
// EXPECT_EQ(program->block()->size(), 9u);
// LOG(INFO) << program->block()->size();
// std::stringstream ss;
// program->Print(ss);
// LOG(INFO) << ss.str();
// // Step 2: Compiler New pir::Program into Runtime Program
// auto target = cinn::common::DefaultNVGPUTarget();
// auto scope = cinn::hlir::framework::BuildScope(target, *program);
// LOG(INFO) << scope->var_names().size();
// ASSERT_EQ(scope->var_names().size(), 8);
// cinn::hlir::framework::PirCompiler ir_compiler(*program, target, scope);
// auto fn_ptr_res = ir_compiler.BuildCUDAJITInfo(groups);
// ::pir::Builder builder = ::pir::Builder(ctx, new_program->block());
// auto x = builder
// .Build<paddle::dialect::FullOp>(std::vector<int64_t>({16,
// 16}),
// 1.0,
// phi::DataType::FLOAT32,
// phi::GPUPlace(0))
// .result(0);
// std::unordered_map<std::string, ::pir::Attribute> op_attrs{
// {cinn::dialect::JitKernelOp::kAttrName,
// cinn::dialect::CINNKernelInfoAttribute::get(ctx, fn_ptr_res[0])},
// };
// std::vector<pir::Type> vec_types;
// vec_types.push_back(groups[0]->ops.back()->result(0).type());
// std::string jit_op_name = cinn::dialect::JitKernelOp::name();
// ::pir::OpInfo op_info = ctx->GetRegisteredOpInfo(jit_op_name);
// ::pir::Operation* cinn_op =
// ::pir::Operation::Create({x}, op_attrs, vec_types, op_info);
// new_program->block()->push_back(cinn_op);
// builder.SetInsertionPointToBlockEnd(new_program->block());
// builder.Build<paddle::dialect::FetchOp>(
// cinn_op->result(cinn_op->num_results() - 1), "out", 0);
// phi::Place place = phi::GPUPlace(0);
// auto kernel_program =
// pir::PdOpLowerToKernelPass(new_program.get(), place);
// paddle::framework::Scope exe_scope;
// paddle::framework::interpreter::ExecutionConfig exe_conf;
// exe_conf.create_local_scope = false;
// paddle::framework::InterpreterCore executor(
// place, {"out@fetch"}, kernel_program->block(), &exe_scope);
// executor.Run({}, true);
// auto out_tensor =
// executor.local_scope()->FindVar("out@fetch")->Get<phi::DenseTensor>();
// bool res0 = simple_cmp(out_tensor.data<float>()[0], 1.0 / 16);
// EXPECT_EQ(res0, true);
// }
// TEST(PirCompiler, CompileGroupOps) {
// // Step 1: Construct pir::Program
// auto prog_info = BuildProgram();
// std::shared_ptr<::pir::Program> program = std::get<0>(prog_info);
// std::vector<GroupPtr> groups = std::get<1>(prog_info);
// EXPECT_EQ(program->block()->size(), 9u);
// LOG(INFO) << program->block()->size();
// std::stringstream ss;
// program->Print(ss);
// LOG(INFO) << ss.str();
// // Step 2: Compiler New pir::Program into Runtime Program
// auto target = cinn::common::DefaultNVGPUTarget();
// auto scope = cinn::hlir::framework::BuildScope(target, *program);
// ASSERT_EQ(scope->var_names().size(), 6);
// cinn::hlir::framework::PirCompiler ir_compiler(*program, target, scope);
// auto runtime_program = ir_compiler.Build(groups);
// // Step 3: Execute Runtime Instruction and check Scope.
// ASSERT_NO_THROW(runtime_program->Execute());
// for (auto& var_name : scope->var_names()) {
// std::string name = {var_name.begin(), var_name.end()};
// std::vector<float> data =
// cinn::GetTensorData<float>(scope->GetTensor(name), target);
// for (int i = 0; i < 1; ++i) {
// LOG_FIRST_N(INFO, 10) << "data: " << data[i];
// }
// }
// }
@@ -0,0 +1,198 @@
// 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/optim/replace_cross_block_reduction.h"
#include <gtest/gtest.h>
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace optim {
TEST(CrossBlockReductionReplacer, SRLayout) {
Context::Global().ResetNameId();
Placeholder<float> A("A", {Expr(8), Expr(16)});
Var reduce_k(8, "reduce_k");
ir::Tensor B = Compute(
{Expr(16)},
[&](Var i) { return lang::ReduceSum(A(reduce_k, i), {reduce_k}); },
"B");
ir::Tensor C = Compute(
{Expr(16)}, [&](Var i) { return lang::Sqrt(B(i)); }, "C");
ast_gen_ius::TensorGroup tensor_group({A, B, C});
auto func = lang::LowerToAst("reduce_sum_sqrt", {C}, &tensor_group);
ir::Expr expr_func_body = ir::ConvertStmtBlockToExprBlock(func->body_block);
ir::ModuleExpr mod_expr({expr_func_body});
ir::IRSchedule ir_sch(mod_expr);
ir_sch.Bind(ir_sch.GetLoops("B")[0], "blockIdx.x");
ir_sch.Bind(ir_sch.GetLoops("B")[1], "blockIdx.y");
ir_sch.Bind(ir_sch.GetLoops("C")[0], "blockIdx.x");
func->body = ir_sch.GetModule().GetExprs()[0];
A->WithBuffer("global", "_A");
B->WithBuffer("local", "_B_temp_buffer");
func->temp_bufs = {A->buffer, B->buffer};
VLOG(6) << "Before ReplaceCrossBlockReduction: " << func;
ReplaceCrossBlockReduction(func);
VLOG(6) << "After ReplaceCrossBlockReduction: " << func;
EXPECT_EQ(utils::GetStreamCnt(func),
utils::Trim(R"ROC(function reduce_sum_sqrt (_C, _A)
{
ScheduleBlock(root)
{
{
thread_bind[blockIdx.x] for (i, 0, 16)
{
ScheduleBlock(B__reduce_init)
{
i0 = axis.bind(i)
{
B__reduce_init[i0] = 0.00000000f
}
}
thread_bind[blockIdx.y] for (reduce_k, 0, 8)
{
ScheduleBlock(B)
{
i0_0, i1 = axis.bind(i, reduce_k)
{
B[i0_0] = cinn_grid_reduce_sum_fp32(Tensor(A, [8,16]), 16, i0_0)
}
}
}
}
thread_bind[blockIdx.x] for (i, 0, 16)
{
ScheduleBlock(C)
{
i0_1 = axis.bind(i)
{
C[i0_1] = sqrt(B[i0_1])
}
}
}
}
}
}
)ROC"));
EXPECT_EQ(func->temp_spaces.size(), 1);
EXPECT_EQ(func->temp_spaces[0].size().as_int64(), 512);
EXPECT_EQ(func->temp_spaces[0].arg_idx(), 1);
EXPECT_EQ(func->temp_spaces[0].need_zero_init(), false);
}
TEST(CrossBlockReductionReplacer, RSLayout) {
Context::Global().ResetNameId();
Placeholder<float> A("A", {Expr(8), Expr(4), Expr(32)});
Var reduce_k(8, "reduce_k");
ir::Tensor B = Compute(
{Expr(4), Expr(32)},
[&](Var i, Var j) {
return lang::ReduceMax(A(reduce_k, i, j), {reduce_k});
},
"B");
ir::Tensor C = Compute(
{Expr(4), Expr(32)},
[&](Var i, Var j) { return lang::Exp(B(i, j)); },
"C");
ast_gen_ius::TensorGroup tensor_group({A, B, C});
auto func = lang::LowerToAst("reduce_max_exp", {C}, &tensor_group);
ir::Expr expr_func_body = ir::ConvertStmtBlockToExprBlock(func->body_block);
ir::ModuleExpr mod_expr({expr_func_body});
ir::IRSchedule ir_sch(mod_expr);
ir_sch.Bind(ir_sch.GetLoops("B")[0], "blockIdx.x");
ir_sch.Bind(ir_sch.GetLoops("B")[1], "threadIdx.x");
ir_sch.Bind(ir_sch.GetLoops("B")[2], "blockIdx.y");
ir_sch.Bind(ir_sch.GetLoops("C")[0], "blockIdx.x");
ir_sch.Bind(ir_sch.GetLoops("C")[1], "threadIdx.x");
func->body = ir_sch.GetModule().GetExprs()[0];
A->WithBuffer("global", "_A");
B->WithBuffer("local", "_B_temp_buffer");
func->temp_bufs = {A->buffer, B->buffer};
VLOG(6) << "Before ReplaceCrossBlockReduction: " << func;
ReplaceCrossBlockReduction(func);
VLOG(6) << "After ReplaceCrossBlockReduction: " << func;
EXPECT_EQ(utils::GetStreamCnt(func),
utils::Trim(R"ROC(function reduce_max_exp (_C, _A)
{
ScheduleBlock(root)
{
{
thread_bind[blockIdx.x] for (i, 0, 4)
{
thread_bind[threadIdx.x] for (j, 0, 32)
{
ScheduleBlock(B__reduce_init)
{
i0, i1 = axis.bind(i, j)
{
B__reduce_init[i0, i1] = -3.40282347e+38f
}
}
thread_bind[blockIdx.y] for (reduce_k, 0, 8)
{
ScheduleBlock(B)
{
i0_0, i1_0, i2 = axis.bind(i, j, reduce_k)
{
B[i0_0, i1_0] = cinn_grid_reduce_max_fp32(Tensor(A, [8,4,32]), 128, ((i0_0 * 32) + i1_0))
}
}
}
}
}
thread_bind[blockIdx.x] for (i, 0, 4)
{
thread_bind[threadIdx.x] for (j, 0, 32)
{
ScheduleBlock(C)
{
i0_1, i1_1 = axis.bind(i, j)
{
C[i0_1, i1_1] = exp(B[i0_1, i1_1])
}
}
}
}
}
}
}
)ROC"));
EXPECT_EQ(func->temp_spaces.size(), 1);
EXPECT_EQ(func->temp_spaces[0].size().as_int64(), 4096);
EXPECT_EQ(func->temp_spaces[0].arg_idx(), 1);
EXPECT_EQ(func->temp_spaces[0].need_zero_init(), false);
}
} // namespace optim
} // namespace cinn
@@ -0,0 +1,71 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/sub_graph_extract_pass.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
void BuildProgram(pir::Builder &builder) { // NOLINT
::pir::IrContext *ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 1.0;
const std::vector<int64_t> shape = {128, 128, 768};
auto x = builder
.Build<paddle::dialect::FullOp>(
shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto max =
builder.Build<paddle::dialect::MaxOp>(x, std::vector<int64_t>{-1}, true)
.result(0);
auto sub = builder.Build<paddle::dialect::SubtractOp>(x, max).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(
exp, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto out = builder.Build<paddle::dialect::DivideOp>(exp, sum).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
}
TEST(SubGraphExtract, softmax_test) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
program.Print(std::cout);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateSubGraphExtractPass());
pm.Run(&program);
program.Print(std::cout);
}
+186
View File
@@ -0,0 +1,186 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/manual_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/framework/pir/op_lowering_group.h"
#include "paddle/cinn/hlir/framework/pir/op_lowering_impl.h"
#include "paddle/cinn/hlir/framework/pir/utils.h"
#include "paddle/cinn/hlir/framework/pir_compiler.h"
#include "paddle/common/ddim.h"
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
#include "paddle/pir/include/dialect/shape/utils/shape_or_data_expr.h"
using cinn::hlir::framework::pir::CompatibleInfo;
using cinn::hlir::framework::pir::OpLoweringGroup;
using cinn::hlir::framework::pir::OpLoweringGroupPtr;
bool simple_cmp(float a, float b) { return std::abs((a - b) / a) < 1e-5; }
std::vector<::pir::Type> CreateDenseTensorTypes(const phi::DDim& dims) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
::pir::Type fp32_dtype = ::pir::Float32Type::get(ctx);
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {};
size_t offset = 0;
std::vector<::pir::Type> op_output_types = {::pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset)};
return op_output_types;
}
std::tuple<std::shared_ptr<::pir::Program>, std::vector<OpLoweringGroupPtr>>
BuildGroupProgramForLowering() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> x_shape = {-1, 2};
const std::vector<int64_t> y_shape = {1, -1, 2};
auto x = builder
.Build<paddle::dialect::DataOp>(
"input_x", x_shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::DataOp>(
"input_y", y_shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({1, -1, 2})));
builder.SetInsertionPointToBlockEnd(group_op.block());
auto exp = builder.Build<paddle::dialect::ExpOp>(x);
auto reshape = builder.Build<cinn::dialect::ReshapeOp>(
exp.result(0), std::vector<int>{-1, 1, 1});
auto sub = builder.Build<paddle::dialect::SubtractOp>(y, reshape.result(0));
builder.Build<::pir::YieldOp>(std::vector<::pir::Value>{sub.result(0)});
builder.SetInsertionPointToBlockEnd(program->block());
builder.Build<paddle::dialect::FetchOp>(group_op->result(0), "out", 0);
std::vector<OpLoweringGroupPtr> groups;
groups.emplace_back(std::make_shared<OpLoweringGroup>(
std::vector<::pir::Operation*>(
{exp.operation(), reshape.operation(), sub.operation()}),
CompatibleInfo::GroupOpsName(std::vector<::pir::Operation*>(
{exp.operation(), reshape.operation(), sub.operation()}))));
groups[0]->mut_output_ops().insert(groups[0]->ops().back());
std::unordered_map<::pir::Value, symbol::ShapeOrDataDimExprs>
value_to_shape_data;
symbol::DimExpr x_dim_0("S0");
symbol::DimExpr x_dim_1(2);
symbol::DimExpr y_dim_0(1);
symbol::DimExpr y_dim_1("S1");
symbol::DimExpr y_dim_2(2);
value_to_shape_data.emplace(
x,
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({x_dim_0, x_dim_1})));
value_to_shape_data.emplace(
y,
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({y_dim_0, y_dim_1, y_dim_2})));
value_to_shape_data.emplace(exp.result(0), value_to_shape_data.at(x));
value_to_shape_data.emplace(reshape.result(0), value_to_shape_data.at(y));
value_to_shape_data.emplace(sub.result(0), value_to_shape_data.at(y));
groups[0]->set_value_to_shape_or_data_exprs(value_to_shape_data);
return {program, groups};
}
std::tuple<std::shared_ptr<::pir::Program>, std::vector<OpLoweringGroupPtr>>
BuildBroadcastGroupProgramForLowering() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> x_shape = {1, 1, 1};
const std::vector<int64_t> y_shape = {1, -1, 128};
auto x = builder
.Build<paddle::dialect::DataOp>(
"input_x", x_shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::DataOp>(
"input_y", y_shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({1, -1, 128})));
builder.SetInsertionPointToBlockEnd(group_op.block());
const std::vector<int64_t> x_broadcast_axes = {0, 1, 2};
auto x_broadcast =
builder.Build<cinn::dialect::BroadcastOp>(x, x_broadcast_axes, y_shape);
auto sub =
builder.Build<paddle::dialect::SubtractOp>(x_broadcast->result(0), y);
builder.Build<::pir::YieldOp>(std::vector<::pir::Value>{sub.result(0)});
builder.SetInsertionPointToBlockEnd(program->block());
builder.Build<paddle::dialect::FetchOp>(group_op->result(0), "out", 0);
std::vector<OpLoweringGroupPtr> groups;
groups.emplace_back(std::make_shared<OpLoweringGroup>(
std::vector<::pir::Operation*>(
{x_broadcast.operation(), sub.operation()}),
CompatibleInfo::GroupOpsName(std::vector<::pir::Operation*>(
{x_broadcast.operation(), sub.operation()}))));
groups[0]->mut_output_ops().insert(groups[0]->ops().back());
std::unordered_map<::pir::Value, symbol::ShapeOrDataDimExprs>
value_to_shape_data;
symbol::DimExpr x_dim_0(1);
symbol::DimExpr x_dim_1(1);
symbol::DimExpr x_dim_2(1);
symbol::DimExpr y_dim_0(1);
symbol::DimExpr y_dim_1("S0");
symbol::DimExpr y_dim_2(128);
value_to_shape_data.emplace(
x,
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({x_dim_0, x_dim_1, x_dim_2})));
value_to_shape_data.emplace(
y,
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({y_dim_0, y_dim_1, y_dim_2})));
value_to_shape_data.emplace(
x_broadcast.result(0),
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({y_dim_0, y_dim_1, y_dim_2})));
value_to_shape_data.emplace(
sub.result(0),
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({y_dim_0, y_dim_1, y_dim_2})));
groups[0]->set_value_to_shape_or_data_exprs(value_to_shape_data);
return {program, groups};
}
@@ -0,0 +1,776 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sys/stat.h>
#include <memory>
#include <sstream>
#include <fstream>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/ir/group_schedule/config/database.h"
#include "paddle/cinn/ir/group_schedule/config/file_database.h"
#include "paddle/cinn/ir/group_schedule/config/group_tile_config.h"
#include "paddle/cinn/ir/group_schedule/config/schedule_config_manager.h"
#include "paddle/cinn/ir/group_schedule/search/config_searcher.h"
#include "paddle/cinn/ir/group_schedule/search/measurer.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/performance_statistician.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
PD_DECLARE_string(tile_config_policy);
PD_DECLARE_string(cinn_tile_config_filename_label);
COMMON_DECLARE_bool(print_ir);
PD_DECLARE_bool(cinn_measure_kernel_time);
PHI_DECLARE_bool(enable_cinn_compile_cache);
#define MKDIR(path) mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
struct PerfTestConfig {
int InnerTestNum;
int OuterTestNum;
int MaxTestNum;
float EarlyStopThreshold;
PerfTestConfig()
: InnerTestNum(1),
OuterTestNum(3),
MaxTestNum(3),
EarlyStopThreshold(1.2) {}
PerfTestConfig(int inner_num, int outer_num, int max_num, float earlystop)
: InnerTestNum(inner_num),
OuterTestNum(outer_num),
MaxTestNum(max_num),
EarlyStopThreshold(earlystop) {}
};
std::string GenCSVFilePath(const cinn::common::Target target,
const cinn::ir::IterSpaceType &iter_space_type) {
std::string dirname = "";
std::string filename = "";
for (auto i : iter_space_type) {
dirname += i.first;
dirname += "_";
filename += i.first + i.second;
filename += "_";
}
const std::string kDirSuffix = "_EREBE";
dirname = dirname.substr(0, dirname.size() - 1) + kDirSuffix;
filename = filename.substr(0, filename.size() - 1);
auto checkexist = [](std::string test_path) {
bool path_exists = false;
struct stat statbuf;
if (stat(test_path.c_str(), &statbuf) != -1) {
if (S_ISDIR(statbuf.st_mode)) {
path_exists = true;
}
}
if (!path_exists) {
PADDLE_ENFORCE_NE(MKDIR(test_path.c_str()),
-1,
::common::errors::PreconditionNotMet(
"Can not create directory: %s, Make sure you "
"have permission to write",
test_path));
}
};
std::string root_path = FLAGS_cinn_tile_config_filename_label;
if (root_path == "") {
const std::string kTestFileDir = "./tile_file_test/";
root_path = kTestFileDir;
}
std::string target_str = target.arch_str() + "_" + target.device_name_str();
checkexist(root_path);
checkexist(root_path + target_str);
checkexist(root_path + target_str + "/" + dirname);
VLOG(3) << "Dump_path is " << root_path + dirname + "/" + filename + ".csv";
return root_path + target_str + "/" + dirname + "/" + filename + ".csv";
}
void WriteBucketInfo(
std::ofstream &os,
const std::vector<std::pair<std::string, std::string>> &iter_space_type,
const cinn::ir::BucketInfo &bucket_info) {
std::stringstream ss;
ss << " { ";
for (int i = 0; i < iter_space_type.size(); ++i) {
ss << iter_space_type[i].first << "_" << iter_space_type[i].second << ": "
<< bucket_info.space[i].lower_bound << "-"
<< bucket_info.space[i].upper_bound << " ";
}
ss << "} ";
os << ss.str() << " \t ";
}
// reduce_sum
std::shared_ptr<::pir::Program> BuildReduceSumProgram(int spatial_size,
int reduce_size) {
::pir::IrContext *ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto out = builder
.Build<paddle::dialect::SumOp>(
x, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// layerNorm
std::shared_ptr<::pir::Program> BuildLayerNormProgram(int spatial_size,
int reduce_size) {
::pir::IrContext *ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto sum_val =
builder
.Build<paddle::dialect::SumOp>(
x, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1024, phi::DataType::FLOAT32)
.out();
auto mean_val =
builder.Build<paddle::dialect::DivideOp>(sum_val, divide_num).result(0);
auto sub_num =
builder.Build<paddle::dialect::SubtractOp>(x, mean_val).result(0);
auto pow_val = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto pow_sum =
builder
.Build<paddle::dialect::SumOp>(
pow_val, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto variance_val =
builder.Build<paddle::dialect::DivideOp>(pow_sum, divide_num).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto add_val =
builder.Build<paddle::dialect::AddOp>(variance_val, add_num).result(0);
auto rsqrt_val = builder.Build<paddle::dialect::RsqrtOp>(add_val).result(0);
auto out =
builder.Build<paddle::dialect::MultiplyOp>(rsqrt_val, sub_num).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// softmax
std::shared_ptr<::pir::Program> BuildSoftmaxProgram(int spatial_size,
int reduce_size) {
::pir::IrContext *ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto max_num =
builder.Build<paddle::dialect::MaxOp>(x, std::vector<int64_t>{-1}, true)
.result(0);
auto sub_num =
builder.Build<paddle::dialect::SubtractOp>(x, max_num).result(0);
auto exp_num = builder.Build<paddle::dialect::ExpOp>(sub_num).result(0);
auto exp_sum =
builder
.Build<paddle::dialect::SumOp>(
exp_num, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto out =
builder.Build<paddle::dialect::DivideOp>(exp_num, exp_sum).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// RMSNorm
std::shared_ptr<::pir::Program> BuildRMSNormProgram(int spatial_size,
int reduce_size) {
::pir::IrContext *ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto pow_val = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto sum_val =
builder
.Build<paddle::dialect::SumOp>(
pow_val, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), reduce_size, phi::DataType::FLOAT32)
.out();
auto div_val =
builder.Build<paddle::dialect::DivideOp>(sum_val, divide_num).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto add_val =
builder.Build<paddle::dialect::AddOp>(div_val, add_num).result(0);
auto rsqrt_val = builder.Build<paddle::dialect::RsqrtOp>(add_val).result(0);
auto out = builder.Build<paddle::dialect::MultiplyOp>(rsqrt_val, x).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// Build test program for spatial reduce.
std::shared_ptr<::pir::Program> BuildSpatialReduceProgram(int spatial_size,
int reduce_size) {
std::shared_ptr<::pir::Program> program;
program = BuildSoftmaxProgram(spatial_size, reduce_size);
return program;
}
// Get the tile size configuration for the given dimension lower bound
// dynamically.
int get_tile_size_config_in_small_area(int dimension_lower) {
if (dimension_lower <= 2) {
return 126;
} else if (dimension_lower <= 128) {
return 384;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 1024) {
return 1024;
} else if (dimension_lower <= 2048) {
return 2048;
} else {
PADDLE_THROW(phi::errors::InvalidArgument(
"dimension_lower (%d) exceeds the supported range (<=2048).",
dimension_lower));
}
}
int get_tile_size_config_in_large_area(int dimension_lower) {
if (dimension_lower <= 2) {
return 510;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 4096) {
return 4096;
} else if (dimension_lower <= 8192) {
return 8192;
} else if (dimension_lower <= 16384) {
return 16384;
} else {
PADDLE_THROW(phi::errors::InvalidArgument(
"dimension_lower (%d) exceeds the supported range (<=16384).",
dimension_lower));
}
}
std::shared_ptr<::pir::Program> BuildProgram(bool is_spatial_dynamic,
bool is_reduce_dynamic,
int s_dimension_lower,
int r_dimension_lower) {
std::shared_ptr<::pir::Program> program;
if (!is_spatial_dynamic && !is_reduce_dynamic) {
program = BuildSpatialReduceProgram(s_dimension_lower, r_dimension_lower);
} else if (is_spatial_dynamic && !is_reduce_dynamic) {
program = BuildSpatialReduceProgram(-1, r_dimension_lower);
} else if (!is_spatial_dynamic && is_reduce_dynamic) {
program = BuildSpatialReduceProgram(s_dimension_lower, -1);
} else {
program = BuildSpatialReduceProgram(-1, -1);
}
return program;
}
cinn::ir::BucketInfo CreateBucket(int s_dimension_lower,
int spatial_tile_width,
int r_dimension_lower,
int reduce_tile_width,
bool is_spatial_dynamic,
bool is_reduce_dynamic) {
cinn::ir::BucketInfo bucket_info;
bucket_info.space.push_back(cinn::ir::BucketInfo::Dimension{
s_dimension_lower,
s_dimension_lower + spatial_tile_width - 1,
"S",
/* is_dynamic = */ is_spatial_dynamic});
bucket_info.space.push_back(
cinn::ir::BucketInfo::Dimension{r_dimension_lower,
r_dimension_lower + reduce_tile_width - 1,
"R",
/* is_dynamic = */ is_reduce_dynamic});
return bucket_info;
}
cinn::ir::search::CandidateType GetCandidate(
const cinn::ir::TileConfigMap &best_tile_config_map,
int s_dimension_lower,
int spatial_tile_width,
int r_dimension_lower,
int reduce_tile_width) {
cinn::ir::search::CandidateType best_candidate;
for (auto &it : best_tile_config_map) {
auto s_flag = false, r_flag = false;
auto dims = it.first.space.size();
// SR type support only
if (dims == 2) {
if (it.first.space[0].lower_bound <= s_dimension_lower &&
it.first.space[0].upper_bound >=
s_dimension_lower + spatial_tile_width - 1) {
s_flag = true;
} else if (it.first.space[0].lower_bound == 4096 &&
s_dimension_lower == 4096) {
s_flag = true;
}
if (it.first.space[1].lower_bound <= r_dimension_lower &&
it.first.space[1].upper_bound >=
r_dimension_lower + reduce_tile_width - 1) {
r_flag = true;
} else if (it.first.space[1].lower_bound == 4096 &&
r_dimension_lower == 4096) {
r_flag = true;
}
} else {
PADDLE_THROW(::common::errors::Unavailable("Now just support SR type."));
}
if (s_flag == true && r_flag == true) {
best_candidate = {it.second.warp_num,
it.second.tree_reduce_num,
it.second.spatial_inner_num};
break;
}
}
if (best_candidate.empty()) {
PADDLE_THROW(
::common::errors::Unavailable("Not found the best candidate."));
}
return best_candidate;
}
void TestWindowPerformance(
std::ofstream &os,
const PerfTestConfig &perf_test_config,
const int graph_num,
const int s_dimension_lower,
const int spatial_tile_width,
const int r_dimension_lower,
const int reduce_tile_width,
const int is_spatial_dynamic,
const int is_reduce_dynamic,
const double s_weight,
const double r_weight,
const double sampling_prob,
const int kMaxSamplingTimes,
const int kRepeats,
const cinn::ir::TileConfigMap &best_tile_config_map,
const std::vector<std::pair<std::string, std::string>> &iter_space_type) {
std::vector<double> s_weights =
std::vector<double>(spatial_tile_width, s_weight);
std::vector<double> r_weights =
std::vector<double>(reduce_tile_width, r_weight);
LOG(INFO) << "spatial tile dimension lower bound = " << s_dimension_lower
<< ", reduce tile dimension lower bound = " << r_dimension_lower
<< std::endl;
// Construct pir::Program.
std::shared_ptr<::pir::Program> program;
program = BuildProgram(is_spatial_dynamic,
is_reduce_dynamic,
s_dimension_lower,
r_dimension_lower);
// Construct iter space and objective function.
cinn::ir::BucketInfo bucket_info = CreateBucket(s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width,
is_spatial_dynamic,
is_reduce_dynamic);
std::unique_ptr<cinn::ir::search::BaseObjectiveFunc> obj_func =
std::make_unique<cinn::ir::search::WeightedSamplingTrailObjectiveFunc>(
program.get(),
bucket_info,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
std::vector<std::vector<double>>{s_weights, r_weights});
cinn::ir::search::CandidateType best_candidate =
GetCandidate(best_tile_config_map,
s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width);
// Write current bucket info to csv file.
WriteBucketInfo(os, iter_space_type, bucket_info);
int InnerTestNum = perf_test_config.InnerTestNum;
int OuterTestNum = perf_test_config.OuterTestNum;
int MaxTestNum = perf_test_config.MaxTestNum;
float EarlyStopThreshold = perf_test_config.EarlyStopThreshold;
cinn::ir::search::ScoreType record_baseline_score;
cinn::ir::search::ScoreType record_best_score;
double record_best_variance = 0;
// start test loop
for (int current_test_num = 0; current_test_num < MaxTestNum;
++current_test_num) {
std::vector<cinn::ir::search::ScoreType> vec_best;
std::vector<cinn::ir::search::ScoreType> vec_baseline;
// start outer measure loop
for (int i = 0; i < OuterTestNum; ++i) {
cinn::ir::search::CandidateType default_candidate;
std::vector<cinn::ir::search::ScoreType> vec_best_score;
std::vector<cinn::ir::search::ScoreType> vec_baseline_score;
// start inner measure loop
for (int i = 0; i < InnerTestNum; i++) {
FLAGS_tile_config_policy = "default";
cinn::ir::search::ScoreType temp_baseline_score =
(*obj_func)(default_candidate);
FLAGS_tile_config_policy = "search";
cinn::ir::search::ScoreType temp_best_score =
(*obj_func)(best_candidate);
vec_best_score.push_back(temp_best_score);
vec_baseline_score.push_back(temp_baseline_score);
}
float best_total_time =
std::accumulate(vec_best_score.begin(), vec_best_score.end(), 0.0);
float baseline_total_time = std::accumulate(
vec_baseline_score.begin(), vec_baseline_score.end(), 0.0);
float best_avg_time = best_total_time / InnerTestNum;
float baseline_avg_time = baseline_total_time / InnerTestNum;
vec_best.push_back(best_avg_time);
vec_baseline.push_back(baseline_avg_time);
}
cinn::ir::search::ScoreType best_mean =
std::accumulate(vec_best.begin(), vec_best.end(), 0.0) / OuterTestNum;
cinn::ir::search::ScoreType baseline_mean =
std::accumulate(vec_baseline.begin(), vec_baseline.end(), 0.0) /
OuterTestNum;
// Compute variance of OuterTestNum times of outer measure in each test
double best_variance = 0.0;
double baseline_variance = 0.0;
for (int i = 0; i < OuterTestNum; i++) {
best_variance = best_variance + pow(vec_best[i] - best_mean, 2);
baseline_variance =
baseline_variance + pow(vec_baseline[i] - baseline_mean, 2);
}
best_variance = pow(best_variance / OuterTestNum, 0.5);
baseline_variance = pow(baseline_variance / OuterTestNum, 0.5);
// Early stop when current best_variance smaller than EarlyStopThreshold
if ((best_variance < EarlyStopThreshold) &&
(baseline_variance < EarlyStopThreshold)) {
record_baseline_score = baseline_mean;
record_best_score = best_mean;
record_best_variance = best_variance + baseline_variance;
break;
} else {
if (record_best_variance == 0) {
record_best_variance = best_variance + baseline_variance;
record_baseline_score = baseline_mean;
record_best_score = best_mean;
} else if ((best_variance + baseline_variance) < record_best_variance) {
record_best_variance = best_variance + baseline_variance;
record_baseline_score = baseline_mean;
record_best_score = best_mean;
}
}
} // end of test_num loop
cinn::ir::search::ScoreType optim_percentage =
(1 / (record_best_score)-1 / record_baseline_score) *
record_baseline_score;
LOG(INFO) << "Best score: " << record_best_score / graph_num;
LOG(INFO) << "Baseline score: " << record_baseline_score / graph_num;
LOG(INFO) << "variance: " << (record_best_variance / 2);
LOG(INFO) << "optim percentage: " << optim_percentage;
LOG(INFO) << "Best candidate: " << best_candidate[0] << " "
<< best_candidate[1] << " " << best_candidate[2];
// Write measure results to csv file
os << std::setprecision(3) << " " << record_baseline_score / graph_num
<< " \t " << record_best_score / graph_num << " \t " << optim_percentage
<< "\n";
}
void TestPerformanceForTileConfig(int spatial_left_bound,
int spatial_right_bound,
int reduce_left_bound,
int reduce_right_bound,
bool is_spatial_dynamic,
bool is_reduce_dynamic,
bool test_single_large) {
FLAGS_enable_cinn_compile_cache = false;
FLAGS_cinn_measure_kernel_time = true;
// set tile_file path to test path when user use default setting
std::string root_path = FLAGS_cinn_tile_config_filename_label;
if (root_path == "") {
const std::string kTestFileDir = "./tile_file_test/";
FLAGS_cinn_tile_config_filename_label = kTestFileDir;
}
constexpr int kThreadsPerWarp = 32;
constexpr int kMaxThreadsPerBlock = 1024;
// now each has the same weight
constexpr double s_w = 0.05;
constexpr double r_w = 0.05;
constexpr double sampling_prob = 1.0;
constexpr int kMaxSamplingTimes = 360;
constexpr int kRepeats = 5;
constexpr int kInnerTestNum = 1;
constexpr int kOuterTestNum = 3;
constexpr int kMaxTestNum = 1;
constexpr float kEarlyStopThreshold = 1.2;
// number of nodes in cudaGraph for test, which is defined in
// performance_statistician.h as graph_nodes_num_. This parameter is set to
// make measure results corresponding to one launch for better readability
constexpr int kGraphNum = 25;
// Define the initial grid size for the spatial and reduction dimensions
int spatial_tile_config = 0, reduce_tile_config = 0;
int spatial_tile_width = 0, reduce_tile_width = 0;
// Define weight for each dimension
double s_weight = (is_spatial_dynamic ? s_w : 1.0);
double r_weight = (is_reduce_dynamic ? r_w : 1.0);
auto s_dimension_type = "S";
auto r_dimension_type = "R";
// Define the performance test configuration.
PerfTestConfig perf_test_config = {
kInnerTestNum, kOuterTestNum, kMaxTestNum, kEarlyStopThreshold};
// Get best configuration from json by file database.
std::vector<std::pair<std::string, std::string>> iter_space_type = {
std::make_pair(s_dimension_type,
is_spatial_dynamic == true ? "dynamic" : "static"),
std::make_pair(r_dimension_type,
is_reduce_dynamic == true ? "dynamic" : "static")};
cinn::ir::FileTileConfigDatabase file_database;
cinn::ir::TileConfigMap best_tile_config_map =
file_database.GetConfigs(cinn::common::DefaultTarget(), iter_space_type);
// build csv file
std::string dump_path =
GenCSVFilePath(cinn::common::DefaultTarget(), iter_space_type);
std::ofstream os(dump_path, std::ofstream::app);
PADDLE_ENFORCE_EQ(os.good(),
true,
::common::errors::InvalidArgument(
"Cannot open the file to write: %s", dump_path));
// run performance test for each data grid
for (int s_dimension_lower = spatial_left_bound;
s_dimension_lower < spatial_right_bound ||
s_dimension_lower == spatial_right_bound &&
spatial_left_bound == spatial_right_bound;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config = get_tile_size_config_in_small_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = reduce_left_bound;
r_dimension_lower < reduce_right_bound ||
r_dimension_lower == reduce_right_bound &&
reduce_left_bound == reduce_right_bound;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_small_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
TestWindowPerformance(os,
perf_test_config,
kGraphNum,
s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width,
is_spatial_dynamic,
is_reduce_dynamic,
s_weight,
r_weight,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
best_tile_config_map,
iter_space_type);
} // end of r_dimension_lower loop
} // end of s_dimension_lower loop
if (test_single_large) {
// (II) Test in the single large areas,
// i.e., S:[4096-32768]*R:[2-1024], S:[2-1024]*R:[4096-32768]
for (int s_dimension_lower = 2; s_dimension_lower < 1024;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dynamically
spatial_tile_config =
get_tile_size_config_in_large_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = 4096; r_dimension_lower < 32768;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_large_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
TestWindowPerformance(os,
perf_test_config,
kGraphNum,
s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width,
is_spatial_dynamic,
is_reduce_dynamic,
s_weight,
r_weight,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
best_tile_config_map,
iter_space_type);
}
}
for (int s_dimension_lower = 4096; s_dimension_lower < 32768;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config =
get_tile_size_config_in_large_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = 2; r_dimension_lower < 1024;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_large_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
// Run performance test and write measure results to csv file.
TestWindowPerformance(os,
perf_test_config,
kGraphNum,
s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width,
is_spatial_dynamic,
is_reduce_dynamic,
s_weight,
r_weight,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
best_tile_config_map,
iter_space_type);
}
}
}
os.close();
}
/**
* @brief Test case for the ConfigSearcher.
*
* This test case performs a perormance test for the best configuration derived
* from the ConfigSearcher. It iterates over different spatial and reduce tile
* sizes and constructs a pir::Program. The objective function used for the
* search is a WeightedSamplingTrailObjectiveFunc. The performance results are
* written into a CSV file, including the default score and the best candidate
* score.
*/
TEST(ConfigSearcher, TestPerfDynamicDynamic) {
constexpr int spatial_left_bound = 2; // for full test, set it to 2
constexpr int spatial_right_bound = 2; // for full test, set it to 4096
constexpr int reduce_left_bound = 2; // for full test, set it to 2
constexpr int reduce_right_bound = 2; // for full test, set it to 4096
constexpr bool is_spatial_dynamic = true;
constexpr bool is_reduce_dynamic = true;
bool test_single_large = false; // To test single large area, set it to true
TestPerformanceForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
test_single_large);
}
TEST(ConfigSearcher, TestPerfStaticDynamic) {
constexpr int spatial_left_bound = 2; // for full test, set it to 2
constexpr int spatial_right_bound = 2; // for full test, set it to 4096
constexpr int reduce_left_bound = 2; // for full test, set it to 2
constexpr int reduce_right_bound = 2; // for full test, set it to 4096
constexpr bool is_spatial_dynamic = false;
constexpr bool is_reduce_dynamic = true;
bool test_single_large = false; // To test single large area, set it to true
TestPerformanceForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
test_single_large);
}
TEST(ConfigSearcher, TestPerfDynamicStatic) {
constexpr int spatial_left_bound = 2; // for full test, set it to 2
constexpr int spatial_right_bound = 2; // for full test, set it to 4096
constexpr int reduce_left_bound = 2; // for full test, set it to 2
constexpr int reduce_right_bound = 2; // for full test, set it to 4096
constexpr bool is_spatial_dynamic = true;
constexpr bool is_reduce_dynamic = false;
bool test_single_large = false; // To test single large area, set it to true
TestPerformanceForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
test_single_large);
}
@@ -0,0 +1,284 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/ir/group_schedule/config/database.h"
#include "paddle/cinn/ir/group_schedule/config/file_database.h"
#include "paddle/cinn/ir/group_schedule/config/group_tile_config.h"
#include "paddle/cinn/ir/group_schedule/config/schedule_config_manager.h"
#include "paddle/cinn/ir/group_schedule/search/config_searcher.h"
#include "paddle/cinn/ir/group_schedule/search/measurer.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/performance_statistician.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
COMMON_DECLARE_bool(print_ir);
PD_DECLARE_bool(cinn_measure_kernel_time);
PHI_DECLARE_bool(enable_cinn_compile_cache);
PD_DECLARE_string(tile_config_policy);
constexpr int kThreadsPerWarp = 32;
constexpr int kMaxThreadsPerBlock = 1024;
// now each has the same weight
constexpr double s_w = 0.05;
constexpr double sampling_prob = 1.0;
constexpr int kMaxSamplingTimes = 300;
constexpr int kRepeats = 3;
// PureSpatial
std::shared_ptr<::pir::Program> BuildSpatialProgram(int spatial_size) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> shape = {spatial_size, 1};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto x_pow = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto x_add = builder.Build<paddle::dialect::AddOp>(add_num, x_pow).result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1024, phi::DataType::FLOAT32)
.out();
auto x_div =
builder.Build<paddle::dialect::DivideOp>(x_add, divide_num).result(0);
builder.Build<paddle::dialect::FetchOp>(x_div, "out", 0);
return program;
}
// Get the tile size configuration for the given dimension lower bound
// dynamically.
int get_tile_size_config(int dimension_lower) {
if (dimension_lower <= 2) {
return 126;
} else if (dimension_lower <= 128) {
return 384;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 1024) {
return 1024;
} else if (dimension_lower <= 2048) {
return 2048;
} else if (dimension_lower <= 4096) {
return 4096;
} else if (dimension_lower <= 8192) {
return 8192;
} else if (dimension_lower <= 16384) {
return 16384;
}
}
std::vector<cinn::ir::search::ConstraintFunc> GetSConstraints(
int s_dimension_lower) {
std::vector<cinn::ir::search::ConstraintFunc> constraints;
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp <= kMaxThreadsPerBlock;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[1] < 256 && candidate[1] % kThreadsPerWarp == 0 ||
candidate[1] == 1 ||
candidate[1] <= 512 && candidate[1] % 128 == 0 ||
candidate[1] % 256 == 0;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp >= candidate[1];
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp % candidate[1] == 0;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[2] == 1 || candidate[2] == 2 || candidate[2] == 4 ||
candidate[2] == 8 || candidate[2] == 16 || candidate[2] == 32;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] <= 4 ||
candidate[0] <= 8 && candidate[0] % 2 == 0 ||
candidate[0] % 4 == 0;
});
return constraints;
}
// Create Bucket Info
cinn::ir::BucketInfo CreateSBucket(int s_dimension_lower,
int spatial_tile_width,
bool is_spatial_dynamic) {
cinn::ir::BucketInfo bucket_info;
bucket_info.space.push_back(cinn::ir::BucketInfo::Dimension{
s_dimension_lower,
s_dimension_lower + spatial_tile_width - 1,
"S",
/* is_dynamic = */ is_spatial_dynamic});
return bucket_info;
}
// Search One Window for corresponding pattern
void SearchSWindow(bool is_spatial_dynamic,
int s_dimension_lower,
int spatial_tile_width,
int spatial_tile_config,
double s_weight) {
std::vector<double> s_weights =
std::vector<double>(spatial_tile_width, s_weight);
// Step 1: Construct pir::Program.
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program_pure_spatial;
if (!is_spatial_dynamic) {
program_pure_spatial = BuildSpatialProgram(s_dimension_lower);
} else {
program_pure_spatial = BuildSpatialProgram(-1);
}
// Step 2: Switch schedule config manager mode.
auto& schedule_config_manager = cinn::ir::ScheduleConfigManager::Instance();
// Step 3: Construct iter space and objective function.
cinn::ir::BucketInfo bucket_info;
bucket_info.space.push_back(cinn::ir::BucketInfo::Dimension{
s_dimension_lower,
s_dimension_lower + spatial_tile_width - 1,
"S",
/* is_dynamic = */ is_spatial_dynamic});
std::unique_ptr<cinn::ir::search::BaseObjectiveFunc> obj_func_pure_spatial =
std::make_unique<cinn::ir::search::WeightedSamplingTrailObjectiveFunc>(
program_pure_spatial.get(),
bucket_info,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
std::vector<std::vector<double>>{s_weights});
std::vector<std::unique_ptr<cinn::ir::search::BaseObjectiveFunc>>
objective_funcs;
objective_funcs.emplace_back(std::move(obj_func_pure_spatial));
// Step 4: Construct config candidate range and constraints.
std::vector<std::pair<int, int>> candidate_range{
{1, 1}, {1, 1}, {1, 1}}; // {1, 32}, {1, 1}, {1, 32}
std::vector<cinn::ir::search::ConstraintFunc> constraints =
GetSConstraints(s_dimension_lower);
// Step 5: Construct searcher and search.
cinn::ir::search::ScheduleConfigSearcher searcher(
std::move(objective_funcs), candidate_range, constraints);
auto search_res = searcher.Search();
// Step 6: Save the best candidate's config of each grid search to json
cinn::ir::FileTileConfigDatabase file_database;
cinn::ir::ScheduleConfig::TileConfig tile_bestconfig;
tile_bestconfig.warp_num = search_res.second[0];
tile_bestconfig.tree_reduce_num = search_res.second[1];
tile_bestconfig.spatial_inner_num = search_res.second[2];
// Extend bucketinfo 's static dim region
if (bucket_info.space[0].is_dynamic == false &&
bucket_info.space[0].lower_bound == bucket_info.space[0].upper_bound) {
bucket_info.space[0].upper_bound =
s_dimension_lower + spatial_tile_config - 1;
}
// Extend bucketinfo 's large value to infinite
if (spatial_tile_config == 16384) {
bucket_info.space[0].upper_bound = static_cast<int>(2e10);
}
file_database.AddConfig(
cinn::common::DefaultTarget(), bucket_info, tile_bestconfig, 0);
LOG(INFO) << "spatial tile dimension lower bound = " << s_dimension_lower
<< std::endl;
LOG(INFO) << "min score = " << search_res.first;
LOG(INFO) << "best candidate: "
<< cinn::utils::Join<int64_t>(search_res.second, ", ");
}
/**
* @brief Test case for the ConfigSearcher.
*
*/
void SearchForSTileConfig(int spatial_l_bound,
int spatial_r_bound,
bool is_s_dynamic) {
FLAGS_cinn_measure_kernel_time = true;
FLAGS_enable_cinn_compile_cache = false;
FLAGS_tile_config_policy = "search";
// Define the search space bounds and sampling probabilities.
int spatial_left_bound = spatial_l_bound;
int spatial_right_bound = spatial_r_bound; // for easy test, set to 2. for
// the whole test, set to 32767
bool is_spatial_dynamic = is_s_dynamic;
// Define the initial grid size for the spatial and reduction dimensions
int spatial_tile_config = 0, reduce_tile_config = 0;
int spatial_tile_width = 0, reduce_tile_width = 0;
// Define weight for each dimension
double s_weight = (is_spatial_dynamic ? s_w : 1.0);
// Search for every window
for (int s_dimension_lower = spatial_left_bound;
s_dimension_lower < spatial_right_bound ||
s_dimension_lower == spatial_right_bound &&
spatial_left_bound == spatial_right_bound;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config = get_tile_size_config(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
SearchSWindow(is_spatial_dynamic,
s_dimension_lower,
spatial_tile_width,
spatial_tile_config,
s_weight);
}
}
TEST(ConfigSearcher, TestPureSpatialstatic) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 32767
bool is_spatial_dynamic = false;
SearchForSTileConfig(
spatial_left_bound, spatial_right_bound, is_spatial_dynamic);
}
TEST(ConfigSearcher, TestPureSpatialDynamic) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 32767
bool is_spatial_dynamic = true;
SearchForSTileConfig(
spatial_left_bound, spatial_right_bound, is_spatial_dynamic);
}
@@ -0,0 +1,553 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/ir/group_schedule/config/database.h"
#include "paddle/cinn/ir/group_schedule/config/file_database.h"
#include "paddle/cinn/ir/group_schedule/config/group_tile_config.h"
#include "paddle/cinn/ir/group_schedule/config/schedule_config_manager.h"
#include "paddle/cinn/ir/group_schedule/search/config_searcher.h"
#include "paddle/cinn/ir/group_schedule/search/measurer.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/performance_statistician.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
COMMON_DECLARE_bool(print_ir);
PD_DECLARE_bool(cinn_measure_kernel_time);
PHI_DECLARE_bool(enable_cinn_compile_cache);
PD_DECLARE_string(tile_config_policy);
PD_DECLARE_string(cinn_tile_config_filename_label);
constexpr int kThreadsPerWarp = 32;
constexpr int kMaxThreadsPerBlock = 1024;
// now each has the same weight
constexpr double s_w = 0.05;
constexpr double r_w = 0.05;
constexpr double sampling_prob = 1.0;
constexpr int kMaxSamplingTimes = 300;
constexpr int kRepeats = 3;
// layernorm
std::shared_ptr<::pir::Program> BuildLayerNormProgram(int spatial_size,
int reduce_size) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const float value_one = 1.0;
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto sum_val =
builder
.Build<paddle::dialect::SumOp>(
x, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1024, phi::DataType::FLOAT32)
.out();
auto mean_val =
builder.Build<paddle::dialect::DivideOp>(sum_val, divide_num).result(0);
auto sub_num =
builder.Build<paddle::dialect::SubtractOp>(x, mean_val).result(0);
auto pow_val = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto pow_sum =
builder
.Build<paddle::dialect::SumOp>(
pow_val, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto variance_val =
builder.Build<paddle::dialect::DivideOp>(pow_sum, divide_num).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto add_val =
builder.Build<paddle::dialect::AddOp>(variance_val, add_num).result(0);
auto rsqrt_val = builder.Build<paddle::dialect::RsqrtOp>(add_val).result(0);
auto out =
builder.Build<paddle::dialect::MultiplyOp>(rsqrt_val, sub_num).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// rmsnorm
std::shared_ptr<::pir::Program> BuildRMSNormProgram(int spatial_size,
int reduce_size) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const float value_one = 1.0;
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto pow_val = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto sum_val =
builder
.Build<paddle::dialect::SumOp>(
pow_val, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), reduce_size, phi::DataType::FLOAT32)
.out();
auto div_val =
builder.Build<paddle::dialect::DivideOp>(sum_val, divide_num).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto add_val =
builder.Build<paddle::dialect::AddOp>(div_val, add_num).result(0);
auto rsqrt_val = builder.Build<paddle::dialect::RsqrtOp>(add_val).result(0);
auto out = builder.Build<paddle::dialect::MultiplyOp>(rsqrt_val, x).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// Get the tile size configuration for the given dimension lower bound
// dynamically.
int get_tile_size_config_in_small_area(int dimension_lower) {
if (dimension_lower <= 2) {
return 126;
} else if (dimension_lower <= 128) {
return 384;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 1024) {
return 1024;
} else if (dimension_lower <= 2048) {
return 2048;
}
}
int get_tile_size_config_in_large_area(int dimension_lower) {
if (dimension_lower <= 2) {
return 510;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 4096) {
return 4096;
} else if (dimension_lower <= 8192) {
return 8192;
} else if (dimension_lower <= 16384) {
return 16384;
}
}
int get_spatial_range(int s_dimension_lower, int r_dimension_lower) {
int compute_size = s_dimension_lower * r_dimension_lower;
if (compute_size <= 1024 * 1024) {
return 1;
} else if (compute_size <= 1024 * 2048) {
return 2;
} else if (compute_size <= 2048 * 2048) {
return 4;
} else if ((s_dimension_lower > 4096) || (r_dimension_lower > 4096)) {
return 8;
}
return 1;
}
void search_then_save_one_window(bool is_spatial_dynamic,
bool is_reduce_dynamic,
int s_dimension_lower,
int r_dimension_lower,
int spatial_tile_width,
int reduce_tile_width,
int spatial_tile_config,
int reduce_tile_config,
double s_weight,
double r_weight) {
std::vector<double> s_weights =
std::vector<double>(spatial_tile_width, s_weight);
std::vector<double> r_weights =
std::vector<double>(reduce_tile_width, r_weight);
// Step 1: Construct pir::Program.
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program_layer_norm;
std::shared_ptr<::pir::Program> program_rms_norm;
if (!is_spatial_dynamic && !is_reduce_dynamic) {
program_layer_norm =
BuildLayerNormProgram(s_dimension_lower, r_dimension_lower);
} else if (is_spatial_dynamic && !is_reduce_dynamic) {
program_layer_norm = BuildLayerNormProgram(-1, r_dimension_lower);
} else if (!is_spatial_dynamic && is_reduce_dynamic) {
program_layer_norm = BuildLayerNormProgram(s_dimension_lower, -1);
} else {
program_layer_norm = BuildLayerNormProgram(-1, -1);
}
if (!is_spatial_dynamic && !is_reduce_dynamic) {
program_rms_norm =
BuildRMSNormProgram(s_dimension_lower, r_dimension_lower);
} else if (is_spatial_dynamic && !is_reduce_dynamic) {
program_rms_norm = BuildRMSNormProgram(-1, r_dimension_lower);
} else if (!is_spatial_dynamic && is_reduce_dynamic) {
program_rms_norm = BuildRMSNormProgram(s_dimension_lower, -1);
} else {
program_rms_norm = BuildRMSNormProgram(-1, -1);
}
// Step 2: Switch schedule config manager mode.
auto& schedule_config_manager = cinn::ir::ScheduleConfigManager::Instance();
// Step 3: Construct iter space and objective function.
cinn::ir::BucketInfo bucket_info;
bucket_info.space.push_back(cinn::ir::BucketInfo::Dimension{
s_dimension_lower,
s_dimension_lower + spatial_tile_width - 1,
"S",
/* is_dynamic = */ is_spatial_dynamic});
bucket_info.space.push_back(
cinn::ir::BucketInfo::Dimension{r_dimension_lower,
r_dimension_lower + reduce_tile_width - 1,
"R",
/* is_dynamic = */ is_reduce_dynamic});
std::unique_ptr<cinn::ir::search::BaseObjectiveFunc> obj_func_layernorm =
std::make_unique<cinn::ir::search::WeightedSamplingTrailObjectiveFunc>(
program_layer_norm.get(),
bucket_info,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
std::vector<std::vector<double>>{s_weights, r_weights});
std::unique_ptr<cinn::ir::search::BaseObjectiveFunc> obj_func_rmsnorm =
std::make_unique<cinn::ir::search::WeightedSamplingTrailObjectiveFunc>(
program_rms_norm.get(),
bucket_info,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
std::vector<std::vector<double>>{s_weights, r_weights});
std::vector<std::unique_ptr<cinn::ir::search::BaseObjectiveFunc>>
objective_funcs;
objective_funcs.emplace_back(std::move(obj_func_layernorm));
objective_funcs.emplace_back(std::move(obj_func_rmsnorm));
// Step 4: Construct config candidate range and constraints.
std::vector<std::pair<int, int>> candidate_range{
{1, 1}, {1, 32}, {1, 1}}; // {1, 32}, {1, 1024}, {1, 8}
std::vector<cinn::ir::search::ConstraintFunc> constraints;
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp <= kMaxThreadsPerBlock;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[1] < 256 && candidate[1] % kThreadsPerWarp == 0 ||
candidate[1] == 1 ||
candidate[1] <= 512 && candidate[1] % 128 == 0 ||
candidate[1] % 256 == 0;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp >= candidate[1];
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp % candidate[1] == 0;
});
constraints.emplace_back(
[&](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp / candidate[1] * candidate[2] <=
s_dimension_lower;
});
constraints.emplace_back(
[&](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[2] <=
get_spatial_range(s_dimension_lower, r_dimension_lower);
});
constraints.emplace_back(
[&](const cinn::ir::search::CandidateType& candidate) -> bool {
return r_dimension_lower % candidate[1] == 0 || candidate[1] == 32 ||
candidate[1] == 64;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[2] <= candidate[1];
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[2] == 1 || candidate[2] == 2 || candidate[2] == 4 ||
candidate[2] == 8;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] <= 4 ||
candidate[0] <= 8 && candidate[0] % 2 == 0 ||
candidate[0] % 4 == 0;
});
// Step 5: Construct searcher and search.
cinn::ir::search::ScheduleConfigSearcher searcher(
std::move(objective_funcs), candidate_range, constraints);
auto search_res = searcher.Search();
// Step 6: Save the best candidate's config of each grid search to json
cinn::ir::FileTileConfigDatabase file_database;
cinn::ir::ScheduleConfig::TileConfig tile_bestconfig;
tile_bestconfig.warp_num = search_res.second[0];
tile_bestconfig.tree_reduce_num = search_res.second[1];
tile_bestconfig.spatial_inner_num = search_res.second[2];
// Extend bucketinfo 's static dim region
if (bucket_info.space[0].is_dynamic == false &&
bucket_info.space[0].lower_bound == bucket_info.space[0].upper_bound) {
bucket_info.space[0].upper_bound =
s_dimension_lower + spatial_tile_config - 1;
}
if (bucket_info.space[1].is_dynamic == false &&
bucket_info.space[1].lower_bound == bucket_info.space[1].upper_bound) {
bucket_info.space[1].upper_bound =
r_dimension_lower + reduce_tile_config - 1;
}
// Extend bucketinfo 's large value to infinite
if (spatial_tile_config == 1000) {
bucket_info.space[0].upper_bound = static_cast<int>(2e10);
}
if (reduce_tile_config == 1000) {
bucket_info.space[1].upper_bound = static_cast<int>(2e10);
}
file_database.AddConfig(
cinn::common::DefaultTarget(), bucket_info, tile_bestconfig, 0);
LOG(INFO) << "spatial tile dimension lower bound = " << s_dimension_lower
<< ", reduce tile dimension lower bound = " << r_dimension_lower
<< std::endl;
LOG(INFO) << "min score = " << search_res.first;
LOG(INFO) << "best candidate: "
<< cinn::utils::Join<int64_t>(search_res.second, ", ");
if (r_dimension_lower * s_dimension_lower >= (2048 * 1024)) {
sleep(15);
} else {
sleep(2);
}
}
/**
* @brief Test case for the ConfigSearcher.
*
* This test case performs a search for the best configuration using the
* ConfigSearcher. It iterates over different spatial and reduce tile sizes and
* constructs a pir::Program. The search is performed using a
* ScheduleConfigSearcher, which takes into account candidate ranges and
* constraints. The objective function used for the search is a
* WeightedSamplingTrailObjectiveFunc. The search results are logged, including
* the minimum score and the best candidate configuration found.
*/
void TestSearchForTileConfig(int spatial_l_bound,
int spatial_r_bound,
int reduce_l_bound,
int reduce_r_bound,
bool is_s_dynamic,
bool is_r_dynamic,
bool search_single_large) {
FLAGS_cinn_measure_kernel_time = true;
FLAGS_enable_cinn_compile_cache = false;
FLAGS_tile_config_policy = "search";
// set tile_file path to test path when user use default setting
std::string root_path = FLAGS_cinn_tile_config_filename_label;
if (root_path == "") {
const std::string kTestFileDir = "./tile_file_test/";
FLAGS_cinn_tile_config_filename_label = kTestFileDir;
}
// Define the search space bounds and sampling probabilities.
int spatial_left_bound = spatial_l_bound;
int spatial_right_bound = spatial_r_bound; // for easy test, set to 2. for
// the whole test, set to 4096
int reduce_left_bound = reduce_l_bound;
int reduce_right_bound = reduce_r_bound; // for easy test : set to 2. for the
// whole test, set to 4096
bool is_spatial_dynamic = is_s_dynamic;
bool is_reduce_dynamic = is_r_dynamic;
// Define the initial grid size for the spatial and reduction dimensions
int spatial_tile_config = 0, reduce_tile_config = 0;
int spatial_tile_width = 0, reduce_tile_width = 0;
// Define weight for each dimension
double s_weight = (is_spatial_dynamic ? s_w : 1.0);
double r_weight = (is_reduce_dynamic ? r_w : 1.0);
// (I) Search in the small area,
// i.e, S:[2-4096]*R:[2-4096]
for (int s_dimension_lower = spatial_left_bound;
s_dimension_lower < spatial_right_bound ||
s_dimension_lower == spatial_right_bound &&
spatial_left_bound == spatial_right_bound;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config = get_tile_size_config_in_small_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = reduce_left_bound;
r_dimension_lower < reduce_right_bound ||
r_dimension_lower == reduce_right_bound &&
reduce_left_bound == reduce_right_bound;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_small_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
search_then_save_one_window(is_spatial_dynamic,
is_reduce_dynamic,
s_dimension_lower,
r_dimension_lower,
spatial_tile_width,
reduce_tile_width,
spatial_tile_config,
reduce_tile_config,
s_weight,
r_weight);
}
}
if (search_single_large) {
// (II) Search in the single large areas,
// i.e., S:[4096-32768]*R:[2-1024], S:[2-1024]*R:[4096-32768]
for (int s_dimension_lower = 2; s_dimension_lower < 1024;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config =
get_tile_size_config_in_large_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = 4096; r_dimension_lower < 32768;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_large_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
search_then_save_one_window(is_spatial_dynamic,
is_reduce_dynamic,
s_dimension_lower,
r_dimension_lower,
spatial_tile_width,
reduce_tile_width,
spatial_tile_config,
reduce_tile_config,
s_weight,
r_weight);
}
}
for (int s_dimension_lower = 4096; s_dimension_lower < 32768;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config =
get_tile_size_config_in_large_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = 2; r_dimension_lower < 1024;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_large_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
search_then_save_one_window(is_spatial_dynamic,
is_reduce_dynamic,
s_dimension_lower,
r_dimension_lower,
spatial_tile_width,
reduce_tile_width,
spatial_tile_config,
reduce_tile_config,
s_weight,
r_weight);
}
}
}
}
TEST(ConfigSearcher, TestDynamicDynamic) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 4096
int reduce_left_bound = 2;
int reduce_right_bound = 2; // To reproduce, set it to 4096
bool is_spatial_dynamic = true;
bool is_reduce_dynamic = true;
bool search_single_large =
false; // To search rsingle large area, set it to true
TestSearchForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
search_single_large);
}
TEST(ConfigSearcher, TestDynamicReduce) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 4096
int reduce_left_bound = 2;
int reduce_right_bound = 2; // To reproduce, set it to 4096
bool is_spatial_dynamic = false;
bool is_reduce_dynamic = true;
bool search_single_large =
false; // To search rsingle large area, set it to true
TestSearchForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
search_single_large);
}
TEST(ConfigSearcher, TestDynamicSpatial) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 4096
int reduce_left_bound = 2;
int reduce_right_bound = 2; // To reproduce, set it to 4096
bool is_spatial_dynamic = true;
bool is_reduce_dynamic = false;
bool search_single_large =
false; // To search single large area, set it to true
TestSearchForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
search_single_large);
}
@@ -0,0 +1,10 @@
if(NOT WITH_ROCM)
paddle_test(if_op_test SRCS if_op_test.cc)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(if_op_test)
endif()
paddle_test(while_op_test SRCS while_op_test.cc)
endif()
@@ -0,0 +1,234 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <iostream>
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/control_flow_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
PD_DECLARE_KERNEL(full, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(add, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(matmul_grad, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(add_grad, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(matmul, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(less_than, CPU, ALL_LAYOUT);
using namespace paddle::dialect; // NOLINT
TEST(if_op_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
auto full_op = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{1}, true, phi::DataType::BOOL);
auto if_op = builder.Build<paddle::dialect::IfOp>(
full_op.out(), std::vector<pir::Type>{builder.bool_type()});
auto& true_block = if_op.true_block();
builder.SetInsertionPointToStart(&true_block);
auto full_op_1 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{2}, true, phi::DataType::BOOL);
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_1.out()});
auto& false_block = if_op.false_block();
builder.SetInsertionPointToStart(&false_block);
auto full_op_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{3}, true, phi::DataType::BOOL);
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_2.out()});
LOG(INFO) << program;
}
TEST(if_op_test, build_by_block) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
auto full_op = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{1}, true, phi::DataType::BOOL);
// construct true block
std::unique_ptr<pir::Block> true_block(new pir::Block());
builder.SetInsertionPointToStart(true_block.get());
auto full_op_1 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{2}, true, phi::DataType::BOOL);
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_1.out()});
// construct false block
std::unique_ptr<pir::Block> false_block(new pir::Block());
builder.SetInsertionPointToStart(false_block.get());
auto full_op_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{3}, true, phi::DataType::BOOL);
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_2.out()});
builder.SetInsertionPointToBlockEnd(block);
auto if_op = builder.Build<paddle::dialect::IfOp>(
full_op.out(), std::move(true_block), std::move(false_block));
EXPECT_FALSE(true_block);
EXPECT_FALSE(false_block);
EXPECT_EQ(full_op_2->GetParentProgram(), &program);
LOG(INFO) << program;
std::vector<pir::Block*> vec;
for (auto& block : if_op->blocks()) {
vec.push_back(&block);
}
EXPECT_EQ(vec.size(), 2u);
EXPECT_EQ(vec[0], &if_op.true_block());
EXPECT_EQ(vec[1], &if_op.false_block());
EXPECT_EQ(if_op.num_results(), 1u);
auto type = if_op.result_type(0).dyn_cast<DenseTensorType>();
EXPECT_TRUE(type);
EXPECT_EQ(type.dims(), common::DDim{-1});
}
TEST(if_op_test, network_with_backward) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::KernelDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
auto x = builder.Build<FullOp>(std::vector<int64_t>{2, 2}, 1.0f).out();
auto y = builder.Build<FullOp>(std::vector<int64_t>{2, 2}, 2.0f).out();
auto cond = builder.Build<LessThanOp>(x, y).out();
auto [stack_0, inlet_0, outlet_0] = builder.Build<pir::StackCreateOp>().out();
auto [stack_1, inlet_1, outlet_1] = builder.Build<pir::StackCreateOp>().out();
(void)(stack_0);
(void)(stack_1);
auto if_op = builder.Build<IfOp>(cond, std::vector<pir::Type>{x.type()});
builder.SetInsertionPointToStart(&if_op.true_block());
auto local1_z = builder.Build<AddOp>(x, y).out();
auto local1_w = builder.Build<AddOp>(local1_z, y).out();
builder.Build<pir::TuplePushOp>(inlet_0,
std::initializer_list<pir::Value>{local1_z});
builder.Build<pir::YieldOp>(std::vector<pir::Value>{local1_w});
builder.SetInsertionPointToStart(&if_op.false_block());
auto local2_z = builder.Build<MatmulOp>(x, y).out();
auto local2_w = builder.Build<MatmulOp>(local2_z, y).out();
builder.Build<pir::TuplePushOp>(inlet_1,
std::initializer_list<pir::Value>{local2_z});
builder.Build<pir::YieldOp>(std::vector<pir::Value>{local2_w});
builder.SetInsertionPointToBlockEnd(block);
// build backward network
auto out_grad = builder.Build<FullOp>(std::vector<int64_t>{2, 2}, 1.0f).out();
// the output of if_grad op is {x_grad, y_grad}
auto if_grad =
builder.Build<IfOp>(cond, std::vector<pir::Type>{x.type(), y.type()});
// construct the true block of if_grad
builder.SetInsertionPointToStart(&if_grad.true_block());
auto pop_local1_z =
builder.Build<pir::TuplePopOp>(outlet_0).outlet_element(0);
auto local1_add_grad_op = builder.Build<AddGradOp>(pop_local1_z, y, out_grad);
auto pop_local1_z_grad = local1_add_grad_op.x_grad(),
local1_y_grad_0 = local1_add_grad_op.y_grad();
auto local1_add_grad_op_1 = builder.Build<AddGradOp>(x, y, pop_local1_z_grad);
auto local1_x_grad = local1_add_grad_op_1.x_grad(),
local1_y_grad_1 = local1_add_grad_op_1.y_grad();
auto local1_y_grad =
builder.Build<AddOp>(local1_y_grad_0, local1_y_grad_1).out();
std::string x_grad = "x_grad";
builder.Build<pir::ShadowOutputOp>(local1_x_grad, x_grad);
std::string y_grad = "y_grad";
builder.Build<pir::ShadowOutputOp>(local1_y_grad, y_grad);
std::string z_grad = "z_grad";
builder.Build<pir::ShadowOutputOp>(pop_local1_z_grad, z_grad);
builder.Build<pir::YieldOp>(
std::vector<pir::Value>{local1_x_grad, local1_y_grad});
// construct the false block of if_grad
builder.SetInsertionPointToStart(&if_grad.false_block());
auto pop_local2_z =
builder.Build<pir::TuplePopOp>(outlet_1).outlet_element(0);
auto local2_matmul_grad_op =
builder.Build<MatmulGradOp>(pop_local2_z, y, out_grad);
auto pop_local2_z_grad = local2_matmul_grad_op.x_grad(),
local2_y_grad_0 = local2_matmul_grad_op.y_grad();
auto local2_matmul_grad_op_1 =
builder.Build<MatmulGradOp>(x, y, pop_local2_z_grad);
auto local2_x_grad = local2_matmul_grad_op_1.x_grad(),
local2_y_grad_1 = local2_matmul_grad_op_1.y_grad();
auto local2_y_grad =
builder.Build<AddOp>(local2_y_grad_0, local2_y_grad_1).out();
builder.Build<pir::YieldOp>(
std::vector<pir::Value>{local2_x_grad, local2_y_grad});
builder.SetInsertionPointToBlockEnd(block);
LOG(INFO) << program;
auto kernel_program = pir::PdOpLowerToKernelPass(&program);
auto place = phi::CPUPlace();
#if defined(PADDLE_WITH_CUDA)
place = phi::GPUPlace();
#endif
paddle::framework::Scope scope;
paddle::framework::InterpreterCore test_core(
place, {}, kernel_program->block(), &scope);
test_core.SetSkipGcVars({x_grad, y_grad, z_grad});
test_core.Run({});
auto x_grad_tensor = test_core.DebugVar(x_grad)->Get<phi::DenseTensor>();
auto y_grad_tensor = test_core.DebugVar(y_grad)->Get<phi::DenseTensor>();
auto z_grad_tensor = test_core.DebugVar(z_grad)->Get<phi::DenseTensor>();
EXPECT_EQ(x_grad_tensor.data<float>()[0], 1.0);
EXPECT_EQ(y_grad_tensor.data<float>()[0], 2.0);
EXPECT_EQ(z_grad_tensor.data<float>()[0], 1.0);
}
@@ -0,0 +1,196 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <iostream>
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/control_flow_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
PD_DECLARE_KERNEL(full, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(less_than, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(add, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(add_grad, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(add_n, CPU, ALL_LAYOUT);
using namespace paddle::dialect; // NOLINT
// example for while_op use
// while(i < ten) { i = i + 1;}
TEST(while_op_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
auto i =
builder.Build<FullOp>(std::vector<int64_t>{1}, 1, phi::DataType::INT32)
.out();
auto ten =
builder.Build<FullOp>(std::vector<int64_t>{1}, 10, phi::DataType::INT32)
.out();
// compute condition value: i < ten
auto cond_value = builder.Build<LessThanOp>(i, ten).out();
auto while_op =
builder.Build<WhileOp>(cond_value, std::vector<pir::Value>{i, ten});
// { i = i + 1}
pir::Block& body_block = while_op.body();
auto body_i_argument = body_block.arg(0);
auto body_ten_argument = body_block.arg(1);
builder.SetInsertionPointToStart(&body_block);
auto one =
builder.Build<FullOp>(std::vector<int64_t>{1}, 1, phi::DataType::INT32)
.out();
auto new_i = builder.Build<AddOp>(body_i_argument, one).out();
// compute new condition value: new_i < new_ten
auto new_cond_value =
builder.Build<LessThanOp>(new_i, body_ten_argument).out();
builder.Build<pir::YieldOp>(
std::vector<pir::Value>{new_cond_value, new_i, body_ten_argument});
builder.SetInsertionPointAfter(while_op);
LOG(INFO) << program;
EXPECT_EQ(while_op.cond(), cond_value);
}
TEST(while_op_test, network_with_backward) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::KernelDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
auto i =
builder.Build<FullOp>(std::vector<int64_t>{1}, 0, phi::DataType::INT32)
.out();
auto ten =
builder.Build<FullOp>(std::vector<int64_t>{10}, 10, phi::DataType::INT32)
.out();
auto x = builder.Build<FullOp>(std::vector<int64_t>{2, 2}, 1.0f).out();
auto y = builder.Build<FullOp>(std::vector<int64_t>{2, 2}, 2.0f).out();
auto one =
builder.Build<FullOp>(std::vector<int64_t>{1}, 1, phi::DataType::INT32)
.out();
// def cond(i, x):
// return i < 10
// def body(i, x):
// return i + 1, x + y
// }
auto cond_value = builder.Build<LessThanOp>(i, ten).out();
auto [stack, inlet, outlet] = builder.Build<pir::StackCreateOp>().out();
(void)(stack);
auto while_op =
builder.Build<WhileOp>(cond_value, std::vector<pir::Value>{i, x});
// { return i + 1, x + y}
auto& body_block = while_op.body();
builder.SetInsertionPointToStart(&body_block);
auto body_i_argument = body_block.arg(0);
auto body_x_argument = body_block.arg(1);
auto new_i = builder.Build<AddOp>(body_i_argument, one).out();
auto new_x = builder.Build<AddOp>(body_x_argument, y).out();
// compute new condition value: new_i < new_ten
auto new_cond_value = builder.Build<LessThanOp>(new_i, ten).out();
builder.Build<pir::TuplePushOp>(
inlet, std::initializer_list<pir::Value>{body_x_argument});
builder.Build<pir::YieldOp>(
std::vector<pir::Value>{new_cond_value, new_i, new_x});
builder.SetInsertionPointAfter(while_op);
auto i_out = while_op->result(0);
auto x_out = while_op->result(1);
EXPECT_EQ(i_out.type(), i.type());
EXPECT_EQ(x_out.type(), x.type());
// build backward network
auto x_out_grad =
builder.Build<FullOp>(std::vector<int64_t>{2, 2}, 1.0f).out();
auto zero = builder.Build<FullOp>(std::vector<int64_t>{2, 2}, 0.0).out();
// the input of while_grad op is {x_out_grad, zero}
// the output of while_grad op is {x_grad, y_grad}
// the value {i , one, ten} is stop gradient.
auto bwd_cond = builder.Build<HasElementsOp>(stack).out();
auto while_grad = builder.Build<WhileOp>(
bwd_cond, std::vector<pir::Value>{x_out_grad, zero});
pir::Block& bwd_body_block = while_grad.body();
builder.SetInsertionPointToStart(&bwd_body_block);
auto local_x_out_grad_arg = bwd_body_block.arg(0);
auto local_y_grad_arg = bwd_body_block.arg(1);
auto pop_op = builder.Build<pir::TuplePopOp>(outlet);
auto bwd_body_x_argument = pop_op.outlet_element(0);
auto add_grad_op =
builder.Build<AddGradOp>(bwd_body_x_argument, y, local_x_out_grad_arg);
auto bwd_body_x_argument_grad = add_grad_op.x_grad();
auto local_y_grad = add_grad_op.y_grad();
// accumulate gradient
auto combine_y = builder
.Build<pir::CombineOp>(std::vector<pir::Value>{
local_y_grad, local_y_grad_arg})
.out();
auto local_next_y_grad = builder.Build<AddNOp>(combine_y).out();
auto next_bwd_cond = builder.Build<HasElementsOp>(stack).out();
builder.Build<pir::YieldOp>(std::vector<pir::Value>{
next_bwd_cond, bwd_body_x_argument_grad, local_next_y_grad});
auto x_grad = while_grad.result(0);
auto y_grad = while_grad.result(1);
EXPECT_EQ(x_grad.type(), x.type());
EXPECT_EQ(y_grad.type(), y.type());
LOG(INFO) << program;
auto place = phi::CPUPlace();
#ifdef PADDLE_WITH_CUDA
place = phi::GPUPlace(0);
#endif
auto kernel_program = pir::PdOpLowerToKernelPass(&program, place);
paddle::framework::Scope scope;
paddle::framework::InterpreterCore test_core(
place, {}, kernel_program->block(), &scope);
test_core.Run({});
}
+56
View File
@@ -0,0 +1,56 @@
paddle_test(type_test SRCS type_test.cc DEPS common)
paddle_test(type_util_test SRCS type_util_test.cc DEPS pir common)
paddle_test(ir_attribute_test SRCS ir_attribute_test.cc)
paddle_test(ir_value_test SRCS ir_value_test.cc)
paddle_test(ir_op_test SRCS ir_op_test.cc DEPS test_dialect)
paddle_test(ir_region_test SRCS ir_region_test.cc)
paddle_test(ir_builder_test SRCS ir_builder_test.cc)
paddle_test(ir_program_test SRCS ir_program_test.cc)
paddle_test(ir_infershape_test SRCS ir_infershape_test.cc)
paddle_test(scalar_attribute_test SRCS scalar_attribute_test.cc)
paddle_test(paddle_fatal_test SRCS paddle_fatal_test.cc)
file(
DOWNLOAD https://paddle-ci.gz.bcebos.com/ir_translator_test/resnet50_main.prog
${CMAKE_CURRENT_BINARY_DIR}/resnet50_main.prog
EXPECTED_MD5 b64c0ad3c96d99fc37d12094623ce1ad)
file(
DOWNLOAD
https://paddle-ci.gz.bcebos.com/ir_translator_test/resnet50_startup.prog
${CMAKE_CURRENT_BINARY_DIR}/resnet50_startup.prog
EXPECTED_MD5 6affc5f40f0f0bb84d956919b95eaf50)
file(
DOWNLOAD
https://paddle-ci.gz.bcebos.com/ir_translator_test/conditional_block_test.prog
${CMAKE_CURRENT_BINARY_DIR}/conditional_block_test.prog
EXPECTED_MD5 cf9dc869ca7f69e2d57b38dbf8427134)
file(
DOWNLOAD https://paddle-ci.gz.bcebos.com/ir_translator_test/while_op_test.prog
${CMAKE_CURRENT_BINARY_DIR}/while_op_test.prog
EXPECTED_MD5 290164ae52a496332b0be5829fc93bcd)
paddle_test(program_translator_test SRCS program_translator_test.cc)
paddle_test(add_dialect_parser_test SRCS add_dialect_parser_test.cc DEPS gtest)
paddle_test(ir_op_info_test SRCS op_info_test.cc)
paddle_test(ir_op_yaml_info_parser_test SRCS op_yaml_info_parser_test.cc)
paddle_test(ir_type_converter_test SRCS ir_type_converter_test.cc)
paddle_test(type_interface_test SRCS type_interface_test.cc DEPS test_dialect
gtest)
paddle_test(block_operand_test SRCS block_operand_test.cc DEPS test_dialect
gtest)
paddle_test(block_argument_test SRCS block_argument_test.cc DEPS test_dialect)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(ir_op_test)
endif()
@@ -0,0 +1,76 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/ir_adaptor/translator/translate.h"
#include "paddle/phi/core/framework/framework.pb.h"
#include "paddle/pir/include/core/attribute.h"
#include "paddle/pir/include/core/attribute_base.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_attribute_storage.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/utils.h"
#include "test/cpp/pir/tools/macros_utils.h"
using OperatorDialect = paddle::dialect::OperatorDialect;
using AttributeStorage = pir::AttributeStorage;
class TestParserDialect : public pir::Dialect {
public:
explicit TestParserDialect(pir::IrContext* context);
static const char* name() { return "tp"; }
void PrintAttribute(pir::Attribute attr, std::ostream& os) const; // NOLINT
private:
void initialize();
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(TestParserDialect);
IR_DEFINE_EXPLICIT_TYPE_ID(TestParserDialect);
DECLARE_BASE_TYPE_ATTRIBUTE_STORAGE(CharAttributeStorage, char);
class CharAttribute : public pir::Attribute {
public:
using Attribute::Attribute;
DECLARE_ATTRIBUTE_UTILITY_FUNCTOR(CharAttribute, CharAttributeStorage);
char data() const;
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(CharAttribute);
IR_DEFINE_EXPLICIT_TYPE_ID(CharAttribute);
void TestParserDialect::initialize() { RegisterAttributes<CharAttribute>(); }
char CharAttribute::data() const { return storage()->data(); }
TestParserDialect::TestParserDialect(pir::IrContext* context)
: pir::Dialect(name(), context, pir::TypeId::get<TestParserDialect>()) {
initialize();
}
void TestParserDialect::PrintAttribute(pir::Attribute attr,
std::ostream& os) const {
auto byte_attr = attr.dyn_cast<CharAttribute>();
os << "(tp.char)" << byte_attr.data();
}
+125
View File
@@ -0,0 +1,125 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/program.h"
#include "test/cpp/pir/tools/test_dialect.h"
#include "test/cpp/pir/tools/test_op.h"
TEST(block_argument_test, base) {
pir::IrContext ctx;
ctx.GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(&ctx);
pir::Block* block = program.block();
pir::Builder builder(&ctx, block);
std::vector<pir::Type> types(3, builder.float32_type());
block->AddArgs(types);
EXPECT_FALSE(block->args_empty());
EXPECT_EQ(block->args_size(), types.size());
uint32_t index = 0;
for (auto iter = block->args_begin(); iter != block->args_end(); ++iter) {
EXPECT_EQ(iter->dyn_cast<pir::BlockArgument>().index(), index++);
}
pir::Value value = block->arg(0);
pir::BlockArgument argument = value.dyn_cast<pir::BlockArgument>();
EXPECT_TRUE(argument);
EXPECT_EQ(argument.owner(), block);
EXPECT_EQ(block->arg_type(0), types[0]);
pir::OpResult op_result = value.dyn_cast<pir::OpResult>();
EXPECT_FALSE(op_result);
auto op = builder.Build<pir::ConstantOp>(builder.double_attr(1.0),
builder.float64_type());
value = op.result(0);
argument = value.dyn_cast<pir::BlockArgument>();
EXPECT_FALSE(argument);
op_result = value.dyn_cast<pir::OpResult>();
EXPECT_TRUE(op_result);
block->AddArgs({builder.bool_type()});
EXPECT_EQ(block->args_size(), 4u);
value = block->AddArg(builder.bool_type());
EXPECT_EQ(value.type(), builder.bool_type());
}
TEST(block_argument_test, kwargs) {
pir::IrContext ctx;
ctx.GetOrRegisterDialect<test::TestDialect>();
pir::Program program(&ctx);
pir::Block* block = program.block();
pir::Builder builder(&ctx, block);
std::unordered_map<std::string, pir::Type> types{
{"a", builder.float32_type()},
{"b", builder.float32_type()},
{"c", builder.float32_type()}};
block->AddKwargs(types);
EXPECT_FALSE(block->kwargs_empty());
EXPECT_EQ(block->kwargs_size(), types.size());
for (auto iter = block->kwargs_begin(); iter != block->kwargs_end(); ++iter) {
EXPECT_EQ(iter->second.dyn_cast<pir::BlockArgument>().type(),
builder.float32_type());
}
pir::Value value = block->kwarg("a");
pir::BlockArgument argument = value.dyn_cast<pir::BlockArgument>();
EXPECT_TRUE(argument);
EXPECT_EQ(argument.owner(), block);
EXPECT_EQ(block->kwarg_type("a"), types["a"]);
pir::OpResult op_result = value.dyn_cast<pir::OpResult>();
EXPECT_FALSE(op_result);
auto op = builder.Build<pir::ConstantOp>(builder.double_attr(1.0),
builder.float64_type());
value = op.result(0);
argument = value.dyn_cast<pir::BlockArgument>();
EXPECT_FALSE(argument);
op_result = value.dyn_cast<pir::OpResult>();
EXPECT_TRUE(op_result);
value = block->AddKwarg("d", builder.bool_type());
EXPECT_EQ(block->kwargs_size(), 4u);
EXPECT_EQ(value.type(), builder.bool_type());
}
TEST(block_argument_test, fatal) {
auto block = new pir::Block();
auto arg = block->AddArg(nullptr);
auto op = pir::Operation::Create({arg}, {}, {}, nullptr);
EXPECT_DEATH(delete block,
"Destroyed a position block argument that is still in use.*");
auto kwarg = block->AddKwarg("a", nullptr);
arg.ReplaceAllUsesWith(kwarg);
block->ClearArgs();
EXPECT_DEATH(delete block,
"Destroyed a keyword block argument that is still in use.*");
op->Destroy();
op = pir::Operation::Create({}, {}, {}, nullptr, 0, {block});
EXPECT_DEATH(delete block, "Destroyed a block that is still in use.*");
op->Destroy();
delete block;
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/program.h"
#include "test/cpp/pir/tools/test_dialect.h"
#include "test/cpp/pir/tools/test_op.h"
TEST(block_operand_test, type_block) {
pir::IrContext ctx;
ctx.GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(&ctx);
pir::Block* block = program.block();
pir::Builder builder(&ctx, block);
test::RegionOp region_op = builder.Build<test::RegionOp>();
auto& region = region_op->region(0);
pir::Block* block_1 = new pir::Block();
pir::Block* block_2 = new pir::Block();
pir::Block* block_3 = new pir::Block();
region.push_back(block_1);
region.push_back(block_2);
region.push_back(block_3);
builder.SetInsertionPointToBlockEnd(block_1);
auto op1 = builder.Build<test::BranchOp>(std::vector<pir::Value>{}, block_2);
EXPECT_TRUE(block_2->HasOneUse());
EXPECT_FALSE(block_2->use_empty());
auto iter_begin = block_2->use_begin();
auto iter_end = block_2->use_end();
auto block_operand = op1->block_operand(0);
auto iter_curr = iter_begin++;
EXPECT_EQ(iter_begin, iter_end);
EXPECT_EQ(*iter_curr, block_operand);
EXPECT_EQ(block_2->first_use(), block_operand);
EXPECT_EQ(iter_curr->owner(), op1);
builder.SetInsertionPointToBlockEnd(block_3);
auto op3 = builder.Build<test::BranchOp>(std::vector<pir::Value>{}, block_1);
block_operand = op3->block_operand(0);
block_operand.set_source(block_2);
EXPECT_EQ(block_2, block_operand.source());
}
+74
View File
@@ -0,0 +1,74 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <map>
#include "paddle/pir/include/core/attribute.h"
#include "paddle/pir/include/core/attribute_base.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "test/cpp/pir/tools/macros_utils.h"
class AttributeA {};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(AttributeA)
IR_DEFINE_EXPLICIT_TYPE_ID(AttributeA)
struct FakeDialect : pir::Dialect {
explicit FakeDialect(pir::IrContext *context)
: pir::Dialect(name(), context, pir::TypeId::get<FakeDialect>()) {}
static const char *name() { return "fake"; }
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(FakeDialect)
IR_DEFINE_EXPLICIT_TYPE_ID(FakeDialect)
TEST(attribute_test, attribute_base) {
// Test 1: Test the function of IrContext to register Dialect.
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Dialect *fake_dialect = ctx->GetOrRegisterDialect<FakeDialect>();
// Test 2: Test the get method of AbstractType.
pir::TypeId a_id = pir::TypeId::get<AttributeA>();
pir::AbstractAttribute abstract_attribute_a =
pir::AbstractAttribute::get(a_id, *fake_dialect);
EXPECT_EQ(abstract_attribute_a.type_id(), a_id);
// Test 3: Test the constructor of AbstractStorage.
pir::AttributeStorage storage_a(&abstract_attribute_a);
EXPECT_EQ(storage_a.abstract_attribute().type_id(),
abstract_attribute_a.type_id());
}
TEST(attribute_test, built_in_attribute) {
pir::IrContext *ctx = pir::IrContext::Instance();
// Test 1: Test the parametric built-in attribute of IrContext.
std::string str_tmp = "string_a";
pir::Attribute string_attr_1 = pir::StrAttribute::get(ctx, str_tmp);
pir::Attribute string_attr_2 = pir::StrAttribute::get(ctx, str_tmp);
EXPECT_EQ(string_attr_1, string_attr_2);
EXPECT_EQ(pir::StrAttribute::classof(string_attr_1), 1);
// Test 2: Test isa and dyn_cast.
EXPECT_EQ(string_attr_1.isa<pir::StrAttribute>(), true);
pir::StrAttribute string_attr_cast_1 =
string_attr_1.dyn_cast<pir::StrAttribute>();
EXPECT_EQ(string_attr_cast_1.isa<pir::StrAttribute>(), true);
EXPECT_EQ(string_attr_cast_1.size() == 8, 1);
pir::Int32Type i32_type = pir::Int32Type::get(ctx);
pir::Attribute type_attr = pir::TypeAttribute::get(ctx, i32_type);
EXPECT_TRUE(type_attr.isa<pir::TypeAttribute>());
EXPECT_EQ(type_attr.dyn_cast<pir::TypeAttribute>().data().type_id(),
i32_type.type_id());
}
+57
View File
@@ -0,0 +1,57 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <map>
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
TEST(builder_test, type_api) {
pir::IrContext ctx;
pir::Builder builder(&ctx);
EXPECT_EQ(pir::UInt8Type::get(&ctx), builder.uint8_type());
EXPECT_EQ(pir::Int8Type::get(&ctx), builder.int8_type());
EXPECT_EQ(pir::VectorType::get(&ctx, std::vector<pir::Type>()),
builder.vec_type({}));
EXPECT_EQ(pir::BFloat16Type::get(&ctx), builder.bfloat16_type());
EXPECT_EQ(pir::Float32Type::get(&ctx), builder.float32_type());
EXPECT_EQ(pir::Float64Type::get(&ctx), builder.float64_type());
EXPECT_EQ(pir::IndexType::get(&ctx), builder.index_type());
EXPECT_EQ(pir::Int16Type::get(&ctx), builder.int16_type());
EXPECT_EQ(pir::BoolType::get(&ctx), builder.bool_type());
EXPECT_EQ(pir::Complex64Type::get(&ctx), builder.complex64_type());
EXPECT_EQ(pir::Complex128Type::get(&ctx), builder.complex128_type());
EXPECT_EQ(pir::Float8E4M3FNType::get(&ctx), builder.float8e4m3fn_type());
EXPECT_EQ(pir::Float8E5M2Type::get(&ctx), builder.float8e5m2_type());
}
TEST(builder_test, attribute_api) {
pir::IrContext ctx;
pir::Builder builder(&ctx);
EXPECT_EQ(pir::StrAttribute::get(&ctx, "test"), builder.str_attr("test"));
EXPECT_EQ(pir::BoolAttribute::get(&ctx, true), builder.bool_attr(true));
EXPECT_EQ(pir::FloatAttribute::get(&ctx, 0.2f), builder.float_attr(0.2f));
EXPECT_EQ(pir::DoubleAttribute::get(&ctx, 2.0), builder.double_attr(2.0));
EXPECT_EQ(pir::Int32Attribute::get(&ctx, 2), builder.int32_attr(2));
EXPECT_EQ(pir::Int64Attribute::get(&ctx, 2), builder.int64_attr(2));
EXPECT_EQ(pir::IndexAttribute::get(&ctx, 2), builder.index_attr(2));
EXPECT_EQ(pir::ArrayAttribute::get(&ctx, std::vector<pir::Attribute>()),
builder.array_attr({}));
EXPECT_EQ(pir::PointerAttribute::get(&ctx, nullptr),
builder.pointer_attr(nullptr));
}
+112
View File
@@ -0,0 +1,112 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/pir/include/core/block.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/op_base.h"
#include "paddle/pir/include/core/region.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/framework/tensor.h"
#include "paddle/fluid/framework/variable.h"
#include "paddle/fluid/framework/variable_helper.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_context.h"
#include "paddle/phi/core/kernel_factory.h"
#include "paddle/fluid/pir/dialect/operator/interface/infermeta.h"
#include "paddle/fluid/platform/init.h"
#include "paddle/phi/core/infermeta_utils.h"
#include "paddle/phi/infermeta/nullary.h"
#include "test/cpp/pir/tools/macros_utils.h"
// Define op
class OperationTest
: public pir::Op<OperationTest, paddle::dialect::InferMetaInterface> {
public:
using Op::Op;
static const char *name() { return "test.operation2"; }
static constexpr uint32_t attributes_num = 2;
static const char *attributes_name[attributes_num]; // NOLINT
static void VerifySig() {}
static void InferMeta(phi::InferMetaContext *infer_meta) {
auto fn = PD_INFER_META(phi::CreateInferMeta);
fn(infer_meta);
}
static std::vector<pir::Type> InferMeta(
const std::vector<pir::Value> &input_values, pir::AttributeMap *) {
VLOG(4) << "Start infermeta OperationTest";
std::vector<pir::Type> argument_outputs;
return argument_outputs;
}
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(OperationTest)
IR_DEFINE_EXPLICIT_TYPE_ID(OperationTest)
const char *OperationTest::attributes_name[attributes_num] = { // NOLINT
"op2_attr1",
"op2_attr2"};
// Define a dialect, op1 and op2 will be registered by this dialect.
class TestDialect : public pir::Dialect {
public:
explicit TestDialect(pir::IrContext *context)
: pir::Dialect(name(), context, pir::TypeId::get<TestDialect>()) {
initialize();
}
static const char *name() { return "test"; }
private:
void initialize() { RegisterOps<OperationTest>(); }
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(TestDialect)
IR_DEFINE_EXPLICIT_TYPE_ID(TestDialect)
TEST(infershape_test, infershape_test) {
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Dialect *test_dialect = ctx->GetOrRegisterDialect<TestDialect>();
EXPECT_EQ(test_dialect != nullptr, true);
// (2) Get registered operations.
std::string op_name = OperationTest::name();
pir::OpInfo op_info = ctx->GetRegisteredOpInfo(op_name);
std::vector<pir::Value> op_inputs = {};
std::vector<pir::Type> op_output_types = {pir::Float32Type::get(ctx)};
pir::Operation *op =
pir::Operation::Create(op_inputs, {}, op_output_types, op_info);
paddle::dialect::InferMetaInterface interface =
op->dyn_cast<paddle::dialect::InferMetaInterface>();
phi::InferMetaContext infer_meta_ctx;
infer_meta_ctx.EmplaceBackAttr(phi::IntArray({5, 6}));
infer_meta_ctx.EmplaceBackAttr(phi::DataType::FLOAT32);
phi::DenseTensor tensor;
infer_meta_ctx.EmplaceBackOutput(phi::MetaTensor(&tensor));
interface.InferMeta(&infer_meta_ctx);
EXPECT_EQ(tensor.dims().size(), 2);
EXPECT_EQ(tensor.dims()[0], 5);
EXPECT_EQ(tensor.dims()[1], 6);
}
+562
View File
@@ -0,0 +1,562 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <sstream>
#include "paddle/common/enforce.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/phi/core/tensor_meta.h"
#include "paddle/pir/include/core/block.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/ir_printer.h"
#include "paddle/pir/include/core/op_base.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/region.h"
#include "test/cpp/pir/tools/test_dialect.h"
#include "test/cpp/pir/tools/test_op.h"
#include "test/cpp/pir/tools/test_pir_utils.h"
TEST(op_test, region_test) {
// (1) Register Dialect, Operation1, Operation2 into IrContext.
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Dialect *test_dialect = ctx->GetOrRegisterDialect<test::TestDialect>();
EXPECT_EQ(test_dialect != nullptr, true);
// (2) Get registered operations.
pir::OpInfo op1_info = ctx->GetRegisteredOpInfo(test::Operation1::name());
pir::OpInfo op2_info = ctx->GetRegisteredOpInfo(test::Operation2::name());
pir::Operation *op1 = pir::Operation::Create(
{},
test::CreateAttributeMap({"op1_attr1", "op1_attr2"},
{"op1_attr1", "op1_attr2"}),
{pir::Float32Type::get(ctx)},
op1_info);
pir::Operation *op_2 =
pir::Operation::Create({}, {}, {pir::Float32Type::get(ctx)}, op2_info);
pir::OperationArgument argument(op2_info);
argument.output_types = {pir::Float32Type::get(ctx)};
argument.AddRegion(nullptr);
pir::Operation *op3 = pir::Operation::Create(std::move(argument));
pir::Region &region = op3->region(0);
EXPECT_EQ(region.empty(), true);
// (3) Test custom operation printer
std::stringstream ss;
op1->Print(ss);
EXPECT_EQ(ss.str(), "(%0) = \"test.operation1\" ()");
region.push_back(new pir::Block());
region.push_front(new pir::Block());
region.insert(region.begin(), new pir::Block());
auto &block = region.front();
block.push_front(op1);
block.insert(block.begin(), op_2);
op3->Destroy();
}
TEST(op_test, module_op_death) {
pir::IrContext *ctx = pir::IrContext::Instance();
pir::OpInfo op_info = ctx->GetRegisteredOpInfo(pir::ModuleOp::name());
std::vector<pir::Value> inputs{pir::Value()};
pir::AttributeMap attrs{{"program", pir::Int32Attribute::get(ctx, 1)}};
std::vector<pir::Type> output_types = {pir::Float32Type::get(ctx)};
EXPECT_THROW(pir::Operation::Create(inputs, {}, {}, op_info),
common::enforce::EnforceNotMet);
EXPECT_THROW(pir::Operation::Create({}, attrs, {}, op_info),
common::enforce::EnforceNotMet);
EXPECT_THROW(pir::Operation::Create({}, {}, output_types, op_info),
common::enforce::EnforceNotMet);
pir::Program program(ctx);
EXPECT_EQ(program.module_op().program(), &program);
EXPECT_EQ(program.module_op().ir_context(), ctx);
program.module_op()->set_attribute("program",
pir::PointerAttribute::get(ctx, &program));
}
TEST(op_test, trait_and_interface) {
pir::IrContext ctx;
ctx.GetOrRegisterDialect<test::TestDialect>();
pir::Program program(&ctx);
auto block = program.block();
pir::Builder builder(&ctx, block);
auto op1 = builder.Build<test::Operation1>();
auto op2 = builder.Build<test::Operation2>();
EXPECT_EQ(op1->HasTrait<test::ReadOnlyTrait>(), false);
EXPECT_EQ(op1->HasInterface<test::InferShapeInterface>(), false);
EXPECT_EQ(op2->HasTrait<test::ReadOnlyTrait>(), true);
EXPECT_EQ(op2->HasInterface<test::InferShapeInterface>(), true);
pir::OperationArgument argument(&ctx, "test.region");
EXPECT_THROW(builder.Build(std::move(argument)),
common::enforce::EnforceNotMet);
}
TEST(op_test, op_traits_test) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype = pir::Float32Type::get(ctx);
phi::DDim dims = {2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_dtype =
pir::DenseTensorType::get(ctx, dtype, dims, data_layout, lod, offset);
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims, {"op1_temp"}, {"op1_attr"}, dtype);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims, {"op2_temp"}, {"op2_attr"}, dtype);
block->push_back(op1);
block->push_back(op2);
auto op3 = builder.Build<test::TraitExampleOp>(
op1->result(0), op2->result(0), dense_tensor_dtype);
EXPECT_EQ(op3->HasTrait<pir::SameOperandsShapeTrait>(), true);
EXPECT_EQ(op3->HasTrait<pir::SameOperandsAndResultShapeTrait>(), true);
EXPECT_EQ(op3->HasTrait<pir::SameOperandsElementTypeTrait>(), true);
EXPECT_EQ(op3->HasTrait<pir::SameOperandsAndResultElementTypeTrait>(), true);
EXPECT_EQ(op3->HasTrait<pir::SameOperandsAndResultTypeTrait>(), true);
EXPECT_EQ(op3->HasTrait<pir::SameTypeOperandsTrait>(), true);
}
TEST(op_test, same_operands_shape_trait_test1) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
EXPECT_THROW(builder.Build<test::SameOperandsShapeTraitOp1>(),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_shape_trait_test2) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype1 = pir::Float32Type::get(ctx);
phi::DDim dims1 = {2, 2};
pir::Type dtype2 = pir::Float64Type::get(ctx);
phi::DDim dims2 = {2, 2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_dtype =
pir::DenseTensorType::get(ctx, dtype1, dims1, data_layout, lod, offset);
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims1, {"op1_temp"}, {"op1_attr"}, dtype1);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims2, {"op2_temp"}, {"op2_attr"}, dtype2);
block->push_back(op1);
block->push_back(op2);
EXPECT_THROW(builder.Build<test::SameOperandsShapeTraitOp2>(
op1->result(0), op2->result(0), dense_tensor_dtype),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_shape_trait_test1) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultShapeTraitOp1>(),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_shape_trait_test2) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype = pir::Float64Type::get(ctx);
phi::DDim dims = {2, 2, 2};
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims, {"op1_temp"}, {"op1_attr"}, dtype);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims, {"op2_temp"}, {"op2_attr"}, dtype);
block->push_back(op1);
block->push_back(op2);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultShapeTraitOp2>(
op1->result(0), op2->result(0)),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_shape_trait_test3) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype1 = pir::Float32Type::get(ctx);
phi::DDim dims1 = {2, 2};
pir::Type dtype2 = pir::Float64Type::get(ctx);
phi::DDim dims2 = {2, 2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_dtype =
pir::DenseTensorType::get(ctx, dtype1, dims1, data_layout, lod, offset);
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims1, {"op1_temp"}, {"op1_attr"}, dtype1);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims2, {"op2_temp"}, {"op2_attr"}, dtype2);
block->push_back(op1);
block->push_back(op2);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultShapeTraitOp3>(
op1->result(0), op2->result(0), dense_tensor_dtype),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_element_type_trait_test1) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
EXPECT_THROW(builder.Build<test::SameOperandsElementTypeTraitOp1>(),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_element_type_trait_test2) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype1 = pir::Float32Type::get(ctx);
pir::Type dtype2 = pir::Float64Type::get(ctx);
phi::DDim dims = {2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_dtype =
pir::DenseTensorType::get(ctx, dtype1, dims, data_layout, lod, offset);
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims, {"op1_temp"}, {"op1_attr"}, dtype1);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims, {"op2_temp"}, {"op2_attr"}, dtype2);
block->push_back(op1);
block->push_back(op2);
EXPECT_THROW(builder.Build<test::SameOperandsElementTypeTraitOp2>(
op1->result(0), op2->result(0), dense_tensor_dtype),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_element_type_trait_test1) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultElementTypeTraitOp1>(),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_element_type_trait_test2) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype = pir::Float32Type::get(ctx);
phi::DDim dims = {2, 2};
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims, {"op1_temp"}, {"op1_attr"}, dtype);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims, {"op2_temp"}, {"op2_attr"}, dtype);
block->push_back(op1);
block->push_back(op2);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultElementTypeTraitOp2>(
op1->result(0), op2->result(0)),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_element_type_trait_test3) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype1 = pir::Float32Type::get(ctx);
phi::DDim dims1 = {2, 2};
pir::Type dtype2 = pir::Float64Type::get(ctx);
phi::DDim dims2 = {2, 2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_dtype1 =
pir::DenseTensorType::get(ctx, dtype1, dims1, data_layout, lod, offset);
pir::DenseTensorType dense_tensor_dtype2 =
pir::DenseTensorType::get(ctx, dtype2, dims2, data_layout, lod, offset);
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims1, {"op1_temp"}, {"op1_attr"}, dtype1);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims2, {"op2_temp"}, {"op2_attr"}, dtype2);
block->push_back(op1);
block->push_back(op2);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultElementTypeTraitOp3>(
op1->result(0),
op2->result(0),
dense_tensor_dtype1,
dense_tensor_dtype1),
common::enforce::EnforceNotMet);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultElementTypeTraitOp3>(
op1->result(0),
op1->result(0),
dense_tensor_dtype1,
dense_tensor_dtype2),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_type_trait_test1) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultTypeTraitOp1>(),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_type_trait_test2) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype = pir::Float32Type::get(ctx);
phi::DDim dims = {2, 2};
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims, {"op1_temp"}, {"op1_attr"}, dtype);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims, {"op2_temp"}, {"op2_attr"}, dtype);
block->push_back(op1);
block->push_back(op2);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultTypeTraitOp2>(
op1->result(0), op2->result(0)),
common::enforce::EnforceNotMet);
}
TEST(op_test, same_operands_and_result_type_trait_test3) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype1 = pir::Float32Type::get(ctx);
phi::DDim dims1 = {2, 2};
pir::Type dtype2 = pir::Float64Type::get(ctx);
phi::DDim dims2 = {2, 2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_dtype1 =
pir::DenseTensorType::get(ctx, dtype1, dims1, data_layout, lod, offset);
pir::DenseTensorType dense_tensor_dtype2 =
pir::DenseTensorType::get(ctx, dtype2, dims2, data_layout, lod, offset);
pir::DenseTensorType dense_tensor_dtype3 =
pir::DenseTensorType::get(ctx, dtype1, dims2, data_layout, lod, offset);
pir::Operation *op1 =
test::CreateDenseTensorOp(ctx, dims1, {"op1_temp"}, {"op1_attr"}, dtype2);
pir::Operation *op2 =
test::CreateDenseTensorOp(ctx, dims2, {"op2_temp"}, {"op2_attr"}, dtype1);
block->push_back(op1);
block->push_back(op2);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultTypeTraitOp3>(
op1->result(0),
op2->result(0),
dense_tensor_dtype1,
dense_tensor_dtype2),
common::enforce::EnforceNotMet);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultTypeTraitOp3>(
op1->result(0),
op2->result(0),
dense_tensor_dtype1,
dense_tensor_dtype3),
common::enforce::EnforceNotMet);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultTypeTraitOp3>(
op1->result(0),
op2->result(0),
dense_tensor_dtype1,
dense_tensor_dtype1),
common::enforce::EnforceNotMet);
EXPECT_THROW(builder.Build<test::SameOperandsAndResultTypeTraitOp3>(
op2->result(0),
op1->result(0),
dense_tensor_dtype1,
dense_tensor_dtype1),
common::enforce::EnforceNotMet);
}
TEST(printer_test, custom_hooks) {
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Dialect *test_dialect = ctx->GetOrRegisterDialect<test::TestDialect>();
EXPECT_EQ(test_dialect != nullptr, true);
pir::OpInfo op1_info = ctx->GetRegisteredOpInfo(test::Operation1::name());
pir::OpInfo op2_info = ctx->GetRegisteredOpInfo(test::Operation2::name());
pir::Operation *op1 = pir::Operation::Create(
{},
test::CreateAttributeMap({"op1_attr1", "op1_attr2"},
{"op1_attr1", "op1_attr2"}),
{pir::Float32Type::get(ctx)},
op1_info);
pir::Operation *op2 = pir::Operation::Create(
{op1->result(0)}, {}, {pir::Float32Type::get(ctx)}, op2_info);
pir::Program program(ctx);
program.block()->push_back(op1);
program.block()->push_back(op2);
pir::PrintHooks hooks;
// this one retains old printing and adds new info
hooks.value_print_hook = [](pir::Value v, pir::IrPrinter &printer) {
printer.IrPrinter::PrintValue(v);
printer.os << " [extra info]";
};
// this one overrides old printing
hooks.op_print_hook = [](const pir::Operation &op, pir::IrPrinter &printer) {
printer.PrintOpResult(op);
printer.os << " :=";
printer.os << " \"" << op.name() << "\"";
printer.PrintOpOperands(op);
printer.PrintAttributeMap(op);
printer.os << " :";
printer.PrintOpReturnType(op);
};
hooks.attribute_print_hook = [](pir::Attribute attr,
pir::IrPrinter &printer) {
printer.os << "[PlaceHolder]";
};
hooks.type_print_hook = [](pir::Type type, pir::IrPrinter &printer) {
printer.os << "[" << type << "]";
};
std::stringstream ss;
ss << pir::CustomPrintHelper{program, hooks};
EXPECT_EQ(
ss.str(),
"{\n"
"(%0 [extra info]) := \"test.operation1\" () "
"{op1_attr1:[PlaceHolder],op1_attr2:[PlaceHolder]} :[f32]\n"
"(%1 [extra info]) := \"test.operation2\" (%0 [extra info]) {} :[f32]\n"
"}\n");
}
+130
View File
@@ -0,0 +1,130 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <sstream>
#include "paddle/fluid/pir/dialect/operator/interface/op_yaml_info.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/phi/core/meta_tensor.h"
#include "paddle/phi/infermeta/binary.h"
#include "paddle/phi/kernels/elementwise_add_kernel.h"
#include "paddle/pir/include/core/block.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/utils.h"
// NOTE(zhangbo9674): File pd_op.h is generated by op_gen.py, see details in
// paddle/fluid/pir/dialect/CMakeLists.txt.
#include "paddle/common/errors.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/dialect/operator/transforms/param_to_variable.h"
#include "paddle/phi/core/enforce.h"
#include "test/cpp/pir/tools/macros_utils.h"
class AddOp : public pir::Op<AddOp> {
public:
using Op::Op;
static const char *name() { return "test.add"; }
static constexpr const char **attributes_name = nullptr;
static constexpr uint32_t attributes_num = 0;
void VerifySig();
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type sum_type);
};
void AddOp::VerifySig() {
if (num_operands() != 2) {
PADDLE_THROW(
common::errors::Fatal("The size of inputs must be equal to 2."));
}
if (num_results() != 1) {
PADDLE_THROW(
common::errors::Fatal("The size of outputs must be equal to 1."));
}
}
void AddOp::Build(pir::Builder &,
pir::OperationArgument &argument,
pir::Value l_operand,
pir::Value r_operand,
pir::Type sum_type) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
argument.AddOutput(sum_type);
}
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(AddOp)
IR_DEFINE_EXPLICIT_TYPE_ID(AddOp)
TEST(program_test, slice_combine_test) {
// (1) Init environment.
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
// (2) Create an empty program object
pir::Program program(ctx);
// pir::Program *program = new pir::Program();
EXPECT_EQ(program.block()->empty(), true);
// (3) Create a float32 DenseTensor Parameter and save into Program
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
// (4) Def a = ParameterOp("a")
std::string op1_name = pir::ParameterOp::name();
pir::OpInfo op1_info = ctx->GetRegisteredOpInfo(op1_name);
std::unordered_map<std::string, pir::Attribute> op1_attribute{
{"parameter_name", pir::StrAttribute::get(ctx, "a")}};
pir::Operation *op1 =
pir::Operation::Create({}, op1_attribute, {fp32_dtype}, op1_info);
program.block()->push_back(op1);
// (5) Def b = Constant("b")
std::string op2_name = std::string(pir::ConstantOp::name());
pir::OpInfo op2_info = ctx->GetRegisteredOpInfo(op2_name);
pir::AttributeMap attr_map;
attr_map.insert(std::pair<std::string, pir::Attribute>(
"value", pir::FloatAttribute::get(ctx, 2.0)));
pir::Operation *op2 =
pir::Operation::Create({}, attr_map, {fp32_dtype}, op2_info);
program.block()->push_back(op2);
// (6) Def combine_op = CombineOp("a", "b")
std::string combine_op_name = std::string(pir::CombineOp::name());
pir::OpInfo combine_op_info = ctx->GetRegisteredOpInfo(combine_op_name);
pir::Type output_type = pir::VectorType::get(
ctx, std::vector<pir::Type>({fp32_dtype, fp32_dtype}));
pir::Operation *combine_op = pir::Operation::Create(
{op1->result(0), op2->result(0)}, {}, {output_type}, combine_op_info);
pir::CombineOp combine_op_type = combine_op->dyn_cast<pir::CombineOp>();
EXPECT_TRUE(combine_op_type.out());
program.block()->push_back(combine_op);
// (7) Def slice_op = SliceOp(combine_op, 0)
std::string slice_op_name = std::string(pir::SliceOp::name());
pir::OpInfo slice_op_info = ctx->GetRegisteredOpInfo(slice_op_name);
pir::Attribute index_attr = pir::Int32Attribute::get(ctx, 0);
pir::Operation *slice_op = pir::Operation::Create({combine_op->result(0)},
{{"index", index_attr}},
{fp32_dtype},
slice_op_info);
program.block()->push_back(slice_op);
// (8) Traverse Program
EXPECT_EQ(program.block()->size() == 4, true);
}
+110
View File
@@ -0,0 +1,110 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/pir/include/core/block.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/ir_mapping.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/utils.h"
TEST(region, erase_op_test) {
// (1) Init environment.
pir::IrContext *ctx = pir::IrContext::Instance();
// (2) Create an empty program object
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
// (3) Def a = ConstantOp("2.0"); b = ConstantOp("2.0");
pir::FloatAttribute fp_attr = builder.float_attr(2.0f);
pir::Float32Type fp32_type = builder.float32_type();
pir::Value a = builder.Build<pir::ConstantOp>(fp_attr, fp32_type)->result(0);
pir::Value b = builder.Build<pir::ConstantOp>(fp_attr, fp32_type)->result(0);
// (6) Def c = CombineOp(a, b)
builder.Build<pir::CombineOp>(std::vector<pir::Value>{a, b});
// Test pir::Block::erase
pir::Block *block = program.block();
EXPECT_EQ(block->size(), 3u);
block->erase(block->back());
EXPECT_EQ(block->size(), 2u);
// Test pir::Region::erase
pir::Region &region = program.module_op()->region(0);
region.push_back(new pir::Block());
EXPECT_EQ(region.size(), 2u);
region.erase(region.begin());
EXPECT_EQ(region.size(), 1u);
}
TEST(region, clone_op_test) {
// (1) Init environment.
pir::IrContext *ctx = pir::IrContext::Instance();
// (2) Create an empty program object
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
// (3) Def a = ConstantOp("2.0"); b = ConstantOp("2.0");
pir::FloatAttribute fp_attr = builder.float_attr(2.0f);
pir::Float32Type fp32_type = builder.float32_type();
pir::Value a = builder.Build<pir::ConstantOp>(fp_attr, fp32_type)->result(0);
pir::Value b = builder.Build<pir::ConstantOp>(fp_attr, fp32_type)->result(0);
// (6) Def c = CombineOp(a, b)
builder.Build<pir::CombineOp>(std::vector<pir::Value>{a, b});
// (7) Test clone module op
pir::Operation &op = *program.module_op();
pir::Block &block = op.region(0).front();
pir::IrMapping mapper;
pir::Operation &new_op = *op.Clone(mapper, pir::CloneOptions::All());
// (8) Check the cloned op recursively
EXPECT_EQ(mapper.Lookup(&op), &new_op);
EXPECT_EQ(new_op.num_regions(), 1u);
pir::Region &new_region = new_op.region(0);
EXPECT_EQ(new_region.size(), 1u);
pir::Block &new_block = new_region.front();
EXPECT_EQ(mapper.Lookup(&block), &new_block);
EXPECT_EQ(new_block.size(), 3u);
for (auto op_iter = block.begin(), new_op_iter = new_block.begin();
op_iter != block.end();
++op_iter, ++new_op_iter) {
pir::Operation &op = *op_iter;
pir::Operation &new_op = *new_op_iter;
EXPECT_EQ(mapper.Lookup(&op), &new_op);
EXPECT_EQ(op.num_operands(), new_op.num_operands());
for (uint32_t i = 0; i < op.num_operands(); ++i) {
EXPECT_EQ(mapper.Lookup(op.operand_source(i)), new_op.operand_source(i));
}
EXPECT_EQ(op.num_results(), new_op.num_results());
for (uint32_t i = 0; i < op.num_results(); ++i) {
EXPECT_EQ(mapper.Lookup(op.result(i)), new_op.result(i));
}
EXPECT_TRUE(std::equal(op.attributes().begin(),
op.attributes().end(),
new_op.attributes().begin(),
new_op.attributes().end()));
EXPECT_EQ(op.info(), new_op.info());
}
}
+110
View File
@@ -0,0 +1,110 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <sstream>
#include "paddle/fluid/framework/convert_utils.h"
#include "paddle/fluid/framework/data_type.h"
#include "paddle/fluid/framework/var_desc.h"
#include "paddle/fluid/ir_adaptor/translator/attribute_translator.h"
#include "paddle/fluid/ir_adaptor/translator/type_translator.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/type.h"
template <typename IR_TYPE>
void test_parameterless_type() {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Type type = IR_TYPE::get(ctx);
std::stringstream ss;
ss << type;
EXPECT_GT(ss.str().size(), 0u);
EXPECT_NE(ss.str(), "<<NULL TYPE>>");
phi::DataType phi_type = paddle::dialect::TransToPhiDataType(type);
EXPECT_EQ(type, paddle::dialect::TransToIrDataType(phi_type));
auto& type_translator = paddle::translator::TypeTranslator::instance();
paddle::framework::VarDesc empty_var_desc("empty");
auto proto_type = paddle::framework::TransToProtoVarType(phi_type);
pir::Type final_type = type_translator[proto_type](ctx, empty_var_desc);
EXPECT_EQ(type, final_type);
}
template <typename... IR_TYPE>
void test_parameterless_type_helper() {
(void)std::initializer_list<int>{0,
(test_parameterless_type<IR_TYPE>(), 0)...};
}
TEST(TypeConverterTest, parameterless_type) {
test_parameterless_type_helper<pir::UInt8Type,
pir::Int8Type,
pir::BFloat16Type,
pir::Float16Type,
pir::Float32Type,
pir::Float64Type,
pir::Int16Type,
pir::Int32Type,
pir::Int64Type,
pir::BoolType,
pir::Complex64Type,
pir::Complex128Type,
pir::Float8E4M3FNType,
pir::Float8E5M2Type>();
}
void test_index_type() {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Type type = pir::IndexType::get(ctx);
std::stringstream ss;
ss << type;
EXPECT_GT(ss.str().size(), 0u);
EXPECT_EQ(ss.str(), "index");
EXPECT_NE(ss.str(), "<<NULL TYPE>>");
phi::DataType phi_type = paddle::dialect::TransToPhiDataType(type);
auto& type_translator = paddle::translator::TypeTranslator::instance();
paddle::framework::VarDesc empty_var_desc("empty");
auto proto_type = paddle::framework::TransToProtoVarType(phi_type);
pir::Type final_type = type_translator[proto_type](ctx, empty_var_desc);
EXPECT_EQ(paddle::dialect::TransToIrDataType(phi_type), final_type);
}
TEST(IndexTypeConverterTest, index_type) { test_index_type(); }
TEST(AttributeConverterTest, int2bool) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
auto& attr_translator = paddle::translator::AttributeTranslator::instance();
int v = 0;
paddle::framework::Attribute attr(v);
pir::Attribute pir_attr = attr_translator("pir::BoolAttribute", attr);
EXPECT_TRUE(pir_attr.isa<pir::BoolAttribute>());
int64_t v1 = 0;
paddle::framework::Attribute attr1(v1);
pir_attr = attr_translator("pir::BoolAttribute", attr1);
EXPECT_TRUE(pir_attr.isa<pir::BoolAttribute>());
pir_attr =
attr_translator("pir::BoolAttribute", paddle::framework::Attribute());
EXPECT_TRUE(pir_attr.isa<pir::BoolAttribute>());
}
+156
View File
@@ -0,0 +1,156 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/pir/include/core/attribute.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/operation.h"
#include "paddle/pir/include/dialect/shape/utils/shape_analysis.h"
#include "paddle/pir/src/core/op_result_impl.h"
// This unittest is used to test the construction interfaces of value class and
// operation. The constructed test scenario is: a = OP1(); b = OP2(); c = OP3(a,
// b); d, e, f, g, h, i, j = OP4(a, c);
namespace test {
pir::AttributeMap CreateAttributeMap(
const std::vector<std::string> &attribute_names,
const std::vector<std::string> &attributes) {
pir::IrContext *ctx = pir::IrContext::Instance();
pir::AttributeMap attr_map;
for (size_t i = 0; i < attribute_names.size(); i++) {
pir::Attribute attr_value = pir::StrAttribute::get(ctx, attributes[i]);
attr_map.insert(
std::pair<std::string, pir::Attribute>(attribute_names[i], attr_value));
}
return attr_map;
}
} // namespace test
TEST(value_test, value_test) {
pir::IrContext *ctx = pir::IrContext::Instance();
// 1. Construct OP1: a = OP1()
std::vector<pir::Value> op1_inputs = {};
std::vector<pir::Type> op1_output_types = {pir::Float32Type::get(ctx)};
pir::Operation *op1 = pir::Operation::Create(
op1_inputs,
test::CreateAttributeMap({"op1_name"}, {"op1_attr"}),
op1_output_types,
nullptr);
op1->Print(std::cout);
pir::Value a = op1->result(0);
EXPECT_TRUE(a.use_empty());
// 2. Construct OP2: b = OP2();
std::vector<pir::Value> op2_inputs = {};
std::vector<pir::Type> op2_output_types = {pir::Float32Type::get(ctx)};
pir::Operation *op2 = pir::Operation::Create(
op2_inputs,
test::CreateAttributeMap({"op2_name"}, {"op2_attr"}),
op2_output_types,
nullptr);
op2->Print(std::cout);
pir::Value b = op2->result(0);
EXPECT_TRUE(b.use_empty());
// 3. Construct OP3: c = OP3(a, b);
std::vector<pir::Value> op3_inputs{a, b};
std::vector<pir::Type> op3_output_types = {pir::Float32Type::get(ctx)};
pir::Operation *op3 = pir::Operation::Create(
op3_inputs,
test::CreateAttributeMap({"op3_name"}, {"op3_attr"}),
op3_output_types,
nullptr);
EXPECT_TRUE(op1->result(0).HasOneUse());
EXPECT_TRUE(op2->result(0).HasOneUse());
op3->Print(std::cout);
pir::Value c = op3->result(0);
// 4. Construct OP4: d, e, f, g, h, i, j = OP4(a, c);
std::vector<pir::Value> op4_inputs = {a, c};
std::vector<pir::Type> op4_output_types;
for (size_t i = 0; i < 7; i++) {
op4_output_types.push_back(pir::Float32Type::get(ctx));
}
pir::Operation *op4 = pir::Operation::Create(
op4_inputs,
test::CreateAttributeMap({"op4_name"}, {"op4_attr"}),
op4_output_types,
nullptr);
op4->Print(std::cout);
// Test 1:
EXPECT_EQ(op1->result(0).defining_op(), op1);
EXPECT_EQ(op2->result(0).defining_op(), op2);
EXPECT_EQ(op3->result(0).defining_op(), op3);
EXPECT_EQ(op4->result(6).defining_op(), op4);
// Test 2: op1_first_output -> op4_first_input
pir::Value op1_first_output = op1->result(0);
pir::OpOperand op4_first_input = op4->operand(0);
EXPECT_EQ(op1_first_output.first_use(), op4_first_input);
pir::OpOperand op3_first_input = op3->operand(0);
EXPECT_EQ(op4_first_input.next_use(), op3_first_input);
EXPECT_EQ(op3_first_input.next_use(), nullptr);
// Test 3: Value iterator
using my_iterator = pir::Value::UseIterator;
my_iterator iter = op1->result(0).use_begin();
EXPECT_EQ(iter.owner(), op4);
++iter;
EXPECT_EQ(iter.owner(), op3);
// Test 4: Value Replace Use
// a = OP1(); b = OP2(); c = OP3(a, b); d, e, f, g, h, i, j = OP4(a, c);
//
c.ReplaceUsesWithIf(b, [](pir::OpOperand) { return true; });
EXPECT_EQ(op4->operand_source(1), b);
EXPECT_TRUE(c.use_empty());
b.ReplaceAllUsesWith(a);
EXPECT_EQ(op4->operand_source(1), a);
EXPECT_TRUE(b.use_empty());
// destroy
VLOG(0) << op1->result(0).PrintUdChain() << std::endl;
op4->Destroy();
VLOG(0) << op1->result(0).PrintUdChain() << std::endl;
op3->Destroy();
VLOG(0) << op1->result(0).PrintUdChain() << std::endl;
op2->Destroy();
VLOG(0) << op1->result(0).PrintUdChain() << std::endl;
op1->Destroy();
}
TEST(op_result_test, exception) {
EXPECT_THROW(
pir::detail::OpInlineResultImpl(nullptr, MAX_INLINE_RESULT_IDX + 1),
common::enforce::EnforceNotMet);
pir::IrContext *ctx = pir::IrContext::Instance();
auto op = pir::Operation::Create(
{}, {{"test", pir::Int32Attribute::get(ctx, 1)}}, {nullptr}, nullptr);
auto result = op->result(0);
auto op2 = pir::Operation::Create({result}, {}, {}, nullptr);
EXPECT_DEATH(op->Destroy(), "Destroyed a op_result that is still in use.*");
EXPECT_THROW(result.set_attribute("test", nullptr),
common::enforce::EnforceNotMet);
EXPECT_THROW(op->result(1), common::enforce::EnforceNotMet);
EXPECT_THROW(op->operand(1), common::enforce::EnforceNotMet);
op2->Destroy();
op->Destroy();
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/pir/include/core/block.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/verify.h"
TEST(ir_op_info_test, op_op_info_test) {
pir::IrContext* context = pir::IrContext::Instance();
pir::Program program(context);
pir::Block* block = program.block();
pir::Builder builder(context, block);
builder.Build<pir::ConstantOp>(pir::Int32Attribute::get(context, 5),
pir::Int32Type::get(context));
auto& op = block->back();
EXPECT_EQ(block->end(), ++pir::Block::Iterator(op));
auto& info_map = context->registered_op_info_map();
EXPECT_FALSE(info_map.empty());
void* info_1 = op.info();
auto info_2 = pir::OpInfo::RecoverFromVoidPointer(info_1);
EXPECT_EQ(op.info(), info_2);
pir::Verify(program.module_op());
}
@@ -0,0 +1,63 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/fluid/pir/dialect/operator/ir/op_attribute.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/utils/op_yaml_info_parser.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/fluid/pir/dialect/operator/interface/op_yaml_info.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/utils.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
TEST(ir_op_info_test, op_op_info_test) {
pir::IrContext* ctx = pir::IrContext::Instance();
pir::Program program(ctx);
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Builder builder(ctx, program.block());
auto uniform1 =
builder.Build<paddle::dialect::UniformOp>(std::vector<int64_t>{2, 2},
phi::DataType::FLOAT32,
0.0,
1.0,
2,
phi::CPUPlace());
uniform1->num_operands();
paddle::dialect::OpYamlInfoInterface op_info_interface =
uniform1->dyn_cast<paddle::dialect::OpYamlInfoInterface>();
auto op_info_res = op_info_interface.GetOpInfo();
paddle::dialect::OpYamlInfoParser op_yaml_info_parser(op_info_res);
EXPECT_EQ((op_yaml_info_parser.AttrTypeName("seed") == "pir::Int32Attribute"),
true);
EXPECT_EQ(op_yaml_info_parser.IsTensorAttribute(0), true);
EXPECT_EQ(op_yaml_info_parser.InputTensorNumber(), 0u);
}
+43
View File
@@ -0,0 +1,43 @@
// Copyright (c) 2024 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/common/enforce.h"
#include "paddle/phi/core/enforce.h"
class FatalClass {
public:
FatalClass() {}
~FatalClass() { PADDLE_FATAL("fatal occurred in deconstructor!"); }
};
void throw_exception_in_func() {
FatalClass test_case;
PADDLE_THROW(::common::errors::External("throw exception in func"));
}
void terminate_in_func() { FatalClass test_case; }
TEST(paddle_fatal_test, base) {
EXPECT_FALSE(::common::enforce::IsPaddleFatalSkip());
EXPECT_DEATH(terminate_in_func(), "fatal occurred in deconstructor!.*");
EXPECT_THROW(throw_exception_in_func(), common::enforce::EnforceNotMet);
EXPECT_FALSE(::common::enforce::IsPaddleFatalSkip());
::common::enforce::SkipPaddleFatal(true);
// skip fatal.
terminate_in_func();
// unskip paddle fatal.
::common::enforce::SkipPaddleFatal(false);
}
@@ -0,0 +1,268 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <chrono>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "paddle/fluid/framework/block_desc.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/ir_adaptor/translator/translate.h"
#include "paddle/fluid/ir_adaptor/translator/utils.h"
#include "paddle/fluid/pir/dialect/operator/ir/control_flow_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/manual_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/phi/core/framework/framework.pb.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/ir_printer.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
using OperatorDialect = paddle::dialect::OperatorDialect;
using ProgramDesc = paddle::framework::ProgramDesc;
using BlockDesc = paddle::framework::BlockDesc;
using OpDesc = paddle::framework::OpDesc;
using VarDesc = paddle::framework::VarDesc;
using VarType = paddle::framework::proto::VarType;
ProgramDesc load_from_file(const std::string &file_name) {
std::ifstream fin(file_name, std::ios::in | std::ios::binary);
fin.seekg(0, std::ios::end);
std::string buffer(fin.tellg(), ' ');
fin.seekg(0, std::ios::beg);
fin.read(&buffer[0], buffer.size()); // NOLINT
fin.close();
return ProgramDesc(buffer);
}
TEST(OperatorDialectTest, MainProgram) {
auto p = load_from_file("resnet50_main.prog");
EXPECT_EQ(p.Size(), 1u);
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
auto program = paddle::TranslateLegacyProgramToProgram(p);
std::stringstream ss;
program->Print(ss);
// ops.size() = op size in BlockDesc + parameter_op + combine op + int
// array op + full op (Note: p already has a full)
EXPECT_EQ(program->block()->size(),
p.Block(0).OpSize() + program->parameters_num() + 20 + 5 + 9 + 2);
EXPECT_GT(ss.str().size(), 0u);
}
TEST(OperatorDialectTest, ConditionBlock) {
auto p = load_from_file("conditional_block_test.prog");
EXPECT_EQ(p.Size(), 7u);
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
auto program = paddle::TranslateLegacyProgramToProgram(p);
EXPECT_EQ(program->block()->size(), 9u);
size_t id = 0;
for (auto &op : *program->block()) {
if (id == 0 || id == 1) {
EXPECT_EQ(op.isa<paddle::dialect::FullOp>(), true);
}
if (id == 2) {
EXPECT_EQ(op.isa<paddle::dialect::LessThanOp>(), true);
}
if (id == 3) {
EXPECT_EQ(op.isa<paddle::dialect::IfOp>(), true);
EXPECT_EQ(op.num_regions(), 2u);
// true block
pir::Block &true_block =
op.dyn_cast<paddle::dialect::IfOp>().true_block();
size_t true_id = 0;
for (auto &op1 : true_block) {
if (true_id == 0 || true_id == 1) {
EXPECT_EQ(op1.isa<paddle::dialect::FullOp>(), true);
}
if (true_id == 2) {
EXPECT_EQ(op1.isa<paddle::dialect::LessThanOp>(), true);
}
if (true_id == 3) {
auto &true_true_block =
op1.dyn_cast<paddle::dialect::IfOp>().true_block();
size_t true_true_id = 0;
for (auto &op2 : true_true_block) {
if (true_true_id == 0) {
EXPECT_EQ(op2.isa<paddle::dialect::AddOp>(), true);
}
if (true_true_id == 1) {
EXPECT_EQ(op2.isa<paddle::dialect::AssignOp>(), true);
}
if (true_true_id == 2) {
EXPECT_EQ(op2.isa<pir::YieldOp>(), true);
}
true_true_id++;
}
}
if (true_id == 4) {
EXPECT_EQ(op1.isa<paddle::dialect::LogicalNotOp>(), true);
}
if (true_id == 5) {
EXPECT_EQ(op1.isa<paddle::dialect::IfOp>(), true);
}
if (true_id == 6) {
EXPECT_EQ(op1.isa<paddle::dialect::CastOp>(), true);
}
if (true_id == 7) {
EXPECT_EQ(op1.isa<paddle::dialect::SelectInputOp>(), true);
}
if (true_id == 8) {
EXPECT_EQ(op1.isa<paddle::dialect::MultiplyOp>(), true);
}
if (true_id == 9 || true_id == 10) {
EXPECT_EQ(op1.isa<paddle::dialect::AssignOp>(), true);
}
if (true_id == 11) {
EXPECT_EQ(op1.isa<pir::YieldOp>(), true);
}
true_id++;
}
}
id++;
}
}
TEST(OperatorDialectTest, StartupProgram) {
auto p = load_from_file("resnet50_startup.prog");
EXPECT_EQ(p.Size(), 1u);
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
auto program = paddle::TranslateLegacyProgramToProgram(p);
size_t op_size = program->block()->size();
// ops.size() = op size in BlockDesc + parameter_op +
// constant_op_for_uniform
// + constant_op for gaussian
EXPECT_EQ(op_size, p.Block(0).OpSize() + program->parameters_num() + 3 + 53);
std::stringstream ss;
program->Print(ss);
EXPECT_GT(ss.str().size(), 0u);
}
TEST(RegisterInfoTest, MainProgram) {
auto p = load_from_file("resnet50_startup.prog");
pir::IrContext *ctx = pir::IrContext::Instance();
auto unregistered_ops =
paddle::translator::CheckUnregisteredOperation(ctx, p);
EXPECT_EQ(unregistered_ops.size(), 0u);
auto new_op = std::unique_ptr<OpDesc>(
new OpDesc("something must not be registered", {}, {}, {}));
auto *block = p.MutableBlock(0);
block->AppendAllocatedOp(std::move(new_op));
unregistered_ops = paddle::translator::CheckUnregisteredOperation(ctx, p);
EXPECT_EQ(unregistered_ops.size(), 1u);
EXPECT_EQ(unregistered_ops[0], "something must not be registered");
}
TEST(OperatorDialectTest, WhileOpProgram) {
auto p = load_from_file("while_op_test.prog");
EXPECT_EQ(p.Size(), 3u);
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
auto program = paddle::TranslateLegacyProgramToProgram(p);
std::stringstream ss;
program->Print(ss);
LOG(INFO) << ss.str();
EXPECT_EQ(program->block()->size(), 4u);
size_t id = 0;
for (auto &op : *program->block()) {
if (id == 0 || id == 1) {
EXPECT_TRUE(op.isa<paddle::dialect::FullOp>());
}
if (id == 2) {
EXPECT_TRUE(op.isa<paddle::dialect::LessThanOp>());
}
if (id == 3) {
EXPECT_TRUE(op.isa<paddle::dialect::WhileOp>());
EXPECT_EQ(op.num_regions(), 1u);
// body block
pir::Block &body_block = op.dyn_cast<paddle::dialect::WhileOp>().body();
size_t body_id = 0;
for (auto &op1 : body_block) {
if (body_id == 0) {
EXPECT_TRUE(op1.isa<paddle::dialect::FullOp>());
}
if (body_id == 1) {
EXPECT_TRUE(op1.isa<paddle::dialect::ScaleOp>());
}
if (body_id == 2) {
EXPECT_TRUE(op1.isa<paddle::dialect::LessThanOp>());
}
if (body_id == 3) {
pir::Block &body_body_block =
op1.dyn_cast<paddle::dialect::WhileOp>().body();
size_t body_body_id = 0;
for (auto &op2 : body_body_block) {
if (body_body_id == 0) {
EXPECT_TRUE(op2.isa<paddle::dialect::FullOp>());
}
if (body_body_id == 1) {
EXPECT_TRUE(op2.isa<paddle::dialect::ScaleOp>());
}
if (body_body_id == 2) {
EXPECT_TRUE(op2.isa<paddle::dialect::LessThanOp>());
}
if (body_body_id == 3 || body_body_id == 4) {
EXPECT_TRUE(op2.isa<paddle::dialect::AssignOut_Op>());
}
if (body_body_id == 5) {
EXPECT_TRUE(op2.isa<pir::YieldOp>());
}
body_body_id++;
}
}
if (body_id == 4) {
EXPECT_TRUE(op1.isa<paddle::dialect::LessThanOp>());
}
if (body_id == 5 || body_id == 6) {
EXPECT_TRUE(op1.isa<paddle::dialect::AssignOut_Op>());
}
if (body_id == 7) {
EXPECT_TRUE(op1.isa<pir::YieldOp>());
}
body_id++;
}
}
id++;
}
}
@@ -0,0 +1,58 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/fluid/pir/dialect/operator/ir/op_attribute.h"
#include "paddle/pir/include/core/attribute.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
using ScalarAttribute = paddle::dialect::ScalarAttribute;
TEST(ScalarTest, base) {
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Attribute bool_scalar = pir::BoolAttribute::get(ctx, false);
EXPECT_TRUE(bool_scalar.isa<ScalarAttribute>());
EXPECT_TRUE(bool_scalar.isa<pir::BoolAttribute>());
pir::BoolAttribute pure_bool = bool_scalar.dyn_cast<pir::BoolAttribute>();
EXPECT_TRUE(pure_bool.isa<ScalarAttribute>());
ScalarAttribute scalar_from_bool = bool_scalar.dyn_cast<ScalarAttribute>();
EXPECT_TRUE(scalar_from_bool.isa<pir::BoolAttribute>());
EXPECT_NO_THROW(scalar_from_bool.dyn_cast<pir::BoolAttribute>());
}
TEST(ScalarTest, test_classof) {
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Attribute bool_scalar = pir::BoolAttribute::get(ctx, false);
EXPECT_TRUE(bool_scalar.isa<ScalarAttribute>());
pir::Attribute float_scalar = pir::FloatAttribute::get(ctx, 1.0f);
EXPECT_TRUE(float_scalar.isa<ScalarAttribute>());
pir::Attribute double_scalar = pir::DoubleAttribute::get(ctx, 1.0);
EXPECT_TRUE(double_scalar.isa<ScalarAttribute>());
pir::Attribute int32_scalar = pir::Int32Attribute::get(ctx, 1);
EXPECT_TRUE(int32_scalar.isa<ScalarAttribute>());
pir::Attribute index_scalar = pir::IndexAttribute::get(ctx, 1l);
EXPECT_TRUE(index_scalar.isa<ScalarAttribute>());
pir::Attribute int64_scalar = pir::Int64Attribute::get(ctx, 1l);
EXPECT_TRUE(int64_scalar.isa<ScalarAttribute>());
}
+68
View File
@@ -0,0 +1,68 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/type.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "test/cpp/pir/tools/test_dialect.h"
#include "test/cpp/pir/tools/test_op.h"
TEST(shape_dtype_test, shape_dtype_test) {
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Dialect *test_dialect = ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
EXPECT_EQ(test_dialect != nullptr, true);
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
phi::DDim dims = {2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_type = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
EXPECT_EQ(dense_tensor_type.dtype().isa<pir::Float32Type>(), true);
EXPECT_EQ(dense_tensor_type.dims(), dims);
EXPECT_EQ(dense_tensor_type.data_layout(), data_layout);
EXPECT_EQ(dense_tensor_type.lod(), lod);
EXPECT_EQ(dense_tensor_type.offset(), offset);
pir::ShapedTypeInterface dense_tensor_type_interface =
dense_tensor_type.dyn_cast<pir::ShapedTypeInterface>();
EXPECT_TRUE(dense_tensor_type_interface);
EXPECT_EQ(
dense_tensor_type_interface.GetElementType().isa<pir::Float32Type>(),
true);
EXPECT_EQ(dense_tensor_type_interface.GetShape(), dims);
EXPECT_EQ(dense_tensor_type_interface.kDynamic, std::int64_t(-1));
EXPECT_EQ(dense_tensor_type_interface.GetRank(), 2);
EXPECT_EQ(dense_tensor_type_interface.IsDynamic(2), false);
EXPECT_EQ(dense_tensor_type_interface.IsDynamicShape(), false);
EXPECT_EQ(dense_tensor_type_interface.IsDynamicDim(1), false);
EXPECT_EQ(dense_tensor_type_interface.GetNumDynamicDims(), 0);
EXPECT_EQ(dense_tensor_type_interface.GetDimSize(0), 2);
pir::Type fp32_type = pir::Float32Type::get(ctx);
pir::ShapedTypeInterface fp32_type_interface =
fp32_type.dyn_cast<pir::ShapedTypeInterface>();
EXPECT_FALSE(fp32_type_interface);
}
+401
View File
@@ -0,0 +1,401 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <unordered_map>
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/type.h"
#include "paddle/pir/include/core/type_base.h"
#include "paddle/pir/include/core/type_name.h"
#include "paddle/pir/include/core/type_utils.h"
#include "paddle/pir/include/core/utils.h"
#include "test/cpp/pir/tools/macros_utils.h"
class TypeA {};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(TypeA)
IR_DEFINE_EXPLICIT_TYPE_ID(TypeA)
class TypeB {};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(TypeB)
IR_DEFINE_EXPLICIT_TYPE_ID(TypeB)
std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
return lhs;
}
TEST(type_test, type_id) {
// Test 1: Test construct TypeId by TypeId::get<T>() and overloaded operator==
// method.
pir::TypeId a_id = pir::TypeId::get<TypeA>();
pir::TypeId a_other_id = pir::TypeId::get<TypeA>();
pir::TypeId b_id = pir::TypeId::get<TypeB>();
EXPECT_EQ(a_id, a_other_id);
EXPECT_NE(a_id, b_id);
// Test 2: Test the hash function of TypeId.
std::unordered_map<pir::TypeId, pir::TypeId *> type_id_register;
type_id_register.emplace(a_id, &a_id);
type_id_register.emplace(b_id, &b_id);
for (auto kv : type_id_register) {
EXPECT_EQ(kv.first, *kv.second);
}
}
// Define a FakeDialect without registering any types.
struct FakeDialect : pir::Dialect {
explicit FakeDialect(pir::IrContext *context)
: pir::Dialect(name(), context, pir::TypeId::get<FakeDialect>()) {}
static const char *name() { return "fake"; }
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(FakeDialect)
IR_DEFINE_EXPLICIT_TYPE_ID(FakeDialect)
TEST(type_test, type_base) {
// Test 1: Test the function of IrContext to register Dialect.
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Dialect *fake_dialect = ctx->GetOrRegisterDialect<FakeDialect>();
// Test 2: Test the get method of AbstractType.
pir::TypeId a_id = pir::TypeId::get<TypeA>();
pir::AbstractType abstract_type_a =
pir::AbstractType::get(a_id, *fake_dialect, {});
EXPECT_EQ(abstract_type_a.type_id(), a_id);
// Test 3: Test the constructor of TypeStorage.
pir::TypeStorage storage_a(&abstract_type_a);
EXPECT_EQ(storage_a.abstract_type().type_id(), abstract_type_a.type_id());
}
TEST(type_test, built_in_type) {
// Test the interfaces of class Type: judgment, type_id, abstract_type,
// classof.
pir::IrContext *ctx = pir::IrContext::Instance();
// Test 1: Test the parameterless built-in type of IrContext.
pir::Type bfp16_1 = pir::BFloat16Type::get(ctx);
pir::Type bfp16_2 = pir::BFloat16Type::get(ctx);
EXPECT_EQ(bfp16_1, bfp16_2);
EXPECT_EQ(bfp16_1.type_id(), bfp16_2.type_id());
EXPECT_EQ(&bfp16_1.abstract_type(),
&pir::AbstractType::lookup(bfp16_1.type_id(), ctx));
EXPECT_EQ(pir::BFloat16Type::classof(bfp16_1), 1);
pir::Type index_1 = pir::IndexType::get(ctx);
pir::Type index_2 = pir::IndexType::get(ctx);
EXPECT_TRUE(index_1.IsIndex());
EXPECT_EQ(index_1, index_2);
EXPECT_EQ(index_1.type_id(), index_2.type_id());
EXPECT_EQ(&index_1.abstract_type(),
&pir::AbstractType::lookup(index_1.type_id(), ctx));
EXPECT_EQ(pir::IndexType::classof(index_1), 1);
pir::Type fp16_1 = pir::Float16Type::get(ctx);
pir::Type fp16_2 = pir::Float16Type::get(ctx);
EXPECT_EQ(fp16_1, fp16_2);
EXPECT_EQ(fp16_1.type_id(), fp16_2.type_id());
EXPECT_EQ(&fp16_1.abstract_type(),
&pir::AbstractType::lookup(fp16_1.type_id(), ctx));
EXPECT_EQ(pir::Float16Type::classof(fp16_1), 1);
pir::Type fp32_1 = pir::Float32Type::get(ctx);
pir::Type fp32_2 = pir::Float32Type::get(ctx);
EXPECT_EQ(fp32_1, fp32_2);
EXPECT_EQ(fp32_1.type_id(), fp32_2.type_id());
EXPECT_EQ(&fp32_1.abstract_type(),
&pir::AbstractType::lookup(fp32_1.type_id(), ctx));
EXPECT_EQ(pir::Float32Type::classof(fp32_1), 1);
pir::Type fp64_1 = pir::Float64Type::get(ctx);
pir::Type fp64_2 = pir::Float64Type::get(ctx);
EXPECT_EQ(fp64_1, fp64_2);
EXPECT_EQ(fp64_1.type_id(), fp64_2.type_id());
EXPECT_EQ(&fp64_1.abstract_type(),
&pir::AbstractType::lookup(fp64_1.type_id(), ctx));
EXPECT_EQ(pir::Float64Type::classof(fp64_1), 1);
pir::Type int16_1 = pir::Int16Type::get(ctx);
pir::Type int16_2 = pir::Int16Type::get(ctx);
EXPECT_EQ(int16_1, int16_2);
EXPECT_EQ(int16_1.type_id(), int16_2.type_id());
EXPECT_EQ(&int16_1.abstract_type(),
&pir::AbstractType::lookup(int16_1.type_id(), ctx));
EXPECT_EQ(pir::Int16Type::classof(int16_1), 1);
pir::Type int32_1 = pir::Int32Type::get(ctx);
pir::Type int32_2 = pir::Int32Type::get(ctx);
EXPECT_EQ(int32_1, int32_2);
EXPECT_EQ(int32_1.type_id(), int32_2.type_id());
EXPECT_EQ(&int32_1.abstract_type(),
&pir::AbstractType::lookup(int32_1.type_id(), ctx));
EXPECT_EQ(pir::Int32Type::classof(int32_1), 1);
pir::Type int64_1 = pir::Int64Type::get(ctx);
pir::Type int64_2 = pir::Int64Type::get(ctx);
EXPECT_EQ(int64_1, int64_2);
EXPECT_EQ(int64_1.type_id(), int64_2.type_id());
EXPECT_EQ(&int64_1.abstract_type(),
&pir::AbstractType::lookup(int64_1.type_id(), ctx));
EXPECT_EQ(pir::Int64Type::classof(int64_1), 1);
// Test 2: Test isa and dyn_cast.
EXPECT_EQ(fp16_1.isa<pir::Float16Type>(), true);
EXPECT_EQ(fp16_1.isa<pir::Float32Type>(), false);
EXPECT_EQ(fp16_1.isa<pir::Type>(), true);
// Test 3: Test VectorType
std::vector<pir::Type> vec_type = {int32_1, int64_1};
pir::Type vector_type = pir::VectorType::get(ctx, vec_type);
EXPECT_EQ(vector_type.isa<pir::VectorType>(), true);
EXPECT_EQ(vector_type.dyn_cast<pir::VectorType>().size() == 2, true);
EXPECT_EQ(vector_type.dyn_cast<pir::VectorType>()[0].isa<pir::Int32Type>(),
true);
EXPECT_EQ(vector_type.dyn_cast<pir::VectorType>()[1].isa<pir::Int64Type>(),
true);
}
// Customize a parameterized TypeStorage IntegerTypeStorage.
struct IntegerTypeStorage : public pir::TypeStorage {
IntegerTypeStorage(unsigned width, unsigned signedness)
: width_(width), signedness_(signedness) {}
using ParamKey = std::pair<unsigned, unsigned>;
static std::size_t HashValue(const ParamKey &key) {
return hash_combine(std::hash<unsigned>()(std::get<0>(key)),
std::hash<unsigned>()(std::get<1>(key)));
}
bool operator==(const ParamKey &key) const {
return ParamKey(width_, signedness_) == key;
}
static IntegerTypeStorage *Construct(const ParamKey &key) {
return new IntegerTypeStorage(key.first, key.second);
}
ParamKey GetAsKey() const { return ParamKey(width_, signedness_); }
unsigned width_ : 30;
unsigned signedness_ : 2;
};
// Customize a parameterized type: IntegerType, storage type is
// IntegerTypeStorage.
class IntegerType
: public pir::Type::TypeBase<IntegerType, pir::Type, IntegerTypeStorage> {
public:
using Base::Base;
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(IntegerType)
IR_DEFINE_EXPLICIT_TYPE_ID(IntegerType)
// Customize a Dialect IntegerDialect, registration type of IntegerType.
struct IntegerDialect : pir::Dialect {
explicit IntegerDialect(pir::IrContext *context)
: pir::Dialect(name(), context, pir::TypeId::get<IntegerDialect>()) {
RegisterType<IntegerType>();
}
static const char *name() { return "integer"; }
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(IntegerDialect)
IR_DEFINE_EXPLICIT_TYPE_ID(IntegerDialect)
TEST(type_test, custom_type_dialect) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
// Test 1: Test the function of IrContext to register Dialect.
ctx->GetOrRegisterDialect<IntegerDialect>();
pir::Type int1_1 = IntegerType::get(ctx, 1, 0);
pir::Type int1_2 = IntegerType::get(ctx, 1, 0);
EXPECT_EQ(int1_1, int1_2);
pir::Type int8 = IntegerType::get(ctx, 8, 0);
EXPECT_NE(int8, int1_2);
// Test 2: Test Dialect interfaces
EXPECT_EQ(ctx, int8.ir_context());
EXPECT_EQ(int8.dialect().id(), pir::TypeId::get<IntegerDialect>());
std::vector<pir::Dialect *> dialect_list = ctx->GetRegisteredDialects();
EXPECT_EQ(dialect_list.size() == 5, 1); // integer, builtin, fake
pir::Dialect *dialect_builtin1 = ctx->GetRegisteredDialect("builtin");
pir::Dialect *dialect_builtin2 =
ctx->GetRegisteredDialect<pir::BuiltinDialect>();
EXPECT_EQ(dialect_builtin1, dialect_builtin2);
pir::Dialect *dialect_integer1 = ctx->GetRegisteredDialect("integer");
pir::Dialect *dialect_integer2 = ctx->GetRegisteredDialect<IntegerDialect>();
EXPECT_EQ(dialect_integer1, dialect_integer2);
}
TEST(type_test, sparse_coo) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
common::DDim dims = {4, 4};
common::DDim non_zero_dims = {4, 1};
common::DataLayout data_layout = common::DataLayout::NCHW;
pir::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType none_zero_indices = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
pir::DenseTensorType none_zero_elements = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
bool coalesced = false;
paddle::dialect::SparseCooTensorTypeStorage storage1(fp32_dtype,
dims,
non_zero_dims,
data_layout,
none_zero_indices,
none_zero_elements,
coalesced);
auto storage2 = std::make_tuple(fp32_dtype,
dims,
non_zero_dims,
data_layout,
none_zero_indices,
none_zero_elements,
coalesced);
pir::Type pir_type =
paddle::dialect::SparseCooTensorType::get(ctx,
fp32_dtype,
dims,
non_zero_dims,
data_layout,
none_zero_indices,
none_zero_elements,
coalesced);
EXPECT_TRUE(storage1 == storage2);
EXPECT_EQ(pir_type.isa<paddle::dialect::SparseCooTensorType>(), true);
paddle::dialect::SparseCooTensorType sparse_coo_tensor_type =
pir_type.dyn_cast<paddle::dialect::SparseCooTensorType>();
EXPECT_EQ(sparse_coo_tensor_type.dims(), dims);
EXPECT_EQ(sparse_coo_tensor_type.non_zero_dims(), non_zero_dims);
EXPECT_EQ(sparse_coo_tensor_type.data_layout(), data_layout);
EXPECT_EQ(sparse_coo_tensor_type.non_zero_indices(), none_zero_indices);
EXPECT_EQ(sparse_coo_tensor_type.non_zero_elements(), none_zero_elements);
EXPECT_EQ(sparse_coo_tensor_type.coalesced(), coalesced);
}
TEST(type_test, pd_op_dialect) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
phi::DDim dims = {2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
paddle::dialect::SelectedRowsType select_rows_dtype =
paddle::dialect::SelectedRowsType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
EXPECT_EQ(select_rows_dtype.dtype().isa<pir::Float32Type>(), true);
EXPECT_EQ(select_rows_dtype.dims(), dims);
EXPECT_EQ(select_rows_dtype.data_layout(), data_layout);
EXPECT_EQ(select_rows_dtype.lod(), lod);
EXPECT_EQ(select_rows_dtype.offset(), offset);
}
TEST(type_test, sparse_csr) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
common::DDim dims = {4, 4};
common::DataLayout data_layout = common::DataLayout::NCHW;
pir::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType non_zero_crows = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
pir::DenseTensorType non_zero_cols = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
pir::DenseTensorType non_zero_elements = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
paddle::dialect::SparseCsrTensorTypeStorage storage1(fp32_dtype,
dims,
data_layout,
non_zero_crows,
non_zero_cols,
non_zero_elements);
auto storage2 = std::make_tuple(fp32_dtype,
dims,
data_layout,
non_zero_crows,
non_zero_cols,
non_zero_elements);
pir::Type pir_type =
paddle::dialect::SparseCsrTensorType::get(ctx,
fp32_dtype,
dims,
data_layout,
non_zero_crows,
non_zero_cols,
non_zero_elements);
EXPECT_TRUE(storage1 == storage2);
EXPECT_EQ(pir_type.isa<paddle::dialect::SparseCsrTensorType>(), true);
paddle::dialect::SparseCsrTensorType sparse_csr_tensor_type =
pir_type.dyn_cast<paddle::dialect::SparseCsrTensorType>();
EXPECT_EQ(sparse_csr_tensor_type.dims(), dims);
EXPECT_EQ(sparse_csr_tensor_type.data_layout(), data_layout);
EXPECT_EQ(sparse_csr_tensor_type.non_zero_crows(), non_zero_crows);
EXPECT_EQ(sparse_csr_tensor_type.non_zero_cols(), non_zero_cols);
EXPECT_EQ(sparse_csr_tensor_type.non_zero_elements(), non_zero_elements);
}
TEST(type_test, type_util) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
phi::DDim dims1 = {2, 2};
phi::DDim dims2 = {2, 2, 3};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
paddle::dialect::SelectedRowsType select_rows_dtype1 =
paddle::dialect::SelectedRowsType::get(
ctx, fp32_dtype, dims1, data_layout, lod, offset);
paddle::dialect::SelectedRowsType select_rows_dtype2 =
paddle::dialect::SelectedRowsType::get(
ctx, fp32_dtype, dims2, data_layout, lod, offset);
std::vector<pir::Type> types1 = {
select_rows_dtype1, select_rows_dtype1, select_rows_dtype1};
std::vector<pir::Type> types2 = {
select_rows_dtype1, select_rows_dtype1, select_rows_dtype1};
std::vector<pir::Type> types3 = {
select_rows_dtype2, select_rows_dtype2, select_rows_dtype2};
EXPECT_TRUE(pir::VerifyCompatibleShapes(types1, types2));
EXPECT_FALSE(pir::VerifyCompatibleShapes(types1, types3));
}
namespace TestNamespace {
class TestClass {};
} // namespace TestNamespace
TEST(type_test, get_type_name) {
auto name = pir::get_type_name<TestNamespace::TestClass>();
EXPECT_EQ(name, "TestNamespace::TestClass");
}
+24
View File
@@ -0,0 +1,24 @@
// Copyright (c) 2026 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/pir/include/core/builtin_type_interfaces.h"
#include "paddle/pir/include/core/type_utils.h"
TEST(type_util_test, verify_compatible_dims) {
EXPECT_TRUE(
pir::VerifyCompatibleDims({pir::ShapedTypeInterface::kDynamic, 2, 2}));
EXPECT_FALSE(pir::VerifyCompatibleDims({2, 3}));
}
+12
View File
@@ -0,0 +1,12 @@
if(WITH_CUSTOM_DEVICE)
paddle_test(test_custom_engine_loadlib SRCS test_custom_engine_loadlib.cc
custom_engine_op.cc)
endif()
paddle_test(test_custom_engine SRCS test_custom_engine_operation.cc
custom_engine_op.cc)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(test_custom_engine)
endif()
@@ -0,0 +1,184 @@
// Copyright (c) 2024 PaddlePaddle 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 "test/cpp/pir/custom_engine/custom_engine_op.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
namespace paddle {
namespace dialect {
const char *FakeEngineOp::attributes_name[2] = {"input_names", "output_names"};
OpInfoTuple FakeEngineOp::GetOpInfo() {
std::vector<paddle::dialect::OpInputInfo> inputs = {
OpInputInfo("x",
"pir::VectorType<paddle::dialect::DenseTensorType>",
false,
false,
false,
false)};
std::vector<paddle::dialect::OpAttributeInfo> attributes = {
paddle::dialect::OpAttributeInfo(
"input_names", "pir::ArrayAttribute", ""),
paddle::dialect::OpAttributeInfo(
"output_names", "pir::ArrayAttribute", "")};
std::vector<paddle::dialect::OpOutputInfo> outputs = {
OpOutputInfo("out",
"pir::VectorType<paddle::dialect::DenseTensorType>",
false,
false)};
paddle::dialect::OpRunTimeInfo run_time_info =
OpRunTimeInfo("", {}, "", {}, {}, {}, {}, {});
return std::make_tuple(
inputs, attributes, outputs, run_time_info, "fake_engine");
}
#define ADD_VEC_ATTRIBUTE(type, name) \
std::vector<pir::Attribute> name##_tmp; \
name##_tmp.reserve(name.size()); \
for (const auto &v : name) { \
name##_tmp.push_back(type::get(pir::IrContext::Instance(), v)); \
} \
pir::Attribute attr_##name = \
pir::ArrayAttribute::get(pir::IrContext::Instance(), name##_tmp); \
argument.AddAttribute(#name, attr_##name)
#define VERIFY_ATTRIBUTE(type, name) \
PADDLE_ENFORCE_GT( \
attributes.count(#name), \
0, \
common::errors::InvalidArgument(#name " does not exist.")); \
PADDLE_ENFORCE_EQ(attributes.at(#name).isa<type>(), \
true, \
common::errors::InvalidArgument( \
"Type of attribute: " #name " is not " #type))
void FakeEngineOp::Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value x,
std::vector<std::string> input_names,
std::vector<std::string> output_names,
std::vector<std::vector<int64_t>> outputs_shape,
std::vector<phi::DataType> outputs_dtype) {
VLOG(4) << "Start build FakeEngineOp";
VLOG(4) << "Builder construction inputs";
std::vector<pir::Value> argument_inputs = {x};
argument.AddInputs(argument_inputs);
VLOG(4) << "Builder construction attributes";
ADD_VEC_ATTRIBUTE(pir::StrAttribute, input_names);
ADD_VEC_ATTRIBUTE(pir::StrAttribute, output_names);
VLOG(4) << "Builder construction outputs";
std::vector<pir::Type> argument_outputs;
std::vector<pir::Type> out_types;
for (size_t i = 0; i < static_cast<size_t>(outputs_shape.size()); i++) {
if (outputs_dtype[i] == phi::DataType::UNDEFINED) {
out_types.push_back(pir::Type());
} else {
out_types.push_back(pir::DenseTensorType::get(
pir::IrContext::Instance(),
TransToIrDataType(outputs_dtype[i]),
phi::DDim(outputs_shape[i].data(), outputs_shape[i].size()),
phi::DataLayout::kNCHW,
phi::LoD(),
0));
}
}
pir::Type out_vector_type =
pir::VectorType::get(pir::IrContext::Instance(), out_types);
argument_outputs.push_back(out_vector_type);
argument.AddOutputs(argument_outputs.begin(), argument_outputs.end());
argument.AddRegion(nullptr);
::pir::PassStopGradientsDefaultly(argument);
}
void FakeEngineOp::VerifySig() {
VLOG(4) << "Start Verifying inputs, outputs and attributes for: "
"FakeEngineOp.";
VLOG(4) << "Verifying inputs:";
{
auto input_size = num_operands();
PADDLE_ENFORCE_EQ(input_size,
1,
common::errors::InvalidArgument(
"The size of inputs must be equal to 1."));
PADDLE_ENFORCE_EQ((*this)->operand_source(0).type().isa<pir::VectorType>(),
true,
common::errors::InvalidArgument(
"Type validation failed for the 0th input, got %s.",
(*this)->operand_source(0).type()));
if (auto vec_type =
(*this)->operand_source(0).type().dyn_cast<pir::VectorType>()) {
for (size_t i = 0; i < vec_type.size(); ++i) {
PADDLE_ENFORCE_EQ(
vec_type[i].isa<pir::DenseTensorType>(),
true,
common::errors::InvalidArgument(
"Type validation failed for the 0th input, got %s.",
(*this)->operand_source(0).type()));
}
}
}
VLOG(4) << "Verifying attributes:";
{
auto &attributes = this->attributes();
VERIFY_ATTRIBUTE(pir::ArrayAttribute, input_names);
VERIFY_ATTRIBUTE(pir::ArrayAttribute, output_names);
}
VLOG(4) << "Verifying outputs:";
{
auto output_size = num_results();
PADDLE_ENFORCE_EQ(output_size,
1,
common::errors::InvalidArgument(
"The size of outputs must be equal to 1."));
auto output_type = (*this)->result(0).type();
PADDLE_ENFORCE_EQ(output_type.isa<pir::VectorType>(),
true,
common::errors::InvalidArgument(
"Type validation failed for the 0th output."));
}
VLOG(4) << "End Verifying for: FakeEngineOp.";
}
pir::Block *FakeEngineOp::block() {
pir::Region &region = (*this)->region(0);
if (region.empty()) region.emplace_back();
return &region.front();
}
pir::Block *FakeEngineOp::block() const {
pir::Region &region = (*this)->region(0);
PADDLE_ENFORCE_EQ(region.empty(),
false,
::common::errors::Unavailable(
"Required CustomEngineOp's region must not be empty."));
return &region.front();
}
} // namespace dialect
} // namespace paddle
IR_DEFINE_EXPLICIT_TYPE_ID(paddle::dialect::FakeEngineOp)
@@ -0,0 +1,81 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <vector>
#include "paddle/fluid/pir/dialect/operator/interface/op_yaml_info.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/op_base.h"
#include "paddle/pir/include/core/op_trait.h"
#include "paddle/pir/include/core/operation_utils.h"
#include "test/cpp/pir/tools/macros_utils.h"
#if defined(_WIN32)
#ifndef EXPORT_API
#define EXPORT_API __declspec(dllexport)
#endif // EXPORT_API
#else
#define EXPORT_API
#endif // _WIN32
#define IR_DECLARE_EXPLICIT_PLUGIN_TYPE_ID(TYPE_CLASS) \
namespace pir { \
namespace detail { \
template <> \
class EXPORT_API TypeIdResolver<TYPE_CLASS> { \
public: \
static TypeId Resolve() { return id_; } \
static UniqueingId id_; \
}; \
} \
} // namespace pir
namespace paddle {
namespace dialect {
class FakeEngineOp
: public pir::Op<FakeEngineOp, paddle::dialect::OpYamlInfoInterface> {
public:
using Op::Op;
static const char *name() { return "custom_engine.fake_engine"; }
static const char *attributes_name[2];
static constexpr uint32_t attributes_num = 2;
static OpInfoTuple GetOpInfo();
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value x,
std::vector<std::string> input_names,
std::vector<std::string> output_names,
std::vector<std::vector<int64_t>> outputs_shape,
std::vector<phi::DataType> outputs_dtype);
void VerifySig();
pir::Block *block();
pir::Block *block() const;
pir::Value x() { return operand_source(0); }
pir::Value out() { return result(0); }
};
} // namespace dialect
} // namespace paddle
IR_DECLARE_EXPLICIT_PLUGIN_TYPE_ID(paddle::dialect::FakeEngineOp)
@@ -0,0 +1,263 @@
// Copyright (c) 2024 PaddlePaddle 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 <unordered_map>
#include "paddle/fluid/custom_engine/custom_engine_ext.h"
#include "paddle/fluid/framework/new_executor/instruction/custom_engine_instruction.h"
#include "paddle/fluid/framework/new_executor/pir_adaptor/pir_adaptor_util.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "test/cpp/pir/custom_engine/custom_engine_op.h"
#include "test/cpp/pir/custom_engine/fake_cpu_engine_base.h"
C_Status RegisterCustomEngineOp() {
pir::IrContext* ctx = pir::IrContext::Instance();
pir::Dialect* custom_engine_dialect =
ctx->GetOrRegisterDialect<paddle::dialect::CustomEngineDialect>();
EXPECT_EQ(custom_engine_dialect != nullptr, true);
ctx->RegisterOpInfo(custom_engine_dialect,
pir::TypeId::get<paddle::dialect::FakeEngineOp>(),
paddle::dialect::FakeEngineOp::name(),
paddle::dialect::FakeEngineOp::interface_set(),
paddle::dialect::FakeEngineOp::GetTraitSet(),
paddle::dialect::FakeEngineOp::attributes_num,
paddle::dialect::FakeEngineOp::attributes_name,
paddle::dialect::FakeEngineOp::VerifySigInvariants,
paddle::dialect::FakeEngineOp::VerifyRegionInvariants);
return C_SUCCESS;
}
C_Status CustomEngineOpLower(C_CustomEngineLowerParams* lower_param) {
// get lower params
pir::IrContext* ctx =
reinterpret_cast<pir::IrContext*>(lower_param->ir_context);
pir::Operation* op_item =
reinterpret_cast<pir::Operation*>(lower_param->operation);
phi::KernelKey* kernel_key =
reinterpret_cast<phi::KernelKey*>(lower_param->kernel_key);
phi::Place* place = reinterpret_cast<phi::Place*>(lower_param->place);
std::unordered_map<pir::Operation*, pir::Operation*>* map_op_pair =
reinterpret_cast<std::unordered_map<pir::Operation*, pir::Operation*>*>(
lower_param->map_op_pair);
std::unordered_map<pir::Value, pir::Value>* map_value_pair =
reinterpret_cast<std::unordered_map<pir::Value, pir::Value>*>(
lower_param->map_value_pair);
pir::Block* block = reinterpret_cast<pir::Block*>(lower_param->block);
// Prepare output types
std::vector<pir::Type> op_output_types;
for (size_t i = 0; i < op_item->num_results(); ++i) {
PushBackOutputTypes(ctx,
op_item,
op_item->result(i).type(),
*place,
*kernel_key,
&op_output_types);
}
// Prepare input
std::vector<pir::Value> vec_inputs;
for (size_t i = 0; i < op_item->num_operands(); ++i) {
auto cur_in = op_item->operand_source(i);
PADDLE_ENFORCE_EQ(
map_value_pair->count(cur_in),
true,
common::errors::PreconditionNotMet(
"[%d]'s input of [%s] op MUST in map pair", i, op_item->name()));
auto new_in = map_value_pair->at(cur_in);
vec_inputs.push_back(new_in);
}
// Prepare attr
std::unordered_map<std::string, pir::Attribute> op_attribute;
auto op_attr_map = op_item->attributes();
for (auto& map_item : op_attr_map) {
op_attribute.emplace(map_item.first, map_item.second);
}
pir::OpInfo custom_engine_op_info =
ctx->GetRegisteredOpInfo(paddle::dialect::FakeEngineOp::name());
pir::Operation* op = pir::Operation::Create(vec_inputs,
op_attribute,
op_output_types,
custom_engine_op_info,
1,
{},
true);
op->set_attribute("origin_id", pir::Int64Attribute::get(ctx, op->id()));
op->set_attribute("op_name", pir::StrAttribute::get(ctx, op->name()));
VLOG(3) << "CustomEngineOpLower get op_item subgraph block.";
pir::Region& op_item_region = op_item->region(0);
PADDLE_ENFORCE_EQ(op_item_region.empty(),
false,
::common::errors::Unavailable(
"Required CustomEngineOp's region must not be empty."));
pir::Block* sub_graph_block = &(op_item_region.front());
VLOG(3) << "CustomEngineOpLower set new op subgraph block.";
pir::Region& region = op->region(0);
if (region.empty()) {
region.emplace_back();
}
pir::Block* op_block = &(region.front());
// process subgraph block
pir::ProcessBlock(
*place, sub_graph_block, op_block, ctx, map_op_pair, map_value_pair);
if (VLOG_IS_ON(3)) {
std::stringstream ss;
ss << "CustomEngineOpLower new op:";
op->Print(ss);
VLOG(3) << ss.str();
}
(*map_op_pair)[op_item] = op;
// only deal with single output
if (op_item->num_results() > 0) {
for (size_t i = 0; i < op_item->num_results(); ++i) {
(*map_value_pair)[op_item->result(i)] = op->result(i);
}
}
block->push_back(op);
return C_SUCCESS;
}
class CustomEngine {
public:
CustomEngine(std::vector<phi::DenseTensor*> tensor_args,
std::vector<phi::DenseTensor*> return_tensor)
: tensor_args_(tensor_args), return_tensor_(return_tensor) {}
~CustomEngine() {}
void Run(const phi::DeviceContext& device_ctx, const phi::Place& place) {
PADDLE_ENFORCE_EQ(
tensor_args_.size(),
2u,
common::errors::PreconditionNotMet("tensor_args.size != 2"));
PADDLE_ENFORCE_EQ(
return_tensor_.size(),
1u,
common::errors::PreconditionNotMet("return_tensor.size != 1"));
// phi::AddKernel<float, phi::DeviceContext>(device_ctx, *(tensor_args_[0]),
// *(tensor_args_[1]),return_tensor_[0]);
phi::Copy(device_ctx, *(tensor_args_[0]), place, true, return_tensor_[0]);
return;
}
private:
std::vector<phi::DenseTensor*> tensor_args_;
std::vector<phi::DenseTensor*> return_tensor_;
std::vector<phi::DenseTensor*> template_tensor_;
};
C_Status GraphEngineExecute(C_CustomEngineInstruction instruction) {
paddle::framework::CustomEngineInstruction* instruction_ =
reinterpret_cast<paddle::framework::CustomEngineInstruction*>(
instruction);
CustomEngine* customengine =
reinterpret_cast<CustomEngine*>(instruction_->CustomEngine());
customengine->Run(instruction_->DeviceContext(),
instruction_->DeviceContext().GetPlace());
return C_SUCCESS;
}
C_Status GraphEngineBuild(C_CustomEngineInstruction instruction) {
paddle::framework::CustomEngineInstruction* instruction_ =
reinterpret_cast<paddle::framework::CustomEngineInstruction*>(
instruction);
pir::Operation* op = instruction_->Operation();
const paddle::framework::ValueExecutionInfo* value_exec_info =
instruction_->GetValueExecutionInfo();
// prepare input tensors
std::vector<phi::DenseTensor*> tensor_args;
PADDLE_ENFORCE_EQ(op->num_operands(),
1u,
common::errors::PreconditionNotMet(
"custom engine op should has 1 operand"));
auto vec_in = op->operand_source(0).defining_op()->operands_source();
for (auto in : vec_in) {
auto var_name = value_exec_info->GetVarName(in);
auto tensor = value_exec_info->GetScope()
->FindVar(var_name)
->GetMutable<phi::DenseTensor>();
tensor_args.push_back(tensor);
}
// prepare output tensors
std::vector<phi::DenseTensor*> return_tensor;
PADDLE_ENFORCE_EQ(op->num_results(),
1u,
common::errors::PreconditionNotMet(
"custom engine op should has 1 result"));
pir::Value vec_result = op->result(0);
PADDLE_ENFORCE_EQ(vec_result.type().isa<pir::VectorType>(),
true,
common::errors::PreconditionNotMet(
"custom engine op result should be vectortype"));
auto vec_out = op->result(0).first_use().owner()->results();
for (auto out : vec_out) {
bool check =
out && out.type() && out.type().isa<paddle::dialect::DenseTensorType>();
PADDLE_ENFORCE_EQ(
check,
true,
common::errors::PreconditionNotMet(
"customEngine instruction only support DenseTensorType"));
auto var_name = value_exec_info->GetVarName(out);
auto tensor = value_exec_info->GetScope()
->Var(var_name)
->GetMutable<phi::DenseTensor>();
return_tensor.push_back(tensor);
auto alloc_tensor_type =
out.type().dyn_cast<paddle::dialect::AllocatedDenseTensorType>();
tensor->set_type(
paddle::dialect::TransToPhiDataType(alloc_tensor_type.dtype()));
tensor->Resize(alloc_tensor_type.dims());
}
CustomEngine* fake_engine = new CustomEngine(tensor_args, return_tensor);
auto customEngineDeleter = [](void* ptr) {
CustomEngine* customEngine = static_cast<CustomEngine*>(ptr);
if (customEngine != nullptr) {
delete customEngine;
} else {
PADDLE_THROW(
common::errors::PreconditionNotMet("customEngine is nullptr"));
}
};
instruction_->SetCustomEngine(reinterpret_cast<void*>(fake_engine));
instruction_->SetCustomEngineDeleter(customEngineDeleter);
return C_SUCCESS;
}
void InitPluginCustomEngine(CustomEngineParams* params) {
memset(reinterpret_cast<void*>(params->interface),
0,
sizeof(C_CustomEngineInterface));
params->interface->register_custom_engine_op = RegisterCustomEngineOp;
params->interface->graph_engine_build = GraphEngineBuild;
params->interface->graph_engine_execute = GraphEngineExecute;
params->interface->custom_engine_op_lower = CustomEngineOpLower;
}
@@ -0,0 +1,173 @@
// Copyright (c) 2024 PaddlePaddle 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 <unordered_map>
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_factory.h"
#include "paddle/phi/core/tensor_utils.h"
#include "paddle/pir/include/core/block.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/operation.h"
using paddle::dialect::AllocatedDenseTensorArrayType;
using paddle::dialect::AllocatedDenseTensorType;
using paddle::dialect::AllocatedSelectedRowsType;
using paddle::dialect::AllocatedSparseCooTensorType;
using paddle::dialect::AllocatedSparseCsrTensorType;
using paddle::dialect::DenseTensorArrayType;
using paddle::dialect::DenseTensorType;
using paddle::dialect::SelectedRowsType;
using paddle::dialect::SparseCooTensorType;
using paddle::dialect::SparseCsrTensorType;
template <class IrType1, class IrType2>
static pir::Type create_sparse_coo_tensor_type(pir::Type type,
const phi::Place& place,
pir::Type out_dtype,
pir::IrContext* ctx) {
auto input_type = type.dyn_cast<IrType1>();
return IrType2::get(ctx,
place,
out_dtype,
input_type.dims(),
input_type.non_zero_dims(),
input_type.data_layout(),
input_type.non_zero_indices(),
input_type.non_zero_elements(),
input_type.coalesced());
}
template <class IrType1, class IrType2>
static pir::Type create_sparse_csr_tensor_type(pir::Type type,
const phi::Place& place,
pir::Type out_dtype,
pir::IrContext* ctx) {
auto input_type = type.dyn_cast<IrType1>();
return IrType2::get(ctx,
place,
out_dtype,
input_type.dims(),
input_type.data_layout(),
input_type.non_zero_crows(),
input_type.non_zero_cols(),
input_type.non_zero_elements());
}
template <class IrType1, class IrType2>
static pir::Type create_type(pir::Type type,
const phi::Place& place,
pir::Type out_dtype,
pir::IrContext* ctx) {
auto input_type = type.dyn_cast<IrType1>();
return IrType2::get(ctx,
place,
out_dtype,
input_type.dims(),
input_type.data_layout(),
input_type.lod(),
input_type.offset());
}
static pir::Type BuildOutputType(pir::Type type,
const phi::Place& place,
pir::IrContext* ctx) {
if (type.isa<DenseTensorType>()) {
auto out_dtype = type.dyn_cast<DenseTensorType>().dtype();
return create_type<DenseTensorType, AllocatedDenseTensorType>(
type, place, out_dtype, ctx);
} else if (type.isa<SelectedRowsType>()) {
auto out_dtype = type.dyn_cast<SelectedRowsType>().dtype();
return create_type<SelectedRowsType, AllocatedSelectedRowsType>(
type, place, out_dtype, ctx);
} else if (type.isa<DenseTensorArrayType>()) {
auto array_type = type.dyn_cast<DenseTensorArrayType>();
return AllocatedDenseTensorArrayType::get(ctx,
place,
array_type.dtype(),
array_type.dims(),
array_type.data_layout());
} else if (type.isa<SparseCooTensorType>()) {
auto out_dtype = type.dyn_cast<SparseCooTensorType>().dtype();
return create_sparse_coo_tensor_type<SparseCooTensorType,
AllocatedSparseCooTensorType>(
type, place, out_dtype, ctx);
} else if (type.isa<SparseCsrTensorType>()) {
auto out_dtype = type.dyn_cast<SparseCsrTensorType>().dtype();
return create_sparse_csr_tensor_type<SparseCsrTensorType,
AllocatedSparseCsrTensorType>(
type, place, out_dtype, ctx);
} else {
PADDLE_THROW(common::errors::Unimplemented(
"BuildOutputType only support DenseTensorType, SelectedRowsType, "
"SparseCooTensorType and SparseCsrTensorType"));
}
}
void PushBackOutputTypes(pir::IrContext* ctx,
pir::Operation* op_item,
const pir::Type& origin_type,
const phi::Place& out_place,
const phi::KernelKey& kernel_key,
std::vector<pir::Type>* op_output_types) {
auto result_type = origin_type;
if (!result_type) {
op_output_types->push_back(result_type);
} else if (result_type.isa<paddle::dialect::DenseTensorType>() ||
result_type.isa<paddle::dialect::SelectedRowsType>() ||
result_type.isa<paddle::dialect::DenseTensorArrayType>() ||
result_type.isa<paddle::dialect::SparseCooTensorType>() ||
result_type.isa<paddle::dialect::SparseCsrTensorType>()) {
} else if (result_type.isa<pir::VectorType>()) {
std::vector<pir::Type> vec_inner_types;
auto base_types = result_type.dyn_cast<pir::VectorType>().data();
for (auto& base_type : base_types) {
if (base_type) {
if (base_type.isa<paddle::dialect::DenseTensorType>() ||
base_type.isa<paddle::dialect::SelectedRowsType>()) {
vec_inner_types.push_back(BuildOutputType(base_type, out_place, ctx));
} else {
PADDLE_THROW(common::errors::Unimplemented(
"only support dense tensor and selected rows in vector type "
"for now"));
}
} else {
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
phi::DDim dims = {};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{}};
size_t offset = 0;
auto dense_tensor_dtype = paddle::dialect::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
auto allocated_dense_tensor_dtype =
paddle::dialect::AllocatedDenseTensorType::get(
ctx, out_place, dense_tensor_dtype);
vec_inner_types.push_back(allocated_dense_tensor_dtype);
}
}
pir::Type t1 = pir::VectorType::get(ctx, vec_inner_types);
op_output_types->push_back(t1);
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Result type only support DenseTensorType, SelectedRowType, "
"SparseCooTensorType, SparseCsrTensorType and "
"VectorType"));
}
}
@@ -0,0 +1,152 @@
// Copyright (c) 2022 PaddlePaddle 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 <gtest/gtest.h>
#include <array>
#include <string>
#include "paddle/fluid/custom_engine/custom_engine_manager.h"
#include "paddle/fluid/platform/init.h"
#include "paddle/phi/backends/custom/fake_cpu_device.h"
#include "paddle/phi/backends/device_manager.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/memory/allocation/allocator_facade.h"
#include "paddle/phi/core/platform/device_context.h"
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/framework/new_executor/pir_interpreter.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/type_id.h"
#include "test/cpp/pir/custom_engine/fake_cpu_engine.h"
#define OUT_NAME "program_out"
void RegisterDevice() {
CustomRuntimeParams runtime_params;
runtime_params.size = sizeof(CustomRuntimeParams);
auto device_interface = std::make_unique<C_DeviceInterface>();
runtime_params.interface = device_interface.get();
std::memset(runtime_params.interface, 0, sizeof(C_DeviceInterface));
runtime_params.interface->size = sizeof(C_DeviceInterface);
InitFakeCPUDevice(&runtime_params);
phi::LoadCustomRuntimeLib(
runtime_params, std::move(device_interface), "", nullptr);
}
void RegisterEngine() {
CustomEngineParams engine_params;
std::memset(&engine_params, 0, sizeof(CustomEngineParams));
engine_params.size = sizeof(CustomEngineParams);
auto engine_interface = new (C_CustomEngineInterface);
engine_params.interface = engine_interface;
std::memset(engine_params.interface, 0, sizeof(C_CustomEngineInterface));
engine_params.interface->size = sizeof(C_CustomEngineInterface);
InitPluginCustomEngine(&engine_params);
paddle::custom_engine::LoadCustomEngineLib("", &engine_params);
}
void InitCustom() {
RegisterDevice();
EXPECT_GT(static_cast<int>(phi::DeviceManager::GetAllDeviceTypes().size()),
0);
auto place = phi::CustomPlace(DEVICE_TYPE, 0);
auto device = phi::DeviceManager::GetDeviceWithPlace(place);
EXPECT_NE(device, nullptr);
std::vector<phi::Place> places;
auto device_types = phi::DeviceManager::GetAllDeviceTypes();
for (auto dev_type : device_types) {
auto devices = phi::DeviceManager::GetDeviceList(dev_type);
for (auto dev_id : devices) {
places.push_back(phi::PlaceHelper::CreatePlace(dev_type, dev_id));
}
}
EXPECT_GT(static_cast<int>(places.size()), 0);
places.emplace_back(phi::CPUPlace());
phi::DeviceContextPool::Init(places);
RegisterEngine();
}
void CreateProgram(pir::Program *program) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Block *block = program->block();
pir::Builder builder(ctx, block);
auto full_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{2, 2}, 100);
auto full_op2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{2, 2}, 10);
auto buildin_combine_op = builder.Build<pir::CombineOp>(
std::vector<pir::Value>{full_op1.result(0), full_op2.result(0)});
auto engine_op = builder.Build<paddle::dialect::FakeEngineOp>(
buildin_combine_op.result(0),
std::vector<std::string>{"input_0", "input_1"},
std::vector<std::string>{"output_0"},
std::vector<std::vector<int64_t>>{{2, 2}},
std::vector<phi::DataType>{phi::DataType::FLOAT32});
engine_op->region(0).emplace_back();
auto output = builder.Build<pir::SplitOp>(engine_op.result(0)).outputs()[0];
builder.Build<pir::ShadowOutputOp>(output, OUT_NAME);
return;
}
TEST(CustomDevice, Tensor) {
paddle::framework::InitMemoryMethod();
InitCustom();
pir::IrContext *ctx = pir::IrContext::Instance();
pir::Program *program = new pir::Program(ctx);
CreateProgram(program);
EXPECT_EQ(program->block()->size(), 6u);
auto kernel_program = pir::PdOpLowerToKernelPass(program);
auto place = phi::CustomPlace(DEVICE_TYPE, 0);
paddle::framework::Scope scope;
paddle::framework::InterpreterCore test_core(
place, {}, kernel_program->block(), &scope);
test_core.SetSkipGcVars({OUT_NAME});
test_core.Run({});
auto out_tensor =
test_core.local_scope() == nullptr
? scope.FindVar(OUT_NAME)->Get<phi::DenseTensor>()
: test_core.local_scope()->FindVar(OUT_NAME)->Get<phi::DenseTensor>();
bool res0 = out_tensor.data<float>()[0] == 100;
EXPECT_EQ(res0, true);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -0,0 +1,104 @@
// Copyright (c) 2024 PaddlePaddle 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 <gtest/gtest.h>
#include <sstream>
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/type_id.h"
#include "test/cpp/pir/custom_engine/custom_engine_op.h"
TEST(op_test, region_test) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Dialect *custom_engine_dialect =
ctx->GetOrRegisterDialect<paddle::dialect::CustomEngineDialect>();
EXPECT_EQ(custom_engine_dialect != nullptr, true);
ctx->RegisterOpInfo(custom_engine_dialect,
pir::TypeId::get<paddle::dialect::FakeEngineOp>(),
paddle::dialect::FakeEngineOp::name(),
paddle::dialect::FakeEngineOp::interface_set(),
paddle::dialect::FakeEngineOp::GetTraitSet(),
paddle::dialect::FakeEngineOp::attributes_num,
paddle::dialect::FakeEngineOp::attributes_name,
paddle::dialect::FakeEngineOp::VerifySigInvariants,
paddle::dialect::FakeEngineOp::VerifyRegionInvariants);
pir::Program program(ctx);
pir::Block *block = program.block();
pir::Builder builder(ctx, block);
auto full_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{2, 2}, 100);
auto full_op2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{2, 2}, 10);
auto buildin_combine_op = builder.Build<pir::CombineOp>(
std::vector<pir::Value>{full_op1.result(0), full_op2.result(0)});
pir::OpInfo fake_engine_op_info =
ctx->GetRegisteredOpInfo(paddle::dialect::FakeEngineOp::name());
std::vector<pir::Type> out_types;
out_types.push_back(
pir::DenseTensorType::get(pir::IrContext::Instance(),
pir::Float32Type::get(ctx),
phi::DDim(std::vector<int64_t>{2, 2}.data(), 2),
phi::DataLayout::kNCHW,
phi::LoD(),
0));
pir::Type out_vector_type =
pir::VectorType::get(pir::IrContext::Instance(), out_types);
std::vector<pir::Type> output_types = {out_vector_type};
pir::AttributeMap attribute_map;
std::vector<pir::Attribute> val;
val.push_back(pir::StrAttribute::get(ctx, "input_0"));
val.push_back(pir::StrAttribute::get(ctx, "input_1"));
attribute_map.insert({"input_names", pir::ArrayAttribute::get(ctx, val)});
std::vector<pir::Attribute> out_val;
out_val.push_back(pir::StrAttribute::get(ctx, "output_0"));
out_val.push_back(pir::StrAttribute::get(ctx, "output_1"));
attribute_map.insert(
{"output_names", pir::ArrayAttribute::get(ctx, out_val)});
pir::Operation *op1 = pir::Operation::Create({buildin_combine_op.result(0)},
attribute_map,
output_types,
fake_engine_op_info);
// (3) Test custom operation printer
std::stringstream ss1;
op1->Print(ss1);
builder.Insert(op1);
auto op2 = builder.Build<paddle::dialect::FakeEngineOp>(
buildin_combine_op.result(0),
std::vector<std::string>{"input_0", "input_1"},
std::vector<std::string>{"output_0"},
std::vector<std::vector<int64_t>>{{2, 2}},
std::vector<phi::DataType>{phi::DataType::FLOAT32});
std::stringstream ss2;
op2->Print(ss2);
EXPECT_EQ(block->size(), 5u);
}
+3
View File
@@ -0,0 +1,3 @@
if(WITH_DISTRIBUTE)
paddle_test(dist_dialect_test SRCS dist_dialect_test.cc)
endif()
@@ -0,0 +1,591 @@
// Copyright (c) 2024 PaddlePaddle 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 <gtest/gtest.h>
#include <iostream>
#include "paddle/fluid/pir/dialect/distributed/ir/dist_attribute.h"
#include "paddle/fluid/pir/dialect/distributed/ir/dist_dialect.h"
#include "paddle/fluid/pir/dialect/distributed/ir/dist_interface.h"
#include "paddle/fluid/pir/dialect/distributed/ir/dist_op.h"
#include "paddle/fluid/pir/dialect/distributed/ir/dist_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/api_builder.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/program.h"
using namespace paddle::dialect; // NOLINT
TEST(process_mesh_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
std::vector<int64_t> mesh_shape = {2, 2};
std::vector<int64_t> process_ids = {0, 1, 2, 3};
std::vector<std::string> dim_names = {"x", "y"};
std::vector<std::string> dim_names_2 = {"x", "s"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
// construct a ProcessMeshAttribute.
auto mesh_attr =
ProcessMeshAttribute::get(ctx, mesh_shape, process_ids, dim_names);
auto mesh_attr_1 = ProcessMeshAttribute::get(ctx, process_mesh);
auto mesh_attr_2 =
ProcessMeshAttribute::get(ctx, mesh_shape, process_ids, dim_names_2);
EXPECT_EQ(mesh_attr, mesh_attr_1);
EXPECT_NE(mesh_attr, mesh_attr_2);
// test member function.
EXPECT_EQ(mesh_attr.process_mesh(), process_mesh);
EXPECT_EQ(mesh_attr.shape(), mesh_shape);
EXPECT_EQ(mesh_attr.process_ids(), process_ids);
EXPECT_EQ(mesh_attr.dim_names(), dim_names);
EXPECT_EQ(mesh_attr.size(), 4);
EXPECT_EQ(mesh_attr.ndim(), 2);
EXPECT_EQ(mesh_attr.dim_size(0), 2);
EXPECT_EQ(mesh_attr.dim_size("y"), 2);
EXPECT_FALSE(mesh_attr.empty());
EXPECT_TRUE(mesh_attr.contains(3));
EXPECT_EQ(mesh_attr.hash(), process_mesh.hash());
EXPECT_EQ(mesh_attr.to_string(), process_mesh.to_string());
}
TEST(tensor_dist_attr_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
std::vector<int64_t> dims_mapping = {0, -1};
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status,
partial_status_1{{1, phi::ReduceType::kRedSum}};
auto mesh_attr =
ProcessMeshAttribute::get(ctx, mesh_shape, process_ids, dim_names);
// construct a TensorDistAttribute.
auto tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
auto tensor_dist_attr_1 =
TensorDistAttribute::get(ctx, process_mesh, dims_mapping, partial_status);
auto tensor_dist_attr_2 = TensorDistAttribute::get(
ctx, process_mesh, dims_mapping, partial_status_1);
EXPECT_EQ(tensor_dist_attr, tensor_dist_attr_1);
EXPECT_NE(tensor_dist_attr, tensor_dist_attr_2);
// test member function.
EXPECT_EQ(tensor_dist_attr.process_mesh_attr(), mesh_attr);
EXPECT_EQ(tensor_dist_attr.process_mesh_attr().process_mesh(), process_mesh);
EXPECT_EQ(tensor_dist_attr.dims_mapping(), dims_mapping);
EXPECT_EQ(tensor_dist_attr.partial_status(), partial_status);
}
TEST(dist_dense_tensor_type_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
auto mesh_attr = ProcessMeshAttribute::get(ctx, process_mesh);
std::vector<int64_t> dims_mapping = {0, -1};
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status{
{1, phi::ReduceType::kRedSum}};
// construct a TensorDistAttribute.
auto tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
common::DDim dims = {2, 2};
common::DataLayout data_layout = common::DataLayout::NCHW;
pir::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_type = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
auto dist_densor_type =
DistDenseTensorType::get(ctx, dense_tensor_type, tensor_dist_attr, dims);
EXPECT_EQ(dist_densor_type.process_mesh_attr(), mesh_attr);
EXPECT_EQ(dist_densor_type.process_mesh_attr().process_mesh(), process_mesh);
EXPECT_EQ(dist_densor_type.dims_mapping(), dims_mapping);
EXPECT_EQ(dist_densor_type.partial_status(), partial_status);
EXPECT_EQ(dist_densor_type.dtype().isa<pir::Float32Type>(), true);
EXPECT_EQ(dist_densor_type.global_ddim(), dims);
EXPECT_EQ(dist_densor_type.data_layout(), data_layout);
EXPECT_EQ(dist_densor_type.local_ddim(), dims);
}
TEST(dist_dense_tensor_type_test, warp_type_interface) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
auto mesh_attr = ProcessMeshAttribute::get(ctx, process_mesh);
std::vector<int64_t> dims_mapping = {0, -1};
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status{
{1, phi::ReduceType::kRedSum}};
// construct a TensorDistAttribute.
auto tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
common::DDim dims = {2, 2};
common::DataLayout data_layout = common::DataLayout::NCHW;
pir::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_type = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
pir::Type dist_densor_type =
DistDenseTensorType::get(ctx, dense_tensor_type, tensor_dist_attr, dims);
EXPECT_TRUE(dist_densor_type.isa<pir::DenseTensorType>());
EXPECT_EQ(dist_densor_type.dyn_cast<pir::DenseTensorType>(),
dense_tensor_type);
}
TEST(dist_dense_tensor_type_test, dist_interface) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
auto mesh_attr = ProcessMeshAttribute::get(ctx, process_mesh);
std::vector<int64_t> dims_mapping = {0, -1};
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status{
{1, phi::ReduceType::kRedSum}};
// construct a TensorDistAttribute.
auto tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
common::DDim dims = {4, 8};
common::DDim local_dims = {2, 8};
common::DataLayout data_layout = common::DataLayout::NCHW;
pir::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::DenseTensorType dense_tensor_type = pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
pir::Type dist_densor_type =
DistDenseTensorType::get(ctx, dense_tensor_type, tensor_dist_attr);
EXPECT_TRUE(dist_densor_type.isa<pir::DenseTensorType>());
EXPECT_EQ(dist_densor_type.dyn_cast<pir::DenseTensorType>(),
dense_tensor_type);
// test local cast
auto local_dense_tensor_type = dist_densor_type.dyn_cast<DistTypeInterface>()
.local_type()
.dyn_cast<pir::DenseTensorType>();
EXPECT_TRUE(local_dense_tensor_type.isa<pir::DenseTensorType>());
EXPECT_FALSE(local_dense_tensor_type.isa<DistDenseTensorType>());
EXPECT_EQ(local_dense_tensor_type.dtype().isa<pir::Float32Type>(), true);
EXPECT_EQ(local_dense_tensor_type.dims(), local_dims);
EXPECT_EQ(local_dense_tensor_type.data_layout(), data_layout);
EXPECT_EQ(local_dense_tensor_type.lod(), lod);
EXPECT_EQ(local_dense_tensor_type.offset(), offset);
}
TEST(operation_dist_attr_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status;
auto mesh_attr =
ProcessMeshAttribute::get(ctx, mesh_shape, process_ids, dim_names);
std::vector<int64_t> dims_mapping = {0, -1};
// construct a OperationDistAttribute.
auto x_tensor_dist_attr =
TensorDistAttribute::get(ctx, process_mesh, dims_mapping, partial_status);
auto y_tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
auto out_tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
auto operand_attrs =
std::vector<pir::Attribute>{x_tensor_dist_attr, y_tensor_dist_attr};
auto result_attrs = std::vector<pir::Attribute>{out_tensor_dist_attr};
auto op_attr = OperationDistAttribute::get(
ctx, process_mesh, operand_attrs, result_attrs);
auto op_attr_1 =
OperationDistAttribute::get(ctx, mesh_attr, operand_attrs, result_attrs);
// construct another OperationDistAttribute.
std::vector<std::string> dim_names_2 = {"x", "s"};
auto mesh_attr_2 =
ProcessMeshAttribute::get(ctx, mesh_shape, process_ids, dim_names_2);
auto x_tensor_dist_attr_2 =
TensorDistAttribute::get(ctx, mesh_attr_2, dims_mapping, partial_status);
auto y_tensor_dist_attr_2 =
TensorDistAttribute::get(ctx, mesh_attr_2, dims_mapping, partial_status);
auto out_tensor_dist_attr_2 =
TensorDistAttribute::get(ctx, mesh_attr_2, dims_mapping, partial_status);
auto operand_attrs_2 =
std::vector<pir::Attribute>{x_tensor_dist_attr_2, y_tensor_dist_attr_2};
auto result_attrs_2 = std::vector<pir::Attribute>{out_tensor_dist_attr_2};
auto op_attr_2 = OperationDistAttribute::get(
ctx, mesh_attr_2, operand_attrs_2, result_attrs_2);
// check
EXPECT_EQ(op_attr, op_attr_1);
EXPECT_NE(op_attr, op_attr_2);
EXPECT_EQ(op_attr.process_mesh_attr(), mesh_attr);
EXPECT_EQ(op_attr.process_mesh_attr().process_mesh(), process_mesh);
EXPECT_EQ(op_attr.operands(), operand_attrs);
EXPECT_EQ(op_attr.operand(0), operand_attrs.at(0));
EXPECT_EQ(op_attr.operand(1), operand_attrs.at(1));
EXPECT_EQ(op_attr.num_operands(), (uint32_t)2);
EXPECT_EQ(op_attr.results(), result_attrs);
EXPECT_EQ(op_attr.result(0), result_attrs.at(0));
EXPECT_EQ(op_attr.num_results(), (uint32_t)1);
}
TEST(shard_tensor_op_replicate_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
auto mesh_attr = ProcessMeshAttribute::get(ctx, process_mesh);
std::vector<int64_t> data_shape = {12, 6};
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status;
// construct a replicated
std::vector<int64_t> dims_mapping = {-1, -1};
auto data_op = builder.Build<paddle::dialect::DataOp>(
"w0", data_shape, phi::DataType::FLOAT32, phi::CPUPlace());
std::vector<int64_t> local_shape = {12, 6};
auto tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
pir::AttributeMap attr_map = {{"tensor_dist_attr", tensor_dist_attr}};
paddle::dialect::ShardTensorOp shard_op =
builder.Build<paddle::dialect::ShardTensorOp>(data_op.result(0),
attr_map);
EXPECT_TRUE(shard_op.out().type().isa<DistDenseTensorType>());
auto op_out_type = shard_op.out().type().dyn_cast<DistDenseTensorType>();
EXPECT_EQ(op_out_type.global_ddim(), phi::make_ddim(data_shape));
EXPECT_EQ(op_out_type.local_ddim(), phi::make_ddim(local_shape));
EXPECT_EQ(op_out_type.process_mesh_attr(), mesh_attr);
EXPECT_EQ(op_out_type.dims_mapping(), dims_mapping);
EXPECT_EQ(op_out_type.partial_dims().size(), (size_t)0);
EXPECT_EQ(
shard_op.attribute<OperationDistAttribute>("op_dist_attr").num_operands(),
(uint32_t)0);
EXPECT_EQ(
shard_op.attribute<OperationDistAttribute>("op_dist_attr").num_results(),
(uint32_t)1);
EXPECT_EQ(shard_op.attribute<OperationDistAttribute>("op_dist_attr")
.process_mesh_attr(),
mesh_attr);
// check reshard
std::vector<int64_t> dst_mesh_shape = {3, 2};
std::vector<int64_t> dst_dims_mapping = {-1, 0};
phi::distributed::ProcessMesh dst_process_mesh(
dst_mesh_shape, process_ids, dim_names);
auto dst_mesh_attr = ProcessMeshAttribute::get(ctx, dst_process_mesh);
auto dst_tensor_dist_attr = TensorDistAttribute::get(
ctx, dst_mesh_attr, dst_dims_mapping, partial_status);
paddle::dialect::ReshardOp reshard_op =
builder.Build<paddle::dialect::ReshardOp>(shard_op.out(),
dst_tensor_dist_attr);
EXPECT_TRUE(reshard_op.result(0).type().isa<DistDenseTensorType>());
auto dst_op_out_type =
reshard_op.result(0).type().dyn_cast<DistDenseTensorType>();
EXPECT_EQ(dst_op_out_type.global_ddim(), phi::make_ddim(data_shape));
EXPECT_EQ(dst_op_out_type.local_ddim(), phi::make_ddim({12, 2}));
EXPECT_EQ(dst_op_out_type.process_mesh_attr(), dst_mesh_attr);
EXPECT_EQ(dst_op_out_type.dims_mapping(), dst_dims_mapping);
EXPECT_EQ(dst_op_out_type.partial_dims().size(), (size_t)0);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.num_operands(),
(uint32_t)1);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.num_results(),
(uint32_t)1);
phi::distributed::ProcessMesh flatten_process_mesh(
{6}, process_ids, {"merged"});
auto flatten_mesh_attr = ProcessMeshAttribute::get(ctx, flatten_process_mesh);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.process_mesh_attr(),
flatten_mesh_attr);
}
TEST(shard_tensor_op_shard_row_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
auto mesh_attr = ProcessMeshAttribute::get(ctx, process_mesh);
std::vector<int64_t> data_shape = {12, 6};
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status;
// construct a row shard
std::vector<int64_t> dims_mapping = {1, -1};
auto data_op = builder.Build<paddle::dialect::DataOp>(
"w1", data_shape, phi::DataType::FLOAT32, phi::CPUPlace());
std::vector<int64_t> local_shape = {4, 6};
auto tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
pir::AttributeMap attr_map = {{"tensor_dist_attr", tensor_dist_attr}};
paddle::dialect::ShardTensorOp shard_op =
builder.Build<paddle::dialect::ShardTensorOp>(data_op.result(0),
attr_map);
EXPECT_TRUE(shard_op.out().type().isa<DistDenseTensorType>());
auto op_out_type = shard_op.out().type().dyn_cast<DistDenseTensorType>();
EXPECT_EQ(op_out_type.global_ddim(), phi::make_ddim(data_shape));
EXPECT_EQ(op_out_type.local_ddim(), phi::make_ddim(local_shape));
EXPECT_EQ(op_out_type.process_mesh_attr(), mesh_attr);
EXPECT_EQ(op_out_type.dims_mapping(), dims_mapping);
EXPECT_EQ(op_out_type.partial_dims().size(), (size_t)0);
EXPECT_EQ(
shard_op.attribute<OperationDistAttribute>("op_dist_attr").num_operands(),
(uint32_t)0);
EXPECT_EQ(
shard_op.attribute<OperationDistAttribute>("op_dist_attr").num_results(),
(uint32_t)1);
EXPECT_EQ(shard_op.attribute<OperationDistAttribute>("op_dist_attr")
.process_mesh_attr(),
mesh_attr);
// check reshard
std::vector<int64_t> dst_mesh_shape = {3, 2};
phi::distributed::ProcessMesh dst_process_mesh(
dst_mesh_shape, process_ids, dim_names);
auto dst_mesh_attr = ProcessMeshAttribute::get(ctx, dst_process_mesh);
auto dst_tensor_dist_attr = TensorDistAttribute::get(
ctx, dst_mesh_attr, dims_mapping, partial_status);
paddle::dialect::ReshardOp reshard_op =
builder.Build<paddle::dialect::ReshardOp>(shard_op.out(),
dst_tensor_dist_attr);
EXPECT_TRUE(reshard_op.result(0).type().isa<DistDenseTensorType>());
auto dst_op_out_type =
reshard_op.result(0).type().dyn_cast<DistDenseTensorType>();
EXPECT_EQ(dst_op_out_type.global_ddim(), phi::make_ddim(data_shape));
EXPECT_EQ(dst_op_out_type.local_ddim(), phi::make_ddim({6, 6}));
EXPECT_EQ(dst_op_out_type.process_mesh_attr(), dst_mesh_attr);
EXPECT_EQ(dst_op_out_type.dims_mapping(), dims_mapping);
EXPECT_EQ(dst_op_out_type.partial_dims().size(), (size_t)0);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.num_operands(),
(uint32_t)1);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.num_results(),
(uint32_t)1);
phi::distributed::ProcessMesh flatten_process_mesh(
{6}, process_ids, {"merged"});
auto flatten_mesh_attr = ProcessMeshAttribute::get(ctx, flatten_process_mesh);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.process_mesh_attr(),
flatten_mesh_attr);
}
TEST(shard_tensor_op_shard_col_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
auto mesh_attr = ProcessMeshAttribute::get(ctx, process_mesh);
std::vector<int64_t> data_shape = {12, 6};
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status;
// construct a col shard
std::vector<int64_t> dims_mapping = {-1, 0};
auto data_op = builder.Build<paddle::dialect::DataOp>(
"w2", data_shape, phi::DataType::FLOAT32, phi::CPUPlace());
std::vector<int64_t> local_shape = {12, 3};
auto tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, dims_mapping, partial_status);
pir::AttributeMap attr_map = {{"tensor_dist_attr", tensor_dist_attr}};
paddle::dialect::ShardTensorOp shard_op =
builder.Build<paddle::dialect::ShardTensorOp>(data_op.result(0),
attr_map);
EXPECT_TRUE(shard_op.out().type().isa<DistDenseTensorType>());
auto op_out_type = shard_op.out().type().dyn_cast<DistDenseTensorType>();
EXPECT_EQ(op_out_type.global_ddim(), phi::make_ddim(data_shape));
EXPECT_EQ(op_out_type.local_ddim(), phi::make_ddim(local_shape));
EXPECT_EQ(op_out_type.process_mesh_attr(), mesh_attr);
EXPECT_EQ(op_out_type.dims_mapping(), dims_mapping);
EXPECT_EQ(op_out_type.partial_dims().size(), (size_t)0);
EXPECT_EQ(
shard_op.attribute<OperationDistAttribute>("op_dist_attr").num_operands(),
(uint32_t)0);
EXPECT_EQ(
shard_op.attribute<OperationDistAttribute>("op_dist_attr").num_results(),
(uint32_t)1);
EXPECT_EQ(shard_op.attribute<OperationDistAttribute>("op_dist_attr")
.process_mesh_attr(),
mesh_attr);
// check reshard
std::vector<int64_t> dst_dims_mapping = {0, 1};
phi::distributed::ProcessMesh dst_process_mesh(
mesh_shape, process_ids, dim_names);
auto dst_mesh_attr = ProcessMeshAttribute::get(ctx, dst_process_mesh);
auto dst_tensor_dist_attr = TensorDistAttribute::get(
ctx, dst_mesh_attr, dst_dims_mapping, partial_status);
paddle::dialect::ReshardOp reshard_op =
builder.Build<paddle::dialect::ReshardOp>(shard_op.out(),
dst_tensor_dist_attr);
EXPECT_TRUE(reshard_op.result(0).type().isa<DistDenseTensorType>());
auto dst_op_out_type =
reshard_op.result(0).type().dyn_cast<DistDenseTensorType>();
EXPECT_EQ(dst_op_out_type.global_ddim(), phi::make_ddim(data_shape));
EXPECT_EQ(dst_op_out_type.local_ddim(), phi::make_ddim({6, 2}));
EXPECT_EQ(dst_op_out_type.process_mesh_attr(), dst_mesh_attr);
EXPECT_EQ(dst_op_out_type.dims_mapping(), dst_dims_mapping);
EXPECT_EQ(dst_op_out_type.partial_dims().size(), (size_t)0);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.num_operands(),
(uint32_t)1);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.num_results(),
(uint32_t)1);
EXPECT_EQ(reshard_op.attribute<OperationDistAttribute>("op_dist_attr")
.process_mesh_attr(),
mesh_attr);
}
TEST(mix_to_dist_pass_test, base) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<DistDialect>();
ctx->GetOrRegisterDialect<OperatorDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
std::vector<int64_t> mesh_shape = {2, 3};
std::vector<int64_t> process_ids = {0, 1, 2, 3, 4, 5};
std::vector<std::string> dim_names = {"x", "y"};
phi::distributed::ProcessMesh process_mesh(
mesh_shape, process_ids, dim_names);
auto mesh_attr = ProcessMeshAttribute::get(ctx, process_mesh);
paddle::flat_hash_map<int64_t, phi::ReduceType> partial_status;
std::vector<int64_t> x_shape = {12, 6};
std::vector<int64_t> y_shape = {6, 8};
// construct x
std::vector<int64_t> x_dims_mapping = {0, 1};
auto x_data_op = builder.Build<paddle::dialect::DataOp>(
"x", x_shape, phi::DataType::FLOAT32, phi::CPUPlace());
std::vector<int64_t> x_local_shape = {6, 2};
auto x_tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, x_dims_mapping, partial_status);
pir::AttributeMap x_attr_map = {{"tensor_dist_attr", x_tensor_dist_attr}};
// construct y
std::vector<int64_t> y_dims_mapping = {1, -1};
auto y_data_op = builder.Build<paddle::dialect::DataOp>(
"y", y_shape, phi::DataType::FLOAT32, phi::CPUPlace());
std::vector<int64_t> y_local_shape = {2, 8};
auto y_tensor_dist_attr =
TensorDistAttribute::get(ctx, mesh_attr, y_dims_mapping, partial_status);
pir::AttributeMap y_attr_map = {{"tensor_dist_attr", y_tensor_dist_attr}};
// shard_tensor op
paddle::dialect::ShardTensorOp x_shard_op =
builder.Build<paddle::dialect::ShardTensorOp>(x_data_op.result(0),
x_attr_map);
paddle::dialect::ShardTensorOp y_shard_op =
builder.Build<paddle::dialect::ShardTensorOp>(y_data_op.result(0),
y_attr_map);
EXPECT_EQ(x_shard_op.attribute<OperationDistAttribute>("op_dist_attr")
.num_results(),
(uint32_t)1);
EXPECT_EQ(y_shard_op.attribute<OperationDistAttribute>("op_dist_attr")
.num_results(),
(uint32_t)1);
}
@@ -0,0 +1,7 @@
paddle_test(ir_kernel_dialect_pass_test SRCS ir_kernel_dialect_pass_test.cc)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(ir_kernel_dialect_pass_test)
endif()
@@ -0,0 +1,157 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <sstream>
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/framework/tensor.h"
#include "paddle/fluid/framework/variable.h"
#include "paddle/fluid/framework/variable_helper.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_attribute.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_dialect.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_op.h"
#include "paddle/fluid/pir/dialect/operator/interface/op_yaml_info.h"
#include "paddle/fluid/pir/dialect/operator/ir/control_flow_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_attribute.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/fluid/platform/init.h"
#include "paddle/phi/common/data_type.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_context.h"
#include "paddle/phi/core/kernel_factory.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/meta_tensor.h"
#include "paddle/phi/infermeta/binary.h"
#include "paddle/phi/kernels/elementwise_add_kernel.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/utils.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
PD_DECLARE_KERNEL(full, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(full_int_array, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(uniform, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(add, CPU, ALL_LAYOUT);
bool simple_cmp(float a, float b) { return std::abs((a - b) / a) < 1e-5; }
TEST(dialect_attr, attr) {
// (1) Init environment.
pir::IrContext* ctx = pir::IrContext::Instance();
pir::Program program((ctx));
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto kernel_dialect =
ctx->GetOrRegisterDialect<paddle::dialect::KernelDialect>();
phi::KernelKey kernel_key(
phi::Backend::CPU, phi::DataLayout::ALL_LAYOUT, phi::DataType::FLOAT32);
auto attr = paddle::dialect::KernelAttribute::get(ctx, kernel_key);
std::stringstream ss;
kernel_dialect->PrintAttribute(attr, ss);
EXPECT_EQ(
ss.str() == "<backend:CPU|layout:Undefined(AnyLayout)|dtype:float32>",
true);
}
pir::AttributeMap CreateAttributeMap(std::vector<std::string> attribute_names,
std::vector<std::string> attributes,
std::string attr_name,
phi::KernelKey kernel_key) {
pir::IrContext* ctx = pir::IrContext::Instance();
pir::AttributeMap attr_map;
for (size_t i = 0; i < attribute_names.size(); i++) {
pir::Attribute attr_value = pir::StrAttribute::get(ctx, attributes[i]);
attr_map.insert(
std::pair<std::string, pir::Attribute>(attribute_names[i], attr_value));
}
auto attr = paddle::dialect::KernelAttribute::get(ctx, kernel_key);
attr_map.insert(std::pair<std::string, pir::Attribute>(attr_name, attr));
return attr_map;
}
TEST(kernel_dialect, legacy_op_test) {
// (1) Init environment.
pir::IrContext* ctx = pir::IrContext::Instance();
pir::Program program((ctx));
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
phi::KernelKey kernel_key(
phi::Backend::CPU, phi::DataLayout::ALL_LAYOUT, phi::DataType::FLOAT32);
pir::OpInfo kernel_op_info =
ctx->GetRegisteredOpInfo(paddle::dialect::LegacyKernelOp::name());
pir::OperationArgument argument(kernel_op_info);
argument.attributes = CreateAttributeMap({"op_name", "kernel_name"},
{"pd_op.kernel_op", "kernel_op"},
"kernel_key",
kernel_key);
pir::Operation* op = pir::Operation::Create(std::move(argument));
EXPECT_EQ("pd_op.kernel_op",
op->dyn_cast<paddle::dialect::LegacyKernelOp>().op_name());
EXPECT_EQ("kernel_op",
op->dyn_cast<paddle::dialect::LegacyKernelOp>().kernel_name());
EXPECT_EQ(kernel_key,
op->dyn_cast<paddle::dialect::LegacyKernelOp>().kernel_key());
}
TEST(kernel_dialect, cond_op_test) {
// (1) Init environment.
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
pir::Program program(ctx);
pir::Block* block = program.block();
pir::Builder builder(ctx, block);
auto full_op = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{1}, true, phi::DataType::BOOL);
auto if_op = builder.Build<paddle::dialect::IfOp>(
full_op.out(), std::vector<pir::Type>{full_op.result(0).type()});
auto& true_block = if_op.true_block();
builder.SetInsertionPointToStart(&true_block);
auto full_op_1 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{2}, true, phi::DataType::BOOL);
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_1.out()});
auto& false_block = if_op.false_block();
builder.SetInsertionPointToStart(&false_block);
auto full_op_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{3}, true, phi::DataType::BOOL);
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_2.out()});
program.Print(std::cout);
auto kernel_program = pir::PdOpLowerToKernelPass(&program);
}
+5
View File
@@ -0,0 +1,5 @@
paddle_test(layout_transformation_interface_test SRCS
layout_transformation_interface_test.cc)
if(WITH_ONNXRUNTIME AND WIN32)
copy_onnx(layout_transformation_interface_test)
endif()
@@ -0,0 +1,78 @@
// Copyright (c) 2024 PaddlePaddle 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 <gtest/gtest.h>
#include <cstdint>
#include "paddle/fluid/pir/dialect/operator/interface/layout_transformation.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/op_trait.h"
#include "paddle/pir/include/core/type.h"
TEST(layout_transformation_interface_test, operator) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder(ctx, program.block());
auto build_input_value = [&](std::vector<int64_t> shape = {2, 2}) {
auto uniform = builder.Build<paddle::dialect::UniformOp>(
shape, phi::DataType::FLOAT16, 0.0, 1.0, 2, phi::CPUPlace());
return uniform;
};
auto fused_conv = builder.Build<paddle::dialect::FusedConv2dAddActOp>(
build_input_value(std::vector<int64_t>{2, 2, 2, 2}).out(),
build_input_value(std::vector<int64_t>{2, 2, 2, 2}).out(),
build_input_value().out(),
build_input_value().out());
auto layout_transformation_iface =
fused_conv->dyn_cast<paddle::dialect::LayoutTransformationInterface>();
EXPECT_TRUE(layout_transformation_iface);
EXPECT_EQ(layout_transformation_iface.PreferLayout(fused_conv),
common::DataLayout::NCHW);
EXPECT_NO_THROW(layout_transformation_iface.RewriteByLayout(
fused_conv, common::DataLayout::NHWC));
EXPECT_EQ(layout_transformation_iface.RelevantInputs(fused_conv).size(),
fused_conv->operands().size());
EXPECT_EQ(layout_transformation_iface.RelevantOutputs(fused_conv).size(), 1u);
}
TEST(immutable_layout_trait_test, operator) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder(ctx, program.block());
auto build_input_value = [&](std::vector<int64_t> shape = {2, 2}) {
auto uniform = builder.Build<paddle::dialect::UniformOp>(
shape, phi::DataType::FLOAT32, 0.0, 1.0, 2, phi::CPUPlace());
return uniform;
};
auto out = builder.Build<pir::ShadowOutputOp>(
build_input_value(std::vector<int64_t>{2, 2, 2, 2}).out(), "test");
EXPECT_TRUE(out->HasTrait<pir::ImmutableLayoutTrait>());
auto immutable_layout_trait = out->dyn_cast<pir::ImmutableLayoutTrait>();
EXPECT_TRUE(immutable_layout_trait);
}
+20
View File
@@ -0,0 +1,20 @@
paddle_test(pass_manager_test SRCS pass_manager_test.cc DEPS common)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(pass_manager_test)
endif()
if(WITH_GPU)
file(DOWNLOAD https://paddle-ci.gz.bcebos.com/test/sd15_unet.pdmodel
${CMAKE_CURRENT_BINARY_DIR}/sd15_unet.pdmodel
EXPECTED_MD5 4b5a3b8ea5b49bfd12172847cfe5a92a)
paddle_test(transfer_layout_pass_test SRCS transfer_layout_pass_test.cc)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(transfer_layout_pass_test)
endif()
endif()
+314
View File
@@ -0,0 +1,314 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "glog/logging.h"
// NOTE(zhangbo9674): File pd_op.h is generated by op_gen.py, see details in
// paddle/fluid/pir/dialect/CMakeLists.txt.
#include "paddle/common/errors.h"
#include "paddle/fluid/pir/dialect/operator/interface/op_yaml_info.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/phi/core/enforce.h"
#include "paddle/phi/kernels/elementwise_add_kernel.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/op_base.h"
#include "paddle/pir/include/core/operation.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "test/cpp/pir/tools/macros_utils.h"
#ifndef _WIN32
class TestAnalysis1 {};
class TestAnalysis2 {};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(TestAnalysis1)
IR_DEFINE_EXPLICIT_TYPE_ID(TestAnalysis1)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(TestAnalysis2)
IR_DEFINE_EXPLICIT_TYPE_ID(TestAnalysis2)
TEST(pass_manager, PreservedAnalyses) {
pir::detail::PreservedAnalyses pa;
PADDLE_ENFORCE_EQ(pa.IsNone(),
true,
common::errors::InvalidArgument(
"Preserved analyses not exist. Expected exist."));
PADDLE_ENFORCE_EQ(pa.IsPreserved<TestAnalysis1>(),
false,
common::errors::InvalidArgument(
"Test Analysis is preserved. Expected not."));
pa.Preserve<TestAnalysis1>();
PADDLE_ENFORCE_EQ(pa.IsPreserved<TestAnalysis1>(),
true,
common::errors::InvalidArgument(
"Test Analysis not preserved. Expected preserved."));
pa.Unpreserve<TestAnalysis1>();
PADDLE_ENFORCE_EQ(pa.IsPreserved<TestAnalysis1>(),
false,
common::errors::InvalidArgument(
"Test Analysis is preserved. Expected not."));
PADDLE_ENFORCE_EQ(pa.IsPreserved<TestAnalysis2>(),
false,
common::errors::InvalidArgument(
"Test Analysis is preserved. Expected not."));
pa.Preserve<TestAnalysis1, TestAnalysis2>();
PADDLE_ENFORCE_EQ(pa.IsPreserved<TestAnalysis1>(),
true,
common::errors::InvalidArgument(
"Test Analysis not preserved. Expected preserved."));
PADDLE_ENFORCE_EQ(pa.IsPreserved<TestAnalysis2>(),
true,
common::errors::InvalidArgument(
"Test Analysis not preserved. Expected preserved."));
PADDLE_ENFORCE_EQ(
pa.IsAll(),
false,
common::errors::InvalidArgument("Test Analysis is all. Expected not."));
pa.PreserveAll();
PADDLE_ENFORCE_EQ(
pa.IsAll(),
true,
common::errors::InvalidArgument("Test Analysis not all. Expected all."));
PADDLE_ENFORCE_EQ(
pa.IsNone(),
false,
common::errors::InvalidArgument("Test Analysis is none. Expected not."));
}
#endif
class AddOp : public pir::Op<AddOp> {
public:
using Op::Op;
static const char *name() { return "test.add"; }
static constexpr const char **attributes_name = nullptr;
static constexpr uint32_t attributes_num = 0;
void VerifySig();
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type sum_type);
};
void AddOp::VerifySig() {
if (num_operands() != 2) {
PADDLE_THROW(
common::errors::Fatal("The size of inputs must be equal to 2."));
}
if (num_results() != 1) {
PADDLE_THROW(
common::errors::Fatal("The size of outputs must be equal to 1."));
}
}
void AddOp::Build(pir::Builder &,
pir::OperationArgument &argument,
pir::Value l_operand,
pir::Value r_operand,
pir::Type sum_type) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
argument.AddOutput(sum_type);
}
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(AddOp)
IR_DEFINE_EXPLICIT_TYPE_ID(AddOp)
struct CountOpAnalysis {
explicit CountOpAnalysis(pir::Operation *container_op) {
PADDLE_ENFORCE_GT(
container_op->num_regions(),
0,
common::errors::InvalidArgument(
"op must be a container with zero or multiple regions."));
LOG(INFO) << "In CountOpAnalysis, op is " << container_op->name() << "\n";
for (size_t i = 0; i < container_op->num_regions(); ++i) {
auto &region = container_op->region(i);
for (auto &block : region) {
count += block.size();
}
}
LOG(INFO) << "-- count is " << count << "\n";
}
int count = 0;
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(CountOpAnalysis)
IR_DEFINE_EXPLICIT_TYPE_ID(CountOpAnalysis)
struct NoOperationAnalysis {
int scale = 0;
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(NoOperationAnalysis)
IR_DEFINE_EXPLICIT_TYPE_ID(NoOperationAnalysis)
class TestPass : public pir::Pass {
public:
TestPass() : pir::Pass("TestPass", 1) {}
void Run(pir::Operation *op) override {
auto count_op_analysis = analysis_manager().GetAnalysis<CountOpAnalysis>();
pass_state()->preserved_analyses.Preserve<CountOpAnalysis>();
PADDLE_ENFORCE_EQ(
pass_state()->preserved_analyses.IsPreserved<CountOpAnalysis>(),
true,
common::errors::InvalidArgument(
"Count op analysis not preserved. Expected preserved."));
auto no_operation_analysis =
analysis_manager().GetAnalysis<NoOperationAnalysis>();
pass_state()->preserved_analyses.Preserve<NoOperationAnalysis>();
PADDLE_ENFORCE_EQ(
pass_state()->preserved_analyses.IsPreserved<NoOperationAnalysis>(),
true,
common::errors::InvalidArgument(
"No operation analysis not preserved. Expected preserved."));
PADDLE_ENFORCE_EQ(count_op_analysis.count,
11UL,
common::errors::InvalidArgument(
"Count op analysis mismatch. Expected 11."));
no_operation_analysis.scale = 8;
PADDLE_ENFORCE_EQ(
no_operation_analysis.scale,
8UL,
common::errors::InvalidArgument(
"Scale of no operation analysis mismatch. Expected 8."));
auto module_op = op->dyn_cast<pir::ModuleOp>();
PADDLE_ENFORCE_EQ(
module_op.operation(),
op,
common::errors::InvalidArgument("module op operation mismatch."));
PADDLE_ENFORCE_EQ(
module_op.name(),
module_op->name(),
common::errors::InvalidArgument("module op name mismatch."));
LOG(INFO) << "In " << pass_info().name << ": " << module_op->name()
<< std::endl;
pass_state()->preserved_analyses.Unpreserve<CountOpAnalysis>();
PADDLE_ENFORCE_EQ(
pass_state()->preserved_analyses.IsPreserved<CountOpAnalysis>(),
false,
common::errors::InvalidArgument(
"Count op analysis is preserved. Expected not."));
pass_state()->preserved_analyses.Unpreserve<NoOperationAnalysis>();
PADDLE_ENFORCE_EQ(
pass_state()->preserved_analyses.IsPreserved<NoOperationAnalysis>(),
false,
common::errors::InvalidArgument(
"No operation analysis is preserved. Expected not."));
}
bool CanApplyOn(pir::Operation *op) const override {
return op->isa<::pir::ModuleOp>() && op->num_regions() > 0;
}
};
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_filter_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 3, 3, 3},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_mean_op = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
paddle::dialect::FullOp full_variance_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_scale_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_bias_op = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
paddle::dialect::Conv2dOp conv2d_op =
builder.Build<paddle::dialect::Conv2dOp>(full_input_op.out(),
full_filter_op.out());
paddle::dialect::BatchNormOp batch_norm_op =
builder.Build<paddle::dialect::BatchNormOp>(conv2d_op.out(),
full_mean_op.out(),
full_variance_op.out(),
full_scale_op.out(),
full_bias_op.out(),
true,
0.9,
1e-6,
"NCHW",
false,
false);
auto transpose1_op = builder.Build<paddle::dialect::TransposeOp>(
batch_norm_op.out(), std::vector<int>{0, 2, 3, 1});
auto transpose2_op = builder.Build<paddle::dialect::TransposeOp>(
transpose1_op.out(), std::vector<int>{0, 3, 1, 2});
builder.Build<paddle::dialect::FetchOp>(transpose2_op.out(), "out", 0);
}
TEST(pass_manager, PassManager) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
EXPECT_EQ(program.block()->size(), 11u);
// (9) Test pass manager for program.
pir::PassManager pm(ctx);
pm.AddPass(std::make_unique<TestPass>());
// pm.EnableIRPrinting();
pm.EnableIRPrinting(std::make_unique<pir::PassManager::IRPrinterOption>(
[](pir::Pass *pass, pir::Operation *op) {
return pass->name() == "TestPass";
},
[](pir::Pass *pass, pir::Operation *op) {
return pass->name() == "TestPass";
},
true,
true));
// pm.EnablePassTiming(true);
PADDLE_ENFORCE_EQ(
pm.Run(&program),
true,
common::errors::InvalidArgument("Program not run. Expected run."));
}
@@ -0,0 +1,106 @@
// Copyright (c) 2024 PaddlePaddle 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 <gtest/gtest.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <memory>
#include <ostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <variant>
#include <vector>
#include "paddle/common/layout.h"
#include "paddle/fluid/framework/block_desc.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/inference/api/paddle_pass_builder.h"
#include "paddle/fluid/ir_adaptor/translator/translate.h"
#include "paddle/fluid/pir/dialect/operator/interface/layout_transformation.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/general/transfer_layout_pass.h"
#include "paddle/fluid/pir/transforms/passes.h"
#include "paddle/phi/core/framework/framework.pb.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/pass/pass_manager.h"
using ProgramDesc = paddle::framework::ProgramDesc;
ProgramDesc load_from_file(const std::string& file_name) {
std::ifstream fin(file_name, std::ios::in | std::ios::binary);
fin.seekg(0, std::ios::end);
std::string buffer(fin.tellg(), ' ');
fin.seekg(0, std::ios::beg);
fin.read(&buffer[0], buffer.size()); // NOLINT
fin.close();
return ProgramDesc(buffer);
}
TEST(transfer_layout_pass, pass_test) {
// Load Unet Program
const std::string model_name = "sd15_unet.pdmodel";
auto p = load_from_file(model_name);
EXPECT_EQ(p.Size(), 1u);
EXPECT_GT(p.Block(0).OpSize(), 0u);
// Translate to PIR Program
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
auto program = paddle::TranslateLegacyProgramToProgram(p);
pir::PassManager pass_pm(::pir::IrContext::Instance(), 3);
// Note(lyk) To avoid windows compiler error:
// paddle::kPirGpuPasses has already been declared as
// PD_INFER_DECL, but we still got LINK ERROR. The
// reason is still unclear, skip it now.
const std::vector<std::string> kCopiedPirGpuPasses{
// Functional pass
"map_op_to_another_pass",
"identity_op_clean_pass",
// Operator fusion pass
"silu_fuse_pass",
"conv2d_bn_fuse_pass",
"conv2d_add_act_fuse_pass",
"conv2d_add_fuse_pass",
"embedding_eltwise_layernorm_fuse_pass",
"fused_flash_attn_pass",
"multihead_matmul_fuse_pass",
"matmul_add_act_fuse_pass",
"fc_elementwise_layernorm_fuse_pass",
"matmul_scale_fuse_pass",
"matmul_transpose_fuse_pass",
"transpose_flatten_concat_fuse_pass",
"remove_redundant_transpose_pass"};
for (const auto& gpu_pass : kCopiedPirGpuPasses) {
pass_pm.AddPass(pir::PassRegistry::Instance().Get(gpu_pass));
}
pass_pm.Run(program.get());
pir::PassManager transfer_layout_manager(::pir::IrContext::Instance(), 4);
transfer_layout_manager.AddPass(pir::CreateTransferLayoutPass());
transfer_layout_manager.Run(program.get());
}
@@ -0,0 +1,24 @@
paddle_test(pattern_rewrite_test SRCS pattern_rewrite_test.cc)
paddle_test(drr_same_type_binding_test SRCS drr_same_type_binding_test.cc)
paddle_test(drr_fuse_linear_test SRCS drr_fuse_linear_test.cc)
paddle_test(drr_fuse_linear_param_grad_add_test SRCS
drr_fuse_linear_param_grad_add_test.cc)
if(WITH_GPU)
paddle_test(drr_attention_fuse_test SRCS drr_attention_fuse_test.cc)
endif()
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(pattern_rewrite_test)
copy_onnx(drr_same_type_binding_test)
copy_onnx(drr_fuse_linear_test)
copy_onnx(drr_fuse_linear_param_grad_add_test)
if(WITH_GPU)
copy_onnx(drr_attention_fuse_test)
endif()
endif()
@@ -0,0 +1,168 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <cstdint>
#include <vector>
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/general/constant_folding_pass.h"
#include "paddle/fluid/pir/transforms/general/dead_code_elimination_pass.h"
#include "paddle/fluid/pir/transforms/gpu/multihead_matmul_fuse_pass.h"
#include "paddle/phi/common/place.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/phi/core/kernel_registry.h"
PD_DECLARE_KERNEL(multihead_matmul, GPU, ALL_LAYOUT);
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp matmul_1_in_1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1, 300, 256},
0.9,
phi::DataType::FLOAT32,
phi::CPUPlace());
// The first path to matmul with scale (q).
paddle::dialect::FullOp matmul_1_in_2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{256, 256},
1.1,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::MatmulOp matmul_1 = builder.Build<paddle::dialect::MatmulOp>(
matmul_1_in_1.out(), matmul_1_in_2.out(), false, false);
paddle::dialect::FullOp add_1_in_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{256}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
paddle::dialect::AddOp add_1 =
builder.Build<paddle::dialect::AddOp>(matmul_1.out(), add_1_in_2.out());
paddle::dialect::ReshapeOp reshape_1 =
builder.Build<paddle::dialect::ReshapeOp>(
add_1.out(), std::vector<int64_t>{0, 0, 8, 32});
paddle::dialect::TransposeOp transpose_1 =
builder.Build<paddle::dialect::TransposeOp>(reshape_1.out(),
std::vector<int>{0, 2, 1, 3});
paddle::dialect::ScaleOp scale_op = builder.Build<paddle::dialect::ScaleOp>(
transpose_1.out(), 0.1767766922712326, 0.0, true);
// The second path to matmul (k).
paddle::dialect::FullOp matmul_2_in_2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{256, 256},
1.1,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::MatmulOp matmul_2 = builder.Build<paddle::dialect::MatmulOp>(
matmul_1_in_1.out(), matmul_2_in_2.out(), false, false);
paddle::dialect::FullOp add_2_in_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{256}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
paddle::dialect::AddOp add_op2 =
builder.Build<paddle::dialect::AddOp>(matmul_2.out(), add_2_in_2.out());
paddle::dialect::ReshapeOp reshape_2 =
builder.Build<paddle::dialect::ReshapeOp>(
add_op2.out(), std::vector<int64_t>{0, 0, 8, 32});
paddle::dialect::TransposeOp transpose_2 =
builder.Build<paddle::dialect::TransposeOp>(reshape_2.out(),
std::vector<int>{0, 2, 1, 3});
// The third path to matmul (v).
paddle::dialect::FullOp matmul_3_in_2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{256, 256},
1.1,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::MatmulOp matmul_3 = builder.Build<paddle::dialect::MatmulOp>(
matmul_1_in_1.out(), matmul_3_in_2.out(), false, false);
paddle::dialect::FullOp add_3_in_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{256}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
paddle::dialect::AddOp add_3 =
builder.Build<paddle::dialect::AddOp>(matmul_3.out(), add_3_in_2.out());
paddle::dialect::ReshapeOp reshape_3 =
builder.Build<paddle::dialect::ReshapeOp>(
add_3.out(), std::vector<int64_t>{0, 0, 8, 32});
paddle::dialect::TransposeOp transpose_3 =
builder.Build<paddle::dialect::TransposeOp>(reshape_3.out(),
std::vector<int>{0, 2, 1, 3});
// softmax(qk)v
paddle::dialect::MatmulOp matmul_4 = builder.Build<paddle::dialect::MatmulOp>(
scale_op.out(), transpose_2.out(), false, true);
paddle::dialect::FullOp add_4_in_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{1, 8, 300, 300},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::AddOp add_4 =
builder.Build<paddle::dialect::AddOp>(matmul_4.out(), add_4_in_2.out());
paddle::dialect::SoftmaxOp softmax_op =
builder.Build<paddle::dialect::SoftmaxOp>(add_4.out(), -1);
paddle::dialect::MatmulOp matmul_5 = builder.Build<paddle::dialect::MatmulOp>(
softmax_op.out(), transpose_3.out(), false, false);
paddle::dialect::TransposeOp transpose_4 =
builder.Build<paddle::dialect::TransposeOp>(matmul_5.out(),
std::vector<int>{0, 2, 1, 3});
paddle::dialect::ReshapeOp reshape_4 =
builder.Build<paddle::dialect::ReshapeOp>(
transpose_4.out(), std::vector<int64_t>{0, 0, 256});
builder.Build<paddle::dialect::FetchOp>(reshape_4.out(), "out", 0);
}
TEST(DrrTest, AttentionFuse) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
EXPECT_EQ(program.block()->size(), 33u);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateMultiHeadMatmulFusePass());
std::unique_ptr<pir::Pass> constant_folding_pass =
pir::CreateConstantFoldingPass();
constant_folding_pass->Set(pir::Pass::kPlaceAttr,
new phi::Place{phi::GPUPlace{}});
constant_folding_pass->Set(pir::Pass::kParamScopeAttr,
new paddle::framework::Scope{});
pm.AddPass(std::move(constant_folding_pass));
pm.AddPass(pir::CreateDeadCodeEliminationPass());
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Unavailable("pm fail to run program"));
EXPECT_EQ(program.block()->size(), 2u);
}
@@ -0,0 +1,301 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/gpu/fused_linear_param_grad_add_pass.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
void BuildProgram0(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_weight_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_bias_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32}, 1.0);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(full_input_op1.out(),
full_weight_op1.out());
paddle::dialect::AddOp add_op1 = builder.Build<paddle::dialect::AddOp>(
matmul_op1.out(), full_bias_op1.out());
paddle::dialect::FullOp full_d_weight_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_d_out_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::AddGradOp add_grad_op1 =
builder.Build<paddle::dialect::AddGradOp>(
matmul_op1.out(), full_bias_op1.out(), full_d_out_op1.out());
paddle::dialect::MatmulGradOp matmul_grad_op1 =
builder.Build<paddle::dialect::MatmulGradOp>(
full_input_op1.out(), full_weight_op1.out(), add_grad_op1.x_grad());
paddle::dialect::Add_Op add__op1 = builder.Build<paddle::dialect::Add_Op>(
full_d_weight_op1.out(), matmul_grad_op1.y_grad());
builder.Build<paddle::dialect::FetchOp>(add_op1.out(), "out", 0);
builder.Build<paddle::dialect::FetchOp>(add_grad_op1.y_grad(), "dbias", 1);
builder.Build<paddle::dialect::FetchOp>(add__op1.out(), "dweight", 2);
}
void BuildProgram1(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_weight_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(full_input_op1.out(),
full_weight_op1.out());
paddle::dialect::FullOp full_d_weight_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_d_out_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::MatmulGradOp matmul_grad_op1 =
builder.Build<paddle::dialect::MatmulGradOp>(
full_input_op1.out(), full_weight_op1.out(), full_d_out_op1.out());
paddle::dialect::Add_Op add__op1 = builder.Build<paddle::dialect::Add_Op>(
full_d_weight_op1.out(), matmul_grad_op1.y_grad());
builder.Build<paddle::dialect::FetchOp>(matmul_op1.out(), "out", 0);
builder.Build<paddle::dialect::FetchOp>(add__op1.out(), "dweight", 1);
}
void BuildProgram2(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_weight_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(full_input_op1.out(),
full_weight_op1.out());
paddle::dialect::FullOp full_d_weight_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_d_out_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::MatmulOp matmul_op2 =
builder.Build<paddle::dialect::MatmulOp>(
full_input_op1.out(), full_d_out_op1.out(), true, false);
paddle::dialect::Add_Op add__op1 = builder.Build<paddle::dialect::Add_Op>(
full_d_weight_op1.out(), matmul_op2.out());
builder.Build<paddle::dialect::FetchOp>(matmul_op1.out(), "out", 0);
builder.Build<paddle::dialect::FetchOp>(add__op1.out(), "dweight", 1);
}
void BuildProgram3(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_weight_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_bias_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32}, 1.0);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(full_input_op1.out(),
full_weight_op1.out());
paddle::dialect::AddOp add_op1 = builder.Build<paddle::dialect::AddOp>(
matmul_op1.out(), full_bias_op1.out());
paddle::dialect::FullOp full_d_weight_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::FullOp full_d_out_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 1.5);
paddle::dialect::AddGradOp add_grad_op1 =
builder.Build<paddle::dialect::AddGradOp>(
matmul_op1.out(), full_bias_op1.out(), full_d_out_op1.out());
paddle::dialect::MatmulOp matmul_op2 =
builder.Build<paddle::dialect::MatmulOp>(
add_grad_op1.x_grad(), full_weight_op1.out(), false, true);
paddle::dialect::MatmulOp matmul_op3 =
builder.Build<paddle::dialect::MatmulOp>(
full_input_op1.out(), add_grad_op1.x_grad(), true, false);
paddle::dialect::Add_Op add__op1 = builder.Build<paddle::dialect::Add_Op>(
full_d_weight_op1.out(), matmul_op3.out());
builder.Build<paddle::dialect::FetchOp>(add_op1.out(), "out", 0);
builder.Build<paddle::dialect::FetchOp>(add_grad_op1.y_grad(), "dbias", 1);
builder.Build<paddle::dialect::FetchOp>(add__op1.out(), "dweight", 2);
builder.Build<paddle::dialect::FetchOp>(matmul_op2.out(), "dx", 3);
}
void BuildProgram4(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp input_x = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{2, 2048, 5120}, 1.5, phi::DataType::FLOAT16);
paddle::dialect::FullOp input_dy = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{2, 2048, 6912}, 2.5, phi::DataType::FLOAT16);
paddle::dialect::FullOp input_weight_grad =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{5120, 6912}, 1.0, phi::DataType::FLOAT16);
paddle::dialect::ReshapeOp reshape_x =
builder.Build<paddle::dialect::ReshapeOp>(
input_x.out(), std::vector<int64_t>{4096, 5120});
paddle::dialect::ReshapeOp reshape_dy =
builder.Build<paddle::dialect::ReshapeOp>(
input_dy.out(), std::vector<int64_t>{4096, 6912});
paddle::dialect::MatmulOp matmul_op =
builder.Build<paddle::dialect::MatmulOp>(
reshape_x.out(), reshape_dy.out(), true, false);
paddle::dialect::ReshapeOp output_dw =
builder.Build<paddle::dialect::ReshapeOp>(
matmul_op.out(), std::vector<int64_t>{5120, 6912});
paddle::dialect::Add_Op add__op1 = builder.Build<paddle::dialect::Add_Op>(
input_weight_grad.out(), output_dw.out());
builder.Build<paddle::dialect::FetchOp>(add__op1.out(), "dw", 0);
}
bool verify_pass(const pir::Program &program) {
for (auto &op : *(program.block())) {
if (op.name() == paddle::dialect::FusedLinearParamGradAddOp::name()) {
return true;
}
}
return false;
}
TEST(DrrTest, FusedLinearParamGradAdd0) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram0(builder);
EXPECT_EQ(program.block()->size(), 13u);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateFusedLinearParamGradAddPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::InvalidArgument(
"Required pm.Run(&program) should be true"));
EXPECT_EQ(verify_pass(program), true);
}
TEST(DrrTest, FusedLinearParamGradAdd1) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram1(builder);
EXPECT_EQ(program.block()->size(), 9u);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateFusedLinearParamGradAddPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::InvalidArgument(
"Required pm.Run(&program) should be true"));
EXPECT_EQ(verify_pass(program), true);
}
TEST(DrrTest, FusedLinearParamGradAdd2) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram2(builder);
EXPECT_EQ(program.block()->size(), 9u);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateFusedLinearParamGradAddPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::InvalidArgument(
"Required pm.Run(&program) should be true"));
EXPECT_EQ(verify_pass(program), true);
}
TEST(DrrTest, FusedLinearParamGradAdd3) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram3(builder);
EXPECT_EQ(program.block()->size(), 15u);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateFusedLinearParamGradAddPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::InvalidArgument(
"Required pm.Run(&program) should be true"));
EXPECT_EQ(verify_pass(program), true);
}
TEST(DrrTest, FusedMatmulReshapeMatmulAddPattern) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram4(builder);
EXPECT_EQ(program.block()->size(), 12u);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateFusedLinearParamGradAddPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::InvalidArgument(
"Required pm.Run(&program) should be true"));
EXPECT_EQ(verify_pass(program), true);
EXPECT_EQ(program.block()->size(), 5u);
}
@@ -0,0 +1,152 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/gpu/fused_gemm_epilogue_pass.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op1 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{1, 512, 64}, 1.5, phi::DataType::FLOAT16);
// linear 1
paddle::dialect::FullOp full_weight_op1 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64, 64}, 1.5, phi::DataType::FLOAT16);
paddle::dialect::FullOp full_bias_op1 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64}, 1.0, phi::DataType::FLOAT16);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(full_input_op1.out(),
full_weight_op1.out());
paddle::dialect::AddOp add_op1 = builder.Build<paddle::dialect::AddOp>(
matmul_op1.out(), full_bias_op1.out());
// linear 2
paddle::dialect::FullOp full_weight_op2 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64, 128}, 1.5, phi::DataType::FLOAT16);
paddle::dialect::FullOp full_bias_op2 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{128}, 1.0, phi::DataType::FLOAT16);
paddle::dialect::MatmulOp matmul_op2 =
builder.Build<paddle::dialect::MatmulOp>(add_op1.out(),
full_weight_op2.out());
paddle::dialect::AddOp add_op2 = builder.Build<paddle::dialect::AddOp>(
matmul_op2.out(), full_bias_op2.out());
paddle::dialect::ReluOp relu_op =
builder.Build<paddle::dialect::ReluOp>(add_op2.out());
// linear 3
paddle::dialect::FullOp full_weight_op3 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{128, 64}, 1.5, phi::DataType::FLOAT16);
paddle::dialect::FullOp full_bias_op3 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64}, 1.0, phi::DataType::FLOAT16);
paddle::dialect::MatmulOp matmul_op3 =
builder.Build<paddle::dialect::MatmulOp>(relu_op.out(),
full_weight_op3.out());
paddle::dialect::AddOp add_op3 = builder.Build<paddle::dialect::AddOp>(
matmul_op3.out(), full_bias_op3.out());
paddle::dialect::GeluOp gelu_op1 =
builder.Build<paddle::dialect::GeluOp>(add_op3.out());
// linear 4
paddle::dialect::FullOp full_weight_op4 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64, 64}, 1.5, phi::DataType::FLOAT16);
paddle::dialect::FullOp full_bias_op4 =
builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64}, 1.0, phi::DataType::FLOAT16);
paddle::dialect::MatmulOp matmul_op4 =
builder.Build<paddle::dialect::MatmulOp>(gelu_op1.out(),
full_weight_op4.out());
paddle::dialect::AddOp add_op4 = builder.Build<paddle::dialect::AddOp>(
matmul_op4.out(), full_bias_op4.out());
paddle::dialect::GeluOp gelu_op2 =
builder.Build<paddle::dialect::GeluOp>(add_op4.out());
// backward
paddle::dialect::FullOp full_grad_op = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{1, 512, 64}, 1.0, phi::DataType::FLOAT16);
paddle::dialect::GeluGradOp gelu_op2_grad =
builder.Build<paddle::dialect::GeluGradOp>(
add_op4.out(), full_grad_op.out(), false);
// backward linear 4
paddle::dialect::AddGradOp add_op4_grad =
builder.Build<paddle::dialect::AddGradOp>(
matmul_op4.out(), full_bias_op4.out(), gelu_op2_grad.x_grad());
paddle::dialect::MatmulGradOp matmul_op4_grad =
builder.Build<paddle::dialect::MatmulGradOp>(
gelu_op1.out(), full_weight_op4.out(), add_op4_grad.x_grad());
paddle::dialect::GeluGradOp gelu_op1_grad =
builder.Build<paddle::dialect::GeluGradOp>(
add_op3.out(), matmul_op4_grad.x_grad(), false);
// backward linear 3
paddle::dialect::AddGradOp add_op3_grad =
builder.Build<paddle::dialect::AddGradOp>(
matmul_op3.out(), full_bias_op3.out(), gelu_op1_grad.x_grad());
paddle::dialect::MatmulGradOp matmul_op3_grad =
builder.Build<paddle::dialect::MatmulGradOp>(
relu_op.out(), full_weight_op3.out(), add_op3_grad.x_grad());
paddle::dialect::ReluGradOp relu_op_grad =
builder.Build<paddle::dialect::ReluGradOp>(relu_op.out(),
matmul_op3_grad.x_grad());
// backward linear 2
paddle::dialect::AddGradOp add_op2_grad =
builder.Build<paddle::dialect::AddGradOp>(
matmul_op2.out(), full_bias_op2.out(), relu_op_grad.x_grad());
paddle::dialect::MatmulGradOp matmul_op2_grad =
builder.Build<paddle::dialect::MatmulGradOp>(
add_op1.out(), full_weight_op2.out(), add_op2_grad.x_grad());
// backward linear 1
paddle::dialect::AddGradOp add_op1_grad =
builder.Build<paddle::dialect::AddGradOp>(
matmul_op1.out(), full_bias_op1.out(), matmul_op2_grad.x_grad());
paddle::dialect::MatmulGradOp matmul_op1_grad =
builder.Build<paddle::dialect::MatmulGradOp>(
full_input_op1.out(), full_weight_op1.out(), add_op1_grad.x_grad());
builder.Build<paddle::dialect::FetchOp>(gelu_op2.out(), "out", 0);
builder.Build<paddle::dialect::FetchOp>(matmul_op1_grad.x_grad(), "dx", 1);
}
TEST(DrrTest, FusedLinear) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
EXPECT_EQ(program.block()->size(), 34u);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateFusedGemmEpiloguePass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Unavailable("pm fail to run program"));
EXPECT_EQ(program.block()->size(), 22u);
}
@@ -0,0 +1,324 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/drr/include/drr_pattern_base.h"
#include "paddle/fluid/pir/transforms/general/dead_code_elimination_pass.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
/* Source pattern:
input1
/ | \ \ \
/ | \ \ \
full / | | \ \ full_tmp
/ | transpose1 | trans2 trans3 \ / |
/ | / | | | | \ / |
softmax1 | / | | | | \ / |
\ | / softmax2 | | | add1 |
\ | / \ | \ / | |
layernorm matmul2 matmul1 \ |
/ | \ | | \ |
/ | \ \ / \ |
/ | \ matmul3 add2
| | | / | \ |
| | | / | \ |
| | | / | \ |
| | | trans4 trans5 trans6 |
| | | | | | |
| | | relu1 softmax3 softmax4 relu2
| | | | | | |
output0 output1 output2 output3 output4 output5 output6
*/
// This class is for test cases of the same type of OP.
// (without considering the computational logic between OPs,
// only focusing on the process of matching and replacing)
class SameTypeBindingTestPattern : public paddle::drr::DrrPatternBase {
public:
std::string name() const override { return "SameTypeBindingTestPattern"; }
void operator()(paddle::drr::DrrPatternContext *ctx) const override {
paddle::drr::SourcePattern src = ctx->SourcePattern();
// path 1
const auto &transpose_1 =
src.Op("pd_op.transpose", {{"perm", src.Attr("perm_1")}});
src.Tensor("transpose_1_out") = transpose_1(src.Tensor("input_1"));
const auto &softmax_2 =
src.Op("pd_op.softmax", {{"axis", src.Attr("softmax_2_axis")}});
src.Tensor("softmax_2_out") = softmax_2(src.Tensor("transpose_1_out"));
const auto &matmul_2 =
src.Op("pd_op.matmul",
{{"transpose_x", src.Attr("matmul_2_transpose_x")},
{"transpose_y", src.Attr("matmul_2_transpose_y")}});
src.Tensor("matmul_2_out") =
matmul_2(src.Tensor("softmax_2_out"), src.Tensor("input_1"));
// path 2
const auto &full_1 = src.Op("pd_op.full",
{{"shape", src.Attr("shape_1")},
{"value", src.Attr("value_1")},
{"dtype", src.Attr("dtype_1")},
{"place", src.Attr("place_1")}});
src.Tensor("full_1_out") = full_1();
const auto &softmax_1 =
src.Op("pd_op.softmax", {{"axis", src.Attr("softmax_1_axis")}});
src.Tensor("softmax_1_out") = softmax_1(src.Tensor("full_1_out"));
const auto &layernorm_1 =
src.Op("pd_op.layer_norm",
{{"epsilon", src.Attr("layernorm_epsilon")},
{"begin_norm_axis", src.Attr("layernorm_begin_norm_axis")}});
layernorm_1({&src.Tensor("transpose_1_out"),
&src.Tensor("full_1_out"),
&src.Tensor("softmax_1_out")},
{&src.Tensor("output0"),
&src.Tensor("output1"),
&src.Tensor("output2")});
// path 3
const auto &transpose_2 =
src.Op("pd_op.transpose", {{"perm", src.Attr("perm_2")}});
const auto &transpose_3 =
src.Op("pd_op.transpose", {{"perm", src.Attr("perm_3")}});
const auto &matmul_1 =
src.Op("pd_op.matmul",
{{"transpose_x", src.Attr("matmul_1_transpose_x")},
{"transpose_y", src.Attr("matmul_1_transpose_y")}});
src.Tensor("matmul_1_out") = matmul_1(transpose_2(src.Tensor("input_1")),
transpose_3(src.Tensor("input_1")));
const auto &matmul_3 =
src.Op("pd_op.matmul",
{{"transpose_x", src.Attr("matmul_3_transpose_x")},
{"transpose_y", src.Attr("matmul_3_transpose_y")}});
src.Tensor("matmul_3_out") =
matmul_3(src.Tensor("matmul_2_out"), src.Tensor("matmul_1_out"));
const auto &transpose_4 =
src.Op("pd_op.transpose", {{"perm", src.Attr("perm_4")}});
const auto &transpose_5 =
src.Op("pd_op.transpose", {{"perm", src.Attr("perm_5")}});
const auto &transpose_6 =
src.Op("pd_op.transpose", {{"perm", src.Attr("perm_6")}});
const auto &relu_1 = src.Op("pd_op.relu");
const auto &softmax_3 =
src.Op("pd_op.softmax", {{"axis", src.Attr("softmax_3_axis")}});
const auto &softmax_4 =
src.Op("pd_op.softmax", {{"axis", src.Attr("softmax_4_axis")}});
src.Tensor("output3") = relu_1(transpose_4(src.Tensor("matmul_3_out")));
src.Tensor("output4") = softmax_3(transpose_5(src.Tensor("matmul_3_out")));
src.Tensor("output5") = softmax_4(transpose_6(src.Tensor("matmul_3_out")));
// path 4
const auto &full_tmp = src.Op("pd_op.full",
{{"shape", src.Attr("shape_tmp")},
{"value", src.Attr("value_tmp")},
{"dtype", src.Attr("dtype_tmp")},
{"place", src.Attr("place_tmp")}});
src.Tensor("full_tmp_out") = full_tmp();
const auto &add_1 = src.Op("pd_op.add");
src.Tensor("add_1_out") =
add_1(src.Tensor("input_1"), src.Tensor("full_tmp_out"));
const auto &add_2 = src.Op("pd_op.add");
src.Tensor("add_2_out") =
add_2(src.Tensor("add_1_out"), src.Tensor("full_tmp_out"));
const auto &relu_2 = src.Op("pd_op.relu");
src.Tensor("output6") = relu_2(src.Tensor("add_2_out"));
paddle::drr::ResultPattern res = src.ResultPattern();
const auto &transpose_7 =
res.Op("pd_op.transpose", {{"perm", src.Attr("perm_4")}});
res.Tensor("output0") = transpose_7(res.Tensor("input_1"));
const auto &transpose_8 =
res.Op("pd_op.transpose", {{"perm", src.Attr("perm_5")}});
res.Tensor("output1") = transpose_8(res.Tensor("input_1"));
const auto &full_2 = res.Op("pd_op.full",
{{"shape", src.Attr("shape_tmp")},
{"value", src.Attr("value_tmp")},
{"dtype", src.Attr("dtype_tmp")},
{"place", src.Attr("place_tmp")}});
const auto &full_3 = res.Op("pd_op.full",
{{"shape", src.Attr("shape_tmp")},
{"value", src.Attr("value_tmp")},
{"dtype", src.Attr("dtype_tmp")},
{"place", src.Attr("place_tmp")}});
const auto &full_4 = res.Op("pd_op.full",
{{"shape", src.Attr("shape_tmp")},
{"value", src.Attr("value_tmp")},
{"dtype", src.Attr("dtype_tmp")},
{"place", src.Attr("place_tmp")}});
const auto &full_5 = res.Op("pd_op.full",
{{"shape", src.Attr("shape_tmp")},
{"value", src.Attr("value_tmp")},
{"dtype", src.Attr("dtype_tmp")},
{"place", src.Attr("place_tmp")}});
const auto &full_6 = res.Op("pd_op.full",
{{"shape", src.Attr("shape_tmp")},
{"value", src.Attr("value_tmp")},
{"dtype", src.Attr("dtype_tmp")},
{"place", src.Attr("place_tmp")}});
res.Tensor("output2") = full_2();
res.Tensor("output3") = full_3();
res.Tensor("output4") = full_4();
res.Tensor("output5") = full_5();
res.Tensor("output6") = full_6();
}
};
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
// path 1
paddle::dialect::TransposeOp transpose_op1 =
builder.Build<paddle::dialect::TransposeOp>(full_input_op1.out(),
std::vector<int>{0, 1, 2});
paddle::dialect::SoftmaxOp softmax_op2 =
builder.Build<paddle::dialect::SoftmaxOp>(transpose_op1.out(), -1);
paddle::dialect::TransposeOp matmul_rhs_trans =
builder.Build<paddle::dialect::TransposeOp>(full_input_op1.out(),
std::vector<int>{0, 2, 1});
paddle::dialect::MatmulOp matmul_op2 =
builder.Build<paddle::dialect::MatmulOp>(softmax_op2.out(),
matmul_rhs_trans.out());
// path 2
paddle::dialect::FullOp full_op_scale =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::SoftmaxOp softmax_op_bias =
builder.Build<paddle::dialect::SoftmaxOp>(full_op_scale.out(), -1);
paddle::dialect::LayerNormOp layernorm_op1 =
builder.Build<paddle::dialect::LayerNormOp>(
transpose_op1.out(), full_op_scale.out(), softmax_op_bias.out());
// path 3
paddle::dialect::TransposeOp transpose_op2 =
builder.Build<paddle::dialect::TransposeOp>(full_input_op1.out(),
std::vector<int>{0, 1, 2});
paddle::dialect::TransposeOp transpose_op3 =
builder.Build<paddle::dialect::TransposeOp>(full_input_op1.out(),
std::vector<int>{0, 2, 1});
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(transpose_op2.out(),
transpose_op3.out());
paddle::dialect::MatmulOp matmul_op3 =
builder.Build<paddle::dialect::MatmulOp>(matmul_op2.out(),
matmul_op1.out());
paddle::dialect::TransposeOp transpose_op4 =
builder.Build<paddle::dialect::TransposeOp>(matmul_op3.out(),
std::vector<int>{0, 1, 2});
paddle::dialect::ReluOp relu_op1 =
builder.Build<paddle::dialect::ReluOp>(transpose_op4.out());
paddle::dialect::TransposeOp transpose_op5 =
builder.Build<paddle::dialect::TransposeOp>(matmul_op3.out(),
std::vector<int>{0, 1, 2});
paddle::dialect::SoftmaxOp softmax_op3 =
builder.Build<paddle::dialect::SoftmaxOp>(transpose_op5.out(), -1);
paddle::dialect::TransposeOp transpose_op6 =
builder.Build<paddle::dialect::TransposeOp>(matmul_op3.out(),
std::vector<int>{0, 1, 2});
paddle::dialect::SoftmaxOp softmax_op4 =
builder.Build<paddle::dialect::SoftmaxOp>(transpose_op6.out(), -1);
// path 4
paddle::dialect::FullOp full_input_op2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::AddOp add_op1 = builder.Build<paddle::dialect::AddOp>(
full_input_op1.out(), full_input_op2.out());
paddle::dialect::AddOp add_op2 = builder.Build<paddle::dialect::AddOp>(
add_op1.out(), full_input_op2.out());
paddle::dialect::ReluOp relu_op2 =
builder.Build<paddle::dialect::ReluOp>(add_op2.out());
// tail
paddle::dialect::AddOp safe_op1 = builder.Build<paddle::dialect::AddOp>(
layernorm_op1.variance(), layernorm_op1.mean());
paddle::dialect::MatmulOp matmul_op5 =
builder.Build<paddle::dialect::MatmulOp>(relu_op1.out(),
softmax_op3.out());
paddle::dialect::MatmulOp matmul_op6 =
builder.Build<paddle::dialect::MatmulOp>(softmax_op4.out(),
relu_op2.out());
builder.Build<paddle::dialect::FetchOp>(safe_op1.out(), "out1", 0);
builder.Build<paddle::dialect::FetchOp>(matmul_op5.out(), "out2", 1);
builder.Build<paddle::dialect::FetchOp>(matmul_op6.out(), "out3", 2);
}
class DrrPatternRewritePass : public pir::PatternRewritePass {
public:
DrrPatternRewritePass()
: pir::PatternRewritePass("drr_pattern_rewrite_pass", 1) {}
pir::RewritePatternSet InitializePatterns(pir::IrContext *context) override {
pir::RewritePatternSet ps(context);
ps.Add(paddle::drr::Create<SameTypeBindingTestPattern>(context));
return ps;
}
};
TEST(DrrTest, drr_demo) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
EXPECT_EQ(program.block()->size(), 28u);
pir::PassManager pm(ctx);
pm.AddPass(std::make_unique<DrrPatternRewritePass>());
pm.AddPass(pir::CreateDeadCodeEliminationPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Unavailable("pm fail to run program"));
EXPECT_EQ(program.block()->size(), 28u);
}
@@ -0,0 +1,644 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <cstdint>
#include <iostream>
#include <memory>
#include <numeric>
#include <sstream>
#include <vector>
#include "paddle/common/enforce.h"
#include "paddle/common/errors.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_attribute.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/general/constant_folding_pass.h"
#include "paddle/fluid/pir/transforms/general/dead_code_elimination_pass.h"
#include "paddle/fluid/pir/transforms/gpu/conv2d_add_act_fuse_pass.h"
#include "paddle/fluid/pir/transforms/gpu/conv2d_add_fuse_pass.h"
#include "paddle/fluid/pir/transforms/gpu/conv2d_bn_fuse_pass.h"
#include "paddle/fluid/pir/utils/general_functions.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/cast_utils.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/op_info.h"
#include "paddle/pir/include/core/parameter.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/value.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/frozen_rewrite_pattern_set.h"
#include "paddle/pir/include/pattern_rewrite/pattern_applicator.h"
#include "paddle/pir/include/pattern_rewrite/pattern_match.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
#include "paddle/common/ddim.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_registry.h"
#include "test/cpp/pir/tools/macros_utils.h"
PD_DECLARE_KERNEL(full, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(add, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(sqrt, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(divide, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(multiply, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(subtract, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(full_int_array, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(reshape, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(fetch, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(conv2d, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(transpose, CPU, ALL_LAYOUT);
PD_DECLARE_KERNEL(cummax, CPU, ALL_LAYOUT);
// Define op1.
class Operation1 : public pir::Op<Operation1> {
public:
using Op::Op;
static const char *name() { return "test.Operation1"; }
static constexpr uint32_t attributes_num = 2;
static const char *attributes_name[attributes_num]; // NOLINT
void VerifySig();
static void InferShape() { VLOG(2) << "This is op2's InferShape interface."; }
};
void Operation1::VerifySig() {
auto &attributes = this->attributes();
if (attributes.count("op2_attr1") == 0 ||
(!attributes.at("op2_attr1").isa<pir::StrAttribute>())) {
PADDLE_THROW(common::errors::InvalidArgument(
"Type of attribute: parameter_name is not right."));
}
if (attributes.count("op2_attr2") == 0 ||
(!attributes.at("op2_attr2").isa<pir::StrAttribute>())) {
PADDLE_THROW(common::errors::InvalidArgument(
"Type of attribute: parameter_name is not right."));
}
}
const char *Operation1::attributes_name[attributes_num] = { // NOLINT
"op2_attr1",
"op2_attr2"};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(Operation1)
IR_DEFINE_EXPLICIT_TYPE_ID(Operation1)
// Define a dialect, op1 and op2 will be registered by this dialect.
class TestDialect : public pir::Dialect {
public:
explicit TestDialect(pir::IrContext *context)
: pir::Dialect(name(), context, pir::TypeId::get<TestDialect>()) {
initialize();
}
static const char *name() { return "test"; }
private:
void initialize() { RegisterOps<Operation1>(); }
};
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(TestDialect)
IR_DEFINE_EXPLICIT_TYPE_ID(TestDialect)
// TODO(wilber): Add logical when ir support erase, replace or update.
class TestPatternRewrite : public pir::OpRewritePattern<Operation1> {
public:
using pir::OpRewritePattern<Operation1>::OpRewritePattern;
void Rewrite(Operation1 op, pir::PatternRewriter &rewriter) const override {}
bool Match(Operation1 op) const override { return false; }
};
class TestPatternRewrite2 : public pir::OpRewritePattern<Operation1> {
public:
using pir::OpRewritePattern<Operation1>::OpRewritePattern;
bool MatchAndRewrite(
Operation1 op,
pir::PatternRewriter &rewriter) const override { // NOLINT
return false;
}
};
TEST(PatternRewrite, PatternBenefit) {
pir::PatternBenefit benefit1(1);
EXPECT_EQ(benefit1.benefit(), 1U);
pir::PatternBenefit benefit2(2);
EXPECT_EQ(benefit2.benefit(), 2U);
EXPECT_TRUE(benefit2 > benefit1);
EXPECT_TRUE(benefit2 >= benefit1);
EXPECT_TRUE(benefit1 < benefit2);
EXPECT_TRUE(benefit1 <= benefit2);
EXPECT_TRUE(benefit1 != benefit2);
pir::PatternBenefit benefit3(2);
EXPECT_TRUE(benefit2 == benefit3);
}
TEST(RewritePattern, RewritePatternSet) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
auto *test_dialect = ctx->GetOrRegisterDialect<TestDialect>();
test_dialect->RegisterOp<Operation1>();
pir::RewritePatternSet ps(ctx);
ps.Add<TestPatternRewrite>(ctx, 1);
EXPECT_EQ(ps.native_patterns().size(), 1U);
EXPECT_TRUE(ps.native_patterns().back()->debug_labels().empty());
EXPECT_EQ(ps.native_patterns().back()->benefit(), 1U);
ps.AddWithLabel<TestPatternRewrite2>({"TestPatternRewrite2"}, ctx, 2);
EXPECT_EQ(ps.native_patterns().size(), 2U);
EXPECT_EQ(ps.native_patterns().back()->debug_labels()[0],
"TestPatternRewrite2");
EXPECT_EQ(ps.native_patterns().back()->benefit(), 2U);
ps.Clear();
ps.Add<TestPatternRewrite, TestPatternRewrite2>(ctx, 2);
EXPECT_EQ(ps.native_patterns().size(), 2U);
EXPECT_EQ(ps.native_patterns()[0]->benefit(), 2U);
EXPECT_EQ(ps.native_patterns()[1]->benefit(), 2U);
}
// TODO(wilber): Add actual case.
// TEST(PatternRewrite, PatternApplicator) {
// pir::IrContext *ctx = pir::IrContext::Instance();
// ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
// auto *test_dialect = ctx->GetOrRegisterDialect<TestDialect>();
// test_dialect->RegisterOp<Operation1>();
// pir::RewritePatternSet ps(ctx);
// ps.Add<TestPatternRewrite, TestPatternRewrite2>(ctx, 2);
// pir::FrozenRewritePatternSet frozen_set(std::move(ps));
// pir::PatternApplicator applicator(frozen_set);
// applicator.ApplyDefaultCostModel();
// }
// // TODO(wilber): Add actual case.
TEST(PatternRewrite, FrozenRewritePatternSet) {
pir::FrozenRewritePatternSet frozen_set;
EXPECT_TRUE(frozen_set.match_any_op_native_patterns().empty());
EXPECT_TRUE(frozen_set.op_specific_native_patterns().empty());
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
auto *test_dialect = ctx->GetOrRegisterDialect<TestDialect>();
test_dialect->RegisterOp<Operation1>();
pir::RewritePatternSet ps(ctx);
ps.Add<TestPatternRewrite, TestPatternRewrite2>(ctx, 2);
pir::FrozenRewritePatternSet frozen_set2(std::move(ps));
EXPECT_TRUE(frozen_set2.match_any_op_native_patterns().empty());
const auto &pattern_maps = frozen_set2.op_specific_native_patterns();
EXPECT_EQ(pattern_maps.size(), 1U);
EXPECT_EQ(pattern_maps.at(ctx->GetRegisteredOpInfo("test.Operation1")).size(),
2U);
}
class RedundantTransposeFusePattern
: public pir::OpRewritePattern<paddle::dialect::TransposeOp> {
public:
using pir::OpRewritePattern<paddle::dialect::TransposeOp>::OpRewritePattern;
bool MatchAndRewrite(paddle::dialect::TransposeOp op,
pir::PatternRewriter &rewriter) const override {
auto prev_op = pir::GetDefiningOpForInput(op, 0);
std::vector<int> axis_last = GetAxis(op);
auto prev_trans_op = prev_op->dyn_cast<paddle::dialect::TransposeOp>();
if (prev_trans_op) {
std::vector<int> axis_first = GetAxis(prev_trans_op);
PADDLE_ENFORCE_EQ(axis_first.size(),
axis_last.size(),
common::errors::InvalidArgument(
"transpose op's perm rank should be same."));
auto new_perm = GetPerm(axis_first, axis_last);
rewriter.set_insertion_point(op);
auto new_transpose_op = rewriter.Build<paddle::dialect::TransposeOp>(
pir::GetDefiningOpForInput(prev_trans_op, 0)->result(0), new_perm);
rewriter.ReplaceOp(op, {new_transpose_op.out()});
return true;
}
return false;
}
private:
std::vector<int> GetAxis(paddle::dialect::TransposeOp op) const {
auto array_attr = op.attribute<pir::ArrayAttribute>("perm").AsVector();
std::vector<int> axis(array_attr.size());
for (size_t i = 0; i < array_attr.size(); ++i) {
axis[i] = array_attr[i].dyn_cast<pir::Int32Attribute>().data();
}
return axis;
}
std::vector<int> GetPerm(const std::vector<int> &perm1,
const std::vector<int> &perm2) const {
int n = static_cast<int>(perm1.size());
std::vector<int> axis(n), axis1(n), axis2(n);
std::iota(axis.begin(), axis.end(), 0);
for (int i = 0; i < n; ++i) {
axis1[i] = axis[perm1[i]];
}
for (int i = 0; i < n; ++i) {
axis2[i] = axis1[perm2[i]];
}
return axis2;
}
};
class TestPass : public pir::PatternRewritePass {
public:
TestPass() : pir::PatternRewritePass("test_pass", 1) {}
pir::RewritePatternSet InitializePatterns(pir::IrContext *context) override {
pir::RewritePatternSet ps(context);
ps.Add<RedundantTransposeFusePattern>(context);
return ps;
}
};
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_filter_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 3, 3, 3},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_input_op_1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_filter_op_1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 3, 3, 3},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp add_op_y =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1, 64, 1, 1},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_input_op_2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_filter_op_2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 3, 3, 3},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp add_op_y_1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1, 64, 1, 1},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp add_op_2_x = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{4, 64, 14, 14},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_mean_op = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
paddle::dialect::FullOp full_variance_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_scale_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_bias_op = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{64}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
paddle::dialect::Conv2dOp conv2d_op =
builder.Build<paddle::dialect::Conv2dOp>(full_input_op.out(),
full_filter_op.out());
paddle::dialect::BatchNorm_Op batch_norm_op =
builder.Build<paddle::dialect::BatchNorm_Op>(conv2d_op.out(),
full_mean_op.out(),
full_variance_op.out(),
full_scale_op.out(),
full_bias_op.out(),
true,
0.9,
1e-6,
"NCHW",
false,
false);
paddle::dialect::Conv2dOp conv2d_op_1 =
builder.Build<paddle::dialect::Conv2dOp>(full_input_op_1.out(),
full_filter_op_1.out());
paddle::dialect::AddOp add_op =
builder.Build<paddle::dialect::AddOp>(conv2d_op_1.out(), add_op_y.out());
paddle::dialect::ReluOp relu_op =
builder.Build<paddle::dialect::ReluOp>(add_op.out());
paddle::dialect::Conv2dOp conv2d_op_2 =
builder.Build<paddle::dialect::Conv2dOp>(full_input_op_2.out(),
full_filter_op_2.out());
paddle::dialect::AddOp add_op_1 = builder.Build<paddle::dialect::AddOp>(
conv2d_op_2.out(), add_op_y_1.out());
paddle::dialect::AddOp add_op_2 =
builder.Build<paddle::dialect::AddOp>(add_op_2_x.out(), add_op_1.out());
paddle::dialect::ReluOp relu_op_1 =
builder.Build<paddle::dialect::ReluOp>(add_op_2.out());
auto transpose1_op = builder.Build<paddle::dialect::TransposeOp>(
batch_norm_op.out(), std::vector<int>{0, 2, 3, 1});
auto transpose2_op = builder.Build<paddle::dialect::TransposeOp>(
transpose1_op.out(), std::vector<int>{0, 3, 1, 2});
auto add_out = builder.Build<paddle::dialect::AddOp>(transpose2_op.out(),
relu_op_1.out());
auto add_out_1 =
builder.Build<paddle::dialect::AddOp>(add_out.out(), relu_op.out());
builder.Build<paddle::dialect::FetchOp>(add_out_1.out(), "out", 0);
}
TEST(pattern_rewrite, Patterns) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
EXPECT_EQ(program.block()->size(), 27u);
pir::PassManager pm(ctx);
pm.AddPass(std::make_unique<TestPass>());
pm.AddPass(pir::CreateConv2dBnFusePass());
pm.AddPass(pir::CreateConv2dAddActFusePass());
pm.AddPass(pir::CreateConv2dAddFusePass());
std::unique_ptr<pir::Pass> constant_folding_pass =
pir::CreateConstantFoldingPass();
phi::Place place = phi::CPUPlace();
constant_folding_pass->SetNotOwned(pir::Pass::kPlaceAttr, &place);
constant_folding_pass->Set(pir::Pass::kParamScopeAttr,
new paddle::framework::Scope());
pm.AddPass(std::move(constant_folding_pass));
pm.AddPass(pir::CreateDeadCodeEliminationPass());
// pm.EnablePassTiming();
pm.EnableIRPrinting();
// pm.EnableIRPrinting(std::make_unique<pir::PassManager::IRPrinterOption>(
// [](pir::Pass *pass, pir::Operation *op) {
// return pass->name() == "constant_folding_pass";
// },
// [](pir::Pass *pass, pir::Operation *op) {
// return pass->name() == "constant_folding_pass";
// },
// true,
// true));
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::ExecutionTimeout(
"Sorry, the for program test is timeout."));
EXPECT_EQ(program.block()->size(), 17u);
}
void BuildConstantFoldingProgram(pir::Program *program,
pir::IrContext *ctx,
paddle::framework::Scope *scope) {
pir::Builder builder = pir::Builder(ctx, program->block());
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
phi::DDim dims = {2, 2};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
pir::Type dense_tensor_dtype = paddle::dialect::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset);
phi::DenseTensorMeta meta(
phi::DataType::FLOAT32, dims, data_layout, lod, offset);
phi::DeviceContext *dev_ctx =
phi::DeviceContextPool::Instance().Get(phi::CPUPlace());
auto op1 = builder.Build<pir::ConstantTensorOp>("a", dense_tensor_dtype);
auto op2 = builder.Build<pir::ConstantTensorOp>("b", dense_tensor_dtype);
auto op3 =
builder.Build<paddle::dialect::AddOp>(op1->result(0), op2->result(0));
auto op4 = builder.Build<pir::ParameterOp>("c", dense_tensor_dtype);
auto op5 =
builder.Build<paddle::dialect::AddOp>(op3->result(0), op4->result(0));
builder.Build<paddle::dialect::FetchOp>(op5.out(), "out", 0);
auto *tensor_a = scope->Var("a")->GetMutable<phi::DenseTensor>();
auto *tensor_b = scope->Var("b")->GetMutable<phi::DenseTensor>();
auto *tensor_c = scope->Var("c")->GetMutable<phi::DenseTensor>();
tensor_a->set_meta(meta);
tensor_b->set_meta(meta);
tensor_c->set_meta(meta);
dev_ctx->Alloc(tensor_a, phi::DataType::FLOAT32);
dev_ctx->Alloc(tensor_b, phi::DataType::FLOAT32);
dev_ctx->Alloc(tensor_c, phi::DataType::FLOAT32);
}
TEST(constant_folding, ConstantFolding) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
paddle::framework::Scope scope;
BuildConstantFoldingProgram(&program, ctx, &scope);
pir::PassManager pm(ctx);
std::unique_ptr<pir::Pass> constant_folding_pass =
pir::CreateConstantFoldingPass();
phi::Place place = phi::CPUPlace();
constant_folding_pass->SetNotOwned(pir::Pass::kPlaceAttr, &place);
constant_folding_pass->SetNotOwned(pir::Pass::kParamScopeAttr, &scope);
pm.AddPass(std::move(constant_folding_pass));
pm.AddPass(pir::CreateDeadCodeEliminationPass());
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::ExecutionTimeout(
"Sorry, the test for program is timeout."));
EXPECT_EQ(program.block()->size(), 2u);
}
TEST(constant_folding, ConstantFolding_Train) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
paddle::framework::Scope scope;
BuildConstantFoldingProgram(&program, ctx, &scope);
pir::PassManager pm(ctx);
std::unique_ptr<pir::Pass> constant_folding_pass =
pir::CreateConstantFoldingPass();
phi::Place place = phi::CPUPlace();
constant_folding_pass->SetNotOwned(pir::Pass::kPlaceAttr, &place);
constant_folding_pass->SetNotOwned(pir::Pass::kParamScopeAttr, &scope);
constant_folding_pass->Set("train_mode", new bool(true));
pm.AddPass(std::move(constant_folding_pass));
pm.AddPass(pir::CreateDeadCodeEliminationPass());
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::ExecutionTimeout(
"Sorry, the test for program is timeout."));
EXPECT_EQ(program.block()->size(), 4u);
}
void BuildConcatProgram(pir::Program *program, pir::IrContext *ctx) {
pir::Builder builder = pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto t1 =
builder.Build<pir::CombineOp>(std::vector<pir::Value>({x, y})).result(0);
auto out1 = builder.Build<paddle::dialect::ConcatOp>(t1, 1).result(0);
auto z = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto w = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto t2 =
builder.Build<pir::CombineOp>(std::vector<pir::Value>({z, w})).result(0);
auto out2 = builder.Build<paddle::dialect::ConcatOp>(t2, 1).result(0);
auto out = builder.Build<paddle::dialect::AddOp>(out1, out2).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
}
TEST(constant_folding, ConstantFolding_Combine) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
BuildConcatProgram(&program, ctx);
pir::PassManager pm(ctx);
std::unique_ptr<pir::Pass> constant_folding_pass =
pir::CreateConstantFoldingPass();
phi::Place place = phi::CPUPlace();
constant_folding_pass->SetNotOwned(pir::Pass::kPlaceAttr, &place);
constant_folding_pass->Set(pir::Pass::kParamScopeAttr,
new paddle::framework::Scope());
pm.AddPass(std::move(constant_folding_pass));
pm.AddPass(pir::CreateDeadCodeEliminationPass());
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::ExecutionTimeout(
"Sorry, the test for program is timeout."));
EXPECT_EQ(program.block()->size(), 2u);
}
void BuildMultiOutputProgram(pir::Program *program, pir::IrContext *ctx) {
pir::Builder builder = pir::Builder(ctx, program->block());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({2, 2}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto cummax_op = builder.Build<paddle::dialect::CummaxOp>(x, 0);
auto out = cummax_op.out();
auto indices = cummax_op.indices();
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
builder.Build<paddle::dialect::FetchOp>(indices, "indices", 1);
}
TEST(constant_folding, ConstantFolding_MultiOutput) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
BuildMultiOutputProgram(&program, ctx);
pir::PassManager pm(ctx);
std::unique_ptr<pir::Pass> constant_folding_pass =
pir::CreateConstantFoldingPass();
phi::Place place = phi::CPUPlace();
constant_folding_pass->SetNotOwned(pir::Pass::kPlaceAttr, &place);
constant_folding_pass->Set(pir::Pass::kParamScopeAttr,
new paddle::framework::Scope());
pm.AddPass(std::move(constant_folding_pass));
pm.AddPass(pir::CreateDeadCodeEliminationPass());
pm.EnableIRPrinting();
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::ExecutionTimeout(
"Sorry, the test for program is timeout."));
EXPECT_EQ(program.block()->size(), 4u);
}
@@ -0,0 +1,9 @@
paddle_test(test_builtin_parameter SRCS test_builtin_parameter.cc)
paddle_test(save_load_version_compat_test SRCS save_load_version_compat_test.cc
DEPS test_dialect)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(test_builtin_parameter)
endif()
@@ -0,0 +1,32 @@
op_pair_patches:
- op_pair : [pd_op.full, pd_op.scale]
actions:
- action : add_value
object : [1,2]
type : pir::DenseTensorType
data : [pir::Float32Type,[1],"NCHW",[],0]
- action : delete_value
object : [1, 2]
- op_pair : [test1.operation4, test1.operation3]
actions:
- action : delete_value
object : [0, 0]
- op_pair : [test1.operation1, test1.operation2]
actions:
- action : add_value
object : [1, 0]
type : pir::DenseTensorType
data : [pir::Int32Type,[1],"NCHW",[],0]
op_patches:
- op_name : builtin.parameter
actions :
- action : add_attr
object : new_attribute
type : pir::StrAttribute
data : "new.attribute"
- op_name : pd_op.fetch
actions :
- action : delete_input
object : 0
- action : delete_attr
object : col
@@ -0,0 +1,110 @@
op_patches:
- op_name : pd_op.data
actions:
- action : modify_output_attr
object : stop_gradient
type : pir::ArrayAttribute
data :
- type: pir::BoolAttribute
data: "false"
- op_name : builtin.constant
actions:
- action : modify_attr
object : value
type : pir::FloatAttribute
data : 1.0
- action : modify_output_type
object : 0
type : pir::Float64Type
data : pir::Float32Type
- op_name : builtin.parameter
actions :
- action : modify_attr
object : parameter_name
type : pir::StrAttribute
data : "fc_0"
- action : modify_output_attr
object : stop_gradient
type: pir::BoolAttribute
data : "false"
- op_name : pd_op.fetch
actions :
- action : add_output
object : 1
type : pir::DenseTensorType
data : [pir::Float32Type,[-1,30],"NCHW",[],0]
- op_name : test.operation1
actions :
- action : modify_attr
object : op1_attr1
type : pir::StrAttribute
data : "op1_attr1_value"
- action : delete_attr
object : op1_attr2
- action : add_attr
object : op1_attr3
type : pir::StrAttribute
data : "op1_attr3_value"
- action : modify_name
data : test1.operation1
- op_name : test.operation2
actions :
- action : add_output
object : 0
type : pir::DenseTensorType
data : [pir::Float32Type,[-1],"NCHW",[],0]
- action : add_output_attr
object : op2_attr1
type : pir::StrAttribute
data : "op2_attr1_value"
- op_name : test1.operation3
actions :
- action : delete_input
object : 0
- op_name : test1.operation4
actions :
- action : modify_attr
object : op4_attr1
type : pir::ArrayAttribute
data :
- type: pir::Int64Attribute
- type: pir::Int64Attribute
- action : add_attr
object : op4_attr2
type : paddle::dialect::DataTypeAttribute
data : phi::DataType::UNDEFINED
- action : add_attr
object : op4_attr3
type : paddle::dialect::PlaceAttribute
data : [0,0,""]
- action : add_attr
object : op4_attr4
type : paddle::dialect::IntArrayAttribute
data : [2, 2, 2]
- action : add_attr
object : op4_attr5
type : paddle::dialect::ScalarAttribute
data :
- type: phi::DataType::COMPLEX64
data: [1.0, 1.0]
- action : add_attr
object : op4_attr6
type : pir::TypeAttribute
data : pir::Float64Type
- action : add_attr
object : op4_attr7
type : paddle::dialect::DataLayoutAttribute
data : phi::DataLayout::kNHWC
attr_patches:
- attr_name : pir::FloatAttribute
actions:
- action : modify_name
type : pir::DoubleAttribute
type_patches:
- type_name : pir::Float32Type
actions:
- action : modify_name
type : pir::Float64Type
@@ -0,0 +1,413 @@
// Copyright (c) 2024 PaddlePaddle 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 <gtest/gtest.h>
#include <stdio.h>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include "paddle/common/enforce.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_attribute.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/fluid/pir/serialize_deserialize/include/interface.h"
#include "paddle/fluid/pir/serialize_deserialize/include/ir_deserialize.h"
#include "paddle/fluid/pir/serialize_deserialize/include/schema.h"
#include "paddle/fluid/pir/serialize_deserialize/include/version_compat.h"
#include "paddle/phi/common/port.h"
#include "paddle/phi/core/tensor_meta.h"
#include "paddle/pir/include/core/block.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/ir_printer.h"
#include "paddle/pir/include/core/op_base.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/region.h"
#include "paddle/pir/include/core/utils.h"
#include "test/cpp/pir/tools/test1_dialect.h"
#include "test/cpp/pir/tools/test_dialect.h"
#include "test/cpp/pir/tools/test_op.h"
#include "test/cpp/pir/tools/test_pir_utils.h"
#define PROGRAM "program"
#define BASE_CODE "base_code"
#define MAGIC "magic"
#define PIRVERSION "version"
#define TRAINABLE "trainable"
#define PIR "pir"
namespace {
void RegisterTestDialectIdsForTest() {
auto *dialect_id_map = pir::DialectIdMap::Instance();
dialect_id_map->insert(test::TestDialect::name(), "-1");
dialect_id_map->insert(test1::Test1Dialect::name(), "-2");
}
} // namespace
// Test for building patches.
TEST(save_load_version_compat, op_patch_test) {
RegisterTestDialectIdsForTest();
// (1) Init environment.
pir::IrContext *ctx = pir::IrContext::Instance();
// (2) Create an empty program object
pir::Program program(ctx);
// pir::Program *program = new pir::Program();
EXPECT_EQ(program.block()->empty(), true);
const uint64_t pir_version = 0;
pir::PatchBuilder builder(pir_version);
builder.SetFileVersion(1);
std::string patch_path = "patch";
VLOG(8) << "Patch path: " << patch_path;
builder.BuildPatch(2, 2, patch_path);
}
bool ReadModuleForTest(const std::string &file_path,
pir::Program *program,
uint64_t pir_version) {
std::ifstream f(file_path);
Json data = Json::parse(f);
pir::PatchBuilder builder(pir_version);
if (data.contains(BASE_CODE) && data[BASE_CODE].contains(MAGIC) &&
data[BASE_CODE][MAGIC] == PIR) {
uint64_t file_version =
data.at(BASE_CODE).at(PIRVERSION).template get<uint64_t>();
if (file_version != pir_version) {
builder.SetFileVersion(file_version);
std::string patch_path = "patch";
VLOG(8) << "Patch path: " << patch_path;
builder.BuildPatch(2, 2, patch_path);
}
} else {
PADDLE_THROW(::common::errors::InvalidArgument("Invalid model file: %s.",
file_path));
}
pir::ProgramReader reader(pir_version);
reader.RecoverProgram(&(data[PROGRAM]), program, &builder);
if (data[BASE_CODE].contains(TRAINABLE)) {
return data[BASE_CODE][TRAINABLE].get<bool>();
} else {
return false;
}
}
// Test for attribute patch and op attribute modification.
TEST(save_load_version_compat, attribute_patch_test1) {
RegisterTestDialectIdsForTest();
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
// Create a float32 DenseTensor Parameter and save into Program
pir::Type fp32_dtype = pir::Float32Type::get(ctx);
// Def a = ParameterOp("a")
std::string op1_name = pir::ParameterOp::name();
pir::OpInfo op1_info = ctx->GetRegisteredOpInfo(op1_name);
std::unordered_map<std::string, pir::Attribute> op1_attribute{
{"parameter_name", pir::StrAttribute::get(ctx, "a")}};
pir::Operation *op1 =
pir::Operation::Create({}, op1_attribute, {fp32_dtype}, op1_info);
program.block()->push_back(op1);
// Def b = Constant("b")
std::string op2_name = std::string(pir::ConstantOp::name());
pir::OpInfo op2_info = ctx->GetRegisteredOpInfo(op2_name);
pir::AttributeMap attr_map;
attr_map.insert(std::pair<std::string, pir::Attribute>(
"value", pir::FloatAttribute::get(ctx, 2.0)));
pir::Operation *op2 =
pir::Operation::Create({}, attr_map, {fp32_dtype}, op2_info);
program.block()->push_back(op2);
// Save the program into file
pir::WriteModule(
program, "./test_save_load", true, false, true, /*pir_version*/ 1);
// Load the program from file
pir::Program new_program(ctx);
ReadModuleForTest("./test_save_load", &new_program, 2);
EXPECT_EQ(new_program.block()->front().name(), op1->name());
EXPECT_EQ(new_program.block()->back().name(), op2->name());
// In patch yaml, the value of attribute "parameter_name" in builtin.parameter
// is changed into "fc_0"
EXPECT_EQ(new_program.block()
->front()
.attribute("parameter_name")
.dyn_cast<::pir::StrAttribute>()
.AsString(),
"fc_0");
// In patch yaml, the value of attribute "stop_gradient" in builtin.parameter
// is changed into false
EXPECT_EQ(new_program.block()
->front()
.attribute("stop_gradient")
.dyn_cast<::pir::ArrayAttribute>()
.AsVector()[0]
.dyn_cast<::pir::BoolAttribute>()
.data(),
false);
// In patch yaml, the value of attribute "value" in builtin.constant is
// changed into 1.0 Also, the type of attribute "FloatAttribute" is changed
// into "DoubleAttribute".
EXPECT_EQ(new_program.block()
->back()
.attribute("value")
.dyn_cast<::pir::DoubleAttribute>()
.data(),
1.0);
// In patch yaml, the type of Float32Type is changed into Float64Type
EXPECT_EQ(new_program.block()->front().result(0).type(),
pir::Float64Type::get(ctx));
EXPECT_EQ(new_program.block()->back().result(0).type(),
pir::Float64Type::get(ctx));
}
TEST(save_load_version_compat, attribute_patch_test2) {
RegisterTestDialectIdsForTest();
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test1::Test1Dialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype = pir::Float32Type::get(ctx);
// Get registered operations.
pir::OpInfo op4_info = ctx->GetRegisteredOpInfo(test1::Operation4::name());
std::unordered_map<std::string, pir::Attribute> op4_attribute{
{"op4_attr1",
pir::ArrayAttribute::get(ctx, {pir::Int32Attribute::get(ctx, 3)})}};
pir::Operation *op4 =
pir::Operation::Create({}, op4_attribute, {dtype}, op4_info);
block->push_back(op4);
// Save the program into file
pir::WriteModule(
program, "./test_save_load", /*pir_version*/ 1, true, false, true);
// Load the program from file
pir::Program new_program(ctx);
ReadModuleForTest("./test_save_load", &new_program, 2);
EXPECT_EQ(new_program.block()
->front()
.attribute("op4_attr1")
.dyn_cast<::pir::ArrayAttribute>()
.AsVector()[0]
.dyn_cast<::pir::Int64Attribute>()
.data(),
3);
EXPECT_EQ(new_program.block()
->front()
.attribute("op4_attr2")
.dyn_cast<::paddle::dialect::DataTypeAttribute>()
.data(),
phi::DataType::UNDEFINED);
EXPECT_EQ(new_program.block()
->front()
.attribute("op4_attr3")
.dyn_cast<::paddle::dialect::PlaceAttribute>()
.data()
.GetType(),
phi::AllocationType::UNDEFINED);
EXPECT_EQ(new_program.block()
->front()
.attribute("op4_attr4")
.dyn_cast<::paddle::dialect::IntArrayAttribute>()
.data()
.size(),
3);
EXPECT_EQ(new_program.block()
->front()
.attribute("op4_attr5")
.dyn_cast<::paddle::dialect::ScalarAttribute>()
.data()
.to<phi::dtype::complex<float>>()
.real,
1.0);
EXPECT_EQ(new_program.block()
->front()
.attribute("op4_attr6")
.dyn_cast<::pir::TypeAttribute>()
.data()
.isa<pir::Float64Type>(),
true);
EXPECT_EQ(new_program.block()
->front()
.attribute("op4_attr7")
.dyn_cast<::paddle::dialect::DataLayoutAttribute>()
.data(),
phi::DataLayout::NHWC);
}
// Test for op I/O and op attribute modification.
TEST(save_load_version_compat, op_patch_test1) {
RegisterTestDialectIdsForTest();
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<test1::Test1Dialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype = pir::Float32Type::get(ctx);
// Get registered operations.
pir::OpInfo op1_info = ctx->GetRegisteredOpInfo(test::Operation1::name());
std::unordered_map<std::string, pir::Attribute> op1_attribute{
{"op1_attr1", pir::StrAttribute::get(ctx, "op1_attr1")},
{"op1_attr2", pir::StrAttribute::get(ctx, "op1_attr2")}};
pir::Operation *op1 =
pir::Operation::Create({}, op1_attribute, {dtype}, op1_info);
block->push_back(op1);
pir::OpInfo op2_info = ctx->GetRegisteredOpInfo(test::Operation2::name());
pir::Operation *op2 = pir::Operation::Create({}, {}, {}, op2_info);
program.block()->push_back(op2);
pir::WriteModule(
program, "./test_save_load", true, false, true, /*pir_version*/ 1);
// Load the program from file
pir::Program new_program(ctx);
ReadModuleForTest("./test_save_load", &new_program, 2);
// In patch yaml, the type of Float32Type is changed into Float64Type
EXPECT_EQ(new_program.block()->front().result(0).type(),
pir::Float64Type::get(ctx));
// In patch yaml, the value of attribute "op1_attr1" in test.operation1 is
// changed into "op1_attr1_value".
EXPECT_EQ(new_program.block()
->front()
.attribute("op1_attr1")
.dyn_cast<::pir::StrAttribute>()
.AsString(),
"op1_attr1_value");
// In patch yaml, the attribute "op1_attr2" is deleted from test.operation1.
EXPECT_EQ(new_program.block()->front().HasAttribute("op1_attr2"), false);
// In patch yaml, a new attribute "op1_attr3" is added into test.operation1.
EXPECT_EQ(new_program.block()
->front()
.attribute("op1_attr3")
.dyn_cast<::pir::StrAttribute>()
.AsString(),
"op1_attr3_value");
// In patch yaml, an output has been added to test.operation2, so the number
// of results is not 0.
EXPECT_EQ(new_program.block()->back().results().empty(), false);
}
// Test for the combination of op I/O and op_pair patch for deleting value.
TEST(save_load_version_compat, op_patch_test2) {
RegisterTestDialectIdsForTest();
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<test1::Test1Dialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype = pir::Float32Type::get(ctx);
pir::OpInfo op1_info = ctx->GetRegisteredOpInfo(test1::Operation4::name());
pir::OpInfo op2_info = ctx->GetRegisteredOpInfo(test1::Operation3::name());
pir::Operation *op1 =
pir::Operation::Create({}, {}, {dtype, dtype}, op1_info);
block->push_back(op1);
pir::Operation *op2 = pir::Operation::Create(
{op1->result(0), op1->result(1)}, {}, {dtype}, op2_info);
block->push_back(op2);
pir::WriteModule(
program, "./test_save_load", true, false, true, /*pir_version*/ 1);
// Load the program from file
pir::Program new_program(ctx);
ReadModuleForTest("./test_save_load", &new_program, 2);
// After applying the op_pair patch which deleted the output of
// test1.operation4 and the input of test1.operation3 the number of results of
// test1.operation4 is changed into 1.
EXPECT_EQ(new_program.block()->front().num_results(), (uint64_t)1);
// And with the combination of the op_patch which deleted another input of
// test1.operation3, the number of operands of test1.operation3 is changed
// into 0.
EXPECT_EQ(new_program.block()->back().operands().empty(), true);
}
// Test for op_pair patch for adding value.
TEST(save_load_version_compat, op_patch_test3) {
RegisterTestDialectIdsForTest();
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<test::TestDialect>();
ctx->GetOrRegisterDialect<test1::Test1Dialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
pir::Program program(ctx);
auto block = program.block();
pir::Builder builder(ctx, block);
pir::Type dtype = pir::Float32Type::get(ctx);
pir::OpInfo op1_info = ctx->GetRegisteredOpInfo(test1::Operation1::name());
pir::OpInfo op2_info = ctx->GetRegisteredOpInfo(test1::Operation2::name());
std::unordered_map<std::string, pir::Attribute> op1_attribute{
{"op1_attr1", pir::StrAttribute::get(ctx, "op1_attr1")},
{"op1_attr3", pir::StrAttribute::get(ctx, "op1_attr3")}};
pir::Operation *op1 =
pir::Operation::Create({}, op1_attribute, {dtype}, op1_info);
block->push_back(op1);
pir::Operation *op2 = pir::Operation::Create({}, {}, {dtype}, op2_info);
block->push_back(op2);
pir::WriteModule(
program, "./test_save_load", true, false, true, /*pir_version*/ 1);
// Load the program from file
pir::Program new_program(ctx);
ReadModuleForTest("./test_save_load", &new_program, 2);
// After applying the op_pair patch which deleted the output of
// test1.operation4 and the input of test1.operation3 the number of results of
// test1.operation4 is changed into 1.
EXPECT_EQ(new_program.block()->front().num_results(), (uint64_t)2);
// And with the combination of the op_patch which deleted another input of
// test1.operation3, the number of operands of test1.operation3 is changed
// into 0.
EXPECT_EQ(new_program.block()->back().num_operands(), (uint64_t)1);
}
@@ -0,0 +1,62 @@
// Copyright (c) 2024 PaddlePaddle 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/serialize_deserialize/include/interface.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/operation.h"
#include "paddle/pir/include/core/program.h"
TEST(SaveTest, uncompelteted_parameter) {
pir::IrContext* ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
paddle::dialect::FullOp full_op1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 64}, 1.5);
pir::OpInfo op_info = ctx->GetRegisteredOpInfo(pir::ParameterOp::name());
std::vector<pir::Value> inputs;
std::vector<pir::Type> output_types;
output_types.push_back(full_op1.out().type());
pir::AttributeMap attributes;
attributes.insert(
{"parameter_name", pir::StrAttribute::get(ctx, "test_param")});
pir::Operation* op =
pir::Operation::Create(inputs, attributes, output_types, op_info);
program.block()->push_back(op);
pir::WriteModule(
program, "./test_param", true, false, true, /*pir_version*/ 0);
pir::Program new_program(ctx);
pir::ReadModule("./test_param", &new_program, /*pir_version*/ 0);
pir::Operation& new_op = new_program.block()->back();
EXPECT_EQ(new_op.attribute("is_distributed").isa<pir::ArrayAttribute>(),
true);
EXPECT_EQ(new_op.attribute("is_parameter").isa<pir::ArrayAttribute>(), true);
EXPECT_EQ(new_op.attribute("need_clip").isa<pir::ArrayAttribute>(), true);
EXPECT_EQ(new_op.attribute("parameter_name").isa<pir::StrAttribute>(), true);
EXPECT_EQ(new_op.attribute("persistable").isa<pir::ArrayAttribute>(), true);
EXPECT_EQ(new_op.attribute("stop_gradient").isa<pir::ArrayAttribute>(), true);
EXPECT_EQ(new_op.attribute("trainable").isa<pir::ArrayAttribute>(), true);
}
+10
View File
@@ -0,0 +1,10 @@
paddle_test(symbol_dim_expr_test SRCS symbol_dim_expr_test.cc)
paddle_test(simplify_dim_expr_test SRCS simplify_dim_expr_test.cc)
paddle_test(dim_expr_util_test SRCS dim_expr_util_test.cc)
paddle_test(constraints_manager_test SRCS constraints_manager_test.cc)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(symbol_dim_expr_test)
endif()
@@ -0,0 +1,78 @@
// Copyright (c) 2024 PaddlePaddle 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 "gtest/gtest.h"
#include "paddle/pir/include/dialect/shape/utils/constraints_manager.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_builder.h"
namespace symbol::test {
TEST(ConstraintsManager, EqualCstr) {
ConstraintsManager cstr_mgr;
DimExprBuilder builder;
// Eq(Mul(S0,S1),Mul(S2,S3))
DimExpr sym_expr_0 = builder.Symbol("S0");
DimExpr sym_expr_1 = builder.Symbol("S1");
DimExpr sym_expr_2 = builder.Symbol("S2");
DimExpr sym_expr_3 = builder.Symbol("S3");
DimExpr mul_expr_0 = builder.Mul(sym_expr_0, sym_expr_1);
DimExpr mul_expr_1 = builder.Mul(sym_expr_2, sym_expr_3);
cstr_mgr.AddEqCstr(mul_expr_0, mul_expr_1);
ASSERT_TRUE(cstr_mgr.IsEqual(mul_expr_0, mul_expr_1));
// Eq(Add(S0,S1),Add(S0,1))
DimExpr int_expr_1 = builder.ConstSize(1);
DimExpr add_expr_0 = builder.Add(sym_expr_0, sym_expr_1);
DimExpr add_expr_1 = builder.Add(sym_expr_0, int_expr_1);
cstr_mgr.AddEqCstr(add_expr_0, add_expr_1);
ASSERT_FALSE(cstr_mgr.IsEqual(add_expr_0, add_expr_1));
DimExpr mul_expr_2 = builder.Mul(sym_expr_0, int_expr_1);
ASSERT_TRUE(cstr_mgr.IsEqual(mul_expr_2, mul_expr_1));
}
TEST(ConstraintsManager, GreatThanOneCstr) {
ConstraintsManager cstr_mgr;
DimExpr sym_expr_0 = DimExpr("S0");
DimExpr int_expr = DimExpr(5);
ASSERT_TRUE(cstr_mgr.IsGTOne(int_expr + sym_expr_0));
ASSERT_TRUE(cstr_mgr.IsGTOne(int_expr * sym_expr_0));
cstr_mgr.AddGTOneCstr(sym_expr_0);
ASSERT_TRUE(cstr_mgr.IsGTOne(sym_expr_0));
}
TEST(ConstraintsManager, BroadcastableCstr) {
ConstraintsManager cstr_mgr;
DimExpr sym_expr_0 = DimExpr("S0");
DimExpr int_expr = DimExpr(5);
cstr_mgr.AddBroadcastableCstr(sym_expr_0, int_expr);
ASSERT_TRUE(cstr_mgr.IsBroadcastable(sym_expr_0, int_expr));
}
TEST(ConstraintsManager, Case1) {
// BC(S0, S1) == S2 => BC(S0, S2) == S2
ConstraintsManager cstr_mgr;
DimExpr s0 = DimExpr("S0");
DimExpr s1 = DimExpr("S1");
DimExpr s2 = DimExpr("S2");
DimExpr bc_s0_s1 = Broadcast<DimExpr>{{s0, s1}};
cstr_mgr.AddEqCstr(bc_s0_s1, s2);
cstr_mgr.AddBroadcastableCstr(s0, s1);
DimExpr bc_s0_s2 = Broadcast<DimExpr>{{s0, s2}};
cstr_mgr.AddBroadcastableCstr(s0, s2);
ASSERT_TRUE(cstr_mgr.IsEqual(bc_s0_s2, s2));
}
} // namespace symbol::test
@@ -0,0 +1,111 @@
// Copyright (c) 2024 PaddlePaddle 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 "gtest/gtest.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_builder.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_util.h"
namespace symbol::test {
namespace {
// (S0 - S1) * 2 / S0
DimExpr CreateExampleDimExpr() {
DimExpr sym0 = DimExpr("S0");
DimExpr sym1 = DimExpr("S1");
DimExpr constant = DimExpr(2);
return (sym0 - sym1) * constant / sym0;
}
} // namespace
TEST(DimExprUtil, SimplifyNeg) {
DimExpr dim_expr = Negative<DimExpr>{-1};
DimExpr ret = SimplifyDimExpr(dim_expr);
ASSERT_TRUE(ret.Has<std::int64_t>());
ASSERT_EQ(ret.Get<std::int64_t>(), 1);
DimExpr double_neg_expr = Negative<DimExpr>{dim_expr};
ret = SimplifyDimExpr(double_neg_expr);
ASSERT_TRUE(ret.Has<std::int64_t>());
ASSERT_EQ(ret.Get<std::int64_t>(), -1);
}
TEST(DimExprUtil, Substitute) {
DimExpr dim_expr = CreateExampleDimExpr();
std::unordered_map<symbol::DimExpr, symbol::DimExpr> naive_to_full_name{
{DimExpr("S0"), DimExpr("symbol0")}, {DimExpr("S1"), DimExpr("symbol1")}};
std::unordered_map<symbol::DimExpr, symbol::DimExpr> full_name_to_naive{
{DimExpr("symbol0"), DimExpr("S0")}, {DimExpr("symbol1"), DimExpr("S1")}};
const auto& mid_expr = SubstituteDimExpr(dim_expr, naive_to_full_name);
const auto& ret_expr = SubstituteDimExpr(mid_expr, full_name_to_naive);
ASSERT_EQ(ret_expr, dim_expr);
}
TEST(DimExprUtil, Calculate) {
// (S0 - S1) * 2 / S0
DimExpr dim_expr = CreateExampleDimExpr();
// (4 - 2) * 2 / 4 => 1
DimExpr substitute_expr = SubstituteDimExpr(dim_expr, {{"S0", 4}, {"S1", 2}});
DimExpr ret = SimplifyDimExpr(substitute_expr);
ASSERT_TRUE(ret.Has<std::int64_t>());
ASSERT_EQ(ret.Get<std::int64_t>(), 1);
}
TEST(DimExprUtil, GetDimExprPriority) {
DimExprBuilder builder;
int priority_int = GetDimExprPriority(builder.ConstSize(1));
ASSERT_EQ(priority_int, 0);
int priority_sym = GetDimExprPriority(builder.Symbol("S0"));
ASSERT_EQ(priority_sym, 1);
int priority_add =
GetDimExprPriority(builder.Add(DimExpr("S1"), DimExpr("S2")));
ASSERT_EQ(priority_add, 2);
int priority_bc =
GetDimExprPriority(builder.Broadcast(DimExpr("S3"), DimExpr("S4")));
ASSERT_EQ(priority_bc, 2);
}
TEST(DimExprUtil, CompareDimExprPriority) {
DimExprBuilder builder;
DimExpr sym_expr_0 = builder.Symbol("S0");
DimExpr sym_expr_1 = builder.Symbol("S1");
DimExpr add_expr = builder.Add(DimExpr("S2"), DimExpr("S3"));
DimExpr bc_expr = builder.Broadcast(DimExpr("S4"), DimExpr("S5"));
ASSERT_EQ(CompareDimExprPriority(sym_expr_0, sym_expr_1),
PriorityComparisonStatus::HIGHER);
ASSERT_EQ(CompareDimExprPriority(add_expr, sym_expr_0),
PriorityComparisonStatus::LOWER);
ASSERT_EQ(CompareDimExprPriority(add_expr, bc_expr),
PriorityComparisonStatus::EQUAL);
}
TEST(DimExpr, CollectDimExprSymbol) {
DimExpr dim_expr = [&]() -> DimExpr {
DimExprBuilder builder;
DimExpr max_expr = builder.Max(DimExpr("S2"), DimExpr("S3"));
DimExpr min_expr = builder.Min(max_expr, DimExpr("S4"));
DimExpr broadcast_expr = builder.Broadcast(min_expr, DimExpr("S5"));
return CreateExampleDimExpr() + broadcast_expr;
}();
std::unordered_set<std::string> symbols = CollectDimExprSymbols(dim_expr);
std::unordered_set<std::string> expected = {
"S0", "S1", "S2", "S3", "S4", "S5"};
ASSERT_EQ(symbols.size(), 6UL);
for (const auto& symbol : symbols) {
ASSERT_TRUE(expected.find(symbol) != expected.end());
}
}
} // namespace symbol::test
@@ -0,0 +1,308 @@
// Copyright (c) 2023 PaddlePaddle 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 <atomic>
#include "gtest/gtest.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_util.h"
namespace symbol::test {
namespace {
DimExpr BD(const DimExpr& lhs, const DimExpr& rhs) {
return Broadcast<DimExpr>{{lhs, rhs}};
}
DimExpr MakeSymbolic() {
static std::atomic<int64_t> cnt(0);
return DimExpr{std::to_string(cnt++)};
}
DimExpr MakeConstant(std::int64_t value) { return DimExpr{value}; }
} // namespace
TEST(DimExpr, flatten_bd) {
DimExpr sym0 = MakeSymbolic();
DimExpr sym1 = MakeSymbolic();
DimExpr sym2 = MakeSymbolic();
DimExpr origin = BD(BD(sym0, sym1), sym2);
DimExpr expected = Broadcast<DimExpr>{{sym0, sym1, sym2}};
ASSERT_EQ(SimplifyDimExpr(origin), expected);
}
TEST(Simplify, NumberAdd) {
List<DimExpr> num_lists{DimExpr(5), Negative<DimExpr>(5)};
DimExpr dim_expr{Add<DimExpr>{num_lists}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr.Get<std::int64_t>()), 0);
}
TEST(Simplify, DoubleNegative) {
DimExpr inner_expr{Negative<DimExpr>(DimExpr{1})};
DimExpr expr{Negative<DimExpr>(inner_expr)};
DimExpr simplified_dim_expr = SimplifyDimExpr(expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr.Get<std::int64_t>()), 1);
}
TEST(Simplify, UnitNegative) {
DimExpr unit{Negative<DimExpr>{DimExpr{0}}};
DimExpr simplified_dim_expr = SimplifyDimExpr(unit);
ASSERT_TRUE((simplified_dim_expr.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr.Get<std::int64_t>()), 0);
}
TEST(Simplify, NumberNaiveMul) {
List<DimExpr> num_lists{DimExpr(5), DimExpr(5)};
DimExpr dim_expr{Mul<DimExpr>{num_lists}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr.Get<std::int64_t>()), 25);
}
TEST(Simplify, NumberNaiveDiv) {
DimExpr dim_expr{Div<DimExpr>{DimExpr(5), DimExpr(5)}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr.Get<std::int64_t>()), 1);
}
TEST(Simplify, NestNumberAddDiv) {
DimExpr div_expr{Div<DimExpr>{DimExpr(5), DimExpr(5)}};
List<DimExpr> sum_lists{DimExpr(0), div_expr};
DimExpr dim_expr{Add<DimExpr>{sum_lists}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr.Get<std::int64_t>()), 1);
}
TEST(Simplify, NestNumberMulAdd) {
List<DimExpr> num_lists{DimExpr(5), Negative<DimExpr>(5)};
List<DimExpr> product_lists{DimExpr(5), Add<DimExpr>{num_lists}};
DimExpr dim_expr{Mul<DimExpr>{product_lists}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr.Get<std::int64_t>()), 0);
}
TEST(Simplify, SymbolicMul) {
DimExpr sym = MakeSymbolic();
List<DimExpr> num_lists{DimExpr(1), sym};
DimExpr dim_expr{Mul<DimExpr>{num_lists}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::string>()));
ASSERT_TRUE((simplified_dim_expr == sym));
}
TEST(Simplify, SymbolicDiv) {
DimExpr sym = MakeSymbolic();
List<DimExpr> num_lists{sym, DimExpr(1)};
DimExpr dim_expr{Mul<DimExpr>{num_lists}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::string>()));
ASSERT_TRUE((simplified_dim_expr == sym));
}
TEST(Simplify, SymbolicMulUnit) {
DimExpr sym = MakeSymbolic();
List<DimExpr> num_lists{sym, DimExpr(1)};
DimExpr dim_expr{Mul<DimExpr>{num_lists}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr == sym));
}
TEST(Simplify, SymbolicDivUnit) {
DimExpr sym = MakeSymbolic();
DimExpr dim_expr{
Div<DimExpr>{Mul<DimExpr>{List<DimExpr>{DimExpr(2), sym}}, sym}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr.Get<std::int64_t>()), 2);
}
TEST(Simplify, NestSymbolicMulAddUnit) {
DimExpr sym = MakeSymbolic();
List<DimExpr> sum_lists{DimExpr(6), Negative<DimExpr>{DimExpr(5)}};
List<DimExpr> product_lists = List<DimExpr>{Add<DimExpr>{sum_lists}, sym};
DimExpr dim_expr{Mul<DimExpr>{product_lists}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::string>()));
ASSERT_TRUE((simplified_dim_expr == sym));
}
TEST(Simplify, NestSymbolicDivAddUnit) {
DimExpr sym = MakeSymbolic();
List<DimExpr> sum_lists{DimExpr(6), Negative<DimExpr>{DimExpr(5)}};
DimExpr dim_expr{Div<DimExpr>{sym, Add<DimExpr>{sum_lists}}};
DimExpr simplified_dim_expr = SimplifyDimExpr(dim_expr);
ASSERT_TRUE((simplified_dim_expr.Has<std::string>()));
ASSERT_TRUE((simplified_dim_expr == sym));
}
TEST(Simplify, ConstantMaxMin) {
List<DimExpr> max_lists{DimExpr(4), DimExpr(6)};
DimExpr dim_expr1{Max<DimExpr>{max_lists}};
DimExpr simplified_dim_expr1 = SimplifyDimExpr(dim_expr1);
ASSERT_TRUE((simplified_dim_expr1.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr1.Get<std::int64_t>()), 6);
List<DimExpr> min_lists{DimExpr(2), DimExpr(3)};
DimExpr dim_expr2{Min<DimExpr>{min_lists}};
DimExpr simplified_dim_expr2 = SimplifyDimExpr(dim_expr2);
ASSERT_TRUE((simplified_dim_expr2.Has<std::int64_t>()));
ASSERT_EQ((simplified_dim_expr2.Get<std::int64_t>()), 2);
}
TEST(Simplify, SimplifyBc) {
// Broadcast(S0, Add(S0, -1)) => S0
DimExpr S0{"S0"};
DimExpr add{Add<DimExpr>{{S0, Negative<DimExpr>{1}}}};
DimExpr bc{Broadcast<DimExpr>{{S0, add}}};
ASSERT_TRUE((SimplifyDimExpr(bc) != Add<DimExpr>{{S0, -1}}));
// TODO(ooooo): improve the simplify ability
DimExpr now_accept{Broadcast<DimExpr>{{Add<DimExpr>{{S0, -1}}, S0}}};
ASSERT_TRUE((SimplifyDimExpr(bc) == now_accept));
}
TEST(Simplify, FoldBroadcast) {
DimExpr sym0{"S0"};
DimExpr sym1{"S1"};
DimExpr mul{Mul<DimExpr>{{sym0, sym1}}};
DimExpr broadcast0{Broadcast<DimExpr>{{mul, sym0}}};
DimExpr broadcast1{Broadcast<DimExpr>{{sym1, mul}}};
DimExpr simplify_broadcast0 = SimplifyDimExpr(broadcast0);
DimExpr simplify_broadcast1 = SimplifyDimExpr(broadcast1);
DimExpr add{Add<DimExpr>{{sym0, sym1}}};
DimExpr broadcast2{Broadcast<DimExpr>{{add, sym0}}};
DimExpr broadcast3{Broadcast<DimExpr>{{sym1, add}}};
DimExpr simplify_broadcast2 = SimplifyDimExpr(broadcast2);
DimExpr simplify_broadcast3 = SimplifyDimExpr(broadcast3);
ASSERT_TRUE(simplify_broadcast0 == mul);
ASSERT_TRUE(simplify_broadcast1 == mul);
ASSERT_TRUE(simplify_broadcast2 == add);
ASSERT_TRUE(simplify_broadcast3 == add);
}
TEST(Simplify, FoldRedundantBroadcast) {
DimExpr S0{"S0"};
DimExpr S1{"S1"};
DimExpr bc{Broadcast<DimExpr>{{S0, S0, S1, S1}}};
DimExpr simplify_bc = SimplifyDimExpr(bc);
ASSERT_TRUE((simplify_bc == Broadcast<DimExpr>{{S0, S1}}));
}
TEST(Simplify, SimplifyDoubleNegForMulAndDiv) {
// Negative(Mul(S0, Negative(1))) => S0
DimExpr S0{"S0"};
DimExpr mul{Mul<DimExpr>{{S0, Negative<DimExpr>{DimExpr(1)}}}};
DimExpr neg_mul{Negative<DimExpr>{mul}};
DimExpr simplify_neg_mul = SimplifyDimExpr(neg_mul);
ASSERT_TRUE((simplify_neg_mul == S0));
// Negative(Div(S0, Negative(1))) => S0
DimExpr div{Div<DimExpr>{S0, Negative<DimExpr>{DimExpr(1)}}};
DimExpr neg_div{Negative<DimExpr>{div}};
DimExpr simplify_neg_div = SimplifyDimExpr(neg_div);
ASSERT_TRUE((simplify_neg_div == S0));
}
TEST(Simplify, Case1) {
// Div(Mul(Div(Mul(Broadcast(S11, S8), Broadcast(S10, S13, S4, S7),
// Broadcast(S12, S3, S6, S9)), S0)), 16), 49)
DimExpr S11{"S11"};
DimExpr S8{"S8"};
DimExpr mul_op1 = Broadcast<DimExpr>{{S11, S8}};
DimExpr S10{"S10"};
DimExpr S13{"S13"};
DimExpr S4{"S4"};
DimExpr S7{"S7"};
DimExpr mul_op2 = Broadcast<DimExpr>{{S10, S13, S4, S7}};
DimExpr S12{"S12"};
DimExpr S3{"S3"};
DimExpr S6{"S6"};
DimExpr S9{"S9"};
DimExpr mul_op3 = Broadcast<DimExpr>{{S12, S3, S6, S9}};
DimExpr S0{"S0"};
DimExpr mul_op4 =
Div<DimExpr>{Mul<DimExpr>{List<DimExpr>{mul_op1, mul_op2, mul_op3}}, S0};
DimExpr dim_expr = Div<DimExpr>{
Mul<DimExpr>{List<DimExpr>{mul_op4, DimExpr{16}}}, DimExpr(49)};
ASSERT_TRUE((SimplifyDimExpr(dim_expr)) == dim_expr);
}
TEST(Simplify, Case2) {
// Div(Mul(S2, S3, 8, 7, 7), Mul( Div(S0, 7), Div(S1, 7), 8, 7, 71, 2))
DimExpr S2{"S2"};
DimExpr S3{"S3"};
DimExpr mul_op1 =
Mul<DimExpr>{List<DimExpr>{S2, S3, DimExpr(8), DimExpr(7), DimExpr(7)}};
DimExpr S0{"S0"};
DimExpr S1{"S1"};
DimExpr mul_op2 = Mul<DimExpr>{List<DimExpr>{Div<DimExpr>{S0, DimExpr(7)},
Div<DimExpr>{S1, DimExpr(7)},
DimExpr(8),
DimExpr(7),
DimExpr(7),
DimExpr(1),
DimExpr(2)}};
DimExpr dim_expr{Div<DimExpr>{mul_op1, mul_op2}};
DimExpr expected = Div<DimExpr>{
Mul<DimExpr>{List<DimExpr>{S2, S3}},
Mul<DimExpr>{List<DimExpr>{
Div<DimExpr>{S0, DimExpr(7)}, Div<DimExpr>{S1, DimExpr(7)}, 2}}};
ASSERT_TRUE((SimplifyDimExpr(dim_expr)) == expected);
}
TEST(Simplify, Case3) {
DimExpr S3{"S3"};
DimExpr S4{"S4"};
DimExpr S5{"S5"};
DimExpr S7{"S7"};
DimExpr S8{"S8"};
DimExpr dim_expr = Mul<DimExpr>{List<DimExpr>{
Div<DimExpr>{Mul<DimExpr>{List<DimExpr>{S3, S4, S5}},
Mul<DimExpr>{List<DimExpr>{S7, S8}}},
Div<DimExpr>{Mul<DimExpr>{List<DimExpr>{S3, S4, S5}},
Div<DimExpr>{Mul<DimExpr>{List<DimExpr>{S3, S4, S5}},
Mul<DimExpr>{List<DimExpr>{S7, S8}}}}}};
ASSERT_TRUE((SimplifyDimExpr(dim_expr) == dim_expr)); // Need to simplify
}
} // namespace symbol::test
@@ -0,0 +1,157 @@
// Copyright (c) 2023 PaddlePaddle 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 "gtest/gtest.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_builder.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/ir_context.h"
namespace symbol::test {
// Construct DimExpr by overloaded operator(+, - , *, /)
TEST(DimExpr, DimExprNaive) {
DimExpr sym0 = DimExpr("S0");
DimExpr sym1 = DimExpr("S1");
DimExpr constant1 = DimExpr(1);
DimExpr output = (sym0 + sym1) * constant1;
}
// Construct DimExpr by DimExprBuilder
TEST(DimExpr, DimExprBuilder) {
DimExprBuilder builder;
DimExpr sym0 = DimExpr("S0");
DimExpr sym1 = DimExpr("S1");
DimExpr constant1 = DimExpr(1);
DimExpr add = builder.Add(sym0, sym1);
DimExpr out = builder.Broadcast(add, constant1);
}
/*
Simulate the ShapeOrDataDimExprs result of below codes:
def (x, y):
extend_x = x.shape
out = pd.reshape(y, extend_x)
*/
TEST(DimExpr, DataShapeExpr) {
// Show ideal ShapeOrDataDimExprs of each pir::Value
std::vector<DimExpr> x_shapes{DimExpr("S0"), DimExpr(2)};
std::vector<DimExpr> y_shapes{DimExpr(1), DimExpr("S1"), DimExpr(2)};
// x => {shape: [S0, 2], data: nullopt}
ShapeOrDataDimExprs x_data_shape{symbol::TensorShapeOrDataDimExprs(x_shapes)};
// y => {shape: [1, S1, 2], data: nullopt}
ShapeOrDataDimExprs y_data_shape{symbol::TensorShapeOrDataDimExprs(y_shapes)};
// out => {shape: [S0, 2], data: nullopt}
ShapeOrDataDimExprs out_value_shape{
symbol::TensorShapeOrDataDimExprs(x_shapes)};
}
/*
Simulate the ShapeOrDataDimExprs result of below codes:
def (x, y):
out = pd.combine(x, y)
*/
TEST(DimExpr, TensorListShapeOrDataDimExprs) {
std::vector<DimExpr> x_shapes{DimExpr("S0"), DimExpr("S1"), DimExpr(2)};
std::vector<DimExpr> y_shapes{DimExpr(1), DimExpr("S3"), DimExpr(2)};
// x => {shape: [S0, S1, 2], data: nullopt}
ShapeOrDataDimExprs x_data_shape{symbol::TensorShapeOrDataDimExprs(x_shapes)};
// y => {shape: [1, S3, 2], data: nullopt}
ShapeOrDataDimExprs y_data_shape{symbol::TensorShapeOrDataDimExprs(y_shapes)};
// out => {shape: [S0, S1, 2], data: nullopt, shape: [1, S3, 2], data:
// nullopt}
ShapeOrDataDimExprs out_data_shape_list(
{symbol::TensorShapeOrDataDimExprs(x_shapes),
symbol::TensorShapeOrDataDimExprs(y_shapes)});
}
TEST(Simplify, NumberArithmetic) {
DimExpr number = DimExpr(5);
DimExpr add_minus = number + number - number;
ASSERT_TRUE((add_minus.Has<std::int64_t>()));
ASSERT_EQ((add_minus.Get<std::int64_t>()), 5);
DimExpr mul_div = number * DimExpr(1) / number;
ASSERT_TRUE((mul_div.Has<std::int64_t>()));
ASSERT_EQ((mul_div.Get<std::int64_t>()), 1);
}
TEST(DimExpr, Equal) {
DimExprBuilder builder;
DimExpr sym0 = DimExpr("S0");
DimExpr sym1 = DimExpr("S1");
DimExpr constant1 = DimExpr(1);
ASSERT_EQ(sym0 + sym1, sym0 + sym1);
ASSERT_EQ(sym0 + sym1, sym1 + sym0);
ASSERT_EQ(sym0 + constant1, DimExpr("S0") + constant1);
ASSERT_EQ(sym0 - sym1, sym0 - sym1);
ASSERT_NE(sym0 - sym1, sym1 - sym0);
ASSERT_EQ(sym0 - constant1, DimExpr("S0") - constant1);
ASSERT_EQ(sym0 * sym1, sym0 * sym1);
ASSERT_EQ(sym0 * sym1, sym1 * sym0);
ASSERT_EQ(sym0 * constant1, DimExpr("S0") * constant1);
ASSERT_EQ(sym0 / sym1, sym0 / sym1);
ASSERT_NE(sym0 / sym1, sym1 / sym0);
ASSERT_EQ(sym0 / constant1, DimExpr("S0") / constant1);
ASSERT_EQ(builder.Max(sym0, sym1), builder.Max(sym0, sym1));
ASSERT_NE(builder.Max(sym0, sym1), builder.Max(sym1, sym0));
ASSERT_EQ(builder.Max(sym0, constant1),
builder.Max(DimExpr("S0"), constant1));
ASSERT_EQ(builder.Min(sym0, sym1), builder.Min(sym0, sym1));
ASSERT_NE(builder.Min(sym0, sym1), builder.Min(sym1, sym0));
ASSERT_EQ(builder.Min(sym0, constant1),
builder.Min(DimExpr("S0"), constant1));
ASSERT_EQ(builder.Broadcast(sym0, sym1), builder.Broadcast(sym0, sym1));
ASSERT_EQ(builder.Broadcast(sym0, sym1), builder.Broadcast(sym1, sym0));
ASSERT_EQ(builder.Broadcast(sym0, constant1),
builder.Broadcast(DimExpr("S0"), constant1));
}
TEST(DimExpr, Print) {
DimExprBuilder builder;
DimExpr sym0 = DimExpr("S0");
DimExpr sym1 = DimExpr("S1");
ASSERT_EQ((ToString(sym0 + sym1)), "Add(S0, S1)");
ASSERT_EQ((ToString(sym0 - sym1)), "Add(S0, -S1)");
ASSERT_EQ((ToString(sym0 * sym1)), "Mul(S0, S1)");
ASSERT_EQ((ToString(sym0 / sym1)), "Div(S0, S1)");
ASSERT_EQ((ToString(builder.Max(sym0, sym1))), "Max(S0, S1)");
ASSERT_EQ((ToString(builder.Min(sym0, sym1))), "Min(S0, S1)");
ASSERT_EQ((ToString(builder.Broadcast(sym0, sym1))), "Broadcast(S0, S1)");
}
TEST(DimExpr, Hash) {
DimExprBuilder builder;
DimExpr sym0 = DimExpr("S0");
DimExpr sym1 = DimExpr("S1");
ASSERT_EQ((std::hash<DimExpr>()(sym0 + sym1)),
(std::hash<DimExpr>()(sym0 + sym1)));
ASSERT_EQ((std::hash<DimExpr>()(sym0 + sym1)),
(std::hash<DimExpr>()(sym1 + sym0)));
ASSERT_NE((std::hash<DimExpr>()(sym0 + sym1)),
(std::hash<DimExpr>()(sym0 - sym1)));
ASSERT_NE((std::hash<DimExpr>()(sym0 + sym1)),
(std::hash<DimExpr>()(sym0 * sym1)));
ASSERT_NE((std::hash<DimExpr>()(sym0 + sym1)),
(std::hash<DimExpr>()(sym0 / sym1)));
ASSERT_NE((std::hash<DimExpr>()(sym0 + sym1)),
(std::hash<DimExpr>()(builder.Max(sym0, sym1))));
ASSERT_NE((std::hash<DimExpr>()(sym0 + sym1)),
(std::hash<DimExpr>()(builder.Min(sym0, sym1))));
ASSERT_NE((std::hash<DimExpr>()(sym0 + sym1)),
(std::hash<DimExpr>()(builder.Broadcast(sym0, sym1))));
}
} // namespace symbol::test
+7
View File
@@ -0,0 +1,7 @@
if(WITH_TESTING AND WITH_CINN)
paddle_test(test_sub_graph_checker SRCS sub_graph_checker_test.cc DEPS
sub_graph_checker)
set_tests_properties(test_sub_graph_checker PROPERTIES LABELS "RUN_TYPE=CINN")
endif()
@@ -0,0 +1,168 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include "paddle/fluid/pir/dialect/operator/ir/op_attribute.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/sub_graph/sub_graph_checker.h"
#include "paddle/pir/include/core/ir_context.h"
using namespace paddle::test; // NOLINT
std::shared_ptr<::pir::Program> BuildBasicProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 1.0;
const std::vector<int64_t> shape = {128, 12, 128, 128};
std::string input_name = std::string(kInputPrefix) + "0";
auto x = builder
.Build<paddle::dialect::DataOp>(
input_name, shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto out = builder.Build<paddle::dialect::SoftmaxOp>(x, -1).result(0);
return program;
}
std::shared_ptr<::pir::Program> BuildPrimProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
std::string input_name = std::string(kInputPrefix) + "0";
const float value_one = 1.0;
const std::vector<int64_t> shape = {128, 12, 128, 128};
auto x = builder
.Build<paddle::dialect::DataOp>(
input_name, shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
// auto out = builder.Build<paddle::dialect::SinOp>(x).result(0);
auto max =
builder.Build<paddle::dialect::MaxOp>(x, std::vector<int64_t>{-1}, true)
.result(0);
auto sub = builder.Build<paddle::dialect::SubtractOp>(x, max).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(
exp, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto out = builder.Build<paddle::dialect::DivideOp>(exp, sum).result(0);
return program;
}
std::shared_ptr<::pir::Program> BuildDropOutPrimProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
std::string input_name = std::string(kInputPrefix) + "0";
auto x =
builder
.Build<paddle::dialect::DataOp>(input_name,
std::vector<int64_t>({128, 128, 768}),
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto prob = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({1}),
0.5,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto random = builder
.Build<paddle::dialect::UniformOp>(
std::vector<int64_t>({128, 128, 768}),
phi::DataType::FLOAT32,
0.0,
1.0,
0,
phi::GPUPlace())
.result(0);
auto mask =
builder.Build<paddle::dialect::GreaterThanOp>(random, prob).result(0);
auto mask1 =
builder.Build<paddle::dialect::CastOp>(mask, phi::DataType::FLOAT32)
.result(0);
auto mul = builder.Build<paddle::dialect::MultiplyOp>(x, mask1).result(0);
auto neg_prob = prob =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({1}),
0.5,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto out = builder.Build<paddle::dialect::DivideOp>(mul, neg_prob).result(0);
return program;
}
std::shared_ptr<::pir::Program> BuildDropOutPhiProgram() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
std::string input_name = std::string(kInputPrefix) + "0";
auto x =
builder
.Build<paddle::dialect::DataOp>(input_name,
std::vector<int64_t>({128, 128, 768}),
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto out = builder
.Build<paddle::dialect::DropoutOp>(
x, pir::Value(), 0.5, false, "upscale_in_train", 0, false)
.result(0);
return program;
}
TEST(sub_graph_checker, test_softmax) {
auto basic_program = BuildBasicProgram();
auto prim_program = BuildPrimProgram();
paddle::test::SubGraphChecker sub_graph_checker(basic_program, prim_program);
ASSERT_TRUE(sub_graph_checker.CheckResult());
std::vector<double> speed_data = sub_graph_checker.CheckSpeed();
ASSERT_EQ(speed_data.size(), 2u);
}
TEST(sub_graph_checker, test_dropout) {
auto basic_program = BuildDropOutPhiProgram();
auto prim_program = BuildDropOutPrimProgram();
paddle::test::SubGraphChecker sub_graph_checker(basic_program, prim_program);
ASSERT_TRUE(sub_graph_checker.CheckResult());
std::vector<double> speed_data = sub_graph_checker.CheckSpeed();
ASSERT_EQ(speed_data.size(), 2u);
}
+11
View File
@@ -0,0 +1,11 @@
if(WIN32)
remove_definitions(-DPADDLE_DLL_EXPORT)
endif()
cc_library(
test_dialect
SRCS test_dialect.cc test_op.cc test_trait.cc test_interface.cc
test1_dialect.cc
DEPS gtest)
get_property(paddle_lib GLOBAL PROPERTY PADDLE_LIB_NAME)
target_link_libraries(test_dialect $<TARGET_LINKER_FILE:${paddle_lib}>)
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 2023 PaddlePaddle 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 "paddle/pir/include/core/type_id.h"
#define IR_DECLARE_EXPLICIT_TEST_TYPE_ID(TYPE_CLASS) \
namespace pir { \
namespace detail { \
template <> \
class PADDLE_EXP_API TypeIdResolver<TYPE_CLASS> { \
public: \
static TypeId Resolve() { return id_; } \
static UniqueingId id_; \
}; \
} \
} // namespace pir
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) 2023 PaddlePaddle 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 "test/cpp/pir/tools/test1_dialect.h"
#include "paddle/pir/include/core/ir_printer.h"
#include "test/cpp/pir/tools/test_op.h"
namespace test1 {
Test1Dialect::Test1Dialect(pir::IrContext *context)
: pir::Dialect(name(), context, pir::TypeId::get<Test1Dialect>()) {
initialize();
}
void Test1Dialect::initialize() {
RegisterOps<Operation1, Operation2, Operation3, Operation4>();
}
pir::OpPrintFn Test1Dialect::PrintOperation(const pir::Operation &op) const {
return [](const pir::Operation &op, pir::IrPrinter &printer) {
printer.PrintOpResult(op);
printer.os << " =";
printer.os << " \"" << op.name() << "\"";
printer.PrintOpOperands(op);
};
}
} // namespace test1
IR_DEFINE_EXPLICIT_TYPE_ID(test1::Test1Dialect)
+32
View File
@@ -0,0 +1,32 @@
// Copyright (c) 2023 PaddlePaddle 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 "paddle/pir/include/core/dialect.h"
#include "test/cpp/pir/tools/macros_utils.h"
namespace test1 {
class Test1Dialect : public pir::Dialect {
public:
explicit Test1Dialect(pir::IrContext *context);
static const char *name() { return "test1"; }
pir::OpPrintFn PrintOperation(const pir::Operation &op) const override;
private:
void initialize();
};
} // namespace test1
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test1::Test1Dialect)
+54
View File
@@ -0,0 +1,54 @@
// Copyright (c) 2023 PaddlePaddle 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 "test/cpp/pir/tools/test_dialect.h"
#include "paddle/pir/include/core/ir_printer.h"
#include "test/cpp/pir/tools/test_op.h"
namespace test {
TestDialect::TestDialect(pir::IrContext *context)
: pir::Dialect(name(), context, pir::TypeId::get<TestDialect>()) {
initialize();
}
void TestDialect::initialize() {
RegisterOps<RegionOp,
BranchOp,
Operation1,
Operation2,
TraitExampleOp,
SameOperandsShapeTraitOp1,
SameOperandsShapeTraitOp2,
SameOperandsAndResultShapeTraitOp1,
SameOperandsAndResultShapeTraitOp2,
SameOperandsAndResultShapeTraitOp3,
SameOperandsElementTypeTraitOp1,
SameOperandsElementTypeTraitOp2,
SameOperandsAndResultElementTypeTraitOp1,
SameOperandsAndResultElementTypeTraitOp2,
SameOperandsAndResultElementTypeTraitOp3,
SameOperandsAndResultTypeTraitOp1,
SameOperandsAndResultTypeTraitOp2,
SameOperandsAndResultTypeTraitOp3>();
}
pir::OpPrintFn TestDialect::PrintOperation(const pir::Operation &op) const {
return [](const pir::Operation &op, pir::IrPrinter &printer) {
printer.PrintOpResult(op);
printer.os << " =";
printer.os << " \"" << op.name() << "\"";
printer.PrintOpOperands(op);
};
}
} // namespace test
IR_DEFINE_EXPLICIT_TYPE_ID(test::TestDialect)
+32
View File
@@ -0,0 +1,32 @@
// Copyright (c) 2023 PaddlePaddle 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 "paddle/pir/include/core/dialect.h"
#include "test/cpp/pir/tools/macros_utils.h"
namespace test {
class TestDialect : public pir::Dialect {
public:
explicit TestDialect(pir::IrContext *context);
static const char *name() { return "test"; }
pir::OpPrintFn PrintOperation(const pir::Operation &op) const override;
private:
void initialize();
};
} // namespace test
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::TestDialect)
+15
View File
@@ -0,0 +1,15 @@
// Copyright (c) 2023 PaddlePaddle 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 "test/cpp/pir/tools/test_interface.h"
IR_DEFINE_EXPLICIT_TYPE_ID(test::InferShapeInterface)
+66
View File
@@ -0,0 +1,66 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <sstream>
#include "paddle/common/enforce.h"
#include "paddle/pir/include/core/block.h"
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/dialect.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/ir_printer.h"
#include "paddle/pir/include/core/op_base.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/core/region.h"
#include "test/cpp/pir/tools/macros_utils.h"
namespace test {
/// \brief Define built-in Interface, derived from OpInterfaceBase. Concepts and
/// Models need to be defined within the class. Concept defines abstract
/// interface functions, and Model is a template class that defines the specific
/// implementation of interface functions based on template parameters.
class InferShapeInterface : public pir::OpInterfaceBase<InferShapeInterface> {
public:
struct Concept {
explicit Concept(void (*infer_shape)(pir::Operation *))
: infer_shape(infer_shape) {}
void (*infer_shape)(pir::Operation *);
};
template <class ConcreteOp>
struct Model : public Concept {
static void InferShape(pir::Operation *op) {
ConcreteOp concrete_op = ConcreteOp(op);
if (concrete_op == nullptr) throw("concrete_op is nullptr");
concrete_op.InferShape();
}
Model() : Concept(InferShape) {}
};
InferShapeInterface(pir::Operation *op, Concept *impl)
: pir::OpInterfaceBase<InferShapeInterface>(op), impl_(impl) {}
void InferShape() { impl_->infer_shape(operation()); }
private:
Concept *impl_;
};
} // namespace test
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::InferShapeInterface)
+234
View File
@@ -0,0 +1,234 @@
// Copyright (c) 2023 PaddlePaddle 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 "test/cpp/pir/tools/test_op.h"
#include "paddle/common/enforce.h"
#include "paddle/common/errors.h"
#include "paddle/phi/core/enforce.h"
#include "paddle/pir/include/core/builtin_attribute.h"
namespace test {
void RegionOp::Build(pir::Builder &builder, pir::OperationArgument &argument) {
argument.AddRegion(nullptr);
}
void BranchOp::Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
const std::vector<pir::Value> &target_operands,
pir::Block *target) {
argument.AddInputs(target_operands.begin(), target_operands.end());
argument.AddSuccessor(target);
}
void BranchOp::VerifySig() const {
PADDLE_ENFORCE_EQ(
(*this)->num_successors(),
1u,
common::errors::InvalidArgument("successors number must equal to 1."));
PADDLE_ENFORCE_NOT_NULL(
(*this)->successor(0),
common::errors::InvalidArgument("successor[0] can't be nullptr"));
}
const char *Operation1::attributes_name[2] = {"op1_attr1", // NOLINT
"op1_attr2"}; // NOLINT
void Operation1::Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) { // NOLINT
std::unordered_map<std::string, pir::Attribute> attributes{
{"op1_attr1", builder.str_attr("op1_attr2")},
{"op1_attr2", builder.str_attr("op1_attr2")}};
argument.AddOutput(builder.float32_type());
argument.AddAttributes(attributes);
}
void Operation1::VerifySig() const {
auto &attributes = this->attributes();
if (attributes.count("op1_attr1") == 0 ||
!attributes.at("op1_attr1").isa<pir::StrAttribute>()) {
PADDLE_THROW(common::errors::Fatal(
"Type of attribute: parameter_name is not right."));
}
if (attributes.count("op1_attr2") == 0 ||
!attributes.at("op1_attr2").isa<pir::StrAttribute>()) {
PADDLE_THROW(common::errors::Fatal(
"Type of attribute: parameter_name is not right."));
}
}
void TraitExampleOp::Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
argument.AddOutput(out_type);
}
void SameOperandsShapeTraitOp2::Build(
pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
argument.AddOutput(out_type);
}
void SameOperandsAndResultShapeTraitOp2::Build(
pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
}
void SameOperandsAndResultShapeTraitOp3::Build(
pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
argument.AddOutput(out_type);
}
void SameOperandsElementTypeTraitOp2::Build(
pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
argument.AddOutput(out_type);
}
void SameOperandsAndResultElementTypeTraitOp2::Build(
pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
}
void SameOperandsAndResultElementTypeTraitOp3::Build(
pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type1,
pir::Type out_type2) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
argument.AddOutput(out_type1);
argument.AddOutput(out_type2);
}
void SameOperandsAndResultTypeTraitOp2::Build(
pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
}
void SameOperandsAndResultTypeTraitOp3::Build(
pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type1,
pir::Type out_type2) {
argument.AddInput(l_operand);
argument.AddInput(r_operand);
argument.AddOutput(out_type1);
argument.AddOutput(out_type2);
}
} // namespace test
IR_DEFINE_EXPLICIT_TYPE_ID(test::RegionOp)
IR_DEFINE_EXPLICIT_TYPE_ID(test::BranchOp)
IR_DEFINE_EXPLICIT_TYPE_ID(test::Operation1)
IR_DEFINE_EXPLICIT_TYPE_ID(test::Operation2)
IR_DEFINE_EXPLICIT_TYPE_ID(test::TraitExampleOp)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsShapeTraitOp1)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsShapeTraitOp2)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultShapeTraitOp1)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultShapeTraitOp2)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultShapeTraitOp3)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsElementTypeTraitOp1)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsElementTypeTraitOp2)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultElementTypeTraitOp1)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultElementTypeTraitOp2)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultElementTypeTraitOp3)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultTypeTraitOp1)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultTypeTraitOp2)
IR_DEFINE_EXPLICIT_TYPE_ID(test::SameOperandsAndResultTypeTraitOp3)
namespace test1 {
const char *Operation1::attributes_name[2] = {"op1_attr1", // NOLINT
"op1_attr3"}; // NOLINT
void Operation1::Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) { // NOLINT
std::unordered_map<std::string, pir::Attribute> attributes{
{"op1_attr1", builder.str_attr("op1_attr1")},
{"op1_attr3", builder.str_attr("op1_attr3")}};
argument.AddOutput(builder.float32_type());
argument.AddAttributes(attributes);
}
void Operation1::VerifySig() const {
auto &attributes = this->attributes();
if (attributes.count("op1_attr1") == 0 ||
!attributes.at("op1_attr1").isa<pir::StrAttribute>()) {
PADDLE_THROW(common::errors::Fatal(
"Type of attribute: parameter_name is not right."));
}
if (attributes.count("op1_attr3") == 0 ||
!attributes.at("op1_attr3").isa<pir::StrAttribute>()) {
PADDLE_THROW(common::errors::Fatal(
"Type of attribute: parameter_name is not right."));
}
}
void Operation2::Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) { // NOLINT
argument.AddOutput(builder.float32_type());
}
void Operation3::Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument,
pir::Value l_operand,
pir::Value r_operand) { // NOLINT
argument.AddInput(l_operand);
argument.AddInput(r_operand);
}
void Operation4::Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) { // NOLINT
argument.AddOutput(builder.float32_type());
argument.AddOutput(builder.float32_type());
}
} // namespace test1
IR_DEFINE_EXPLICIT_TYPE_ID(test1::Operation1)
IR_DEFINE_EXPLICIT_TYPE_ID(test1::Operation2)
IR_DEFINE_EXPLICIT_TYPE_ID(test1::Operation3)
IR_DEFINE_EXPLICIT_TYPE_ID(test1::Operation4)
+401
View File
@@ -0,0 +1,401 @@
// Copyright (c) 2023 PaddlePaddle 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 <glog/logging.h>
#include "paddle/pir/include/core/builder.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/op_base.h"
#include "paddle/pir/include/core/op_trait.h"
#include "paddle/pir/include/core/operation_utils.h"
#include "test/cpp/pir/tools/macros_utils.h"
#include "test/cpp/pir/tools/test_interface.h"
#include "test/cpp/pir/tools/test_trait.h"
namespace test {
///
/// \brief TestRegionOp
///
class RegionOp : public pir::Op<RegionOp, OneRegionTrait> {
public:
using Op::Op;
static const char *name() { return "test.region"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument); // NOLINT
void VerifySig() const {}
};
///
/// \brief TestBranchOp
///
class BranchOp : public pir::Op<BranchOp> {
public:
using Op::Op;
static const char *name() { return "test.branch"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
const std::vector<pir::Value> &target_operands,
pir::Block *target);
void VerifySig() const;
};
// Define case op1.
class Operation1 : public pir::Op<Operation1> {
public:
using Op::Op;
static const char *name() { return "test.operation1"; }
static constexpr uint32_t attributes_num = 2;
static const char *attributes_name[attributes_num];
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument); // NOLINT
void VerifySig() const;
};
// Define op2.
class Operation2
: public pir::Op<Operation2, ReadOnlyTrait, InferShapeInterface> {
public:
using Op::Op;
static const char *name() { return "test.operation2"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) {} // NOLINT
void VerifySig() const {}
static void InferShape() { VLOG(2) << "This is op2's InferShape interface."; }
};
// Define TraitExampleOp.
class TraitExampleOp
: public pir::Op<TraitExampleOp,
pir::SameOperandsShapeTrait,
pir::SameOperandsAndResultShapeTrait,
pir::SameOperandsElementTypeTrait,
pir::SameOperandsAndResultElementTypeTrait,
pir::SameOperandsAndResultTypeTrait,
pir::SameTypeOperandsTrait> {
public:
using Op::Op;
static const char *name() { return "test.trait_example_op"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type);
void VerifySig() const {}
};
// Define SameOperandsShapeTraitOp1.
class SameOperandsShapeTraitOp1
: public pir::Op<SameOperandsShapeTraitOp1, pir::SameOperandsShapeTrait> {
public:
using Op::Op;
static const char *name() { return "test.same_operands_shape_op1"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) {} // NOLINT
void VerifySig() const {}
};
// Define SameOperandsShapeTraitOp2.
class SameOperandsShapeTraitOp2
: public pir::Op<SameOperandsShapeTraitOp2, pir::SameOperandsShapeTrait> {
public:
using Op::Op;
static const char *name() { return "test.same_operands_shape_op2"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type);
void VerifySig() const {}
};
// Define SameOperandsAndResultShapeTraitOp1.
class SameOperandsAndResultShapeTraitOp1
: public pir::Op<SameOperandsAndResultShapeTraitOp1,
pir::SameOperandsAndResultShapeTrait> {
public:
using Op::Op;
static const char *name() {
return "test.same_operands_and_result_shape_op1";
}
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) {} // NOLINT
void VerifySig() const {}
};
// Define SameOperandsAndResultShapeTraitOp2.
class SameOperandsAndResultShapeTraitOp2
: public pir::Op<SameOperandsAndResultShapeTraitOp2,
pir::SameOperandsAndResultShapeTrait> {
public:
using Op::Op;
static const char *name() {
return "test.same_operands_and_result_shape_op2";
}
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand);
void VerifySig() const {}
};
// Define SameOperandsAndResultShapeTraitOp3.
class SameOperandsAndResultShapeTraitOp3
: public pir::Op<SameOperandsAndResultShapeTraitOp3,
pir::SameOperandsAndResultShapeTrait> {
public:
using Op::Op;
static const char *name() {
return "test.same_operands_and_result_shape_op3";
}
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type);
void VerifySig() const {}
};
// Define SameOperandsElementTypeTraitOp1.
class SameOperandsElementTypeTraitOp1
: public pir::Op<SameOperandsElementTypeTraitOp1,
pir::SameOperandsElementTypeTrait> {
public:
using Op::Op;
static const char *name() { return "test.same_operands_element_type_op1"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) {} // NOLINT
void VerifySig() const {}
};
// Define SameOperandsElementTypeTraitOp2.
class SameOperandsElementTypeTraitOp2
: public pir::Op<SameOperandsElementTypeTraitOp2,
pir::SameOperandsElementTypeTrait> {
public:
using Op::Op;
static const char *name() { return "test.same_operands_element_type_op1"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type);
void VerifySig() const {}
};
// Define SameOperandsAndResultElementTypeTraitOp1.
class SameOperandsAndResultElementTypeTraitOp1
: public pir::Op<SameOperandsAndResultElementTypeTraitOp1,
pir::SameOperandsAndResultElementTypeTrait> {
public:
using Op::Op;
static const char *name() {
return "test.same_operands_and_result_element_type_op1";
}
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) {} // NOLINT
void VerifySig() const {}
};
// Define SameOperandsAndResultElementTypeTraitOp2.
class SameOperandsAndResultElementTypeTraitOp2
: public pir::Op<SameOperandsAndResultElementTypeTraitOp2,
pir::SameOperandsAndResultElementTypeTrait> {
public:
using Op::Op;
static const char *name() {
return "test.same_operands_and_result_element_type_op2";
}
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand);
void VerifySig() const {}
};
// Define SameOperandsAndResultElementTypeTraitOp3.
class SameOperandsAndResultElementTypeTraitOp3
: public pir::Op<SameOperandsAndResultElementTypeTraitOp3,
pir::SameOperandsAndResultElementTypeTrait> {
public:
using Op::Op;
static const char *name() {
return "test.same_operands_and_result_element_type_op3";
}
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type1,
pir::Type out_type2);
void VerifySig() const {}
};
// Define SameOperandsAndResultTypeTraitOp1.
class SameOperandsAndResultTypeTraitOp1
: public pir::Op<SameOperandsAndResultTypeTraitOp1,
pir::SameOperandsAndResultTypeTrait> {
public:
using Op::Op;
static const char *name() { return "test.same_operands_and_result_type_op1"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument) {} // NOLINT
void VerifySig() const {}
};
// Define SameOperandsAndResultTypeTraitOp2.
class SameOperandsAndResultTypeTraitOp2
: public pir::Op<SameOperandsAndResultTypeTraitOp2,
pir::SameOperandsAndResultTypeTrait> {
public:
using Op::Op;
static const char *name() { return "test.same_operands_and_result_type_op2"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand);
void VerifySig() const {}
};
// Define SameOperandsAndResultTypeTraitOp3.
class SameOperandsAndResultTypeTraitOp3
: public pir::Op<SameOperandsAndResultTypeTraitOp3,
pir::SameOperandsAndResultTypeTrait> {
public:
using Op::Op;
static const char *name() { return "test.same_operands_and_result_type_op3"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand,
pir::Type out_type1,
pir::Type out_type2);
void VerifySig() const {}
};
} // namespace test
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::RegionOp)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::BranchOp)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::Operation1)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::Operation2)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::TraitExampleOp)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsShapeTraitOp1)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsShapeTraitOp2)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultShapeTraitOp1)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultShapeTraitOp2)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultShapeTraitOp3)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsElementTypeTraitOp1)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsElementTypeTraitOp2)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultElementTypeTraitOp1)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultElementTypeTraitOp2)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultElementTypeTraitOp3)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultTypeTraitOp1)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultTypeTraitOp2)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::SameOperandsAndResultTypeTraitOp3)
namespace test1 {
// Define case op1.
class Operation1 : public pir::Op<Operation1> {
public:
using Op::Op;
static const char *name() { return "test1.operation1"; }
static constexpr uint32_t attributes_num = 2;
static const char *attributes_name[attributes_num];
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument); // NOLINT
void VerifySig() const;
};
// Define op2.
class Operation2 : public pir::Op<Operation2> {
public:
using Op::Op;
static const char *name() { return "test1.operation2"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument); // NOLINT
void VerifySig() const {}
static void InferShape() { VLOG(2) << "This is op2's InferShape interface."; }
};
// Define op3.
class Operation3 : public pir::Op<Operation3> {
public:
using Op::Op;
static const char *name() { return "test1.operation3"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument, // NOLINT
pir::Value l_operand,
pir::Value r_operand);
void VerifySig() const {}
static void InferShape() { VLOG(2) << "This is op3's InferShape interface."; }
};
// Define op4.
class Operation4 : public pir::Op<Operation4> {
public:
using Op::Op;
static const char *name() { return "test1.operation4"; }
static constexpr uint32_t attributes_num = 0;
static constexpr const char **attributes_name = nullptr;
static void Build(pir::Builder &builder, // NOLINT
pir::OperationArgument &argument); // NOLINT
void VerifySig() const {}
};
} // namespace test1
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test1::Operation1)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test1::Operation2)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test1::Operation3)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test1::Operation4)
+56
View File
@@ -0,0 +1,56 @@
// Copyright (c) 2023 PaddlePaddle 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 "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/dialect/shape/utils/shape_analysis.h"
namespace test {
pir::AttributeMap CreateAttributeMap(
const std::vector<std::string> &attribute_names,
const std::vector<std::string> &attributes) {
pir::IrContext *ctx = pir::IrContext::Instance();
pir::AttributeMap attr_map;
for (size_t i = 0; i < attribute_names.size(); i++) {
pir::Attribute attr_value = pir::StrAttribute::get(ctx, attributes[i]);
attr_map.insert(
std::pair<std::string, pir::Attribute>(attribute_names[i], attr_value));
}
return attr_map;
}
pir::Operation *CreateDenseTensorOp(
pir::IrContext *ctx,
const phi::DDim &dims,
const std::vector<std::string> &attribute_names,
const std::vector<std::string> &attributes,
const pir::Type &dtype =
pir::Float32Type::get(pir::IrContext::Instance())) {
std::vector<pir::Value> op_inputs = {};
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {{0, 1, 2}};
size_t offset = 0;
std::vector<pir::Type> op_output_types = {
paddle::dialect::DenseTensorType::get(
ctx, dtype, dims, data_layout, lod, offset)};
return pir::Operation::Create(op_inputs,
CreateAttributeMap(attribute_names, attributes),
op_output_types,
pir::OpInfo());
}
} // namespace test
+32
View File
@@ -0,0 +1,32 @@
// Copyright (c) 2023 PaddlePaddle 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 "test/cpp/pir/tools/test_trait.h"
#include "glog/logging.h"
#include "paddle/common/enforce.h"
namespace test {
void OneRegionTrait::Verify(pir::Operation *op) {
VLOG(1) << "here";
PADDLE_ENFORCE_EQ(op->num_regions(),
1u,
common::errors::InvalidArgument(
"%s op has one region trait, but its region size is %u",
op->name(),
op->num_regions()));
}
} // namespace test
IR_DEFINE_EXPLICIT_TYPE_ID(test::ReadOnlyTrait)
IR_DEFINE_EXPLICIT_TYPE_ID(test::OneRegionTrait)
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) 2023 PaddlePaddle 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 <gtest/gtest.h>
#include <sstream>
#include "paddle/pir/include/core/op_base.h"
#include "test/cpp/pir/tools/macros_utils.h"
namespace test {
class ReadOnlyTrait : public pir::OpTraitBase<ReadOnlyTrait> {
public:
explicit ReadOnlyTrait(const pir::Operation *op)
: pir::OpTraitBase<ReadOnlyTrait>(op) {}
};
class OneRegionTrait : public pir::OpTraitBase<OneRegionTrait> {
public:
explicit OneRegionTrait(const pir::Operation *op)
: pir::OpTraitBase<OneRegionTrait>(op) {}
static void Verify(pir::Operation *op);
};
} // namespace test
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::ReadOnlyTrait)
IR_DECLARE_EXPLICIT_TEST_TYPE_ID(test::OneRegionTrait)