s390x: fpr - gpr transfer facility
We need to introduce a new hwcap to model the presence of the fpr - gpr
transfer facility. If it is not available, we cannot use the LDGR and LGDR
insns and need to use a trick similar to what ppc does (write/read stack
location).
Fixes #268619 (vex side).
(Florian Krohm, britzel@acm.org)



git-svn-id: svn://svn.valgrind.org/vex/trunk@2131 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/host_s390_defs.c b/priv/host_s390_defs.c
index 17cc737..7524516 100644
--- a/priv/host_s390_defs.c
+++ b/priv/host_s390_defs.c
@@ -2794,7 +2794,8 @@
 static UChar *
 s390_emit_LDGR(UChar *p, UChar r1, UChar r2)
 {
-   /* fixs390: PR 268619 */
+   vassert(s390_host_has_fgx);
+
    if (unlikely(vex_traceflags & VEX_TRACE_ASM))
       s390_disasm(ENC3(MNM, FPR, GPR), "ldgr", r1, r2);
 
@@ -2805,7 +2806,8 @@
 static UChar *
 s390_emit_LGDR(UChar *p, UChar r1, UChar r2)
 {
-   /* fixs390: PR 268619 */
+   vassert(s390_host_has_fgx);
+
    if (unlikely(vex_traceflags & VEX_TRACE_ASM))
       s390_disasm(ENC3(MNM, GPR, FPR), "lgdr", r1, r2);
 
@@ -3875,6 +3877,55 @@
    return s390_emit_CLR(p, r1, R0);
 }
 
+
+static UChar *
+s390_emit_LGDRw(UChar *p, UChar r1, UChar r2)
+{
+   if (s390_host_has_fgx) {
+      return s390_emit_LGDR(p, r1, r2);
+   }
+
+   /* Store the FPR at memory[sp - 8]. This is safe because SP grows towards
+      smaller addresses and is 8-byte aligned. Then load the GPR from that
+      memory location/ */
+   if (s390_host_has_ldisp) {
+      p = s390_emit_STDY(p, r2, R0, S390_REGNO_STACK_POINTER, DISP20(-8));
+      return s390_emit_LG(p, r1, R0, S390_REGNO_STACK_POINTER, DISP20(-8));
+   }
+
+   /* No long displacement. Need to adjust SP explicitly as to avoid negative
+      displacements. */
+   p = s390_emit_AGHI(p, S390_REGNO_STACK_POINTER, -8);
+   p = s390_emit_STD(p, r2, R0, S390_REGNO_STACK_POINTER, 0);
+   p = s390_emit_LG(p, r1, R0, S390_REGNO_STACK_POINTER, DISP20(0));
+   return s390_emit_AGHI(p, S390_REGNO_STACK_POINTER, 8);
+}
+
+
+static UChar *
+s390_emit_LDGRw(UChar *p, UChar r1, UChar r2)
+{
+   if (s390_host_has_fgx) {
+      return s390_emit_LDGR(p, r1, r2);
+   }
+
+   /* Store the GPR at memory[sp - 8]. This is safe because SP grows towards
+      smaller addresses and is 8-byte aligned. Then load the FPR from that
+      memory location/ */
+   if (s390_host_has_ldisp) {
+      p = s390_emit_STG(p, r2, R0, S390_REGNO_STACK_POINTER, DISP20(-8));
+      return s390_emit_LDY(p, r1, R0, S390_REGNO_STACK_POINTER, DISP20(-8));
+   }
+
+   /* No long displacement. Need to adjust SP explicitly as to avoid negative
+      displacements. */
+   p = s390_emit_AGHI(p, S390_REGNO_STACK_POINTER, -8);
+   p = s390_emit_STG(p, r2, R0, S390_REGNO_STACK_POINTER, DISP20(0));
+   p = s390_emit_LD(p, r1, R0, S390_REGNO_STACK_POINTER, 0);
+   return s390_emit_AGHI(p, S390_REGNO_STACK_POINTER, 8);
+}
+
+
 /* Split up a 20-bit displacement into its high and low piece
    suitable for passing as function arguments */
 #define DISP20(d) ((d) & 0xFFF), (((d) >> 12) & 0xFF)
@@ -5051,9 +5102,9 @@
          return s390_emit_LDR(buf, dst, src);
    } else {
       if (dst_class == HRcFlt64 && src_class == HRcInt64)
-         return s390_emit_LDGR(buf, dst, src);   /* fixs390: PR 268619 */
+         return s390_emit_LDGRw(buf, dst, src);
       if (dst_class == HRcInt64 && src_class == HRcFlt64)
-         return s390_emit_LGDR(buf, dst, src);   /* fixs390: PR 268619 */
+         return s390_emit_LGDRw(buf, dst, src);
       /* A move between floating point registers and general purpose
          registers of different size should never occur and indicates
          an error elsewhere. */