* 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.
*/
#include "dflow/compiler/data_flow_graph/function_compile.h"
#include <regex>
#include <sys/file.h>
#include <mutex>
#include "framework/common/scope_guard.h"
#include "dflow/compiler/data_flow_graph/compile_config_json.h"
#include "mmpa/mmpa_api.h"
#include "graph_metadef/common/ge_common/util.h"
#include "dflow/base/utils/data_flow_utils.h"
namespace ge {
FunctionCompile::FunctionCompile(const std::string &pp_name, const CompileConfigJson::FunctionPpConfig &func_pp_cfg) :
function_pp_name_(pp_name), func_pp_cfg_{func_pp_cfg}, compile_result_{} {}
Status FunctionCompile::GetPathToSource(std::string &src_path) const {
std::regex pattern(R"([A-Za-z0-9./+\-_]+)");
std::smatch match_result;
std::string temp_path;
if (!func_pp_cfg_.cmakelist.empty()) {
GE_CHK_BOOL_RET_STATUS(std::regex_match(func_pp_cfg_.cmakelist, match_result, pattern), PARAM_INVALID,
"Invalid cmakelist: %s", func_pp_cfg_.cmakelist.c_str());
temp_path = func_pp_cfg_.workspace + "/" + func_pp_cfg_.cmakelist;
} else {
temp_path = func_pp_cfg_.workspace + "/CMakeLists.txt";
}
src_path = RealPath(temp_path.c_str());
GE_CHK_BOOL_RET_STATUS(!src_path.empty(), FAILED, "Invalid path:%s.", temp_path.c_str());
const auto index = src_path.rfind("/");
if (index != std::string::npos) {
src_path = src_path.substr(0, index);
}
return SUCCESS;
}
Status FunctionCompile::CompileSingleResourceType(const std::string &toolchain, const std::string &resource_type,
const std::string &target_bin) {
std::regex pattern(R"([A-Za-z0-9./+\-_]+)");
std::smatch match_result;
GE_CHK_BOOL_RET_STATUS(std::regex_match(toolchain, match_result, pattern), PARAM_INVALID, "Invalid toolchain: %s",
toolchain.c_str());
GE_CHK_BOOL_RET_STATUS(std::regex_match(resource_type, match_result, pattern), PARAM_INVALID,
"Invalid resource_type: %s", resource_type.c_str());
const std::string &workspace = func_pp_cfg_.workspace;
std::string target_dir = workspace + "/build/_" + target_bin;
std::string release_dir = resource_type + "/release";
std::string inner_build_dir = resource_type + +"/build";
std::string cmd = "cd " + target_dir + "; mkdir -p " + release_dir + "; mkdir -p " + inner_build_dir;
GE_CHK_BOOL_RET_STATUS(system(cmd.c_str()) == 0, FAILED, "Failed to execute cmd[%s] for pp[%s].", cmd.c_str(),
function_pp_name_.c_str());
GELOGI("Execute cmd[%s]", cmd.c_str());
const std::string compile_lock = target_dir + "/" + inner_build_dir + "/compile_lock";
int32_t fd = -1;
{
static std::mutex mu;
std::lock_guard<std::mutex> lk(mu);
fd = mmOpen2(compile_lock.c_str(), M_CREAT | M_WRONLY, M_IRUSR | M_IWUSR);
if (fd == -1) {
const int32_t error_code = mmGetErrorCode();
GELOGE(FAILED, "Failed to open file[%s], error msg[%s].", compile_lock.c_str(),
GetErrorNumStr(error_code).c_str());
return FAILED;
}
}
bool is_locked = false;
ScopeGuard auto_close([&fd, &is_locked] {
if (is_locked) {
(void)flock(fd, LOCK_UN);
}
(void)close(fd);
fd = -1;
});
if (flock(fd, LOCK_EX) != 0) {
const int32_t error_code = mmGetErrorCode();
GELOGE(FAILED, "Failed to lock file[%s], error msg[%s].", compile_lock.c_str(), GetErrorNumStr(error_code).c_str());
return FAILED;
}
is_locked = true;
const std::string build_dir = target_dir + "/" + inner_build_dir;
cmd = "bash -c \"cd " + build_dir + "; cmake -S " + src_path_ + " -DTOOLCHAIN=" + toolchain +
" -DUDF_TARGET_LIB=" + target_bin + " -DRELEASE_DIR=../release -DRESOURCE_TYPE=" + resource_type +
" >output.log 2>&1\"";
auto sys_ret = system(cmd.c_str());
if (sys_ret != 0) {
GELOGW("Failed to execute cmd[%s] for pp[%s], ret=%d, see also %s/output.log.", cmd.c_str(),
function_pp_name_.c_str(), sys_ret, build_dir.c_str());
return FAILED;
}
GELOGI("Execute cmd[%s].", cmd.c_str());
cmd = "cd " + build_dir + "; make 2>> output.log";
sys_ret = system(cmd.c_str());
GE_CHK_BOOL_RET_STATUS(sys_ret == 0, FAILED,
"Failed to execute cmd[%s] for pp[%s], ret=%d, see also %s/output.log.", cmd.c_str(),
function_pp_name_.c_str(), sys_ret, build_dir.c_str());
GELOGI("Execute cmd[%s]", cmd.c_str());
return SUCCESS;
}
void FunctionCompile::SetRunningResourceInfoToCompileResult() {
if (func_pp_cfg_.running_resources_info.empty()) {
constexpr uint32_t kDefaultMemorySize = 100U;
CompileConfigJson::RunningResourceInfo resource_info;
resource_info.resource_type = "cpu";
resource_info.resource_num = 1;
compile_result_.running_resources_info.emplace_back(resource_info);
resource_info.resource_type = "memory";
resource_info.resource_num = kDefaultMemorySize;
compile_result_.running_resources_info.emplace_back(resource_info);
} else {
for (const auto &resource_info : func_pp_cfg_.running_resources_info) {
compile_result_.running_resources_info.emplace_back(resource_info);
}
}
return;
}
std::string FunctionCompile::GetAscendLatestInstallPath() {
return DataFlowUtils::GetAscendHomePath();
}
std::string FunctionCompile::GetAscendToolchainPath() {
static std::once_flag flag;
static std::string toolchain_path;
std::call_once(flag, []() {
toolchain_path = GetAscendLatestInstallPath() + "/toolkit/toolchain/hcc/bin/aarch64-target-linux-gnu-g++";
});
return toolchain_path;
}
Status FunctionCompile::GetToolchainByResourceType(const std::string &resource_type, std::string &toolchain) {
if (func_pp_cfg_.toolchain_map[resource_type].empty()) {
if (resource_type == kResourceTypeAscend) {
toolchain = GetAscendToolchainPath();
INT32 mm_ret = mmAccess(toolchain.c_str());
if (mm_ret != EN_OK) {
GELOGW("Default ascend toolchain file[%s] does not exist.", toolchain.c_str());
return FAILED;
}
} else {
toolchain = "g++";
}
} else {
toolchain = func_pp_cfg_.toolchain_map[resource_type];
}
return SUCCESS;
}
Status FunctionCompile::CompileAllResourceType() {
std::set<std::string> resource_types;
GE_CHK_STATUS_RET(CompileConfigJson::GetResourceTypeFromNumaConfig(resource_types),
"Failed to get resource types from numa config.");
const std::string &workspace = func_pp_cfg_.workspace;
std::regex workspace_pattern(R"([A-Za-z0-9./+\-_]+)");
std::regex lib_pattern(R"(^lib[A-Za-z0-9.\-_]+\.so$)");
std::smatch match_result;
GE_CHK_BOOL_RET_STATUS(std::regex_match(workspace, match_result, workspace_pattern), PARAM_INVALID,
"Invalid workspace: %s", workspace.c_str());
std::string target_bin = func_pp_cfg_.target_bin;
GE_CHK_BOOL_RET_STATUS(std::regex_match(target_bin, match_result, lib_pattern), PARAM_INVALID,
"Invalid target_bin: %s", target_bin.c_str());
auto index = target_bin.rfind(".");
if (index != std::string::npos) {
target_bin = target_bin.substr(0, index);
target_bin = target_bin.substr(strlen("lib"));
}
std::string cmd = "cd " + workspace + "; mkdir -p build/_" + target_bin;
GELOGD("Execute cmd[%s]", cmd.c_str());
auto ret = system(cmd.c_str());
if (ret != 0) {
GELOGE(FAILED, "Failed to execute cmd[%s] for pp[%s]", cmd.c_str(), function_pp_name_.c_str());
return FAILED;
}
const auto ge_ret = GetPathToSource(src_path_);
if (ge_ret != SUCCESS) {
GELOGE(ge_ret, "Failed to get path to source for pp[%s].", function_pp_name_.c_str());
return ge_ret;
}
for (const auto &resource_type : resource_types) {
std::string toolchain;
if (GetToolchainByResourceType(resource_type, toolchain) != SUCCESS) {
GELOGI("Toolchain file[%s] for resource type[%s] does not exist.", toolchain.c_str(), resource_type.c_str());
continue;
}
auto compile_ret = CompileSingleResourceType(toolchain, resource_type, target_bin);
if (compile_ret != SUCCESS) {
GELOGW("compile function pp[%s]'s so failed, resource type[%s], compile_ret=%d.", function_pp_name_.c_str(),
resource_type.c_str(), compile_ret);
} else {
compile_result_.compile_bin_info[resource_type] =
func_pp_cfg_.workspace + "/build/_" + target_bin + "/" + resource_type + "/release/";
}
}
if (compile_result_.compile_bin_info.empty()) {
GELOGE(FAILED, "Compile all resource type[%s] for function pp[%s] failed.",
ToString(std::vector<std::string>(resource_types.cbegin(), resource_types.cend())).c_str(),
function_pp_name_.c_str());
return FAILED;
}
SetRunningResourceInfoToCompileResult();
return SUCCESS;
}
Status FunctionCompile::GetBuiltInFuncCompileResult(CompileResult &compile_result) {
static std::mutex mt;
static CompileResult built_in_func_compile_result;
std::lock_guard<std::mutex> guard(mt);
if (!built_in_func_compile_result.compile_bin_info.empty()) {
compile_result = built_in_func_compile_result;
return SUCCESS;
}
constexpr uint32_t kDefaultMemorySize = 100U;
CompileConfigJson::RunningResourceInfo resource_info;
resource_info.resource_type = "cpu";
resource_info.resource_num = 1;
built_in_func_compile_result.running_resources_info.emplace_back(resource_info);
resource_info.resource_type = "memory";
resource_info.resource_num = kDefaultMemorySize;
built_in_func_compile_result.running_resources_info.emplace_back(resource_info);
std::set<std::string> resource_types;
GE_CHK_STATUS_RET(CompileConfigJson::GetResourceTypeFromNumaConfig(resource_types),
"Failed to get resource types from numa config.");
for (const auto &resource_type : resource_types) {
built_in_func_compile_result.compile_bin_info[resource_type] = "";
}
compile_result = built_in_func_compile_result;
return SUCCESS;
}
}