Files
paddlepaddle--paddle/test/cpp/pir/pattern_rewrite/pattern_rewrite_test.cc
T
2026-07-13 12:40:42 +08:00

645 lines
26 KiB
C++

// 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);
}