https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78103
--- Comment #8 from Jakub Jelinek <jakub at gcc dot gnu.org> --- I actually think this should be doable with combiner splitters. For the unsigned int findLastSet3(unsigned long x) { return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; } or unsigned int findLastSet4(unsigned int x) { return x ? 8 * sizeof(unsigned int) - __builtin_clz(x) : 0; } the combiner sees everything together, in the first case (parallel [ (set (reg:SI 84 [ <retval> ]) (minus:SI (const_int 64 [0x40]) (xor:SI (minus:SI (const_int 63 [0x3f]) (subreg:SI (clz:DI (reg/v:DI 85 [ x ])) 0)) (const_int 63 [0x3f])))) (clobber (reg:CC 17 flags)) ]) and in the latter case (parallel [ (set (reg/v:SI 85 [ x ]) (minus:SI (const_int 32 [0x20]) (xor:SI (minus:SI (const_int 31 [0x1f]) (clz:SI (reg/v:SI 85 [ x ]))) (const_int 31 [0x1f])))) (clobber (reg:CC 17 flags)) ]) so we just need to split it - into bsr and add 1 (of course only if !TARGET_LZCNT, otherwise it is defined even for 0 and we don't know that it will be UB on zero). For the #c0 findLastSet we don't know that, but again we should know that it is UB at zero when !TARGET_LZCNT and so we could first split (set (reg:DI 87 [ _1 ]) (xor:DI (sign_extend:DI (minus:SI (const_int 63 [0x3f]) (subreg:SI (clz:DI (reg/v:DI 85 [ x ])) 0))) (const_int 63 [0x3f]))) into bsr and xor (i.e. get rid of the sign extension, if it is UB at zero, then we know clz will be in range 0 to 63 and sign extension or zero extension are the same and performed already when using the bs{q,l} instruction.