diff --git a/valgrind-3.15.0-s390x-HRcVec128.patch b/valgrind-3.15.0-s390x-HRcVec128.patch
new file mode 100644
index 0000000..c0a5202
--- /dev/null
+++ b/valgrind-3.15.0-s390x-HRcVec128.patch
@@ -0,0 +1,153 @@
+From abbd80e7e23deb1f27fb50410c8b3a3f7cdfb646 Mon Sep 17 00:00:00 2001
+From: Andreas Arnez <arnez@linux.ibm.com>
+Date: Wed, 12 Feb 2020 14:13:55 +0100
+Subject: [PATCH] Bug 417452 - s390_insn_store_emit: dst->tag for HRcVec128
+
+It was seen that the s390 instruction selector chose a wrong addressing
+mode for storing a vector register.  The VST instruction only handles
+short (12-bit unsigned) displacements, but a long (20-bit signed)
+displacement was generated instead, resulting in a panic:
+
+vex: the `impossible' happened:
+   s390_insn_store_emit: unknown dst->tag for HRcVec128
+
+The fix prevents long displacements for vector store operations.  It also
+optimizes vector store operations from an Iex_Get, by converting them to a
+memory copy.  This optimization was already performed for integer
+registers.
+---
+ VEX/priv/host_s390_defs.c |  2 +-
+ VEX/priv/host_s390_isel.c | 67 +++++++++++++++++++++++++++------------
+ 2 files changed, 48 insertions(+), 21 deletions(-)
+
+diff --git a/VEX/priv/host_s390_defs.c b/VEX/priv/host_s390_defs.c
+index 9ad7240c4..47928cbe1 100644
+--- a/VEX/priv/host_s390_defs.c
++++ b/VEX/priv/host_s390_defs.c
+@@ -6314,7 +6314,7 @@ s390_insn_memcpy(UChar size, s390_amode *dst, s390_amode *src)
+    insn->variant.memcpy.src = src;
+    insn->variant.memcpy.dst = dst;
+ 
+-   vassert(size == 1 || size == 2 || size == 4 || size == 8);
++   vassert(size == 1 || size == 2 || size == 4 || size == 8 || size == 16);
+ 
+    return insn;
+ }
+diff --git a/VEX/priv/host_s390_isel.c b/VEX/priv/host_s390_isel.c
+index 97614c873..fff81fe0f 100644
+--- a/VEX/priv/host_s390_isel.c
++++ b/VEX/priv/host_s390_isel.c
+@@ -302,12 +302,14 @@ ulong_fits_signed_8bit(ULong val)
+    return val == v;
+ }
+ 
+-/* EXPR is an expression that is used as an address. Return an s390_amode
+-   for it. If select_b12_b20_only is true the returned amode must be either
+-   S390_AMODE_B12 or S390_AMODE_B20. */
++/* EXPR is an expression that is used as an address. Return an s390_amode for
++   it. If no_index is true the returned amode must be either S390_AMODE_B12 or
++   S390_AMODE_B20. If short_displacement is true it must be either
++   S390_AMODE_B12 or S390_AMODE_BX12. */
+ static s390_amode *
+ s390_isel_amode_wrk(ISelEnv *env, IRExpr *expr,
+-                    Bool select_b12_b20_only __attribute__((unused)))
++                    Bool no_index __attribute__((unused)),
++                    Bool short_displacement)
+ {
+    if (expr->tag == Iex_Binop && expr->Iex.Binop.op == Iop_Add64) {
+       IRExpr *arg1 = expr->Iex.Binop.arg1;
+@@ -328,7 +330,7 @@ s390_isel_amode_wrk(ISelEnv *env, IRExpr *expr,
+          if (ulong_fits_unsigned_12bit(value)) {
+             return s390_amode_b12((Int)value, s390_isel_int_expr(env, arg1));
+          }
+-         if (ulong_fits_signed_20bit(value)) {
++         if (!short_displacement && ulong_fits_signed_20bit(value)) {
+             return s390_amode_b20((Int)value, s390_isel_int_expr(env, arg1));
+          }
+       }
+@@ -348,7 +350,25 @@ s390_isel_amode(ISelEnv *env, IRExpr *expr)
+    /* Address computation should yield a 64-bit value */
+    vassert(typeOfIRExpr(env->type_env, expr) == Ity_I64);
+ 
+-   am = s390_isel_amode_wrk(env, expr, /* B12, B20 only */ False);
++   am = s390_isel_amode_wrk(env, expr, False, False);
++
++   /* Check post-condition */
++   vassert(s390_amode_is_sane(am));
++
++   return am;
++}
++
++/* Sometimes we need an amode with short (12-bit) displacement. An example is
++   the vector-store opcode. */
++static s390_amode *
++s390_isel_amode_short(ISelEnv *env, IRExpr *expr)
++{
++   s390_amode *am;
++
++   /* Address computation should yield a 64-bit value */
++   vassert(typeOfIRExpr(env->type_env, expr) == Ity_I64);
++
++   am = s390_isel_amode_wrk(env, expr, False, True);
+ 
+    /* Check post-condition */
+    vassert(s390_amode_is_sane(am));
+@@ -379,7 +399,7 @@ s390_isel_amode_b12_b20(ISelEnv *env, IRExpr *expr)
+    /* Address computation should yield a 64-bit value */
+    vassert(typeOfIRExpr(env->type_env, expr) == Ity_I64);
+ 
+-   am = s390_isel_amode_wrk(env, expr, /* B12, B20 only */ True);
++   am = s390_isel_amode_wrk(env, expr, True, False);
+ 
+    /* Check post-condition */
+    vassert(s390_amode_is_sane(am) &&
+@@ -4727,7 +4747,26 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
+ 
+       if (stmt->Ist.Store.end != Iend_BE) goto stmt_fail;
+ 
+-      am = s390_isel_amode(env, stmt->Ist.Store.addr);
++      if (tyd == Ity_V128) {
++         am = s390_isel_amode_short(env, stmt->Ist.Store.addr);
++      } else {
++         am = s390_isel_amode(env, stmt->Ist.Store.addr);
++      }
++
++      /* Check whether we can use a memcpy. Currently, the restriction
++         is that both amodes need to be B12, so MVC can be emitted.
++         We do not consider a store whose data expression is a load because
++         we don't want to deal with overlapping locations. */
++      /* store(get) never overlaps*/
++      if (am->tag == S390_AMODE_B12 &&
++          stmt->Ist.Store.data->tag == Iex_Get) {
++         UInt offset = stmt->Ist.Store.data->Iex.Get.offset;
++         s390_amode *from = s390_amode_for_guest_state(offset);
++         if (from->tag == S390_AMODE_B12) {
++            addInstr(env, s390_insn_memcpy(sizeofIRType(tyd), am, from));
++            return;
++         }
++      }
+ 
+       switch (tyd) {
+       case Ity_I8:
+@@ -4742,18 +4781,6 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
+             addInstr(env, s390_insn_mimm(sizeofIRType(tyd), am, value));
+             return;
+          }
+-         /* Check whether we can use a memcpy here. Currently, the restriction
+-            is that both amodes need to be B12, so MVC can be emitted.
+-            We do not consider a store whose data expression is a load because
+-            we don't want to deal with overlapping locations. */
+-         /* store(get) never overlaps*/
+-         if (am->tag == S390_AMODE_B12 &&
+-             stmt->Ist.Store.data->tag == Iex_Get) {
+-            UInt offset = stmt->Ist.Store.data->Iex.Get.offset;
+-            s390_amode *from = s390_amode_for_guest_state(offset);
+-            addInstr(env, s390_insn_memcpy(sizeofIRType(tyd), am, from));
+-            return;
+-         }
+          /* General case: compile data into a register */
+          src = s390_isel_int_expr(env, stmt->Ist.Store.data);
+          break;
+-- 
+2.23.0
+
diff --git a/valgrind.spec b/valgrind.spec
index 8573d78..e3b2491 100644
--- a/valgrind.spec
+++ b/valgrind.spec
@@ -3,7 +3,7 @@
 Summary: Tool for finding memory management bugs in programs
 Name: %{?scl_prefix}valgrind
 Version: 3.15.0
-Release: 15%{?dist}
+Release: 16%{?dist}
 Epoch: 1
 License: GPLv2+
 URL: http://www.valgrind.org/
@@ -149,6 +149,9 @@ Patch23: valgrind-3.15.0-gcc10-ppc64-asm-constraints.patch
 # KDE#416301 s390x: "compare and signal" not supported
 Patch24: valgrind-3.15.0-s390x-compare-and-signal.patch
 
+# KDE#417452 s390_insn_store_emit: dst->tag for HRcVec128
+Patch25: valgrind-3.15.0-s390x-HRcVec128.patch
+
 BuildRequires: glibc-devel
 
 %if %{build_openmpi}
@@ -313,6 +316,7 @@ Valgrind User Manual for details.
 %patch22 -p1
 %patch23 -p1
 %patch24 -p1
+%patch25 -p1
 
 %build
 
@@ -533,6 +537,9 @@ fi
 %endif
 
 %changelog
+* Wed Jan 29 2020 Mark Wielaard <mjw@fedoraproject.org> - 3.15.0-16
+- Add valgrind-3.15.0-s390x-HRcVec128.patch
+
 * Wed Jan 29 2020 Mark Wielaard <mjw@fedoraproject.org> - 3.15.0-15
 - Don't use valgrind-3.15.0-ptrace-siginfo.patch on ppc64[le]
 - Add valgrind-3.15.0-s390x-compare-and-signal.patch