Files
2026-07-13 12:40:42 +08:00

173 lines
5.9 KiB
C++

// 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.
#pragma once
#include <string>
#include <unordered_set>
#include <vector>
#include "paddle/cinn/backends/codegen_c.h"
#include "paddle/cinn/common/common.h"
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/lowered_func.h"
#include "paddle/cinn/ir/module.h"
#include "paddle/cinn/lang/packed_func.h"
#include "paddle/cinn/runtime/cinn_runtime.h"
namespace cinn::ir {
class Module;
} // namespace cinn::ir
namespace cinn {
namespace backends {
/**
* CUDA/HIP device code generator.
*
* It generates the device function, e.g, the function called "myadd" will have
* a __global__ function called "myadd_kernel", different from codegen_c, the
* declaration of the "myadd_kernel" function has an expanded argument list,
* which finally similar to `__global__ void myadd(float* __restrict__ A, float*
* __restrict__ B, int n);`
*/
class CodeGenGpuDev : public CodeGenC {
public:
explicit CodeGenGpuDev(Target target);
/**
* Compile the \p module to \p outputs.
*/
void Compile(const ir::Module& module, const Outputs& outputs);
//! Compile on RTC (RunTime Compilation).
std::string Compile(const ir::Module& module, bool use_rtc = true);
void Compile(const ir::LoweredFunc& func);
/**
* \brief Print a function argument in cuda/hip syntax. Currently, just some
* decoration of __restrict__.
* @param arg the argument.
* @return the representation in cuda/hip syntax.
*
* We make it a static to make the test easier.
*/
virtual void PrintFuncArg(const ir::Argument& arg);
std::string Compile(const ir::Module& module, OutputKind output_kind);
ir::Expr GetDynSharedMemOffset() const {
if (MathEqual(dyn_shared_mem_offset_, Expr(-1))) {
return Expr(0);
}
return dyn_shared_mem_offset_;
}
protected:
void Visit(const ir::_Var_* op) override;
void Visit(const ir::_LoweredFunc_* op) override;
void Visit(const ir::Min* op) override;
void Visit(const ir::Max* op) override;
void Visit(const ir::Call* op) override;
void Visit(const ir::Load* op) override;
void VisitStmt(const ir::stmt::Free& stmt) override;
void VisitStmt(const ir::stmt::Alloc& stmt) override;
void VisitStmt(const ir::stmt::Store& op) override;
void VisitStmt(const ir::stmt::Let& stmt) override;
// Print element access at a cuda/hip built-in vector on a load/store node
bool PrintBuiltinVectorAccess(const ir::LoadStoreAddrMnger* op,
ir::Expr index,
bool is_store);
// Print element access at a cuda/hip built-in vector on a store node
bool PrintBuiltinVectorAccess(const ir::stmt::Store& stmt,
ir::Expr index,
bool is_store);
void PrintBuiltinCodes();
virtual void PrintIncludes() = 0;
virtual void PrintTempBufferCreation(const ir::Buffer& buffer);
void PrintTempBufferAliasDefinition(const ir::Buffer& buffer);
std::vector<ir::stmt::StmtRef> GenerateBufferAliasStmts(
const ir::_LoweredFunc_* op, const std::vector<ir::Buffer>& temp_buffers);
std::vector<ir::stmt::StmtRef> FilterDeallocTempBuffers(
const std::vector<ir::stmt::StmtRef>& frees);
/**
* Print the function declaration, this is different from C, we expand the
* arguments and get something like
* `__global__ void myadd(float* __restrict__ A, float* __restrict__ B, int
* n);`
*/
virtual void PrintFunctionDeclaration(const ir::_LoweredFunc_* op);
inline void ProcessMinMaxOperand(ir::Expr* a,
ir::Expr* b,
int unify_bit,
bool both_dyn) {
if (unify_bit > 0) {
std::string type_func = "int" + std::to_string(unify_bit) + "_t";
if (both_dyn) {
// if both contains dynamic symbol, like: min(S0, S1), it it likely that
// S0 is int and S1 is int64_t. So we need to enforce the type cast by
// ir::Call
*a = ir::Call::Make(common::Int(unify_bit),
type_func,
{*a},
{},
ir::CallType::Intrinsic);
*b = ir::Call::Make(common::Int(unify_bit),
type_func,
{*b},
{},
ir::CallType::Intrinsic);
} else {
*a = ir::Cast::Make(common::Int(unify_bit), *a);
*b = ir::Cast::Make(common::Int(unify_bit), *b);
}
}
}
std::unordered_map<std::string, common::Type>& DynamicShapeMap() {
return dynamic_shape_map_;
}
private:
Target target_;
bool use_rtc_{false};
// names of vectorized tensors from `Let` statements where dtypes of the
// tensors are customized_type with customized_type::k_builtin_vector_t
// prefix
std::unordered_set<std::string> vectorized_tensor_names_;
// This map is used to store the name and type of the dynamic shape func args
// so that during codegen of ir::Min & ir::Max call, we can have correct type
std::unordered_map<std::string, common::Type> dynamic_shape_map_;
ir::Expr dyn_shared_mem_offset_{-1};
std::vector<ir::Buffer> dynamic_alloc_buffers_;
};
ir::Expr CalculateSharedMemory(const ir::LoweredFunc& func);
} // namespace backends
} // namespace cinn