diff --git a/gcc/match.pd b/gcc/match.pd
index ec00347a968..4fca75d6fb6 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -12532,6 +12532,15 @@ and,
       && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0)))
   (convert @0)))
 
+/* VEC_PERM_EXPR of two results of the same unary operation needs only one
+   such operation, applied to the permuted vector.  The selector and both
+   vector types are unchanged, so the permute itself costs the same, and
+   the lanes that the permute drops are no longer computed.  */
+(for uop (negate bit_not abs absu)
+ (simplify
+  (vec_perm (uop:s @0) (uop:s @1) @2)
+  (uop (vec_perm @0 @1 @2))))
+
 /* Optimize
    c1 = VEC_PERM_EXPR (a, a, mask)
    c2 = VEC_PERM_EXPR (b, b, mask)
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vec-perm-unary-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vec-perm-unary-1.c
new file mode 100644
index 00000000000..7ff2498a6ce
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vec-perm-unary-1.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* A permute of two results of the same unary operation needs only one such
+   operation, applied to the permuted vector.  */
+
+typedef float v4f __attribute__((vector_size (16)));
+typedef int   v4i __attribute__((vector_size (16)));
+
+v4f
+f (v4f a, v4f b)
+{
+  v4i m = { 3, 6, 1, 4 };
+  return __builtin_shuffle (-a, -b, m);
+}
+
+v4i
+g (v4i a, v4i b)
+{
+  v4i m = { 0, 4, 1, 5 };
+  return __builtin_shuffle (~a, ~b, m);
+}
+
+v4i
+h (v4i a, v4i b)
+{
+  v4i m = { 0, 4, 1, 5 };
+  return __builtin_shuffle (-a, -b, m);
+}
+
+/* The absolute value written as a sign mask folds to ABS_EXPR first, so
+   this exercises the abs case of the same rule.  */
+v4i
+k (v4i a, v4i b)
+{
+  v4i m = { 0, 4, 1, 5 };
+  v4i sa = a >> 31, sb = b >> 31;
+  return __builtin_shuffle ((a ^ sa) - sa, (b ^ sb) - sb, m);
+}
+
+/* { dg-final { scan-tree-dump-times "VEC_PERM_EXPR" 4 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "ABS_EXPR" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not " = -a" "optimized" } } */
+/* { dg-final { scan-tree-dump-not " = ~a" "optimized" } } */
