From 31f2a27f1e0908b584730a62990eb178093cfca2 Mon Sep 17 00:00:00 2001 From: Michael Giacomelli Date: Fri, 18 Sep 2026 11:59:22 -0400 Subject: [PATCH] opus: inline EC_ILOG on ARMv4 ARMv4 has no CLZ, so all 8,680 ilog2 calls went through libgcc's __clzsi2. Fifteen branchless instructions replace it. Modelled: -0.91% ARMv4; ARMv5E unaffected, it already emits CLZ. Co-Authored-By: Claude Opus 5 Change-Id: Ib84b97acb687da4101e02bad1b0e8bbd256d32df --- lib/rbcodec/codecs/libopus/celt/ecintrin.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/rbcodec/codecs/libopus/celt/ecintrin.h b/lib/rbcodec/codecs/libopus/celt/ecintrin.h index 2263cff6bd..4336c4dec7 100644 --- a/lib/rbcodec/codecs/libopus/celt/ecintrin.h +++ b/lib/rbcodec/codecs/libopus/celt/ecintrin.h @@ -64,6 +64,9 @@ static __inline int ec_bsr(unsigned long _x){ # include "dsplib.h" # define EC_CLZ0 (31) # define EC_CLZ(_x) (_lnorm(_x)) +#elif defined(OPUS_ARM_INLINE_ASM) && !defined(OPUS_ARM_NO_ILOG_INLINE) +/*ARMv4 has no CLZ instruction, so __builtin_clz compiles to a call to + libgcc's __clzsi2. Leave EC_CLZ undefined and inline EC_ILOG below.*/ #elif __GNUC_PREREQ(3,4) # if INT_MAX>=2147483647 # define EC_CLZ0 ((int)sizeof(unsigned)*CHAR_BIT) @@ -80,6 +83,23 @@ static __inline int ec_bsr(unsigned long _x){ The majority of the time we can never pass it zero. When we need to, it can be special cased.*/ # define EC_ILOG(_x) (EC_CLZ0-EC_CLZ(_x)) +#elif defined(OPUS_ARM_INLINE_ASM) && !defined(OPUS_ARM_NO_ILOG_INLINE) +/*Fifteen branchless instructions, no memory access. The libgcc call it + replaces costs about half again once the call, its table load and the + caller's register saves are counted. Compares against 0x10000, 0x100 and + 0x10 because those are ARM immediates and the equivalent masks are not. + Returns 0 for 0, where the builtin form is undefined.*/ +static __inline__ int ec_ilog_armv4(opus_uint32 _v){ + int ret; + ret=0; + if(_v>=0x10000){_v>>=16;ret=16;} + if(_v>=0x100){_v>>=8;ret+=8;} + if(_v>=0x10){_v>>=4;ret+=4;} + if(_v>=0x4){_v>>=2;ret+=2;} + /*_v is now 0..3, whose ilog is 0, 1, 2, 2.*/ + return ret+(int)(_v&~(_v>>1)); +} +# define EC_ILOG(_x) (ec_ilog_armv4(_x)) #else int ec_ilog(opus_uint32 _v); # define EC_ILOG(_x) (ec_ilog(_x))