已合并
【PR】: Revert HostCPU 融合 Codegen 迁移至标准 CustomOp SO #4667
yangyongqiang创建于 4 天前
【PR】: Revert HostCPU 融合 Codegen 迁移至标准 CustomOp SO #4667
已合并
yangyongqiang创建于 4 天前
33 个文件变更+4512-1591
@@ -606,9 +606,7 @@ Status ModelHelper::SaveAutofuseSoBin(const GeRootModelPtr &ge_root_model) {
606 if (bin_file_buffer != nullptr) {606 if (bin_file_buffer != nullptr) {
607 GELOGD("bin_file_buffer already exists, sync autofuse so to op_so_store_.");607 GELOGD("bin_file_buffer already exists, sync autofuse so to op_so_store_.");
608 for (const auto &bin_entry : *bin_file_buffer) {608 for (const auto &bin_entry : *bin_file_buffer) {
609- if ((bin_entry.second != nullptr) && (bin_entry.second->GetSoBinType() == SoBinType::kAutofuse)) {609+ op_so_store_.AddKernel(bin_entry.second);
610- op_so_store_.AddKernel(bin_entry.second);
611- }
612 }610 }
613 return SUCCESS;611 return SUCCESS;
614 }612 }
@@ -625,21 +623,8 @@ Status ModelHelper::SaveCustomOpSoBin(const GeRootModelPtr &ge_root_model) {
625 if (!OpSoStoreUtils::IsSoBinType(ge_root_model->GetSoInOmFlag(), SoBinType::kCustomOp)) {623 if (!OpSoStoreUtils::IsSoBinType(ge_root_model->GetSoInOmFlag(), SoBinType::kCustomOp)) {
626 return SUCCESS;624 return SUCCESS;
627 }625 }
628- auto root_graph = ge_root_model->GetRootGraph();
629- GE_ASSERT_NOTNULL(root_graph);
630- size_t embedded_so_num = 0U;
631- const auto so_buffer = root_graph->GetExtAttr<std::map<std::string, ge::OpSoBinPtr>>("bin_file_buffer");
632- if (so_buffer != nullptr) {
633- for (const auto &entry : *so_buffer) {
634- if ((entry.second != nullptr) && (entry.second->GetSoBinType() == SoBinType::kCustomOp)) {
635- op_so_store_.AddKernel(entry.second);
636- ++embedded_so_num;
637- }
638- }
639- }
640 GE_ASSERT_SUCCESS(LoadAndStoreOppSo(ge_root_model->GetCustomOpSoSet(), SoBinType::kCustomOp));626 GE_ASSERT_SUCCESS(LoadAndStoreOppSo(ge_root_model->GetCustomOpSoSet(), SoBinType::kCustomOp));
641- GELOGI("[CustomOp]Save %zu path-based and %zu embedded custom op so to OpSoStore success.",627+ GELOGI("[CustomOp]Save %zu custom op so to OpSoStore success.", ge_root_model->GetCustomOpSoSet().size());
642- ge_root_model->GetCustomOpSoSet().size(), embedded_so_num);
643 return SUCCESS;628 return SUCCESS;
644}629}
645 630 
@@ -32,8 +32,6 @@
32#include "external/ge_common/ge_common_api_types.h"32#include "external/ge_common/ge_common_api_types.h"
33#include "external/graph/custom_op.h"33#include "external/graph/custom_op.h"
34#include "graph/custom_op/cast.h"34#include "graph/custom_op/cast.h"
35-#include "framework/common/host_cpu_fusion_attr.h"
36-#include "graph/op_so_bin.h"
37#include "common/om2/om2_model_data.h"35#include "common/om2/om2_model_data.h"
38 36 
39namespace ge {37namespace ge {
@@ -173,20 +171,6 @@ Status CollectCustomOpTypesFromGraph(const ComputeGraphPtr &graph, const CustomO
173 }171 }
174 return SUCCESS;172 return SUCCESS;
175}173}
176- 
177-bool IsEmbeddedHostCpuFusionSo(const ComputeGraphPtr &root_graph, const std::string &op_type) {
178- if (root_graph == nullptr) {
179- return false;
180- }
181- const auto so_buffer = root_graph->GetExtAttr<std::map<std::string, OpSoBinPtr>>("bin_file_buffer");
182- if (so_buffer == nullptr) {
183- return false;
184- }
185- const std::string so_key = std::string(kFusedHostCpuSoVendor) + "/lib" + op_type + ".so";
186- const auto so_it = so_buffer->find(so_key);
187- return (so_it != so_buffer->cend()) && (so_it->second != nullptr) &&
188- (so_it->second->GetSoBinType() == SoBinType::kCustomOp);
189-}
190} // namespace174} // namespace
191Status GeRootModel::Initialize(const ComputeGraphPtr &root_graph) {175Status GeRootModel::Initialize(const ComputeGraphPtr &root_graph) {
192 GE_ASSERT_NOTNULL(root_graph);176 GE_ASSERT_NOTNULL(root_graph);
@@ -425,11 +409,6 @@ Status GeRootModel::CheckAndSetCustomOpSo() {
425 continue;409 continue;
426 }410 }
427 411 
428- if (IsEmbeddedHostCpuFusionSo(root_graph_, op_type)) {
429- GELOGI("[CustomOp] op[%s] uses embedded HostCPU fusion SO, skip path collect.", op_type.c_str());
430- continue;
431- }
432- 
433 std::string so_path;412 std::string so_path;
434 GE_ASSERT_SUCCESS(ResolvePortableOpSoPath(op_type, portable_op, so_path),413 GE_ASSERT_SUCCESS(ResolvePortableOpSoPath(op_type, portable_op, so_path),
435 "Resolve custom op so path failed for op[%s].", op_type.c_str());414 "Resolve custom op so path failed for op[%s].", op_type.c_str());
@@ -446,20 +425,7 @@ Status GeRootModel::CheckAndSetCustomOpSo() {
446 if (!custom_op_so_set_.empty()) {425 if (!custom_op_so_set_.empty()) {
447 OpSoStoreUtils::SetSoBinType(SoBinType::kCustomOp, so_in_om_);426 OpSoStoreUtils::SetSoBinType(SoBinType::kCustomOp, so_in_om_);
448 }427 }
449- size_t embedded_custom_so_num = 0U;428+ GELOGI("[CustomOp]The num of so is %zu.", custom_op_so_set_.size());
450- const auto so_buffer = root_graph_->GetExtAttr<std::map<std::string, OpSoBinPtr>>("bin_file_buffer");
451- if (so_buffer != nullptr) {
452- for (const auto &entry : *so_buffer) {
453- if ((entry.second != nullptr) && (entry.second->GetSoBinType() == SoBinType::kCustomOp)) {
454- ++embedded_custom_so_num;
455- }
456- }
457- }
458- if (embedded_custom_so_num > 0U) {
459- OpSoStoreUtils::SetSoBinType(SoBinType::kCustomOp, so_in_om_);
460- }
461- GELOGI("[CustomOp]The num of path-based so is %zu, embedded so is %zu.", custom_op_so_set_.size(),
462- embedded_custom_so_num);
463 return SUCCESS;429 return SUCCESS;
464}430}
465 431 
@@ -25,7 +25,7 @@ namespace {
25const std::string kConstantFoldingName = "libconstant_folding_ops.so";25const std::string kConstantFoldingName = "libconstant_folding_ops.so";
26const std::string kOpsHostCpuName = "libops_host_cpu.so";26const std::string kOpsHostCpuName = "libops_host_cpu.so";
27const std::string kAicpuConstFoldingName = "libaicpu_const_folding.so";27const std::string kAicpuConstFoldingName = "libaicpu_const_folding.so";
28-constexpr char kAicpuHostFindFunc[] = "AicpuHostFindFunc";28+const char *const kIsFusedCpuKernelSupported = "IsCpuConstantFoldingFusedOpSupported";
29 29 
30Status GetDataNumber(const GeTensorDesc &out_desc, uint64_t &data_num) {30Status GetDataNumber(const GeTensorDesc &out_desc, uint64_t &data_num) {
31 int64_t num_size = out_desc.GetShape().IsScalar() ? 1 : out_desc.GetShape().GetShapeSize();31 int64_t num_size = out_desc.GetShape().IsScalar() ? 1 : out_desc.GetShape().GetShapeSize();
@@ -141,13 +141,15 @@ void HostCpuEngine::Finalize() const {
141 GELOGI("start HostCpuEngine::Finalize");141 GELOGI("start HostCpuEngine::Finalize");
142}142}
143 143 
144-bool HostCpuEngine::IsHostKernelSupported(const std::string &op_type) const {144+bool HostCpuEngine::IsFusedCpuKernelSupported(const std::string &op_type) const {
145- if (host_kernel_finder_ == nullptr) {145+ if (is_fused_cpu_kernel_supported_ == nullptr) {
146- GELOGD("HostCPU Gert HostKernel finder is unavailable for op[%s].", op_type.c_str());146+ GELOGD("HostCPU fused-kernel support query is unavailable for op[%s].", op_type.c_str());
147 return false;147 return false;
148 }148 }
149- const bool supported = host_kernel_finder_(op_type) != nullptr;149+ const bool supported = is_fused_cpu_kernel_supported_(op_type.c_str()) == 1;
150- GELOGD("HostCPU Gert HostKernel query: op[%s], supported[%d].", op_type.c_str(), static_cast<int32_t>(supported));150+ if (!supported) {
151+ GELOGD("HostCPU fused-kernel support query rejected op[%s].", op_type.c_str());
152+ }
151 return supported;153 return supported;
152}154}
153 155 
@@ -306,11 +308,14 @@ Status HostCpuEngine::LoadLib(const std::string &lib_path, bool invoke_init) {
306 GELOGI("Lib: %s has been opened", lib_path.c_str());308 GELOGI("Lib: %s has been opened", lib_path.c_str());
307 if (lib_path.find(kConstantFoldingName) != lib_path.npos) {309 if (lib_path.find(kConstantFoldingName) != lib_path.npos) {
308 constant_folding_handle_ = handle;310 constant_folding_handle_ = handle;
309- host_kernel_finder_ = reinterpret_cast<HostKernelFinder>(mmDlsym(handle, kAicpuHostFindFunc));311+ }
310- if (host_kernel_finder_ == nullptr) {312+ if (lib_path.find(kAicpuConstFoldingName) != lib_path.npos) {
313+ is_fused_cpu_kernel_supported_ =
314+ reinterpret_cast<int32_t (*)(const char *)>(mmDlsym(handle, kIsFusedCpuKernelSupported));
315+ if (is_fused_cpu_kernel_supported_ == nullptr) {
311 const char_t *reason = mmDlerror();316 const char_t *reason = mmDlerror();
312 reason = (reason == nullptr) ? "" : reason;317 reason = (reason == nullptr) ? "" : reason;
313- GELOGW("Gert HostKernel finder is unavailable in lib: %s, reason = %s", lib_path.c_str(), reason);318+ GELOGW("Fused HostCPU support query symbol is unavailable in lib: %s, reason = %s", lib_path.c_str(), reason);
314 }319 }
315 }320 }
316 (void)lib_handles_.emplace_back(handle);321 (void)lib_handles_.emplace_back(handle);
@@ -19,10 +19,6 @@
19#include "graph/operator.h"19#include "graph/operator.h"
20#include "graph_metadef/register/graph_register.h"20#include "graph_metadef/register/graph_register.h"
21 21 
22-namespace gert {
23-class KernelContext;
24-}
25- 
26namespace ge {22namespace ge {
27class HostCpuEngine {23class HostCpuEngine {
28 public:24 public:
@@ -41,13 +37,9 @@ class HostCpuEngine {
41 return constant_folding_handle_;37 return constant_folding_handle_;
42 }38 }
43 39 
44- // 查询 libconstant_folding_ops.so 的 Gert HostKernel 路由。只要 op_type 能找到函数即可参与融合。40+ bool IsFusedCpuKernelSupported(const std::string &op_type) const;
45- bool IsHostKernelSupported(const std::string &op_type) const;
46 41 
47 private:42 private:
48- using HostKernelFunc = graphStatus (*)(gert::KernelContext *);
49- using HostKernelFinder = HostKernelFunc (*)(std::string);
50- 
51 HostCpuEngine() = default;43 HostCpuEngine() = default;
52 44 
53 void *DlopenLib(const std::string &lib_path) const;45 void *DlopenLib(const std::string &lib_path) const;
@@ -71,7 +63,7 @@ class HostCpuEngine {
71 std::mutex mu_;63 std::mutex mu_;
72 std::vector<void *> lib_handles_;64 std::vector<void *> lib_handles_;
73 void *constant_folding_handle_ = nullptr;65 void *constant_folding_handle_ = nullptr;
74- HostKernelFinder host_kernel_finder_ = nullptr;66+ int32_t (*is_fused_cpu_kernel_supported_)(const char *) = nullptr;
75 bool initialized_ = false;67 bool initialized_ = false;
76};68};
77} // namespace ge69} // namespace ge
@@ -18,7 +18,11 @@
18#include <vector>18#include <vector>
19#include <set>19#include <set>
20#include <string>20#include <string>
21+#include <memory>
22+#include <new>
23+#include <limits>
21#include <unordered_map>24#include <unordered_map>
25+#include <unordered_set>
22#include <dirent.h>26#include <dirent.h>
23#include <dlfcn.h>27#include <dlfcn.h>
24#include <cstring>28#include <cstring>
@@ -54,6 +58,8 @@ const char *const kExcludedConstantFoldingSo = "libconstant_folding_ops.so";
54const char *const kSymGetAllRegisteredOpTypesV2 = "GetAllRegisteredOpTypesV2";58const char *const kSymGetAllRegisteredOpTypesV2 = "GetAllRegisteredOpTypesV2";
55const char *const kSymIsRegisteredV2 = "IsRegisteredV2";59const char *const kSymIsRegisteredV2 = "IsRegisteredV2";
56const char *const kSymRunCpuKernelV2 = "RunCpuKernelV2";60const char *const kSymRunCpuKernelV2 = "RunCpuKernelV2";
61+constexpr uint32_t kFusedHostCpuShapeChanged = 1U;
62+constexpr uint32_t kFusedHostCpuDataChanged = 2U;
57 63 
58using AttrValueMap = google::protobuf::Map<string, aicpuops::AttrValue>;64using AttrValueMap = google::protobuf::Map<string, aicpuops::AttrValue>;
59 65 
@@ -69,7 +75,62 @@ struct V2ModuleBinding {
69 std::string so_name;75 std::string so_name;
70};76};
71 77 
78+struct FusedTensorBindingState {
79+ ge::DataType data_type = ge::DT_UNDEFINED;
80+ ge::Format format = ge::FORMAT_RESERVED;
81+ const void *data = nullptr;
82+ size_t data_size = 0U;
83+ std::vector<int64_t> dims;
84+ bool initialized = false;
85+};
86+ 
87+struct FusedCpuKernelPlan {
88+ std::unique_ptr<aicpuops::NodeDef> node_def;
89+ std::unique_ptr<aicpu::CpuKernelContext> context;
90+ const V2ModuleBinding *v2_binding = nullptr;
91+ std::shared_ptr<aicpu::CpuKernel> v1_kernel;
92+ std::vector<aicpu::Tensor *> input_tensors;
93+ std::vector<aicpu::Tensor *> output_tensors;
94+ std::vector<FusedTensorBindingState> input_states;
95+ std::vector<FusedTensorBindingState> output_states;
96+};
97+ 
98+struct FusedCpuKernelChainNodeDesc {
99+ const ge::Operator *op;
100+ const ge::Tensor *const *inputs;
101+ size_t input_num;
102+ ge::Tensor *const *outputs;
103+ size_t output_num;
104+ const int32_t *input_binding_indices;
105+ const int32_t *output_binding_indices;
106+};
107+ 
108+struct FusedHostCpuTensorBinding {
109+ const int64_t *dims;
110+ uint8_t *data;
111+ size_t dim_num;
112+ size_t data_size;
113+ uint32_t flags;
114+};
115+ 
116+struct FusedCpuKernelBinding {
117+ const ge::Tensor *source;
118+ aicpu::Tensor *target;
119+ FusedTensorBindingState *state;
120+ size_t binding_index;
121+};
122+ 
123+struct FusedCpuKernelChainNode {
124+ FusedCpuKernelPlan plan;
125+};
126+ 
127+struct FusedCpuKernelChainPlan {
128+ std::vector<FusedCpuKernelChainNode> nodes;
129+ std::vector<FusedCpuKernelBinding> bindings;
130+};
131+ 
72std::vector<V2ModuleBinding> g_v2_bindings;132std::vector<V2ModuleBinding> g_v2_bindings;
133+std::unordered_set<std::string> g_v1_op_types;
73// op_type->binding反向索引, Init阶段一次性构建, 运行期只读。134// op_type->binding反向索引, Init阶段一次性构建, 运行期只读。
74std::unordered_map<std::string, const V2ModuleBinding *> g_v2_op_index;135std::unordered_map<std::string, const V2ModuleBinding *> g_v2_op_index;
75 136 
@@ -95,6 +156,27 @@ void ConvertGeToAicpuTensor(const ge::GeTensorDesc &tensor_desc, const std::stri
95 static_cast<int>(tensor_desc.GetDataType()), ge_tensor.GetData(), ge_tensor.GetSize());156 static_cast<int>(tensor_desc.GetDataType()), ge_tensor.GetData(), ge_tensor.GetSize());
96}157}
97 158 
159+void ConvertFusedGeToAicpuTensor(const std::string &tensor_name, const ge::Tensor &ge_tensor,
160+ aicpuops::Tensor *aicpu_tensor) {
161+ aicpu_tensor->set_name(tensor_name);
162+ aicpu_tensor->set_tensor_type(ge_tensor.GetDataType());
163+ aicpu_tensor->set_data_ptr(static_cast<uint64_t>(reinterpret_cast<intptr_t>(ge_tensor.GetData())));
164+ aicpu_tensor->set_data_size(static_cast<uint64_t>(ge_tensor.GetSize()));
165+ auto shape = aicpu_tensor->mutable_tensor_shape();
166+ if (shape != nullptr) {
167+ shape->clear_dim();
168+ for (size_t i = 0U; i < ge_tensor.GetShapeDimNum(); ++i) {
169+ aicpuops::TensorShape_Dim *aicpu_dim = shape->add_dim();
170+ if (aicpu_dim != nullptr) {
171+ aicpu_dim->set_size(ge_tensor.GetShapeDim(i));
172+ }
173+ }
174+ shape->set_data_format(ge_tensor.GetFormat());
175+ }
176+ AICPUE_LOGI("Op set fused tensor[%s], tensor info[type:%d, data:%p, size:%llu].", tensor_name.c_str(),
177+ static_cast<int>(ge_tensor.GetDataType()), ge_tensor.GetData(), ge_tensor.GetSize());
178+}
179+ 
98int32_t AddStringAttrToNodeDef(const ge::Operator &op, const char *name, [[maybe_unused]] aicpuops::NodeDef node_def,180int32_t AddStringAttrToNodeDef(const ge::Operator &op, const char *name, [[maybe_unused]] aicpuops::NodeDef node_def,
99 aicpuops::AttrValue &attr_value) {181 aicpuops::AttrValue &attr_value) {
100 std::string s;182 std::string s;
@@ -526,6 +608,8 @@ __attribute__((visibility("default"))) int32_t InitCpuConstantFoldingNew(ge::Hos
526 608 
527 std::vector<std::string> ops = aicpu::CpuKernelRegister::Instance().GetAllRegisteredOpTypes();609 std::vector<std::string> ops = aicpu::CpuKernelRegister::Instance().GetAllRegisteredOpTypes();
528 AICPUE_LOGI("Registered V1 ops: %llu", static_cast<uint64_t>(ops.size()));610 AICPUE_LOGI("Registered V1 ops: %llu", static_cast<uint64_t>(ops.size()));
611+ g_v1_op_types.clear();
612+ g_v1_op_types.insert(ops.cbegin(), ops.cend());
529 RegisterHostCpuOp(ops, create_fn);613 RegisterHostCpuOp(ops, create_fn);
530 614 
531 // 枚举每个ops so的V2算子, 同时构建op_type->binding反向索引。615 // 枚举每个ops so的V2算子, 同时构建op_type->binding反向索引。
@@ -572,7 +656,7 @@ int32_t BuildInputTensors(const ge::OpDescPtr &op_desc, const std::map<std::stri
572 return 0;656 return 0;
573}657}
574 658 
575-int32_t BuildOutputTensors(const ge::OpDescPtr &op_desc, std::map<std::string, ge::Tensor> &outputs,659+int32_t BuildOutputTensors(const ge::OpDescPtr &op_desc, const std::map<std::string, ge::Tensor> &outputs,
576 const char *op_type, aicpuops::NodeDef &node_def) {660 const char *op_type, aicpuops::NodeDef &node_def) {
577 uint32_t count = static_cast<uint32_t>(op_desc->GetOutputsSize());661 uint32_t count = static_cast<uint32_t>(op_desc->GetOutputsSize());
578 for (uint32_t i = 0; i < count; ++i) {662 for (uint32_t i = 0; i < count; ++i) {
@@ -592,6 +676,55 @@ int32_t BuildOutputTensors(const ge::OpDescPtr &op_desc, std::map<std::string, g
592 return 0;676 return 0;
593}677}
594 678 
679+int32_t BuildFusedInputTensorArray(const ge::OpDescPtr &op_desc, const ge::Tensor *const *inputs,
680+ const size_t input_num, aicpuops::NodeDef &node_def) {
681+ const size_t count = op_desc->GetAllInputsSize();
682+ if ((count != input_num) || ((count != 0U) && (inputs == nullptr))) {
683+ AICPUE_LOGE("Invalid fused input tensor array: op[%s], expected_num[%zu], actual_num[%zu], inputs_null[%d].",
684+ AICPUE_ERROR_CODE, op_desc->GetTypePtr(), count, input_num, static_cast<int32_t>(inputs == nullptr));
685+ return -1;
686+ }
687+ for (size_t i = 0U; i < count; ++i) {
688+ if (inputs[i] == nullptr) {
689+ AICPUE_LOGE("Fused input tensor is null: op[%s], input_index[%zu].", AICPUE_ERROR_CODE, op_desc->GetTypePtr(), i);
690+ return -1;
691+ }
692+ aicpuops::Tensor *tensor = node_def.add_inputs();
693+ if (tensor == nullptr) {
694+ AICPUE_LOGE("Failed to add fused input tensor to NodeDef: op[%s], input_index[%zu].", AICPUE_ERROR_CODE,
695+ op_desc->GetTypePtr(), i);
696+ return -1;
697+ }
698+ ConvertFusedGeToAicpuTensor(op_desc->GetInputNameByIndex(static_cast<uint32_t>(i)), *inputs[i], tensor);
699+ }
700+ return 0;
701+}
702+ 
703+int32_t BuildFusedOutputTensorArray(const ge::OpDescPtr &op_desc, ge::Tensor *const *outputs, const size_t output_num,
704+ aicpuops::NodeDef &node_def) {
705+ const size_t count = op_desc->GetOutputsSize();
706+ if ((count != output_num) || ((count != 0U) && (outputs == nullptr))) {
707+ AICPUE_LOGE("Invalid fused output tensor array: op[%s], expected_num[%zu], actual_num[%zu], outputs_null[%d].",
708+ AICPUE_ERROR_CODE, op_desc->GetTypePtr(), count, output_num, static_cast<int32_t>(outputs == nullptr));
709+ return -1;
710+ }
711+ for (size_t i = 0U; i < count; ++i) {
712+ if (outputs[i] == nullptr) {
713+ AICPUE_LOGE("Fused output tensor is null: op[%s], output_index[%zu].", AICPUE_ERROR_CODE, op_desc->GetTypePtr(),
714+ i);
715+ return -1;
716+ }
717+ aicpuops::Tensor *tensor = node_def.add_outputs();
718+ if (tensor == nullptr) {
719+ AICPUE_LOGE("Failed to add fused output tensor to NodeDef: op[%s], output_index[%zu].", AICPUE_ERROR_CODE,
720+ op_desc->GetTypePtr(), i);
721+ return -1;
722+ }
723+ ConvertFusedGeToAicpuTensor(op_desc->GetOutputNameByIndex(static_cast<uint32_t>(i)), *outputs[i], tensor);
724+ }
725+ return 0;
726+}
727+ 
595int32_t BuildNodeDefAttrs(const ge::Operator &op, aicpuops::NodeDef &node_def) {728int32_t BuildNodeDefAttrs(const ge::Operator &op, aicpuops::NodeDef &node_def) {
596 std::map<ge::AscendString, ge::AscendString> attrs;729 std::map<ge::AscendString, ge::AscendString> attrs;
597 if (op.GetAllAttrNamesAndTypes(attrs) != ge::GRAPH_SUCCESS) {730 if (op.GetAllAttrNamesAndTypes(attrs) != ge::GRAPH_SUCCESS) {
@@ -637,6 +770,277 @@ int32_t BuildNodeDef(const ge::Operator &op, const std::string &op_type_str,
637 return BuildNodeDefAttrs(op, node_def);770 return BuildNodeDefAttrs(op, node_def);
638}771}
639 772 
773+int32_t BuildFusedNodeDefFromTensorArray(const ge::Operator &op, const std::string &op_type_str,
774+ const ge::Tensor *const *inputs, const size_t input_num,
775+ ge::Tensor *const *outputs, const size_t output_num,
776+ aicpuops::NodeDef &node_def) {
777+ const ge::OpDescPtr op_desc = ge::OpDescUtils::GetOpDescFromOperator(op);
778+ if (op_desc == nullptr) {
779+ AICPUE_LOGW("Op[%s] get op desc failed.", op_type_str.c_str());
780+ return -1;
781+ }
782+ node_def.set_op(op_type_str);
783+ int32_t ret = BuildFusedInputTensorArray(op_desc, inputs, input_num, node_def);
784+ if (ret != 0) {
785+ return ret;
786+ }
787+ ret = BuildFusedOutputTensorArray(op_desc, outputs, output_num, node_def);
788+ if (ret != 0) {
789+ return ret;
790+ }
791+ return BuildNodeDefAttrs(op, node_def);
792+}
793+ 
794+bool HasSameShape(const ge::Tensor &source, const FusedTensorBindingState &state) {
795+ const size_t dim_num = source.GetShapeDimNum();
796+ if (state.dims.size() != dim_num) {
797+ return false;
798+ }
799+ for (size_t i = 0U; i < dim_num; ++i) {
800+ if (state.dims[i] != source.GetShapeDim(i)) {
801+ return false;
802+ }
803+ }
804+ return true;
805+}
806+ 
807+int32_t InitializeFusedTensor(const ge::Tensor &source, aicpu::Tensor *target, FusedTensorBindingState &state) {
808+ const void *data = static_cast<const void *>(source.GetData());
809+ const size_t data_size = source.GetSize();
810+ target->SetData(const_cast<void *>(data));
811+ target->SetDataSize(static_cast<uint64_t>(data_size));
812+ target->SetDataType(static_cast<aicpu::DataType>(source.GetDataType()));
813+ 
814+ const std::shared_ptr<aicpu::TensorShape> tensor_shape = target->GetTensorShape();
815+ if (tensor_shape == nullptr) {
816+ AICPUE_LOGE("Failed to get target TensorShape while initializing fused Tensor.", AICPUE_ERROR_CODE);
817+ return -1;
818+ }
819+ state.dims.resize(source.GetShapeDimNum());
820+ for (size_t i = 0U; i < state.dims.size(); ++i) {
821+ state.dims[i] = source.GetShapeDim(i);
822+ }
823+ tensor_shape->SetDimSizes(state.dims);
824+ const ge::Format format = source.GetFormat();
825+ tensor_shape->SetFormat(static_cast<aicpu::Format>(format));
826+ 
827+ state.data = data;
828+ state.data_size = data_size;
829+ state.data_type = source.GetDataType();
830+ state.format = format;
831+ state.initialized = true;
832+ return 0;
833+}
834+ 
835+int32_t RebindFusedTensor(const ge::Tensor &source, aicpu::Tensor *target, FusedTensorBindingState &state) {
836+ if ((target == nullptr) || ((source.GetSize() != 0U) && (source.GetData() == nullptr))) {
837+ AICPUE_LOGE("Failed to rebind fused Tensor: target_null[%d], data_null[%d], data_size[%zu].", AICPUE_ERROR_CODE,
838+ static_cast<int32_t>(target == nullptr),
839+ static_cast<int32_t>((source.GetSize() != 0U) && (source.GetData() == nullptr)), source.GetSize());
840+ return -1;
841+ }
842+ if (!state.initialized) {
843+ return InitializeFusedTensor(source, target, state);
844+ }
845+ const void *data = static_cast<const void *>(source.GetData());
846+ const size_t data_size = source.GetSize();
847+ if (state.data != data) {
848+ target->SetData(const_cast<void *>(data));
849+ state.data = data;
850+ }
851+ if (state.data_size != data_size) {
852+ target->SetDataSize(static_cast<uint64_t>(data_size));
853+ state.data_size = data_size;
854+ }
855+ 
856+ const ge::DataType data_type = source.GetDataType();
857+ if (state.data_type != data_type) {
858+ target->SetDataType(static_cast<aicpu::DataType>(data_type));
859+ state.data_type = data_type;
860+ }
861+ 
862+ const ge::Format format = source.GetFormat();
863+ const bool shape_changed = !HasSameShape(source, state);
864+ if (shape_changed || (state.format != format)) {
865+ const std::shared_ptr<aicpu::TensorShape> tensor_shape = target->GetTensorShape();
866+ if (tensor_shape == nullptr) {
867+ AICPUE_LOGE("Failed to get target TensorShape while rebinding fused Tensor.", AICPUE_ERROR_CODE);
868+ return -1;
869+ }
870+ if (shape_changed) {
871+ state.dims.resize(source.GetShapeDimNum());
872+ for (size_t i = 0U; i < state.dims.size(); ++i) {
873+ state.dims[i] = source.GetShapeDim(i);
874+ }
875+ tensor_shape->SetDimSizes(state.dims);
876+ }
877+ tensor_shape->SetFormat(static_cast<aicpu::Format>(format));
878+ state.format = format;
879+ }
880+ state.initialized = true;
881+ return 0;
882+}
883+ 
884+int32_t RebindFusedTensorDataByFlags(const ge::Tensor &source, aicpu::Tensor *target, FusedTensorBindingState &state,
885+ const uint32_t binding_flags) {
886+ if ((binding_flags & kFusedHostCpuDataChanged) == 0U) {
887+ return 0;
888+ }
889+ const void *data = static_cast<const void *>(source.GetData());
890+ const size_t data_size = source.GetSize();
891+ if ((data_size != 0U) && (data == nullptr)) {
892+ AICPUE_LOGE("Failed to rebind fused Tensor data by flags: data is null, data_size[%zu], binding_flags[%u].",
893+ AICPUE_ERROR_CODE, data_size, binding_flags);
894+ return -1;
895+ }
896+ if (state.data != data) {
897+ target->SetData(const_cast<void *>(data));
898+ state.data = data;
899+ }
900+ if (state.data_size != data_size) {
901+ target->SetDataSize(static_cast<uint64_t>(data_size));
902+ state.data_size = data_size;
903+ }
904+ return 0;
905+}
906+ 
907+int32_t RebindFusedTensorShapeByFlags(const ge::Tensor &source, aicpu::Tensor *target, FusedTensorBindingState &state,
908+ const uint32_t binding_flags) {
909+ if ((binding_flags & kFusedHostCpuShapeChanged) == 0U) {
910+ return 0;
911+ }
912+ const ge::DataType data_type = source.GetDataType();
913+ if (state.data_type != data_type) {
914+ target->SetDataType(static_cast<aicpu::DataType>(data_type));
915+ state.data_type = data_type;
916+ }
917+ 
918+ const ge::Format format = source.GetFormat();
919+ const bool shape_changed = !HasSameShape(source, state);
920+ if (shape_changed || (state.format != format)) {
921+ const std::shared_ptr<aicpu::TensorShape> tensor_shape = target->GetTensorShape();
922+ if (tensor_shape == nullptr) {
923+ AICPUE_LOGE("Failed to get target TensorShape while rebinding fused Tensor by flags: binding_flags[%u].",
924+ AICPUE_ERROR_CODE, binding_flags);
925+ return -1;
926+ }
927+ if (shape_changed) {
928+ state.dims.resize(source.GetShapeDimNum());
929+ for (size_t i = 0U; i < state.dims.size(); ++i) {
930+ state.dims[i] = source.GetShapeDim(i);
931+ }
932+ tensor_shape->SetDimSizes(state.dims);
933+ }
934+ tensor_shape->SetFormat(static_cast<aicpu::Format>(format));
935+ state.format = format;
936+ }
937+ return 0;
938+}
939+ 
940+int32_t RebindFusedTensorByFlags(const ge::Tensor &source, aicpu::Tensor *target, FusedTensorBindingState &state,
941+ uint32_t binding_flags) {
942+ if (target == nullptr) {
943+ AICPUE_LOGE("Failed to rebind fused Tensor by flags: target is null, binding_flags[%u].", AICPUE_ERROR_CODE,
944+ binding_flags);
945+ return -1;
946+ }
947+ if (!state.initialized) {
948+ return RebindFusedTensor(source, target, state);
949+ }
950+ if ((RebindFusedTensorDataByFlags(source, target, state, binding_flags) != 0) ||
951+ (RebindFusedTensorShapeByFlags(source, target, state, binding_flags) != 0)) {
952+ return -1;
953+ }
954+ state.initialized = true;
955+ return 0;
956+}
957+ 
958+int32_t RebindFusedTensorByBinding(const FusedHostCpuTensorBinding &binding, aicpu::Tensor *target,
959+ FusedTensorBindingState &state) {
960+ if ((target == nullptr) || ((binding.dim_num != 0U) && (binding.dims == nullptr)) ||
961+ ((binding.data_size != 0U) && (binding.data == nullptr))) {
962+ AICPUE_LOGE(
963+ "Invalid fused Tensor binding: target_null[%d], dim_num[%zu], dims_null[%d], data_size[%zu], "
964+ "data_null[%d], flags[%u].",
965+ AICPUE_ERROR_CODE, static_cast<int32_t>(target == nullptr), binding.dim_num,
966+ static_cast<int32_t>((binding.dim_num != 0U) && (binding.dims == nullptr)), binding.data_size,
967+ static_cast<int32_t>((binding.data_size != 0U) && (binding.data == nullptr)), binding.flags);
968+ return -1;
969+ }
970+ uint32_t binding_flags = binding.flags;
971+ if (!state.initialized) {
972+ binding_flags |= kFusedHostCpuShapeChanged | kFusedHostCpuDataChanged;
973+ }
974+ if ((binding_flags & kFusedHostCpuDataChanged) != 0U) {
975+ target->SetData(binding.data);
976+ target->SetDataSize(static_cast<uint64_t>(binding.data_size));
977+ state.data = binding.data;
978+ state.data_size = binding.data_size;
979+ }
980+ if ((binding_flags & kFusedHostCpuShapeChanged) != 0U) {
981+ const std::shared_ptr<aicpu::TensorShape> tensor_shape = target->GetTensorShape();
982+ if (tensor_shape == nullptr) {
983+ AICPUE_LOGE("Failed to get target TensorShape from fused binding: dim_num[%zu], flags[%u].", AICPUE_ERROR_CODE,
984+ binding.dim_num, binding.flags);
985+ return -1;
986+ }
987+ state.dims.resize(binding.dim_num);
988+ for (size_t i = 0U; i < binding.dim_num; ++i) {
989+ state.dims[i] = binding.dims[i];
990+ }
991+ tensor_shape->SetDimSizes(state.dims);
992+ }
993+ state.initialized = true;
994+ return 0;
995+}
996+ 
997+int32_t RebindFusedPlan(FusedCpuKernelPlan &plan, const ge::Tensor *const *inputs, const size_t input_num,
998+ ge::Tensor *const *outputs, const size_t output_num) {
999+ if ((plan.context == nullptr) || (input_num != plan.input_tensors.size()) ||
1000+ (output_num != plan.output_tensors.size()) || ((input_num != 0U) && (inputs == nullptr)) ||
1001+ ((output_num != 0U) && (outputs == nullptr))) {
1002+ AICPUE_LOGE(
1003+ "Invalid fused CPU plan binding: context_null[%d], input_num[%zu], expected_inputs[%zu], "
1004+ "output_num[%zu], expected_outputs[%zu], inputs_null[%d], outputs_null[%d].",
1005+ AICPUE_ERROR_CODE, static_cast<int32_t>(plan.context == nullptr), input_num, plan.input_tensors.size(),
1006+ output_num, plan.output_tensors.size(), static_cast<int32_t>(inputs == nullptr),
1007+ static_cast<int32_t>(outputs == nullptr));
1008+ return -1;
1009+ }
1010+ for (size_t i = 0U; i < input_num; ++i) {
1011+ if ((inputs[i] == nullptr) || (RebindFusedTensor(*inputs[i], plan.input_tensors[i], plan.input_states[i]) != 0)) {
1012+ AICPUE_LOGE("Failed to rebind fused CPU plan input: input_index[%zu], input_null[%d].", AICPUE_ERROR_CODE, i,
1013+ static_cast<int32_t>(inputs[i] == nullptr));
1014+ return -1;
1015+ }
1016+ }
1017+ for (size_t i = 0U; i < output_num; ++i) {
1018+ if ((outputs[i] == nullptr) ||
1019+ (RebindFusedTensor(*outputs[i], plan.output_tensors[i], plan.output_states[i]) != 0)) {
1020+ AICPUE_LOGE("Failed to rebind fused CPU plan output: output_index[%zu], output_null[%d].", AICPUE_ERROR_CODE, i,
1021+ static_cast<int32_t>(outputs[i] == nullptr));
1022+ return -1;
1023+ }
1024+ }
1025+ return 0;
1026+}
1027+ 
1028+int32_t RunFusedCpuKernelPlan(FusedCpuKernelPlan &plan) {
1029+ uint32_t ret = 0U;
1030+ if (plan.v2_binding != nullptr) {
1031+ ret = plan.v2_binding->run_cpu_kernel(*plan.context);
1032+ } else if (plan.v1_kernel != nullptr) {
1033+ ret = plan.v1_kernel->Compute(*plan.context);
1034+ } else {
1035+ AICPUE_LOGE("Fused CPU kernel plan has neither V1 kernel nor V2 binding.", AICPUE_ERROR_CODE);
1036+ return -1;
1037+ }
1038+ if (ret != 0U) {
1039+ AICPUE_LOGE("Fused CPU kernel execution failed: ret[%u].", AICPUE_ERROR_CODE, ret);
1040+ }
1041+ return (ret == 0U) ? 0 : -1;
1042+}
1043+ 
640// 查找op_type对应的V2 binding。未命中返回nullptr表示走V1路径。1044// 查找op_type对应的V2 binding。未命中返回nullptr表示走V1路径。
641const V2ModuleBinding *LookupV2Binding(const std::string &op_type) {1045const V2ModuleBinding *LookupV2Binding(const std::string &op_type) {
642 auto iter = g_v2_op_index.find(op_type);1046 auto iter = g_v2_op_index.find(op_type);
@@ -650,6 +1054,14 @@ const V2ModuleBinding *LookupV2Binding(const std::string &op_type) {
650 return binding;1054 return binding;
651}1055}
652 1056 
1057+__attribute__((visibility("default"))) int32_t IsCpuConstantFoldingFusedOpSupported(const char *op_type) {
1058+ if ((op_type == nullptr) || (op_type[0] == '\0')) {
1059+ return 0;
1060+ }
1061+ const std::string op_type_str(op_type);
1062+ return ((LookupV2Binding(op_type_str) != nullptr) || (g_v1_op_types.count(op_type_str) > 0U)) ? 1 : 0;
1063+}
1064+ 
653__attribute__((visibility("default"))) int32_t1065__attribute__((visibility("default"))) int32_t
654CpuConstantFoldingComputeNew(const ge::Operator &op, const std::map<std::string, const ge::Tensor> &inputs,1066CpuConstantFoldingComputeNew(const ge::Operator &op, const std::map<std::string, const ge::Tensor> &inputs,
655 std::map<std::string, ge::Tensor> outputs) {1067 std::map<std::string, ge::Tensor> outputs) {
@@ -697,4 +1109,302 @@ CpuConstantFoldingComputeNew(const ge::Operator &op, const std::map<std::string,
697 AICPUE_LOGI("Finish cpu op[%s].", op_type.GetString());1109 AICPUE_LOGI("Finish cpu op[%s].", op_type.GetString());
698 return 0;1110 return 0;
699}1111}
1112+ 
1113+int32_t InitializeFusedCpuKernel(const ge::Operator &op, FusedCpuKernelPlan &plan, std::string &op_type_str,
1114+ ge::AscendString &op_type) {
1115+ if (op.GetOpType(op_type) != ge::GRAPH_SUCCESS) {
1116+ return -1;
1117+ }
1118+ op_type_str = op_type.GetString();
1119+ plan.v2_binding = LookupV2Binding(op_type_str);
1120+ if (plan.v2_binding == nullptr) {
1121+ plan.v1_kernel = aicpu::CpuKernelRegister::Instance().GetCpuKernel(op_type_str);
1122+ if (plan.v1_kernel == nullptr) {
1123+ AICPUE_LOGW("op type [%s] is not registered in v1 nor v2.", op_type.GetString());
1124+ return -1;
1125+ }
1126+ }
1127+ return 0;
1128+}
1129+ 
1130+int32_t AllocateFusedCpuKernelPlan(FusedCpuKernelPlan &plan, const ge::AscendString &op_type) {
1131+ plan.node_def.reset(new (std::nothrow) aicpuops::NodeDef());
1132+ plan.context.reset(new (std::nothrow) aicpu::CpuKernelContext(aicpu::HOST));
1133+ if ((plan.node_def == nullptr) || (plan.context == nullptr)) {
1134+ AICPUE_LOGE("Failed to allocate fused CPU kernel plan objects: op[%s], node_def_null[%d], context_null[%d].",
1135+ AICPUE_ERROR_CODE, op_type.GetString(), static_cast<int32_t>(plan.node_def == nullptr),
1136+ static_cast<int32_t>(plan.context == nullptr));
1137+ return -1;
1138+ }
1139+ return 0;
1140+}
1141+ 
1142+int32_t InitializeFusedCpuKernelTensors(FusedCpuKernelPlan &plan, const size_t input_num, const size_t output_num,
1143+ const ge::AscendString &op_type) {
1144+ plan.input_tensors.resize(input_num);
1145+ plan.output_tensors.resize(output_num);
1146+ plan.input_states.resize(input_num);
1147+ plan.output_states.resize(output_num);
1148+ for (size_t i = 0U; i < input_num; ++i) {
1149+ plan.input_tensors[i] = plan.context->Input(static_cast<uint32_t>(i));
1150+ if (plan.input_tensors[i] == nullptr) {
1151+ AICPUE_LOGE("Fused CPU kernel context input is null: op[%s], input_index[%zu], input_num[%zu].",
1152+ AICPUE_ERROR_CODE, op_type.GetString(), i, input_num);
1153+ return -1;
1154+ }
1155+ }
1156+ for (size_t i = 0U; i < output_num; ++i) {
1157+ plan.output_tensors[i] = plan.context->Output(static_cast<uint32_t>(i));
1158+ if (plan.output_tensors[i] == nullptr) {
1159+ AICPUE_LOGE("Fused CPU kernel context output is null: op[%s], output_index[%zu], output_num[%zu].",
1160+ AICPUE_ERROR_CODE, op_type.GetString(), i, output_num);
1161+ return -1;
1162+ }
1163+ }
1164+ return 0;
1165+}
1166+ 
1167+int32_t InitializeFusedCpuKernelPlan(const ge::Operator &op, const ge::Tensor *const *inputs, const size_t input_num,
1168+ ge::Tensor *const *outputs, const size_t output_num, FusedCpuKernelPlan &plan) {
1169+ ge::AscendString op_type;
1170+ std::string op_type_str;
1171+ if (InitializeFusedCpuKernel(op, plan, op_type_str, op_type) != 0) {
1172+ return -1;
1173+ }
1174+ if (AllocateFusedCpuKernelPlan(plan, op_type) != 0) {
1175+ return -1;
1176+ }
1177+ if (BuildFusedNodeDefFromTensorArray(op, op_type_str, inputs, input_num, outputs, output_num, *plan.node_def) != 0) {
1178+ AICPUE_LOGE("Failed to build fused CPU NodeDef: op[%s], inputs[%zu], outputs[%zu].", AICPUE_ERROR_CODE,
1179+ op_type.GetString(), input_num, output_num);
1180+ return -1;
1181+ }
1182+ const int32_t context_ret = plan.context->Init(plan.node_def.get());
1183+ if (context_ret != 0) {
1184+ AICPUE_LOGE("Failed to initialize fused CPU kernel context: op[%s], ret[%d].", AICPUE_ERROR_CODE,
1185+ op_type.GetString(), context_ret);
1186+ return -1;
1187+ }
1188+ if (InitializeFusedCpuKernelTensors(plan, input_num, output_num, op_type) != 0) {
1189+ return -1;
1190+ }
1191+ if (RebindFusedPlan(plan, inputs, input_num, outputs, output_num) != 0) {
1192+ AICPUE_LOGE("Failed to bind fused CPU kernel plan tensors: op[%s], inputs[%zu], outputs[%zu].", AICPUE_ERROR_CODE,
1193+ op_type.GetString(), input_num, output_num);
1194+ return -1;
1195+ }
1196+ AICPUE_LOGD("Created fused cpu execution plan for op[%s], inputs[%zu], outputs[%zu].", op_type.GetString(), input_num,
1197+ output_num);
1198+ return 0;
1199+}
1200+ 
1201+int32_t CalculateFusedChainBindingCapacity(const FusedCpuKernelChainNodeDesc *descs, const size_t node_num,
1202+ size_t &binding_capacity) {
1203+ binding_capacity = 0U;
1204+ for (size_t i = 0U; i < node_num; ++i) {
1205+ if (descs[i].input_num > (std::numeric_limits<size_t>::max() - descs[i].output_num)) {
1206+ AICPUE_LOGE("Fused CPU chain node binding count overflows size_t: node_index[%zu], inputs[%zu], outputs[%zu].",
1207+ AICPUE_ERROR_CODE, i, descs[i].input_num, descs[i].output_num);
1208+ return -1;
1209+ }
1210+ const size_t node_binding_count = descs[i].input_num + descs[i].output_num;
1211+ if (binding_capacity > (std::numeric_limits<size_t>::max() - node_binding_count)) {
1212+ AICPUE_LOGE(
1213+ "Fused CPU chain binding capacity overflows size_t: node_index[%zu], current_capacity[%zu], "
1214+ "node_binding_count[%zu].",
1215+ AICPUE_ERROR_CODE, i, binding_capacity, node_binding_count);
1216+ return -1;
1217+ }
1218+ binding_capacity += node_binding_count;
1219+ }
1220+ return 0;
1221+}
1222+ 
1223+int32_t AddFusedChainInputBindings(const FusedCpuKernelChainNodeDesc &desc, const size_t node_index,
1224+ const size_t external_binding_num, FusedCpuKernelChainNode &node,
1225+ FusedCpuKernelChainPlan &chain) {
1226+ for (size_t j = 0U; j < desc.input_num; ++j) {
1227+ const int32_t binding_index =
1228+ (desc.input_binding_indices == nullptr) ? static_cast<int32_t>(j) : desc.input_binding_indices[j];
1229+ if ((binding_index >= 0) && (static_cast<size_t>(binding_index) < external_binding_num) &&
1230+ (desc.inputs[j] != nullptr)) {
1231+ chain.bindings.push_back(
1232+ {desc.inputs[j], node.plan.input_tensors[j], &node.plan.input_states[j], static_cast<size_t>(binding_index)});
1233+ } else if ((binding_index >= 0) &&
1234+ ((desc.inputs[j] == nullptr) || (static_cast<size_t>(binding_index) >= external_binding_num))) {
1235+ AICPUE_LOGE(
1236+ "Invalid fused input binding: node_index[%zu], input_index[%zu], binding_index[%d], "
1237+ "external_binding_num[%zu], input_null[%d].",
1238+ AICPUE_ERROR_CODE, node_index, j, binding_index, external_binding_num,
1239+ static_cast<int32_t>(desc.inputs[j] == nullptr));
1240+ return -1;
1241+ }
1242+ }
1243+ return 0;
1244+}
1245+ 
1246+int32_t AddFusedChainOutputBindings(const FusedCpuKernelChainNodeDesc &desc, const size_t node_index,
1247+ const size_t external_binding_num, FusedCpuKernelChainNode &node,
1248+ FusedCpuKernelChainPlan &chain) {
1249+ for (size_t j = 0U; j < desc.output_num; ++j) {
1250+ const int32_t binding_index =
1251+ (desc.output_binding_indices == nullptr) ? static_cast<int32_t>(j) : desc.output_binding_indices[j];
1252+ if ((binding_index >= 0) && (static_cast<size_t>(binding_index) < external_binding_num) &&
1253+ (desc.outputs[j] != nullptr)) {
1254+ chain.bindings.push_back({desc.outputs[j], node.plan.output_tensors[j], &node.plan.output_states[j],
1255+ static_cast<size_t>(binding_index)});
1256+ } else if ((binding_index >= 0) &&
1257+ ((desc.outputs[j] == nullptr) || (static_cast<size_t>(binding_index) >= external_binding_num))) {
1258+ AICPUE_LOGE(
1259+ "Invalid fused output binding: node_index[%zu], output_index[%zu], binding_index[%d], "
1260+ "external_binding_num[%zu], output_null[%d].",
1261+ AICPUE_ERROR_CODE, node_index, j, binding_index, external_binding_num,
1262+ static_cast<int32_t>(desc.outputs[j] == nullptr));
1263+ return -1;
1264+ }
1265+ }
1266+ return 0;
1267+}
1268+ 
1269+int32_t InitializeFusedChainNode(const FusedCpuKernelChainNodeDesc &desc, const size_t node_index,
1270+ const size_t external_binding_num, FusedCpuKernelChainPlan &chain) {
1271+ if ((desc.op == nullptr) || ((desc.input_num != 0U) && (desc.inputs == nullptr)) ||
1272+ ((desc.output_num != 0U) && (desc.outputs == nullptr))) {
1273+ AICPUE_LOGE(
1274+ "Invalid fused CPU chain node descriptor: node_index[%zu], op_null[%d], input_num[%zu], "
1275+ "inputs_null[%d], output_num[%zu], outputs_null[%d].",
1276+ AICPUE_ERROR_CODE, node_index, static_cast<int32_t>(desc.op == nullptr), desc.input_num,
1277+ static_cast<int32_t>(desc.inputs == nullptr), desc.output_num, static_cast<int32_t>(desc.outputs == nullptr));
1278+ return -1;
1279+ }
1280+ chain.nodes.emplace_back();
1281+ FusedCpuKernelChainNode &node = chain.nodes.back();
1282+ const int32_t init_ret =
1283+ InitializeFusedCpuKernelPlan(*desc.op, desc.inputs, desc.input_num, desc.outputs, desc.output_num, node.plan);
1284+ if (init_ret != 0) {
1285+ AICPUE_LOGE("Initialize fused CPU kernel plan failed: node_index[%zu], input_num[%zu], output_num[%zu], ret[%d].",
1286+ AICPUE_ERROR_CODE, node_index, desc.input_num, desc.output_num, init_ret);
1287+ return -1;
1288+ }
1289+ if ((node.plan.input_tensors.size() != desc.input_num) || (node.plan.input_states.size() != desc.input_num) ||
1290+ (node.plan.output_tensors.size() != desc.output_num) || (node.plan.output_states.size() != desc.output_num)) {
1291+ AICPUE_LOGE(
1292+ "Fused CPU kernel plan size mismatch: node_index[%zu], expected inputs[%zu], input_states[%zu], "
1293+ "outputs[%zu], output_states[%zu], actual inputs[%zu], input_states[%zu], outputs[%zu], "
1294+ "output_states[%zu].",
1295+ AICPUE_ERROR_CODE, node_index, desc.input_num, desc.input_num, desc.output_num, desc.output_num,
1296+ node.plan.input_tensors.size(), node.plan.input_states.size(), node.plan.output_tensors.size(),
1297+ node.plan.output_states.size());
1298+ return -1;
1299+ }
1300+ return (AddFusedChainInputBindings(desc, node_index, external_binding_num, node, chain) == 0) &&
1301+ (AddFusedChainOutputBindings(desc, node_index, external_binding_num, node, chain) == 0)
1302+ ? 0
1303+ : -1;
1304+}
1305+ 
1306+__attribute__((visibility("default"))) void *CreateCpuConstantFoldingFusedChainPlan(const void *node_descs,
1307+ const size_t node_num,
1308+ const size_t external_input_num,
1309+ const size_t external_output_num) {
1310+ if ((node_descs == nullptr) || (node_num == 0U)) {
1311+ AICPUE_LOGE(
1312+ "Invalid fused CPU chain plan arguments: node_descs_null[%d], node_num[%zu], external_inputs[%zu], "
1313+ "external_outputs[%zu].",
1314+ AICPUE_ERROR_CODE, static_cast<int32_t>(node_descs == nullptr), node_num, external_input_num,
1315+ external_output_num);
1316+ return nullptr;
1317+ }
1318+ const auto *descs = static_cast<const FusedCpuKernelChainNodeDesc *>(node_descs);
1319+ std::unique_ptr<FusedCpuKernelChainPlan> chain = std::make_unique<FusedCpuKernelChainPlan>();
1320+ 
1321+ // 防止计算外部 binding 数量时发生 size_t 整数溢出
1322+ if (external_input_num > (std::numeric_limits<size_t>::max() - external_output_num)) {
1323+ AICPUE_LOGE("Fused CPU chain external binding count overflows size_t: external_inputs[%zu], external_outputs[%zu].",
1324+ AICPUE_ERROR_CODE, external_input_num, external_output_num);
1325+ return nullptr;
1326+ }
1327+ const size_t external_binding_num = external_input_num + external_output_num;
1328+ chain->nodes.reserve(node_num);
1329+ size_t binding_capacity = 0U;
1330+ if (CalculateFusedChainBindingCapacity(descs, node_num, binding_capacity) != 0) {
1331+ return nullptr;
1332+ }
1333+ chain->bindings.reserve(binding_capacity);
1334+ for (size_t i = 0U; i < node_num; ++i) {
1335+ if (InitializeFusedChainNode(descs[i], i, external_binding_num, *chain) != 0) {
1336+ return nullptr;
1337+ }
1338+ }
1339+ AICPUE_LOGD("Created fused cpu chain execution plan, nodes[%zu], dynamic bindings[%zu].", node_num,
1340+ chain->bindings.size());
1341+ return chain.release();
1342+}
1343+ 
1344+__attribute__((visibility("default"))) int32_t RunCpuConstantFoldingFusedChainPlan(void *plan,
1345+ const uint32_t binding_flags) {
1346+ FusedCpuKernelChainPlan *chain = static_cast<FusedCpuKernelChainPlan *>(plan);
1347+ if (chain == nullptr) {
1348+ AICPUE_LOGE("Run fused CPU chain plan received null plan: binding_flags[%u].", AICPUE_ERROR_CODE, binding_flags);
1349+ return -1;
1350+ }
1351+ if (binding_flags != 0U) {
1352+ for (FusedCpuKernelBinding &binding : chain->bindings) {
1353+ if (RebindFusedTensorByFlags(*binding.source, binding.target, *binding.state, binding_flags) != 0) {
1354+ AICPUE_LOGE("Failed to rebind fused CPU chain binding: binding_index[%zu], flags[%u].", AICPUE_ERROR_CODE,
1355+ binding.binding_index, binding_flags);
1356+ return -1;
1357+ }
1358+ }
1359+ }
1360+ for (size_t node_index = 0U; node_index < chain->nodes.size(); ++node_index) {
1361+ if (RunFusedCpuKernelPlan(chain->nodes[node_index].plan) != 0) {
1362+ AICPUE_LOGE("Failed to run fused CPU chain node: node_index[%zu], node_count[%zu].", AICPUE_ERROR_CODE,
1363+ node_index, chain->nodes.size());
1364+ return -1;
1365+ }
1366+ }
1367+ return 0;
1368+}
1369+ 
1370+__attribute__((visibility("default"))) int32_t
1371+RunCpuConstantFoldingFusedChainPlanBindings(void *plan, const void *binding_data, const uint32_t binding_flags) {
1372+ FusedCpuKernelChainPlan *chain = static_cast<FusedCpuKernelChainPlan *>(plan);
1373+ if (chain == nullptr) {
1374+ AICPUE_LOGE("Run fused CPU chain bindings received null plan: binding_flags[%u].", AICPUE_ERROR_CODE,
1375+ binding_flags);
1376+ return -1;
1377+ }
1378+ if (binding_flags != 0U) {
1379+ if (binding_data == nullptr) {
1380+ AICPUE_LOGE("Run fused CPU chain bindings received null binding data: binding_flags[%u], binding_count[%zu].",
1381+ AICPUE_ERROR_CODE, binding_flags, chain->bindings.size());
1382+ return -1;
1383+ }
1384+ const auto *bindings = static_cast<const FusedHostCpuTensorBinding *>(binding_data);
1385+ for (FusedCpuKernelBinding &binding : chain->bindings) {
1386+ const FusedHostCpuTensorBinding *runtime_binding = &bindings[binding.binding_index];
1387+ if ((runtime_binding->flags != 0U) &&
1388+ (RebindFusedTensorByBinding(*runtime_binding, binding.target, *binding.state) != 0)) {
1389+ AICPUE_LOGE(
1390+ "Failed to rebind fused CPU runtime binding: binding_index[%zu], runtime_flags[%u], "
1391+ "global_flags[%u].",
1392+ AICPUE_ERROR_CODE, binding.binding_index, runtime_binding->flags, binding_flags);
1393+ return -1;
1394+ }
1395+ }
1396+ }
1397+ for (size_t node_index = 0U; node_index < chain->nodes.size(); ++node_index) {
1398+ if (RunFusedCpuKernelPlan(chain->nodes[node_index].plan) != 0) {
1399+ AICPUE_LOGE("Failed to run fused CPU chain node with runtime bindings: node_index[%zu], node_count[%zu].",
1400+ AICPUE_ERROR_CODE, node_index, chain->nodes.size());
1401+ return -1;
1402+ }
1403+ }
1404+ return 0;
1405+}
1406+ 
1407+__attribute__((visibility("default"))) void DestroyCpuConstantFoldingFusedChainPlan(void *plan) {
1408+ delete static_cast<FusedCpuKernelChainPlan *>(plan);
1409+}
700}1410}
@@ -17,6 +17,7 @@
17#ifndef GE_GE_LOCAL_ENGINE_ENGINE_AICPU_FOLDING_FOLDING_H_17#ifndef GE_GE_LOCAL_ENGINE_ENGINE_AICPU_FOLDING_FOLDING_H_
18#define GE_GE_LOCAL_ENGINE_ENGINE_AICPU_FOLDING_FOLDING_H_18#define GE_GE_LOCAL_ENGINE_ENGINE_AICPU_FOLDING_FOLDING_H_
19 19 
20+#include <cstddef>
20#include <cstdint>21#include <cstdint>
21#include <map>22#include <map>
22#include <string>23#include <string>
@@ -27,8 +28,23 @@
27extern "C" {28extern "C" {
28__attribute__((visibility("default"))) int32_t InitCpuConstantFoldingNew(ge::HostCpuOp *(*create_fn)());29__attribute__((visibility("default"))) int32_t InitCpuConstantFoldingNew(ge::HostCpuOp *(*create_fn)());
29 30 
31+__attribute__((visibility("default"))) int32_t IsCpuConstantFoldingFusedOpSupported(const char *op_type);
32+ 
30__attribute__((visibility("default"))) int32_t33__attribute__((visibility("default"))) int32_t
31CpuConstantFoldingComputeNew(const ge::Operator &op, const std::map<std::string, const ge::Tensor> &inputs,34CpuConstantFoldingComputeNew(const ge::Operator &op, const std::map<std::string, const ge::Tensor> &inputs,
32 std::map<std::string, ge::Tensor> outputs);35 std::map<std::string, ge::Tensor> outputs);
36+ 
37+__attribute__((visibility("default"))) void *CreateCpuConstantFoldingFusedChainPlan(const void *node_descs,
38+ size_t node_num,
39+ size_t external_input_num,
40+ size_t external_output_num);
41+ 
42+__attribute__((visibility("default"))) int32_t RunCpuConstantFoldingFusedChainPlan(void *plan, uint32_t binding_flags);
43+ 
44+__attribute__((visibility("default"))) int32_t RunCpuConstantFoldingFusedChainPlanBindings(void *plan,
45+ const void *bindings,
46+ uint32_t binding_flags);
47+ 
48+__attribute__((visibility("default"))) void DestroyCpuConstantFoldingFusedChainPlan(void *plan);
33}49}
34#endif // GE_GE_LOCAL_ENGINE_ENGINE_AICPU_FOLDING_FOLDING_H_50#endif // GE_GE_LOCAL_ENGINE_ENGINE_AICPU_FOLDING_FOLDING_H_
@@ -28,6 +28,7 @@
28#include "aicpu_graph_optimizer/graph_optimizer_utils.h"28#include "aicpu_graph_optimizer/graph_optimizer_utils.h"
29#include "graph/anchor.h"29#include "graph/anchor.h"
30#include "graph/node.h"30#include "graph/node.h"
31+#include "framework/common/host_cpu_fusion_attr.h"
31 32 
32using namespace std;33using namespace std;
33using namespace ge;34using namespace ge;
@@ -279,12 +280,24 @@ static ge::Status FillOutputTensorOfAicpuNodeDef(const ge::OpDescPtr &op_desc_pt
279 aicpu_shape->set_data_format(static_cast<ge::Format>(output_desc.GetFormat()));280 aicpu_shape->set_data_format(static_cast<ge::Format>(output_desc.GetFormat()));
280 aicpu_shape->set_unknown_rank(is_unknow_shape);281 aicpu_shape->set_unknown_rank(is_unknow_shape);
281 output_tensor->set_tensor_type(static_cast<ge::DataType>(output_desc.GetDataType()));282 output_tensor->set_tensor_type(static_cast<ge::DataType>(output_desc.GetDataType()));
283+ if (op_desc_ptr->GetType() == ge::kFusedHostCpuOpType) {
284+ output_tensor->set_name(op_desc_ptr->GetOutputNameByIndex(static_cast<uint32_t>(i)));
285+ }
282 }286 }
283 return ge::SUCCESS;287 return ge::SUCCESS;
284}288}
285 289 
286ge::Status BuildAicpuNodeDef(const ge::OpDescPtr &op_desc_ptr, aicpuops::NodeDef &node_def) {290ge::Status BuildAicpuNodeDef(const ge::OpDescPtr &op_desc_ptr, aicpuops::NodeDef &node_def) {
287 std::string op_type = op_desc_ptr->GetType();291 std::string op_type = op_desc_ptr->GetType();
292+ // 将通用节点类型替换成动态 Kernel 注册名
293+ if (op_type == ge::kFusedHostCpuOpType) {
294+ if (!ge::AttrUtils::GetStr(op_desc_ptr, ge::kFusedHostCpuRegisterName, op_type) || op_type.empty()) {
295+ AICPUE_LOGE("Get fused HostCPU register name failed for op [%s].", op_desc_ptr->GetName().c_str());
296+ return ge::PARAM_INVALID;
297+ }
298+ AICPUE_LOGD("Resolve fused HostCPU node[%s] to dynamic kernel[%s].", op_desc_ptr->GetName().c_str(),
299+ op_type.c_str());
300+ }
288 node_def.set_op(op_type);301 node_def.set_op(op_type);
289 302 
290 bool is_unknow_shape = false;303 bool is_unknow_shape = false;
@@ -13,9 +13,11 @@
13#include <memory>13#include <memory>
14#include <vector>14#include <vector>
15#include "common/config/config_file.h"15#include "common/config/config_file.h"
16+#include "common/util/cpu_engine_util.h"
16#include "base/err_msg.h"17#include "base/err_msg.h"
17#include "common/util/util.h"18#include "common/util/util.h"
18#include "error_code/error_code.h"19#include "error_code/error_code.h"
20+#include "framework/common/host_cpu_fusion_attr.h"
19#include "ge/ge_api_types.h"21#include "ge/ge_api_types.h"
20#include "register/ops_kernel_builder_registry.h"22#include "register/ops_kernel_builder_registry.h"
21#include "graph/utils/op_desc_utils_ex.h"23#include "graph/utils/op_desc_utils_ex.h"
@@ -61,7 +63,6 @@ ge::Status HostCpuOpsKernelBuilder::CalcOpRunningParam(ge::Node &node) {
61 ge::OpDescUtilsEx::SetType(op_desc_ptr, *op_original_type);63 ge::OpDescUtilsEx::SetType(op_desc_ptr, *op_original_type);
62 op_type = *op_original_type;64 op_type = *op_original_type;
63 }65 }
64- 
65 FACTORY_ENGINE::FactoryType host_engine_ptr = FACTORY_ENGINE::Produce(engine_name_);66 FACTORY_ENGINE::FactoryType host_engine_ptr = FACTORY_ENGINE::Produce(engine_name_);
66 AICPU_CHECK_NOTNULL_ERRCODE(host_engine_ptr, ErrorCode::INPUT_PARAM_NULL)67 AICPU_CHECK_NOTNULL_ERRCODE(host_engine_ptr, ErrorCode::INPUT_PARAM_NULL)
67 AicpuOpsKernelInfoStorePtr host_ops_kernel_info_store_ptr = host_engine_ptr->GetAicpuOpsKernelInfoStore();68 AicpuOpsKernelInfoStorePtr host_ops_kernel_info_store_ptr = host_engine_ptr->GetAicpuOpsKernelInfoStore();
@@ -77,7 +78,17 @@ ge::Status HostCpuOpsKernelBuilder::CalcOpRunningParam(ge::Node &node) {
77 AICPUE_LOGI("Node[%s] set attr optional_input_placeholder is [%s]", node.GetName().c_str(),78 AICPUE_LOGI("Node[%s] set attr optional_input_placeholder is [%s]", node.GetName().c_str(),
78 optional_input ? "true" : "false");79 optional_input ? "true" : "false");
79 }80 }
80- 81+ if ((op_type == ge::kFusedHostCpuOpType) && !ge::AttrUtils::HasAttr(op_desc_ptr, kCustomizedOpDef)) {
82+ // 首次构建时将动态注册名和完整 IO 描述写入 NodeDef,后续沿用通用 HostCPU TaskDef 生成流程。
83+ AICPUE_LOGD("Build customized NodeDef for fused HostCPU node[%s], inputs[%zu], outputs[%zu].",
84+ node.GetName().c_str(), op_desc_ptr->GetAllInputsSize(), op_desc_ptr->GetOutputsSize());
85+ aicpuops::NodeDef node_def;
86+ AICPU_CHECK_RES_WITH_LOG(BuildAicpuNodeDef(op_desc_ptr, node_def), "Build NodeDef for fused HostCPU op[%s] failed.",
87+ node.GetName().c_str());
88+ AICPU_CHECK_RES_WITH_LOG(InsertAicpuNodeDefAttrToOp(op_desc_ptr, node_def, kCustomizedOpDef),
89+ "Serialize NodeDef for fused HostCPU op[%s] failed.", node.GetName().c_str());
90+ AICPUE_LOGD("Customized NodeDef is ready for fused HostCPU node[%s].", node.GetName().c_str());
91+ }
81 const KernelBuilderPtr &kernel_builder = kernel_builder_map_["HOSTCPUBuilder"];92 const KernelBuilderPtr &kernel_builder = kernel_builder_map_["HOSTCPUBuilder"];
82 AICPU_CHECK_NOTNULL_ERRCODE(kernel_builder, ErrorCode::NONE_KERNEL_BUILDER);93 AICPU_CHECK_NOTNULL_ERRCODE(kernel_builder, ErrorCode::NONE_KERNEL_BUILDER);
83 return kernel_builder->CalcOpRunningParam(node);94 return kernel_builder->CalcOpRunningParam(node);
@@ -101,6 +112,10 @@ ge::Status HostCpuOpsKernelBuilder::GenerateTask(const ge::Node &ge_node, ge::Ru
101 112 
102 const KernelBuilderPtr &kernel_builder = kernel_builder_map_["HOSTCPUBuilder"];113 const KernelBuilderPtr &kernel_builder = kernel_builder_map_["HOSTCPUBuilder"];
103 AICPU_CHECK_NOTNULL_ERRCODE(kernel_builder, ErrorCode::NONE_KERNEL_BUILDER);114 AICPU_CHECK_NOTNULL_ERRCODE(kernel_builder, ErrorCode::NONE_KERNEL_BUILDER);
115+ if (op_type == ge::kFusedHostCpuOpType) {
116+ // TaskDef 仍走通用 HOSTCPUBuilder;其中的 NodeDef 已将公共类型替换为 JIT kernel 注册名。
117+ AICPUE_LOGD("Generate generic HostCPU task for fused node[%s].", ge_node.GetName().c_str());
118+ }
104 return kernel_builder->GenerateTask(ge_node, context, tasks);119 return kernel_builder->GenerateTask(ge_node, context, tasks);
105}120}
106 121 
@@ -34,7 +34,6 @@
34#include "graph/attribute_group/attr_group_shape_env.h"34#include "graph/attribute_group/attr_group_shape_env.h"
35#include "graph_metadef/common/ge_common/util.h"35#include "graph_metadef/common/ge_common/util.h"
36#include "common/ge_common/ge_types.h"36#include "common/ge_common/ge_types.h"
37-#include "graph/op_so_bin.h"
38 37 
39namespace ge {38namespace ge {
40namespace {39namespace {
@@ -51,7 +50,6 @@ const char_t *const kAnchorIndex = "anchorIndex";
51const char_t *const kTaskL2FusionInfo = "_task_L2FusionInfo";50const char_t *const kTaskL2FusionInfo = "_task_L2FusionInfo";
52const char_t *const kDataAnchorIndexForLxfusion = "_data_anchor_index_for_lxfusion";51const char_t *const kDataAnchorIndexForLxfusion = "_data_anchor_index_for_lxfusion";
53const char_t *const kEnableCvParallel = "_enable_cv_parallel";52const char_t *const kEnableCvParallel = "_enable_cv_parallel";
54-const char_t *const kSoBufferAttr = "bin_file_buffer";
55const char_t *const kVectorEngineName = "VectorEngine";53const char_t *const kVectorEngineName = "VectorEngine";
56const char_t *const kHostCpuEngineName = "DNN_VM_HOST_CPU";54const char_t *const kHostCpuEngineName = "DNN_VM_HOST_CPU";
57const std::string kStableRdfsSort = "3";55const std::string kStableRdfsSort = "3";
@@ -482,20 +480,6 @@ Status EnginePartitioner::InheritOriginalAttr(const ComputeGraphPtr &original_co
482 output_merged_compute_graph->SetExtAttr(ge::ATTR_NAME_DEVICE_INDEX_TO_LOGIC_DEVICE_ID, *device_mapping));480 output_merged_compute_graph->SetExtAttr(ge::ATTR_NAME_DEVICE_INDEX_TO_LOGIC_DEVICE_ID, *device_mapping));
483 }481 }
484 482 
485- // HostCPU fusion stores generated custom-op SOs in this ext attr. The merge step
486- // creates a new graph, so preserve the buffer together with the graph attrs.
487- const auto so_buffer = original_compute_graph->GetExtAttr<std::map<std::string, ge::OpSoBinPtr>>(kSoBufferAttr);
488- if (so_buffer != nullptr) {
489- std::map<std::string, ge::OpSoBinPtr> merged_so_buffer;
490- const auto existing_so_buffer =
491- output_merged_compute_graph->GetExtAttr<std::map<std::string, ge::OpSoBinPtr>>(kSoBufferAttr);
492- if (existing_so_buffer != nullptr) {
493- merged_so_buffer = *existing_so_buffer;
494- }
495- merged_so_buffer.insert(so_buffer->begin(), so_buffer->end());
496- GE_ASSERT_TRUE(output_merged_compute_graph->SetExtAttr(kSoBufferAttr, merged_so_buffer));
497- }
498- 
499 // AttrStore里面属性组没有被拷贝,并且没有提供CopyAllAttrStore方法,暂时先手动拷贝必须的483 // AttrStore里面属性组没有被拷贝,并且没有提供CopyAllAttrStore方法,暂时先手动拷贝必须的
500 auto origin_shape_env_attr = original_compute_graph->GetAttrsGroup<ShapeEnvAttr>();484 auto origin_shape_env_attr = original_compute_graph->GetAttrsGroup<ShapeEnvAttr>();
501 if (origin_shape_env_attr != nullptr) {485 if (origin_shape_env_attr != nullptr) {
@@ -34,7 +34,7 @@ struct HostCpuFusionRegion {
34 std::vector<HostCpuFusionOutput> external_outputs;34 std::vector<HostCpuFusionOutput> external_outputs;
35};35};
36 36 
37-// source 用于维测和测试,so_data 作为标准 custom-op SO 写入模型37+// source 用于维测和测试,so_data 写入图属性并在运行时加载
38struct HostCpuFusionCodegenResult {38struct HostCpuFusionCodegenResult {
39 std::string register_name;39 std::string register_name;
40 std::string source;40 std::string source;
@@ -50,8 +50,8 @@ class HostCpuFusionCompiler {
50 virtual Status Compile(const std::string &source, std::vector<uint8_t> &so_data) const;50 virtual Status Compile(const std::string &source, std::vector<uint8_t> &so_data) const;
51};51};
52 52 
53-// 生成注册到 CustomOpRegistry(kHostCPU)普通 HostCpuExecuteOp。执行时按 op_type 53+// 生成注册到 aicpu::CpuKernelRegister融合 CpuKernel;内部编排为每个原始算子缓存 CpuKernel 执行计划,
54-// libconstant_folding_ops.so 查询 Gert HostKernel,使用临时 KernelContext 按拓扑序执行。54+// 稳态仅更新 Tensor 数据和变化的描述信息并按拓扑序执行。
55class HostCpuFusionCodegen {55class HostCpuFusionCodegen {
56 public:56 public:
57 Status Generate(const HostCpuFusionRegion &region, HostCpuFusionCodegenResult &result) const;57 Status Generate(const HostCpuFusionRegion &region, HostCpuFusionCodegenResult &result) const;
@@ -14,23 +14,14 @@
14#include <cstdint>14#include <cstdint>
15#include <deque>15#include <deque>
16#include <iomanip>16#include <iomanip>
17-#include <limits>
18-#include <map>
19-#include <memory>
20#include <sstream>17#include <sstream>
21#include <unordered_map>18#include <unordered_map>
22#include <unordered_set>19#include <unordered_set>
23 20 
24#include "framework/common/debug/ge_log.h"21#include "framework/common/debug/ge_log.h"
25#include "framework/common/host_cpu_fusion_attr.h"22#include "framework/common/host_cpu_fusion_attr.h"
26-#include "common/ge_common/ge_types.h"
27-#include "common/helper/custom_op_registry_builder.h"
28-#include "common/helper/custom_op_so_loader.h"
29-#include "common/util/mem_utils.h"
30#include "host_cpu_engine/host_cpu_engine.h"23#include "host_cpu_engine/host_cpu_engine.h"
31-#include "graph/custom_op_factory.h"
32#include "graph/debug/ge_attr_define.h"24#include "graph/debug/ge_attr_define.h"
33-#include "graph/op_so_bin.h"
34#include "graph/utils/attr_utils.h"25#include "graph/utils/attr_utils.h"
35#include "graph/utils/graph_utils.h"26#include "graph/utils/graph_utils.h"
36#include "graph/utils/node_utils.h"27#include "graph/utils/node_utils.h"
@@ -40,9 +31,10 @@ namespace {
40constexpr size_t kMaxGeneratedSoSize = 10U * 1024U * 1024U;31constexpr size_t kMaxGeneratedSoSize = 10U * 1024U * 1024U;
41constexpr char kHostCpuEngineName[] = "DNN_VM_HOST_CPU";32constexpr char kHostCpuEngineName[] = "DNN_VM_HOST_CPU";
42constexpr char kHostCpuKernelLibName[] = "DNN_VM_HOST_CPU_OP_STORE";33constexpr char kHostCpuKernelLibName[] = "DNN_VM_HOST_CPU_OP_STORE";
34+constexpr char kHostCpuTaskKernelLibName[] = "HOSTCPUKernel";
35+constexpr char kOpKernelLibAttr[] = "opKernelLib";
43constexpr char kSmallShapeHostCpu[] = "SmallShapeHostcpu";36constexpr char kSmallShapeHostCpu[] = "SmallShapeHostcpu";
44constexpr char kResourceListAttr[] = "_resource_list";37constexpr char kResourceListAttr[] = "_resource_list";
45-constexpr char kSoBufferAttr[] = "bin_file_buffer";
46 38 
47bool IsValidFusedHostCpuSoElf(const std::vector<uint8_t> &data) {39bool IsValidFusedHostCpuSoElf(const std::vector<uint8_t> &data) {
48 return (data.size() >= 20U) && (data.size() <= kMaxGeneratedSoSize) && (data[0] == 0x7FU) && (data[1] == 'E') &&40 return (data.size() >= 20U) && (data.size() <= kMaxGeneratedSoSize) && (data[0] == 0x7FU) && (data[1] == 'E') &&
@@ -140,8 +132,7 @@ bool IsCandidate(const NodePtr &node, const HostCpuFusionOpSupportChecker &op_su
140 GELOGD("Skip HostCPU fusion node[%s]: OpDesc is null.", node->GetNamePtr());132 GELOGD("Skip HostCPU fusion node[%s]: OpDesc is null.", node->GetNamePtr());
141 return false;133 return false;
142 }134 }
143- bool generated = false;135+ if (node->GetType() == kFusedHostCpuOpType) {
144- if (AttrUtils::GetBool(node->GetOpDesc(), kFusedHostCpuGenerated, generated) && generated) {
145 GELOGD("Skip HostCPU fusion node[%s]: node is already fused.", node->GetNamePtr());136 GELOGD("Skip HostCPU fusion node[%s]: node is already fused.", node->GetNamePtr());
146 return false;137 return false;
147 }138 }
@@ -327,15 +318,10 @@ std::vector<NodePtr> GetComponentSinks(const std::vector<NodePtr> &component,
327 return sinks;318 return sinks;
328}319}
329 320 
330-std::unordered_set<const Node *> CollectComponentAncestors(const std::unordered_set<const Node *> &component_set,321+HostCpuFusionRegion BuildRegionForSink(const std::vector<NodePtr> &topological_nodes,
331- const NodePtr &sink) {322+ const std::unordered_set<const Node *> &component_set, const NodePtr &sink) {
332 std::unordered_set<const Node *> ancestors;323 std::unordered_set<const Node *> ancestors;
333- std::deque<NodePtr> pending;324+ std::deque<NodePtr> pending{sink};
334- for (const auto &in_node : sink->GetInDataNodes()) {
335- if (component_set.count(in_node.get()) > 0U) {
336- pending.emplace_back(in_node);
337- }
338- }
339 while (!pending.empty()) {325 while (!pending.empty()) {
340 const auto current = pending.front();326 const auto current = pending.front();
341 pending.pop_front();327 pending.pop_front();
@@ -348,46 +334,10 @@ std::unordered_set<const Node *> CollectComponentAncestors(const std::unordered_
348 }334 }
349 }335 }
350 }336 }
351- return ancestors;
352-}
353- 
354-bool HasSameAncestors(const std::unordered_set<const Node *> &lhs, const std::unordered_set<const Node *> &rhs) {
355- return (lhs.size() == rhs.size()) &&
356- std::all_of(lhs.cbegin(), lhs.cend(), [&rhs](const Node *node) { return rhs.count(node) > 0U; });
357-}
358- 
359-struct SinkAncestorGroup {
360- std::unordered_set<const Node *> ancestors;
361- std::vector<NodePtr> sinks;
362-};
363- 
364-std::vector<SinkAncestorGroup> GroupSinksByAncestors(const std::unordered_set<const Node *> &component_set,
365- const std::vector<NodePtr> &sinks) {
366- std::vector<SinkAncestorGroup> groups;
367- for (const auto &sink : sinks) {
368- auto ancestors = CollectComponentAncestors(component_set, sink);
369- const auto group = std::find_if(groups.begin(), groups.end(), [&ancestors](const SinkAncestorGroup &candidate) {
370- return HasSameAncestors(candidate.ancestors, ancestors);
371- });
372- if (group != groups.end()) {
373- group->sinks.emplace_back(sink);
374- continue;
375- }
376- groups.push_back({std::move(ancestors), {sink}});
377- }
378- return groups;
379-}
380- 
381-HostCpuFusionRegion BuildRegionForSinkGroup(const std::vector<NodePtr> &topological_nodes,
382- const SinkAncestorGroup &group) {
383- std::unordered_set<const Node *> region_nodes = group.ancestors;
384- for (const auto &sink : group.sinks) {
385- region_nodes.emplace(sink.get());
386- }
387 337 
388 HostCpuFusionRegion region;338 HostCpuFusionRegion region;
389 for (const auto &node : topological_nodes) {339 for (const auto &node : topological_nodes) {
390- if (region_nodes.count(node.get()) > 0U) {340+ if (ancestors.count(node.get()) > 0U) {
391 region.nodes.emplace_back(node);341 region.nodes.emplace_back(node);
392 }342 }
393 }343 }
@@ -469,17 +419,14 @@ Status BuildComponentRegions(const ComputeGraphPtr &graph, const std::vector<Nod
469 sinks.size());419 sinks.size());
470 return FAILED;420 return FAILED;
471 }421 }
472- GELOGD("HostCPU fusion component[%zu] contains %zu nodes and %zu sinks, ancestor_grouping=%d, nodes=[%s].",422+ GELOGD("HostCPU fusion component[%zu] contains %zu nodes and %zu sinks, clone_and_split=%d, nodes=[%s].",
473 component_index, component.size(), sinks.size(), static_cast<int32_t>(requires_split),423 component_index, component.size(), sinks.size(), static_cast<int32_t>(requires_split),
474 GetNodeNames(component).c_str());424 GetNodeNames(component).c_str());
475- const auto sink_groups = GroupSinksByAncestors(component_set, sinks);425+ for (const auto &sink : sinks) {
476- GELOGD("HostCPU fusion component[%zu] groups %zu sinks into %zu ancestor group(s).", component_index, sinks.size(),426+ auto region = BuildRegionForSink(topological_nodes, component_set, sink);
477- sink_groups.size());
478- for (const auto &group : sink_groups) {
479- auto region = BuildRegionForSinkGroup(topological_nodes, group);
480 if (region.nodes.size() < 2U) {427 if (region.nodes.size() < 2U) {
481- GELOGD("Skip HostCPU fusion component[%zu] sink group[%s]: ancestor region has only %zu node(s).",428+ GELOGD("Skip HostCPU fusion component[%zu] sink[%s]: ancestor region has only %zu node(s).", component_index,
482- component_index, GetNodeNames(group.sinks).c_str(), region.nodes.size());429+ sink->GetNamePtr(), region.nodes.size());
483 continue;430 continue;
484 }431 }
485 regions.emplace_back(std::move(region));432 regions.emplace_back(std::move(region));
@@ -533,15 +480,16 @@ bool AddFusedOutputDescs(const HostCpuFusionRegion &region, const OpDescPtr &op_
533}480}
534 481 
535bool SetFusedOpAttributes(const PreparedFusionRegion &prepared, const OpDescPtr &op_desc) {482bool SetFusedOpAttributes(const PreparedFusionRegion &prepared, const OpDescPtr &op_desc) {
536- op_desc->SetOpEngineName(kEngineNameCustom);483+ const auto &region = prepared.region;
537- op_desc->SetOpKernelLibName(kCustomOpKernelLibName);484+ op_desc->SetOpEngineName(kHostCpuEngineName);
538- return AttrUtils::SetStr(op_desc, ATTR_NAME_ENGINE_NAME_FOR_LX, kEngineNameCustom) &&485+ op_desc->SetOpKernelLibName(kHostCpuKernelLibName);
539- AttrUtils::SetStr(op_desc, ATTR_NAME_KKERNEL_LIB_NAME_FOR_LX, kCustomOpKernelLibName) &&486+ return AttrUtils::SetStr(op_desc, ATTR_NAME_ENGINE_NAME_FOR_LX, kHostCpuEngineName) &&
540- AttrUtils::SetStr(op_desc, kAttrLowingFunc, kHostCpuCustomOpLowerFunc) &&487+ AttrUtils::SetStr(op_desc, ATTR_NAME_KKERNEL_LIB_NAME_FOR_LX, kHostCpuKernelLibName) &&
488+ AttrUtils::SetStr(op_desc, kOpKernelLibAttr, kHostCpuTaskKernelLibName) &&
541 AttrUtils::SetInt(op_desc, ATTR_NAME_UNKNOWN_SHAPE_TYPE, DEPEND_IN_SHAPE) &&489 AttrUtils::SetInt(op_desc, ATTR_NAME_UNKNOWN_SHAPE_TYPE, DEPEND_IN_SHAPE) &&
542 AttrUtils::SetBool(op_desc, kSmallShapeHostCpu, true) &&490 AttrUtils::SetBool(op_desc, kSmallShapeHostCpu, true) &&
543- AttrUtils::SetBool(op_desc, kFusedHostCpuGenerated, true) &&491+ AttrUtils::SetStr(op_desc, kFusedHostCpuRegisterName, prepared.codegen.register_name) &&
544- AttrUtils::SetStr(op_desc, kFusedHostCpuRegisterName, prepared.codegen.register_name);492+ AttrUtils::SetStr(op_desc, kFusedHostCpuSoKey, std::string(kFusedHostCpuSoDataPrefix) + region.chain_id);
545}493}
546 494 
547bool SetFusedOpMetadata(const HostCpuFusionRegion &region, const OpDescPtr &op_desc) {495bool SetFusedOpMetadata(const HostCpuFusionRegion &region, const OpDescPtr &op_desc) {
@@ -564,7 +512,8 @@ bool SetFusedOpMetadata(const HostCpuFusionRegion &region, const OpDescPtr &op_d
564 512 
565OpDescPtr CreateFusedOpDesc(const PreparedFusionRegion &prepared) {513OpDescPtr CreateFusedOpDesc(const PreparedFusionRegion &prepared) {
566 const auto &region = prepared.region;514 const auto &region = prepared.region;
567- auto op_desc = std::make_shared<OpDesc>(prepared.codegen.register_name, prepared.codegen.register_name);515+ auto op_desc =
516+ std::make_shared<OpDesc>(std::string(kFusedHostCpuOpType) + "_" + region.chain_id, kFusedHostCpuOpType);
568 if (!AddFusedInputDescs(region, op_desc) || !AddFusedOutputDescs(region, op_desc)) {517 if (!AddFusedInputDescs(region, op_desc) || !AddFusedOutputDescs(region, op_desc)) {
569 return nullptr;518 return nullptr;
570 }519 }
@@ -599,8 +548,10 @@ struct ReplacedFusionOutput {
599 OutDataAnchorPtr new_source;548 OutDataAnchorPtr new_source;
600};549};
601 550 
602-Status RollbackFusionCommit(const ComputeGraphPtr &graph, const std::vector<NodePtr> &fused_nodes,551+Status RollbackFusionCommit(const ComputeGraphPtr &graph, const ComputeGraphPtr &root_graph,
603- const std::vector<ReplacedFusionOutput> &replaced_outputs) {552+ const std::vector<NodePtr> &fused_nodes,
553+ const std::vector<ReplacedFusionOutput> &replaced_outputs,
554+ const std::vector<std::string> &root_graph_so_keys) {
604 GELOGW("Rollback HostCPU fusion graph commit: graph[%s], new_nodes=%zu, replaced_edges=%zu.",555 GELOGW("Rollback HostCPU fusion graph commit: graph[%s], new_nodes=%zu, replaced_edges=%zu.",
605 graph->GetName().c_str(), fused_nodes.size(), replaced_outputs.size());556 graph->GetName().c_str(), fused_nodes.size(), replaced_outputs.size());
606 for (auto iter = replaced_outputs.rbegin(); iter != replaced_outputs.rend(); ++iter) {557 for (auto iter = replaced_outputs.rbegin(); iter != replaced_outputs.rend(); ++iter) {
@@ -609,10 +560,28 @@ Status RollbackFusionCommit(const ComputeGraphPtr &graph, const std::vector<Node
609 graph->GetName().c_str(), static_cast<int32_t>(iter->consumer == nullptr));560 graph->GetName().c_str(), static_cast<int32_t>(iter->consumer == nullptr));
610 }561 }
611 }562 }
612- return RollbackNewNodes(graph, fused_nodes);563+ const Status rollback_status = RollbackNewNodes(graph, fused_nodes);
564+ for (const auto &so_key : root_graph_so_keys) {
565+ if (root_graph->DelAttr(so_key) != GRAPH_SUCCESS) {
566+ GELOGE(FAILED, "Failed to remove rolled-back fused HostCPU SO data: graph[%s], so_key[%s].",
567+ root_graph->GetName().c_str(), so_key.c_str());
568+ }
569+ }
570+ return rollback_status;
613}571}
614 572 
615-NodePtr CreateFusedNode(const ComputeGraphPtr &graph, const PreparedFusionRegion &prepared) {573+NodePtr CreateAndRegisterFusedNode(const ComputeGraphPtr &graph, const ComputeGraphPtr &root_graph,
574+ const PreparedFusionRegion &prepared, std::vector<std::string> &root_graph_so_keys,
575+ std::string &so_key) {
576+ so_key = std::string(kFusedHostCpuSoDataPrefix) + prepared.region.chain_id;
577+ if (AttrUtils::HasAttr(root_graph, so_key) ||
578+ !AttrUtils::SetBytes(root_graph, so_key,
579+ Buffer::CopyFrom(prepared.codegen.so_data.data(), prepared.codegen.so_data.size()))) {
580+ GELOGE(FAILED, "Failed to set fused HostCPU graph SO data: graph[%s], so_key[%s], so_size=%zu.",
581+ graph->GetName().c_str(), so_key.c_str(), prepared.codegen.so_data.size());
582+ return nullptr;
583+ }
584+ root_graph_so_keys.emplace_back(so_key);
616 const auto op_desc = CreateFusedOpDesc(prepared);585 const auto op_desc = CreateFusedOpDesc(prepared);
617 if (op_desc == nullptr) {586 if (op_desc == nullptr) {
618 GELOGE(FAILED, "Failed to create fused HostCPU OpDesc: graph[%s], chain[%s].", graph->GetName().c_str(),587 GELOGE(FAILED, "Failed to create fused HostCPU OpDesc: graph[%s], chain[%s].", graph->GetName().c_str(),
@@ -630,9 +599,10 @@ NodePtr CreateFusedNode(const ComputeGraphPtr &graph, const PreparedFusionRegion
630 op_desc->GetName().c_str());599 op_desc->GetName().c_str());
631 return nullptr;600 return nullptr;
632 }601 }
633- GELOGD("HostCPU fusion adds custom-op node[%s]: chain[%s], so_size=%zu, inputs=%zu, outputs=%zu.",602+ GELOGD("HostCPU fusion adds node[%s]: chain[%s], so_key[%s], so_graph[%s], so_size=%zu, inputs=%zu, outputs=%zu.",
634- fused_node->GetNamePtr(), prepared.region.chain_id.c_str(), prepared.codegen.so_data.size(),603+ fused_node->GetNamePtr(), prepared.region.chain_id.c_str(), so_key.c_str(), root_graph->GetName().c_str(),
635- prepared.region.external_inputs.size(), prepared.region.external_outputs.size());604+ prepared.codegen.so_data.size(), prepared.region.external_inputs.size(),
605+ prepared.region.external_outputs.size());
636 return fused_node;606 return fused_node;
637}607}
638 608 
@@ -671,9 +641,12 @@ bool ReplaceFusedNodeOutputs(const ComputeGraphPtr &graph, const PreparedFusionR
671 return true;641 return true;
672}642}
673 643 
674-bool AddPreparedFusionNode(const ComputeGraphPtr &graph, const PreparedFusionRegion &prepared,644+bool AddPreparedFusionNode(const ComputeGraphPtr &graph, const ComputeGraphPtr &root_graph,
675- std::vector<NodePtr> &fused_nodes, std::vector<ReplacedFusionOutput> &replaced_outputs) {645+ const PreparedFusionRegion &prepared, std::vector<NodePtr> &fused_nodes,
676- const auto fused_node = CreateFusedNode(graph, prepared);646+ std::vector<std::string> &root_graph_so_keys,
647+ std::vector<ReplacedFusionOutput> &replaced_outputs) {
648+ std::string so_key;
649+ const auto fused_node = CreateAndRegisterFusedNode(graph, root_graph, prepared, root_graph_so_keys, so_key);
677 if (fused_node == nullptr) {650 if (fused_node == nullptr) {
678 return false;651 return false;
679 }652 }
@@ -712,107 +685,6 @@ bool RemoveOriginalFusionNodes(const ComputeGraphPtr &graph, const std::vector<P
712 return true;685 return true;
713}686}
714 687 
715-struct FusionCustomOpArtifacts {
716- std::vector<std::string> inserted_so_keys;
717- std::vector<AscendString> registered_op_types;
718-};
719- 
720-OpSoBinPtr CreateFusionCustomOpSoBin(const PreparedFusionRegion &prepared) {
721- const auto &so_data = prepared.codegen.so_data;
722- if (so_data.empty() || (so_data.size() > std::numeric_limits<uint32_t>::max())) {
723- GELOGE(PARAM_INVALID, "Invalid generated HostCPU custom-op SO size[%zu], op_type[%s].", so_data.size(),
724- prepared.codegen.register_name.c_str());
725- return nullptr;
726- }
727- auto data = std::make_unique<char_t[]>(so_data.size());
728- std::copy(so_data.cbegin(), so_data.cend(), data.get());
729- const std::string so_name = "lib" + prepared.codegen.register_name + ".so";
730- return MakeShared<OpSoBin>(so_name, kFusedHostCpuSoVendor, std::move(data), static_cast<uint32_t>(so_data.size()),
731- SoBinType::kCustomOp);
732-}
733- 
734-bool IsSameSoBin(const OpSoBinPtr &lhs, const OpSoBinPtr &rhs) {
735- return (lhs != nullptr) && (rhs != nullptr) && (lhs->GetSoBinType() == rhs->GetSoBinType()) &&
736- (lhs->GetBinDataSize() == rhs->GetBinDataSize()) &&
737- std::equal(lhs->GetBinData(), lhs->GetBinData() + lhs->GetBinDataSize(), rhs->GetBinData());
738-}
739- 
740-void RollbackFusionCustomOpArtifacts(const ComputeGraphPtr &root_graph, const FusionCustomOpArtifacts &artifacts) {
741- if (!artifacts.registered_op_types.empty()) {
742- CustomOpFactory::RemoveCustomOps(artifacts.registered_op_types);
743- }
744- if (artifacts.inserted_so_keys.empty()) {
745- return;
746- }
747- auto so_buffer = root_graph->GetExtAttr<std::map<std::string, OpSoBinPtr>>(kSoBufferAttr);
748- if (so_buffer == nullptr) {
749- return;
750- }
751- auto updated_buffer = *so_buffer;
752- for (const auto &key : artifacts.inserted_so_keys) {
753- (void)updated_buffer.erase(key);
754- }
755- if (updated_buffer.empty()) {
756- (void)root_graph->DelExtAttr(kSoBufferAttr);
757- } else if (!root_graph->SetExtAttr(kSoBufferAttr, updated_buffer)) {
758- GELOGW("Failed to restore custom-op SO buffer while rolling back HostCPU fusion for graph[%s].",
759- root_graph->GetName().c_str());
760- }
761-}
762- 
763-Status PrepareFusionCustomOpArtifacts(const ComputeGraphPtr &root_graph,
764- const std::vector<PreparedFusionRegion> &prepared_regions,
765- FusionCustomOpArtifacts &artifacts) {
766- artifacts = {};
767- std::map<std::string, OpSoBinPtr> updated_buffer;
768- const auto current_buffer = root_graph->GetExtAttr<std::map<std::string, OpSoBinPtr>>(kSoBufferAttr);
769- if (current_buffer != nullptr) {
770- updated_buffer = *current_buffer;
771- }
772- 
773- std::vector<OpSoBinPtr> bins_to_load;
774- const auto registry = CustomOpFactory::GetGlobalRegistryPtr();
775- GE_CHECK_NOTNULL(registry);
776- for (const auto &prepared : prepared_regions) {
777- const auto so_bin = CreateFusionCustomOpSoBin(prepared);
778- GE_CHECK_NOTNULL(so_bin);
779- const std::string so_key = so_bin->GetVendorName() + "/" + so_bin->GetSoName();
780- const auto existing = updated_buffer.find(so_key);
781- if ((existing != updated_buffer.end()) && !IsSameSoBin(existing->second, so_bin)) {
782- GELOGE(PARAM_INVALID, "HostCPU fusion custom-op SO key[%s] maps to different contents.", so_key.c_str());
783- return PARAM_INVALID;
784- }
785- if (existing == updated_buffer.end()) {
786- updated_buffer.emplace(so_key, so_bin);
787- artifacts.inserted_so_keys.emplace_back(so_key);
788- }
789- const AscendString op_type(prepared.codegen.register_name.c_str());
790- if (!registry->HasCreator(op_type, OpBackend::kHostCPU)) {
791- bins_to_load.emplace_back(so_bin);
792- artifacts.registered_op_types.emplace_back(op_type);
793- }
794- }
795- 
796- if (!bins_to_load.empty()) {
797- std::vector<CustomOpSoHandlePtr> handles;
798- GE_CHK_STATUS_RET(CustomOpSoLoader::GetInstance().LoadCustomOpSoBins(bins_to_load, handles),
799- "Failed to load generated HostCPU custom-op SOs.");
800- const auto status = CustomOpRegistryBuilder::AddCreatorsFromSoHandles(handles, registry);
801- if (status != SUCCESS) {
802- GELOGE(status, "Failed to register generated HostCPU custom-op creators.");
803- artifacts.registered_op_types.clear();
804- return status;
805- }
806- }
807- if (!root_graph->SetExtAttr(kSoBufferAttr, updated_buffer)) {
808- CustomOpFactory::RemoveCustomOps(artifacts.registered_op_types);
809- artifacts.registered_op_types.clear();
810- GELOGE(FAILED, "Failed to save generated HostCPU custom-op SOs on root graph[%s].", root_graph->GetName().c_str());
811- return FAILED;
812- }
813- return SUCCESS;
814-}
815- 
816Status CommitFusionRegions(const ComputeGraphPtr &graph, const std::vector<PreparedFusionRegion> &prepared_regions,688Status CommitFusionRegions(const ComputeGraphPtr &graph, const std::vector<PreparedFusionRegion> &prepared_regions,
817 NodeEngineMap &node_atomic_engine_map, NodeEngineMap &node_composite_engine_map) {689 NodeEngineMap &node_atomic_engine_map, NodeEngineMap &node_composite_engine_map) {
818 const auto root_graph = GraphUtils::FindRootGraph(graph);690 const auto root_graph = GraphUtils::FindRootGraph(graph);
@@ -820,23 +692,19 @@ Status CommitFusionRegions(const ComputeGraphPtr &graph, const std::vector<Prepa
820 GELOGE(FAILED, "Failed to find root graph when committing HostCPU fusion for graph %s.", graph->GetName().c_str());692 GELOGE(FAILED, "Failed to find root graph when committing HostCPU fusion for graph %s.", graph->GetName().c_str());
821 return FAILED;693 return FAILED;
822 }694 }
823- FusionCustomOpArtifacts artifacts;
824- GE_CHK_STATUS_RET(PrepareFusionCustomOpArtifacts(root_graph, prepared_regions, artifacts),
825- "Failed to prepare HostCPU fusion custom-op artifacts for graph[%s].", graph->GetName().c_str());
826 std::vector<NodePtr> fused_nodes;695 std::vector<NodePtr> fused_nodes;
696+ std::vector<std::string> root_graph_so_keys;
827 std::vector<ReplacedFusionOutput> replaced_outputs;697 std::vector<ReplacedFusionOutput> replaced_outputs;
828 GELOGD("HostCPU fusion starts graph commit: graph[%s], regions=%zu.", graph->GetName().c_str(),698 GELOGD("HostCPU fusion starts graph commit: graph[%s], regions=%zu.", graph->GetName().c_str(),
829 prepared_regions.size());699 prepared_regions.size());
830 for (const auto &prepared : prepared_regions) {700 for (const auto &prepared : prepared_regions) {
831- if (!AddPreparedFusionNode(graph, prepared, fused_nodes, replaced_outputs)) {701+ if (!AddPreparedFusionNode(graph, root_graph, prepared, fused_nodes, root_graph_so_keys, replaced_outputs)) {
832- (void)RollbackFusionCommit(graph, fused_nodes, replaced_outputs);702+ (void)RollbackFusionCommit(graph, root_graph, fused_nodes, replaced_outputs, root_graph_so_keys);
833- RollbackFusionCustomOpArtifacts(root_graph, artifacts);
834 return FAILED;703 return FAILED;
835 }704 }
836 }705 }
837 if (!ValidateFusionGraph(graph, "transition")) {706 if (!ValidateFusionGraph(graph, "transition")) {
838- (void)RollbackFusionCommit(graph, fused_nodes, replaced_outputs);707+ (void)RollbackFusionCommit(graph, root_graph, fused_nodes, replaced_outputs, root_graph_so_keys);
839- RollbackFusionCustomOpArtifacts(root_graph, artifacts);
840 return FAILED;708 return FAILED;
841 }709 }
842 GELOGD("HostCPU fusion transition graph validation passed: graph[%s], fused_nodes=%zu.", graph->GetName().c_str(),710 GELOGD("HostCPU fusion transition graph validation passed: graph[%s], fused_nodes=%zu.", graph->GetName().c_str(),
@@ -846,8 +714,8 @@ Status CommitFusionRegions(const ComputeGraphPtr &graph, const std::vector<Prepa
846 return FAILED;714 return FAILED;
847 }715 }
848 for (const auto &node : fused_nodes) {716 for (const auto &node : fused_nodes) {
849- node_atomic_engine_map[node] = kEngineNameCustom;717+ node_atomic_engine_map[node] = kHostCpuEngineName;
850- node_composite_engine_map[node] = kEngineNameCustom;718+ node_composite_engine_map[node] = kHostCpuEngineName;
851 }719 }
852 if (!ValidateFusionGraph(graph, "final")) {720 if (!ValidateFusionGraph(graph, "final")) {
853 return FAILED;721 return FAILED;
@@ -866,7 +734,7 @@ HostCpuFusionPass::HostCpuFusionPass(std::shared_ptr<HostCpuFusionCompiler> comp
866 }734 }
867 if (op_support_checker_ == nullptr) {735 if (op_support_checker_ == nullptr) {
868 op_support_checker_ = [](const std::string &op_type) {736 op_support_checker_ = [](const std::string &op_type) {
869- return HostCpuEngine::GetInstance().IsHostKernelSupported(op_type);737+ return HostCpuEngine::GetInstance().IsFusedCpuKernelSupported(op_type);
870 };738 };
871 }739 }
872}740}
@@ -12,11 +12,11 @@
12#define INC_FRAMEWORK_COMMON_HOST_CPU_FUSION_ATTR_H_12#define INC_FRAMEWORK_COMMON_HOST_CPU_FUSION_ATTR_H_
13 13 
14namespace ge {14namespace ge {
15-// Compiler 和 RT2 通过这些属性识别由 HostCPU 融合生成的普通 HostCPU 自定义算子15+// Compiler、HostCPU Builder 和 RT2 通过这些属性传递 FusedHostCpu 内部序列化信息
16constexpr char kFusedHostCpuOpType[] = "FusedHostCpu";16constexpr char kFusedHostCpuOpType[] = "FusedHostCpu";
17constexpr char kFusedHostCpuRegisterName[] = "_host_cpu_fusion_register_name";17constexpr char kFusedHostCpuRegisterName[] = "_host_cpu_fusion_register_name";
18-constexpr char kFusedHostCpuGenerated[] = "_host_cpu_fusion_generated";18+constexpr char kFusedHostCpuSoKey[] = "_host_cpu_fusion_so_key";
19-constexpr char kFusedHostCpuSoVendor[] = "host_cpu_fusion";19+constexpr char kFusedHostCpuSoDataPrefix[] = "_hostcpu_codegen_so_data_";
20constexpr char kFusedHostCpuOriginalNodes[] = "_host_cpu_fusion_original_nodes";20constexpr char kFusedHostCpuOriginalNodes[] = "_host_cpu_fusion_original_nodes";
21constexpr char kFusedHostCpuOriginalTypes[] = "_host_cpu_fusion_original_types";21constexpr char kFusedHostCpuOriginalTypes[] = "_host_cpu_fusion_original_types";
22constexpr char kFusedHostCpuOutputRefs[] = "_host_cpu_fusion_output_refs";22constexpr char kFusedHostCpuOutputRefs[] = "_host_cpu_fusion_output_refs";
@@ -29,7 +29,11 @@
29#include "register/kernel_registry.h"29#include "register/kernel_registry.h"
30#include "graph_builder/bg_rt_session.h"30#include "graph_builder/bg_rt_session.h"
31#include "engine/aicpu/kernel/aicpu_resource_manager.h"31#include "engine/aicpu/kernel/aicpu_resource_manager.h"
32+#include "engine/aicpu/kernel/fused_host_cpu_compute.h"
32#include "graph/utils/graph_utils.h"33#include "graph/utils/graph_utils.h"
34+#include "rt_external_mem.h"
35+#include "exe_graph/lowering/frame_selector.h"
36+#include "framework/common/host_cpu_fusion_attr.h"
33 37 
34namespace gert {38namespace gert {
35namespace {39namespace {
@@ -242,11 +246,133 @@ LowerResult LoweringAiCpuCCNode(const ge::NodePtr &node, const LowerInput &lower
242 return {HyperStatus::Success(), {cc_launch_holder, launch_holder}, node_output.shapes, out_addrs};246 return {HyperStatus::Success(), {cc_launch_holder, launch_holder}, node_output.shapes, out_addrs};
243}247}
244 248 
249+bool GetFusedHostCpuSoData(const ge::NodePtr &node, std::string &fused_register_name, ge::Buffer &so_data,
250+ const char *&error_message, std::string &so_key, ge::ComputeGraphPtr &root_graph) {
251+ error_message = "Load fused HostCPU kernel failed";
252+ if (!ge::AttrUtils::GetStr(node->GetOpDescBarePtr(), ge::kFusedHostCpuRegisterName, fused_register_name)) {
253+ error_message = "Load fused HostCPU kernel failed";
254+ GELOGE(ge::INTERNAL_ERROR, "Load fused HostCPU kernel failed for node %s: register name is missing.",
255+ node->GetNamePtr());
256+ return false;
257+ }
258+ if (!ge::AttrUtils::GetStr(node->GetOpDescBarePtr(), ge::kFusedHostCpuSoKey, so_key)) {
259+ error_message = "Load fused HostCPU kernel failed";
260+ GELOGE(ge::INTERNAL_ERROR, "Load fused HostCPU kernel failed for node %s: so key is missing.", node->GetNamePtr());
261+ return false;
262+ }
263+ const auto owner_graph = node->GetOwnerComputeGraph();
264+ root_graph = ge::GraphUtils::FindRootGraph(owner_graph);
265+ if (root_graph == nullptr) {
266+ error_message = "Load fused HostCPU kernel failed";
267+ GELOGE(ge::INTERNAL_ERROR, "Load fused HostCPU kernel failed for node %s: root graph was not found.",
268+ node->GetNamePtr());
269+ return false;
270+ }
271+ if (!ge::AttrUtils::GetBytes(root_graph, so_key, so_data)) {
272+ error_message = "Load fused HostCPU kernel failed";
273+ GELOGE(ge::INTERNAL_ERROR,
274+ "Load fused HostCPU kernel failed for node %s: so key[%s] was not found in root graph[%s], "
275+ "owner graph[%s].",
276+ node->GetNamePtr(), so_key.c_str(), root_graph->GetName().c_str(),
277+ owner_graph == nullptr ? "null" : owner_graph->GetName().c_str());
278+ return false;
279+ }
280+ return true;
281+}
282+ 
283+bool LoadFusedHostCpuKernel(const ge::NodePtr &node, std::string &fused_register_name,
284+ FusedHostCpuKernelFunctions &kernel_funcs, void *&fused_kernel_state,
285+ const char *&error_message) {
286+ std::string so_key;
287+ ge::Buffer so_data;
288+ ge::ComputeGraphPtr root_graph;
289+ if (!GetFusedHostCpuSoData(node, fused_register_name, so_data, error_message, so_key, root_graph)) {
290+ return false;
291+ }
292+ GELOGD("Load fused HostCPU kernel for node[%s]: register_name[%s], so_key[%s], so_graph[%s], so_size=%zu.",
293+ node->GetNamePtr(), fused_register_name.c_str(), so_key.c_str(), root_graph->GetName().c_str(),
294+ so_data.GetSize());
295+ auto &resource_manager = AicpuResourceManager::GetInstance();
296+ if (resource_manager.LoadFusedHostCpuSo(fused_register_name, so_data.GetData(), so_data.GetSize()) !=
297+ ge::GRAPH_SUCCESS) {
298+ GELOGE(ge::INTERNAL_ERROR, "Load fused HostCPU kernel failed for node %s.", node->GetNamePtr());
299+ return false;
300+ }
301+ kernel_funcs = resource_manager.GetFusedHostCpuKernelFunctions(fused_register_name);
302+ if ((kernel_funcs.create_func == nullptr) || (kernel_funcs.destroy_func == nullptr) ||
303+ (kernel_funcs.run_func == nullptr)) {
304+ error_message = "Resolve fused HostCPU entry failed";
305+ GELOGE(ge::INTERNAL_ERROR, "Resolve fused HostCPU private entry failed for node %s.", node->GetNamePtr());
306+ return false;
307+ }
308+ fused_kernel_state = kernel_funcs.create_func();
309+ if (fused_kernel_state == nullptr) {
310+ error_message = "Prepare fused HostCPU state failed";
311+ (void)resource_manager.ReleaseFusedHostCpuSo(fused_register_name);
312+ GELOGE(ge::INTERNAL_ERROR, "Prepare fused HostCPU execution state failed for node %s.", node->GetNamePtr());
313+ return false;
314+ }
315+ return true;
316+}
317+ 
318+void *CreateFusedHostCpuComputeState(const ge::NodePtr &node, const size_t in_num, const size_t io_num,
319+ const std::string &fused_register_name,
320+ const FusedHostCpuKernelFunctions &kernel_funcs, void *fused_kernel_state) {
321+ std::vector<FusedHostCpuTensorMeta> tensor_metas;
322+ tensor_metas.reserve(io_num);
323+ for (size_t i = 0U; i < in_num; ++i) {
324+ const ge::GeTensorDesc desc = node->GetOpDescBarePtr()->GetInputDesc(i);
325+ tensor_metas.emplace_back(FusedHostCpuTensorMeta{desc.GetShape().GetDimNum()});
326+ }
327+ for (size_t i = 0U; i < node->GetAllOutDataAnchorsSize(); ++i) {
328+ const ge::GeTensorDesc desc = node->GetOpDescBarePtr()->GetOutputDesc(i);
329+ tensor_metas.emplace_back(FusedHostCpuTensorMeta{desc.GetShape().GetDimNum()});
330+ }
331+ void *fused_compute_state =
332+ kernel::CreateFusedHostCpuComputeState(fused_register_name.c_str(), fused_kernel_state, kernel_funcs.destroy_func,
333+ kernel_funcs.run_func, tensor_metas.data(), tensor_metas.size());
334+ if (fused_compute_state == nullptr) {
335+ kernel_funcs.destroy_func(fused_kernel_state);
336+ (void)AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(fused_register_name);
337+ GELOGE(ge::INTERNAL_ERROR, "Prepare fused HostCPU compute state failed for node %s.", node->GetNamePtr());
338+ return nullptr;
339+ }
340+ const FusedHostCpuDestroyMeta destroy_meta = {fused_compute_state};
341+ bg::FrameSelector::OnDeInitRoot([destroy_meta]() -> std::vector<bg::ValueHolderPtr> {
342+ auto meta_holder = bg::ValueHolder::CreateConst(&destroy_meta, sizeof(destroy_meta));
343+ return {bg::ValueHolder::CreateVoidGuarder("ReleaseFusedHostCpuKernelState", meta_holder, {})};
344+ });
345+ bg::FrameSelector::OnDeInitRoot([fused_register_name]() -> std::vector<bg::ValueHolderPtr> {
346+ auto name_holder = bg::ValueHolder::CreateConst(fused_register_name.c_str(), fused_register_name.size() + 1U, true);
347+ return {bg::ValueHolder::CreateVoidGuarder("ReleaseFusedHostCpuSo", name_holder, {})};
348+ });
349+ GELOGD("Fused HostCPU kernel[%s] is ready for node[%s].", fused_register_name.c_str(), node->GetNamePtr());
350+ return fused_compute_state;
351+}
352+ 
353+void *PrepareFusedHostCpuComputeState(const ge::NodePtr &node, const size_t in_num, const size_t io_num,
354+ std::string &fused_register_name, const char *&error_message) {
355+ FusedHostCpuKernelFunctions kernel_funcs;
356+ void *fused_kernel_state = nullptr;
357+ if (!LoadFusedHostCpuKernel(node, fused_register_name, kernel_funcs, fused_kernel_state, error_message)) {
358+ return nullptr;
359+ }
360+ void *fused_compute_state =
361+ CreateFusedHostCpuComputeState(node, in_num, io_num, fused_register_name, kernel_funcs, fused_kernel_state);
362+ if (fused_compute_state == nullptr) {
363+ error_message = "Prepare fused HostCPU compute state failed";
364+ }
365+ return fused_compute_state;
366+}
367+ 
245struct HostAiCpuLoweringData {368struct HostAiCpuLoweringData {
246 const domi::KernelDef *kernel_def = nullptr;369 const domi::KernelDef *kernel_def = nullptr;
247 bg::ValueHolderPtr session_id;370 bg::ValueHolderPtr session_id;
248 bg::AicpuArgs aicpu_args;371 bg::AicpuArgs aicpu_args;
249 size_t in_num = 0U;372 size_t in_num = 0U;
373+ bool is_fused_host_cpu = false;
374+ std::string fused_register_name;
375+ void *fused_compute_state = nullptr;
250};376};
251 377 
252const char *PrepareHostAiCpuLowering(const ge::NodePtr &node, const LowerInput &lower_input,378const char *PrepareHostAiCpuLowering(const ge::NodePtr &node, const LowerInput &lower_input,
@@ -258,6 +384,18 @@ const char *PrepareHostAiCpuLowering(const ge::NodePtr &node, const LowerInput &
258 }384 }
259 lowering_data.kernel_def = &task_def->kernel();385 lowering_data.kernel_def = &task_def->kernel();
260 lowering_data.session_id = bg::GetSessionId(*lower_input.global_data);386 lowering_data.session_id = bg::GetSessionId(*lower_input.global_data);
387+ lowering_data.is_fused_host_cpu = node->GetType() == ge::kFusedHostCpuOpType;
388+ if (lowering_data.is_fused_host_cpu) {
389+ GELOGD("Lower fused HostCPU node[%s]: inputs=%zu, outputs=%zu.", node->GetNamePtr(),
390+ node->GetAllInDataAnchorsSize(), node->GetAllOutDataAnchorsSize());
391+ }
392+ 
393+ // 融合 so 只注册编排 kernel,原始 HostCPU kernel 仍由基础库提供,必须先加载基础库。
394+ if (lowering_data.is_fused_host_cpu &&
395+ (AicpuResourceManager::GetInstance().LoadConstantFoldingLib() != ge::GRAPH_SUCCESS)) {
396+ GELOGE(ge::INTERNAL_ERROR, "Load HostCPU base library failed for fused node %s.", node->GetNamePtr());
397+ return "Load HostCPU base library failed";
398+ }
261 399 
262 // alloc args400 // alloc args
263 lowering_data.in_num = node->GetInDataNodesAndAnchors().size();401 lowering_data.in_num = node->GetInDataNodesAndAnchors().size();
@@ -270,6 +408,15 @@ const char *PrepareHostAiCpuLowering(const ge::NodePtr &node, const LowerInput &
270 }408 }
271 const auto io_num = lowering_data.in_num + node->GetAllOutDataAnchorsSize();409 const auto io_num = lowering_data.in_num + node->GetAllOutDataAnchorsSize();
272 lowering_data.aicpu_args = bg::BuildHostCCAicpuArg(node, *lowering_data.kernel_def, io_num, lowering_data.session_id);410 lowering_data.aicpu_args = bg::BuildHostCCAicpuArg(node, *lowering_data.kernel_def, io_num, lowering_data.session_id);
411+ 
412+ if (lowering_data.is_fused_host_cpu) {
413+ const char *fused_state_error = nullptr;
414+ lowering_data.fused_compute_state = PrepareFusedHostCpuComputeState(
415+ node, lowering_data.in_num, io_num, lowering_data.fused_register_name, fused_state_error);
416+ if (lowering_data.fused_compute_state == nullptr) {
417+ return fused_state_error;
418+ }
419+ }
273 return nullptr;420 return nullptr;
274}421}
275 422 
@@ -281,6 +428,12 @@ LowerResult BuildHostAiCpuLoweringResult(const ge::NodePtr &node, const LowerInp
281 428 
282 std::vector<bg::DevMemValueHolderPtr> output_addrs;429 std::vector<bg::DevMemValueHolderPtr> output_addrs;
283 const bg::IoInfo io_info{lower_input.input_addrs, lower_input.input_shapes, output_sizes, output_shapes};430 const bg::IoInfo io_info{lower_input.input_addrs, lower_input.input_shapes, output_sizes, output_shapes};
431+ if (lowering_data.is_fused_host_cpu) {
432+ auto compute_holder = bg::BuildFusedHostCpuComputeNode(node, lowering_data.fused_compute_state, io_info,
433+ *lower_input.global_data, output_addrs);
434+ SetReleaseAfter(lower_input.input_addrs, compute_holder);
435+ return {HyperStatus::Success(), {}, output_shapes, output_addrs};
436+ }
284 auto compute_holder =437 auto compute_holder =
285 bg::AicpuHostCompute(node, lowering_data.aicpu_args, io_info, *lower_input.global_data, output_addrs);438 bg::AicpuHostCompute(node, lowering_data.aicpu_args, io_info, *lower_input.global_data, output_addrs);
286 439 
@@ -10,6 +10,7 @@
10 10 
11#include "bg_launch.h"11#include "bg_launch.h"
12#include "bg_ext_info.h"12#include "bg_ext_info.h"
13+#include <algorithm>
13#include <mutex>14#include <mutex>
14#include "framework/common/taskdown_common.h"15#include "framework/common/taskdown_common.h"
15#include "register/kernel_registry.h"16#include "register/kernel_registry.h"
@@ -22,6 +23,7 @@
22#include "graph_builder/bg_condition.h"23#include "graph_builder/bg_condition.h"
23#include "graph/utils/node_utils.h"24#include "graph/utils/node_utils.h"
24#include "exe_graph/lowering/frame_selector.h"25#include "exe_graph/lowering/frame_selector.h"
26+#include "engine/aicpu/kernel/fused_host_cpu_compute.h"
25 27 
26namespace gert {28namespace gert {
27namespace bg {29namespace bg {
@@ -45,6 +47,7 @@ DevMemValueHolderPtr AllocHostCpuOutputMemory(const ge::NodePtr &node, const IoI
45 inputs.insert(inputs.cend(), io_info.input_addrs.cbegin(), io_info.input_addrs.cend());47 inputs.insert(inputs.cend(), io_info.input_addrs.cbegin(), io_info.input_addrs.cend());
46 auto output = DevMemValueHolder::CreateSingleDataOutput("AllocHostCpuOutputMemory", inputs,48 auto output = DevMemValueHolder::CreateSingleDataOutput("AllocHostCpuOutputMemory", inputs,
47 node->GetOpDescBarePtr()->GetStreamId());49 node->GetOpDescBarePtr()->GetStreamId());
50+ GE_ASSERT_NOTNULL(output);
48 output->SetPlacement(kOnHost);51 output->SetPlacement(kOnHost);
49 return output;52 return output;
50}53}
@@ -269,5 +272,46 @@ ValueHolderPtr AicpuHostCompute(const ge::NodePtr &node, const AicpuArgs &args,
269 }272 }
270 return compute_holder;273 return compute_holder;
271}274}
275+ 
276+ValueHolderPtr BuildFusedHostCpuComputeNode(const ge::NodePtr &node, void *compute_state, const IoInfo &io_info,
277+ LoweringGlobalData &global_data,
278+ std::vector<DevMemValueHolderPtr> &output_addrs) {
279+ GE_ASSERT_NOTNULL(node);
280+ GE_ASSERT_NOTNULL(compute_state);
281+ const auto op_desc = node->GetOpDescBarePtr();
282+ GE_ASSERT_NOTNULL(op_desc);
283+ GE_ASSERT_TRUE(io_info.input_shapes.size() == io_info.input_addrs.size());
284+ GE_ASSERT_TRUE(io_info.output_shapes.size() == io_info.output_sizes.size());
285+ 
286+ output_addrs = AllocHostCpuOutputsMemory(node, io_info, global_data);
287+ GE_ASSERT_TRUE(io_info.output_shapes.size() == output_addrs.size());
288+ 
289+ const size_t input_num = io_info.input_shapes.size();
290+ const size_t output_num = io_info.output_shapes.size();
291+ GE_ASSERT_TRUE(input_num == op_desc->GetAllInputsSize());
292+ GE_ASSERT_TRUE(output_num == op_desc->GetOutputsSize());
293+ GE_ASSERT_TRUE(output_num != 0U);
294+ 
295+ const FusedHostCpuComputeMeta compute_meta = {compute_state, input_num, output_num};
296+ 
297+ std::vector<ValueHolderPtr> inputs;
298+ inputs.emplace_back(ValueHolder::CreateConst(&compute_meta, sizeof(compute_meta)));
299+ inputs.insert(inputs.cend(), io_info.input_shapes.cbegin(), io_info.input_shapes.cend());
300+ inputs.insert(inputs.cend(), io_info.input_addrs.cbegin(), io_info.input_addrs.cend());
301+ inputs.insert(inputs.cend(), io_info.output_shapes.cbegin(), io_info.output_shapes.cend());
302+ inputs.insert(inputs.cend(), output_addrs.cbegin(), output_addrs.cend());
303+ 
304+ const auto allocated_output_addrs = output_addrs;
305+ output_addrs = DevMemValueHolder::CreateDataOutput("FusedHostCpuCompute", inputs, output_num, op_desc->GetStreamId());
306+ GE_ASSERT_EQ(output_addrs.size(), allocated_output_addrs.size());
307+ for (size_t i = 0U; i < output_addrs.size(); ++i) {
308+ GE_ASSERT_NOTNULL(output_addrs[i]);
309+ GE_ASSERT_NOTNULL(allocated_output_addrs[i]);
310+ output_addrs[i]->SetPlacement(allocated_output_addrs[i]->GetPlacement());
311+ }
312+ GELOGD("Build fused HostCPU private-entry compute: node[%s], inputs=%zu, outputs=%zu.", node->GetNamePtr(), input_num,
313+ output_num);
314+ return output_addrs[0U];
315+}
272} // namespace bg316} // namespace bg
273} // namespace gert317} // namespace gert
@@ -52,6 +52,10 @@ ValueHolderPtr AicpuHostExecFuncProcess(const AicpuHostProcFunc &func, const IoI
52 const std::vector<DevMemValueHolderPtr> &output_addrs);52 const std::vector<DevMemValueHolderPtr> &output_addrs);
53ValueHolderPtr AicpuHostCompute(const ge::NodePtr &node, const AicpuArgs &args, const IoInfo &io_info,53ValueHolderPtr AicpuHostCompute(const ge::NodePtr &node, const AicpuArgs &args, const IoInfo &io_info,
54 LoweringGlobalData &global_data, std::vector<DevMemValueHolderPtr> &output_addrs);54 LoweringGlobalData &global_data, std::vector<DevMemValueHolderPtr> &output_addrs);
55+// Build the ExecuteGraph node; the registered runtime kernel is FusedHostCpuCompute.
56+ValueHolderPtr BuildFusedHostCpuComputeNode(const ge::NodePtr &node, void *compute_state, const IoInfo &io_info,
57+ LoweringGlobalData &global_data,
58+ std::vector<DevMemValueHolderPtr> &output_addrs);
55ValueHolderPtr GetContainerIdHolder(const LowerInput &lower_input);59ValueHolderPtr GetContainerIdHolder(const LowerInput &lower_input);
56} // namespace bg60} // namespace bg
57} // namespace gert61} // namespace gert
@@ -12,6 +12,8 @@
12#include <cstddef>12#include <cstddef>
13#include <iomanip>13#include <iomanip>
14#include <cinttypes>14#include <cinttypes>
15+#include <memory>
16+#include <new>
15#include "aicpu_ext_info_handle.h"17#include "aicpu_ext_info_handle.h"
16#include "graph/error_codes.h"18#include "graph/error_codes.h"
17#include "register/kernel_registry.h"19#include "register/kernel_registry.h"
@@ -29,6 +31,7 @@
29#include "core/debug/kernel_tracing.h"31#include "core/debug/kernel_tracing.h"
30#include "core/executor/multi_thread_topological/executor/schedule/producer/producers/kernel_tags/critical_section_config.h"32#include "core/executor/multi_thread_topological/executor/schedule/producer/producers/kernel_tags/critical_section_config.h"
31#include "aicpu_resource_manager.h"33#include "aicpu_resource_manager.h"
34+#include "fused_host_cpu_compute.h"
32#include "engine/aicpu/graph_builder/bg_aicpu_arg.h"35#include "engine/aicpu/graph_builder/bg_aicpu_arg.h"
33#include "aicpu_args_handler.h"36#include "aicpu_args_handler.h"
34#include "block_op_utils.h"37#include "block_op_utils.h"
@@ -39,6 +42,7 @@
39#include "exe_graph/runtime/gert_tensor_data.h"42#include "exe_graph/runtime/gert_tensor_data.h"
40#include "graph/load/model_manager/model_manager.h"43#include "graph/load/model_manager/model_manager.h"
41#include "aicpu_bin_handler.h"44#include "aicpu_bin_handler.h"
45+#include "exe_graph/runtime/storage_shape.h"
42 46 
43using namespace ge;47using namespace ge;
44 48 
@@ -412,6 +416,183 @@ ge::graphStatus AicpuHostCompute(KernelContext *context) {
412}416}
413REGISTER_KERNEL(AicpuHostCompute).RunFunc(AicpuHostCompute);417REGISTER_KERNEL(AicpuHostCompute).RunFunc(AicpuHostCompute);
414 418 
419+namespace {
420+struct FusedHostCpuTensorState {
421+ const void *data = nullptr;
422+ size_t data_size = 0U;
423+ std::vector<int64_t> dims;
424+ bool initialized = false;
425+};
426+ 
427+struct FusedHostCpuCallState {
428+ std::string register_name;
429+ void *kernel_state = nullptr;
430+ FusedHostCpuDestroyFunc destroy_func = nullptr;
431+ FusedHostCpuRunFunc run_func = nullptr;
432+ std::vector<FusedHostCpuTensorBinding> bindings;
433+ std::vector<FusedHostCpuTensorState> tensor_states;
434+};
435+ 
436+bool HasSameShape(const gert::Shape &shape, const FusedHostCpuTensorState &state) {
437+ if (state.dims.size() != shape.GetDimNum()) {
438+ return false;
439+ }
440+ for (size_t i = 0U; i < shape.GetDimNum(); ++i) {
441+ if (state.dims[i] != shape.GetDim(i)) {
442+ return false;
443+ }
444+ }
445+ return true;
446+}
447+ 
448+ge::graphStatus BuildFusedHostCpuBinding(const StorageShape *storage_shape, GertTensorData *tensor_data,
449+ FusedHostCpuTensorState &state, FusedHostCpuTensorBinding &binding) {
450+ GE_ASSERT_NOTNULL(storage_shape);
451+ GE_ASSERT_NOTNULL(tensor_data);
452+ GE_ASSERT_TRUE((tensor_data->GetSize() == 0U) || (tensor_data->GetAddr() != nullptr));
453+ const auto &origin_shape = storage_shape->GetOriginShape();
454+ const bool shape_changed = !state.initialized || !HasSameShape(origin_shape, state);
455+ if (shape_changed) {
456+ if (state.dims.size() != origin_shape.GetDimNum()) {
457+ state.dims.resize(origin_shape.GetDimNum());
458+ }
459+ for (size_t i = 0U; i < state.dims.size(); ++i) {
460+ state.dims[i] = origin_shape.GetDim(i);
461+ }
462+ binding.dims = state.dims.data();
463+ binding.dim_num = state.dims.size();
464+ }
465+ const void *data = tensor_data->GetAddr();
466+ const size_t data_size = tensor_data->GetSize();
467+ const bool data_changed = !state.initialized || (state.data != data) || (state.data_size != data_size);
468+ if (data_changed) {
469+ binding.data = reinterpret_cast<uint8_t *>(tensor_data->GetAddr());
470+ binding.data_size = data_size;
471+ state.data = data;
472+ state.data_size = data_size;
473+ }
474+ binding.flags = (shape_changed ? kFusedHostCpuShapeChanged : 0U) | (data_changed ? kFusedHostCpuDataChanged : 0U);
475+ state.initialized = true;
476+ return ge::GRAPH_SUCCESS;
477+}
478+} // namespace
479+ 
480+void *CreateFusedHostCpuComputeState(const char *register_name, void *kernel_state,
481+ const FusedHostCpuDestroyFunc destroy_func, const FusedHostCpuRunFunc run_func,
482+ const FusedHostCpuTensorMeta *tensor_metas, const size_t io_num) {
483+ if ((register_name == nullptr) || (kernel_state == nullptr) || (destroy_func == nullptr) || (run_func == nullptr) ||
484+ (tensor_metas == nullptr) || (io_num == 0U)) {
485+ GELOGE(ge::PARAM_INVALID,
486+ "Invalid fused HostCPU compute state arguments: register_null[%d], kernel_state_null[%d], "
487+ "destroy_null[%d], run_null[%d], tensor_metas_null[%d], io_num[%zu].",
488+ static_cast<int32_t>(register_name == nullptr), static_cast<int32_t>(kernel_state == nullptr),
489+ static_cast<int32_t>(destroy_func == nullptr), static_cast<int32_t>(run_func == nullptr),
490+ static_cast<int32_t>(tensor_metas == nullptr), io_num);
491+ return nullptr;
492+ }
493+ std::unique_ptr<FusedHostCpuCallState> state = std::make_unique<FusedHostCpuCallState>();
494+ state->register_name = register_name;
495+ state->kernel_state = kernel_state;
496+ state->destroy_func = destroy_func;
497+ state->run_func = run_func;
498+ state->bindings.reserve(io_num);
499+ state->tensor_states.reserve(io_num);
500+ for (size_t i = 0U; i < io_num; ++i) {
501+ std::vector<int64_t> dims(tensor_metas[i].dim_num, ge::UNKNOWN_DIM);
502+ FusedHostCpuTensorState tensor_state;
503+ tensor_state.dims = std::move(dims);
504+ state->tensor_states.emplace_back(std::move(tensor_state));
505+ state->bindings.emplace_back(
506+ FusedHostCpuTensorBinding{state->tensor_states.back().dims.data(), nullptr, tensor_metas[i].dim_num, 0U, 0U});
507+ }
508+ return state.release();
509+}
510+ 
511+void DestroyFusedHostCpuComputeState(void *compute_state) {
512+ FusedHostCpuCallState *state = static_cast<FusedHostCpuCallState *>(compute_state);
513+ if (state == nullptr) {
514+ return;
515+ }
516+ state->destroy_func(state->kernel_state);
517+ delete state;
518+}
519+ 
520+// Runtime callback for the ExecuteGraph kernel registered as FusedHostCpuCompute.
521+ge::graphStatus RunFusedHostCpuCompute(KernelContext *context) {
522+ GE_ASSERT_NOTNULL(context);
523+ const auto compute_meta = context->GetInputPointer<FusedHostCpuComputeMeta>(0U);
524+ GE_ASSERT_NOTNULL(compute_meta);
525+ FusedHostCpuCallState *call_state = static_cast<FusedHostCpuCallState *>(compute_meta->compute_state);
526+ GE_ASSERT_NOTNULL(call_state);
527+ GE_ASSERT_NOTNULL(call_state->kernel_state);
528+ GE_ASSERT_NOTNULL(call_state->run_func);
529+ const auto input_num = compute_meta->input_num;
530+ const auto output_num = compute_meta->output_num;
531+ const auto io_num = input_num + output_num;
532+ GE_ASSERT_TRUE(call_state->bindings.size() == io_num);
533+ GE_ASSERT_TRUE(call_state->tensor_states.size() == io_num);
534+ 
535+ const size_t input_shape_start = 1U;
536+ const size_t input_addr_start = input_shape_start + input_num;
537+ const size_t output_shape_start = input_addr_start + input_num;
538+ const size_t output_addr_start = output_shape_start + output_num;
539+ GE_ASSERT_TRUE(context->GetInputNum() == (output_addr_start + output_num));
540+ 
541+ uint32_t binding_flags = 0U;
542+ for (size_t i = 0U; i < input_num; ++i) {
543+ const auto storage_shape = context->GetInputPointer<StorageShape>(input_shape_start + i);
544+ auto tensor_data = context->MutableInputPointer<GertTensorData>(input_addr_start + i);
545+ GE_ASSERT_SUCCESS(
546+ BuildFusedHostCpuBinding(storage_shape, tensor_data, call_state->tensor_states[i], call_state->bindings[i]));
547+ binding_flags |= call_state->bindings[i].flags;
548+ }
549+ 
550+ for (size_t i = 0U; i < output_num; ++i) {
551+ const auto storage_shape = context->GetInputPointer<StorageShape>(output_shape_start + i);
552+ auto tensor_data = context->MutableInputPointer<GertTensorData>(output_addr_start + i);
553+ const size_t tensor_index = input_num + i;
554+ GE_ASSERT_SUCCESS(BuildFusedHostCpuBinding(storage_shape, tensor_data, call_state->tensor_states[tensor_index],
555+ call_state->bindings[tensor_index]));
556+ binding_flags |= call_state->bindings[tensor_index].flags;
557+ }
558+ 
559+ const uint32_t ret = call_state->run_func(call_state->kernel_state,
560+ static_cast<const void *>(call_state->bindings.data()), binding_flags);
561+ GE_ASSERT_TRUE(ret == 0U, "Fused HostCPU private entry failed: register_name[%s], ret=%u.",
562+ call_state->register_name.c_str(), ret);
563+ return ge::GRAPH_SUCCESS;
564+}
565+ 
566+ge::graphStatus CreateFusedHostCpuComputeOutputs(const ge::FastNode *node, KernelContext *context) {
567+ (void)node;
568+ GE_ASSERT_NOTNULL(context);
569+ GE_ASSERT_TRUE(context->GetInputNum() >= context->GetOutputNum());
570+ const size_t output_addr_start = context->GetInputNum() - context->GetOutputNum();
571+ for (size_t i = 0U; i < context->GetOutputNum(); ++i) {
572+ auto chain = context->GetOutput(i);
573+ auto tensor_data = context->MutableInputPointer<GertTensorData>(output_addr_start + i);
574+ GE_ASSERT_NOTNULL(chain);
575+ GE_ASSERT_NOTNULL(tensor_data);
576+ chain->Set(tensor_data, nullptr);
577+ }
578+ return ge::GRAPH_SUCCESS;
579+}
580+ 
581+REGISTER_KERNEL(FusedHostCpuCompute)
582+ .RunFunc(RunFusedHostCpuCompute)
583+ .OutputsCreator(CreateFusedHostCpuComputeOutputs)
584+ .ConcurrentCriticalSectionKey(kKernelUseMemory);
585+ 
586+ge::graphStatus ReleaseFusedHostCpuKernelState(KernelContext *context) {
587+ GE_ASSERT_NOTNULL(context);
588+ const auto destroy_meta = context->GetInputPointer<FusedHostCpuDestroyMeta>(0U);
589+ GE_ASSERT_NOTNULL(destroy_meta);
590+ GE_ASSERT_NOTNULL(destroy_meta->compute_state);
591+ DestroyFusedHostCpuComputeState(destroy_meta->compute_state);
592+ return ge::GRAPH_SUCCESS;
593+}
594+REGISTER_KERNEL(ReleaseFusedHostCpuKernelState).RunFunc(ReleaseFusedHostCpuKernelState);
595+ 
415ge::graphStatus AicpuHostExecFunc(KernelContext *context) {596ge::graphStatus AicpuHostExecFunc(KernelContext *context) {
416 const auto input_size = context->GetInputNum();597 const auto input_size = context->GetInputNum();
417 // func取输入的最后一个,因为前面输入个数不固定。598 // func取输入的最后一个,因为前面输入个数不固定。
@@ -9,6 +9,8 @@
9 */9 */
10 10 
11#include "aicpu_resource_manager.h"11#include "aicpu_resource_manager.h"
12+#include <algorithm>
13+#include <cstddef>
12#include "framework/common/debug/ge_log.h"14#include "framework/common/debug/ge_log.h"
13#include "exe_graph/runtime/kernel_context.h"15#include "exe_graph/runtime/kernel_context.h"
14#include "rt_external_kernel.h"16#include "rt_external_kernel.h"
@@ -23,6 +25,14 @@
23#include "mmpa/mmpa_api.h"25#include "mmpa/mmpa_api.h"
24#include "graph/load/model_manager/model_manager.h"26#include "graph/load/model_manager/model_manager.h"
25#include "common/aclrt_malloc_helper.h"27#include "common/aclrt_malloc_helper.h"
28+#include "framework/common/host_cpu_fusion_attr.h"
29+ 
30+#if defined(__linux__)
31+#include <cerrno>
32+#include <dlfcn.h>
33+#include <sys/syscall.h>
34+#include <unistd.h>
35+#endif
26 36 
27namespace gert {37namespace gert {
28namespace {38namespace {
@@ -34,6 +44,78 @@ void FreeHbmMem(void *p) {
34 44 
35const std::string kHostCpuLibRelativePathOld = "/op_impl/built-in/host_cpu/libconstant_folding_ops.so";45const std::string kHostCpuLibRelativePathOld = "/op_impl/built-in/host_cpu/libconstant_folding_ops.so";
36const std::string kHostCpuLibRelativePath = "/built-in/op_impl/host_cpu/libconstant_folding_ops.so";46const std::string kHostCpuLibRelativePath = "/built-in/op_impl/host_cpu/libconstant_folding_ops.so";
47+constexpr size_t kMaxFusedHostCpuSoSize = 10U * 1024U * 1024U;
48+constexpr size_t kMaxFusedRegisterNameSize = 160U;
49+constexpr char kValidateFusedHostCpuKernelRegistration[] = "ValidateFusedHostCpuKernelRegistration";
50+constexpr char kCreateFusedHostCpuKernelState[] = "CreateFusedHostCpuKernelState";
51+constexpr char kDestroyFusedHostCpuKernelState[] = "DestroyFusedHostCpuKernelState";
52+constexpr char kRunFusedHostCpuKernel[] = "RunFusedHostCpuKernel";
53+ 
54+bool IsAsciiAlphaNumeric(const unsigned char ch) {
55+ return ((ch >= '0') && (ch <= '9')) || ((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z'));
56+}
57+ 
58+bool IsValidFusedRegisterName(const std::string &register_name) {
59+ const std::string prefix = std::string(ge::kFusedHostCpuOpType) + "_";
60+ if ((register_name.size() <= prefix.size()) || (register_name.size() > kMaxFusedRegisterNameSize) ||
61+ (register_name.compare(0U, prefix.size(), prefix) != 0)) {
62+ return false;
63+ }
64+ return std::all_of(register_name.cbegin() + static_cast<std::ptrdiff_t>(prefix.size()), register_name.cend(),
65+ [](const unsigned char ch) { return IsAsciiAlphaNumeric(ch) || (ch == '_'); });
66+}
67+ 
68+uint64_t HashFusedSo(const uint8_t *data, const size_t size) {
69+ uint64_t hash = 1469598103934665603ULL;
70+ for (size_t i = 0U; i < size; ++i) {
71+ hash ^= data[i];
72+ hash *= 1099511628211ULL;
73+ }
74+ return hash;
75+}
76+ 
77+bool IsExpectedFusedElf(const uint8_t *data, const size_t size) {
78+ if ((size < 20U) || (data[0] != 0x7FU) || (data[1] != 'E') || (data[2] != 'L') || (data[3] != 'F') ||
79+ (data[4] != 2U) || (data[5] != 1U) || (data[6] != 1U) || (data[16] != 3U) || (data[17] != 0U)) {
80+ return false;
81+ }
82+ const uint16_t machine = static_cast<uint16_t>(data[18]) | (static_cast<uint16_t>(data[19]) << 8U);
83+#if defined(__aarch64__)
84+ return machine == 183U;
85+#elif defined(__x86_64__)
86+ return machine == 62U;
87+#else
88+ (void)machine;
89+ return true;
90+#endif
91+}
92+ 
93+bool ValidateFusedHostCpuRegistration(void *handle, const std::string &register_name) {
94+ using ValidateRegistration = bool (*)(const char *);
95+ const auto validate =
96+ reinterpret_cast<ValidateRegistration>(mmDlsym(handle, kValidateFusedHostCpuKernelRegistration));
97+ return (validate != nullptr) && validate(register_name.c_str());
98+}
99+ 
100+#if defined(__linux__)
101+bool WriteAll(const int fd, const uint8_t *data, const size_t size) {
102+ size_t offset = 0U;
103+ while (offset < size) {
104+ const ssize_t written = write(fd, data + offset, size - offset);
105+ if (written < 0) {
106+ if (errno == EINTR) {
107+ continue;
108+ }
109+ return false;
110+ }
111+ if (written == 0) {
112+ return false;
113+ }
114+ offset += static_cast<size_t>(written);
115+ }
116+ return true;
117+}
118+#endif
37 119 
38ge::graphStatus GetRealPath(std::string &path) {120ge::graphStatus GetRealPath(std::string &path) {
39 const std::string real_path = ge::RealPath(path.c_str());121 const std::string real_path = ge::RealPath(path.c_str());
@@ -72,10 +154,20 @@ AicpuResourceManager &AicpuResourceManager::GetInstance() {
72}154}
73 155 
74AicpuResourceManager::~AicpuResourceManager() {156AicpuResourceManager::~AicpuResourceManager() {
157+ // CpuKernelRegister 没有注销接口,其 std::function creator 指向 JIT so。这里必须先让基础 HostCPU
158+ // 库在 dlclose 时销毁 registry,再由进程回收仍映射的 JIT so,不能提前 dlclose 形成悬空 creator。
75 if (so_handle_ != nullptr) {159 if (so_handle_ != nullptr) {
76 (void)mmDlclose(so_handle_);160 (void)mmDlclose(so_handle_);
77 so_handle_ = nullptr;161 so_handle_ = nullptr;
78 }162 }
163+#if defined(__linux__)
164+ for (const std::pair<const uint64_t, int> &fd_entry : fused_so_fds_) {
165+ (void)close(fd_entry.second);
166+ }
167+ for (const int fd : fused_quarantined_so_fds_) {
168+ (void)close(fd);
169+ }
170+#endif
79}171}
80 172 
81ge::graphStatus AicpuResourceManager::LoadConstantFoldingLib() {173ge::graphStatus AicpuResourceManager::LoadConstantFoldingLib() {
@@ -118,6 +210,174 @@ ge::graphStatus AicpuResourceManager::LoadConstantFoldingLib() {
118 return ge::GRAPH_SUCCESS;210 return ge::GRAPH_SUCCESS;
119}211}
120 212 
213+ge::graphStatus AicpuResourceManager::TryReuseFusedHostCpuSo(const std::string &register_name, const uint8_t *so_data,
214+ const size_t so_size, const uint64_t so_hash,
215+ bool &handled) {
216+ handled = false;
217+ const auto register_iter = fused_register_hashes_.find(register_name);
218+ if (register_iter != fused_register_hashes_.end()) {
219+ handled = true;
220+ const auto content_iter = fused_so_contents_.find(so_hash);
221+ if ((register_iter->second != so_hash) || (content_iter == fused_so_contents_.end()) ||
222+ (content_iter->second.size() != so_size) ||
223+ !std::equal(content_iter->second.cbegin(), content_iter->second.cend(), so_data)) {
224+ GELOGE(ge::PARAM_INVALID, "Fused HostCPU register name %s maps to different shared objects.",
225+ register_name.c_str());
226+ return ge::PARAM_INVALID;
227+ }
228+ ++fused_register_ref_counts_[register_name];
229+ ++fused_so_ref_counts_[so_hash];
230+ GELOGD("Reuse fused HostCPU shared object by register name[%s].", register_name.c_str());
231+ return ge::GRAPH_SUCCESS;
232+ }
233+ const auto handle_iter = fused_so_handles_.find(so_hash);
234+ if (handle_iter == fused_so_handles_.end()) {
235+ return ge::GRAPH_SUCCESS;
236+ }
237+ handled = true;
238+ const auto content_iter = fused_so_contents_.find(so_hash);
239+ if ((content_iter == fused_so_contents_.end()) || (content_iter->second.size() != so_size) ||
240+ !std::equal(content_iter->second.cbegin(), content_iter->second.cend(), so_data)) {
241+ GELOGE(ge::PARAM_INVALID, "Hash collision detected while loading fused HostCPU shared object %s.",
242+ register_name.c_str());
243+ return ge::PARAM_INVALID;
244+ }
245+ GELOGE(ge::PARAM_INVALID, "Fused HostCPU shared object content is already cached by another register name %s.",
246+ register_name.c_str());
247+ return ge::PARAM_INVALID;
248+}
249+ 
250+ge::graphStatus AicpuResourceManager::LoadFusedHostCpuSo(const std::string &register_name, const uint8_t *so_data,
251+ const size_t so_size) {
252+ if (!IsValidFusedRegisterName(register_name) || (so_data == nullptr) || (so_size > kMaxFusedHostCpuSoSize) ||
253+ !IsExpectedFusedElf(so_data, so_size)) {
254+ GELOGE(ge::PARAM_INVALID, "Invalid fused HostCPU shared object for register name %s.", register_name.c_str());
255+ return ge::PARAM_INVALID;
256+ }
257+ const uint64_t so_hash = HashFusedSo(so_data, so_size);
258+ GELOGD("Load fused HostCPU shared object: register_name[%s], so_size=%zu, hash=%llu.", register_name.c_str(), so_size,
259+ static_cast<unsigned long long>(so_hash));
260+ // 同一注册名可被多个模型复用;每次成功加载都对应模型卸载阶段的一次 Release。
261+ const std::lock_guard<std::mutex> lock(fused_so_mutex_);
262+ bool handled = false;
263+ const auto reuse_status = TryReuseFusedHostCpuSo(register_name, so_data, so_size, so_hash, handled);
264+ if (handled) {
265+ return reuse_status;
266+ }
267+#if !defined(__linux__)
268+ GELOGW("Fused HostCPU shared object loading is unsupported on the current platform: register_name[%s].",
269+ register_name.c_str());
270+ return ge::UNSUPPORTED;
271+#else
272+ return LoadNewFusedHostCpuSo(register_name, so_data, so_size, so_hash);
273+#endif
274+}
275+ 
276+#if defined(__linux__)
277+ge::graphStatus AicpuResourceManager::LoadNewFusedHostCpuSo(const std::string &register_name, const uint8_t *so_data,
278+ const size_t so_size, const uint64_t so_hash) {
279+ void *handle = nullptr;
280+ int fd = -1;
281+ FusedHostCpuKernelFunctions kernel_funcs;
282+ if (OpenFusedHostCpuSo(register_name, so_data, so_size, handle, fd, kernel_funcs) != ge::GRAPH_SUCCESS) {
283+ return ge::INTERNAL_ERROR;
284+ }
285+ fused_so_handles_[so_hash] = handle;
286+ // glibc 会按 dlopen 路径复用已加载对象。保持 fd 存活,确保后续融合 SO 不会再次取得相同的
287+ // /proc/self/fd/<fd> 路径而错误复用当前 handle。
288+ fused_so_fds_[so_hash] = fd;
289+ fused_so_ref_counts_[so_hash] = 1U;
290+ fused_so_contents_[so_hash] = std::vector<uint8_t>(so_data, so_data + so_size);
291+ fused_register_hashes_[register_name] = so_hash;
292+ fused_register_ref_counts_[register_name] = 1U;
293+ fused_kernel_funcs_[register_name] = kernel_funcs;
294+ GELOGD("Fused HostCPU kernel[%s] registered successfully, cached_so_count=%zu.", register_name.c_str(),
295+ fused_so_handles_.size());
296+ return ge::GRAPH_SUCCESS;
297+}
298+ 
299+ge::graphStatus AicpuResourceManager::OpenFusedHostCpuSo(const std::string &register_name, const uint8_t *so_data,
300+ const size_t so_size, void *&handle, int &fd,
301+ FusedHostCpuKernelFunctions &kernel_funcs) {
302+ fd = static_cast<int>(syscall(__NR_memfd_create, "fused_host_cpu", 0U));
303+ if (fd < 0) {
304+ GELOGE(ge::INTERNAL_ERROR, "Create memfd for fused HostCPU shared object failed, errno=%d.", errno);
305+ return ge::INTERNAL_ERROR;
306+ }
307+ if (!WriteAll(fd, so_data, so_size)) {
308+ GELOGE(ge::INTERNAL_ERROR, "Write fused HostCPU shared object failed, errno=%d.", errno);
309+ (void)close(fd);
310+ return ge::INTERNAL_ERROR;
311+ }
312+ const std::string path = "/proc/self/fd/" + std::to_string(fd);
313+ const auto open_flag = static_cast<uint32_t>(MMPA_RTLD_NOW) | static_cast<uint32_t>(RTLD_LOCAL);
314+ GELOGD("Open fused HostCPU shared object from anonymous fd[%d] for register name[%s].", fd, register_name.c_str());
315+ handle = mmDlopen(path.c_str(), static_cast<int32_t>(open_flag));
316+ if (handle == nullptr) {
317+ const ge::char_t *error = mmDlerror();
318+ GELOGE(ge::INTERNAL_ERROR, "Load fused HostCPU shared object failed for %s, error=%s.", register_name.c_str(),
319+ (error == nullptr) ? "" : error);
320+ (void)close(fd);
321+ return ge::INTERNAL_ERROR;
322+ }
323+ if (!ValidateFusedHostCpuRegistration(handle, register_name)) {
324+ GELOGE(ge::INTERNAL_ERROR, "Shared object did not register fused HostCPU CpuKernel %s.", register_name.c_str());
325+ fused_quarantined_so_handles_.emplace_back(handle);
326+ fused_quarantined_so_fds_.emplace_back(fd);
327+ return ge::INTERNAL_ERROR;
328+ }
329+ kernel_funcs.create_func = reinterpret_cast<FusedHostCpuCreateFunc>(mmDlsym(handle, kCreateFusedHostCpuKernelState));
330+ kernel_funcs.destroy_func =
331+ reinterpret_cast<FusedHostCpuDestroyFunc>(mmDlsym(handle, kDestroyFusedHostCpuKernelState));
332+ kernel_funcs.run_func = reinterpret_cast<FusedHostCpuRunFunc>(mmDlsym(handle, kRunFusedHostCpuKernel));
333+ if ((kernel_funcs.create_func == nullptr) || (kernel_funcs.destroy_func == nullptr) ||
334+ (kernel_funcs.run_func == nullptr)) {
335+ GELOGE(ge::INTERNAL_ERROR, "Shared object does not export complete private fused HostCPU entries for %s.",
336+ register_name.c_str());
337+ fused_quarantined_so_handles_.emplace_back(handle);
338+ fused_quarantined_so_fds_.emplace_back(fd);
339+ return ge::INTERNAL_ERROR;
340+ }
341+ return ge::GRAPH_SUCCESS;
342+}
343+#endif
344+ 
345+FusedHostCpuKernelFunctions AicpuResourceManager::GetFusedHostCpuKernelFunctions(const std::string &register_name) {
346+ const std::lock_guard<std::mutex> lock(fused_so_mutex_);
347+ const auto iter = fused_kernel_funcs_.find(register_name);
348+ return (iter == fused_kernel_funcs_.end()) ? FusedHostCpuKernelFunctions() : iter->second;
349+}
350+ 
351+ge::graphStatus AicpuResourceManager::ReleaseFusedHostCpuSo(const std::string &register_name) {
352+ const std::lock_guard<std::mutex> lock(fused_so_mutex_);
353+ const auto ref_iter = fused_register_ref_counts_.find(register_name);
354+ const auto hash_iter = fused_register_hashes_.find(register_name);
355+ if ((ref_iter == fused_register_ref_counts_.end()) || (hash_iter == fused_register_hashes_.end()) ||
356+ (ref_iter->second == 0U)) {
357+ GELOGE(ge::PARAM_INVALID, "Fused HostCPU kernel %s is not owned by any loaded model.", register_name.c_str());
358+ return ge::PARAM_INVALID;
359+ }
360+ const uint64_t so_hash = hash_iter->second;
361+ const auto so_ref_iter = fused_so_ref_counts_.find(so_hash);
362+ const auto handle_iter = fused_so_handles_.find(so_hash);
363+ if ((so_ref_iter == fused_so_ref_counts_.end()) || (so_ref_iter->second == 0U) ||
364+ (handle_iter == fused_so_handles_.end())) {
365+ GELOGE(ge::INTERNAL_ERROR, "Fused HostCPU kernel %s has incomplete shared object ownership.",
366+ register_name.c_str());
367+ return ge::INTERNAL_ERROR;
368+ }
369+ --so_ref_iter->second;
370+ if (--ref_iter->second > 0U) {
371+ GELOGD("Keep fused HostCPU kernel[%s], remaining model references=%zu.", register_name.c_str(), ref_iter->second);
372+ return ge::GRAPH_SUCCESS;
373+ }
374+ 
375+ // CpuKernelRegister 不提供注销接口。creator 是定义在 JIT so 中的 std::function,引用归零后仍必须保留
376+ // so 映射和内容缓存,后续模型可直接复用;进程退出时由操作系统统一回收映射。
377+ GELOGD("Released model reference for fused HostCPU kernel[%s]; keep JIT so in process cache.", register_name.c_str());
378+ return ge::GRAPH_SUCCESS;
379+}
380+ 
121std::function<uint32_t(void *)> AicpuResourceManager::GetRunCpuKernel() const {381std::function<uint32_t(void *)> AicpuResourceManager::GetRunCpuKernel() const {
122 return run_cpu_kernel_;382 return run_cpu_kernel_;
123}383}
@@ -196,6 +456,14 @@ ge::graphStatus EnsureCreateTfSession(KernelContext *context) {
196}456}
197REGISTER_KERNEL(EnsureCreateTfSession).RunFunc(EnsureCreateTfSession);457REGISTER_KERNEL(EnsureCreateTfSession).RunFunc(EnsureCreateTfSession);
198 458 
459+ge::graphStatus ReleaseFusedHostCpuSo(KernelContext *context) {
460+ GE_ASSERT_NOTNULL(context);
461+ GE_ASSERT_NOTNULL(context->GetInputValue<const char *>(0U));
462+ const std::string register_name(context->GetInputValue<const char *>(0U));
463+ return AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(register_name);
464+}
465+REGISTER_KERNEL(ReleaseFusedHostCpuSo).RunFunc(ReleaseFusedHostCpuSo);
466+ 
199ge::graphStatus CreateStepId(KernelContext *context) {467ge::graphStatus CreateStepId(KernelContext *context) {
200 auto step_id = context->GetOutputPointer<void *>(0U);468 auto step_id = context->GetOutputPointer<void *>(0U);
201 auto iteration = context->GetOutputPointer<int64_t>(1U);469 auto iteration = context->GetOutputPointer<int64_t>(1U);
@@ -11,8 +11,12 @@
11#ifndef AIR_CXX_RUNTIME_V2_KERNEL_LAUNCH_KERNEL_AICPU_AICPU_RESOURCE_MANAGER_H_11#ifndef AIR_CXX_RUNTIME_V2_KERNEL_LAUNCH_KERNEL_AICPU_AICPU_RESOURCE_MANAGER_H_
12#define AIR_CXX_RUNTIME_V2_KERNEL_LAUNCH_KERNEL_AICPU_AICPU_RESOURCE_MANAGER_H_12#define AIR_CXX_RUNTIME_V2_KERNEL_LAUNCH_KERNEL_AICPU_AICPU_RESOURCE_MANAGER_H_
13 13 
14+#include <cstddef>
15+#include <cstdint>
14#include <mutex>16#include <mutex>
15#include <deque>17#include <deque>
18+#include <map>
19+#include <vector>
16#include "ge/ge_api_error_codes.h"20#include "ge/ge_api_error_codes.h"
17#include "exe_graph/runtime/tensor_data.h"21#include "exe_graph/runtime/tensor_data.h"
18#include "graph/op_desc.h"22#include "graph/op_desc.h"
@@ -26,6 +30,15 @@
26namespace gert {30namespace gert {
27 31 
28using AicpuHostProcFunc = ge::graphStatus (*)(KernelContext *);32using AicpuHostProcFunc = ge::graphStatus (*)(KernelContext *);
33+using FusedHostCpuCreateFunc = void *(*)();
34+using FusedHostCpuDestroyFunc = void (*)(void *);
35+using FusedHostCpuRunFunc = uint32_t (*)(void *, const void *, uint32_t);
36+ 
37+struct FusedHostCpuKernelFunctions {
38+ FusedHostCpuCreateFunc create_func = nullptr;
39+ FusedHostCpuDestroyFunc destroy_func = nullptr;
40+ FusedHostCpuRunFunc run_func = nullptr;
41+};
29 42 
30class AicpuResourceManager {43class AicpuResourceManager {
31 public:44 public:
@@ -33,6 +46,9 @@ class AicpuResourceManager {
33 ~AicpuResourceManager();46 ~AicpuResourceManager();
34 47 
35 ge::graphStatus LoadConstantFoldingLib();48 ge::graphStatus LoadConstantFoldingLib();
49+ ge::graphStatus LoadFusedHostCpuSo(const std::string &register_name, const uint8_t *so_data, size_t so_size);
50+ ge::graphStatus ReleaseFusedHostCpuSo(const std::string &register_name);
51+ FusedHostCpuKernelFunctions GetFusedHostCpuKernelFunctions(const std::string &register_name);
36 std::function<uint32_t(void *)> GetRunCpuKernel() const;52 std::function<uint32_t(void *)> GetRunCpuKernel() const;
37 std::function<AicpuHostProcFunc(std::string)> GetAicpuHostFindFunc() const;53 std::function<AicpuHostProcFunc(std::string)> GetAicpuHostFindFunc() const;
38 54 
@@ -52,6 +68,12 @@ class AicpuResourceManager {
52 ge::graphStatus HasLoadedCustAicpuSo(const std::string &so_name, bool &loaded);68 ge::graphStatus HasLoadedCustAicpuSo(const std::string &so_name, bool &loaded);
53 69 
54 private:70 private:
71+ ge::graphStatus TryReuseFusedHostCpuSo(const std::string &register_name, const uint8_t *so_data, size_t so_size,
72+ uint64_t so_hash, bool &handled);
73+ ge::graphStatus LoadNewFusedHostCpuSo(const std::string &register_name, const uint8_t *so_data, size_t so_size,
74+ uint64_t so_hash);
75+ ge::graphStatus OpenFusedHostCpuSo(const std::string &register_name, const uint8_t *so_data, size_t so_size,
76+ void *&handle, int &fd, FusedHostCpuKernelFunctions &kernel_funcs);
55 ge::graphStatus CheckOrCreateHandle(const std::string &op_name, const rtStream_t stream,77 ge::graphStatus CheckOrCreateHandle(const std::string &op_name, const rtStream_t stream,
56 const GertTensorData *handle_data);78 const GertTensorData *handle_data);
57 AicpuResourceManager() = default;79 AicpuResourceManager() = default;
@@ -61,6 +83,18 @@ class AicpuResourceManager {
61 std::function<uint32_t(void *)> run_cpu_kernel_ = nullptr;83 std::function<uint32_t(void *)> run_cpu_kernel_ = nullptr;
62 std::function<AicpuHostProcFunc(std::string)> aicpu_host_find_func_ = nullptr;84 std::function<AicpuHostProcFunc(std::string)> aicpu_host_find_func_ = nullptr;
63 void *so_handle_ = nullptr;85 void *so_handle_ = nullptr;
86+ // CpuKernelRegister 没有注销接口。引用计数仅表示活跃模型数;引用归零后仍保留 so 映射,避免其中的
87+ // std::function creator 因 dlclose 变成悬空指针,并支持后续模型复用同一产物。
88+ std::mutex fused_so_mutex_;
89+ std::map<std::string, uint64_t> fused_register_hashes_;
90+ std::map<std::string, size_t> fused_register_ref_counts_;
91+ std::map<std::string, FusedHostCpuKernelFunctions> fused_kernel_funcs_;
92+ std::map<uint64_t, void *> fused_so_handles_;
93+ std::map<uint64_t, int> fused_so_fds_;
94+ std::map<uint64_t, size_t> fused_so_ref_counts_;
95+ std::map<uint64_t, std::vector<uint8_t>> fused_so_contents_;
96+ std::vector<void *> fused_quarantined_so_handles_;
97+ std::vector<int> fused_quarantined_so_fds_;
64 98 
65 std::map<uint64_t, std::deque<GertTensorData>> tensors_;99 std::map<uint64_t, std::deque<GertTensorData>> tensors_;
66 std::map<std::string, uint64_t> handles_;100 std::map<std::string, uint64_t> handles_;
@@ -0,0 +1,50 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef AIR_CXX_RUNTIME_V2_ENGINE_AICPU_KERNEL_FUSED_HOST_CPU_COMPUTE_H_
12+#define AIR_CXX_RUNTIME_V2_ENGINE_AICPU_KERNEL_FUSED_HOST_CPU_COMPUTE_H_
13+ 
14+#include "aicpu_resource_manager.h"
15+ 
16+namespace gert {
17+struct FusedHostCpuTensorMeta {
18+ size_t dim_num;
19+};
20+ 
21+enum FusedHostCpuBindingFlag : uint32_t { kFusedHostCpuShapeChanged = 1U, kFusedHostCpuDataChanged = 2U };
22+ 
23+// Private C ABI payload shared by RT2 and the generated JIT SO. Keep it standard-layout; compiler, runtime and
24+// generated SO must be deployed as one ABI-compatible set.
25+struct FusedHostCpuTensorBinding {
26+ const int64_t *dims;
27+ uint8_t *data;
28+ size_t dim_num;
29+ size_t data_size;
30+ uint32_t flags;
31+};
32+ 
33+struct FusedHostCpuComputeMeta {
34+ void *compute_state;
35+ size_t input_num;
36+ size_t output_num;
37+};
38+ 
39+struct FusedHostCpuDestroyMeta {
40+ void *compute_state;
41+};
42+ 
43+namespace kernel {
44+void *CreateFusedHostCpuComputeState(const char *register_name, void *kernel_state,
45+ FusedHostCpuDestroyFunc destroy_func, FusedHostCpuRunFunc run_func,
46+ const FusedHostCpuTensorMeta *tensor_metas, size_t io_num);
47+void DestroyFusedHostCpuComputeState(void *compute_state);
48+} // namespace kernel
49+} // namespace gert
50+#endif // AIR_CXX_RUNTIME_V2_ENGINE_AICPU_KERNEL_FUSED_HOST_CPU_COMPUTE_H_
@@ -26,6 +26,7 @@
26#include "graph/ge_context.h"26#include "graph/ge_context.h"
27#include "common/sgt_slice_type.h"27#include "common/sgt_slice_type.h"
28#include "ge/ge_api_types.h"28#include "ge/ge_api_types.h"
29+#include "framework/common/host_cpu_fusion_attr.h"
29 30 
30using namespace aicpu;31using namespace aicpu;
31using namespace ge;32using namespace ge;
@@ -752,6 +753,27 @@ TEST(AicpuGraphOptimizer, InsertAicpuNodeDefAttrToOp_Deterministic) {
752 ASSERT_EQ(str1, str2);753 ASSERT_EQ(str1, str2);
753}754}
754 755 
756+TEST(AicpuGraphOptimizer, BuildAicpuNodeDefUsesFusedRegisterAndIoNames) {
757+ OpDescPtr op_desc_ptr = make_shared<OpDesc>("fused", kFusedHostCpuOpType);
758+ GeTensorDesc tensor_desc(GeShape({2}), FORMAT_ND, DT_INT64);
759+ ASSERT_EQ(op_desc_ptr->AddInputDesc("input_0", tensor_desc), GRAPH_SUCCESS);
760+ ASSERT_EQ(op_desc_ptr->AddOutputDesc("output_0", tensor_desc), GRAPH_SUCCESS);
761+ ASSERT_TRUE(AttrUtils::SetStr(op_desc_ptr, kFusedHostCpuRegisterName, "FusedHostCpu_test"));
762+ aicpuops::NodeDef node_def;
763+ ASSERT_EQ(BuildAicpuNodeDef(op_desc_ptr, node_def), SUCCESS);
764+ EXPECT_EQ(node_def.op(), "FusedHostCpu_test");
765+ ASSERT_EQ(node_def.inputs_size(), 1);
766+ ASSERT_EQ(node_def.outputs_size(), 1);
767+ EXPECT_EQ(node_def.inputs(0).name(), "input_0");
768+ EXPECT_EQ(node_def.outputs(0).name(), "output_0");
769+}
770+ 
771+TEST(AicpuGraphOptimizer, BuildAicpuNodeDefRejectsMissingFusedRegisterName) {
772+ OpDescPtr op_desc_ptr = make_shared<OpDesc>("fused", kFusedHostCpuOpType);
773+ aicpuops::NodeDef node_def;
774+ EXPECT_EQ(BuildAicpuNodeDef(op_desc_ptr, node_def), ge::PARAM_INVALID);
775+}
776+ 
755TEST(AicpuGraphOptimizer, test_GENERATETRANSPOSE_001) {777TEST(AicpuGraphOptimizer, test_GENERATETRANSPOSE_001) {
756 map<string, GraphOptimizerPtr> graphOptimizers;778 map<string, GraphOptimizerPtr> graphOptimizers;
757 GetGraphOptimizerObjs(graphOptimizers);779 GetGraphOptimizerObjs(graphOptimizers);
@@ -113,6 +113,15 @@ TEST_F(AicpuConstFoldingTest, InitCpuConstantFoldingNew_NoEnvFail) {
113 ASSERT_EQ(ret, -1);113 ASSERT_EQ(ret, -1);
114}114}
115 115 
116+TEST_F(AicpuConstFoldingTest, IsCpuConstantFoldingFusedOpSupported) {
117+ SetEnvSafe("ASCEND_HOME_PATH", "/tmp", 1);
118+ ASSERT_EQ(InitCpuConstantFoldingNew([]() -> ge::HostCpuOp * { return new (std::nothrow) ge::HostCpuTestOp(); }), 0);
119+ EXPECT_EQ(IsCpuConstantFoldingFusedOpSupported(nullptr), 0);
120+ EXPECT_EQ(IsCpuConstantFoldingFusedOpSupported(""), 0);
121+ EXPECT_EQ(IsCpuConstantFoldingFusedOpSupported("unsupported"), 0);
122+ EXPECT_EQ(IsCpuConstantFoldingFusedOpSupported("testop"), 1);
123+}
124+ 
116TEST_F(AicpuConstFoldingTest, InitCpuConstantFoldingNew_LoadSoTraversal) {125TEST_F(AicpuConstFoldingTest, InitCpuConstantFoldingNew_LoadSoTraversal) {
117 std::string tmp_dir = "/tmp/test_folding_ut_" + std::to_string(getpid());126 std::string tmp_dir = "/tmp/test_folding_ut_" + std::to_string(getpid());
118 std::string host_cpu_dir = tmp_dir + "/opp/built-in/op_impl/host_cpu/";127 std::string host_cpu_dir = tmp_dir + "/opp/built-in/op_impl/host_cpu/";
@@ -18,7 +18,9 @@
18 18 
19#include "ge/ge_api_types.h"19#include "ge/ge_api_types.h"
20#include "graph/debug/ge_attr_define.h"20#include "graph/debug/ge_attr_define.h"
21+#include "framework/common/host_cpu_fusion_attr.h"
21#include "graph/utils/op_desc_utils_ex.h"22#include "graph/utils/op_desc_utils_ex.h"
23+#include "proto/aicpu/cpu_node_def.pb.h"
22 24 
23using namespace aicpu;25using namespace aicpu;
24using namespace ge;26using namespace ge;
@@ -148,6 +150,41 @@ TEST(HostCpuOpsKernelBuilder, GenerateTask_SUCCESS) {
148 printf("end===================\n");150 printf("end===================\n");
149}151}
150 152 
153+TEST(HostCpuOpsKernelBuilder, CalcOpRunningParamBuildsFusedNodeDef) {
154+ HostCpuOpsKernelBuilder hostCpuKernelBuilder;
155+ map<string, string> options;
156+ options[SOC_VERSION] = "Ascend910";
157+ ASSERT_EQ(hostCpuKernelBuilder.Initialize(options), SUCCESS);
158+ 
159+ auto graph = make_shared<ComputeGraph>("fused_host_cpu_graph");
160+ auto op_desc = make_shared<OpDesc>("fused", kFusedHostCpuOpType);
161+ GeTensorDesc tensor_desc(GeShape({2}), FORMAT_ND, DT_INT64);
162+ ASSERT_EQ(op_desc->AddInputDesc("input_0", tensor_desc), GRAPH_SUCCESS);
163+ ASSERT_EQ(op_desc->AddOutputDesc("output_0", tensor_desc), GRAPH_SUCCESS);
164+ ASSERT_TRUE(AttrUtils::SetStr(op_desc, kFusedHostCpuRegisterName, "FusedHostCpu_builder_test"));
165+ ASSERT_TRUE(AttrUtils::SetStr(op_desc, "opKernelLib", "HOSTCPUKernel"));
166+ ASSERT_TRUE(AttrUtils::SetInt(op_desc, ge::ATTR_NAME_UNKNOWN_SHAPE_TYPE, DEPEND_IN_SHAPE));
167+ auto node = graph->AddNode(op_desc);
168+ ASSERT_NE(node, nullptr);
169+ 
170+ ASSERT_EQ(hostCpuKernelBuilder.CalcOpRunningParam(*node), SUCCESS);
171+ Buffer node_def_buffer;
172+ ASSERT_TRUE(AttrUtils::GetZeroCopyBytes(op_desc, kCustomizedOpDef, node_def_buffer));
173+ aicpuops::NodeDef node_def;
174+ ASSERT_TRUE(node_def.ParseFromArray(node_def_buffer.GetData(), static_cast<int32_t>(node_def_buffer.GetSize())));
175+ EXPECT_EQ(node_def.op(), "FusedHostCpu_builder_test");
176+ ASSERT_EQ(node_def.inputs_size(), 1);
177+ ASSERT_EQ(node_def.outputs_size(), 1);
178+ EXPECT_EQ(node_def.inputs(0).name(), "input_0");
179+ EXPECT_EQ(node_def.outputs(0).name(), "output_0");
180+ 
181+ RunContext context = CreateContext();
182+ vector<domi::TaskDef> tasks;
183+ ASSERT_EQ(hostCpuKernelBuilder.GenerateTask(*node, context, tasks), SUCCESS);
184+ ASSERT_EQ(tasks.size(), 1U);
185+ EXPECT_EQ(tasks[0].kernel().context().kernel_type(), 8U);
186+ DestroyContext(context);
187+}
151TEST(HostCpuOpsKernelBuilder, CalcOpRunningParam_Original_Unknown_FAIL) {188TEST(HostCpuOpsKernelBuilder, CalcOpRunningParam_Original_Unknown_FAIL) {
152 HostCpuOpsKernelBuilder hostCpuKernelBuilder;189 HostCpuOpsKernelBuilder hostCpuKernelBuilder;
153 map<string, string> options;190 map<string, string> options;
@@ -62,6 +62,7 @@ target_include_directories(autofuse_test PRIVATE
62 ${ASCEND_INSTALL_PATH}/pkg_inc/autofuse/graph_metadef62 ${ASCEND_INSTALL_PATH}/pkg_inc/autofuse/graph_metadef
63 ${ASCEND_INSTALL_PATH}/pkg_inc/autofuse/graph_metadef/graph63 ${ASCEND_INSTALL_PATH}/pkg_inc/autofuse/graph_metadef/graph
64 ${ASCEND_INSTALL_PATH}/pkg_inc/autofuse/ascir64 ${ASCEND_INSTALL_PATH}/pkg_inc/autofuse/ascir
65+ ${ASCEND_INSTALL_PATH}/pkg_inc
65)66)
66 67 
67target_link_libraries(autofuse_test PRIVATE68target_link_libraries(autofuse_test PRIVATE
@@ -8,287 +8,754 @@
8 * See LICENSE in the root of the software repository for the full text of the License.8 * See LICENSE in the root of the software repository for the full text of the License.
9 */9 */
10 10 
11-#include <gtest/gtest.h>
12- 
13#include <cstdlib>11#include <cstdlib>
12+#include <limits>
13+#include <map>
14#include <string>14#include <string>
15+#include <utility>
15#include <vector>16#include <vector>
16 17 
18+#include <gtest/gtest.h>
19+ 
20+#include "aicpu/cpu_kernels/cpu_kernel_register.h"
21+#include "common/env_path.h"
17#include "framework/common/host_cpu_fusion_attr.h"22#include "framework/common/host_cpu_fusion_attr.h"
18-#include "graph/op_so_bin.h"23+#include "graph/ge_local_context.h"
19#include "graph/partition/optimizer/host_cpu_fusion_codegen.h"24#include "graph/partition/optimizer/host_cpu_fusion_codegen.h"
20#include "graph/partition/optimizer/host_cpu_fusion_pass.h"25#include "graph/partition/optimizer/host_cpu_fusion_pass.h"
21#include "graph/utils/attr_utils.h"26#include "graph/utils/attr_utils.h"
22#include "graph/utils/graph_utils.h"27#include "graph/utils/graph_utils.h"
28+#include "runtime/v2/engine/aicpu/kernel/aicpu_resource_manager.h"
29+#include "runtime/v2/engine/aicpu/kernel/fused_host_cpu_compute.h"
30+ 
31+// The open-source ST links a placeholder constant-folding library, so provide the CpuKernel registration symbols
32+// that the generated JIT so normally resolves from the real libconstant_folding_ops.so on a product installation.
33+namespace aicpu {
34+namespace {
35+std::map<std::string, KERNEL_CREATOR_FUN> &GetTestCpuKernelCreators() {
36+ static std::map<std::string, KERNEL_CREATOR_FUN> creators;
37+ return creators;
38+}
39+} // namespace
40+ 
41+bool RegistCpuKernel(const std::string &type, const KERNEL_CREATOR_FUN &fun) {
42+ return GetTestCpuKernelCreators().emplace(type, fun).second;
43+}
44+ 
45+CpuKernelRegister &CpuKernelRegister::Instance() {
46+ static CpuKernelRegister instance;
47+ return instance;
48+}
49+ 
50+std::shared_ptr<CpuKernel> CpuKernelRegister::GetCpuKernel(const std::string &op_type) {
51+ const auto iter = GetTestCpuKernelCreators().find(op_type);
52+ return (iter == GetTestCpuKernelCreators().cend()) ? nullptr : iter->second();
53+}
54+ 
55+std::string CpuKernelContext::GetOpType() const {
56+ return {};
57+}
58+ 
59+Tensor *CpuKernelContext::Input(const uint32_t) const {
60+ return nullptr;
61+}
62+ 
63+Tensor *CpuKernelContext::Output(const uint32_t) const {
64+ return nullptr;
65+}
66+ 
67+uint32_t CpuKernelContext::GetInputsSize() const {
68+ return 0U;
69+}
70+ 
71+uint32_t CpuKernelContext::GetOutputsSize() const {
72+ return 0U;
73+}
74+ 
75+std::shared_ptr<TensorShape> Tensor::GetTensorShape() const {
76+ return nullptr;
77+}
78+ 
79+DataType Tensor::GetDataType() const {
80+ return DT_UNDEFINED;
81+}
82+ 
83+void *Tensor::GetData() const {
84+ return nullptr;
85+}
86+ 
87+uint64_t Tensor::GetDataSize() const {
88+ return 0U;
89+}
90+ 
91+Format TensorShape::GetFormat() const {
92+ return FORMAT_ND;
93+}
94+ 
95+std::vector<int64_t> TensorShape::GetDimSizes() const {
96+ return {};
97+}
98+ 
99+int64_t TensorShape::GetDimSize(int32_t) const {
100+ return 0;
101+}
102+ 
103+int32_t TensorShape::GetDims() const {
104+ return 0;
105+}
106+} // namespace aicpu
107+ 
108+// The loader test does not load the product constant-folding library. Export the private chain-plan symbols so the
109+// generated JIT so can resolve them when validating registration; this test does not execute the plan.
110+size_t g_create_fused_chain_plan_count = 0U;
111+ 
112+extern "C" __attribute__((visibility("default"))) void *CreateCpuConstantFoldingFusedChainPlan(const void *, size_t,
113+ size_t, size_t) {
114+ ++g_create_fused_chain_plan_count;
115+ return reinterpret_cast<void *>(1U);
116+}
117+ 
118+extern "C" __attribute__((visibility("default"))) int32_t RunCpuConstantFoldingFusedChainPlan(void *, uint32_t) {
119+ return 0;
120+}
121+ 
122+extern "C" __attribute__((visibility("default"))) int32_t RunCpuConstantFoldingFusedChainPlanBindings(void *,
123+ const void *,
124+ uint32_t) {
125+ return 0;
126+}
127+ 
128+extern "C" __attribute__((visibility("default"))) void DestroyCpuConstantFoldingFusedChainPlan(void *) {}
23 129 
24namespace ge {130namespace ge {
25namespace {131namespace {
132+#if defined(__linux__)
133+constexpr char kHostFusionStA[] = "HostFusionStA";
134+constexpr char kHostFusionStB[] = "HostFusionStB";
135+constexpr char kHostFusionStMissing[] = "HostFusionStMissing";
136+constexpr char kHostFusionStAfterMissing[] = "HostFusionStAfterMissing";
137+constexpr char kHostCpuEngineName[] = "DNN_VM_HOST_CPU";
138+constexpr char kHostCpuKernelLibName[] = "DNN_VM_HOST_CPU_OP_STORE";
26 139 
27-std::string GetToolkitHome() {140+class ScopedEnvVar {
28- const char *home = std::getenv("ASCEND_HOME_PATH");
29- if ((home != nullptr) && (home[0] != '\0')) {
30- return home;
31- }
32- const char *install_path = std::getenv("ASCEND_INSTALL_PATH");
33- if ((install_path != nullptr) && (install_path[0] != '\0')) {
34- return install_path;
35- }
36- return "/usr/local/Ascend/cann-9.2.0/x86_64-linux";
37-}
38- 
39-std::string GetToolkitOpp(const std::string &home) {
40- constexpr char kX86Suffix[] = "/x86_64-linux";
41- if (home.size() > (sizeof(kX86Suffix) - 1U) &&
42- home.compare(home.size() - (sizeof(kX86Suffix) - 1U), sizeof(kX86Suffix) - 1U, kX86Suffix) == 0) {
43- return home.substr(0U, home.size() - (sizeof(kX86Suffix) - 1U)) + "/opp";
44- }
45- return home + "/opp";
46-}
47- 
48-NodePtr MakeNode(const ComputeGraphPtr &graph, const std::string &name, const std::string &type, const size_t inputs,
49- const size_t outputs) {
50- const GeTensorDesc desc(GeShape({2, 3}), FORMAT_ND, DT_INT64);
51- auto op_desc = std::make_shared<OpDesc>(name, type);
52- for (size_t i = 0U; i < inputs; ++i) {
53- EXPECT_EQ(op_desc->AddInputDesc("input" + std::to_string(i), desc), GRAPH_SUCCESS);
54- }
55- for (size_t i = 0U; i < outputs; ++i) {
56- EXPECT_EQ(op_desc->AddOutputDesc("output" + std::to_string(i), desc), GRAPH_SUCCESS);
57- }
58- return graph->AddNode(op_desc);
59-}
60- 
61-void Connect(const NodePtr &source, const size_t source_index, const NodePtr &target, const size_t target_index) {
62- ASSERT_EQ(GraphUtils::AddEdge(source->GetOutDataAnchor(static_cast<int32_t>(source_index)),
63- target->GetInDataAnchor(static_cast<int32_t>(target_index))),
64- GRAPH_SUCCESS);
65-}
66- 
67-HostCpuFusionRegion MakeRegion(const ComputeGraphPtr &graph) {
68- const auto data = MakeNode(graph, "data", "Data", 0U, 1U);
69- const auto first = MakeNode(graph, "first", "HostST", 1U, 2U);
70- const auto second = MakeNode(graph, "second", "HostST", 1U, 1U);
71- const auto side = MakeNode(graph, "side", "DeviceOp", 1U, 1U);
72- const auto output = MakeNode(graph, "output", "NetOutput", 2U, 0U);
73- Connect(data, 0U, first, 0U);
74- Connect(first, 0U, second, 0U);
75- Connect(first, 1U, side, 0U);
76- Connect(second, 0U, output, 0U);
77- Connect(side, 0U, output, 1U);
78- 
79- HostCpuFusionRegion region;
80- region.chain_id = "st_codegen_chain";
81- region.nodes = {first, second};
82- region.external_inputs = {data->GetOutDataAnchor(0)};
83- region.external_outputs = {{first->GetOutDataAnchor(1), {side->GetInDataAnchor(0)}},
84- {second->GetOutDataAnchor(0), {output->GetInDataAnchor(0)}}};
85- return region;
86-}
87- 
88-void MarkHostCpuCandidateForPassSt(const NodePtr &node) {
89- node->GetOpDesc()->SetOpEngineName("DNN_VM_HOST_CPU");
90- node->GetOpDesc()->SetOpKernelLibName("DNN_VM_HOST_CPU_OP_STORE");
91- ASSERT_TRUE(AttrUtils::SetBool(node->GetOpDesc(), "SmallShapeHostcpu", true));
92-}
93- 
94-ComputeGraphPtr BuildPassGraph() {
95- auto graph = std::make_shared<ComputeGraph>("host_cpu_fusion_pass_st");
96- const auto data = MakeNode(graph, "pass_data", "Data", 0U, 1U);
97- const auto first = MakeNode(graph, "pass_first", "HostPassA", 1U, 1U);
98- const auto second = MakeNode(graph, "pass_second", "HostPassB", 1U, 1U);
99- const auto output = MakeNode(graph, "pass_output", "NetOutput", 1U, 0U);
100- Connect(data, 0U, first, 0U);
101- Connect(first, 0U, second, 0U);
102- Connect(second, 0U, output, 0U);
103- MarkHostCpuCandidateForPassSt(first);
104- MarkHostCpuCandidateForPassSt(second);
105- return graph;
106-}
107- 
108-class MinimalCustomOpCompilerForPassSt final : public HostCpuFusionCompiler {
109 public:141 public:
110- Status Compile(const std::string &, std::vector<uint8_t> &so_data) const override {142+ ScopedEnvVar(std::string name, const std::string &value) : name_(std::move(name)) {
111- return HostCpuFusionCompiler::Compile(143+ const char *old_value = std::getenv(name_.c_str());
112- "extern \"C\" __attribute__((visibility(\"default\"))) unsigned int "144+ if (old_value != nullptr) {
113- "GetRegisteredCustomOpCreatorAbiVersion() { return 2U; }\n"145+ had_old_value_ = true;
114- "extern \"C\" __attribute__((visibility(\"default\"))) unsigned long "146+ old_value_ = old_value;
115- "GetRegisteredCustomOpCreatorNum() { return 0U; }\n"
116- "extern \"C\" __attribute__((visibility(\"default\"))) int "
117- "GetRegisteredCustomOpCreators(void *, unsigned long, unsigned long) { return 0; }\n",
118- so_data);
119- }
120-};
121- 
122-bool SupportAllForPassSt(const std::string &) {
123- return true;
124-}
125- 
126-class EnvGuard final {
127- public:
128- EnvGuard(const char *name, const char *value) : name_(name), old_(), had_old_(false) {
129- const char *old = std::getenv(name);
130- if (old != nullptr) {
131- old_ = old;
132- had_old_ = true;
133 }147 }
134- EXPECT_EQ(setenv(name, value, 1), 0);148+ valid_ = (setenv(name_.c_str(), value.c_str(), 1) == 0);
135 }149 }
136- ~EnvGuard() {150+ 
137- if (had_old_) {151+ ~ScopedEnvVar() {
138- (void)setenv(name_.c_str(), old_.c_str(), 1);152+ if (had_old_value_) {
153+ (void)setenv(name_.c_str(), old_value_.c_str(), 1);
139 } else {154 } else {
140 (void)unsetenv(name_.c_str());155 (void)unsetenv(name_.c_str());
141 }156 }
142 }157 }
143 158 
159+ bool IsValid() const {
160+ return valid_;
161+ }
162+ 
144 private:163 private:
145 std::string name_;164 std::string name_;
146- std::string old_;165+ std::string old_value_;
147- bool had_old_;166+ bool had_old_value_ = false;
167+ bool valid_ = false;
148};168};
149 169 
170+class StFakeCompiler final : public HostCpuFusionCompiler {
171+ public:
172+ Status Compile(const std::string &, std::vector<uint8_t> &so_data) const override {
173+ so_data.assign(20U, 0U);
174+ so_data[0] = 0x7FU;
175+ so_data[1] = 'E';
176+ so_data[2] = 'L';
177+ so_data[3] = 'F';
178+ so_data[4] = 2U;
179+ so_data[5] = 1U;
180+ so_data[6] = 1U;
181+ so_data[16] = 3U;
182+ return SUCCESS;
183+ }
184+};
185+ 
186+NodePtr AddNode(const ComputeGraphPtr &graph, const std::string &name, const std::string &type,
187+ const size_t input_count, const size_t output_count) {
188+ const GeTensorDesc desc(GeShape({2}), FORMAT_ND, DT_INT64);
189+ auto op_desc = std::make_shared<OpDesc>(name, type);
190+ for (size_t i = 0U; i < input_count; ++i) {
191+ if (op_desc->AddInputDesc("x", desc) != GRAPH_SUCCESS) {
192+ return nullptr;
193+ }
194+ }
195+ for (size_t i = 0U; i < output_count; ++i) {
196+ if (op_desc->AddOutputDesc("y", desc) != GRAPH_SUCCESS) {
197+ return nullptr;
198+ }
199+ }
200+ return graph->AddNode(op_desc);
201+}
202+ 
203+void MarkFusionCandidate(const NodePtr &node) {
204+ node->GetOpDesc()->SetOpEngineName(kHostCpuEngineName);
205+ node->GetOpDesc()->SetOpKernelLibName(kHostCpuKernelLibName);
206+ ASSERT_TRUE(AttrUtils::SetBool(node->GetOpDesc(), "SmallShapeHostcpu", true));
207+}
208+ 
209+ComputeGraphPtr BuildCoverageChainGraph() {
210+ auto graph = std::make_shared<ComputeGraph>("host_cpu_coverage_chain");
211+ auto data = AddNode(graph, "data", "Data", 0U, 1U);
212+ auto first = AddNode(graph, "first", kHostFusionStA, 1U, 1U);
213+ auto second = AddNode(graph, "second", kHostFusionStB, 1U, 1U);
214+ auto output = AddNode(graph, "output", "NetOutput", 1U, 0U);
215+ if ((data == nullptr) || (first == nullptr) || (second == nullptr) || (output == nullptr)) {
216+ return nullptr;
217+ }
218+ if ((GraphUtils::AddEdge(data->GetOutDataAnchor(0), first->GetInDataAnchor(0)) != GRAPH_SUCCESS) ||
219+ (GraphUtils::AddEdge(first->GetOutDataAnchor(0), second->GetInDataAnchor(0)) != GRAPH_SUCCESS) ||
220+ (GraphUtils::AddEdge(second->GetOutDataAnchor(0), output->GetInDataAnchor(0)) != GRAPH_SUCCESS)) {
221+ return nullptr;
222+ }
223+ MarkFusionCandidate(first);
224+ MarkFusionCandidate(second);
225+ return graph;
226+}
227+ 
228+HostCpuFusionRegion BuildCoverageRegion(const ComputeGraphPtr &graph) {
229+ HostCpuFusionRegion region;
230+ region.chain_id = "st_coverage_region";
231+ const auto data = graph->FindNode("data");
232+ const auto first = graph->FindNode("first");
233+ const auto second = graph->FindNode("second");
234+ const auto output = graph->FindNode("output");
235+ region.nodes = {first, second};
236+ region.external_inputs = {data->GetOutDataAnchor(0)};
237+ region.external_outputs = {{second->GetOutDataAnchor(0), {output->GetInDataAnchor(0)}}};
238+ return region;
239+}
240+#endif
150} // namespace241} // namespace
151 242 
152-// 用例描述:验证 HostCPU 融合区域能够生成当前 CustomOp 编排源码编译 ELF SO243+// 用例描述:验证 HostCPU 编排源码可真实编译,并注册到共享 CpuKernelRegister
153-// 预置条件:使本机 Toolkit 头文件和 g++,构造带内部中间张量及两个外部输出的融合区域244+// 预置条件:Linux 环境已安装可用 Toolkit 头文件和目标架构 g++,测试提供 CpuKernelRegister 符号
154-// 测试步骤:生成源码,检查输入/输出绑定与 CustomOp ABI执行 JIT 编译245+// 测试步骤:生成两节点融合源码并编译同一 so 加载两次创建状态后首次绑定执行,再释放引用并复用
155-// 预期结果:源码生成成功;工具包提供 HostCPU ABI 时编译产物为有效 ELF 共享对象否则返回 UNSUPPORTED246+// 预期结果:模型加载期不创建整链 plan首次执行才创建一次;引用归零后 so 仍可重新加载
156-TEST(HostCpuFusionCodegenST, GeneratesAndCompilesCurrentCustomOp) {247+TEST(HostCpuFusionCodegenST, CompilesLoadsAndKeepsCpuKernelRegistered) {
157-#if defined(__linux__)248+#if !defined(__linux__)
158- const auto toolkit_home = GetToolkitHome();
159- EnvGuard home("ASCEND_HOME_PATH", toolkit_home.c_str());
160- EnvGuard opp("ASCEND_OPP_PATH", "");
161- auto graph = std::make_shared<ComputeGraph>("host_cpu_codegen_st");
162- const auto region = MakeRegion(graph);
163- HostCpuFusionCodegenResult result;
164- ASSERT_EQ(HostCpuFusionCodegen().Generate(region, result), SUCCESS);
165- EXPECT_EQ(result.register_name, "FusedHostCpu_st_codegen_chain");
166- EXPECT_NE(result.source.find("internal_tensor_0"), std::string::npos);
167- EXPECT_NE(result.source.find("std::array<const gert::Tensor *, 1U>"), std::string::npos);
168- EXPECT_NE(result.source.find("std::array<gert::Tensor *, 2U>"), std::string::npos);
169- EXPECT_NE(result.source.find("REG_OP_BACKEND"), std::string::npos);
170- EXPECT_NE(result.source.find("GetRegisteredCustomOpCreatorAbiVersion"), std::string::npos);
171- 
172- std::vector<uint8_t> so_data;
173- const auto compile_status = HostCpuFusionCompiler().Compile(result.source, so_data);
174- if (compile_status == SUCCESS) {
175- ASSERT_GT(so_data.size(), 20U);
176- EXPECT_EQ(so_data[0], 0x7FU);
177- EXPECT_EQ(so_data[1], 'E');
178- EXPECT_EQ(so_data[2], 'L');
179- EXPECT_EQ(so_data[3], 'F');
180- } else {
181- // Older Toolkit packages may not expose the HostCpuExecuteOp ABI yet.
182- EXPECT_EQ(compile_status, UNSUPPORTED);
183- EXPECT_TRUE(so_data.empty());
184- }
185-#else
186 GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";249 GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
250+#else
251+ const std::string opp_path = EnvPath().GetAscendInstallPath() + "/opp";
252+ const ScopedEnvVar opp_env("ASCEND_OPP_PATH", opp_path);
253+ const ScopedEnvVar home_env("ASCEND_HOME_PATH", EnvPath().GetAirBasePath());
254+ ASSERT_TRUE(opp_env.IsValid());
255+ ASSERT_TRUE(home_env.IsValid());
256+ 
257+ auto graph = std::make_shared<ComputeGraph>("host_cpu_fusion_st");
258+ auto data = AddNode(graph, "data", "Data", 0U, 1U);
259+ auto first = AddNode(graph, "first", kHostFusionStA, 1U, 1U);
260+ auto second = AddNode(graph, "second", kHostFusionStB, 1U, 1U);
261+ auto output = AddNode(graph, "output", "NetOutput", 1U, 0U);
262+ ASSERT_NE(data, nullptr);
263+ ASSERT_NE(first, nullptr);
264+ ASSERT_NE(second, nullptr);
265+ ASSERT_NE(output, nullptr);
266+ ASSERT_EQ(GraphUtils::AddEdge(data->GetOutDataAnchor(0), first->GetInDataAnchor(0)), GRAPH_SUCCESS);
267+ ASSERT_EQ(GraphUtils::AddEdge(first->GetOutDataAnchor(0), second->GetInDataAnchor(0)), GRAPH_SUCCESS);
268+ ASSERT_EQ(GraphUtils::AddEdge(second->GetOutDataAnchor(0), output->GetInDataAnchor(0)), GRAPH_SUCCESS);
269+ 
270+ HostCpuFusionRegion region;
271+ region.chain_id = "st_actual_compile";
272+ region.nodes = {first, second};
273+ region.external_inputs = {data->GetOutDataAnchor(0)};
274+ region.external_outputs = {{second->GetOutDataAnchor(0), {output->GetInDataAnchor(0)}}};
275+ HostCpuFusionCodegenResult result;
276+ HostCpuFusionCodegen codegen;
277+ ASSERT_EQ(codegen.Generate(region, result), SUCCESS);
278+ HostCpuFusionCompiler compiler;
279+ ASSERT_EQ(compiler.Compile(result.source, result.so_data), SUCCESS);
280+ ASSERT_EQ(gert::AicpuResourceManager::GetInstance().LoadFusedHostCpuSo(result.register_name, result.so_data.data(),
281+ result.so_data.size()),
282+ GRAPH_SUCCESS);
283+ ASSERT_EQ(gert::AicpuResourceManager::GetInstance().LoadFusedHostCpuSo(result.register_name, result.so_data.data(),
284+ result.so_data.size()),
285+ GRAPH_SUCCESS);
286+ 
287+ EXPECT_NE(aicpu::CpuKernelRegister::Instance().GetCpuKernel(result.register_name), nullptr);
288+ const auto kernel_funcs =
289+ gert::AicpuResourceManager::GetInstance().GetFusedHostCpuKernelFunctions(result.register_name);
290+ ASSERT_NE(kernel_funcs.create_func, nullptr);
291+ ASSERT_NE(kernel_funcs.destroy_func, nullptr);
292+ ASSERT_NE(kernel_funcs.run_func, nullptr);
293+ g_create_fused_chain_plan_count = 0U;
294+ void *kernel_state = kernel_funcs.create_func();
295+ ASSERT_NE(kernel_state, nullptr);
296+ EXPECT_EQ(g_create_fused_chain_plan_count, 0U);
297+ int64_t dims[] = {2};
298+ int64_t input_data[] = {1, 2};
299+ int64_t output_data[] = {0, 0};
300+ gert::FusedHostCpuTensorBinding bindings[] = {
301+ {dims, reinterpret_cast<uint8_t *>(input_data), 1U, sizeof(input_data),
302+ gert::kFusedHostCpuShapeChanged | gert::kFusedHostCpuDataChanged},
303+ {dims, reinterpret_cast<uint8_t *>(output_data), 1U, sizeof(output_data),
304+ gert::kFusedHostCpuShapeChanged | gert::kFusedHostCpuDataChanged}};
305+ EXPECT_EQ(
306+ kernel_funcs.run_func(kernel_state, bindings, gert::kFusedHostCpuShapeChanged | gert::kFusedHostCpuDataChanged),
307+ 0U);
308+ EXPECT_EQ(g_create_fused_chain_plan_count, 1U);
309+ // 绑定未变化时应复用已创建的 plan,不重复构建内部执行计划。
310+ EXPECT_EQ(kernel_funcs.run_func(kernel_state, bindings, 0U), 0U);
311+ EXPECT_EQ(g_create_fused_chain_plan_count, 1U);
312+ kernel_funcs.destroy_func(kernel_state);
313+ ASSERT_EQ(gert::AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(result.register_name), GRAPH_SUCCESS);
314+ EXPECT_NE(aicpu::CpuKernelRegister::Instance().GetCpuKernel(result.register_name), nullptr);
315+ ASSERT_EQ(gert::AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(result.register_name), GRAPH_SUCCESS);
316+ EXPECT_NE(aicpu::CpuKernelRegister::Instance().GetCpuKernel(result.register_name), nullptr);
317+ EXPECT_EQ(gert::AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(result.register_name), ge::PARAM_INVALID);
318+ ASSERT_EQ(gert::AicpuResourceManager::GetInstance().LoadFusedHostCpuSo(result.register_name, result.so_data.data(),
319+ result.so_data.size()),
320+ GRAPH_SUCCESS);
321+ ASSERT_EQ(gert::AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(result.register_name), GRAPH_SUCCESS);
187#endif322#endif
188}323}
189 324 
190-TEST(HostCpuFusionCodegenST, ExercisesCodegenRejectionAndNameBoundaries) {325+// 用例描述:验证同一进程连续加载两个不同的 HostCPU 融合 SO。
191- auto graph = std::make_shared<ComputeGraph>("host_cpu_codegen_rejection");326+// 预置条件:Linux 环境已安装可用 Toolkit 头文件和目标架构 g++,测试提供 CpuKernelRegister 符号。
192- const auto valid = MakeRegion(graph);327+// 测试步骤:为同一融合区域生成两个不同注册名的 SO,依次加载并查询各自的 private C ABI。
328+// 预期结果:两个 SO 均独立完成静态注册,且分别返回有效的 private C ABI。
329+TEST(HostCpuFusionCodegenST, LoadsDifferentFusedSharedObjectsIndependently) {
330+#if !defined(__linux__)
331+ GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
332+#else
333+ const std::string opp_path = EnvPath().GetAscendInstallPath() + "/opp";
334+ const ScopedEnvVar opp_env("ASCEND_OPP_PATH", opp_path);
335+ const ScopedEnvVar home_env("ASCEND_HOME_PATH", EnvPath().GetAirBasePath());
336+ ASSERT_TRUE(opp_env.IsValid());
337+ ASSERT_TRUE(home_env.IsValid());
338+ 
339+ auto graph = std::make_shared<ComputeGraph>("host_cpu_fusion_multi_so_st");
340+ auto data = AddNode(graph, "data", "Data", 0U, 1U);
341+ auto first = AddNode(graph, "first", kHostFusionStA, 1U, 1U);
342+ auto second = AddNode(graph, "second", kHostFusionStB, 1U, 1U);
343+ auto output = AddNode(graph, "output", "NetOutput", 1U, 0U);
344+ ASSERT_NE(data, nullptr);
345+ ASSERT_NE(first, nullptr);
346+ ASSERT_NE(second, nullptr);
347+ ASSERT_NE(output, nullptr);
348+ ASSERT_EQ(GraphUtils::AddEdge(data->GetOutDataAnchor(0), first->GetInDataAnchor(0)), GRAPH_SUCCESS);
349+ ASSERT_EQ(GraphUtils::AddEdge(first->GetOutDataAnchor(0), second->GetInDataAnchor(0)), GRAPH_SUCCESS);
350+ ASSERT_EQ(GraphUtils::AddEdge(second->GetOutDataAnchor(0), output->GetInDataAnchor(0)), GRAPH_SUCCESS);
351+ 
352+ HostCpuFusionRegion region;
353+ region.nodes = {first, second};
354+ region.external_inputs = {data->GetOutDataAnchor(0)};
355+ region.external_outputs = {{second->GetOutDataAnchor(0), {output->GetInDataAnchor(0)}}};
356+ HostCpuFusionCodegen codegen;
357+ HostCpuFusionCompiler compiler;
358+ HostCpuFusionCodegenResult first_result;
359+ region.chain_id = "st_multi_so_first";
360+ ASSERT_EQ(codegen.Generate(region, first_result), SUCCESS);
361+ ASSERT_EQ(compiler.Compile(first_result.source, first_result.so_data), SUCCESS);
362+ HostCpuFusionCodegenResult second_result;
363+ region.chain_id = "st_multi_so_second";
364+ ASSERT_EQ(codegen.Generate(region, second_result), SUCCESS);
365+ ASSERT_EQ(compiler.Compile(second_result.source, second_result.so_data), SUCCESS);
366+ ASSERT_NE(first_result.so_data, second_result.so_data);
367+ 
368+ ASSERT_EQ(gert::AicpuResourceManager::GetInstance().LoadFusedHostCpuSo(
369+ first_result.register_name, first_result.so_data.data(), first_result.so_data.size()),
370+ GRAPH_SUCCESS);
371+ ASSERT_EQ(gert::AicpuResourceManager::GetInstance().LoadFusedHostCpuSo(
372+ second_result.register_name, second_result.so_data.data(), second_result.so_data.size()),
373+ GRAPH_SUCCESS);
374+ const gert::FusedHostCpuKernelFunctions first_funcs =
375+ gert::AicpuResourceManager::GetInstance().GetFusedHostCpuKernelFunctions(first_result.register_name);
376+ const gert::FusedHostCpuKernelFunctions second_funcs =
377+ gert::AicpuResourceManager::GetInstance().GetFusedHostCpuKernelFunctions(second_result.register_name);
378+ EXPECT_NE(first_funcs.create_func, nullptr);
379+ EXPECT_NE(first_funcs.destroy_func, nullptr);
380+ EXPECT_NE(first_funcs.run_func, nullptr);
381+ EXPECT_NE(second_funcs.create_func, nullptr);
382+ EXPECT_NE(second_funcs.destroy_func, nullptr);
383+ EXPECT_NE(second_funcs.run_func, nullptr);
384+ EXPECT_NE(aicpu::CpuKernelRegister::Instance().GetCpuKernel(first_result.register_name), nullptr);
385+ EXPECT_NE(aicpu::CpuKernelRegister::Instance().GetCpuKernel(second_result.register_name), nullptr);
386+ EXPECT_EQ(gert::AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(first_result.register_name), GRAPH_SUCCESS);
387+ EXPECT_EQ(gert::AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(second_result.register_name),
388+ GRAPH_SUCCESS);
389+#endif
390+}
391+ 
392+// 用例描述:验证融合外层与内部原算子都不再依赖 HostCPU registry,稳态执行复用 CpuKernel plan。
393+// 预置条件:Linux 环境已安装可用 Toolkit 头文件和目标架构 g++。
394+// 测试步骤:生成三节点融合源码并编译,检查外层注册及内部 plan 创建、复用调用。
395+// 预期结果:外层使用 REGISTER_CPU_KERNEL;内部使用数组和线程级 plan,不包含 map 或 HostCpuOp API。
396+TEST(HostCpuFusionCodegenST, UsesCachedCpuKernelPlanForInternalNodes) {
397+#if !defined(__linux__)
398+ GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
399+#else
400+ const std::string opp_path = EnvPath().GetAscendInstallPath() + "/opp";
401+ const ScopedEnvVar opp_env("ASCEND_OPP_PATH", opp_path);
402+ const ScopedEnvVar home_env("ASCEND_HOME_PATH", EnvPath().GetAirBasePath());
403+ ASSERT_TRUE(opp_env.IsValid());
404+ ASSERT_TRUE(home_env.IsValid());
405+ 
406+ auto graph = std::make_shared<ComputeGraph>("host_cpu_fusion_missing_kernel_st");
407+ auto data = AddNode(graph, "data", "Data", 0U, 1U);
408+ auto first = AddNode(graph, "first", kHostFusionStA, 1U, 1U);
409+ auto missing = AddNode(graph, "missing", kHostFusionStMissing, 1U, 1U);
410+ auto after_missing = AddNode(graph, "after_missing", kHostFusionStAfterMissing, 1U, 1U);
411+ auto output = AddNode(graph, "output", "NetOutput", 1U, 0U);
412+ ASSERT_NE(data, nullptr);
413+ ASSERT_NE(first, nullptr);
414+ ASSERT_NE(missing, nullptr);
415+ ASSERT_NE(after_missing, nullptr);
416+ ASSERT_NE(output, nullptr);
417+ ASSERT_EQ(GraphUtils::AddEdge(data->GetOutDataAnchor(0), first->GetInDataAnchor(0)), GRAPH_SUCCESS);
418+ ASSERT_EQ(GraphUtils::AddEdge(first->GetOutDataAnchor(0), missing->GetInDataAnchor(0)), GRAPH_SUCCESS);
419+ ASSERT_EQ(GraphUtils::AddEdge(missing->GetOutDataAnchor(0), after_missing->GetInDataAnchor(0)), GRAPH_SUCCESS);
420+ ASSERT_EQ(GraphUtils::AddEdge(after_missing->GetOutDataAnchor(0), output->GetInDataAnchor(0)), GRAPH_SUCCESS);
421+ 
422+ HostCpuFusionRegion region;
423+ region.chain_id = "st_missing_kernel";
424+ region.nodes = {first, missing, after_missing};
425+ region.external_inputs = {data->GetOutDataAnchor(0)};
426+ region.external_outputs = {{after_missing->GetOutDataAnchor(0), {output->GetInDataAnchor(0)}}};
427+ HostCpuFusionCodegenResult result;
428+ HostCpuFusionCodegen codegen;
429+ ASSERT_EQ(codegen.Generate(region, result), SUCCESS);
430+ HostCpuFusionCompiler compiler;
431+ ASSERT_EQ(compiler.Compile(result.source, result.so_data), SUCCESS);
432+ EXPECT_NE(result.source.find("REGISTER_CPU_KERNEL(kFusedHostCpuKernel_st_missing_kernel"), std::string::npos);
433+ EXPECT_EQ(result.source.find("REGISTER_HOST_CPU_OP_BUILDER"), std::string::npos);
434+ EXPECT_EQ(result.source.find("host_cpu_kernel_registry.h"), std::string::npos);
435+ EXPECT_EQ(result.source.find("HostCpuOp"), std::string::npos);
436+ EXPECT_EQ(result.source.find("CreateHostCpuOp"), std::string::npos);
437+ EXPECT_EQ(result.source.find("dlsym"), std::string::npos);
438+ EXPECT_EQ(result.source.find("UpdateInputDesc"), std::string::npos);
439+ EXPECT_EQ(result.source.find("UpdateOutputDesc"), std::string::npos);
440+ EXPECT_EQ(result.source.find("std::map"), std::string::npos);
441+ EXPECT_NE(result.source.find("FusedHostCpuChainPlanGuard chain_plan_"), std::string::npos);
442+ EXPECT_NE(result.source.find("CreateCpuConstantFoldingFusedChainPlan("), std::string::npos);
443+ EXPECT_NE(result.source.find("node_descs.data(), node_descs.size(), 1U, 1U"), std::string::npos);
444+ EXPECT_NE(result.source.find("RunCpuConstantFoldingFusedChainPlan(chain_plan_.Get(), binding_flags)"),
445+ std::string::npos);
446+ EXPECT_NE(result.source.find("RunCpuConstantFoldingFusedChainPlanBindings("), std::string::npos);
447+ EXPECT_NE(result.source.find("FusedHostCpuTensorState"), std::string::npos);
448+ EXPECT_NE(result.source.find("FusedHostCpuTensorBinding"), std::string::npos);
449+ EXPECT_NE(result.source.find("ComputeBindings"), std::string::npos);
450+ EXPECT_NE(result.source.find("BuildFusedHostCpuRuntimeTensor"), std::string::npos);
451+ EXPECT_NE(result.source.find("InitializeBindings"), std::string::npos);
452+ EXPECT_EQ(result.source.find("BindFusedHostCpuTensor"), std::string::npos);
453+ EXPECT_EQ(result.source.find("if ((binding_flags == 0U) && runtime_bound_) { return Run(0U); }"), std::string::npos);
454+ EXPECT_EQ(result.source.find("binding_flags |= inputs["), std::string::npos);
455+ EXPECT_EQ(result.source.find("binding_flags |= outputs["), std::string::npos);
456+ EXPECT_NE(result.source.find("HasSameFusedHostCpuShape"), std::string::npos);
457+ EXPECT_EQ(result.source.find("GetDimSizes"), std::string::npos);
458+ EXPECT_NE(result.source.find("bool bindings_changed = false"), std::string::npos);
459+ EXPECT_NE(result.source.find("node_input_binding_indices_0{{0}}"), std::string::npos);
460+ EXPECT_NE(result.source.find("node_output_binding_indices_0{{-1}}"), std::string::npos);
461+ EXPECT_NE(result.source.find("node_input_binding_indices_1{{-1}}"), std::string::npos);
462+ EXPECT_NE(result.source.find("node_output_binding_indices_1{{-1}}"), std::string::npos);
463+ EXPECT_NE(result.source.find("node_input_binding_indices_2{{-1}}"), std::string::npos);
464+ EXPECT_NE(result.source.find("node_output_binding_indices_2{{1}}"), std::string::npos);
465+ EXPECT_EQ(result.source.find("RunCpuConstantFoldingFusedPlan"), std::string::npos);
466+ EXPECT_NE(result.source.find("static thread_local ge::FusedHostCpuOrchestration_st_missing_kernel orchestration"),
467+ std::string::npos);
468+ EXPECT_NE(result.source.find("void *CreateFusedHostCpuKernelState()"), std::string::npos);
469+ EXPECT_NE(result.source.find("if (state == nullptr) { return nullptr; }"), std::string::npos);
470+ EXPECT_EQ(result.source.find("state->Initialize()"), std::string::npos);
471+ EXPECT_NE(result.source.find("void DestroyFusedHostCpuKernelState(void *kernel_state)"), std::string::npos);
472+ EXPECT_NE(result.source.find("uint32_t RunFusedHostCpuKernel(void *kernel_state, const void *binding_data"),
473+ std::string::npos);
474+ EXPECT_NE(result.source.find("const bool bindings_changed"), std::string::npos);
475+#endif
476+}
477+ 
478+// 用例描述:验证 HostCpuFusionPass 从候选扫描、真实 JIT 到图提交和运行时加载的完整链路。
479+// 预置条件:Linux 环境已安装可用 Toolkit 头文件和目标架构 g++,测试提供融合 SO 所需符号。
480+// 测试步骤:构造两个 HostCPU 候选节点,运行融合 Pass,从根图读取生成 SO 并交给资源管理器加载。
481+// 预期结果:原节点被一个 FusedHostCpu 替换,SO 属性完整,注册名可成功加载和释放。
482+TEST(HostCpuFusionCodegenST, PassCompilesCommitsAndLoadsFusedSharedObject) {
483+#if !defined(__linux__)
484+ GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
485+#else
486+ const std::string opp_path = EnvPath().GetAscendInstallPath() + "/opp";
487+ const ScopedEnvVar opp_env("ASCEND_OPP_PATH", opp_path);
488+ const ScopedEnvVar home_env("ASCEND_HOME_PATH", EnvPath().GetAirBasePath());
489+ ASSERT_TRUE(opp_env.IsValid());
490+ ASSERT_TRUE(home_env.IsValid());
491+ 
492+ auto graph = std::make_shared<ComputeGraph>("host_cpu_fusion_pass_st");
493+ auto data = AddNode(graph, "data", "Data", 0U, 1U);
494+ auto first = AddNode(graph, "first", kHostFusionStA, 1U, 1U);
495+ auto second = AddNode(graph, "second", kHostFusionStB, 1U, 1U);
496+ auto output = AddNode(graph, "output", "NetOutput", 1U, 0U);
497+ ASSERT_NE(data, nullptr);
498+ ASSERT_NE(first, nullptr);
499+ ASSERT_NE(second, nullptr);
500+ ASSERT_NE(output, nullptr);
501+ ASSERT_EQ(GraphUtils::AddEdge(data->GetOutDataAnchor(0), first->GetInDataAnchor(0)), GRAPH_SUCCESS);
502+ ASSERT_EQ(GraphUtils::AddEdge(first->GetOutDataAnchor(0), second->GetInDataAnchor(0)), GRAPH_SUCCESS);
503+ ASSERT_EQ(GraphUtils::AddEdge(second->GetOutDataAnchor(0), output->GetInDataAnchor(0)), GRAPH_SUCCESS);
504+ MarkFusionCandidate(first);
505+ MarkFusionCandidate(second);
506+ 
507+ NodeEngineMap atomic_map;
508+ NodeEngineMap composite_map;
509+ HostCpuFusionPass pass(std::make_shared<HostCpuFusionCompiler>(), [](const std::string &) { return true; });
510+ ASSERT_EQ(pass.Run(graph, atomic_map, composite_map), SUCCESS);
511+ EXPECT_EQ(graph->FindNode("first"), nullptr);
512+ EXPECT_EQ(graph->FindNode("second"), nullptr);
513+ 
514+ NodePtr fused_node;
515+ for (const auto &node : graph->GetDirectNode()) {
516+ if (node->GetType() == kFusedHostCpuOpType) {
517+ fused_node = node;
518+ break;
519+ }
520+ }
521+ ASSERT_NE(fused_node, nullptr);
522+ ASSERT_EQ(atomic_map.size(), 1U);
523+ ASSERT_EQ(composite_map.size(), 1U);
524+ EXPECT_EQ(output->GetInDataAnchor(0)->GetPeerOutAnchor()->GetOwnerNode(), fused_node);
525+ 
526+ std::string register_name;
527+ std::string so_key;
528+ Buffer so_data;
529+ ASSERT_TRUE(AttrUtils::GetStr(fused_node->GetOpDesc(), kFusedHostCpuRegisterName, register_name));
530+ ASSERT_TRUE(AttrUtils::GetStr(fused_node->GetOpDesc(), kFusedHostCpuSoKey, so_key));
531+ ASSERT_TRUE(AttrUtils::GetBytes(graph, so_key, so_data));
532+ ASSERT_GT(so_data.GetSize(), 0U);
533+ ASSERT_EQ(
534+ gert::AicpuResourceManager::GetInstance().LoadFusedHostCpuSo(register_name, so_data.GetData(), so_data.GetSize()),
535+ GRAPH_SUCCESS);
536+ EXPECT_NE(aicpu::CpuKernelRegister::Instance().GetCpuKernel(register_name), nullptr);
537+ EXPECT_EQ(gert::AicpuResourceManager::GetInstance().ReleaseFusedHostCpuSo(register_name), GRAPH_SUCCESS);
538+#endif
539+}
540+ 
541+// 用例描述:验证 JIT 编译器对空源码和编译错误源码均安全回退。
542+// 预置条件:Linux 环境已安装可用 Toolkit 头文件和目标架构 g++。
543+// 测试步骤:依次提交空源码和语法错误源码,并检查返回码及输出缓存。
544+// 预期结果:两次调用均返回 UNSUPPORTED,且不会留下伪造的共享库数据。
545+TEST(HostCpuFusionCodegenST, CompilerRejectsEmptyAndInvalidSources) {
546+#if !defined(__linux__)
547+ GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
548+#else
549+ const std::string opp_path = EnvPath().GetAscendInstallPath() + "/opp";
550+ const ScopedEnvVar opp_env("ASCEND_OPP_PATH", opp_path);
551+ const ScopedEnvVar home_env("ASCEND_HOME_PATH", EnvPath().GetAirBasePath());
552+ ASSERT_TRUE(opp_env.IsValid());
553+ ASSERT_TRUE(home_env.IsValid());
554+ 
555+ HostCpuFusionCompiler compiler;
556+ std::vector<uint8_t> so_data{1U};
557+ EXPECT_EQ(compiler.Compile("", so_data), UNSUPPORTED);
558+ EXPECT_TRUE(so_data.empty());
559+ EXPECT_EQ(compiler.Compile("this is not valid C++;", so_data), UNSUPPORTED);
560+ EXPECT_TRUE(so_data.empty());
561+#endif
562+}
563+ 
564+// 用例描述:覆盖 JIT 工具链路径为空、缺失头文件和不同目标 CPU 的编译器选择。
565+// 预置条件:Linux 环境;不要求实际 Toolkit 头文件存在。
566+// 测试步骤:清空或设置无效 Toolkit 路径,分别设置 aarch64、x86_64 和未知目标 CPU。
567+// 预期结果:编译在头文件校验前安全返回 UNSUPPORTED,不启动外部编译器。
568+TEST(HostCpuFusionCodegenST, CompilerRejectsMissingToolkitAndSelectsTargetCompiler) {
569+#if !defined(__linux__)
570+ GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
571+#else
572+ const auto original_options = GetThreadLocalContext().GetAllGlobalOptions();
573+ const ScopedEnvVar empty_opp("ASCEND_OPP_PATH", "");
574+ const ScopedEnvVar empty_home("ASCEND_HOME_PATH", "");
575+ ASSERT_TRUE(empty_opp.IsValid());
576+ ASSERT_TRUE(empty_home.IsValid());
577+ HostCpuFusionCompiler compiler;
578+ std::vector<uint8_t> so_data;
579+ 
580+ GetThreadLocalContext().SetGlobalOption({{OPTION_HOST_ENV_CPU, "aarch64"}});
581+ EXPECT_EQ(compiler.Compile("int value = 0;", so_data), UNSUPPORTED);
582+ GetThreadLocalContext().SetGlobalOption({{OPTION_HOST_ENV_CPU, "x86_64"}});
583+ EXPECT_EQ(compiler.Compile("int value = 0;", so_data), UNSUPPORTED);
584+ GetThreadLocalContext().SetGlobalOption({{OPTION_HOST_ENV_CPU, "unsupported_cpu"}});
585+ EXPECT_EQ(compiler.Compile("int value = 0;", so_data), UNSUPPORTED);
586+ GetThreadLocalContext().SetGlobalOption(original_options);
587+ 
588+ const ScopedEnvVar invalid_opp("ASCEND_OPP_PATH", "/tmp/nonexistent/opp/");
589+ const ScopedEnvVar invalid_home("ASCEND_HOME_PATH", "/tmp/nonexistent/home/");
590+ ASSERT_TRUE(invalid_opp.IsValid());
591+ ASSERT_TRUE(invalid_home.IsValid());
592+ EXPECT_EQ(compiler.Compile("int value = 0;", so_data), UNSUPPORTED);
593+#endif
594+}
595+ 
596+// 用例描述:覆盖融合源码生成器的输入校验、描述符序列化和属性序列化边界。
597+// 预置条件:Linux 环境可构造 GE 图;本用例不依赖实际设备执行。
598+// 测试步骤:构造有效融合区域,再逐项注入非法节点、边、名称、Shape 和属性。
599+// 预期结果:非法输入均返回 PARAM_INVALID 或 UNSUPPORTED,合法输入能生成稳定源码。
600+TEST(HostCpuFusionCodegenST, CoversCodegenValidationAndDescriptorBoundaries) {
601+#if !defined(__linux__)
602+ GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
603+#else
604+ const auto graph = BuildCoverageChainGraph();
605+ ASSERT_NE(graph, nullptr);
606+ const auto valid = BuildCoverageRegion(graph);
193 HostCpuFusionCodegen codegen;607 HostCpuFusionCodegen codegen;
194 HostCpuFusionCodegenResult result;608 HostCpuFusionCodegenResult result;
609+ ASSERT_EQ(codegen.Generate(valid, result), SUCCESS);
610+ EXPECT_FALSE(result.source.empty());
195 611 
196 auto invalid = valid;612 auto invalid = valid;
197- invalid.chain_id.clear();613+ invalid.nodes.resize(1U);
198 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);614 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
199 invalid = valid;615 invalid = valid;
200- invalid.chain_id = "9bad";616+ invalid.chain_id = "1bad";
201 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);617 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
202 invalid = valid;618 invalid = valid;
203- invalid.chain_id = "has-dash";619+ invalid.chain_id = "bad-name";
204 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);620 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
205 invalid = valid;621 invalid = valid;
206- invalid.chain_id.assign(160U, 'a');622+ invalid.chain_id.assign(200U, 'x');
207- EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
208- invalid = valid;
209- invalid.external_outputs.clear();
210 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);623 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
211 invalid = valid;624 invalid = valid;
212 invalid.nodes[0] = nullptr;625 invalid.nodes[0] = nullptr;
213 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);626 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
214 invalid = valid;627 invalid = valid;
215- invalid.nodes[1] = invalid.nodes[0];628+ invalid.nodes.push_back(invalid.nodes.front());
216 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);629 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
217 invalid = valid;630 invalid = valid;
218 invalid.external_inputs[0] = nullptr;631 invalid.external_inputs[0] = nullptr;
219 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);632 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
220 invalid = valid;633 invalid = valid;
221- invalid.external_inputs.push_back(invalid.external_inputs[0]);634+ invalid.external_inputs.push_back(invalid.external_inputs.front());
222 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);635 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
223 invalid = valid;636 invalid = valid;
224 invalid.external_outputs[0].source = nullptr;637 invalid.external_outputs[0].source = nullptr;
225 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);638 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
226 invalid = valid;639 invalid = valid;
227- invalid.external_outputs.push_back(invalid.external_outputs[0]);640+ invalid.external_outputs.push_back(invalid.external_outputs.front());
641+ EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
642+ invalid = valid;
643+ std::swap(invalid.nodes[0], invalid.nodes[1]);
228 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);644 EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
229 645 
230 invalid = valid;646 invalid = valid;
231- invalid.nodes[1]->GetOpDesc()->MutableOutputDesc(0)->SetShape(GeShape({-1}));647+ invalid.external_inputs[0] = valid.nodes[0]->GetOutDataAnchor(0);
232- EXPECT_EQ(codegen.Generate(invalid, result), UNSUPPORTED);648+ EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
233 invalid = valid;649 invalid = valid;
234- invalid.nodes[0]->GetOpDesc()->MutableOutputDesc(0)->SetShape(GeShape({-1}));650+ invalid.external_outputs[0].source = graph->FindNode("data")->GetOutDataAnchor(0);
235- EXPECT_EQ(codegen.Generate(invalid, result), UNSUPPORTED);651+ EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
236 invalid = valid;652 invalid = valid;
237- ASSERT_EQ(GraphUtils::RemoveEdge(invalid.nodes[0]->GetInDataAnchor(0)->GetPeerOutAnchor(),653+ invalid.external_inputs.clear();
238- invalid.nodes[0]->GetInDataAnchor(0)),654+ EXPECT_EQ(codegen.Generate(invalid, result), PARAM_INVALID);
655+ 
656+ const auto missing_peer_graph = BuildCoverageChainGraph();
657+ ASSERT_NE(missing_peer_graph, nullptr);
658+ ASSERT_EQ(GraphUtils::RemoveEdge(missing_peer_graph->FindNode("data")->GetOutDataAnchor(0),
659+ missing_peer_graph->FindNode("first")->GetInDataAnchor(0)),
239 GRAPH_SUCCESS);660 GRAPH_SUCCESS);
240- EXPECT_EQ(codegen.Generate(invalid, result), UNSUPPORTED);661+ EXPECT_EQ(codegen.Generate(BuildCoverageRegion(missing_peer_graph), result), UNSUPPORTED);
241 662 
242- EXPECT_EQ(GetHostCpuFusionInputName(nullptr, 1U), "input_1_tensor");663+ const auto invalid_name_graph = BuildCoverageChainGraph();
243- const auto data = graph->FindNode("data");664+ ASSERT_NE(invalid_name_graph, nullptr);
244- ASSERT_NE(data, nullptr);665+ invalid_name_graph->FindNode("first")->GetOpDesc()->MutableAllInputName().clear();
245- data->GetOpDesc()->MutableAllOutputName().clear();666+ EXPECT_EQ(codegen.Generate(BuildCoverageRegion(invalid_name_graph), result), UNSUPPORTED);
246- data->GetOpDesc()->MutableAllOutputName().emplace("with-dash", 0U);
247- EXPECT_EQ(GetHostCpuFusionInputName(data->GetOutDataAnchor(0), 2U), "input_2_with_dash");
248-}
249 667 
250-TEST(HostCpuFusionCodegenST, CompilerHandlesMissingToolkitAndDiagnostics) {668+ const auto invalid_output_name_graph = BuildCoverageChainGraph();
251-#if defined(__linux__)669+ ASSERT_NE(invalid_output_name_graph, nullptr);
252- EnvGuard home("ASCEND_HOME_PATH", "");670+ invalid_output_name_graph->FindNode("second")->GetOpDesc()->MutableAllOutputName().clear();
253- EnvGuard opp("ASCEND_OPP_PATH", "");671+ EXPECT_EQ(codegen.Generate(BuildCoverageRegion(invalid_output_name_graph), result), UNSUPPORTED);
254- HostCpuFusionCompiler compiler;
255- std::vector<uint8_t> so_data;
256- EXPECT_EQ(compiler.Compile("int value = 0;", so_data), UNSUPPORTED);
257- EXPECT_TRUE(so_data.empty());
258 672 
259- const auto toolkit_home = GetToolkitHome();673+ const auto invalid_size_graph = BuildCoverageChainGraph();
260- EnvGuard valid_home("ASCEND_HOME_PATH", toolkit_home.c_str());674+ ASSERT_NE(invalid_size_graph, nullptr);
261- EXPECT_EQ(compiler.Compile("this is invalid C++;", so_data), UNSUPPORTED);675+ invalid_size_graph->FindNode("first")->GetOpDesc()->MutableOutputDesc(0)->SetShape(
262- EXPECT_TRUE(so_data.empty());676+ GeShape({std::numeric_limits<int64_t>::max(), 2}));
263- {677+ EXPECT_EQ(codegen.Generate(BuildCoverageRegion(invalid_size_graph), result), UNSUPPORTED);
264- EnvGuard no_home("ASCEND_HOME_PATH", "");678+ 
265- const auto toolkit_opp = GetToolkitOpp(toolkit_home);679+ const auto scalar_graph = BuildCoverageChainGraph();
266- EnvGuard opp_only("ASCEND_OPP_PATH", toolkit_opp.c_str());680+ ASSERT_NE(scalar_graph, nullptr);
267- EXPECT_EQ(compiler.Compile("", so_data), UNSUPPORTED);681+ auto first_desc = scalar_graph->FindNode("first")->GetOpDesc();
682+ first_desc->MutableOutputDesc(0)->SetShape(GeShape(std::vector<int64_t>{}));
683+ first_desc->MutableOutputDesc(0)->SetOriginShape(GeShape({3, 4}));
684+ ASSERT_EQ(first_desc->MutableOutputDesc(0)->SetShapeRange({{1, 3}, {2, 4}}), GRAPH_SUCCESS);
685+ ASSERT_TRUE(AttrUtils::SetInt(first_desc, "axis", 1));
686+ ASSERT_TRUE(AttrUtils::SetFloat(first_desc, "scale", 2.0F));
687+ ASSERT_TRUE(AttrUtils::SetBool(first_desc, "keep", true));
688+ ASSERT_TRUE(AttrUtils::SetStr(first_desc, "label", "value\r\t"));
689+ ASSERT_TRUE(AttrUtils::SetListInt(first_desc, "sizes", {1, 2}));
690+ ASSERT_TRUE(AttrUtils::SetListFloat(first_desc, "ratios", {0.25F, 0.75F}));
691+ for (const auto &name : {"axis", "scale", "keep", "label", "sizes", "ratios"}) {
692+ first_desc->AppendIrAttrName(name);
268 }693 }
269-#else694+ ASSERT_EQ(codegen.Generate(BuildCoverageRegion(scalar_graph), result), SUCCESS);
270- GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";695+ EXPECT_NE(result.source.find("SetOriginShape"), std::string::npos);
696+ EXPECT_NE(result.source.find("SetShapeRange"), std::string::npos);
697+ EXPECT_NE(result.source.find("std::vector<int64_t>{1LL, 2LL}"), std::string::npos);
698+ EXPECT_NE(result.source.find("std::vector<float>{0.25F, 0.75F}"), std::string::npos);
699+ 
700+ const auto unsupported_type_graph = BuildCoverageChainGraph();
701+ ASSERT_NE(unsupported_type_graph, nullptr);
702+ unsupported_type_graph->FindNode("first")->GetOpDesc()->MutableOutputDesc(0)->SetShape(
703+ GeShape(std::vector<int64_t>{}));
704+ unsupported_type_graph->FindNode("first")->GetOpDesc()->MutableOutputDesc(0)->SetDataType(DT_UNDEFINED);
705+ EXPECT_EQ(codegen.Generate(BuildCoverageRegion(unsupported_type_graph), result), UNSUPPORTED);
271#endif706#endif
272}707}
273 708 
274-TEST(HostCpuFusionCodegenST, CommitsHostCpuFusionPassWithEmbeddedCustomOpSo) {709+// 用例描述:覆盖 HostCPU 融合 Pass 的候选拒绝和提交回滚路径。
275-#if defined(__linux__)710+// 预置条件:使用假的编译器返回合法 ELF,避免测试依赖 JIT 工具链。
276- const auto toolkit_home = GetToolkitHome();711+// 测试步骤:构造候选链,分别注入不支持属性、控制边、Shape 边界和重复融合节点。
277- EnvGuard home("ASCEND_HOME_PATH", toolkit_home.c_str());712+// 预期结果:Pass 保持原图不变,并返回对应状态。
278- EnvGuard opp("ASCEND_OPP_PATH", "");713+TEST(HostCpuFusionCodegenST, CoversFusionPassCandidateAndRollbackPaths) {
279- const auto graph = BuildPassGraph();714+#if !defined(__linux__)
715+ GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
716+#else
717+ const auto graph = BuildCoverageChainGraph();
718+ ASSERT_NE(graph, nullptr);
719+ HostCpuFusionPass pass(std::make_shared<StFakeCompiler>(), [](const std::string &) { return false; });
720+ std::vector<std::vector<HostCpuFusionRegion> > components;
721+ EXPECT_EQ(pass.BuildFusionRegions(graph, components), NOT_CHANGED);
722+ EXPECT_TRUE(components.empty());
723+ 
724+ const auto control_graph = BuildCoverageChainGraph();
725+ ASSERT_NE(control_graph, nullptr);
726+ ASSERT_EQ(GraphUtils::AddEdge(control_graph->FindNode("data")->GetOutControlAnchor(),
727+ control_graph->FindNode("first")->GetInControlAnchor()),
728+ GRAPH_SUCCESS);
729+ components.clear();
730+ HostCpuFusionPass control_pass(std::make_shared<StFakeCompiler>(), [](const std::string &) { return true; });
731+ EXPECT_EQ(control_pass.BuildFusionRegions(control_graph, components), NOT_CHANGED);
732+ EXPECT_TRUE(components.empty());
733+ 
734+ const auto shape_graph = BuildCoverageChainGraph();
735+ ASSERT_NE(shape_graph, nullptr);
736+ shape_graph->FindNode("first")->GetOpDesc()->MutableOutputDesc(0)->SetShape(GeShape({-1}));
737+ components.clear();
738+ EXPECT_EQ(control_pass.BuildFusionRegions(shape_graph, components), NOT_CHANGED);
739+ EXPECT_TRUE(components.empty());
740+ 
741+ const auto empty_graph = std::make_shared<ComputeGraph>("empty_host_cpu_coverage");
742+ components.clear();
743+ EXPECT_EQ(control_pass.BuildFusionRegions(nullptr, components), PARAM_INVALID);
744+ EXPECT_EQ(control_pass.BuildFusionRegions(empty_graph, components), PARAM_INVALID);
745+ 
746+ const auto rollback_graph = BuildCoverageChainGraph();
747+ ASSERT_NE(rollback_graph, nullptr);
748+ HostCpuFusionPass region_pass(std::make_shared<StFakeCompiler>(), [](const std::string &) { return true; });
749+ ASSERT_EQ(region_pass.BuildFusionRegions(rollback_graph, components), SUCCESS);
750+ ASSERT_EQ(components.size(), 1U);
751+ ASSERT_EQ(components[0].size(), 1U);
752+ const std::string fused_name = std::string(kFusedHostCpuOpType) + "_" + components[0][0].chain_id;
753+ ASSERT_NE(rollback_graph->AddNode(std::make_shared<OpDesc>(fused_name, kFusedHostCpuOpType)), nullptr);
280 NodeEngineMap atomic_map;754 NodeEngineMap atomic_map;
281 NodeEngineMap composite_map;755 NodeEngineMap composite_map;
282- HostCpuFusionPass pass(std::make_shared<MinimalCustomOpCompilerForPassSt>(), SupportAllForPassSt);756+ EXPECT_EQ(region_pass.Run(rollback_graph, atomic_map, composite_map), FAILED);
283- ASSERT_EQ(pass.Run(graph, atomic_map, composite_map), SUCCESS);757+ EXPECT_NE(rollback_graph->FindNode("first"), nullptr);
284- EXPECT_EQ(graph->FindNode("pass_first"), nullptr);758+ EXPECT_NE(rollback_graph->FindNode("second"), nullptr);
285- EXPECT_EQ(graph->FindNode("pass_second"), nullptr);
286- EXPECT_EQ(graph->GetDirectNodesSize(), 3U);
287- EXPECT_EQ(atomic_map.size(), 1U);
288- EXPECT_EQ(composite_map.size(), 1U);
289-#else
290- GTEST_SKIP() << "HostCPU fusion JIT uses Linux memfd.";
291#endif759#endif
292}760}
293- 
294} // namespace ge761} // namespace ge
@@ -17,7 +17,6 @@
17#include <elf.h>17#include <elf.h>
18#include <fstream>18#include <fstream>
19#include <memory>19#include <memory>
20-#include <map>
21#include <set>20#include <set>
22#include <string>21#include <string>
23#include <unistd.h>22#include <unistd.h>
@@ -29,7 +28,6 @@
29#include "common/helper/custom_op_so_loader.h"28#include "common/helper/custom_op_so_loader.h"
30#include "common/model/ge_root_model.h"29#include "common/model/ge_root_model.h"
31#undef private30#undef private
32-#include "common/op_so_store/op_so_store_utils.h"
33#include "depends/mmpa/src/mmpa_stub.h"31#include "depends/mmpa/src/mmpa_stub.h"
34#include "faker/space_registry_faker.h"32#include "faker/space_registry_faker.h"
35#include "common/plugin/plugin_manager.h"33#include "common/plugin/plugin_manager.h"
@@ -41,7 +39,6 @@
41#include "graph/custom_op_factory.h"39#include "graph/custom_op_factory.h"
42#include "graph/custom_op_pull_registry.h"40#include "graph/custom_op_pull_registry.h"
43#include "graph/custom_op_registry.h"41#include "graph/custom_op_registry.h"
44-#include "graph/partition/engine_partitioner.h"
45#include "mmpa/mmpa_api.h"42#include "mmpa/mmpa_api.h"
46 43 
47namespace ge {44namespace ge {
@@ -1071,61 +1068,4 @@ TEST_F(TestModelCustomOpsHelper, ge_root_model_target_host_env_and_cross_compile
1071 const std::string mismatch_cpu = (current_env_cpu == "x86_64") ? "aarch64" : "x86_64";1068 const std::string mismatch_cpu = (current_env_cpu == "x86_64") ? "aarch64" : "x86_64";
1072 EXPECT_TRUE(ge_root_model.IsCrossCompileTarget(current_env_os, mismatch_cpu));1069 EXPECT_TRUE(ge_root_model.IsCrossCompileTarget(current_env_os, mismatch_cpu));
1073}1070}
1074- 
1075-TEST_F(TestModelCustomOpsHelper, model_helper_saves_embedded_custom_op_so) {
1076- auto root_graph = std::make_shared<ComputeGraph>("st_embedded_custom_so");
1077- auto ge_root_model = std::make_shared<GeRootModel>();
1078- ASSERT_EQ(ge_root_model->Initialize(root_graph), SUCCESS);
1079- OpSoStoreUtils::SetSoBinType(SoBinType::kCustomOp, ge_root_model->so_in_om_);
1080- const auto custom_so = BuildCustomOpSoBinForSt("libst_embedded_custom.so", "st_custom_vendor", {'E', 'L', 'F'});
1081- ASSERT_NE(custom_so, nullptr);
1082- ASSERT_TRUE(root_graph->SetExtAttr(
1083- "bin_file_buffer", std::map<std::string, OpSoBinPtr>{{"st_custom_vendor/libst_embedded_custom.so", custom_so}}));
1084- 
1085- ModelHelper model_helper;
1086- EXPECT_EQ(model_helper.SaveCustomOpSoBin(ge_root_model), SUCCESS);
1087-}
1088- 
1089-TEST_F(TestModelCustomOpsHelper, ge_root_model_uses_embedded_host_cpu_fusion_so) {
1090- RegisterCustomOpCreatorForSt(kCollectRootType, []() -> std::unique_ptr<BaseCustomOp> {
1091- return std::make_unique<PortableOpForSerializeSuccess>();
1092- });
1093- const auto ge_root_model = CreateRootModelForCustomOps(kCollectRootType, "", false, false);
1094- ASSERT_NE(ge_root_model, nullptr);
1095- std::string current_env_os;
1096- std::string current_env_cpu;
1097- GetCurrentEnvWithFallbackForSt(current_env_os, current_env_cpu);
1098- ScopedHostEnvOptionForSt host_env_guard(current_env_os, current_env_cpu);
1099- ScopedEnvVarForSt custom_opp_guard("ASCEND_CUSTOM_OPP_PATH");
1100- ASSERT_EQ(mmSetEnv("ASCEND_CUSTOM_OPP_PATH", "", 1), EN_OK);
1101- const auto embedded_so =
1102- BuildCustomOpSoBinForSt("libStModelHelperCollectRootPortableOp.so", "host_cpu_fusion", {'E', 'L', 'F'});
1103- ASSERT_NE(embedded_so, nullptr);
1104- ASSERT_TRUE(ge_root_model->GetRootGraph()->SetExtAttr(
1105- "bin_file_buffer",
1106- std::map<std::string, OpSoBinPtr>{{"host_cpu_fusion/libStModelHelperCollectRootPortableOp.so", embedded_so}}));
1107- 
1108- ASSERT_EQ(ge_root_model->CheckAndSetNeedSoInOM(), SUCCESS);
1109- EXPECT_TRUE(ge_root_model->GetCustomOpSoSet().empty());
1110- EXPECT_TRUE(OpSoStoreUtils::IsSoBinType(ge_root_model->GetSoInOmFlag(), SoBinType::kCustomOp));
1111-}
1112- 
1113-TEST_F(TestModelCustomOpsHelper, engine_partitioner_merges_embedded_custom_op_so_buffers) {
1114- EnginePartitioner partitioner;
1115- const auto original_graph = std::make_shared<ComputeGraph>("st_original_so");
1116- auto merged_graph = std::make_shared<ComputeGraph>("st_merged_so");
1117- const auto original_so = BuildCustomOpSoBinForSt("libst_original.so", "host_cpu_fusion", {'A'});
1118- const auto existing_so = BuildCustomOpSoBinForSt("libst_existing.so", "host_cpu_fusion", {'B'});
1119- ASSERT_NE(original_so, nullptr);
1120- ASSERT_NE(existing_so, nullptr);
1121- ASSERT_TRUE(original_graph->SetExtAttr(
1122- "bin_file_buffer", std::map<std::string, OpSoBinPtr>{{"host_cpu_fusion/libst_original.so", original_so}}));
1123- ASSERT_TRUE(merged_graph->SetExtAttr(
1124- "bin_file_buffer", std::map<std::string, OpSoBinPtr>{{"host_cpu_fusion/libst_existing.so", existing_so}}));
1125- 
1126- ASSERT_EQ(partitioner.InheritOriginalAttr(original_graph, merged_graph), SUCCESS);
1127- const auto so_buffer = merged_graph->GetExtAttr<std::map<std::string, OpSoBinPtr>>("bin_file_buffer");
1128- ASSERT_NE(so_buffer, nullptr);
1129- EXPECT_EQ(so_buffer->size(), 2U);
1130-}
1131} // namespace ge1071} // namespace ge
@@ -536,7 +536,6 @@ set(MULTI_PARTS_TEST_FILES
536 "graph/partition/graph_partition_unittest.cc"536 "graph/partition/graph_partition_unittest.cc"
537 "graph/partition/stage_partition_unittest.cc"537 "graph/partition/stage_partition_unittest.cc"
538 "graph/partition/optimizer/dynamic_data_flow_engine_reassign_unittest.cc"538 "graph/partition/optimizer/dynamic_data_flow_engine_reassign_unittest.cc"
539- "graph/partition/optimizer/host_cpu_fusion_codegen_unittest.cc"
540 "graph/partition/optimizer/host_cpu_fusion_pass_unittest.cc"539 "graph/partition/optimizer/host_cpu_fusion_pass_unittest.cc"
541 "graph/partition/optimizer/hostcpu_engine_update_pass_unittest.cc"540 "graph/partition/optimizer/hostcpu_engine_update_pass_unittest.cc"
542 "graph/manager/graph_manager_unittest.cc"541 "graph/manager/graph_manager_unittest.cc"
@@ -27,7 +27,6 @@
27#define private public27#define private public
28#include "common/model/ge_root_model.h"28#include "common/model/ge_root_model.h"
29#include "common/helper/custom_op_so_loader.h"29#include "common/helper/custom_op_so_loader.h"
30-#include "common/op_so_store/op_so_store_utils.h"
31#undef private30#undef private
32#include "framework/common/helper/model_helper.h"31#include "framework/common/helper/model_helper.h"
33#include "hybrid/node_executor/aicore/aicore_op_task.h"32#include "hybrid/node_executor/aicore/aicore_op_task.h"
@@ -2796,54 +2795,6 @@ TEST_F(UtestModelHelper, SaveAutofuseSoBinWithExtAttrPopulatesOpSoStore) {
2796 EXPECT_EQ(so_bins[0U]->GetSoBinType(), SoBinType::kAutofuse);2795 EXPECT_EQ(so_bins[0U]->GetSoBinType(), SoBinType::kAutofuse);
2797}2796}
2798 2797 
2799-TEST_F(UtestModelHelper, SaveCustomOpSoBinWithExtAttrStoresOnlyCustomOpSo) {
2800- auto root_graph = std::make_shared<ComputeGraph>("root_graph_custom_so_ext");
2801- auto ge_root_model = std::make_shared<GeRootModel>();
2802- ASSERT_EQ(ge_root_model->Initialize(root_graph), SUCCESS);
2803- OpSoStoreUtils::SetSoBinType(SoBinType::kCustomOp, ge_root_model->so_in_om_);
2804- 
2805- const auto custom_so =
2806- BuildOpSoBinForModelHelperUt("libcustom_repack_ut.so", "custom_vendor_ut", {0x10U, 0x20U}, SoBinType::kCustomOp);
2807- const auto autofuse_so = BuildOpSoBinForModelHelperUt("libautofuse_repack_ut.so", "autofuse_vendor_ut",
2808- {0x30U, 0x40U}, SoBinType::kAutofuse);
2809- ASSERT_NE(custom_so, nullptr);
2810- ASSERT_NE(autofuse_so, nullptr);
2811- std::map<std::string, OpSoBinPtr> so_buffer;
2812- so_buffer.emplace("custom_vendor_ut/libcustom_repack_ut.so", custom_so);
2813- so_buffer.emplace("autofuse_vendor_ut/libautofuse_repack_ut.so", autofuse_so);
2814- ASSERT_TRUE(root_graph->SetExtAttr("bin_file_buffer", so_buffer));
2815- 
2816- ModelHelper model_helper;
2817- ASSERT_EQ(model_helper.SaveCustomOpSoBin(ge_root_model), SUCCESS);
2818- const auto so_bins = model_helper.op_so_store_.GetSoBin();
2819- ASSERT_EQ(so_bins.size(), 1U);
2820- EXPECT_EQ(so_bins[0U]->GetSoName(), "libcustom_repack_ut.so");
2821- EXPECT_EQ(so_bins[0U]->GetSoBinType(), SoBinType::kCustomOp);
2822-}
2823- 
2824-TEST_F(UtestModelHelper, EmbeddedHostCpuFusionSoSkipsPortableSoPathCollect) {
2825- RegisterCustomOpCreatorForModelHelperUt(kPortableOpTypeForModelHelper, []() -> std::unique_ptr<BaseCustomOp> {
2826- return std::make_unique<ModelHelperPortableOpForUt>();
2827- });
2828- const auto ge_root_model =
2829- CreateGeRootModelForModelHelperUt(kPortableOpTypeForModelHelper, kPortableOpTypeForModelHelper);
2830- ASSERT_NE(ge_root_model, nullptr);
2831- ScopedHostEnvForModelHelperUt host_env_guard("linux", GetCurArch());
2832- ScopedEnvVarForModelHelperUt custom_opp_guard(kEnvNameCustom);
2833- ASSERT_EQ(mmSetEnv(kEnvNameCustom, "", 1), EN_OK);
2834- 
2835- const auto embedded_so = BuildOpSoBinForModelHelperUt("libModelHelperPortableOpForUt.so", "host_cpu_fusion",
2836- {0x7FU, 0x45U, 0x4CU, 0x46U}, SoBinType::kCustomOp);
2837- ASSERT_NE(embedded_so, nullptr);
2838- std::map<std::string, OpSoBinPtr> so_buffer;
2839- so_buffer.emplace("host_cpu_fusion/libModelHelperPortableOpForUt.so", embedded_so);
2840- ASSERT_TRUE(ge_root_model->GetRootGraph()->SetExtAttr("bin_file_buffer", so_buffer));
2841- 
2842- ASSERT_EQ(ge_root_model->CheckAndSetNeedSoInOM(), SUCCESS);
2843- EXPECT_TRUE(ge_root_model->GetCustomOpSoSet().empty());
2844- EXPECT_TRUE(OpSoStoreUtils::IsSoBinType(ge_root_model->GetSoInOmFlag(), SoBinType::kCustomOp));
2845-}
2846- 
2847// 边界用例:ExtAttr("bin_file_buffer")存在但map为空时不应异常,2798// 边界用例:ExtAttr("bin_file_buffer")存在但map为空时不应异常,
2848// op_so_store_应保持空。2799// op_so_store_应保持空。
2849TEST_F(UtestModelHelper, SaveAutofuseSoBinWithEmptyExtAttrDoesNotAddToStore) {2800TEST_F(UtestModelHelper, SaveAutofuseSoBinWithEmptyExtAttrDoesNotAddToStore) {
@@ -35,7 +35,6 @@
35#include "macro_utils/dt_public_unscope.h"35#include "macro_utils/dt_public_unscope.h"
36#include "graph/attribute_group/attr_group_shape_env.h"36#include "graph/attribute_group/attr_group_shape_env.h"
37#include "graph/custom_op_factory.h"37#include "graph/custom_op_factory.h"
38-#include "graph/op_so_bin.h"
39 38 
40namespace ge {39namespace ge {
41namespace airut {40namespace airut {
@@ -358,60 +357,6 @@ TEST_F(UtestGraphPartition, merge_overflow_attr) {
358 ASSERT_EQ(EnginePartitioner.global_workspace_type_, 0);357 ASSERT_EQ(EnginePartitioner.global_workspace_type_, 0);
359}358}
360 359 
361-TEST_F(UtestGraphPartition, inherit_original_attr_preserves_custom_op_so_buffer) {
362- EnginePartitioner partitioner;
363- const auto original_graph = std::make_shared<ComputeGraph>("original");
364- auto merged_graph = std::make_shared<ComputeGraph>("merged");
365- 
366- auto so_data = std::make_unique<char_t[]>(4U);
367- so_data[0] = 'E';
368- so_data[1] = 'L';
369- so_data[2] = 'F';
370- so_data[3] = '\0';
371- const auto so_bin =
372- MakeShared<OpSoBin>("libFusedHostCpu_test.so", "host_cpu_fusion", std::move(so_data), 4U, SoBinType::kCustomOp);
373- ASSERT_NE(so_bin, nullptr);
374- std::map<std::string, OpSoBinPtr> so_buffer;
375- so_buffer.emplace("host_cpu_fusion/libFusedHostCpu_test.so", so_bin);
376- ASSERT_TRUE(original_graph->SetExtAttr("bin_file_buffer", so_buffer));
377- 
378- ASSERT_EQ(partitioner.InheritOriginalAttr(original_graph, merged_graph), SUCCESS);
379- const auto inherited = merged_graph->GetExtAttr<std::map<std::string, OpSoBinPtr>>("bin_file_buffer");
380- ASSERT_NE(inherited, nullptr);
381- ASSERT_EQ(inherited->size(), 1U);
382- const auto iter = inherited->find("host_cpu_fusion/libFusedHostCpu_test.so");
383- ASSERT_NE(iter, inherited->end());
384- ASSERT_NE(iter->second, nullptr);
385- EXPECT_EQ(iter->second->GetSoBinType(), SoBinType::kCustomOp);
386-}
387- 
388-TEST_F(UtestGraphPartition, inherit_original_attr_merges_existing_custom_op_so_buffer) {
389- EnginePartitioner partitioner;
390- const auto original_graph = std::make_shared<ComputeGraph>("original_with_so");
391- auto merged_graph = std::make_shared<ComputeGraph>("merged_with_so");
392- auto original_data = std::make_unique<char_t[]>(1U);
393- original_data[0] = 'A';
394- auto existing_data = std::make_unique<char_t[]>(1U);
395- existing_data[0] = 'B';
396- const auto original_so =
397- MakeShared<OpSoBin>("liboriginal.so", "host_cpu_fusion", std::move(original_data), 1U, SoBinType::kCustomOp);
398- const auto existing_so =
399- MakeShared<OpSoBin>("libexisting.so", "host_cpu_fusion", std::move(existing_data), 1U, SoBinType::kCustomOp);
400- ASSERT_NE(original_so, nullptr);
401- ASSERT_NE(existing_so, nullptr);
402- ASSERT_TRUE(original_graph->SetExtAttr(
403- "bin_file_buffer", std::map<std::string, OpSoBinPtr>{{"host_cpu_fusion/liboriginal.so", original_so}}));
404- ASSERT_TRUE(merged_graph->SetExtAttr(
405- "bin_file_buffer", std::map<std::string, OpSoBinPtr>{{"host_cpu_fusion/libexisting.so", existing_so}}));
406- 
407- ASSERT_EQ(partitioner.InheritOriginalAttr(original_graph, merged_graph), SUCCESS);
408- const auto inherited = merged_graph->GetExtAttr<std::map<std::string, OpSoBinPtr>>("bin_file_buffer");
409- ASSERT_NE(inherited, nullptr);
410- EXPECT_EQ(inherited->size(), 2U);
411- EXPECT_EQ(inherited->at("host_cpu_fusion/liboriginal.so"), original_so);
412- EXPECT_EQ(inherited->at("host_cpu_fusion/libexisting.so"), existing_so);
413-}
414- 
415TEST_F(UtestGraphPartition, merge_after_sub_graph_optimization_test_with_func_sub_graph) {360TEST_F(UtestGraphPartition, merge_after_sub_graph_optimization_test_with_func_sub_graph) {
416 EnginePartitioner EnginePartitioner;361 EnginePartitioner EnginePartitioner;
417 ComputeGraphPtr graph = std::make_shared<ComputeGraph>("default");362 ComputeGraphPtr graph = std::make_shared<ComputeGraph>("default");