Hi Hongtao,
Many thanks for the review, and fantastic suggestions.  I initially
implemented your first suggestion, of using const_4_to_7 as a
predicate in the four patterns, but while waiting for the bootstrap
and regression test on this (v3), I experimented with your alternate
proposal to use a new insertps_parallel predicate, that allows us
to combine everything conveniently into a single define_insn, and
also by reusing this predicate, allowed me to implement a similar
improvement for SSE2 (&& !SSE4_1) using pairs of shifts (which
reduces register usage for default -O2 compilation).

This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with no new failures.  Ok for mainline?


2026-09-07  Roger Sayle  <[email protected]>
            Hongtao Liu  <[email protected]>

gcc/ChangeLog
        * config/i386/predicates.md (insertps_parallel): New predicate.
        * config/i386/sse.md (sse4_1_insertps_<mode>_perm): New
        define_insn to implement V4SI or V4SF permute using insertps.
        (*sse2_insertps_<mode>_perm): Emulate the above insn on SSE2
        by splitting to one or two of pslldq, psrldq and pshufd.

gcc/testsuite/ChangeLog
        * gcc.target/i386/sse4_1-insertps-8.c: New test case.
        * gcc.target/i386/sse4_1-insertps-9.c: Likewise.
        * gcc.target/i386/vperm-v4sf-3-sse4.c: Likewise.
        * gcc.target/i386/vperm-v4sf-3.c: Likewise.
        * gcc.target/i386/vperm-v4si-3-sse4.c: Likewise.
        * gcc.target/i386/vperm-v4si-3.c: Likewise.


Thanks again,
Roger
--

> -----Original Message-----
> From: Hongtao Liu <[email protected]>
> Sent: 03 September 2026 06:25
> To: Roger Sayle <[email protected]>
> Cc: Patches GCC <[email protected]>; Liu, Hongtao
> <[email protected]>; Uros Bizjak <[email protected]>
> Subject: Re: [x86 SSE PATCH v2] More uses of insertps to implement
> VEC_PERM_EXPR.
> 
> On Wed, Sep 2, 2026 at 8:21 PM Roger Sayle <[email protected]>
> wrote:
> >
> > Hi Hongtao,
> > Very many thanks for the review/feedback, and great catch on my
> > mistake with the insertps immediate encoding.  This revised patch
> > implements all of your suggested changes (fixing the regression of
> > avx10_2-vmovd-1.c) and I've also added execution tests to confirm the
> > correct encoding on sse4.1, and also with the default/prevailing
> > options.
> >
> > This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
> > and make -k check, both with and without --target_board=unix{-m32}
> > with no new failures.  Ok for mainline?
> >
> >
> +  (parallel [(match_operand:SI 3 "const_0_to_3_operand")
> +     (const_int 5)
> +     (const_int 6)
> +     (const_int 7)])))]
> +  "TARGET_SSE4_1"
> 
> We can use const_4_to_7_operand instead of hard-coded const_int 5, const_int 
> 6,
> const_int 7 since they are equivalent const0.
> .i.e below testcase still generate pxor + 2 shufps, but it should generate 
> insertps
> as your i21 in your new test(0, x[2], 0, 0)
> 
> typedef int v4si __attribute__((vector_size(16)));
> 
> v4si
> foo (v4si x)
> {
>     return __builtin_shuffle (x, (v4si){0, 0, 0, 0}, (v4si){7, 2, 4, 5}); }
> 
> So either replace them with const_4_to_7 for all three patterns in the 
> parallel or
> maybe just define an new match_parallel predicate for them(.i.e
> pmovzx_parallel)
> 
> > > -----Original Message-----
> > > From: Hongtao Liu <[email protected]>
> > > Sent: 26 August 2026 07:42
> > > To: Roger Sayle <[email protected]>
> > > Cc: Patches GCC <[email protected]>; Liu, Hongtao
> > > <[email protected]>; Uros Bizjak <[email protected]>
> > > Subject: Re: [x86 SSE PATCH] More uses of insertps to implement
> > > VEC_PERM_EXPR.
> > >
> > > On Wed, Aug 26, 2026 at 4:25 AM Roger Sayle
> > > <[email protected]>
> > > wrote:
> > >
> > > > Alas this patch causes a single regression: avx10_2-vmovd-1.c.
> > > > AVX 10.2's vmovd (like pslldq+psrldq) can also be used to perform
> > > > {x[0],0,0,0}.  I was hoping Hongtao, Uros or HJ, can suggest the
> > > > right way to allow reload to select the most appropriate form for
> > > > the target ISA.
> > > I think you can just emit vmovd\t{%1, %0|%0, %1} from perm_0 when
> > > INTVAL
> > > (operands[3]) == 0 && TARGET_AVX10_2,
> > >
> > > +(define_insn "*sse4_1_insertps_<mode>_perm_1"
> > > +  [(set (match_operand:VI4F_128 0 "register_operand" "=x,v")
> > > + (vec_select:VI4F_128
> > > +  (vec_concat:<ssedoublevecmode>
> > > +    (match_operand:VI4F_128 1 "register_operand" "0,v")
> > > +    (match_operand:VI4F_128 2 "const0_operand"))
> > > +  (parallel [(const_int 4)
> > > +     (match_operand:SI 3 "const_0_to_3_operand")
> > > +     (const_int 6)
> > > +     (const_int 7)])))]
> > > +  "TARGET_SSE4_1"
> > > +{
> > > +  operands[3] = GEN_INT ((INTVAL (operands[3]) << 6) + (32 + 13));
> > >
> > > Should it be (16 + 13), not (32 + 13), similar for perm_2 (32 + 11),
> > > perm_3 (48 + 7)?
> > >
> > > According to SMD:
> > > IF (SRC = REG) THEN COUNT_S := imm8[7:6]
> > >     ELSE COUNT_S := 0
> > > COUNT_D := imm8[5:4]
> > > ZMASK := imm8[3:0]
> > >
> > > so INTVAL (operands[3]) << 6) is correct, 13 is correct, but count_d
> > > is imm[5:4], should be << 4 which is 16, not 32.
> > > BR,
> > > Hongtao
> BR,
> Hongtao
diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
index 289bf807e11..d295edd023d 100644
--- a/gcc/config/i386/predicates.md
+++ b/gcc/config/i386/predicates.md
@@ -2047,6 +2047,30 @@
   return true;
 })
 
