// 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/backends/codegen_c.h" #include #include #include "paddle/cinn/backends/extern_func_emitter.h" #include "paddle/cinn/backends/extern_func_emitter_builtin.h" #include "paddle/cinn/ir/lowered_func.h" #include "paddle/cinn/ir/op/ir_operators.h" #include "paddle/cinn/ir/utils/ir_verify.h" #include "paddle/cinn/optim/ir_simplify.h" #include "paddle/cinn/runtime/cpu/thread_backend.h" #include "paddle/cinn/runtime/intrinsic.h" #include "paddle/cinn/utils/string.h" #include "paddle/common/enforce.h" //! Root of the builtin code. PD_DECLARE_string(cinn_x86_builtin_code_root); namespace cinn { namespace backends { using namespace utils; // NOLINT using cinn::common::float16; const char *kCKeywordRestrict = "__restrict__"; void CodeGenC::Compile(const ir::Module &module, const Outputs &outputs) { ir::ir_utils::IrVerify(module.As()); if (!outputs.c_header_name.empty()) { auto source = Compile(module, OutputKind::CHeader); str_ = ""; std::ofstream file(outputs.c_header_name); PADDLE_ENFORCE_EQ(file.is_open(), true, ::common::errors::InvalidArgument( "failed to open file %s", outputs.c_header_name)); file << source; file.close(); LOG(WARNING) << "Output C header to file " << outputs.c_header_name; } if (!outputs.c_source_name.empty()) { auto source = Compile(module, OutputKind::CImpl); str_ = ""; std::ofstream file(outputs.c_source_name); PADDLE_ENFORCE_EQ(file.is_open(), true, ::common::errors::InvalidArgument( "failed to open file %s", outputs.c_source_name)); file << source; file.close(); LOG(WARNING) << "Output C source to file " << outputs.c_source_name; } } CodeGenC::CodeGenC(Target target) : ir::IrPrinter(ss_) {} std::string CodeGenC::Compile(const ir::Module &module, OutputKind output_kind) { if (output_kind == OutputKind::CHeader) { GenerateHeaderFile(module); } else if (output_kind == OutputKind::CImpl) { PrintIncludes(); if (inline_builtin_codes_) PrintBuiltinCodes(); for (auto &func : module.functions()) { Compile(func); } } else { PADDLE_THROW(::common::errors::Unimplemented("Not supported OutputKind")); } return str_; } // TODO(LiuYang): Here the Ret type seems unuseful void CodeGenC::Compile(const ir::LoweredFunc &function) { PADDLE_ENFORCE_EQ( function.defined(), true, ::common::errors::InvalidArgument("The function is not defined.")); IrPrinter::Visit(function); str_ += "\n\n"; } std::string CodeGenC::GetTypeName(Type type) { // common scalar type #define GET_SCALAR_TYPE(pred_expr, scalar_name) \ if (pred_expr) { \ return scalar_name; \ } GET_SCALAR_TYPE(type.is_void(), "void"); GET_SCALAR_TYPE(type.is_bool(), "bool"); GET_SCALAR_TYPE(type.is_int(8), "int8_t"); GET_SCALAR_TYPE(type.is_int(16), "int16_t"); GET_SCALAR_TYPE(type.is_int(32), "int32_t"); GET_SCALAR_TYPE(type.is_int(64), "int64_t"); GET_SCALAR_TYPE(type.is_uint(8), "uint8_t"); GET_SCALAR_TYPE(type.is_uint(16), "uint16_t"); GET_SCALAR_TYPE(type.is_uint(32), "uint32_t"); GET_SCALAR_TYPE(type.is_uint(64), "uint64_t"); GET_SCALAR_TYPE(type.is_float8e4m3(), "float8e4m3"); GET_SCALAR_TYPE(type.is_bfloat16(), "bfloat16"); GET_SCALAR_TYPE(type.is_float16(), "float16"); GET_SCALAR_TYPE(type.is_float(32), "float") GET_SCALAR_TYPE(type.is_float(64), "double") #undef GET_SCALAR_TYPE // customized_type if (type.is_customized_type()) { PADDLE_ENFORCE_EQ(type.customized_type().empty(), false, ::common::errors::InvalidArgument( "customized_type is empty. It can't be empty.")); auto customized_name = type.customized_type(); // get name of a cuda built-in vector type, it is started with a // 'CudaVectorType::' prefix if (utils::StartsWith( customized_name, cinn::common::customized_type::kcuda_builtin_vector_t)) { customized_name.erase( 0, strlen(cinn::common::customized_type::kcuda_builtin_vector_t)); } return customized_name; } // other types are not implemented yet CINN_NOT_IMPLEMENTED return ""; } std::string CodeGenC::GetTypeRepr(Type type) { std::string str; if (type.is_cpp_const()) { str = "const "; } str += GetTypeName(type); if (type.is_cpp_handle()) { str += "*"; } else if (type.is_cpp_handle2()) { str += "**"; } return str; } void CodeGenC::Visit(const ir::IntImm *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::UIntImm *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::FloatImm *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::StringImm *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Add *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Sub *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Mul *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Div *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Mod *op) { auto copied = op->b(); copied = optim::ArithSimplify(copied); if (copied.is_constant()) { int temp = static_cast(copied.get_constant()); if ((temp & (temp - 1)) == 0) { str_ += "("; IrPrinter::Visit(op->a()); str_ += " & "; str_ += std::to_string(temp - 1); str_ += ")"; return; } } PrintBinaryOp("%", op); } void CodeGenC::Visit(const ir::EQ *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::NE *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::LT *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::LE *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::GT *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::GE *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::And *op) { PrintBinaryOp("&&", op); } void CodeGenC::Visit(const ir::Or *op) { PrintBinaryOp("||", op); } void CodeGenC::Visit(const ir::Min *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Max *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Minus *op) { IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Not *op) { str_ += "(!"; IrPrinter::Visit(op->v()); str_ += ")"; } void CodeGenC::Visit(const ir::Cast *op) { PrintCastExpr(op->type(), op->v()); } void CodeGenC::Visit(const ir::For *op) { Expr extent = op->extent; Expr min = op->min; int num_task = 1; if (op->is_parallel()) { str_ += "int num_task = max_concurrency();\n"; DoIndent(); str_ += "omp_set_num_threads(num_task);\n"; DoIndent(); str_ += "auto flambda = [=](int task_id, int num_task) -> int {\n"; IncIndent(); DoIndent(); str_ += "int n_per_task = "; Expr num_task_var = Var("num_task"); IrPrinter::Visit((op->extent + num_task_var - 1) / num_task_var); str_ += ";\n"; PADDLE_ENFORCE_EQ(min.as_int32(), 0, ::common::errors::InvalidArgument( "The min of the for loop should be 0")); auto task_id = Var("task_id"); auto n_per_task = Var("n_per_task"); min = task_id * n_per_task; extent = (task_id + 1) * n_per_task; DoIndent(); } str_ += "for ("; str_ += GetTypeRepr(Int(32)); str_ += " "; str_ += op->loop_var->name; str_ += " = "; IrPrinter::Visit(min); str_ += "; "; str_ += op->loop_var->name; str_ += " < "; IrPrinter::Visit(op->extent); if (op->is_parallel()) { str_ += " && "; str_ += op->loop_var->name; str_ += " < "; IrPrinter::Visit(extent); } str_ += "; "; str_ += op->loop_var->name; str_ += " += 1"; str_ += ") "; IrPrinter::Visit(op->body); if (op->is_parallel()) { str_ += "\n"; DoIndent(); str_ += "return 0;\n"; DecIndent(); DoIndent(); str_ += "};\n"; str_ += "#pragma omp parallel num_threads(num_task)\n"; DoIndent(); str_ += "{\n"; IncIndent(); DoIndent(); str_ += "int task_id = omp_get_thread_num();\n"; DoIndent(); str_ += "flambda(task_id, num_task);\n"; DecIndent(); DoIndent(); str_ += "}"; } } void CodeGenC::VisitStmt(const ir::stmt::For &stmt) { Expr extent = stmt->extent(); Expr min = stmt->min(); int num_task = 1; if (stmt->is_parallel()) { str_ += "int num_task = max_concurrency();\n"; DoIndent(); str_ += "omp_set_num_threads(num_task);\n"; DoIndent(); str_ += "auto flambda = [=](int task_id, int num_task) -> int {\n"; IncIndent(); DoIndent(); str_ += "int n_per_task = "; Expr num_task_var = Var("num_task"); IrPrinter::Visit((stmt->extent() + num_task_var - 1) / num_task_var); str_ += ";\n"; PADDLE_ENFORCE_EQ(min.as_int32(), 0, ::common::errors::InvalidArgument( "The min of the for loop should be 0")); auto task_id = Var("task_id"); auto n_per_task = Var("n_per_task"); min = task_id * n_per_task; extent = (task_id + 1) * n_per_task; DoIndent(); } str_ += "for ("; str_ += GetTypeRepr(Int(32)); str_ += " "; str_ += stmt->loop_var()->name; str_ += " = "; IrPrinter::Visit(min); str_ += "; "; str_ += stmt->loop_var()->name; str_ += " < "; IrPrinter::Visit(stmt->extent()); if (stmt->is_parallel()) { str_ += " && "; str_ += stmt->loop_var()->name; str_ += " < "; IrPrinter::Visit(extent); } str_ += "; "; str_ += stmt->loop_var()->name; str_ += " += 1"; str_ += ") "; VisitBlock(stmt->body()); if (stmt->is_parallel()) { str_ += "\n"; DoIndent(); str_ += "return 0;\n"; DecIndent(); DoIndent(); str_ += "};\n"; str_ += "#pragma omp parallel num_threads(num_task)\n"; DoIndent(); str_ += "{\n"; IncIndent(); DoIndent(); str_ += "int task_id = omp_get_thread_num();\n"; DoIndent(); str_ += "flambda(task_id, num_task);\n"; DecIndent(); DoIndent(); str_ += "}"; } } void CodeGenC::Visit(const ir::PolyFor *op) { str_ += "for ("; str_ += GetTypeRepr(Int(32)); str_ += " "; str_ += op->iterator->name; str_ += " = "; IrPrinter::Visit(op->init); str_ += "; "; IrPrinter::Visit(op->condition); str_ += "; "; str_ += op->iterator->name; str_ += " += "; IrPrinter::Visit(op->inc); str_ += ") "; IrPrinter::Visit(op->body); } void CodeGenC::Visit(const ir::Select *op) { str_ += "("; str_ += "("; IrPrinter::Visit(op->condition); str_ += ") ? "; IrPrinter::Visit(op->true_value); str_ += " : "; IrPrinter::Visit(op->false_value); str_ += ")"; } void CodeGenC::Visit(const ir::IfThenElse *op) { str_ += "if ("; IrPrinter::Visit(op->condition); str_ += ") "; IrPrinter::Visit(op->true_case); if (op->false_case.defined()) { str_ += " else "; IrPrinter::Visit(op->false_case); } } void CodeGenC::VisitStmt(const ir::stmt::IfThenElse &stmt) { str_ += "if ("; IrPrinter::Visit(stmt->condition()); str_ += ") "; VisitBlock(stmt->true_case()); if (!stmt->false_case()->stmts().empty()) { str_ += " else "; VisitBlock(stmt->false_case()); } } void CodeGenC::Visit(const ir::Block *op) { str_ += "{\n"; IncIndent(); // Note: size_t (0 - 1) = 18446744073709551615 if (op->stmts.size() >= 1) { for (int i = 0; i < op->stmts.size() - 1; i++) { DoIndent(); IrPrinter::Visit(op->stmts[i]); str_ += ";\n"; } DoIndent(); IrPrinter::Visit(op->stmts.back()); str_ += ";"; } DecIndent(); str_ += "\n"; DoIndent(); str_ += "}"; } void CodeGenC::VisitBlock(const ir::stmt::BlockRef &stmt) { str_ += "{\n"; IncIndent(); // Note: size_t (0 - 1) = 18446744073709551615 if (stmt->stmts().size() >= 1) { for (int i = 0; i < stmt->stmts().size() - 1; i++) { DoIndent(); IrPrinter::VisitStmt(stmt->stmts()[i]); str_ += ";\n"; } DoIndent(); IrPrinter::VisitStmt(stmt->stmts().back()); str_ += ";"; } DecIndent(); str_ += "\n"; DoIndent(); str_ += "}"; } void CodeGenC::Visit(const ir::Call *op) { if (op->name == runtime::intrinsic::buffer_malloc) { PrintCall_buffer_malloc(op); } else if (op->name == runtime::intrinsic::pod_values_to_array_repr) { PrintCall_pod_values_to_array(op); } else if (op->is_intrinsic_call()) { str_ += op->name; str_ += "("; PrintCallArgs(op); str_ += ")"; } else if (op->is_cinn_call()) { // call CINN LoweredFunc str_ += op->name; str_ += "("; PrintCallArgs(op); str_ += ")"; } else if (op->is_extern_call()) { const auto &fn_name = ExternFunctionEmitterRegistry::Global().Lookup( ExternFuncID{backend_C, op->name.c_str()}); if (!fn_name.empty()) { ExternFunctionLLVMEmitter emitter(fn_name); emitter.BindCodeGen(this); emitter.Emit(op); } else { if (op->read_args.empty()) PADDLE_ENFORCE_EQ( op->read_args.empty(), false, ::common::errors::InvalidArgument( "Either read_args or write_args must not be empty.")); str_ += op->name; str_ += "("; PrintCallArgs(op); str_ += ")"; } } else { CINN_NOT_IMPLEMENTED } } void CodeGenC::PrintCallArgs(const ir::Call *op) { if (!op->read_args.empty()) { for (int i = 0; i < op->read_args.size() - 1; i++) { IrPrinter::Visit(op->read_args[i]); str_ += ", "; } IrPrinter::Visit(op->read_args.back()); } if (!op->write_args.empty()) { if (!op->read_args.empty()) str_ += ", "; for (int i = 0; i < op->write_args.size() - 1; i++) { IrPrinter::Visit(op->write_args[i]); str_ += ", "; } IrPrinter::Visit(op->write_args.back()); } } void CodeGenC::PrintCall_buffer_malloc(const ir::Call *op) { PADDLE_ENFORCE_EQ( op->read_args.size(), 2UL, ::common::errors::InvalidArgument("The number of read_args should be 2")); str_ += op->name; str_ += "("; PrintCastExpr("void*", op->read_args[0]); str_ += ", "; IrPrinter::Visit(op->read_args[1]); str_ += ")"; } void CodeGenC::PrintCall_cinn_pod_value_to_(const ir::Call *op) { PADDLE_ENFORCE_EQ( op->read_args.size(), 1UL, ::common::errors::InvalidArgument("The number of read_args should be 1")); str_ += op->name; str_ += "("; str_ += "&("; IrPrinter::Visit(op->read_args[0]); str_ += ")"; str_ += ")"; } void CodeGenC::PrintCall_get_address(const ir::Call *op) { PADDLE_ENFORCE_EQ( op->read_args.size(), 1UL, ::common::errors::InvalidArgument("The number of read_args should be 1")); PADDLE_ENFORCE_EQ(op->write_args.empty(), true, ::common::errors::InvalidArgument( "write_args is not empty. It must be empty.")); auto *read_var = op->read_args.front().as_var(); auto *read_buf = op->read_args.front().as_buffer(); if (!read_var) PADDLE_ENFORCE_NOT_NULL(read_buf, ::common::errors::InvalidArgument( "Only Var or Buffer can get address")); if (read_var) { if (read_var->type().lanes() <= 1) str_ += "&"; str_ += read_var->name; } else if (read_buf) { if (read_buf->type().lanes() <= 1) str_ += "&"; str_ += read_buf->name; } else { CINN_NOT_IMPLEMENTED } } void CodeGenC::PrintCall_pod_values_to_array(const ir::Call *op) { PADDLE_ENFORCE_EQ(op->read_args.empty(), false, ::common::errors::InvalidArgument( "The read_args is empty. It should not be empty.")); PADDLE_ENFORCE_EQ(op->write_args.size(), 1UL, ::common::errors::InvalidArgument( "The number of write_args should be 1")); auto output_var = op->write_args.front().as_var_ref(); PADDLE_ENFORCE_EQ( output_var.defined(), true, ::common::errors::InvalidArgument( "The variable 'output_var' is not be defined. It must be defined.")); std::vector arg_names; for (auto &arg : op->read_args) { auto arg_var = arg.as_var(); PADDLE_ENFORCE_NOT_NULL( arg_var, ::common::errors::InvalidArgument( "The 'arg_var' is an invalid argument. It must be true.")); arg_names.push_back(arg_var->name); } str_ += "cinn_pod_value_t "; str_ += output_var->name; str_ += "[] = "; str_ += "{ "; str_ += utils::Join(arg_names, ", "); str_ += " }"; } void CodeGenC::Visit(const ir::_Module_ *op) { CINN_NOT_IMPLEMENTED } void CodeGenC::Visit(const ir::_Var_ *op) { str_ += op->name; } void CodeGenC::Visit(const ir::Load *op) { ir::Expr offset = [&] { if (load_to_offset_.count(op) == 0) { load_to_offset_[op] = op->index(); } return load_to_offset_.at(op); }(); Expr dense_strided_ramp = detail::StridedRampBase(offset, 1); if (dense_strided_ramp.defined()) { // Loading a continuous Ramp address. PADDLE_ENFORCE_EQ( op->type().is_vector(), true, ::common::errors::InvalidArgument( "The operation type is not a vector. It must be a vector.")); PrintStackVecType(op->type().ElementOf(), offset.type().lanes()); str_ += "::"; str_ += "Load("; str_ += op->tensor.As()->name; str_ += ","; IrPrinter::Visit(dense_strided_ramp); str_ += ")"; } else if (offset.type().is_vector()) { // gather PADDLE_ENFORCE_EQ( op->type().is_vector(), true, ::common::errors::InvalidArgument( "The operation type is not a vector. It must be a vector.")); PrintStackVecType(op->type().ElementOf(), offset.type().lanes()); str_ += "::Load("; str_ += op->tensor.As()->name; str_ += ","; IrPrinter::Visit(offset); str_ += ")"; } else if (op->is_addr_tensor()) { auto *tensor = op->tensor.As(); str_ += tensor->name; str_ += "["; IrPrinter::Visit(offset); str_ += "]"; } else { IrPrinter::Visit(op); } } void CodeGenC::VisitStmt(const ir::stmt::Store &stmt) { PADDLE_ENFORCE_EQ( stmt->is_addr_tensor(), true, ::common::errors::InvalidArgument( "The operation type is invalid. It must be an address tensor.")); ir::Expr offset = [&] { if (store_stmt_to_offset_.count(stmt) == 0) { store_stmt_to_offset_[stmt] = stmt->index(); } return store_stmt_to_offset_.at(stmt); }(); auto *tensor = stmt->tensor().As(); PADDLE_ENFORCE_NOT_NULL(tensor, ::common::errors::InvalidArgument( "The tensor is null. It must not be null.")); str_ += tensor->name; str_ += "["; IrPrinter::Visit(offset); str_ += "]"; str_ += " = "; IrPrinter::Visit(stmt->value()); } void CodeGenC::Visit(const ir::Store *op) { PADDLE_ENFORCE_EQ( op->is_addr_tensor(), true, ::common::errors::InvalidArgument( "The operation type is invalid. It must be an address tensor.")); ir::Expr offset = [&] { if (store_to_offset_.count(op) == 0) { store_to_offset_[op] = op->index(); } return store_to_offset_.at(op); }(); auto *tensor = op->tensor.As(); PADDLE_ENFORCE_NOT_NULL(tensor, ::common::errors::InvalidArgument( "The tensor is null. It must not be null.")); str_ += tensor->name; str_ += "["; IrPrinter::Visit(offset); str_ += "]"; str_ += " = "; IrPrinter::Visit(op->value); } void CodeGenC::Visit(const ir::Alloc *op) { str_ += runtime::intrinsic::buffer_malloc; str_ += "("; str_ += "(void*)(0), "; auto *buffer = op->destination.As(); str_ += buffer->name; str_ += ")"; } void CodeGenC::VisitStmt(const ir::stmt::Alloc &stmt) { str_ += runtime::intrinsic::buffer_malloc; str_ += "("; str_ += "(void*)(0), "; auto *buffer = stmt->destination().As(); str_ += buffer->name; str_ += ")"; } void CodeGenC::Visit(const ir::Free *op) { str_ += runtime::intrinsic::buffer_free; str_ += "("; str_ += "(void*)(0), "; auto *buffer = op->destination.As(); str_ += buffer->name; str_ += ")"; } void CodeGenC::VisitStmt(const ir::stmt::Free &stmt) { str_ += runtime::intrinsic::buffer_free; str_ += "("; str_ += "(void*)(0), "; auto *buffer = stmt->destination().As(); str_ += buffer->name; str_ += ")"; } void CodeGenC::Visit(const ir::_Buffer_ *op) { str_ += op->name; } void CodeGenC::Visit(const ir::_Tensor_ *op) { str_ += op->buffer->name; } void CodeGenC::Visit(const ir::Let *op) { bool is_vec = false; PADDLE_ENFORCE_EQ(op->type().valid(), true, ::common::errors::InvalidArgument( "The operation type is invalid. It must be valid.")); if (op->body.defined() && op->body.As()) { // broadcast's type is hard to print, so use c++11 auto instead. str_ += "auto"; is_vec = true; } else { str_ += GetTypeRepr(op->type()); } str_ += " "; IrPrinter::Visit(op->symbol); // native C array. if (op->type().lanes() > 1 && !is_vec) { str_ += "["; str_ += std::to_string(op->type().lanes()); str_ += "]"; } if (op->body.defined()) { str_ += " = "; IrPrinter::Visit(op->body); } } void CodeGenC::VisitStmt(const ir::stmt::Let &stmt) { bool is_vec = false; PADDLE_ENFORCE_EQ(stmt->type().valid(), true, ::common::errors::InvalidArgument( "The operation type is invalid. It must be valid.")); if (stmt->body().defined() && stmt->body().As()) { // broadcast's type is hard to decide, so use c++11 auto instead. str_ += "auto"; is_vec = true; } else { str_ += GetTypeRepr(stmt->type()); } str_ += " "; IrPrinter::Visit(stmt->symbol()); // native C array. if (stmt->type().lanes() > 1 && !is_vec) { str_ += "["; str_ += std::to_string(stmt->type().lanes()); str_ += "]"; } if (stmt->body().defined()) { str_ += " = "; IrPrinter::Visit(stmt->body()); } } void CodeGenC::Visit(const ir::Reduce *op) { PADDLE_THROW(::common::errors::InvalidArgument( "Reduce IR is just for internal representation, should not be " "used for CodeGen.")); } void CodeGenC::Visit(const ir::Ramp *op) { str_ += "StackVec<"; str_ += std::to_string(op->lanes); str_ += ","; str_ += GetTypeRepr(op->type().ElementOf()); str_ += ">::Ramp("; IrPrinter::Visit(op->base); str_ += ", "; IrPrinter::Visit(op->stride); str_ += ", "; str_ += std::to_string(op->lanes); str_ += ")"; } void CodeGenC::Visit(const ir::Broadcast *op) { str_ += "StackVec<"; str_ += std::to_string(op->lanes); str_ += ","; str_ += GetTypeRepr(op->type().ElementOf()); str_ += ">::Broadcast("; IrPrinter::Visit(op->value); str_ += ", "; str_ += std::to_string(op->lanes); str_ += ")"; } void CodeGenC::Visit(const ir::FracOp *op) { ir::IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Sum *op) { ir::IrPrinter::Visit(op); } void CodeGenC::Visit(const ir::Product *op) { ir::IrPrinter::Visit(op); } void CodeGenC::PrintCastExpr(const Type &type, Expr e) { str_ += "(("; str_ += GetTypeRepr(type); str_ += ")"; str_ += "("; IrPrinter::Visit(e); str_ += "))"; } void CodeGenC::PrintCastExpr(const std::string &type, Expr e) { str_ += "("; str_ += type; str_ += ")"; str_ += "("; IrPrinter::Visit(e); str_ += ")"; } void CodeGenC::PrintShape(const std::vector &shape, char leftb, char rightb) { str_ += leftb; str_ += " "; for (int i = 0; i < shape.size() - 1; i++) { IrPrinter::Visit(shape[i]); str_ += ", "; } if (shape.size() > 1) IrPrinter::Visit(shape.back()); str_ += " "; str_ += rightb; } void CodeGenC::Visit(const ir::_LoweredFunc_ *op) { PrintFunctionDeclaration(op); str_ += "\n"; DoIndent(); PADDLE_ENFORCE_EQ( op->alloc_output_buffer_exprs.size(), op->dealloc_output_buffer_exprs.size(), ::common::errors::InvalidArgument( "The count of allocation and deallocation expressions is not " "match")); std::vector new_body; std::vector create_temp_buffers = op->PrepareCreateTempBufferExprs(); std::vector alloc_temp_buffers = op->PrepareAllocTempBufferExprs(); std::vector dealloc_temp_buffers = op->PrepareDeallocTempBufferExprs(); #define APPEND_TO_NEW_BODY(field__) \ new_body.insert( \ std::end(new_body), std::begin(op->field__), std::end(op->field__)); APPEND_TO_NEW_BODY(argument_prepare_exprs) new_body.insert(std::end(new_body), std::begin(create_temp_buffers), std::end(create_temp_buffers)); APPEND_TO_NEW_BODY(alloc_output_buffer_exprs) new_body.insert(std::end(new_body), std::begin(alloc_temp_buffers), std::end(alloc_temp_buffers)); APPEND_TO_NEW_BODY(buffer_data_cast_exprs) new_body.push_back(op->body); new_body.insert(std::end(new_body), std::begin(dealloc_temp_buffers), std::end(dealloc_temp_buffers)); APPEND_TO_NEW_BODY(dealloc_output_buffer_exprs) Expr func_body = ir::Block::Make(new_body); optim::SimplifyUnitBlock(&func_body); IrPrinter::Visit(func_body); } void CodeGenC::PrintIncludes() { str_ += "#include \n"; str_ += "#include \n"; str_ += "\n"; } void CodeGenC::PrintFileGuardOpen(const std::string &name) { str_ += utils::StringFormat("#ifndef _%s_CINN_H_\n", Uppercase(name).c_str()); str_ += utils::StringFormat("#define _%s_CINN_H_\n", Uppercase(name).c_str()); str_ += "\n"; } void CodeGenC::PrintFileGuardClose(const std::string &module_name) { str_ += utils::StringFormat("#endif // _%s_CINN_H_\n", Uppercase(module_name).c_str()); } void CodeGenC::PrintBufferCreation(const std::vector &buffers) { for (auto &buffer : buffers) { // Ignore the buffer in other devices. if (!buffer->is_on_host()) continue; DoIndent(); auto buffer_ptr_type = Type() .set_customized_type(cinn::common::customized_type::kbuffer_t) .set_cpp_handle(); Var variable = ir::_Var_::Make(buffer->name, buffer_ptr_type); auto expr = ir::intrinsics::BufferCreate::Make(buffer); expr = ir::Let::Make(variable, expr); IrPrinter::Visit(expr); str_ += ";\n"; } } void CodeGenC::PrintBufferDestroy(const std::vector &buffers) { for (auto &buffer : buffers) { DoIndent(); IrPrinter::Visit(buffer.DestroyExpr()); str_ += ";\n"; } } void CodeGenC::GenerateHeaderFile(const ir::Module &module) { PrintFileGuardOpen(module.name()); PrintIncludes(); for (auto &func : module.functions()) { PrintFunctionDeclaration(func.As()); str_ += ";\n"; str_ += "\n\n"; } PrintFileGuardClose(module.name()); } void CodeGenC::PrintFuncArg(const ir::Argument &arg) { if (arg.is_buffer()) { if (arg.is_input()) { str_ += "const struct cinn_buffer_t *"; } else { str_ += "struct cinn_buffer_t *"; } } else if (arg.is_var()) { str_ += GetTypeRepr(arg.type()); str_ += " "; str_ += arg.name(); } else { CINN_NOT_IMPLEMENTED } str_ += arg.name(); } void CodeGenC::PrintRuntimeType(const cinn_type_t &type) { if (type == cinn_bool_t()) { str_ += "cinn_bool_t()"; } else if (type == cinn_int8_t()) { str_ += "cinn_int8_t()"; } else if (type == cinn_int16_t()) { str_ += "cinn_int16_t()"; } else if (type == cinn_int32_t()) { str_ += "cinn_int32_t()"; } else if (type == cinn_int64_t()) { str_ += "cinn_int64_t()"; } else if (type == cinn_uint8_t()) { str_ += "cinn_uint8_t()"; } else if (type == cinn_uint16_t()) { str_ += "cinn_uint16_t()"; } else if (type == cinn_uint32_t()) { str_ += "cinn_uint32_t()"; } else if (type == cinn_uint64_t()) { str_ += "cinn_uint64_t()"; } else if (type == cinn_bfloat16_t()) { str_ += "cinn_bfloat16_t()"; } else if (type == cinn_float8e4m3_t()) { str_ += "cinn_float8e4m3_t()"; } else if (type == cinn_float16_t()) { str_ += "cinn_float16_t()"; } else if (type == cinn_float32_t()) { str_ += "cinn_float32_t()"; } else if (type == cinn_float64_t()) { str_ += "cinn_float64_t()"; } else { PADDLE_THROW(::common::errors::InvalidArgument( "Unknown type is not supported to print")); } } void CodeGenC::PrintStackVecType(Type type, int lanes) { str_ += "StackedVec<"; str_ += GetTypeRepr(type); str_ += ","; str_ += std::to_string(lanes); str_ += ">"; } void CodeGenC::Visit(const ir::PrimitiveNode *op) { CINN_NOT_IMPLEMENTED } void CodeGenC::Visit(const ir::_BufferRange_ *op) { CINN_NOT_IMPLEMENTED } void CodeGenC::Visit(const ir::ScheduleBlock *op) { CINN_NOT_IMPLEMENTED } void CodeGenC::VisitStmt(const ir::stmt::Schedule &stmt) { CINN_NOT_IMPLEMENTED } void CodeGenC::Visit(const ir::ScheduleBlockRealize *op) { CINN_NOT_IMPLEMENTED } void CodeGenC::Visit(const ir::_Dim_ *op) { CINN_NOT_IMPLEMENTED } void CodeGenC::Visit(const ir::IntrinsicOp *op) { switch (op->getKind()) { #define __(x) \ case ir::IntrinsicKind::k##x: \ Visit(llvm::dyn_cast(op)); \ break; INTRINSIC_KIND_FOR_EACH(__) #undef __ } } void CodeGenC::Visit(const ir::intrinsics::BufferGetDataHandle *op) { str_ += op->buffer.as_buffer()->name; str_ += "->"; str_ += "memory"; } void CodeGenC::Visit(const ir::intrinsics::BufferGetDataConstHandle *op) { str_ += op->buffer.as_buffer()->name; str_ += "->"; str_ += "memory"; } void CodeGenC::Visit(const ir::intrinsics::PodValueToX *op) { auto to_type = op->GetOutputType(0); if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_float; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_double; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_float16; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_bool; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_int8; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_int16; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_int32; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_int64; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_uint8; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_uint16; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_uint32; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_uint64; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_void_p; } else if (to_type == type_of()) { str_ += runtime::intrinsic::pod_value_to_buffer_p; } else { std::stringstream ss; ss << "Not supported type: " << to_type; PADDLE_THROW(::common::errors::InvalidArgument(ss.str())); } str_ += "("; IrPrinter::Visit(op->pod_value_ptr); str_ += ")"; } void CodeGenC::Visit(const ir::intrinsics::BufferCreate *op) { const ir::_Buffer_ *buffer_arg = op->buffer.as_buffer(); PADDLE_ENFORCE_NOT_NULL( buffer_arg, ::common::errors::InvalidArgument( "The buffer argument is invalid. It must be true.")); str_ += runtime::intrinsic::buffer_create; str_ += "("; PrintCastExpr("cinn_device_kind_t", Expr(buffer_arg->target.runtime_arch())); str_ += "/*target*/, "; PrintRuntimeType(runtime::ToRuntimeType(buffer_arg->dtype.ElementOf())); str_ += ", "; PrintShape(buffer_arg->shape); if (buffer_arg->data_alignment > 0) { str_ += ", "; str_ += std::to_string(buffer_arg->data_alignment); str_ += "/*align*/"; } str_ += ")"; } void CodeGenC::Visit(const ir::intrinsics::GetAddr *op) { if (op->data.as_buffer()) { str_ += "&"; str_ += op->data.as_buffer()->name; } else if (op->data.as_var()) { str_ += "&"; str_ += op->data.as_var()->name; } else { str_ += "&("; IrPrinter::Visit(op->data); str_ += ")"; } } void CodeGenC::Visit(const ir::intrinsics::ArgsConstruct *op) { str_ += runtime::intrinsic::args_construct_repr; str_ += "("; str_ += op->var->name; str_ += ", "; str_ += std::to_string(op->args.size()); str_ += ", "; for (int i = 0; i < op->args.size() - 1; i++) { IrPrinter::Visit(op->args[i]); str_ += ", "; } if (!op->args.empty()) { IrPrinter::Visit(op->args.back()); } str_ += ")"; } void CodeGenC::Visit(const ir::intrinsics::BuiltinIntrin *op) { str_ += op->name; str_ += "("; if (!op->args.empty()) { for (int i = 0; i < op->args.size() - 1; i++) { IrPrinter::Visit(op->args[i]); str_ += ", "; } IrPrinter::Visit(op->args.back()); } str_ += ")"; } std::string ReadWholeFile(const std::string &path) { PADDLE_ENFORCE_EQ(path.empty(), false, ::common::errors::InvalidArgument( "The path is empty. It must not be empty.")); std::ifstream file(path); PADDLE_ENFORCE_EQ(file.is_open(), true, ::common::errors::InvalidArgument("Failed to open file: %s", path.c_str())); std::stringstream ss; ss << file.rdbuf(); return ss.str(); } void CodeGenC::PrintBuiltinCodes() { PADDLE_ENFORCE_EQ( FLAGS_cinn_x86_builtin_code_root.empty(), false, ::common::errors::InvalidArgument( "The flag cinn_x86_builtin_code_root should be set first")); const std::string x86_code_file = "_x86_builtin_source.cc"; auto source = ReadWholeFile(FLAGS_cinn_x86_builtin_code_root + "/" + x86_code_file); str_ += source; str_ += "\n"; } namespace detail { Expr StridedRampBase(Expr e, int stride) { auto *ramp_n = e.As(); if (ramp_n) { auto *iv = ramp_n->stride.As(); if (iv && iv->value == stride) return ramp_n->base; } return Expr(); } } // namespace detail } // namespace backends } // namespace cinn