[x86,SSE,v2] More uses of insertps to implement VEC_PERM_EXPR.

Message ID 002701dd3ad5$a210d4c0$e6327e40$@nextmovesoftware.com
State New
Headers
Series [x86,SSE,v2] More uses of insertps to implement VEC_PERM_EXPR. |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap success Build passed

Commit Message

Roger Sayle Sept. 2, 2026, 12:21 p.m. UTC
  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?


2026-09-02  Roger Sayle  <roger@nextmovesoftware.com>
            Hongtao Liu  <hongtao.liu@intel.com>

gcc/ChangeLog
        * config/i386/sse.md (sse4_1_insertps_<mode>_perm_0): New
        define_insn to implement V4SI or V4SF permute using insertps.
        (sse4_1_insertps_<mode>_perm_1): Likewise.
        (sse4_1_insertps_<mode>_perm_2): Likewise.
        (sse4_1_insertps_<mode>_perm_3): Likewise.

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 <crazylht@gmail.com>
> Sent: 26 August 2026 07:42
> To: Roger Sayle <roger@nextmovesoftware.com>
> Cc: Patches GCC <gcc-patches@gcc.gnu.org>; Liu, Hongtao
> <hongtao.liu@intel.com>; Uros Bizjak <ubizjak@gmail.com>
> 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 <roger@nextmovesoftware.com>
> 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.
> 
> ┌─────────┬─────────┬─────────┬─────────────────────┬────────
> ────────────────┐
> │ pattern │ dst elt │ count_d │ zmask = 15 ^ (1<<d) │          term
>       │
> ├─────────┼─────────┼─────────┼─────────────────────┼───────
> ─────────────────┤
> │ perm_0  │ 0       │ 0       │ 14                  │ + 14 (already
> correct) │
> ├─────────┼─────────┼─────────┼─────────────────────┼───────
> ─────────────────┤
> │ perm_1  │ 1       │ 16      │ 13                  │ + (16 + 13) = 29
>       │
> ├─────────┼─────────┼─────────┼─────────────────────┼───────
> ─────────────────┤
> │ perm_2  │ 2       │ 32      │ 11                  │ + (32 + 11) = 43
>       │
> ├─────────┼─────────┼─────────┼─────────────────────┼───────
> ─────────────────┤
> │ perm_3  │ 3       │ 48      │ 7                   │ + (48 + 7) = 55
>       │
> └─────────┴─────────┴─────────┴─────────────────────┴────────
> ────────────────┘
> 
> --
> BR,
> Hongtao
  

Comments

Hongtao Liu Sept. 3, 2026, 5:25 a.m. UTC | #1
On Wed, Sep 2, 2026 at 8:21 PM Roger Sayle <roger@nextmovesoftware.com> 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)

>
>
> Thanks again.
> Roger
> --
>
> > -----Original Message-----
> > From: Hongtao Liu <crazylht@gmail.com>
> > Sent: 26 August 2026 07:42
> > To: Roger Sayle <roger@nextmovesoftware.com>
> > Cc: Patches GCC <gcc-patches@gcc.gnu.org>; Liu, Hongtao
> > <hongtao.liu@intel.com>; Uros Bizjak <ubizjak@gmail.com>
> > 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 <roger@nextmovesoftware.com>
> > 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.
> >
> > ┌─────────┬─────────┬─────────┬─────────────────────┬────────
> > ────────────────┐
> > │ pattern │ dst elt │ count_d │ zmask = 15 ^ (1<<d) │          term
> >       │
> > ├─────────┼─────────┼─────────┼─────────────────────┼───────
> > ─────────────────┤
> > │ perm_0  │ 0       │ 0       │ 14                  │ + 14 (already
> > correct) │
> > ├─────────┼─────────┼─────────┼─────────────────────┼───────
> > ─────────────────┤
> > │ perm_1  │ 1       │ 16      │ 13                  │ + (16 + 13) = 29
> >       │
> > ├─────────┼─────────┼─────────┼─────────────────────┼───────
> > ─────────────────┤
> > │ perm_2  │ 2       │ 32      │ 11                  │ + (32 + 11) = 43
> >       │
> > ├─────────┼─────────┼─────────┼─────────────────────┼───────
> > ─────────────────┤
> > │ perm_3  │ 3       │ 48      │ 7                   │ + (48 + 7) = 55
> >       │
> > └─────────┴─────────┴─────────┴─────────────────────┴────────
> > ────────────────┘
> >
> > --
> > BR,
> > Hongtao



-- 
BR,
Hongtao
  

Patch

diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 36c8c255832..757f2ba0770 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -13059,6 +13059,133 @@ 
    (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_0"
+  [(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 [(match_operand:SI 3 "const_0_to_3_operand")
+		     (const_int 5)
+		     (const_int 6)
+		     (const_int 7)])))]
+  "TARGET_SSE4_1"
+{
+  if (TARGET_AVX10_2 && operands[3] == const0_rtx)
+    return "vmovd\t{%1, %0|%0, %1}";
+  operands[3] = GEN_INT ((INTVAL (operands[3]) << 6) + 14);
+  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")])
+
+(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) + (16 + 13));
+  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")])
+
+(define_insn "*sse4_1_insertps_<mode>_perm_2"
+  [(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)
+		     (const_int 5)
+		     (match_operand:SI 3 "const_0_to_3_operand")
+		     (const_int 7)])))]
+  "TARGET_SSE4_1"
+{
+  operands[3] = GEN_INT ((INTVAL (operands[3]) << 6) + (32 + 11));
+  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")])
+
+(define_insn "*sse4_1_insertps_<mode>_perm_3"
+  [(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)
+		     (const_int 5)
+		     (const_int 6)
+		     (match_operand:SI 3 "const_0_to_3_operand")])))]
+  "TARGET_SSE4_1"
+{
+  operands[3] = GEN_INT ((INTVAL (operands[3]) << 6) + (48 + 7));
+  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")])
+
 (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;
+}
+