Repository: ggerganov/llama.cpp
File: ggml/src/ggml-cann/ggml-cann.cpp (C++)

Code before:
            } else {
                prop.src_address[src] = nullptr;
                std::fill_n(prop.src_ne[src], GGML_MAX_DIMS, 0);
                std::fill_n(prop.src_nb[src], GGML_MAX_DIMS, 0);
            }
        }

        memcpy(prop.op_params, node->op_params, GGML_MAX_OP_PARAMS);
    }

    // Insert into the LRU cache (cache takes ownership and will delete it when evicted).
    cann_ctx->graph_lru_cache.push(new_graph);
}

/**
 * @brief Check if a ggml tensor node matches a previously captured CANN graph node.
 *
 * This function compares all relevant fields (address, op type, shape, source inputs, op params)
 * to determine whether the current node matches a previously recorded version.
 *
 * @param node                  The current ggml tensor node.
 * @param graph_node_properties The stored properties of a CANN graph node.
 * @return true if all fields match (excluding GGML_OP_VIEW); false otherwise.
 */
static bool ggml_graph_node_has_matching_properties(ggml_tensor *                node,
                                                    ggml_graph_node_properties * graph_node_properties) {
    if (node->data != graph_node_properties->node_address && node->op != GGML_OP_VIEW) {
        return false;
    }

    if (node->op != graph_node_properties->node_op) {
        return false;
    }

    for (int i = 0; i < GGML_MAX_DIMS; i++) {
        if (node->ne[i] != graph_node_properties->ne[i]) {
            return false;
        }
        if (node->nb[i] != graph_node_properties->nb[i]) {
            return false;
        }
    }

    for (int i = 0; i < GGML_MAX_SRC; i++) {
        if (node->src[i]) {
            if (node->src[i]->data != graph_node_properties->src_address[i] && node->op != GGML_OP_VIEW) {
                return false;
            }

            for (int d = 0; d < GGML_MAX_DIMS; d++) {
                if (node->src[i]->ne[d] != graph_node_properties->src_ne[i][d]) {

Review comment (suggestion):
```suggestion
                if (ggml_cann_can_fuse(cgraph, i, { cgraph->nodes[i]->op, cgraph->nodes[i+1]->op})) {

```

Code after:
 *
 * Otherwise, it falls back to op-by-op execution using the CANN compute kernel dispatcher.
 *
 * @param cann_ctx                     The CANN backend context.
 * @param cgraph                       The ggml computation graph.
 * @param use_cann_graph               Whether to use CANN graph execution.
 * @param cann_graph_capture_required  Whether graph capture is needed due to graph changes.
 */
static void evaluate_and_capture_cann_graph(ggml_backend_cann_context * cann_ctx,
                                            ggml_cgraph *               cgraph,
                                            bool                        use_cann_graph,
                                            bool                        cann_graph_capture_required) {
#ifdef USE_ACL_GRAPH
    if (use_cann_graph && cann_graph_capture_required) {  // Begin CANN graph capture
        ACL_CHECK(aclmdlRICaptureBegin(cann_ctx->stream(), ACL_MODEL_RI_CAPTURE_MODE_GLOBAL));
    }
#endif  // USE_ACL_GRAPH
    // Only perform the graph execution if CANN graphs are not enabled, or we are capturing the graph.
    // With the use of CANN graphs, the execution will be performed by the graph launch.
    static bool opt_fusion = parse_bool(get_env("GGML_CANN_OPERATOR_FUSION").value_or(""));

    if (!use_cann_graph || cann_graph_capture_required) {
        for (int i = 0; i < cgraph->n_nodes; i++) {
            ggml_tensor * node = cgraph->nodes[i];
            if (opt_fusion) {
                if (ggml_cann_can_fuse(cgraph, i, { GGML_OP_ADD, GGML_OP_RMS_NORM })) {
                    ggml_cann_op_add_rms_norm_fused(*cann_ctx, node, cgraph->nodes[i + 1]);
                    i++;
                    continue;
                }
            }

            if (ggml_is_empty(node) || node->op == GGML_OP_RESHAPE || node->op == GGML_OP_TRANSPOSE ||
                node->op == GGML_OP_VIEW || node->op == GGML_OP_PERMUTE || node->op == GGML_OP_NONE) {
                continue;
            }

            bool ok = ggml_cann_compute_forward(*cann_ctx, node);
            if (!ok) {
                GGML_LOG_ERROR("%s: op not supported %s (%s)\n", __func__, node->name, ggml_op_name(node->op));
            }
            GGML_ASSERT(ok);
        }
    }

#ifdef USE_ACL_GRAPH
    if (use_cann_graph) {
        GGML_ASSERT(!cann_ctx->graph_lru_cache.cache_list.empty());
        ggml_cann_graph * matched_graph = cann_ctx->graph_lru_cache.cache_list.front();

        if (cann_graph_capture_required) {  // End CANN graph capture