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;
+}
+