|
[Valgrind-developers] [valgrind] Bug 385412 - s390x: new non-vector
z13 instructions not implemented
From: Julian S. <se...@so...> - 2018-07-24 08:22:41
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=d44563c49e55f47016e23591f708c7aa57f7a098 commit d44563c49e55f47016e23591f708c7aa57f7a098 Author: Julian Seward <js...@ac...> Date: Tue Jul 24 10:10:40 2018 +0200 Bug 385412 - s390x: new non-vector z13 instructions not implemented Apart from instructions with vector operands, Valgrind does not implement the additional z/Architecture instructions introduced with z13. These are: - load and zero rightmost byte (LZRF, LZRG); - load logical and zero rightmost byte (LLZRGF); - load halfword high immediate on condition (LOCHHI); - load halfword immediate on condition (LOCHI, LOCGHI); - load high on condition (LOCFHR, LOCFH); - store high on condition (STOCFH); - perform pseudorandom number operation (PPNO), with the functions PPNO-Query and PPNO-SHA-512-DRNG; - load count to block boundary (LCBB). Patches from Vadim Barkov (vb...@gm...), with coordination, testing and format cleanups from Andreas Arnez (ar...@li...). Diff: --- .gitignore | 2 + VEX/priv/guest_s390_defs.h | 4 +- VEX/priv/guest_s390_helpers.c | 70 ++++++ VEX/priv/guest_s390_toIR.c | 372 ++++++++++++++++++++++++++++- VEX/priv/host_s390_defs.c | 80 +++++-- VEX/priv/host_s390_defs.h | 3 +- VEX/priv/s390_defs.h | 22 ++ VEX/priv/s390_disasm.c | 12 + VEX/priv/s390_disasm.h | 8 +- VEX/pub/libvex.h | 5 +- VEX/pub/libvex_emnote.h | 3 + VEX/pub/libvex_s390x_common.h | 2 + coregrind/m_machine.c | 3 +- none/tests/s390x/Makefile.am | 3 +- none/tests/s390x/lsc2.c | 490 +++++++++++++++++++++++++++++++++++++++ none/tests/s390x/lsc2.stderr.exp | 2 + none/tests/s390x/lsc2.stdout.exp | 37 +++ none/tests/s390x/lsc2.vgtest | 1 + none/tests/s390x/ppno.c | 127 ++++++++++ none/tests/s390x/ppno.stderr.exp | 2 + none/tests/s390x/ppno.stdout.exp | 340 +++++++++++++++++++++++++++ none/tests/s390x/ppno.vgtest | 2 + tests/s390x_features.c | 2 + 23 files changed, 1551 insertions(+), 41 deletions(-) diff --git a/.gitignore b/.gitignore index 305bbb2..72b7c3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1875,6 +1875,8 @@ /none/tests/s390x/fixbr /none/tests/s390x/popcnt /none/tests/s390x/vector +/none/tests/s390x/lsc2 +/none/tests/s390x/ppno # /none/tests/scripts/ /none/tests/scripts/*.dSYM diff --git a/VEX/priv/guest_s390_defs.h b/VEX/priv/guest_s390_defs.h index d3dede7..4f9e962 100644 --- a/VEX/priv/guest_s390_defs.h +++ b/VEX/priv/guest_s390_defs.h @@ -95,7 +95,9 @@ UInt s390_do_cvb(ULong decimal); ULong s390_do_cvd(ULong binary); ULong s390_do_ecag(ULong op2addr); UInt s390_do_pfpo(UInt gpr0); - +void s390x_dirtyhelper_PPNO_query(VexGuestS390XState *guest_state, ULong r1, ULong r2); +ULong s390x_dirtyhelper_PPNO_sha512(VexGuestS390XState *guest_state, ULong r1, ULong r2); +void s390x_dirtyhelper_PPNO_sha512_load_param_block( void ); /* The various ways to compute the condition code. */ enum { S390_CC_OP_BITWISE = 0, diff --git a/VEX/priv/guest_s390_helpers.c b/VEX/priv/guest_s390_helpers.c index 77772fe..52e4ce9 100644 --- a/VEX/priv/guest_s390_helpers.c +++ b/VEX/priv/guest_s390_helpers.c @@ -365,6 +365,7 @@ s390x_dirtyhelper_STFLE(VexGuestS390XState *guest_state, ULong *addr) s390_set_facility_bit(addr, S390_FAC_GIE, 1); s390_set_facility_bit(addr, S390_FAC_EXEXT, 1); s390_set_facility_bit(addr, S390_FAC_HIGHW, 1); + s390_set_facility_bit(addr, S390_FAC_LSC2, 1); s390_set_facility_bit(addr, S390_FAC_HFPMAS, 0); s390_set_facility_bit(addr, S390_FAC_HFPUNX, 0); @@ -2525,6 +2526,75 @@ s390x_dirtyhelper_vec_binop(VexGuestS390XState *guest_state, ULong opcode, #endif +/*-----------------------------------------------------------------*/ +/*--- Dirty helper for Perform Pseudorandom number instruction ---*/ +/*-----------------------------------------------------------------*/ + +/* Dummy helper that is needed to indicate load of parameter block. + We have to use it because dirty helper cannot have two memory side + effects. + */ +void s390x_dirtyhelper_PPNO_sha512_load_param_block( void ) +{ +} + +#if defined(VGA_s390x) + +/* IMPORTANT! + We return here bit mask where only supported functions are set to one. + If you implement new functions don't forget the supported array. + */ +void +s390x_dirtyhelper_PPNO_query(VexGuestS390XState *guest_state, ULong r1, ULong r2) +{ + ULong supported[2] = {0x9000000000000000ULL, 0x0000000000000000ULL}; + ULong *result = (ULong*) guest_state->guest_r1; + + result[0] = supported[0]; + result[1] = supported[1]; +} + +ULong +s390x_dirtyhelper_PPNO_sha512(VexGuestS390XState *guest_state, ULong r1, ULong r2) +{ + ULong* op1 = (ULong*) (((ULong)(&guest_state->guest_r0)) + r1 * sizeof(ULong)); + ULong* op2 = (ULong*) (((ULong)(&guest_state->guest_r0)) + r2 * sizeof(ULong)); + + register ULong reg0 asm("0") = guest_state->guest_r0; + register ULong reg1 asm("1") = guest_state->guest_r1; + register ULong reg2 asm("2") = op1[0]; + register ULong reg3 asm("3") = op1[1]; + register ULong reg4 asm("4") = op2[0]; + register ULong reg5 asm("5") = op2[1]; + + ULong cc = 0; + asm volatile(".insn rre, 0xb93c0000, %%r2, %%r4\n" + "ipm %[cc]\n" + "srl %[cc], 28\n" + : "+d"(reg0), "+d"(reg1), + "+d"(reg2), "+d"(reg3), + "+d"(reg4), "+d"(reg5), + [cc] "=d"(cc) + : + : "cc", "memory"); + + return cc; +} + +#else + +void +s390x_dirtyhelper_PPNO_query(VexGuestS390XState *guest_state, ULong r1, ULong r2) +{ +} + +ULong +s390x_dirtyhelper_PPNO_sha512(VexGuestS390XState *guest_state, ULong r1, ULong r2) +{ + return 0; +} + +#endif /* VGA_s390x */ /*---------------------------------------------------------------*/ /*--- end guest_s390_helpers.c ---*/ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index cca684a..1697e97 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -2244,6 +2244,18 @@ s390_format_RIE_RUPI(const HChar *(*irgen)(UChar r1, UChar m3, UShort i4, } static void +s390_format_RIE_RUPIX(const HChar *(*irgen)(UChar r1, UChar m3, UShort i4, + UChar i2), + UChar r1, UChar m3, UShort i4, UChar i2, Int xmnm_kind) +{ + const HChar *mnm = irgen(r1, m3, i4, i2); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC5(XMNM, GPR, INT, CABM, PCREL), xmnm_kind, mnm, m3, r1, + (Int)(Char)i2, m3, (Int)(Short)i4); +} + +static void s390_format_RIL(const HChar *(*irgen)(UChar r1, UInt i2), UChar r1, UInt i2) { @@ -2788,6 +2800,23 @@ s390_format_RXE_FRRD(const HChar *(*irgen)(UChar r1, IRTemp op2addr), } static void +s390_format_RXE_RRRDR(const HChar *(*irgen)(UChar r1, IRTemp op2addr, UChar m3), + UChar r1, UChar x2, UChar b2, UShort d2, UChar m3) +{ + const HChar *mnm; + IRTemp op2addr = newTemp(Ity_I64); + + assign(op2addr, binop(Iop_Add64, binop(Iop_Add64, mkU64(d2), + b2 != 0 ? get_gpr_dw0(b2) : mkU64(0)), x2 != 0 ? get_gpr_dw0(x2) : + mkU64(0))); + + mnm = irgen(r1, op2addr, m3); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC3(MNM, GPR, UDXB), mnm, r1, d2, x2, b2); +} + +static void s390_format_RXF_FRRDF(const HChar *(*irgen)(UChar, IRTemp, UChar), UChar r3, UChar x2, UChar b2, UShort d2, UChar r1) { @@ -15699,6 +15728,288 @@ s390_irgen_VNO(UChar v1, UChar v2, UChar v3) return "vno"; } +static const HChar * +s390_irgen_LZRF(UChar r1, IRTemp op2addr) +{ + IRTemp op2 = newTemp(Ity_I32); + + assign(op2, binop(Iop_And32, load(Ity_I32, mkexpr(op2addr)), mkU32(0xffffff00))); + put_gpr_w1(r1, mkexpr(op2)); + + return "lzrf"; +} + +static const HChar * +s390_irgen_LZRG(UChar r1, IRTemp op2addr) +{ + IRTemp op2 = newTemp(Ity_I64); + + assign(op2, binop(Iop_And64, load(Ity_I64, mkexpr(op2addr)), mkU64(0xffffffffffffff00UL))); + put_gpr_dw0(r1, mkexpr(op2)); + + return "lzrg"; +} + +static const HChar * +s390_irgen_LLZRGF(UChar r1, IRTemp op2addr) +{ + IRTemp op2 = newTemp(Ity_I32); + + assign(op2, binop(Iop_And32, load(Ity_I32, mkexpr(op2addr)), mkU32(0xffffff00))); + put_gpr_w1(r1, mkexpr(op2)); + put_gpr_w0(r1, mkU32(0)); + + return "llzrgf"; +} + +static const HChar * +s390_irgen_LOCFH(UChar r1, IRTemp op2addr) +{ + /* condition is checked in format handler */ + put_gpr_w0(r1, load(Ity_I32, mkexpr(op2addr))); + + return "locfh"; +} + +static const HChar * +s390_irgen_LOCFHR(UChar m3, UChar r1, UChar r2) +{ + next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0))); + put_gpr_w0(r1, get_gpr_w0(r2)); + + return "locfhr"; +} + +static const HChar * +s390_irgen_LOCHHI(UChar r1, UChar m3, UShort i2, UChar unused) +{ + next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0))); + put_gpr_w0(r1, mkU32(i2)); + + return "lochhi"; +} + +static const HChar * +s390_irgen_LOCHI(UChar r1, UChar m3, UShort i2, UChar unused) +{ + next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0))); + put_gpr_w1(r1, mkU32(i2)); + + return "lochi"; +} + +static const HChar * +s390_irgen_LOCGHI(UChar r1, UChar m3, UShort i2, UChar unused) +{ + next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0))); + put_gpr_dw0(r1, mkU64(i2)); + + return "locghi"; +} + +static const HChar * +s390_irgen_STOCFH(UChar r1, IRTemp op2addr) +{ + /* condition is checked in format handler */ + store(mkexpr(op2addr), get_gpr_w1(r1)); + + return "stocfh"; +} + +static const HChar * +s390_irgen_LCBB(UChar r1, IRTemp op2addr, UChar m3) +{ + IRTemp op2 = newTemp(Ity_I32); + assign(op2, s390_getCountToBlockBoundary(op2addr, m3)); + put_gpr_w1(r1, mkexpr(op2)); + + IRExpr* cc = mkite(binop(Iop_CmpEQ32, mkexpr(op2), mkU32(16)), mkU64(0), mkU64(3)); + s390_cc_thunk_fill(mkU64(S390_CC_OP_SET), cc, mkU64(0), mkU64(0)); + + return "lcbb"; +} + +/* Regarding the use of + // Dummy helper which is used to signal VEX library that memory was loaded + sha512_loadparam + = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_load_param_block", + &s390x_dirtyhelper_PPNO_sha512_load_param_block, + mkIRExprVec_0()); + + in the following function (s390_irgen_PPNO). This is a workaround to get + around the fact that IRDirty annotations cannot indicate two memory side + effects, which are unfortunately necessary here. It will possibly lead to + losing undefinedness (undefinedness in some inputs might not be propagated + to the outputs as it shouod, in Memcheck). The correct fix would be to + extend IRDirty to represent two memory side effects, but that's quite a bit + of work. + + Here's a summary of what this insn does. + + // getReg(RegisterNumber n) returns the value of GPR number 'n' + + // reg1 and reg2 are even + void ppno(RegisterNumber reg1, RegisterNumber reg2) { + + switch(getReg(0)) { + case 0x0: + // Query mode, ignore reg1 and reg2 + // Write 16 bytes at getReg(1) + break; + + case 0x3: + // SHA-512 generate mode, ignore reg2 + + // Read 240 bytes at getReg(1) + // Write getReg(reg1 + 1) bytes at getReg(reg1) + // Write some of 240 bytes starting at getReg(1) + break; + + case 0x83: + // SHA-512 seed mode, ignore reg1 + + // Read some of 240 bytes starting at getReg(1) + // Read getReg(reg2 + 1) bytes at getReg(reg2) + // Write 240 bytes at getReg(1) + break; + + default: + // Specification exception, abort execution. + } + } +*/ +/* Also known as "prno" + If you implement new functions please don't forget to update + "s390x_dirtyhelper_PPNO_query" function. + */ +static const HChar * +s390_irgen_PPNO(UChar r1, UChar r2) +{ + if (!s390_host_has_msa5) { + emulation_failure(EmFail_S390X_ppno); + return "ppno"; + } + + /* Theese conditions lead to specification exception */ + vassert(r1 % 2 == 0); + vassert(r2 % 2 == 0); + vassert((r1 != 0) && (r2 != 0)); + + IRDirty *query, *sha512_gen, *sha512_seed, *sha512_loadparam; + IRTemp gpr1num = newTemp(Ity_I64); + IRTemp gpr2num = newTemp(Ity_I64); + + IRTemp funcCode = newTemp(Ity_I8); + IRTemp is_query = newTemp(Ity_I1); + IRTemp is_sha512_gen = newTemp(Ity_I1); + IRTemp is_sha512_seed = newTemp(Ity_I1); + IRTemp is_sha512 = newTemp(Ity_I1); + + assign(funcCode, unop(Iop_64to8, binop(Iop_And64, get_gpr_dw0(0), mkU64(0xffULL)))); + assign(gpr1num, mkU64(r1)); + assign(gpr2num, mkU64(r2)); + + assign(is_query, binop(Iop_CmpEQ8, mkexpr(funcCode), mkU8(S390_PPNO_QUERY))); + assign(is_sha512_gen, binop(Iop_CmpEQ8, mkexpr(funcCode), mkU8(S390_PPNO_SHA512_GEN))); + assign(is_sha512_seed, binop(Iop_CmpEQ8, mkexpr(funcCode), mkU8(S390_PPNO_SHA512_SEED))); + assign(is_sha512, binop(Iop_CmpEQ8, + mkU8(S390_PPNO_SHA512_GEN), + binop(Iop_And8, + mkexpr(funcCode), + mkU8(S390_PPNO_SHA512_GEN) + ) + )); + + query = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_query", + &s390x_dirtyhelper_PPNO_query, + mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num), mkexpr(gpr2num))); + query->guard = mkexpr(is_query); + query->nFxState = 1; + vex_bzero(&query->fxState, sizeof(query->fxState)); + query->fxState[0].fx = Ifx_Read; + query->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0); + query->fxState[0].size = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */ + query->mAddr = get_gpr_dw0(1); + query->mSize = S390_PPNO_PARAM_BLOCK_SIZE_QUERY; + query->mFx = Ifx_Write; + + IRTemp gen_cc = newTemp(Ity_I64); + sha512_gen = unsafeIRDirty_1_N(gen_cc, 0, "s390x_dirtyhelper_PPNO_sha512", + &s390x_dirtyhelper_PPNO_sha512, + mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num), mkexpr(gpr2num))); + sha512_gen->guard = mkexpr(is_sha512_gen); + sha512_gen->nFxState = 3; + vex_bzero(&sha512_gen->fxState, sizeof(sha512_gen->fxState)); + sha512_gen->fxState[0].fx = Ifx_Read; + sha512_gen->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0); + sha512_gen->fxState[0].size = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */ + sha512_gen->fxState[1].fx = Ifx_Read; + sha512_gen->fxState[1].offset = S390X_GUEST_OFFSET(guest_r0) + r1 * sizeof(ULong); + sha512_gen->fxState[1].size = sizeof(ULong); + sha512_gen->fxState[2].fx = Ifx_Modify; + sha512_gen->fxState[2].offset = S390X_GUEST_OFFSET(guest_r0) + (r1 + 1) * sizeof(ULong); + sha512_gen->fxState[2].size = sizeof(ULong); + sha512_gen->mAddr = get_gpr_dw0(r1); + sha512_gen->mSize = S390_PPNO_MAX_SIZE_SHA512_GEN; + sha512_gen->mFx = Ifx_Write; + + IRTemp unused = newTemp(Ity_I64); + sha512_seed = unsafeIRDirty_1_N(unused, 0, "s390x_dirtyhelper_PPNO_sha512", + &s390x_dirtyhelper_PPNO_sha512, + mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num), mkexpr(gpr2num))); + sha512_seed->guard = mkexpr(is_sha512_seed); + sha512_seed->nFxState = 2; + vex_bzero(&sha512_seed->fxState, sizeof(sha512_seed->fxState)); + sha512_seed->fxState[0].fx = Ifx_Read; + sha512_seed->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0); + sha512_seed->fxState[0].size = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */ + sha512_seed->fxState[1].fx = Ifx_Read; + sha512_seed->fxState[1].offset = S390X_GUEST_OFFSET(guest_r0) + r2 * sizeof(ULong); + sha512_seed->fxState[1].size = 2 * sizeof(ULong); /* r2 and r2 + 1 are read */ + sha512_seed->mAddr = get_gpr_dw0(r2); + sha512_seed->mSize = S390_PPNO_MAX_SIZE_SHA512_SEED; + sha512_seed->mFx = Ifx_Write; + + /* Dummy helper which is used to signal VEX library that memory was loaded */ + sha512_loadparam = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_load_param_block", + &s390x_dirtyhelper_PPNO_sha512_load_param_block, + mkIRExprVec_0()); + sha512_loadparam->guard = mkexpr(is_sha512); + sha512_loadparam->nFxState = 0; + vex_bzero(&sha512_loadparam->fxState, sizeof(sha512_loadparam->fxState)); + sha512_loadparam->mAddr = get_gpr_dw0(1); + sha512_loadparam->mSize = S390_PPNO_PARAM_BLOCK_SIZE_SHA512; + sha512_loadparam->mFx = Ifx_Read; + + IRDirty* + sha512_saveparam = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_save_param_block", + &s390x_dirtyhelper_PPNO_sha512_load_param_block, + mkIRExprVec_0()); + sha512_saveparam->guard = mkexpr(is_sha512); + sha512_saveparam->nFxState = 0; + vex_bzero(&sha512_saveparam->fxState, sizeof(sha512_saveparam->fxState)); + sha512_saveparam->mAddr = get_gpr_dw0(1); + sha512_saveparam->mSize = S390_PPNO_PARAM_BLOCK_SIZE_SHA512; + sha512_saveparam->mFx = Ifx_Write; + + stmt(IRStmt_Dirty(query)); + stmt(IRStmt_Dirty(sha512_loadparam)); + stmt(IRStmt_Dirty(sha512_gen)); + stmt(IRStmt_Dirty(sha512_seed)); + stmt(IRStmt_Dirty(sha512_saveparam)); + + IRTemp cc = newTemp(Ity_I64); + assign(cc, + mkite(mkexpr(is_sha512_gen), + mkexpr(gen_cc), + mkU64(0) + ) + ); + + s390_cc_thunk_fill(mkU64(S390_CC_OP_SET), mkexpr(cc), mkU64(0), mkU64(0)); + + return "ppno"; +} /* New insns are added here. If an insn is contingent on a facility being installed also @@ -16590,7 +16901,8 @@ s390_decode_4byte_and_irgen(const UChar *bytes) ovl.fmt.RRE.r2); goto ok; case 0xb931: s390_format_RRE_RR(s390_irgen_CLGFR, ovl.fmt.RRE.r1, ovl.fmt.RRE.r2); goto ok; - case 0xb93c: /* PPNO */ goto unimplemented; + case 0xb93c: s390_format_RRE_RR(s390_irgen_PPNO, ovl.fmt.RRE.r1, + ovl.fmt.RRE.r2); goto ok; case 0xb93e: /* KIMD */ goto unimplemented; case 0xb93f: /* KLMD */ goto unimplemented; case 0xb941: s390_format_RRF_UURF(s390_irgen_CFDTR, ovl.fmt.RRF2.m3, @@ -16733,7 +17045,9 @@ s390_decode_4byte_and_irgen(const UChar *bytes) ovl.fmt.RRE.r2); goto ok; case 0xb9df: s390_format_RRE_RR(s390_irgen_CLHLR, ovl.fmt.RRE.r1, ovl.fmt.RRE.r2); goto ok; - case 0xb9e0: /* LOCFHR */ goto unimplemented; + case 0xb9e0: s390_format_RRF_U0RR(s390_irgen_LOCFHR, ovl.fmt.RRF3.r3, + ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2, + S390_XMNM_LOCFHR); goto ok; case 0xb9e1: s390_format_RRE_RR(s390_irgen_POPCNT, ovl.fmt.RRE.r1, ovl.fmt.RRE.r2); goto ok; case 0xb9e2: s390_format_RRF_U0RR(s390_irgen_LOCGR, ovl.fmt.RRF3.r3, @@ -17310,7 +17624,10 @@ s390_decode_6byte_and_irgen(const UChar *bytes) ovl.fmt.RXY.x2, ovl.fmt.RXY.b2, ovl.fmt.RXY.dl2, ovl.fmt.RXY.dh2); goto ok; - case 0xe3000000002aULL: /* LZRG */ goto unimplemented; + case 0xe3000000002aULL: s390_format_RXY_RRRD(s390_irgen_LZRG, ovl.fmt.RXY.r1, + ovl.fmt.RXY.x2, ovl.fmt.RXY.b2, + ovl.fmt.RXY.dl2, + ovl.fmt.RXY.dh2); goto ok; case 0xe3000000002eULL: /* CVDG */ goto unimplemented; case 0xe3000000002fULL: s390_format_RXY_RRRD(s390_irgen_STRVG, ovl.fmt.RXY.r1, ovl.fmt.RXY.x2, @@ -17338,8 +17655,14 @@ s390_decode_6byte_and_irgen(const UChar *bytes) ovl.fmt.RXY.dh2); goto ok; case 0xe30000000038ULL: /* AGH */ goto unimplemented; case 0xe30000000039ULL: /* SGH */ goto unimplemented; - case 0xe3000000003aULL: /* LLZRGF */ goto unimplemented; - case 0xe3000000003bULL: /* LZRF */ goto unimplemented; + case 0xe3000000003aULL: s390_format_RXY_RRRD(s390_irgen_LLZRGF, ovl.fmt.RXY.r1, + ovl.fmt.RXY.x2, ovl.fmt.RXY.b2, + ovl.fmt.RXY.dl2, + ovl.fmt.RXY.dh2); goto ok; + case 0xe3000000003bULL: s390_format_RXY_RRRD(s390_irgen_LZRF, ovl.fmt.RXY.r1, + ovl.fmt.RXY.x2, ovl.fmt.RXY.b2, + ovl.fmt.RXY.dl2, + ovl.fmt.RXY.dh2); goto ok; case 0xe3000000003cULL: /* MGH */ goto unimplemented; case 0xe3000000003eULL: s390_format_RXY_RRRD(s390_irgen_STRV, ovl.fmt.RXY.r1, ovl.fmt.RXY.x2, ovl.fmt.RXY.b2, @@ -17665,10 +17988,12 @@ s390_decode_6byte_and_irgen(const UChar *bytes) case 0xe70000000021ULL: s390_format_VRS_RRDVM(s390_irgen_VLGV, ovl.fmt.VRS.v1, ovl.fmt.VRS.b2, ovl.fmt.VRS.d2, ovl.fmt.VRS.v3, ovl.fmt.VRS.m4, ovl.fmt.VRS.rxb); goto ok; - case 0xe70000000027ULL: /* LCBB */ goto unimplemented; case 0xe70000000022ULL: s390_format_VRS_VRRDM(s390_irgen_VLVG, ovl.fmt.VRS.v1, ovl.fmt.VRS.b2, ovl.fmt.VRS.d2, ovl.fmt.VRS.v3, ovl.fmt.VRS.m4, ovl.fmt.VRS.rxb); goto ok; + case 0xe70000000027ULL: s390_format_RXE_RRRDR(s390_irgen_LCBB, ovl.fmt.RXE.r1, + ovl.fmt.RXE.x2, ovl.fmt.RXE.b2, + ovl.fmt.RXE.d2, ovl.fmt.RXE.m3); goto ok; case 0xe70000000030ULL: /* VESL */ goto unimplemented; case 0xe70000000033ULL: /* VERLL */ goto unimplemented; case 0xe70000000036ULL: s390_format_VRS_VRDV(s390_irgen_VLM, ovl.fmt.VRS.v1, @@ -17941,7 +18266,7 @@ s390_decode_6byte_and_irgen(const UChar *bytes) ovl.fmt.RSY.dh2); goto ok; case 0xeb000000004cULL: s390_format_RSY_RRRD(s390_irgen_ECAG, ovl.fmt.RSY.r1, ovl.fmt.RSY.r3, ovl.fmt.RSY.b2, - ovl.fmt.RSY.dl2, + ovl.fmt.RSY.dl2, ovl.fmt.RSY.dh2); goto ok; case 0xeb0000000051ULL: s390_format_SIY_URD(s390_irgen_TMY, ovl.fmt.SIY.i2, ovl.fmt.SIY.b1, ovl.fmt.SIY.dl1, @@ -18020,8 +18345,16 @@ s390_decode_6byte_and_irgen(const UChar *bytes) ovl.fmt.RSY.r3, ovl.fmt.RSY.b2, ovl.fmt.RSY.dl2, ovl.fmt.RSY.dh2); goto ok; - case 0xeb00000000e0ULL: /* LOCFH */ goto unimplemented; - case 0xeb00000000e1ULL: /* STOCFH */ goto unimplemented; + case 0xeb00000000e0ULL: s390_format_RSY_RDRM(s390_irgen_LOCFH, ovl.fmt.RSY.r1, + ovl.fmt.RSY.r3, ovl.fmt.RSY.b2, + ovl.fmt.RSY.dl2, + ovl.fmt.RSY.dh2, + S390_XMNM_LOCFH); goto ok; + case 0xeb00000000e1ULL: s390_format_RSY_RDRM(s390_irgen_STOCFH, ovl.fmt.RSY.r1, + ovl.fmt.RSY.r3, ovl.fmt.RSY.b2, + ovl.fmt.RSY.dl2, + ovl.fmt.RSY.dh2, + S390_XMNM_STOCFH); goto ok; case 0xeb00000000e2ULL: s390_format_RSY_RDRM(s390_irgen_LOCG, ovl.fmt.RSY.r1, ovl.fmt.RSY.r3, ovl.fmt.RSY.b2, ovl.fmt.RSY.dl2, @@ -18082,15 +18415,30 @@ s390_decode_6byte_and_irgen(const UChar *bytes) ovl.fmt.RSY.r3, ovl.fmt.RSY.b2, ovl.fmt.RSY.dl2, ovl.fmt.RSY.dh2); goto ok; - case 0xec0000000042ULL: /* LOCHI */ goto unimplemented; + case 0xec0000000042ULL: s390_format_RIE_RUPIX(s390_irgen_LOCHI, + ovl.fmt.RIEv3.r1, + ovl.fmt.RIEv3.m3, + ovl.fmt.RIEv3.i4, + ovl.fmt.RIEv3.i2, + S390_XMNM_LOCHI); goto ok; case 0xec0000000044ULL: s390_format_RIE_RRP(s390_irgen_BRXHG, ovl.fmt.RIE.r1, ovl.fmt.RIE.r3, ovl.fmt.RIE.i2); goto ok; case 0xec0000000045ULL: s390_format_RIE_RRP(s390_irgen_BRXLG, ovl.fmt.RIE.r1, ovl.fmt.RIE.r3, ovl.fmt.RIE.i2); goto ok; - case 0xec0000000046ULL: /* LOCGHI */ goto unimplemented; - case 0xec000000004eULL: /* LOCHHI */ goto unimplemented; + case 0xec0000000046ULL: s390_format_RIE_RUPIX(s390_irgen_LOCGHI, + ovl.fmt.RIEv3.r1, + ovl.fmt.RIEv3.m3, + ovl.fmt.RIEv3.i4, + ovl.fmt.RIEv3.i2, + S390_XMNM_LOCGHI); goto ok; + case 0xec000000004eULL: s390_format_RIE_RUPIX(s390_irgen_LOCHHI, + ovl.fmt.RIEv3.r1, + ovl.fmt.RIEv3.m3, + ovl.fmt.RIEv3.i4, + ovl.fmt.RIEv3.i2, + S390_XMNM_LOCHHI); goto ok; case 0xec0000000051ULL: s390_format_RIE_RRUUU(s390_irgen_RISBLG, ovl.fmt.RIE_RRUUU.r1, ovl.fmt.RIE_RRUUU.r2, diff --git a/VEX/priv/host_s390_defs.c b/VEX/priv/host_s390_defs.c index d03ed5f..d41093e 100644 --- a/VEX/priv/host_s390_defs.c +++ b/VEX/priv/host_s390_defs.c @@ -9289,36 +9289,37 @@ s390_insn_helper_call_emit(UChar *buf, const s390_insn *insn) { s390_cc_t cond; ULong target; - UChar *ptmp = buf; + Int delta; s390_helper_call *helper_call = insn->variant.helper_call.details; cond = helper_call->cond; target = helper_call->target; - if (cond != S390_CC_ALWAYS - && helper_call->rloc.pri != RLPri_None) { - /* The call might not happen (it isn't unconditional) and it - returns a result. In this case we will need to generate a - control flow diamond to put 0x555..555 in the return - register(s) in the case where the call doesn't happen. If - this ever becomes necessary, maybe copy code from the ARM - equivalent. Until that day, just give up. */ - return buf; /* To denote failure. */ - } + const Bool not_always = (cond != S390_CC_ALWAYS); + const Bool not_void_return = (helper_call->rloc.pri != RLPri_None); + + /* We have this situation: + ( *** code in this braces is for not_always && not_void_return*** ) + ... + before: + brc{!cond} else + call_helper + preElse: + *** j after *** + else: + *** load_64imm $0x5555555555555555, %%r2 *** // e.g. for Int RetLoc + after: + ... + */ - if (cond != S390_CC_ALWAYS) { - /* So we have something like this - if (cond) call X; - Y: ... - We convert this into - if (! cond) goto Y; // BRC opcode; 4 bytes - call X; - Y: - */ + // before: + UChar *pBefore = buf; + if (not_always) { /* 4 bytes (a BRC insn) to be filled in here */ buf += 4; } + // call_helper /* Load the target address into a register, that (a) is not used for passing parameters to the helper and (b) can be clobbered by the callee @@ -9336,12 +9337,45 @@ s390_insn_helper_call_emit(UChar *buf, const s390_insn *insn) buf = s390_emit_LFPC(buf, S390_REGNO_STACK_POINTER, // restore FPC S390_OFFSET_SAVED_FPC_C); - if (cond != S390_CC_ALWAYS) { - Int delta = buf - ptmp; + // preElse: + UChar* pPreElse = buf; + if (not_always && not_void_return) { + /* 4 bytes (a BRC insn) to be filled in here */ + buf += 4; + } + + // else: + UChar* pElse = buf; + if (not_always && not_void_return) { + switch (helper_call->rloc.pri) { + case RLPri_Int: + buf = s390_emit_load_64imm(buf, S390_REGNO_RETURN_VALUE, 0x5555555555555555ULL); + break; + default: + ppS390Instr(insn, True); + vpanic("s390_insn_helper_call_emit: invalid conditional RetLoc."); + } + } + + // after: + UChar* pAfter = buf; + // fill "brc{!cond} else" + if(not_always) + { + delta = pElse - pBefore; delta >>= 1; /* immediate constant is #half-words */ vassert(delta > 0 && delta < (1 << 16)); - s390_emit_BRC(ptmp, s390_cc_invert(cond), delta); + s390_emit_BRC(pBefore, s390_cc_invert(cond), delta); + } + + // fill "brc{ALWAYS} after" + if (not_always && not_void_return) + { + delta = pAfter - pPreElse; + delta >>= 1; /* immediate constant is #half-words */ + vassert(delta > 0 && delta < (1 << 16)); + s390_emit_BRC(pPreElse, S390_CC_ALWAYS, delta); } return buf; diff --git a/VEX/priv/host_s390_defs.h b/VEX/priv/host_s390_defs.h index de1ab19..c88075d 100644 --- a/VEX/priv/host_s390_defs.h +++ b/VEX/priv/host_s390_defs.h @@ -873,7 +873,8 @@ extern UInt s390_host_hwcaps; (s390_host_hwcaps & (VEX_HWCAPS_S390X_PFPO)) #define s390_host_has_vx \ (s390_host_hwcaps & (VEX_HWCAPS_S390X_VX)) - +#define s390_host_has_msa5 \ + (s390_host_hwcaps & (VEX_HWCAPS_S390X_MSA5)) #endif /* ndef __VEX_HOST_S390_DEFS_H */ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/s390_defs.h b/VEX/priv/s390_defs.h index 4c2de40..56886db 100644 --- a/VEX/priv/s390_defs.h +++ b/VEX/priv/s390_defs.h @@ -146,6 +146,28 @@ typedef enum { S390_PFPO_D128_TO_F128 = 0x01070A } s390_pfpo_function_t; +/* PPNO function code as it is encoded in bits [57:63] of GR0 + when PPNO insn is executed. */ +typedef enum +{ + S390_PPNO_QUERY = 0x00, + S390_PPNO_SHA512_GEN = 0x03, + S390_PPNO_SHA512_SEED = 0x83 +} s390_ppno_function_t; + +/* Size of parameter block for PPNO functions. + All values are in bytes. + */ +#define S390_PPNO_PARAM_BLOCK_SIZE_QUERY 16 +#define S390_PPNO_PARAM_BLOCK_SIZE_SHA512 240 + +/* Maximum length of modified memory for PPNO functions. + All values are in bytes. +*/ +#define S390_PPNO_MAX_SIZE_SHA512_SEED 512 +#define S390_PPNO_MAX_SIZE_SHA512_GEN 64 + + /* The length of the longest mnemonic: locgrnhe */ #define S390_MAX_MNEMONIC_LEN 8 diff --git a/VEX/priv/s390_disasm.c b/VEX/priv/s390_disasm.c index a7305c9..f95d7ec 100644 --- a/VEX/priv/s390_disasm.c +++ b/VEX/priv/s390_disasm.c @@ -251,6 +251,12 @@ cls_operand(Int kind, UInt mask) case S390_XMNM_LOCG: prefix = "locg"; break; case S390_XMNM_STOC: prefix = "stoc"; break; case S390_XMNM_STOCG: prefix = "stocg"; break; + case S390_XMNM_STOCFH: prefix = "stocfh"; break; + case S390_XMNM_LOCFH: prefix = "locgh"; break; + case S390_XMNM_LOCFHR: prefix = "locghr"; break; + case S390_XMNM_LOCHI: prefix = "lochi"; break; + case S390_XMNM_LOCGHI: prefix = "locghi"; break; + case S390_XMNM_LOCHHI: prefix = "lochhi"; break; default: vpanic("cls_operand"); } @@ -416,6 +422,12 @@ s390_disasm(UInt command, ...) case S390_XMNM_LOCG: case S390_XMNM_STOC: case S390_XMNM_STOCG: + case S390_XMNM_STOCFH: + case S390_XMNM_LOCFH: + case S390_XMNM_LOCFHR: + case S390_XMNM_LOCHI: + case S390_XMNM_LOCGHI: + case S390_XMNM_LOCHHI: mask = va_arg(args, UInt); mnm = cls_operand(kind, mask); p += vex_sprintf(p, "%s", mnemonic(mnm)); diff --git a/VEX/priv/s390_disasm.h b/VEX/priv/s390_disasm.h index e789898..9d29b96 100644 --- a/VEX/priv/s390_disasm.h +++ b/VEX/priv/s390_disasm.h @@ -83,7 +83,13 @@ enum { S390_XMNM_LOC = 7, S390_XMNM_LOCG = 8, S390_XMNM_STOC = 9, - S390_XMNM_STOCG = 10 + S390_XMNM_STOCG = 10, + S390_XMNM_STOCFH = 11, + S390_XMNM_LOCFH = 12, + S390_XMNM_LOCFHR = 13, + S390_XMNM_LOCHI = 14, + S390_XMNM_LOCGHI = 15, + S390_XMNM_LOCHHI = 16 }; void s390_disasm(UInt command, ...); diff --git a/VEX/pub/libvex.h b/VEX/pub/libvex.h index 93aaf43..e6c1974 100644 --- a/VEX/pub/libvex.h +++ b/VEX/pub/libvex.h @@ -159,6 +159,8 @@ typedef #define VEX_HWCAPS_S390X_LSC (1<<16) /* Conditional load/store facility */ #define VEX_HWCAPS_S390X_PFPO (1<<17) /* Perform floating point ops facility */ #define VEX_HWCAPS_S390X_VX (1<<18) /* Vector facility */ +#define VEX_HWCAPS_S390X_MSA5 (1<<19) /* message security assistance facility */ + /* Special value representing all available s390x hwcaps */ #define VEX_HWCAPS_S390X_ALL (VEX_HWCAPS_S390X_LDISP | \ @@ -173,7 +175,8 @@ typedef VEX_HWCAPS_S390X_ETF3 | \ VEX_HWCAPS_S390X_ETF2 | \ VEX_HWCAPS_S390X_PFPO | \ - VEX_HWCAPS_S390X_VX) + VEX_HWCAPS_S390X_VX | \ + VEX_HWCAPS_S390X_MSA5) #define VEX_HWCAPS_S390X(x) ((x) & ~VEX_S390X_MODEL_MASK) #define VEX_S390X_MODEL(x) ((x) & VEX_S390X_MODEL_MASK) diff --git a/VEX/pub/libvex_emnote.h b/VEX/pub/libvex_emnote.h index 4aaeac8..2feb4ba 100644 --- a/VEX/pub/libvex_emnote.h +++ b/VEX/pub/libvex_emnote.h @@ -123,6 +123,9 @@ typedef /* some insn needs vector facility which is not available on this host */ EmFail_S390X_vx, + /* ppno insn is not supported on this host */ + EmFail_S390X_ppno, + EmNote_NUMBER } VexEmNote; diff --git a/VEX/pub/libvex_s390x_common.h b/VEX/pub/libvex_s390x_common.h index 40185c2..a8a66b9 100644 --- a/VEX/pub/libvex_s390x_common.h +++ b/VEX/pub/libvex_s390x_common.h @@ -99,6 +99,8 @@ #define S390_FAC_DFPZC 48 // DFP zoned-conversion #define S390_FAC_MISC 49 // miscellaneous insn #define S390_FAC_CTREXE 50 // constrained transactional execution +#define S390_FAC_LSC2 53 // load/store on condition 2 and load and zero rightmost byte +#define S390_FAC_MSA5 57 // message-security-assist 5 #define S390_FAC_TREXE 73 // transactional execution #define S390_FAC_MSA4 77 // message-security-assist 4 #define S390_FAC_VX 128 // vector facility diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c index f6ec7d5..31b0e1b 100644 --- a/coregrind/m_machine.c +++ b/coregrind/m_machine.c @@ -1495,7 +1495,8 @@ Bool VG_(machine_get_hwcaps)( void ) { False, S390_FAC_FPEXT, VEX_HWCAPS_S390X_FPEXT, "FPEXT" }, { False, S390_FAC_LSC, VEX_HWCAPS_S390X_LSC, "LSC" }, { False, S390_FAC_PFPO, VEX_HWCAPS_S390X_PFPO, "PFPO" }, - { False, S390_FAC_VX, VEX_HWCAPS_S390X_VX, "VX" } + { False, S390_FAC_VX, VEX_HWCAPS_S390X_VX, "VX" }, + { False, S390_FAC_MSA5, VEX_HWCAPS_S390X_MSA5, "MSA5" } }; /* Set hwcaps according to the detected facilities */ diff --git a/none/tests/s390x/Makefile.am b/none/tests/s390x/Makefile.am index fb6adbb..cb11df9 100644 --- a/none/tests/s390x/Makefile.am +++ b/none/tests/s390x/Makefile.am @@ -18,7 +18,7 @@ INSN_TESTS = clc clcle cvb cvd icm lpr tcxb lam_stam xc mvst add sub mul \ spechelper-cr spechelper-clr \ spechelper-ltr spechelper-or \ spechelper-icm-1 spechelper-icm-2 spechelper-tmll \ - spechelper-tm laa vector + spechelper-tm laa vector lsc2 ppno if BUILD_DFP_TESTS INSN_TESTS += dfp-1 dfp-2 dfp-3 dfp-4 dfptest dfpext dfpconv srnmt pfpo endif @@ -65,3 +65,4 @@ fixbr_CFLAGS = $(AM_CFLAGS) @FLAG_MLONG_DOUBLE_128@ fpext_CFLAGS = $(AM_CFLAGS) @FLAG_MLONG_DOUBLE_128@ ex_clone_LDADD = -lpthread vector_CFLAGS = $(AM_CFLAGS) -march=z13 +lsc2_CFLAGS = -march=z13 -DS390_TESTS_NOCOLOR diff --git a/none/tests/s390x/lsc2.c b/none/tests/s390x/lsc2.c new file mode 100644 index 0000000..c3bface --- /dev/null +++ b/none/tests/s390x/lsc2.c @@ -0,0 +1,490 @@ +/* + * s390x z13 instructions test + * + * Copyright (c) 2017 Vadim Barkov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#define _GNU_SOURCE +#include "assert.h" +#include "math.h" +#include "stdbool.h" +#include "stdint.h" +#include "stdio.h" +#include "string.h" + +/* Colors for output */ +#ifndef S390_TESTS_NOCOLOR +# define RED "\033[31m" +# define GREEN "\033[32m" +# define RESET "\033[0m" +#else +# define RED +# define GREEN +# define RESET +#endif + +#define printPassed(insn) \ + printf(RESET "%15s :\t " GREEN "PASSED" RESET "\n", #insn) +#define printFailed(insn) \ + printf(RESET "%15s :\t " RED "FAILED" RESET "\n", #insn) + +#define test(insn) \ + { \ + if (test_##insn()) \ + printPassed(insn); \ + else \ + printFailed(insn); \ + } + +#define SMART_RETURN_R64(insn) \ + bool result = after == expected; \ + if (!result) { \ + printf("[ERROR] %s:\n", #insn); \ + printf("after: %lx\n", (uint64_t) after); \ + printf("expected: %lx\n", (uint64_t) expected); \ + } \ + return result + +/* Constant value for immediate instructions' tests */ +#define IMMEDIATE_VALUE 0x0abc +#define IMMEDIATE_VALUE_STR "0x0abc" + +/* Useful macros for testing many input values + and printing message after test execution + */ +#define test_each(type, var, instruction) \ + { \ + type##_foreach((var), test_##instruction((var))); \ + } + +#define test_range(var, from, to, instruction) \ + { \ + bool result = true; \ + __int_foreach(var, from, to, result &= (test_##instruction((var)))); \ + if (result) \ + printPassed(instruction); \ + else \ + printFailed(instruction); \ + } + +#define __int_foreach(var, minValue, maxValue, statement) \ + { \ + for (var = minValue; var < maxValue; var++) { \ + statement; \ + } \ + \ + { statement; } \ + } + +#define uint32_foreach(var, statement) \ + __int_foreach(var, 0, UINT32_MAX, statement) +#define int32_foreach(var, statement) \ + __int_foreach(var, INT32_MIN, INT32_MAX, statement) +#define uint64_foreach(var, statement) \ + __int_foreach(var, 0, UINT64_MAX, statement) + +/* load and zero rightmost byte */ +static bool test_lzrf(const uint32_t testedValue) +{ + uint32_t after = testedValue; + uint32_t expected = testedValue & 0xffffff00; + __asm__ volatile("lzrf %%r6, %0\n" + "st %%r6, %0\n" + : + : "g"(after)); + + SMART_RETURN_R64(lzrf); +} + +/* load and zero rightmost byte 64bit*/ +static bool test_lzrg(const uint64_t testedValue) +{ + uint64_t after = testedValue; + uint64_t expected = testedValue & 0xffffffffffffff00UL; + + __asm__ volatile("lzrg %%r6, %0\n" + "stg %%r6, %0\n" + : + : "g"(after)); + + SMART_RETURN_R64(lzrg); +} + +/* load logical and zero rightmost byte */ +static bool test_llzrgf(const uint32_t testedValue) +{ + uint64_t after = 0; + uint64_t expected = ((uint64_t)testedValue << 32) & 0xffffff0000000000UL; + + __asm__ volatile("llzrgf %%r6, %0\n" + "st %%r6, %1\n" + : + : "g"(testedValue), "g"(after)); + + SMART_RETURN_R64(llzrgf); +} + +/* compare instructions */ +#define __compare_r "cr" +#define __compare_m "c" + +/* load high on condition */ +#define declare_load_high_on_condition(TESTED_INSTRUCTION, CONDITION_SYMBOL, \ + ARGUMENT_ASM_TYPE) \ + bool test_##TESTED_INSTRUCTION(const int32_t testedValue) \ + { \ + uint64_t expected; \ + const uint64_t valueBeforeTest = 0x6666666699999999UL; \ + const uint64_t overrideValue = 0xeeeeeeeeeeeeeeeeUL; \ + const int32_t invertedValue = testedValue ^ 0xffffffff; \ + uint64_t after = valueBeforeTest; \ + if (testedValue CONDITION_SYMBOL invertedValue) { \ + expected = 0xeeeeeeee99999999UL; \ + } else { \ + expected = valueBeforeTest; \ + } \ + \ + __asm__ volatile( \ + "cr %[testedValue], %[invertedValue]\n" #TESTED_INSTRUCTION \ + " %[after], %[overrideValue]\n" \ + : [after] "=r"(after) \ + : [testedValue] "r"(testedValue), \ + [invertedValue] "r"(invertedValue), \ + [overrideValue] #ARGUMENT_ASM_TYPE(overrideValue), \ + "[after]"(after)); \ + \ + SMART_RETURN_R64(TESTED_INSTRUCTION); \ + } + +declare_load_high_on_condition(locfhre, ==, r) +declare_load_high_on_condition(locfhrne, !=, r) +declare_load_high_on_condition(locfhrh, >, r) +declare_load_high_on_condition(locfhrl, <, r) +declare_load_high_on_condition(locfhe, ==, m) +declare_load_high_on_condition(locfhne, !=, m) +declare_load_high_on_condition(locfhh, >, m) +declare_load_high_on_condition(locfhl, <, m) + +/* store high on condition */ +#define declare_store_high_on_condition(TESTED_INSTRUCTION, CONDITION_SYMBOL, \ + ARGUMENT_ASM_TYPE) \ + bool test_##TESTED_INSTRUCTION(const int32_t testedValue) \ + { \ + uint64_t expected; \ + const uint64_t valueBeforeTest = 0x6666666699999999UL; \ + const uint64_t overrideValue = 0xeeeeeeeeeeeeeeeeUL; \ + const int32_t invertedValue = testedValue ^ 0xffffffff; \ + uint64_t after = valueBeforeTest; \ + if (testedValue CONDITION_SYMBOL invertedValue) { \ + expected = 0xeeeeeeee99999999UL; \ + } else { \ + expected = valueBeforeTest; \ + } \ + \ + __asm__ volatile( \ + "cr %[testedValue], %[invertedValue]\n" #TESTED_INSTRUCTION \ + " %[overrideValue], %[after]\n" \ + : [after] "=" #ARGUMENT_ASM_TYPE(after) \ + : [testedValue] "r"(testedValue), \ + [invertedValue] "r"(invertedValue), \ + [overrideValue] "r"(overrideValue)); \ + \ + SMART_RETURN_R64(TESTED_INSTRUCTION); \ + } + +declare_store_high_on_condition(stocfhe, ==, m) +declare_store_high_on_condition(stocfhne, !=, m) +declare_store_high_on_condition(stocfhh, >, m) +declare_store_high_on_condition(stocfhl, <, m) + +#define __format_uint32_t "%x" +#define __format_uint64_t "%lx" +#define __halfword_valueBefore_uint32_t 0x66669999 +#define __halfword_valueBefore_uint64_t 0x6666666699999999UL +/* load halfword immediate on condition */ +#define declare_load_halfword_immediate_on_condition( \ + TESTED_INSTRUCTION, ARGUMENT_TYPE, CONDITION_SYMBOL, ARGUMENT_ASM_TYPE) \ + bool test_##TESTED_INSTRUCTION(const int32_t testedValue) \ + { \ + ARGUMENT_TYPE expected; \ + const ARGUMENT_TYPE valueBeforeTest = \ + __halfword_valueBefore_##ARGUMENT_TYPE; \ + const int32_t invertedValue = testedValue ^ 0xffffffff; \ + ARGUMENT_TYPE after = valueBeforeTest; \ + if (testedValue CONDITION_SYMBOL invertedValue) { \ + expected = IMMEDIATE_VALUE; \ + } else { \ + expected = valueBeforeTest; \ + } \ + \ + __asm__ volatile( \ + "cr %[testedValue], %[invertedValue]\n" #TESTED_INSTRUCTION \ + " %[after], " IMMEDIATE_VALUE_STR "\n" \ + : [after] "=r"(after) \ + : [testedValue] "r"(testedValue), \ + [invertedValue] "r"(invertedValue), \ + "[after]"(after)); \ + \ + SMART_RETURN_R64(TESTED_INSTRUCTION); \ + } + +declare_load_halfword_immediate_on_condition(lochie, uint32_t, ==, r) +declare_load_halfword_immediate_on_condition(lochine, uint32_t, !=, r) +declare_load_halfword_immediate_on_condition(lochih, uint32_t, >, r) +declare_load_halfword_immediate_on_condition(lochil, uint32_t, <, r) +declare_load_halfword_immediate_on_condition(locghie, uint64_t, ==, r) +declare_load_halfword_immediate_on_condition(locghine, uint64_t, !=, r) +declare_load_halfword_immediate_on_condition(locghih, uint64_t, >, r) +declare_load_halfword_immediate_on_condition(locghil, uint64_t, <, r) + +/* load halfword high immediate on condition */ +#define declare_load_halfword_high_immediate_on_condition( \ + TESTED_INSTRUCTION, CONDITION_SYMBOL, ARGUMENT_ASM_TYPE) \ + bool test_##TESTED_INSTRUCTION(const int32_t testedValue) \ + { \ + uint64_t expected; \ + const uint64_t valueBeforeTest = __halfword_valueBefore_uint64_t; \ + const int32_t invertedValue = testedValue ^ 0xffffffff; \ + uint64_t after = valueBeforeTest; \ + if (testedValue CONDITION_SYMBOL invertedValue) { \ + expected = IMMEDIATE_VALUE; \ + expected <<= 32; \ + expected += \ + __halfword_valueBefore_uint64_t & 0x00000000ffffffffUL; \ + } else { \ + expected = valueBeforeTest; \ + } \ + \ + __asm__ volatile( \ + "cr %[testedValue], %[invertedValue]\n" #TESTED_INSTRUCTION \ + " %[after], " IMMEDIATE_VALUE_STR "\n" \ + : [after] "=r"(after) \ + : [testedValue] "r"(testedValue), \ + [invertedValue] "r"(invertedValue), \ + "[after]"(after)); \ + \ + SMART_RETURN_R64(TESTED_INSTRUCTION); \ + } + +declare_load_halfword_high_immediate_on_condition(lochhie, ==, r) +declare_load_halfword_high_immediate_on_condition(lochhine, !=, r) +declare_load_halfword_high_immediate_on_condition(lochhih, >, r) +declare_load_halfword_high_immediate_on_condition(lochhil, <, r) + +static void test_all_locfh() +{ + int32_t signed32bit = 0; + + test_each(int32, signed32bit, locfhe); + test_each(int32, signed32bit, locfhne); + test_each(int32, signed32bit, locfhh); + test_each(int32, signed32bit, locfhl); + + test_each(int32, signed32bit, locfhre); + test_each(int32, signed32bit, locfhrne); + test_each(int32, signed32bit, locfhrh); + test_each(int32, signed32bit, locfhrl); +} + +/* load count to block boundary */ +#define declare_load_count_to_block_boundary(M_FIELD) \ + bool test_lcbb##M_FIELD(const uint32_t testedValue) \ + { \ + const size_t boundary = 64 * pow(2, M_FIELD); \ + const uint32_t *testedPointer = \ + (uint32_t *)(boundary - \ + ((testedValue < boundary) ? testedValue : 0)); \ + uint32_t after = 0; \ + uint32_t expected = boundary - ((size_t)testedPointer % boundary); \ + if (expected > 16) \ + expected = 16; \ + \ + __asm__ volatile("lcbb %[after], %[testedPointer], " #M_FIELD \ + "\n" \ + : [after] "=r"(after) \ + : [testedPointer] "m"(*testedPointer)); \ + \ + SMART_RETURN_R64(lcbb##M_FIELD); \ + } + +declare_load_count_to_block_boundary(0) +declare_load_count_to_block_boundary(1) +declare_load_count_to_block_boundary(2) +declare_load_count_to_block_boundary(3) +declare_load_count_to_block_boundary(4) +declare_load_count_to_block_boundary(5) +declare_load_count_to_block_boundary(6) + +static bool test_lcbb0_cc(const uint32_t testedValue) +{ + const size_t boundary = 64; + const uint32_t *testedPointer = + (uint32_t *)(boundary - + ((testedValue < boundary) ? testedValue : 0)); + uint32_t expectedForLCBB = boundary - ((size_t)testedPointer % boundary); + + uint32_t after = 0; + uint32_t expected = (expectedForLCBB >= 16) ? 0 : 3; + __asm__ volatile("lcbb %[cc], %[testedPointer], 0 \n" + "ipm %[cc] \n" + "srl %[cc], 28 \n" + : [cc] "=d"(after) + : [testedPointer] "m"(*testedPointer) + : "cc"); + SMART_RETURN_R64(lcbb0_cc); +} + + +static void test_all_lzr() +{ + uint32_t unsigned32bit = 0; + + test_each(uint32, unsigned32bit, lzrf); + test_each(uint32, unsigned32bit, lzrg); +} + +static void test_all_stocfh() +{ + int32_t signed32bit = 0; + + test_each(int32, signed32bit, stocfhne); + test_each(int32, signed32bit, stocfhe); + test_each(int32, signed32bit, stocfhh); + test_each(int32, signed32bit, stocfhl); +} + +static void test_all_lochi() +{ + int32_t signed32bit = 0; + + test_each(int32, signed32bit, locghine); + test_each(int32, signed32bit, locghine); + test_each(int32, signed32bit, locghih); + test_each(int32, signed32bit, locghil); + + test_each(int32, signed32bit, lochine); + test_each(int32, signed32bit, lochie); + test_each(int32, signed32bit, lochil); + test_each(int32, signed32bit, lochih); +} + +static void test_all_lochhi() +{ + int32_t signed32bit = 0; + + test_each(int32, signed32bit, lochhine); + test_each(int32, signed32bit, lochhie); + test_each(int32, signed32bit, lochhih); + test_each(int32, signed32bit, lochhil); +} + +static void test_all_lcbb() +{ + size_t tested = 0; + + test_range(tested, 0, 64, lcbb0); + test_range(tested, 0, 128, lcbb1); + test_range(tested, 0, 256, lcbb2); + test_range(tested, 0, 512, lcbb3); + test_range(tested, 0, 1024, lcbb4); + test_range(tested, 0, 2048, lcbb5); + test_range(tested, 0, 4096, lcbb6); + test_range(tested, 0, 64, lcbb0_cc); +} + +void test_long_all() { + uint64_t unsigned64bit = 0; + + test_all_lcbb(); + test_all_lochhi(); + test_all_lochi(); + test_all_stocfh(); + test_all_locfh(); + test_all_lzr(); + test_each(uint64, unsigned64bit, llzrgf); +} + +#define SHORT_TESTS_UNSIGNED_FROM 0 +#define SHORT_TESTS_SIGNED_FROM -0xffff +#define SHORT_TESTS_TO 0xffff + +void test_short_all() +{ + uint32_t unsigned32bit = 0; + int32_t signed32bit = 0; + uint64_t unsigned64bit = 0; + + test_range(unsigned32bit, SHORT_TESTS_UNSIGNED_FROM, SHORT_TESTS_TO, lzrf); + test_range(unsigned64bit, SHORT_TESTS_UNSIGNED_FROM, SHORT_TESTS_TO, lzrg); + test_range(unsigned64bit, SHORT_TESTS_UNSIGNED_FROM, SHORT_TESTS_TO, llzrgf); + + /* stocfh */ + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, stocfhne); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, stocfhe); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, stocfhh); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, stocfhl); + + /* locfh */ + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhe); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhne); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhh); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhl); + + /* locfhr */ + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhre); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhrne); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhrh); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhrl); + + /* lochhi */ + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochhine); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochhie); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochhih); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochhil); + + /* lochi */ + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochine); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochie); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochih); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochil); + + /* locghi */ + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locghine); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locghie); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locghih); + test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locghil); + + test_all_lcbb(); /* These test is not long, so we can run it on all range */ +} + +int main(int argc, char *argv[]) +{ + bool shouldRunLongTests = false; + + /* --long option forces to test all possible values (it is very long)*/ + if (argc > 1) + if (strcmp(argv[1], "--long") == 0) + shouldRunLongTests = true; + + printf("Tests started:\n"); + if (shouldRunLongTests) + test_long_all(); + else + test_short_all(); + + printf("Tests ended.\n"); + return 0; +} diff --git a/none/tests/s390x/lsc2.stderr.exp b/none/tests/s390x/lsc2.stderr.exp new file mode 100644 index 0000000..139597f --- /dev/null +++ b/none/tes... [truncated message content] |