chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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)
|
||||
@@ -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())));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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。"));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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({});
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 ®ion = 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");
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 ®ion = 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());
|
||||
}
|
||||
}
|
||||
@@ -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>());
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>());
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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}));
|
||||
}
|
||||
@@ -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 ®ion = (*this)->region(0);
|
||||
if (region.empty()) region.emplace_back();
|
||||
return ®ion.front();
|
||||
}
|
||||
|
||||
pir::Block *FakeEngineOp::block() const {
|
||||
pir::Region ®ion = (*this)->region(0);
|
||||
PADDLE_ENFORCE_EQ(region.empty(),
|
||||
false,
|
||||
::common::errors::Unavailable(
|
||||
"Required CustomEngineOp's region must not be empty."));
|
||||
return ®ion.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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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 ®ion = 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);
|
||||
}
|
||||
@@ -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, 7,1, 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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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}>)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user