diff --git a/ggml/src/ggml-hrx/dispatch_registration/common/dispatch-copy.cpp b/ggml/src/ggml-hrx/dispatch_registration/common/dispatch-copy.cpp index 5f091f40e564..37c9f1ffe893 100644 --- a/ggml/src/ggml-hrx/dispatch_registration/common/dispatch-copy.cpp +++ b/ggml/src/ggml-hrx/dispatch_registration/common/dispatch-copy.cpp @@ -122,9 +122,17 @@ static bool match_copy_f32_dispatch(const DispatchMatchContext & context, Dispat return true; } -static bool match_cont_f32_dispatch(const DispatchMatchContext & context, DispatchMatch & match) { +static bool match_cont_or_dup_f32_dispatch(const DispatchMatchContext & context, DispatchMatch & match) { const GraphNode * node = context.root_node; - if (node == nullptr || node->op != GGML_OP_CONT || node->inputs.size() != 1) { + if (node == nullptr) { + return false; + } + const bool is_cont = node->op == GGML_OP_CONT; + const bool is_dup = node->op == GGML_OP_DUP; + const bool is_copy = is_cont || is_dup; + const bool has_source = node->inputs.size() == 1; + const bool matches = is_copy && has_source; + if (!matches) { return false; } @@ -216,7 +224,15 @@ void register_copy_dispatch(DispatchRegistryBuilder & registry) { DispatchMatchKind::SingleOp, 0, DispatchSource::Common, - match_cont_f32_dispatch, + match_cont_or_dup_f32_dispatch, + }); + registry.add({ + "common.dup_f32", + GGML_OP_DUP, + DispatchMatchKind::SingleOp, + 0, + DispatchSource::Common, + match_cont_or_dup_f32_dispatch, }); registry.add({ "common.concat_dim0_f32", diff --git a/ggml/src/ggml-hrx/ggml-hrx.cpp b/ggml/src/ggml-hrx/ggml-hrx.cpp index 71b4f9977ae6..0f21047a92d0 100644 --- a/ggml/src/ggml-hrx/ggml-hrx.cpp +++ b/ggml/src/ggml-hrx/ggml-hrx.cpp @@ -1032,6 +1032,23 @@ static bool device_supports_op(ggml_backend_dev_t device, const ggml_tensor * op if (zero_output_elision_supported(op)) { return true; } + if (op->op == GGML_OP_DUP) { + const ggml_tensor * source = op->src[0]; + if (source == nullptr) { + return false; + } + const bool source_f32 = source->type == GGML_TYPE_F32; + const bool output_f32 = op->type == GGML_TYPE_F32; + const bool source_contiguous = ggml_is_contiguous(source); + const bool output_contiguous = ggml_is_contiguous(op); + const bool same_shape = ggml_are_same_shape(source, op); + const bool separate_output = op->view_src == nullptr; + const int64_t elements = ggml_nelements(op); + const bool nonempty = elements > 0; + const bool bounded = elements <= (int64_t{ 1 } << 30); + return source_f32 && output_f32 && source_contiguous && output_contiguous && same_shape && + separate_output && nonempty && bounded; + } const bool supported_binary = supported_binary_f32_tensor(op); if (supported_binary) { return true; diff --git a/tests/hrx-ops-test.cpp b/tests/hrx-ops-test.cpp index e8cb79bac47b..73d5f79efc83 100644 --- a/tests/hrx-ops-test.cpp +++ b/tests/hrx-ops-test.cpp @@ -3639,6 +3639,8 @@ static void run_dynamic_generic_ops_cpu_reference_case() { ggml_tensor * hrx_copy_target = ggml_new_tensor_1d(hrx_ctx, GGML_TYPE_F32, element_count); ggml_tensor * cpu_copy = ggml_cpy(cpu_ctx, cpu_input, cpu_copy_target); ggml_tensor * hrx_copy = ggml_cpy(hrx_ctx, hrx_input, hrx_copy_target); + ggml_tensor * cpu_dup = ggml_dup(cpu_ctx, cpu_input); + ggml_tensor * hrx_dup = ggml_dup(hrx_ctx, hrx_input); ggml_tensor * cpu_concat_lhs = ggml_new_tensor_2d(cpu_ctx, GGML_TYPE_F32, 64, 7); ggml_tensor * cpu_concat_rhs = ggml_new_tensor_2d(cpu_ctx, GGML_TYPE_F32, 32, 7); ggml_tensor * hrx_concat_lhs = ggml_new_tensor_2d(hrx_ctx, GGML_TYPE_F32, 64, 7); @@ -3647,6 +3649,9 @@ static void run_dynamic_generic_ops_cpu_reference_case() { ggml_tensor * hrx_concat = ggml_concat(hrx_ctx, hrx_concat_lhs, hrx_concat_rhs, 0); REQUIRE(cpu_unary != nullptr && hrx_unary != nullptr); REQUIRE(cpu_copy != nullptr && hrx_copy != nullptr); + REQUIRE(cpu_dup != nullptr); + REQUIRE(hrx_dup != nullptr); + REQUIRE(ggml_backend_supports_op(hrx_backend, hrx_dup)); REQUIRE(cpu_concat != nullptr && hrx_concat != nullptr); ggml_cgraph * cpu_graph = ggml_new_graph(cpu_ctx); @@ -3654,13 +3659,15 @@ static void run_dynamic_generic_ops_cpu_reference_case() { REQUIRE(cpu_graph != nullptr && hrx_graph != nullptr); ggml_build_forward_expand(cpu_graph, cpu_unary); ggml_build_forward_expand(cpu_graph, cpu_copy); + ggml_build_forward_expand(cpu_graph, cpu_dup); ggml_build_forward_expand(cpu_graph, cpu_concat); ggml_build_forward_expand(hrx_graph, hrx_unary); ggml_build_forward_expand(hrx_graph, hrx_copy); + ggml_build_forward_expand(hrx_graph, hrx_dup); ggml_build_forward_expand(hrx_graph, hrx_concat); require_kernel_subsequence(scheduled_kernel_sequence(hrx_graph), - { "loom_libs:ggml_unary_f32", "loom_libs:ggml_copy_f32", + { "loom_libs:ggml_unary_f32", "loom_libs:ggml_copy_f32", "loom_libs:ggml_copy_f32", "loom_libs:ggml_concat_dim0_f32" }); for (const char * kernel_name : { "loom_libs:ggml_unary_f32", "loom_libs:ggml_copy_f32", "loom_libs:ggml_concat_dim0_f32" }) { @@ -3687,6 +3694,7 @@ static void run_dynamic_generic_ops_cpu_reference_case() { ggml_backend_synchronize(hrx_backend); require_close(get_f32_tensor(hrx_backend, hrx_unary), get_f32_tensor(cpu_backend, cpu_unary), 0.0f); require_close(get_f32_tensor(hrx_backend, hrx_copy), get_f32_tensor(cpu_backend, cpu_copy), 0.0f); + require_close(get_f32_tensor(hrx_backend, hrx_dup), get_f32_tensor(cpu_backend, cpu_dup), 0.0f); require_close(get_f32_tensor(hrx_backend, hrx_concat), get_f32_tensor(cpu_backend, cpu_concat), 0.0f); ggml_backend_buffer_free(cpu_buffer);