* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file compile_control_bin.cpp
* \brief
*/
#include <iosfwd>
#include <vector>
#include <nlohmann/json.hpp>
#include "interface/program/program.h"
#include "interface/utils/file_utils.h"
#include "interface/utils/common.h"
#include "interface/configs/config_manager.h"
#include "interface/utils/op_info_manager.h"
#include "machine/utils/checkinject.h"
#include "machine/utils/machine_utils.h"
#include "tilefwk/pypto_fwk_log.h"
#include "tilefwk/error_code.h"
using Json = nlohmann::json;
namespace {
const std::string CustomOpCtrolRunFuncName = "PyptoKernelCtrlServer";
const std::string CustomOpCtrolInitFuncName = "PyptoKernelCtrlServerInit";
const std::string CustomKerneLib = "CUSTKFCKernel";
std::string GetMachineCompilerPath()
{
constexpr const char* kAscendHomeEnv = "ASCEND_HOME_PATH";
const std::string homePath = npu::tile_fwk::GetEnvVar(kAscendHomeEnv);
if (homePath.empty()) {
MACHINE_LOGE(npu::tile_fwk::DevCommonErr::GET_ENV_FAILED,
"%s is unset, empty after trimming whitespace, or exceeds max env length; device control-flow compiler "
"path is disabled.",
kAscendHomeEnv);
return "";
}
const std::string compiler =
homePath + "/toolkit/toolchain/hcc/bin/aarch64-target-linux-gnu-g++";
if (!npu::tile_fwk::FileExist(compiler)) {
MACHINE_LOGE(npu::tile_fwk::DevCommonErr::FILE_ERROR,
"%s is set but aarch64 toolchain g++ was not found at \"%s\" (canonical path must exist).",
kAscendHomeEnv, compiler.c_str());
return "";
}
return compiler;
}
const std::string DeviceMahineCompiler = GetMachineCompilerPath();
}
namespace npu::tile_fwk {
constexpr int DUMP_LEVEL_FOUR = 4;
void GenCustomOpInfo(
const std::string& funcName, const std::string& controlAicpuPath, const std::string& constrolSoName)
{
Json customOp;
AicpuOpConfig costomInit;
costomInit.functionName = CustomOpCtrolInitFuncName;
costomInit.kernelSo = constrolSoName + ".so";
costomInit.opKernelLib = CustomKerneLib;
costomInit.opType = funcName + "Init";
AicpuOpConfig costomRun = costomInit;
costomRun.opType = funcName + "Run";
costomRun.functionName = CustomOpCtrolRunFuncName;
GenAicpuOpInfoJson(customOp, {costomInit, costomRun});
std::string fileName = controlAicpuPath + "/" + constrolSoName + ".json";
if (!DumpFile(customOp.dump(DUMP_LEVEL_FOUR), fileName)) {
MACHINE_LOGE(DevCommonErr::FILE_ERROR, "Contrust custom op json failed");
return;
}
OpInfoManager::GetInstance().GetCustomOpJsonPath() = fileName;
}
bool GenTilingFunc(const std::string& funcName, const std::string& controlAicpuPath)
{
std::ostringstream oss;
oss << "#include <vector>\n";
oss << "#include <string>\n";
oss << "#include \"controlFlow_dev" << funcName << ".h\"\n";
oss << "#include <map>\n";
oss << "#include \""
<< "tilefwk/aicpu_runtime.h"
<< "\"\n";
oss << "namespace npu::tile_fwk { \n";
oss << "using controlFlowFuncPtr = uint64_t (*)(void*, int64_t*, RuntimeCallEntryType*, DevStartArgsBase*);\n";
oss << "namespace " << funcName << "{\n";
oss << "controlFlowFuncPtr controlFlowptr = ControlFlowEntry;\n";
oss << "} // end namespace " << funcName << "\n";
oss << "extern \"C\" __attribute__((visibility(\"default\"))) void* GetCtrlFlowFunc() {\n";
oss << "return reinterpret_cast<void*>(" << funcName << "::controlFlowptr);\n";
oss << "}\n";
oss << "}\n";
std::string fileName = controlAicpuPath + "/control_flow_kernel.cpp";
return DumpFile(oss.str(), fileName);
}
bool TieFwkAicpuPreCompile(std::string& preCompileO, std::string& controlAicpuPath)
{
std::stringstream preCompileStream;
std::string ext = "cpp";
auto files = GetFiles(controlAicpuPath, ext);
std::string includePath = GetCurrentSharedLibPath() + "/../include/tilefwk";
for (auto file : files) {
std::string objFile = controlAicpuPath + file.substr(0, file.find(".")) + ".o";
std::string compileCmd = DeviceMahineCompiler + " -Wall -O2 -fPIC -c -std=gnu++17 -fno-common " +
controlAicpuPath + file + " -I" + includePath + " -I" + includePath + "/include/" +
" -I" + GetCurrentSharedLibPath() + "/include/" + " -o " + objFile;
MACHINE_LOGD("PreCompileCmd is %s, file is %s.\n", compileCmd.c_str(), file.c_str());
int ret = Checkinject(compileCmd.c_str(), compileCmd.size());
if (ret != 0) {
MACHINE_LOGE(DevCommonErr::SYSTEM_CALL_FAILED, "Precompile %s cmd illegal char.\n", file.c_str());
return false;
}
ret = std::system(compileCmd.c_str());
if (ret != 0) {
MACHINE_LOGE(DevCommonErr::SYSTEM_CALL_FAILED, "Precompile %s fail\n", file.c_str());
return false;
}
preCompileStream << objFile << " ";
}
preCompileO = preCompileStream.str();
return true;
}
bool SharedAicpuCompile(const std::string& funcName, const std::string& aicpuDirPath, const std::string& preCompileO)
{
std::string cmdGccCompile = "LD_PRELOAD= " + DeviceMahineCompiler +
" -std=gnu++17 -fno-common -shared -fPIC -O2 -Wl,--no-warn-rwx-segments -o " +
aicpuDirPath + "/lib" + funcName + "_control.so " + preCompileO +
" -Wl,--whole-archive " + GetCurrentSharedLibPath() + "/libpypto_ctrl_server.a" +
" -Wl,--no-whole-archive";
int ret = Checkinject(cmdGccCompile.c_str(), cmdGccCompile.size());
if (ret != 0) {
MACHINE_LOGE(DevCommonErr::SYSTEM_CALL_FAILED, "RunDeviceMachine compile cmd illegal char.\n");
return false;
}
ret = std::system(cmdGccCompile.c_str());
if (ret != 0) {
MACHINE_LOGE(DevCommonErr::SYSTEM_CALL_FAILED, "RUNDeviceMachine compile fail\n");
return false;
}
std::string srcSoPath = aicpuDirPath + "/lib" + funcName + "_control.so";
std::string constrolSoName = "lib" + funcName + "_control";
GenCustomOpInfo(funcName, aicpuDirPath, constrolSoName);
return ReadBytesFromFile(srcSoPath, OpInfoManager::GetInstance().GetControlBuffer());
}
bool TileFwkAiCpuCompile(const std::string& funcName, const std::string& aicpuDirPath)
{
OpInfoManager::GetInstance().GetOpFuncName() = funcName;
std::string controlAicpuPath = aicpuDirPath + "/" + funcName + "/aicpu/";
if (!GenTilingFunc(funcName, controlAicpuPath)) {
MACHINE_LOGE(HostBackEndErr::GEN_DYNAMIC_OP_FAILED, "Gen op[%s] not success\n", funcName.c_str());
return false;
}
std::string preCompileO = "";
if (!TieFwkAicpuPreCompile(preCompileO, controlAicpuPath)) {
MACHINE_LOGE(HostBackEndErr::PRECOMPILE_FAILED, "Op %s preCompile fail\n", funcName.c_str());
return false;
}
return SharedAicpuCompile(funcName, aicpuDirPath, preCompileO);
}
}