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

125 lines
4.0 KiB
C++

// Copyright (c) 2021 CINN Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/cinn/optim/lower_intrin.h"
#include <string>
#include "paddle/cinn/backends/llvm/llvm_intrin_rule.h"
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/intrinsic_ops.h"
#include "paddle/cinn/ir/ir_mutator.h"
#include "paddle/cinn/ir/registry.h"
namespace cinn {
namespace optim {
template <typename T>
void LowerIntrinImpl(const T &, const Target &target, ir::Expr *expr) {
// Do nothing.
}
void LowerIntrinImpl(common::X86Arch, const Target &target, ir::Expr *expr) {
codegen::RegisterCpuIntrinRule();
struct Mutator : ir::IRMutator<Expr *> {
Target target;
explicit Mutator(Target target) : target(target) {}
void operator()(ir::Expr *expr) {
ir::IRMutator<ir::Expr *>::Visit(expr, expr);
}
void Visit(const ir::Add *op, Expr *expr) override {
auto *node = expr->As<ir::Add>();
PADDLE_ENFORCE_NOT_NULL(node, "The node can not be treat as a Add node.");
Expr ret;
if (node->type().is_float()) {
if (const ir::Mul *mul = node->b().As<ir::Mul>()) {
ret = ir::Call::Make(node->type(),
"fma",
{mul->a(), mul->b(), node->a()},
{},
ir::CallType::Intrinsic);
} else if (const ir::Mul *mul = node->a().As<ir::Mul>()) {
ret = ir::Call::Make(node->type(),
"fma",
{mul->a(), mul->b(), node->b()},
{},
ir::CallType::Intrinsic);
}
if (ret.defined()) {
ir::IRMutator<>::Visit(&ret, &ret);
*expr = ret;
return;
}
}
ir::IRMutator<>::Visit(&node->a(), &node->a());
ir::IRMutator<>::Visit(&node->b(), &node->b());
}
void Visit(const ir::Call *op, Expr *expr) override {
auto *node = expr->As<ir::Call>();
PADDLE_ENFORCE_NOT_NULL(node,
"The node can not be treat as a Call node.");
LowerCpuIntrinsicOp(node, expr);
}
void LowerCpuIntrinsicOp(ir::Call *op, Expr *expr) {
auto *node = expr->As<ir::Call>();
if (kIntrinsicCalls.count(node->name)) {
PADDLE_ENFORCE_EQ(
!node->name.empty(),
true,
::common::errors::InvalidArgument("The node name is empty."));
auto *func_ptr = ir::Registry::Get("lower_cpu_intrinsic_" + node->name);
PADDLE_ENFORCE_NOT_NULL(
func_ptr,
::common::errors::InvalidArgument(
"find no rule to lower cpu intrinsic for lower_cpu_intrinsic_" +
node->name));
Expr ret = (*func_ptr)(Expr(node));
if (!ret.same_as(*expr)) {
ir::IRMutator<>::Visit(&ret, &ret);
}
*expr = ret;
return;
}
for (auto &expr : node->read_args) {
ir::IRMutator<>::Visit(&expr, &expr);
}
for (auto &expr : node->write_args) {
ir::IRMutator<>::Visit(&expr, &expr);
}
}
};
Mutator m(target);
m(expr);
}
void LowerIntrinByArch(ir::Expr *expr, const Target &target) {
return std::visit(
[&](const auto &impl) { return LowerIntrinImpl(impl, target, expr); },
target.arch.variant());
}
void LowerIntrin(ir::Expr *expr, Target target) {
return LowerIntrinByArch(expr, target);
}
} // namespace optim
} // namespace cinn