https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128545

--- Comment #4 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
Completely untested patch:
--- a/gcc/internal-fn.cc        2026-11-07 19:36:19.412932623 +0100
+++ b/gcc/internal-fn.cc        2026-11-07 20:22:08.441387193 +0100
@@ -6090,6 +6090,56 @@ expand_clmul (machine_mode mode, machine
          delete_insns_since (start);
        }
     }
+  if (CONST_SCALAR_INT_P (op1))
+    {
+      wide_int wop1 = rtx_mode_t (op1, op_mode);
+      unsigned lz = wi::clz (wop1);
+      unsigned tz = wi::ctz (wop1);
+      unsigned cnt = wi::popcount (wop1);
+      if (lz + cnt + tz == op_prec
+         && (pow2p_hwi (cnt) || (lz == 0 && prec == op_prec)))
+       {
+         /* Optimize the cases where one argument is a constant
+            equal to ((((T) 1) << M) - 1) << N for some M and N
+            constants.
+            In this case the clmul (x, ((((T) 1) << M) - 1) << N)
+            result can be computed as
+            T r = x;
+            for (unsigned i = 1; i < M; i = i + i)
+              r ^= x << i;
+            return r << N;
+            If M is not a power of two, it shouldn't be a widening
+            shift and most significant bit of the constant should be
+            set.  */
+         rtx x = convert_modes (mode, op_mode, op0, 1);
+         rtx res = gen_reg_rtx (mode);
+         emit_move_insn (res, x);
+         rtx i = gen_reg_rtx (TYPE_MODE (integer_type_node));
+         emit_move_insn (i, const0_rtx);
+         rtx_code_label *loop_label = gen_label_rtx ();
+         emit_label (loop_label);
+         rtx tem = expand_variable_shift (LSHIFT_EXPR, mode, x,
+                                          make_tree (integer_type_node, i),
+                                          NULL_RTX, 1);
+         tem = expand_binop (mode, xor_optab, res, tem, res, 1, olw);
+         if (tem != res)
+           emit_move_insn (res, tem);
+         tem = expand_binop (GET_MODE (i), add_optab, i, i, i, 1, olw);
+         if (tem != res)
+           emit_move_insn (i, tem);
+         do_compare_rtx_and_jump (i, gen_int_mode (cnt, GET_MODE (i)),
+                                  LTU, true, GET_MODE (i), NULL_RTX, NULL,
+                                  loop_label,
+                                  profile_probability::very_likely ());
+         if (tz)
+           {
+             tem = expand_shift (LSHIFT_EXPR, mode, res, tz, res, 1);
+             if (tem != res)
+               emit_move_insn (res, tem);
+           }
+         return res;
+       }
+    }
   /* Fallback using Kronecker substitution.
      Depending on the number of active bits in the operand with smaller
      number of those and the result precision we choose smallest spacing

Reply via email to