+;; Return true if OP is a parallel for an insertps vec_select,
+;; where one of the two operands of the vec_concat is const0_operand.
+(define_predicate "insertps_parallel"
+  (and (match_code "parallel")
+       (match_code "const_int" "a"))
+{
+  int i;
+
+  if (XVECLEN (op, 0) != 4)
+    return false;
+
+  /* One element in [0..3], and the other 3 in [4..7].  */
+  bool found = false;
+  for (i = 0; i < 4; ++i)
+    if (INTVAL (XVECEXP (op, 0, i)) < 4)
+      {
+       if (found)
+         return false;
+       found = true;
+      }
+
+  return found;
+})
+
 ;; Return true if OP is a const vector with duplicate value.
 (define_predicate "const_vector_duplicate_operand"
   (match_code "const_vector")
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index e4bc746dacc..04e7c027229 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -13087,6 +13087,111 @@
    (set_attr "prefix" "orig")
    (set_attr "mode" "V4SF")])
 
+;; Use sse4_1_insertps_v4s[if] to permute to a one non-zero vector.
+(define_insn "*sse4_1_insertps_<mode>_perm"
+  [(set (match_operand:VI4F_128 0 "register_operand" "=x,v")
+       (vec_select:VI4F_128
+         (vec_concat:<ssedoublevecmode>
+           (match_operand:VI4F_128 1 "register_operand" "0,v")
+           (match_operand:VI4F_128 2 "const0_operand"))
+         (match_parallel 3 "insertps_parallel"
+           [(match_operand 4 "const_int_operand")])))]
+  "TARGET_SSE4_1"
+{
+  rtx op3 = operands[3];
+  int src, dst;
+
+  for (dst = 0; dst < 4; dst++)
+    {
+      src = INTVAL (XVECEXP (op3, 0, dst));
+      if (src < 4)
+       break;
+    }
+
+  if (TARGET_AVX10_2 && src == 0 && dst == 0)
+    return "vmovd\t{%1, %0|%0, %1}";
+  operands[3] = GEN_INT ((src << 6) + (dst << 4) + (15 - (1 << dst)));
+  switch (which_alternative)
+    {
+    case 0:
+      return "insertps\t{%3, %1, %0|%0, %1, %3}";
+    case 1:
+      return "vinsertps\t{%3, %1, %1, %0|%0, %1, %1, %3}";
+    default:
+      gcc_unreachable ();
+    }
+}
+  [(set_attr "isa" "noavx,avx")
+   (set_attr "type" "sselog")
+   (set_attr "prefix_data16" "1,*")
+   (set_attr "prefix_extra" "1")
+   (set_attr "length_immediate" "1")
+   (set_attr "prefix" "orig,maybe_evex")
+   (set_attr "mode" "V4SF")])
+
+;; Emulate the above insn on SSE2 using two of pslldq, psrldq and pshufd.
+(define_insn_and_split "*sse2_insertps_<mode>_perm"
+  [(set (match_operand:VI4F_128 0 "register_operand")
+       (vec_select:VI4F_128
+         (vec_concat:<ssedoublevecmode>
+           (match_operand:VI4F_128 1 "register_operand")
+           (match_operand:VI4F_128 2 "const0_operand"))
+         (match_parallel 3 "insertps_parallel"
+           [(match_operand 4 "const_int_operand")])))]
+  "TARGET_SSE2 && !TARGET_SSE4_1 && ix86_pre_reload_split ()"
+  "#"
+  "&& 1"
+  [(const_int 0)]
+{
+  rtx op3 = operands[3];
+  int src, dst;
+
+  for (dst = 0; dst < 4; dst++)
+    {
+      src = INTVAL (XVECEXP (op3, 0, dst));
+      if (src < 4)
+       break;
+    }
+
+  if (src == 0 && dst == 3)
+    emit_insn (gen_vec_shl_<mode> (operands[0], operands[1], GEN_INT (96)));
+  else if (src == 3 && dst == 0)
+    emit_insn (gen_vec_shr_<mode> (operands[0], operands[1], GEN_INT (96)));
+  else
+    {
+      rtx tmp1 = gen_reg_rtx (<MODE>mode);
+      if (src == 0 || dst == 0)
+       {
+         emit_insn (gen_vec_shl_<mode> (tmp1, operands[1],
+                                        GEN_INT ((3 - src) * 32)));
+         emit_insn (gen_vec_shr_<mode> (operands[0], tmp1,
+                                        GEN_INT ((3 - dst) * 32)));
+       }
+      else
+       {
+         emit_insn (gen_vec_shr_<mode> (tmp1, operands[1],
+                                        GEN_INT (src * 32)));
+         if (src == 3 || dst == 3)
+           emit_insn (gen_vec_shl_<mode> (operands[0], tmp1,
+                                          GEN_INT (dst * 32)));
+         else /* (src == 1 || src == 2) && (dst == 1 || dst == 2).  */
+           {
+             rtx tmp2 = gen_lowpart (V4SImode, tmp1);
+             rtx tmp3 = <MODE>mode == V4SImode ? operands[0]
+                                               : gen_reg_rtx (V4SImode);
+             emit_insn (gen_sse2_pshufd_1 (tmp3, tmp2,
+                                           GEN_INT (3),
+                                           GEN_INT (dst == 1 ? 0 : 3),
+                                           GEN_INT (dst == 2 ? 0 : 3),
+                                           GEN_INT (3)));
+             if (<MODE>mode != V4SImode)
+               emit_move_insn (operands[0], gen_lowpart (<MODE>mode, tmp3));
+           }
+       }
+    }
+  DONE;
+})
+
 (define_split
   [(set (match_operand:VI4F_128 0 "memory_operand")
        (vec_merge:VI4F_128
diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-insertps-8.c 
b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-8.c
new file mode 100644
index 00000000000..5ba57600260
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-8.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msse4.1 -mno-avx" } */
+
+typedef int v4si __attribute__ ((__vector_size__ (16)));
+
+v4si i00(v4si x) { return (v4si){x[0],0,0,0}; }
+v4si i10(v4si x) { return (v4si){x[1],0,0,0}; }
+v4si i20(v4si x) { return (v4si){x[2],0,0,0}; }
+v4si i30(v4si x) { return (v4si){x[3],0,0,0}; }
+
+v4si i01(v4si x) { return (v4si){0,x[0],0,0}; }
+v4si i11(v4si x) { return (v4si){0,x[1],0,0}; }
+v4si i21(v4si x) { return (v4si){0,x[2],0,0}; }
+v4si i31(v4si x) { return (v4si){0,x[3],0,0}; }
+
+v4si i02(v4si x) { return (v4si){0,0,x[0],0}; }
+v4si i12(v4si x) { return (v4si){0,0,x[1],0}; }
+v4si i22(v4si x) { return (v4si){0,0,x[2],0}; }
+v4si i32(v4si x) { return (v4si){0,0,x[3],0}; }
+
+v4si i03(v4si x) { return (v4si){0,0,0,x[0]}; }
+v4si i13(v4si x) { return (v4si){0,0,0,x[1]}; }
+v4si i23(v4si x) { return (v4si){0,0,0,x[2]}; }
+v4si i33(v4si x) { return (v4si){0,0,0,x[3]}; }
+
+/* { dg-final { scan-assembler-times "\tv?insertps\t" 14 } } */
+/* { dg-final { scan-assembler-times "\tv?pslldq\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tv?psrldq\t" 1 } } */
+/* { dg-final { scan-assembler-not "\tv?movdqa\t" } } */
+/* { dg-final { scan-assembler-not "\tv?movss\t" } } */
+/* { dg-final { scan-assembler-not "\tv?pxor\t" } } */
diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-insertps-9.c 
b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-9.c
new file mode 100644
index 00000000000..76d25d724bd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-9.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msse4.1 -mno-avx" } */
+
+typedef float v4sf __attribute__ ((__vector_size__ (16)));
+
+v4sf f00(v4sf x) { return (v4sf){x[0],0.0f,0.0f,0.0f}; }
+v4sf f10(v4sf x) { return (v4sf){x[1],0.0f,0.0f,0.0f}; }
+v4sf f20(v4sf x) { return (v4sf){x[2],0.0f,0.0f,0.0f}; }
+v4sf f30(v4sf x) { return (v4sf){x[3],0.0f,0.0f,0.0f}; }
+
+v4sf f01(v4sf x) { return (v4sf){0.0f,x[0],0.0f,0.0f}; }
+v4sf f11(v4sf x) { return (v4sf){0.0f,x[1],0.0f,0.0f}; }
+v4sf f21(v4sf x) { return (v4sf){0.0f,x[2],0.0f,0.0f}; }
+v4sf f31(v4sf x) { return (v4sf){0.0f,x[3],0.0f,0.0f}; }
+
+v4sf f02(v4sf x) { return (v4sf){0.0f,0.0f,x[0],0.0f}; }
+v4sf f12(v4sf x) { return (v4sf){0.0f,0.0f,x[1],0.0f}; }
+v4sf f22(v4sf x) { return (v4sf){0.0f,0.0f,x[2],0.0f}; }
+v4sf f32(v4sf x) { return (v4sf){0.0f,0.0f,x[3],0.0f}; }
+
+v4sf f03(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[0]}; }
+v4sf f13(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[1]}; }
+v4sf f23(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[2]}; }
+v4sf f33(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[3]}; }
+
+/* { dg-final { scan-assembler-times "\tv?insertps\t" 14 } } */
+/* { dg-final { scan-assembler-times "\tv?pslldq\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tv?psrldq\t" 1 } } */
+/* { dg-final { scan-assembler-not "\tv?movdqa\t" } } */
+/* { dg-final { scan-assembler-not "\tv?movss\t" } } */
+/* { dg-final { scan-assembler-not "\tv?pxor\t" } } */
diff --git a/gcc/testsuite/gcc.target/i386/vperm-v4sf-3-sse4.c 
b/gcc/testsuite/gcc.target/i386/vperm-v4sf-3-sse4.c
new file mode 100644
index 00000000000..d79e5451390
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/vperm-v4sf-3-sse4.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-require-effective-target sse4 } */
+/* { dg-options "-O -msse4.1" } */
+#include "isa-check.h"
+#define CHECK_ISA
+#include "vperm-v4sf-3.c"
diff --git a/gcc/testsuite/gcc.target/i386/vperm-v4sf-3.c 
b/gcc/testsuite/gcc.target/i386/vperm-v4sf-3.c
new file mode 100644
index 00000000000..6ed939bef32
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/vperm-v4sf-3.c
@@ -0,0 +1,74 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef float v4sf __attribute__ ((__vector_size__ (16)));
+
+v4sf f00(v4sf x) { return (v4sf){x[0],0.0f,0.0f,0.0f}; }
+v4sf f10(v4sf x) { return (v4sf){x[1],0.0f,0.0f,0.0f}; }
+v4sf f20(v4sf x) { return (v4sf){x[2],0.0f,0.0f,0.0f}; }
+v4sf f30(v4sf x) { return (v4sf){x[3],0.0f,0.0f,0.0f}; }
+
+v4sf f01(v4sf x) { return (v4sf){0.0f,x[0],0.0f,0.0f}; }
+v4sf f11(v4sf x) { return (v4sf){0.0f,x[1],0.0f,0.0f}; }
+v4sf f21(v4sf x) { return (v4sf){0.0f,x[2],0.0f,0.0f}; }
+v4sf f31(v4sf x) { return (v4sf){0.0f,x[3],0.0f,0.0f}; }
+
+v4sf f02(v4sf x) { return (v4sf){0.0f,0.0f,x[0],0.0f}; }
+v4sf f12(v4sf x) { return (v4sf){0.0f,0.0f,x[1],0.0f}; }
+v4sf f22(v4sf x) { return (v4sf){0.0f,0.0f,x[2],0.0f}; }
+v4sf f32(v4sf x) { return (v4sf){0.0f,0.0f,x[3],0.0f}; }
+
+v4sf f03(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[0]}; }
+v4sf f13(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[1]}; }
+v4sf f23(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[2]}; }
+v4sf f33(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[3]}; }
+
+
+typedef v4sf (*fun_t)(v4sf);
+typedef struct {
+  fun_t fun;
+  float a0, a1, a2, a3;
+  float b0, b1, b2, b3;
+} test_t;
+
+const test_t tests[16] = {
+  { f00,  1.0f, 0.0f, 0.0f, 0.0f,  4.0f, 0.0f, 0.0f, 0.0f },
+  { f10,  2.0f, 0.0f, 0.0f, 0.0f,  3.0f, 0.0f, 0.0f, 0.0f },
+  { f20,  3.0f, 0.0f, 0.0f, 0.0f,  2.0f, 0.0f, 0.0f, 0.0f },
+  { f30,  4.0f, 0.0f, 0.0f, 0.0f,  1.0f, 0.0f, 0.0f, 0.0f },
+  { f01,  0.0f, 1.0f, 0.0f, 0.0f,  0.0f, 4.0f, 0.0f, 0.0f },
+  { f11,  0.0f, 2.0f, 0.0f, 0.0f,  0.0f, 3.0f, 0.0f, 0.0f },
+  { f21,  0.0f, 3.0f, 0.0f, 0.0f,  0.0f, 2.0f, 0.0f, 0.0f },
+  { f31,  0.0f, 4.0f, 0.0f, 0.0f,  0.0f, 1.0f, 0.0f, 0.0f },
+  { f02,  0.0f, 0.0f, 1.0f, 0.0f,  0.0f, 0.0f, 4.0f, 0.0f },
+  { f12,  0.0f, 0.0f, 2.0f, 0.0f,  0.0f, 0.0f, 3.0f, 0.0f },
+  { f22,  0.0f, 0.0f, 3.0f, 0.0f,  0.0f, 0.0f, 2.0f, 0.0f },
+  { f32,  0.0f, 0.0f, 4.0f, 0.0f,  0.0f, 0.0f, 1.0f, 0.0f },
+  { f03,  0.0f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f, 0.0f, 4.0f },
+  { f13,  0.0f, 0.0f, 0.0f, 2.0f,  0.0f, 0.0f, 0.0f, 3.0f },
+  { f23,  0.0f, 0.0f, 0.0f, 3.0f,  0.0f, 0.0f, 0.0f, 2.0f },
+  { f33,  0.0f, 0.0f, 0.0f, 4.0f,  0.0f, 0.0f, 0.0f, 1.0f }
+};
+
+int main()
+{
+#ifdef CHECK_ISA
+  check_isa ();
+#endif
+
+  int i;
+  v4sf a = (v4sf){ 1.0f, 2.0f, 3.0f, 4.0f };
+  v4sf b = (v4sf){ 4.0f, 3.0f, 2.0f, 1.0f };
+  for (i = 0; i < 16; i++) {
+  const test_t *p = &tests[i];
+    v4sf o1 = (*p->fun)(a);
+    if (o1[0] != p->a0 || o1[1] != p->a1 || o1[2] != p->a2 || o1[3] != p->a3)
+      __builtin_abort ();
+
+    v4sf o2 = (*p->fun)(b);
+    if (o2[0] != p->b0 || o2[1] != p->b1 || o2[2] != p->b2 || o2[3] != p->b3)
+      __builtin_abort ();
+  }
+  return 0;
+}
+
diff --git a/gcc/testsuite/gcc.target/i386/vperm-v4si-3-sse4.c 
b/gcc/testsuite/gcc.target/i386/vperm-v4si-3-sse4.c
new file mode 100644
index 00000000000..75cca2fc232
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/vperm-v4si-3-sse4.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-require-effective-target sse4 } */
+/* { dg-options "-O -msse4.1" } */
+#include "isa-check.h"
+#define CHECK_ISA
+#include "vperm-v4si-3.c"
diff --git a/gcc/testsuite/gcc.target/i386/vperm-v4si-3.c 
b/gcc/testsuite/gcc.target/i386/vperm-v4si-3.c
new file mode 100644
index 00000000000..2ad9bf1d7f0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/vperm-v4si-3.c
@@ -0,0 +1,73 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef int v4si __attribute__ ((__vector_size__ (16)));
+
+v4si i00(v4si x) { return (v4si){x[0],0,0,0}; }
+v4si i10(v4si x) { return (v4si){x[1],0,0,0}; }
+v4si i20(v4si x) { return (v4si){x[2],0,0,0}; }
+v4si i30(v4si x) { return (v4si){x[3],0,0,0}; }
+
+v4si i01(v4si x) { return (v4si){0,x[0],0,0}; }
+v4si i11(v4si x) { return (v4si){0,x[1],0,0}; }
+v4si i21(v4si x) { return (v4si){0,x[2],0,0}; }
+v4si i31(v4si x) { return (v4si){0,x[3],0,0}; }
+
+v4si i02(v4si x) { return (v4si){0,0,x[0],0}; }
+v4si i12(v4si x) { return (v4si){0,0,x[1],0}; }
+v4si i22(v4si x) { return (v4si){0,0,x[2],0}; }
+v4si i32(v4si x) { return (v4si){0,0,x[3],0}; }
+
+v4si i03(v4si x) { return (v4si){0,0,0,x[0]}; }
+v4si i13(v4si x) { return (v4si){0,0,0,x[1]}; }
+v4si i23(v4si x) { return (v4si){0,0,0,x[2]}; }
+v4si i33(v4si x) { return (v4si){0,0,0,x[3]}; }
+
+typedef v4si (*fun_t)(v4si);
+typedef struct {
+  fun_t fun;
+  int a0, a1, a2, a3;
+  int b0, b1, b2, b3;
+} test_t;
+
+const test_t tests[16] = {
+  { i00,  1, 0, 0, 0,  4, 0, 0, 0 },
+  { i10,  2, 0, 0, 0,  3, 0, 0, 0 },
+  { i20,  3, 0, 0, 0,  2, 0, 0, 0 },
+  { i30,  4, 0, 0, 0,  1, 0, 0, 0 },
+  { i01,  0, 1, 0, 0,  0, 4, 0, 0 },
+  { i11,  0, 2, 0, 0,  0, 3, 0, 0 },
+  { i21,  0, 3, 0, 0,  0, 2, 0, 0 },
+  { i31,  0, 4, 0, 0,  0, 1, 0, 0 },
+  { i02,  0, 0, 1, 0,  0, 0, 4, 0 },
+  { i12,  0, 0, 2, 0,  0, 0, 3, 0 },
+  { i22,  0, 0, 3, 0,  0, 0, 2, 0 },
+  { i32,  0, 0, 4, 0,  0, 0, 1, 0 },
+  { i03,  0, 0, 0, 1,  0, 0, 0, 4 },
+  { i13,  0, 0, 0, 2,  0, 0, 0, 3 },
+  { i23,  0, 0, 0, 3,  0, 0, 0, 2 },
+  { i33,  0, 0, 0, 4,  0, 0, 0, 1 }
+};
+
+int main()
+{
+#ifdef CHECK_ISA
+  check_isa ();
+#endif
+
+  int i;
+  v4si a = (v4si){ 1, 2, 3, 4 };
+  v4si b = (v4si){ 4, 3, 2, 1 };
+  for (i = 0; i < 16; i++) {
+  const test_t *p = &tests[i];
+    v4si o1 = (*p->fun)(a);
+    if (o1[0] != p->a0 || o1[1] != p->a1 || o1[2] != p->a2 || o1[3] != p->a3)
+      __builtin_abort ();
+
+    v4si o2 = (*p->fun)(b);
+    if (o2[0] != p->b0 || o2[1] != p->b1 || o2[2] != p->b2 || o2[3] != p->b3)
+      __builtin_abort ();
+  }
+  return 0;
+}
+

Reply via email to