f6cbdf
From abbd80e7e23deb1f27fb50410c8b3a3f7cdfb646 Mon Sep 17 00:00:00 2001
f6cbdf
From: Andreas Arnez <arnez@linux.ibm.com>
f6cbdf
Date: Wed, 12 Feb 2020 14:13:55 +0100
f6cbdf
Subject: [PATCH] Bug 417452 - s390_insn_store_emit: dst->tag for HRcVec128
f6cbdf
f6cbdf
It was seen that the s390 instruction selector chose a wrong addressing
f6cbdf
mode for storing a vector register.  The VST instruction only handles
f6cbdf
short (12-bit unsigned) displacements, but a long (20-bit signed)
f6cbdf
displacement was generated instead, resulting in a panic:
f6cbdf
f6cbdf
vex: the `impossible' happened:
f6cbdf
   s390_insn_store_emit: unknown dst->tag for HRcVec128
f6cbdf
f6cbdf
The fix prevents long displacements for vector store operations.  It also
f6cbdf
optimizes vector store operations from an Iex_Get, by converting them to a
f6cbdf
memory copy.  This optimization was already performed for integer
f6cbdf
registers.
f6cbdf
---
f6cbdf
 VEX/priv/host_s390_defs.c |  2 +-
f6cbdf
 VEX/priv/host_s390_isel.c | 67 +++++++++++++++++++++++++++------------
f6cbdf
 2 files changed, 48 insertions(+), 21 deletions(-)
f6cbdf
f6cbdf
diff --git a/VEX/priv/host_s390_defs.c b/VEX/priv/host_s390_defs.c
f6cbdf
index 9ad7240c4..47928cbe1 100644
f6cbdf
--- a/VEX/priv/host_s390_defs.c
f6cbdf
+++ b/VEX/priv/host_s390_defs.c
f6cbdf
@@ -6314,7 +6314,7 @@ s390_insn_memcpy(UChar size, s390_amode *dst, s390_amode *src)
f6cbdf
    insn->variant.memcpy.src = src;
f6cbdf
    insn->variant.memcpy.dst = dst;
f6cbdf
 
f6cbdf
-   vassert(size == 1 || size == 2 || size == 4 || size == 8);
f6cbdf
+   vassert(size == 1 || size == 2 || size == 4 || size == 8 || size == 16);
f6cbdf
 
f6cbdf
    return insn;
f6cbdf
 }
f6cbdf
diff --git a/VEX/priv/host_s390_isel.c b/VEX/priv/host_s390_isel.c
f6cbdf
index 97614c873..fff81fe0f 100644
f6cbdf
--- a/VEX/priv/host_s390_isel.c
f6cbdf
+++ b/VEX/priv/host_s390_isel.c
f6cbdf
@@ -302,12 +302,14 @@ ulong_fits_signed_8bit(ULong val)
f6cbdf
    return val == v;
f6cbdf
 }
f6cbdf
 
f6cbdf
-/* EXPR is an expression that is used as an address. Return an s390_amode
f6cbdf
-   for it. If select_b12_b20_only is true the returned amode must be either
f6cbdf
-   S390_AMODE_B12 or S390_AMODE_B20. */
f6cbdf
+/* EXPR is an expression that is used as an address. Return an s390_amode for
f6cbdf
+   it. If no_index is true the returned amode must be either S390_AMODE_B12 or
f6cbdf
+   S390_AMODE_B20. If short_displacement is true it must be either
f6cbdf
+   S390_AMODE_B12 or S390_AMODE_BX12. */
f6cbdf
 static s390_amode *
f6cbdf
 s390_isel_amode_wrk(ISelEnv *env, IRExpr *expr,
f6cbdf
-                    Bool select_b12_b20_only __attribute__((unused)))
f6cbdf
+                    Bool no_index __attribute__((unused)),
f6cbdf
+                    Bool short_displacement)
f6cbdf
 {
f6cbdf
    if (expr->tag == Iex_Binop && expr->Iex.Binop.op == Iop_Add64) {
f6cbdf
       IRExpr *arg1 = expr->Iex.Binop.arg1;
f6cbdf
@@ -328,7 +330,7 @@ s390_isel_amode_wrk(ISelEnv *env, IRExpr *expr,
f6cbdf
          if (ulong_fits_unsigned_12bit(value)) {
f6cbdf
             return s390_amode_b12((Int)value, s390_isel_int_expr(env, arg1));
f6cbdf
          }
f6cbdf
-         if (ulong_fits_signed_20bit(value)) {
f6cbdf
+         if (!short_displacement && ulong_fits_signed_20bit(value)) {
f6cbdf
             return s390_amode_b20((Int)value, s390_isel_int_expr(env, arg1));
f6cbdf
          }
f6cbdf
       }
f6cbdf
@@ -348,7 +350,25 @@ s390_isel_amode(ISelEnv *env, IRExpr *expr)
f6cbdf
    /* Address computation should yield a 64-bit value */
f6cbdf
    vassert(typeOfIRExpr(env->type_env, expr) == Ity_I64);
f6cbdf
 
f6cbdf
-   am = s390_isel_amode_wrk(env, expr, /* B12, B20 only */ False);
f6cbdf
+   am = s390_isel_amode_wrk(env, expr, False, False);
f6cbdf
+
f6cbdf
+   /* Check post-condition */
f6cbdf
+   vassert(s390_amode_is_sane(am));
f6cbdf
+
f6cbdf
+   return am;
f6cbdf
+}
f6cbdf
+
f6cbdf
+/* Sometimes we need an amode with short (12-bit) displacement. An example is
f6cbdf
+   the vector-store opcode. */
f6cbdf
+static s390_amode *
f6cbdf
+s390_isel_amode_short(ISelEnv *env, IRExpr *expr)
f6cbdf
+{
f6cbdf
+   s390_amode *am;
f6cbdf
+
f6cbdf
+   /* Address computation should yield a 64-bit value */
f6cbdf
+   vassert(typeOfIRExpr(env->type_env, expr) == Ity_I64);
f6cbdf
+
f6cbdf
+   am = s390_isel_amode_wrk(env, expr, False, True);
f6cbdf
 
f6cbdf
    /* Check post-condition */
f6cbdf
    vassert(s390_amode_is_sane(am));
f6cbdf
@@ -379,7 +399,7 @@ s390_isel_amode_b12_b20(ISelEnv *env, IRExpr *expr)
f6cbdf
    /* Address computation should yield a 64-bit value */
f6cbdf
    vassert(typeOfIRExpr(env->type_env, expr) == Ity_I64);
f6cbdf
 
f6cbdf
-   am = s390_isel_amode_wrk(env, expr, /* B12, B20 only */ True);
f6cbdf
+   am = s390_isel_amode_wrk(env, expr, True, False);
f6cbdf
 
f6cbdf
    /* Check post-condition */
f6cbdf
    vassert(s390_amode_is_sane(am) &&
f6cbdf
@@ -4727,7 +4747,26 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
f6cbdf
 
f6cbdf
       if (stmt->Ist.Store.end != Iend_BE) goto stmt_fail;
f6cbdf
 
f6cbdf
-      am = s390_isel_amode(env, stmt->Ist.Store.addr);
f6cbdf
+      if (tyd == Ity_V128) {
f6cbdf
+         am = s390_isel_amode_short(env, stmt->Ist.Store.addr);
f6cbdf
+      } else {
f6cbdf
+         am = s390_isel_amode(env, stmt->Ist.Store.addr);
f6cbdf
+      }
f6cbdf
+
f6cbdf
+      /* Check whether we can use a memcpy. Currently, the restriction
f6cbdf
+         is that both amodes need to be B12, so MVC can be emitted.
f6cbdf
+         We do not consider a store whose data expression is a load because
f6cbdf
+         we don't want to deal with overlapping locations. */
f6cbdf
+      /* store(get) never overlaps*/
f6cbdf
+      if (am->tag == S390_AMODE_B12 &&
f6cbdf
+          stmt->Ist.Store.data->tag == Iex_Get) {
f6cbdf
+         UInt offset = stmt->Ist.Store.data->Iex.Get.offset;
f6cbdf
+         s390_amode *from = s390_amode_for_guest_state(offset);
f6cbdf
+         if (from->tag == S390_AMODE_B12) {
f6cbdf
+            addInstr(env, s390_insn_memcpy(sizeofIRType(tyd), am, from));
f6cbdf
+            return;
f6cbdf
+         }
f6cbdf
+      }
f6cbdf
 
f6cbdf
       switch (tyd) {
f6cbdf
       case Ity_I8:
f6cbdf
@@ -4742,18 +4781,6 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
f6cbdf
             addInstr(env, s390_insn_mimm(sizeofIRType(tyd), am, value));
f6cbdf
             return;
f6cbdf
          }
f6cbdf
-         /* Check whether we can use a memcpy here. Currently, the restriction
f6cbdf
-            is that both amodes need to be B12, so MVC can be emitted.
f6cbdf
-            We do not consider a store whose data expression is a load because
f6cbdf
-            we don't want to deal with overlapping locations. */
f6cbdf
-         /* store(get) never overlaps*/
f6cbdf
-         if (am->tag == S390_AMODE_B12 &&
f6cbdf
-             stmt->Ist.Store.data->tag == Iex_Get) {
f6cbdf
-            UInt offset = stmt->Ist.Store.data->Iex.Get.offset;
f6cbdf
-            s390_amode *from = s390_amode_for_guest_state(offset);
f6cbdf
-            addInstr(env, s390_insn_memcpy(sizeofIRType(tyd), am, from));
f6cbdf
-            return;
f6cbdf
-         }
f6cbdf
          /* General case: compile data into a register */
f6cbdf
          src = s390_isel_int_expr(env, stmt->Ist.Store.data);
f6cbdf
          break;
f6cbdf
-- 
f6cbdf
2.23.0
f6cbdf