From 76f46ad29d61fd8c1401e8221842934bf62a6064 Mon Sep 17 00:00:00 2001 From: Todor Boinovski Date: Mon, 20 Jul 2026 16:12:09 -0700 Subject: [PATCH] hexagon: add CLAMP op (#25934) --- ggml/src/ggml-hexagon/ggml-hexagon.cpp | 2 ++ ggml/src/ggml-hexagon/htp/htp-ops.h | 1 + ggml/src/ggml-hexagon/htp/main.c | 1 + ggml/src/ggml-hexagon/htp/unary-ops.c | 31 ++++++++++++++++++++++++++ ggml/src/ggml-hexagon/htp/unary-ops.h | 1 + 5 files changed, 36 insertions(+) diff --git a/ggml/src/ggml-hexagon/ggml-hexagon.cpp b/ggml/src/ggml-hexagon/ggml-hexagon.cpp index 9388a59c1a..34f1ff57b2 100644 --- a/ggml/src/ggml-hexagon/ggml-hexagon.cpp +++ b/ggml/src/ggml-hexagon/ggml-hexagon.cpp @@ -3476,6 +3476,7 @@ static htp_op_code op_remap_to_htp(const ggml_tensor * t) { case GGML_OP_RMS_NORM: return HTP_OP_RMS_NORM; case GGML_OP_CONCAT: return HTP_OP_CONCAT; case GGML_OP_SCALE: return HTP_OP_SCALE; + case GGML_OP_CLAMP: return HTP_OP_CLAMP; case GGML_OP_SQR: return HTP_OP_SQR; case GGML_OP_SQRT: return HTP_OP_SQRT; case GGML_OP_SOFT_MAX: return HTP_OP_SOFTMAX; @@ -4126,6 +4127,7 @@ static bool ggml_backend_hexagon_device_supports_op(ggml_backend_dev_t dev, cons case GGML_OP_L2_NORM: case GGML_OP_RMS_NORM: case GGML_OP_SCALE: + case GGML_OP_CLAMP: supp = ggml_hexagon_supported_unary(sess, op); break; diff --git a/ggml/src/ggml-hexagon/htp/htp-ops.h b/ggml/src/ggml-hexagon/htp/htp-ops.h index 1feaf2f999..262d6110fa 100644 --- a/ggml/src/ggml-hexagon/htp/htp-ops.h +++ b/ggml/src/ggml-hexagon/htp/htp-ops.h @@ -97,6 +97,7 @@ enum htp_op_code { HTP_OP_PAD, HTP_OP_NORM, HTP_OP_CONCAT, + HTP_OP_CLAMP, HTP_OP_INVALID }; diff --git a/ggml/src/ggml-hexagon/htp/main.c b/ggml/src/ggml-hexagon/htp/main.c index b53bb5024e..e76ac7f488 100644 --- a/ggml/src/ggml-hexagon/htp/main.c +++ b/ggml/src/ggml-hexagon/htp/main.c @@ -718,6 +718,7 @@ static int execute_op(struct htp_ops_context * octx) { case HTP_OP_RMS_NORM: case HTP_OP_RMS_NORM_MUL: case HTP_OP_SCALE: + case HTP_OP_CLAMP: case HTP_OP_SQR: case HTP_OP_SQRT: case HTP_OP_UNARY_SOFTPLUS: diff --git a/ggml/src/ggml-hexagon/htp/unary-ops.c b/ggml/src/ggml-hexagon/htp/unary-ops.c index 82792c610b..c6806efc37 100644 --- a/ggml/src/ggml-hexagon/htp/unary-ops.c +++ b/ggml/src/ggml-hexagon/htp/unary-ops.c @@ -138,6 +138,24 @@ static void scale_f32(const float * restrict src, } } +static void clamp_f32(const float * restrict src, + float * restrict dst, + const uint32_t num_rows, + const struct htp_unary_context * uctx) { + htp_unary_op_preamble; + float min = 0.f; + float max = 0.f; + memcpy(&min, &op_params[0], sizeof(float)); + memcpy(&max, &op_params[1], sizeof(float)); + + for (uint32_t ir = 0; ir < num_rows; ir++) { + const uint8_t * restrict src_local = (const uint8_t *)src + (ir * src0_row_size_aligned); + uint8_t * restrict dst_local = (uint8_t *)dst + (ir * dst_row_size_aligned); + + hvx_clamp_scalar_f32(dst_local, src_local, min, max, ne0); + } +} + static void rms_norm_f32(const float * restrict src, float * restrict dst, const uint32_t num_rows, @@ -542,6 +560,7 @@ DEFINE_UNARY_TASK(norm, false, false, norm_f32(src0_vtcm, dst_vtcm, bl DEFINE_UNARY_TASK(rms_norm, false, false, rms_norm_f32(src0_vtcm, dst_vtcm, block_size, uctx)) DEFINE_UNARY_TASK(rms_norm_mul, true, false, rms_norm_mul_f32(src0_vtcm, uctx->broadcast_weight ? (const float *) src1_vtcm_data : src1_vtcm, dst_vtcm, block_size, uctx)) DEFINE_UNARY_TASK(scale, false, false, scale_f32(src0_vtcm, dst_vtcm, block_size, uctx)) +DEFINE_UNARY_TASK(clamp, false, false, clamp_f32(src0_vtcm, dst_vtcm, block_size, uctx)) DEFINE_UNARY_TASK(sqr, false, false, sqr_f32(src0_vtcm, dst_vtcm, block_size, uctx)) DEFINE_UNARY_TASK(sqrt, false, false, sqrt_f32(src0_vtcm, dst_vtcm, block_size, uctx)) DEFINE_UNARY_TASK(unary_neg, false, false, neg_f32(src0_vtcm, dst_vtcm, block_size, uctx)) @@ -681,6 +700,14 @@ static inline void tile_scale_f32(uint8_t * dst_vtcm, const uint8_t * src_vtcm, hvx_scale_offset_f32_aa(dst_vtcm, src_vtcm, tw, scale, bias); } +static inline void tile_clamp_f32(uint8_t * dst_vtcm, const uint8_t * src_vtcm, uint32_t tw, const int32_t * op_params) { + float min = 0.f; + float max = 0.f; + memcpy(&min, &op_params[0], sizeof(float)); + memcpy(&max, &op_params[1], sizeof(float)); + hvx_clamp_scalar_f32(dst_vtcm, src_vtcm, min, max, tw); +} + static inline void tile_unary_softplus_f32(uint8_t * dst_vtcm, const uint8_t * src_vtcm, uint32_t tw) { const float * restrict sf = (const float *) src_vtcm; float * restrict df = (float *) dst_vtcm; @@ -765,6 +792,7 @@ static inline void tri_apply_tile_f32(const uint8_t * restrict src, uint8_t * re } DEFINE_UNARY_TILED_TASK(scale, false, tile_scale_f32(dst_vtcm, src_vtcm, tw, op_params)) +DEFINE_UNARY_TILED_TASK(clamp, false, tile_clamp_f32(dst_vtcm, src_vtcm, tw, op_params)) DEFINE_UNARY_TILED_TASK(sqr, false, hvx_sqr_f32_aa(dst_vtcm, src_vtcm, tw)) DEFINE_UNARY_TILED_TASK(sqrt, false, hvx_sqrt_f32_aa(dst_vtcm, src_vtcm, tw)) DEFINE_UNARY_TILED_TASK(unary_neg, false, hvx_scale_f32_aa(dst_vtcm, src_vtcm, tw, -1.0f)) @@ -787,6 +815,7 @@ static int execute_op_unary_f32(struct htp_ops_context * octx) { case HTP_OP_RMS_NORM: op_type = "rmsnorm-f32"; break; case HTP_OP_RMS_NORM_MUL: op_type = "rmsnorm-mul-f32"; break; case HTP_OP_SCALE: op_type = "scale-f32"; break; + case HTP_OP_CLAMP: op_type = "clamp-f32"; break; case HTP_OP_SQR: op_type = "sqr-f32"; break; case HTP_OP_SQRT: op_type = "sqrt-f32"; break; case HTP_OP_UNARY_NEG: op_type = "neg-f32"; break; @@ -882,6 +911,7 @@ static int execute_op_unary_f32(struct htp_ops_context * octx) { if (col_tile) { switch (octx->op) { case HTP_OP_SCALE: task_func = unary_task_f32_tiled_scale; break; + case HTP_OP_CLAMP: task_func = unary_task_f32_tiled_clamp; break; case HTP_OP_SQR: task_func = unary_task_f32_tiled_sqr; break; case HTP_OP_SQRT: task_func = unary_task_f32_tiled_sqrt; break; case HTP_OP_UNARY_NEG: task_func = unary_task_f32_tiled_unary_neg; break; @@ -898,6 +928,7 @@ static int execute_op_unary_f32(struct htp_ops_context * octx) { case HTP_OP_RMS_NORM: task_func = unary_task_f32_rms_norm; break; case HTP_OP_RMS_NORM_MUL: task_func = unary_task_f32_rms_norm_mul; break; case HTP_OP_SCALE: task_func = unary_task_f32_scale; break; + case HTP_OP_CLAMP: task_func = unary_task_f32_clamp; break; case HTP_OP_SQR: task_func = unary_task_f32_sqr; break; case HTP_OP_SQRT: task_func = unary_task_f32_sqrt; break; case HTP_OP_UNARY_NEG: task_func = unary_task_f32_unary_neg; break; diff --git a/ggml/src/ggml-hexagon/htp/unary-ops.h b/ggml/src/ggml-hexagon/htp/unary-ops.h index b90b095ac9..4005e65151 100644 --- a/ggml/src/ggml-hexagon/htp/unary-ops.h +++ b/ggml/src/ggml-hexagon/htp/unary-ops.h @@ -41,6 +41,7 @@ _Static_assert(sizeof(struct htp_unary_kernel_params) <= 128, "htp_unary_kernel_ static inline bool htp_op_is_unary(uint32_t opcode) { switch (opcode) { + case HTP_OP_CLAMP: case HTP_OP_NORM: case HTP_OP_RMS_NORM: case HTP_OP_RMS_NORM_MUL: