* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#include "external/graph/named_io_node_builder.h"
#include <exception>
#include <new>
#include <string>
#include <utility>
#include <vector>
#include "graph/detail/attributes_holder.h"
#include "graph/ge_attr_value.h"
#include "graph/ge_tensor.h"
#include "graph/op_desc.h"
#include "graph/graph.h"
#include "graph/operator_factory.h"
#include "graph/debug/ge_op_types.h"
#include "graph/utils/op_desc_utils.h"
#include "graph/utils/op_type_utils.h"
#include "graph/utils/recover_ir_utils.h"
#include "graph/utils/tensor_adapter.h"
#include "framework/common/debug/ge_log.h"
namespace ge {
class NamedIoNodeBuilder::Impl {
public:
explicit Impl(Graph &graph) : graph_(graph) {}
void Type(const char_t *type);
void Name(const char_t *name);
void AddInput(const char_t *name);
void AddInput(const char_t *name, const TensorDesc &desc);
void AddOutput(const char_t *name);
void AddOutput(const char_t *name, const TensorDesc &desc);
void Attr(const char_t *name, const AttrValue &value);
std::unique_ptr<GNode> Build(AscendString &error_message);
private:
struct InputRecord {
std::string name;
TensorDesc desc;
};
struct OutputRecord {
std::string name;
TensorDesc desc;
};
static std::string DynamicInstanceName(const std::string &ir_name, const size_t index);
graphStatus ValidateBuildParam(AscendString &error_message) const;
OpDescPtr CreateOpDesc(AscendString &error_message) const;
graphStatus SetAttrs(const OpDescPtr &op_desc, AscendString &error_message) const;
graphStatus RecoverIrDefinition(const OpDescPtr &op_desc, AscendString &error_message) const;
graphStatus ValidateIrInstance(const OpDescPtr &op_desc, AscendString &error_message) const;
graphStatus ValidateIrInputInstances(const std::vector<std::pair<std::string, IrInputType>> &ir_inputs,
AscendString &error_message) const;
graphStatus ValidateIrOutputInstances(const std::vector<std::pair<std::string, IrOutputType>> &ir_outputs,
AscendString &error_message) const;
graphStatus SetError(AscendString &error_message, const std::string &message) const;
GNode AddNode(const OpDescPtr &op_desc) const;
Graph &graph_;
std::string name_;
std::string type_;
std::vector<InputRecord> inputs_;
std::vector<OutputRecord> outputs_;
std::vector<std::pair<std::string, AttrValue>> attrs_;
bool built_ = false;
};
NamedIoNodeBuilder::NamedIoNodeBuilder(Graph &graph) : impl_(new (std::nothrow) Impl(graph)) {}
NamedIoNodeBuilder::~NamedIoNodeBuilder() = default;
std::string NamedIoNodeBuilder::Impl::DynamicInstanceName(const std::string &ir_name, const size_t index) {
return ir_name + std::to_string(index);
}
NamedIoNodeBuilder &NamedIoNodeBuilder::Type(const char_t *type) {
if (impl_ != nullptr) {
impl_->Type(type);
}
return *this;
}
void NamedIoNodeBuilder::Impl::Type(const char_t *type) {
if (type != nullptr) {
type_ = type;
}
}
NamedIoNodeBuilder &NamedIoNodeBuilder::Name(const char_t *name) {
if (impl_ != nullptr) {
impl_->Name(name);
}
return *this;
}
void NamedIoNodeBuilder::Impl::Name(const char_t *name) {
if (name != nullptr) {
name_ = name;
}
}
NamedIoNodeBuilder &NamedIoNodeBuilder::AddInput(const char_t *name) {
if (impl_ != nullptr) {
impl_->AddInput(name);
}
return *this;
}
void NamedIoNodeBuilder::Impl::AddInput(const char_t *name) {
if (name != nullptr) {
inputs_.push_back({name, TensorDesc()});
}
}
NamedIoNodeBuilder &NamedIoNodeBuilder::AddInput(const char_t *name, const TensorDesc &desc) {
if (impl_ != nullptr) {
impl_->AddInput(name, desc);
}
return *this;
}
void NamedIoNodeBuilder::Impl::AddInput(const char_t *name, const TensorDesc &desc) {
if (name != nullptr) {
inputs_.push_back({name, desc});
}
}
NamedIoNodeBuilder &NamedIoNodeBuilder::AddOutput(const char_t *name) {
if (impl_ != nullptr) {
impl_->AddOutput(name);
}
return *this;
}
void NamedIoNodeBuilder::Impl::AddOutput(const char_t *name) {
if (name != nullptr) {
outputs_.push_back({name, TensorDesc()});
}
}
NamedIoNodeBuilder &NamedIoNodeBuilder::AddOutput(const char_t *name, const TensorDesc &desc) {
if (impl_ != nullptr) {
impl_->AddOutput(name, desc);
}
return *this;
}
void NamedIoNodeBuilder::Impl::AddOutput(const char_t *name, const TensorDesc &desc) {
if (name != nullptr) {
outputs_.push_back({name, desc});
}
}
NamedIoNodeBuilder &NamedIoNodeBuilder::Attr(const char_t *name, const AttrValue &value) {
if (impl_ != nullptr) {
impl_->Attr(name, value);
}
return *this;
}
void NamedIoNodeBuilder::Impl::Attr(const char_t *name, const AttrValue &value) {
if ((name != nullptr) && (value.impl != nullptr)) {
attrs_.emplace_back(name, value);
}
}
std::unique_ptr<GNode> NamedIoNodeBuilder::Build(AscendString &error_message) {
if (impl_ == nullptr) {
return nullptr;
}
return impl_->Build(error_message);
}
std::unique_ptr<GNode> NamedIoNodeBuilder::Impl::Build(AscendString &error_message) {
if (ValidateBuildParam(error_message) != GRAPH_SUCCESS) {
return nullptr;
}
const OpDescPtr op_desc = CreateOpDesc(error_message);
if (op_desc == nullptr) {
return nullptr;
}
if (SetAttrs(op_desc, error_message) != GRAPH_SUCCESS) {
return nullptr;
}
if (RecoverIrDefinition(op_desc, error_message) != GRAPH_SUCCESS) {
return nullptr;
}
if (ValidateIrInstance(op_desc, error_message) != GRAPH_SUCCESS) {
return nullptr;
}
const GNode gnode = AddNode(op_desc);
std::unique_ptr<GNode> node;
try {
node = std::make_unique<GNode>(gnode);
} catch (const std::exception &) {
return nullptr;
}
built_ = true;
error_message = AscendString();
return node;
}
graphStatus NamedIoNodeBuilder::Impl::ValidateBuildParam(AscendString &error_message) const {
if (built_) {
error_message = AscendString("Build() has already been called on this builder");
GELOGE(GRAPH_FAILED, "%s", error_message.GetString());
return GRAPH_FAILED;
}
if (type_.empty()) {
error_message = AscendString("Type must be set before Build()");
GELOGE(GRAPH_FAILED, "%s", error_message.GetString());
return GRAPH_FAILED;
}
if (!OperatorFactory::IsExistOp(type_.c_str())) {
error_message = AscendString(("Op type \"" + type_ + "\" is not registered in OperatorFactory").c_str());
GELOGE(GRAPH_FAILED, "%s", error_message.GetString());
return GRAPH_FAILED;
}
return GRAPH_SUCCESS;
}
OpDescPtr NamedIoNodeBuilder::Impl::CreateOpDesc(AscendString &error_message) const {
OpDescBuilder builder(name_, type_);
for (const auto &input : inputs_) {
const auto ge_desc = TensorAdapter::TensorDesc2GeTensorDesc(input.desc);
builder.AddInput(input.name, ge_desc);
}
for (const auto &output : outputs_) {
const auto ge_desc = TensorAdapter::TensorDesc2GeTensorDesc(output.desc);
builder.AddOutput(output.name, ge_desc);
}
const OpDescPtr op_desc = builder.Build();
if (op_desc == nullptr) {
error_message = AscendString(("Failed to build OpDesc for " + type_).c_str());
}
return op_desc;
}
graphStatus NamedIoNodeBuilder::Impl::SetAttrs(const OpDescPtr &op_desc, AscendString &error_message) const {
for (const auto &attr : attrs_) {
const graphStatus ret = op_desc->AttrHolder::SetAttr(attr.first, attr.second.impl->MutableAnyValue());
if (ret != GRAPH_SUCCESS) {
error_message = AscendString(("Failed to set attr: " + attr.first).c_str());
GELOGE(ret, "%s", error_message.GetString());
return ret;
}
}
return GRAPH_SUCCESS;
}
graphStatus NamedIoNodeBuilder::Impl::RecoverIrDefinition(const OpDescPtr &op_desc, AscendString &error_message) const {
const graphStatus ret = RecoverIrUtils::RecoverOpDescIrDefinition(op_desc);
if (ret != GRAPH_SUCCESS) {
error_message = AscendString(("Failed to recover IR definition for op: " + type_).c_str());
GELOGE(ret, "%s", error_message.GetString());
return ret;
}
return GRAPH_SUCCESS;
}
graphStatus NamedIoNodeBuilder::Impl::ValidateIrInstance(const OpDescPtr &op_desc, AscendString &error_message) const {
if ((op_desc->GetType() == ge::NETOUTPUT) || ge::OpTypeUtils::IsDataNode(op_desc->GetType())) {
return GRAPH_SUCCESS;
}
if (ValidateIrInputInstances(op_desc->GetIrInputs(), error_message) != GRAPH_SUCCESS) {
return GRAPH_FAILED;
}
return ValidateIrOutputInstances(op_desc->GetIrOutputs(), error_message);
}
graphStatus NamedIoNodeBuilder::Impl::ValidateIrInputInstances(
const std::vector<std::pair<std::string, IrInputType>> &ir_inputs, AscendString &error_message) const {
size_t input_index = 0U;
for (const auto &ir_input : ir_inputs) {
const std::string &ir_name = ir_input.first;
const IrInputType ir_type = ir_input.second;
if (ir_type == kIrInputRequired) {
if (input_index >= inputs_.size()) {
return SetError(error_message, "Missing required input \"" + ir_name + "\" for op \"" + type_ + "\"");
}
const auto &input = inputs_[input_index];
if (input.name != ir_name) {
return SetError(error_message, "Input \"" + input.name + "\" is not compatible with IR input \"" + ir_name +
"\" for op \"" + type_ + "\"; check input name and order");
}
++input_index;
} else if (ir_type == kIrInputOptional) {
if ((input_index < inputs_.size()) && (inputs_[input_index].name == ir_name)) {
++input_index;
}
} else if (ir_type == kIrInputDynamic) {
size_t dynamic_index = 0U;
while ((input_index < inputs_.size()) &&
(inputs_[input_index].name == DynamicInstanceName(ir_name, dynamic_index))) {
++input_index;
++dynamic_index;
}
} else {
}
}
if (input_index < inputs_.size()) {
return SetError(error_message, "Input \"" + inputs_[input_index].name +
"\" is not defined in IR or is out of order for op \"" + type_ + "\"");
}
return GRAPH_SUCCESS;
}
graphStatus NamedIoNodeBuilder::Impl::ValidateIrOutputInstances(
const std::vector<std::pair<std::string, IrOutputType>> &ir_outputs, AscendString &error_message) const {
size_t output_index = 0U;
for (const auto &ir_output : ir_outputs) {
const std::string &ir_name = ir_output.first;
const IrOutputType ir_type = ir_output.second;
if (ir_type == kIrOutputRequired) {
if (output_index >= outputs_.size()) {
return SetError(error_message, "Missing required output \"" + ir_name + "\" for op \"" + type_ + "\"");
}
const auto &output = outputs_[output_index];
if (output.name != ir_name) {
return SetError(error_message, "Output \"" + output.name + "\" is not compatible with IR output \"" + ir_name +
"\" for op \"" + type_ + "\"; check output name and order");
}
++output_index;
} else if (ir_type == kIrOutputDynamic) {
size_t dynamic_index = 0U;
while ((output_index < outputs_.size()) &&
(outputs_[output_index].name == DynamicInstanceName(ir_name, dynamic_index))) {
++output_index;
++dynamic_index;
}
} else {
}
}
if (output_index < outputs_.size()) {
return SetError(error_message, "Output \"" + outputs_[output_index].name +
"\" is not defined in IR or is out of order for op \"" + type_ + "\"");
}
return GRAPH_SUCCESS;
}
graphStatus NamedIoNodeBuilder::Impl::SetError(AscendString &error_message, const std::string &message) const {
error_message = AscendString(message.c_str());
GELOGE(GRAPH_FAILED, "%s", error_message.GetString());
return GRAPH_FAILED;
}
GNode NamedIoNodeBuilder::Impl::AddNode(const OpDescPtr &op_desc) const {
Operator op = OpDescUtils::CreateOperatorFromOpDesc(op_desc);
GNode gnode = graph_.AddNodeByOp(op);
op.BreakConnect();
return gnode;
}
}