Bug 416301 - s390x: Support "compare and signal" instructions

Add VEX support for the s390x "compare and signal" instructions KEBR,
KDBR, KXBR, KEB, and KDB.  For now, let them behave exactly like their
non-signalling counterparts.  Enhance the bfp-4 test case to cover these
instructions as well.  Update the list of supported instructions in
s390-opcodes.csv.  Add a disclaimer to README.s390, explaining that FP
signalling is not handled accurately on s390x at the moment.
This commit is contained in:
Andreas Arnez 2020-01-16 13:49:10 +01:00
parent 82da9c1006
commit e83c28e10c
6 changed files with 257 additions and 115 deletions

1
NEWS
View File

@ -105,6 +105,7 @@ where XXXXXX is the bug number as listed below.
415757 vex x86->IR: 0x66 0xF 0xCE 0x4F (bswapw)
416239 valgrind crashes when handling clock_adjtime
416286 DRD reports "conflicting load" error on std::mutex::lock()
416301 s390x: "compare and signal" not supported
416387 finit_module and bpf syscalls are unhandled on arm64
416464 Fix false reports for uninitialized memory for PR_CAPBSET_READ/DROP
n-i-bz Fix minor one time leaks in dhat.

View File

@ -11,7 +11,10 @@ Limitations
-----------
- 31-bit client programs are not supported.
- Hexadecimal floating point is not supported.
- Transactional memory is not supported.
- Transactional memory is not supported. The transactional-execution
facility is masked off from HWCAP.
- FP signalling is not accurate. E.g., the "compare and signal"
instructions behave like their non-signalling counterparts.
- memcheck, cachegrind, drd, helgrind, massif, lackey, and none are
supported.
- On machine models predating z10, cachegrind will assume a z10 cache
@ -21,8 +24,6 @@ Limitations
- Some gcc versions use mvc to copy 4/8 byte values. This will affect
certain debug messages. For example, memcheck will complain about
4 one-byte reads/writes instead of just a single read/write.
- The transactional-execution facility is not supported; it is masked
off from HWCAP.
Hardware facilities

View File

@ -1204,6 +1204,16 @@ get_dpr_dw0(UInt archreg)
return IRExpr_Get(fpr_dw0_offset(archreg), Ity_D64);
}
/* Read a float of given type from an fpr. */
static IRExpr *
get_fpr_float(UInt archreg, IRType type)
{
if (type == Ity_F128)
return get_fpr_pair(archreg);
else
return IRExpr_Get(fpr_offset(archreg), type);
}
/*------------------------------------------------------------*/
/*--- gpr registers ---*/
/*------------------------------------------------------------*/
@ -14055,94 +14065,103 @@ s390_irgen_AXBR(UChar r1, UChar r2)
return "axbr";
}
/* Helper for "compare" insns CEBR, CDBR, CXBR, and their signalling
counterparts. */
static const HChar *
s390_irgen_CEBR(UChar r1, UChar r2)
s390_irgen_CxBR(const HChar *mnem, UChar r1, UChar r2, IRType type, IROp cmp_op)
{
IRTemp op1 = newTemp(Ity_F32);
IRTemp op2 = newTemp(Ity_F32);
IRTemp op1 = newTemp(type);
IRTemp op2 = newTemp(type);
IRTemp cc_vex = newTemp(Ity_I32);
IRTemp cc_s390 = newTemp(Ity_I32);
assign(op1, get_fpr_w0(r1));
assign(op2, get_fpr_w0(r2));
assign(cc_vex, binop(Iop_CmpF32, mkexpr(op1), mkexpr(op2)));
assign(op1, get_fpr_float(r1, type));
assign(op2, get_fpr_float(r2, type));
assign(cc_vex, binop(cmp_op, mkexpr(op1), mkexpr(op2)));
assign(cc_s390, convert_vex_bfpcc_to_s390(cc_vex));
s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
return mnem;
}
return "cebr";
static const HChar *
s390_irgen_CEBR(UChar r1, UChar r2)
{
return s390_irgen_CxBR("cebr", r1, r2, Ity_F32, Iop_CmpF32);
}
static const HChar *
s390_irgen_KEBR(UChar r1, UChar r2)
{
return s390_irgen_CxBR("kebr", r1, r2, Ity_F32, Iop_CmpF32);
}
static const HChar *
s390_irgen_CDBR(UChar r1, UChar r2)
{
IRTemp op1 = newTemp(Ity_F64);
IRTemp op2 = newTemp(Ity_F64);
IRTemp cc_vex = newTemp(Ity_I32);
IRTemp cc_s390 = newTemp(Ity_I32);
return s390_irgen_CxBR("cdbr", r1, r2, Ity_F64, Iop_CmpF64);
}
assign(op1, get_fpr_dw0(r1));
assign(op2, get_fpr_dw0(r2));
assign(cc_vex, binop(Iop_CmpF64, mkexpr(op1), mkexpr(op2)));
assign(cc_s390, convert_vex_bfpcc_to_s390(cc_vex));
s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
return "cdbr";
static const HChar *
s390_irgen_KDBR(UChar r1, UChar r2)
{
return s390_irgen_CxBR("kdbr", r1, r2, Ity_F64, Iop_CmpF64);
}
static const HChar *
s390_irgen_CXBR(UChar r1, UChar r2)
{
IRTemp op1 = newTemp(Ity_F128);
IRTemp op2 = newTemp(Ity_F128);
return s390_irgen_CxBR("cxbr", r1, r2, Ity_F128, Iop_CmpF128);
}
static const HChar *
s390_irgen_KXBR(UChar r1, UChar r2)
{
return s390_irgen_CxBR("kxbr", r1, r2, Ity_F128, Iop_CmpF128);
}
/* Helper for "compare" insns CEB, CDB, and their signalling counterparts. */
static const HChar *
s390_irgen_CxB(const HChar *mnem, UChar r1, IRTemp op2addr, IRType type,
IROp cmp_op)
{
IRTemp op1 = newTemp(type);
IRTemp op2 = newTemp(type);
IRTemp cc_vex = newTemp(Ity_I32);
IRTemp cc_s390 = newTemp(Ity_I32);
assign(op1, get_fpr_pair(r1));
assign(op2, get_fpr_pair(r2));
assign(cc_vex, binop(Iop_CmpF128, mkexpr(op1), mkexpr(op2)));
assign(op1, get_fpr_float(r1, type));
assign(op2, load(type, mkexpr(op2addr)));
assign(cc_vex, binop(cmp_op, mkexpr(op1), mkexpr(op2)));
assign(cc_s390, convert_vex_bfpcc_to_s390(cc_vex));
s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
return "cxbr";
return mnem;
}
static const HChar *
s390_irgen_CEB(UChar r1, IRTemp op2addr)
{
IRTemp op1 = newTemp(Ity_F32);
IRTemp op2 = newTemp(Ity_F32);
IRTemp cc_vex = newTemp(Ity_I32);
IRTemp cc_s390 = newTemp(Ity_I32);
return s390_irgen_CxB("ceb", r1, op2addr, Ity_F32, Iop_CmpF32);
}
assign(op1, get_fpr_w0(r1));
assign(op2, load(Ity_F32, mkexpr(op2addr)));
assign(cc_vex, binop(Iop_CmpF32, mkexpr(op1), mkexpr(op2)));
assign(cc_s390, convert_vex_bfpcc_to_s390(cc_vex));
s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
return "ceb";
static const HChar *
s390_irgen_KEB(UChar r1, IRTemp op2addr)
{
return s390_irgen_CxB("keb", r1, op2addr, Ity_F32, Iop_CmpF32);
return "keb";
}
static const HChar *
s390_irgen_CDB(UChar r1, IRTemp op2addr)
{
IRTemp op1 = newTemp(Ity_F64);
IRTemp op2 = newTemp(Ity_F64);
IRTemp cc_vex = newTemp(Ity_I32);
IRTemp cc_s390 = newTemp(Ity_I32);
return s390_irgen_CxB("cdb", r1, op2addr, Ity_F64, Iop_CmpF64);
}
assign(op1, get_fpr_dw0(r1));
assign(op2, load(Ity_F64, mkexpr(op2addr)));
assign(cc_vex, binop(Iop_CmpF64, mkexpr(op1), mkexpr(op2)));
assign(cc_s390, convert_vex_bfpcc_to_s390(cc_vex));
s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
return "cdb";
static const HChar *
s390_irgen_KDB(UChar r1, IRTemp op2addr)
{
return s390_irgen_CxB("kdb", r1, op2addr, Ity_F64, Iop_CmpF64);
}
static const HChar *
@ -19270,7 +19289,8 @@ s390_decode_4byte_and_irgen(const UChar *bytes)
case 0xb306: s390_format_RRE_FF(s390_irgen_LXEBR, RRE_r1(ovl),
RRE_r2(ovl)); goto ok;
case 0xb307: /* MXDBR */ goto unimplemented;
case 0xb308: /* KEBR */ goto unimplemented;
case 0xb308: s390_format_RRE_FF(s390_irgen_KEBR, RRE_r1(ovl),
RRE_r2(ovl)); goto ok;
case 0xb309: s390_format_RRE_FF(s390_irgen_CEBR, RRE_r1(ovl),
RRE_r2(ovl)); goto ok;
case 0xb30a: s390_format_RRE_FF(s390_irgen_AEBR, RRE_r1(ovl),
@ -19300,7 +19320,8 @@ s390_decode_4byte_and_irgen(const UChar *bytes)
RRE_r2(ovl)); goto ok;
case 0xb317: s390_format_RRE_FF(s390_irgen_MEEBR, RRE_r1(ovl),
RRE_r2(ovl)); goto ok;
case 0xb318: /* KDBR */ goto unimplemented;
case 0xb318: s390_format_RRE_FF(s390_irgen_KDBR, RRE_r1(ovl),
RRE_r2(ovl)); goto ok;
case 0xb319: s390_format_RRE_FF(s390_irgen_CDBR, RRE_r1(ovl),
RRE_r2(ovl)); goto ok;
case 0xb31a: s390_format_RRE_FF(s390_irgen_ADBR, RRE_r1(ovl),
@ -19351,7 +19372,8 @@ s390_decode_4byte_and_irgen(const UChar *bytes)
case 0xb347: s390_format_RRF_UUFF(s390_irgen_FIXBRA, RRF2_m3(ovl),
RRF2_m4(ovl), RRF2_r1(ovl),
RRF2_r2(ovl)); goto ok;
case 0xb348: /* KXBR */ goto unimplemented;
case 0xb348: s390_format_RRE_FF(s390_irgen_KXBR, RRE_r1(ovl),
RRE_r2(ovl)); goto ok;
case 0xb349: s390_format_RRE_FF(s390_irgen_CXBR, RRE_r1(ovl),
RRE_r2(ovl)); goto ok;
case 0xb34a: s390_format_RRE_FF(s390_irgen_AXBR, RRE_r1(ovl),
@ -21408,7 +21430,9 @@ s390_decode_6byte_and_irgen(const UChar *bytes)
RXE_x2(ovl), RXE_b2(ovl),
RXE_d2(ovl)); goto ok;
case 0xed0000000007ULL: /* MXDB */ goto unimplemented;
case 0xed0000000008ULL: /* KEB */ goto unimplemented;
case 0xed0000000008ULL: s390_format_RXE_FRRD(s390_irgen_KEB, RXE_r1(ovl),
RXE_x2(ovl), RXE_b2(ovl),
RXE_d2(ovl)); goto ok;
case 0xed0000000009ULL: s390_format_RXE_FRRD(s390_irgen_CEB, RXE_r1(ovl),
RXE_x2(ovl), RXE_b2(ovl),
RXE_d2(ovl)); goto ok;
@ -21448,7 +21472,9 @@ s390_decode_6byte_and_irgen(const UChar *bytes)
case 0xed0000000017ULL: s390_format_RXE_FRRD(s390_irgen_MEEB, RXE_r1(ovl),
RXE_x2(ovl), RXE_b2(ovl),
RXE_d2(ovl)); goto ok;
case 0xed0000000018ULL: /* KDB */ goto unimplemented;
case 0xed0000000018ULL: s390_format_RXE_FRRD(s390_irgen_KDB, RXE_r1(ovl),
RXE_x2(ovl), RXE_b2(ovl),
RXE_d2(ovl)); goto ok;
case 0xed0000000019ULL: s390_format_RXE_FRRD(s390_irgen_CDB, RXE_r1(ovl),
RXE_x2(ovl), RXE_b2(ovl),
RXE_d2(ovl)); goto ok;

View File

@ -268,11 +268,11 @@ cdbr,"compare long bfp",implemented,
cdb,"compare long bfp",implemented,
cebr,"compare short bfp",implemented,
ceb,"compare short bfp",implemented,
kxbr,"compare and signal extended bfp","not implemented",
kdbr,"compare and signal long bfp","not implemented",
kdb,"compare and signal long bfp","not implemented",
kebr,"compare and signal short bfp","not implemented",
keb,"compare and signal short bfp","not implemented",
kxbr,"compare and signal extended bfp",implemented,
kdbr,"compare and signal long bfp",implemented,
kdb,"compare and signal long bfp",implemented,
kebr,"compare and signal short bfp",implemented,
keb,"compare and signal short bfp",implemented,
cxfbr,"convert from fixed 32 to extended bfp",implemented,
cdfbr,"convert from fixed 32 to long bfp",implemented,
cefbr,"convert from fixed 32 to short bfp",implemented,

Can't render this file because it has a wrong number of fields in line 70.

View File

@ -1,61 +1,101 @@
#include <stdio.h>
/* Test BFP comparison for 32/64-bit. */
static const char *const cmp_result_str[] = {
"==", "<", ">", "??"
};
void cebr(float v1, float v2)
#define TEST_CxB(insn, fmt, mode, v1, v2) \
do { \
int cc; \
\
__asm__ volatile(insn " %[r1],%[r2]\n\t" \
"ipm %[psw]\n\t" \
"srl %[psw],28\n\t" \
: [psw]"=d"(cc) \
: [r1]"f"(v1), [r2]mode(v2) \
: "cc"); \
printf("%-6s" fmt " %s " fmt "\n", \
insn ":", v1, cmp_result_str[cc], v2); \
} while (0)
/* Test BFP comparison for 32/64/128-bit. */
static void cebr(float a, float b)
{
int cc;
__asm__ volatile("cebr %[r1],%[r2]\n\t"
"ipm %[psw]\n\t"
"srl %[psw],28\n\t"
: [psw]"=d"(cc) : [r1]"f"(v1), [r2]"f"(v2) : "cc");
if (cc == 0)
printf("cfebr: %f == %f\n", v1, v2);
if (cc == 1)
printf("cfebr: %f < %f\n", v1, v2);
if (cc == 2)
printf("cfebr: %f > %f\n", v1, v2);
TEST_CxB("cebr", "%g", "f", a, b);
}
void cdbr(double v1, double v2)
static void ceb(float a, float b)
{
int cc;
TEST_CxB("ceb", "%g", "R", a, b);
}
__asm__ volatile("cdbr %[r1],%[r2]\n\t"
"ipm %[psw]\n\t"
"srl %[psw],28\n\t"
: [psw]"=d"(cc) : [r1]"f"(v1), [r2]"f"(v2) : "cc");
if (cc == 0)
printf("cdebr: %f == %f\n", v1, v2);
if (cc == 1)
printf("cdebr: %f < %f\n", v1, v2);
if (cc == 2)
printf("cdebr: %f > %f\n", v1, v2);
static void cdbr(double a, double b)
{
TEST_CxB("cdbr", "%g", "f", a, b);
}
static void cdb(double a, double b)
{
TEST_CxB("cdb", "%g", "R", a, b);
}
static void cxbr(long double a, long double b)
{
TEST_CxB("cxbr", "%Lg", "f", a, b);
}
static void kebr(float a, float b)
{
TEST_CxB("kebr", "%g", "f", a, b);
}
static void keb(float a, float b)
{
TEST_CxB("keb", "%g", "R", a, b);
}
static void kdbr(double a, double b)
{
TEST_CxB("kdbr", "%g", "f", a, b);
}
static void kdb(double a, double b)
{
TEST_CxB("kdb", "%g", "R", a, b);
}
static void kxbr(long double a, long double b)
{
TEST_CxB("kxbr", "%Lg", "f", a, b);
}
static void do_compare(float a, float b)
{
cebr(a, b);
ceb(a, b);
kebr(a, b);
keb(a, b);
cdbr((double) a, (double) b);
cdb((double) a, (double) b);
kdbr((double) a, (double) b);
kdb((double) a, (double) b);
cxbr((long double) a, (long double) b);
kxbr((long double) a, (long double) b);
}
int main(void)
{
float f1, f2;
float d1, d2;
// compare 4 bytes
f1 = 3.14f;
f2 = f1;
cebr(f1, f2);
f2 = f1 + 10.;
cebr(f1, f2);
f2 = f1 - 100.;
cebr(f1, f2);
// compare 8 bytes
d1 = 2.78;
d2 = d1;
cdbr(d1, d2);
d2 = d1 + 10.;
cdbr(d1, d2);
d2 = d1 - 100.;
cdbr(d1, d2);
float inf = 1.f / 0.;
float neg_inf = -1.f / 0.;
do_compare(3.14f, 3.14f);
do_compare(-2.78f, 2.78f);
do_compare(inf, inf);
do_compare(inf, neg_inf);
do_compare(neg_inf, neg_inf);
do_compare(inf, 1.f);
do_compare(neg_inf, -1.f);
do_compare(1.f / inf, -1.f / inf);
return 0;
}

View File

@ -1,6 +1,80 @@
cfebr: 3.140000 == 3.140000
cfebr: 3.140000 < 13.140000
cfebr: 3.140000 > -96.860001
cdebr: 2.780000 == 2.780000
cdebr: 2.780000 < 12.780000
cdebr: 2.780000 > -97.220001
cebr: 3.14 == 3.14
ceb: 3.14 == 3.14
kebr: 3.14 == 3.14
keb: 3.14 == 3.14
cdbr: 3.14 == 3.14
cdb: 3.14 == 3.14
kdbr: 3.14 == 3.14
kdb: 3.14 == 3.14
cxbr: 3.14 == 3.14
kxbr: 3.14 == 3.14
cebr: -2.78 < 2.78
ceb: -2.78 < 2.78
kebr: -2.78 < 2.78
keb: -2.78 < 2.78
cdbr: -2.78 < 2.78
cdb: -2.78 < 2.78
kdbr: -2.78 < 2.78
kdb: -2.78 < 2.78
cxbr: -2.78 < 2.78
kxbr: -2.78 < 2.78
cebr: inf == inf
ceb: inf == inf
kebr: inf == inf
keb: inf == inf
cdbr: inf == inf
cdb: inf == inf
kdbr: inf == inf
kdb: inf == inf
cxbr: inf == inf
kxbr: inf == inf
cebr: inf > -inf
ceb: inf > -inf
kebr: inf > -inf
keb: inf > -inf
cdbr: inf > -inf
cdb: inf > -inf
kdbr: inf > -inf
kdb: inf > -inf
cxbr: inf > -inf
kxbr: inf > -inf
cebr: -inf == -inf
ceb: -inf == -inf
kebr: -inf == -inf
keb: -inf == -inf
cdbr: -inf == -inf
cdb: -inf == -inf
kdbr: -inf == -inf
kdb: -inf == -inf
cxbr: -inf == -inf
kxbr: -inf == -inf
cebr: inf > 1
ceb: inf > 1
kebr: inf > 1
keb: inf > 1
cdbr: inf > 1
cdb: inf > 1
kdbr: inf > 1
kdb: inf > 1
cxbr: inf > 1
kxbr: inf > 1
cebr: -inf < -1
ceb: -inf < -1
kebr: -inf < -1
keb: -inf < -1
cdbr: -inf < -1
cdb: -inf < -1
kdbr: -inf < -1
kdb: -inf < -1
cxbr: -inf < -1
kxbr: -inf < -1
cebr: 0 == -0
ceb: 0 == -0
kebr: 0 == -0
keb: 0 == -0
cdbr: 0 == -0
cdb: 0 == -0
kdbr: 0 == -0
kdb: 0 == -0
cxbr: 0 == -0
kxbr: 0 == -0