/* -*- mode: C; c-basic-offset: 3; -*- */ /*---------------------------------------------------------------*/ /*--- begin guest_s390_helpers.c ---*/ /*---------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright IBM Corp. 2010-2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. The GNU General Public License is contained in the file COPYING. */ /* Contributed by Florian Krohm */ #include "libvex_basictypes.h" #include "libvex_emwarn.h" #include "libvex_guest_s390x.h" #include "libvex_ir.h" #include "libvex.h" #include "libvex_s390x_common.h" #include "main_util.h" #include "guest_generic_bb_to_IR.h" #include "guest_s390_defs.h" void LibVEX_GuestS390X_initialise(VexGuestS390XState *state) { /*------------------------------------------------------------*/ /*--- Initialise ar registers ---*/ /*------------------------------------------------------------*/ state->guest_a0 = 0; state->guest_a1 = 0; state->guest_a2 = 0; state->guest_a3 = 0; state->guest_a4 = 0; state->guest_a5 = 0; state->guest_a6 = 0; state->guest_a7 = 0; state->guest_a8 = 0; state->guest_a9 = 0; state->guest_a10 = 0; state->guest_a11 = 0; state->guest_a12 = 0; state->guest_a13 = 0; state->guest_a14 = 0; state->guest_a15 = 0; /*------------------------------------------------------------*/ /*--- Initialise fpr registers ---*/ /*------------------------------------------------------------*/ state->guest_f0 = 0; state->guest_f1 = 0; state->guest_f2 = 0; state->guest_f3 = 0; state->guest_f4 = 0; state->guest_f5 = 0; state->guest_f6 = 0; state->guest_f7 = 0; state->guest_f8 = 0; state->guest_f9 = 0; state->guest_f10 = 0; state->guest_f11 = 0; state->guest_f12 = 0; state->guest_f13 = 0; state->guest_f14 = 0; state->guest_f15 = 0; /*------------------------------------------------------------*/ /*--- Initialise gpr registers ---*/ /*------------------------------------------------------------*/ state->guest_r0 = 0; state->guest_r1 = 0; state->guest_r2 = 0; state->guest_r3 = 0; state->guest_r4 = 0; state->guest_r5 = 0; state->guest_r6 = 0; state->guest_r7 = 0; state->guest_r8 = 0; state->guest_r9 = 0; state->guest_r10 = 0; state->guest_r11 = 0; state->guest_r12 = 0; state->guest_r13 = 0; state->guest_r14 = 0; state->guest_r15 = 0; /*------------------------------------------------------------*/ /*--- Initialise S390 miscellaneous registers ---*/ /*------------------------------------------------------------*/ state->guest_counter = 0; state->guest_fpc = 0; state->guest_IA = 0; /*------------------------------------------------------------*/ /*--- Initialise S390 pseudo registers ---*/ /*------------------------------------------------------------*/ state->guest_SYSNO = 0; /*------------------------------------------------------------*/ /*--- Initialise generic pseudo registers ---*/ /*------------------------------------------------------------*/ state->guest_NRADDR = 0; state->guest_TISTART = 0; state->guest_TILEN = 0; state->guest_IP_AT_SYSCALL = 0; state->guest_EMWARN = EmWarn_NONE; /*------------------------------------------------------------*/ /*--- Initialise thunk ---*/ /*------------------------------------------------------------*/ state->guest_CC_OP = 0; state->guest_CC_DEP1 = 0; state->guest_CC_DEP2 = 0; state->guest_CC_NDEP = 0; } /* Figure out if any part of the guest state contained in minoff .. maxoff requires precise memory exceptions. If in doubt return True (but this is generates significantly slower code). */ Bool guest_s390x_state_requires_precise_mem_exns(Int minoff, Int maxoff) { Int lr_min = S390X_GUEST_OFFSET(guest_LR); Int lr_max = lr_min + 8 - 1; Int sp_min = S390X_GUEST_OFFSET(guest_SP); Int sp_max = sp_min + 8 - 1; Int fp_min = S390X_GUEST_OFFSET(guest_FP); Int fp_max = fp_min + 8 - 1; Int ia_min = S390X_GUEST_OFFSET(guest_IA); Int ia_max = ia_min + 8 - 1; if (maxoff < lr_min || minoff > lr_max) { /* No overlap with LR */ } else { return True; } if (maxoff < sp_min || minoff > sp_max) { /* No overlap with SP */ } else { return True; } if (maxoff < fp_min || minoff > fp_max) { /* No overlap with FP */ } else { return True; } if (maxoff < ia_min || minoff > ia_max) { /* No overlap with IA */ } else { return True; } return False; } #define ALWAYSDEFD(field) \ { S390X_GUEST_OFFSET(field), \ (sizeof ((VexGuestS390XState*)0)->field) } VexGuestLayout s390xGuest_layout = { /* Total size of the guest state, in bytes. */ .total_sizeB = sizeof(VexGuestS390XState), /* Describe the stack pointer. */ .offset_SP = S390X_GUEST_OFFSET(guest_SP), .sizeof_SP = 8, /* Describe the frame pointer. */ .offset_FP = S390X_GUEST_OFFSET(guest_FP), .sizeof_FP = 8, /* Describe the instruction pointer. */ .offset_IP = S390X_GUEST_OFFSET(guest_IA), .sizeof_IP = 8, /* Describe any sections to be regarded by Memcheck as 'always-defined'. */ .n_alwaysDefd = 9, /* Flags thunk: OP and NDEP are always defined, whereas DEP1 and DEP2 have to be tracked. See detailed comment in gdefs.h on meaning of thunk fields. */ .alwaysDefd = { /* 0 */ ALWAYSDEFD(guest_CC_OP), /* generic */ /* 1 */ ALWAYSDEFD(guest_CC_NDEP), /* generic */ /* 2 */ ALWAYSDEFD(guest_EMWARN), /* generic */ /* 3 */ ALWAYSDEFD(guest_TISTART), /* generic */ /* 4 */ ALWAYSDEFD(guest_TILEN), /* generic */ /* 5 */ ALWAYSDEFD(guest_IP_AT_SYSCALL), /* generic */ /* 6 */ ALWAYSDEFD(guest_IA), /* control reg */ /* 7 */ ALWAYSDEFD(guest_fpc), /* control reg */ /* 8 */ ALWAYSDEFD(guest_counter), /* internal usage register */ } }; /*------------------------------------------------------------*/ /*--- Dirty helper for invalid opcode 00 ---*/ /*------------------------------------------------------------*/ #if defined(VGA_s390x) void s390x_dirtyhelper_00(VexGuestS390XState *guest_state) { /* Avoid infinite loop in case SIGILL is caught. See also none/tests/s390x/op_exception.c */ guest_state->guest_IA += 2; asm volatile(".hword 0\n"); } #else void s390x_dirtyhelper_00(VexGuestS390XState *guest_state) { } #endif /*------------------------------------------------------------*/ /*--- Dirty helper for EXecute ---*/ /*------------------------------------------------------------*/ void s390x_dirtyhelper_EX(ULong torun) { last_execute_target = torun; } /*------------------------------------------------------------*/ /*--- Dirty helper for Clock instructions ---*/ /*------------------------------------------------------------*/ #if defined(VGA_s390x) ULong s390x_dirtyhelper_STCK(ULong *addr) { int cc; asm volatile("stck %0\n" "ipm %1\n" "srl %1,28\n" : "+Q" (*addr), "=d" (cc) : : "cc"); return cc; } ULong s390x_dirtyhelper_STCKE(ULong *addr) { int cc; asm volatile("stcke %0\n" "ipm %1\n" "srl %1,28\n" : "+Q" (*addr), "=d" (cc) : : "cc"); return cc; } ULong s390x_dirtyhelper_STCKF(ULong *addr) { int cc; asm volatile(".insn s,0xb27c0000,%0\n" "ipm %1\n" "srl %1,28\n" : "+Q" (*addr), "=d" (cc) : : "cc"); return cc; } #else ULong s390x_dirtyhelper_STCK(ULong *addr) {return 3;} ULong s390x_dirtyhelper_STCKF(ULong *addr) {return 3;} ULong s390x_dirtyhelper_STCKE(ULong *addr) {return 3;} #endif /* VGA_s390x */ /*------------------------------------------------------------*/ /*--- Dirty helper for Store Facility instruction ---*/ /*------------------------------------------------------------*/ #if defined(VGA_s390x) ULong s390x_dirtyhelper_STFLE(VexGuestS390XState *guest_state, HWord addr) { ULong hoststfle[S390_NUM_FACILITY_DW], cc, num_dw, i; register ULong reg0 asm("0") = guest_state->guest_r0 & 0xF; /* r0[56:63] */ /* We cannot store more than S390_NUM_FACILITY_DW (and it makes not much sense to do so anyhow) */ if (reg0 > S390_NUM_FACILITY_DW - 1) reg0 = S390_NUM_FACILITY_DW - 1; num_dw = reg0 + 1; /* number of double words written */ asm volatile(" .insn s,0xb2b00000,%0\n" /* stfle */ "ipm %2\n" "srl %2,28\n" : "=m" (hoststfle), "+d"(reg0), "=d"(cc) : : "cc", "memory"); /* Update guest register 0 with what STFLE set r0 to */ guest_state->guest_r0 = reg0; for (i = 0; i < num_dw; ++i) ((ULong *)addr)[i] = hoststfle[i]; return cc; } #else ULong s390x_dirtyhelper_STFLE(VexGuestS390XState *guest_state, HWord addr) { return 3; } #endif /* VGA_s390x */ /*------------------------------------------------------------*/ /*--- Helper for condition code. ---*/ /*------------------------------------------------------------*/ #define S390_CC_FOR_BINARY(opcode,cc_dep1,cc_dep2) \ ({ \ __asm__ volatile ( \ opcode " %[op1],%[op2]\n\t" \ "ipm %[psw]\n\t" : [psw] "=d"(psw), [op1] "+d"(cc_dep1) \ : [op2] "d"(cc_dep2) \ : "cc");\ psw >> 28; /* cc */ \ }) #define S390_CC_FOR_TERNARY_SUBB(opcode,cc_dep1,cc_dep2,cc_ndep) \ ({ \ /* Recover the original DEP2 value. See comment near s390_cc_thunk_put3 \ for rationale. */ \ cc_dep2 = cc_dep2 ^ cc_ndep; \ __asm__ volatile ( \ "lghi 0,1\n\t" \ "sr 0,%[op3]\n\t" /* borrow to cc */ \ opcode " %[op1],%[op2]\n\t" /* then redo the op */\ "ipm %[psw]\n\t" : [psw] "=d"(psw), [op1] "+&d"(cc_dep1) \ : [op2] "d"(cc_dep2), [op3] "d"(cc_ndep) \ : "0", "cc");\ psw >> 28; /* cc */ \ }) #define S390_CC_FOR_TERNARY_ADDC(opcode,cc_dep1,cc_dep2,cc_ndep) \ ({ \ /* Recover the original DEP2 value. See comment near s390_cc_thunk_put3 \ for rationale. */ \ cc_dep2 = cc_dep2 ^ cc_ndep; \ __asm__ volatile ( \ "lgfr 0,%[op3]\n\t" /* first load cc_ndep */ \ "aghi 0,0\n\t" /* and convert it into a cc */ \ opcode " %[op1],%[op2]\n\t" /* then redo the op */\ "ipm %[psw]\n\t" : [psw] "=d"(psw), [op1] "+&d"(cc_dep1) \ : [op2] "d"(cc_dep2), [op3] "d"(cc_ndep) \ : "0", "cc");\ psw >> 28; /* cc */ \ }) #define S390_CC_FOR_BFP_RESULT(opcode,cc_dep1) \ ({ \ __asm__ volatile ( \ opcode " 0,%[op]\n\t" \ "ipm %[psw]\n\t" : [psw] "=d"(psw) \ : [op] "f"(cc_dep1) \ : "cc", "f0");\ psw >> 28; /* cc */ \ }) #define S390_CC_FOR_BFP128_RESULT(hi,lo) \ ({ \ __asm__ volatile ( \ "ldr 4,%[high]\n\t" \ "ldr 6,%[low]\n\t" \ "ltxbr 0,4\n\t" \ "ipm %[psw]\n\t" : [psw] "=d"(psw) \ : [high] "f"(hi), [low] "f"(lo) \ : "cc", "f0", "f2", "f4", "f6");\ psw >> 28; /* cc */ \ }) #define S390_CC_FOR_BFP_CONVERT(opcode,cc_dep1) \ ({ \ __asm__ volatile ( \ opcode " 0,0,%[op]\n\t" \ "ipm %[psw]\n\t" : [psw] "=d"(psw) \ : [op] "f"(cc_dep1) \ : "cc", "r0");\ psw >> 28; /* cc */ \ }) #define S390_CC_FOR_BFP128_CONVERT(opcode,hi,lo) \ ({ \ __asm__ volatile ( \ "ldr 4,%[high]\n\t" \ "ldr 6,%[low]\n\t" \ opcode " 0,0,4\n\t" \ "ipm %[psw]\n\t" : [psw] "=d"(psw) \ : [high] "f"(hi), [low] "f"(lo) \ : "cc", "r0", "f4", "f6");\ psw >> 28; /* cc */ \ }) #define S390_CC_FOR_BFP_TDC(opcode,cc_dep1,cc_dep2) \ ({ \ __asm__ volatile ( \ opcode " %[value],0(%[class])\n\t" \ "ipm %[psw]\n\t" : [psw] "=d"(psw) \ : [value] "f"(cc_dep1), \ [class] "a"(cc_dep2) \ : "cc");\ psw >> 28; /* cc */ \ }) #define S390_CC_FOR_BFP128_TDC(cc_dep1,cc_dep2,cc_ndep) \ ({ \ /* Recover the original DEP2 value. See comment near s390_cc_thunk_put1f128Z \ for rationale. */ \ cc_dep2 = cc_dep2 ^ cc_ndep; \ __asm__ volatile ( \ "ldr 4,%[high]\n\t" \ "ldr 6,%[low]\n\t" \ "tcxb 4,0(%[class])\n\t" \ "ipm %[psw]\n\t" : [psw] "=d"(psw) \ : [high] "f"(cc_dep1), [low] "f"(cc_dep2), \ [class] "a"(cc_ndep) \ : "cc", "f4", "f6");\ psw >> 28; /* cc */ \ }) /* Return the value of the condition code from the supplied thunk parameters. This is not the value of the PSW. It is the value of the 2 CC bits within the PSW. The returned value is thusly in the interval [0:3]. */ UInt s390_calculate_cc(ULong cc_op, ULong cc_dep1, ULong cc_dep2, ULong cc_ndep) { #if defined(VGA_s390x) UInt psw; switch (cc_op) { case S390_CC_OP_BITWISE: return S390_CC_FOR_BINARY("ogr", cc_dep1, (ULong)0); case S390_CC_OP_SIGNED_COMPARE: return S390_CC_FOR_BINARY("cgr", cc_dep1, cc_dep2); case S390_CC_OP_UNSIGNED_COMPARE: return S390_CC_FOR_BINARY("clgr", cc_dep1, cc_dep2); case S390_CC_OP_SIGNED_ADD_64: return S390_CC_FOR_BINARY("agr", cc_dep1, cc_dep2); case S390_CC_OP_SIGNED_ADD_32: return S390_CC_FOR_BINARY("ar", cc_dep1, cc_dep2); case S390_CC_OP_SIGNED_SUB_64: return S390_CC_FOR_BINARY("sgr", cc_dep1, cc_dep2); case S390_CC_OP_SIGNED_SUB_32: return S390_CC_FOR_BINARY("sr", cc_dep1, cc_dep2); case S390_CC_OP_UNSIGNED_ADD_64: return S390_CC_FOR_BINARY("algr", cc_dep1, cc_dep2); case S390_CC_OP_UNSIGNED_ADD_32: return S390_CC_FOR_BINARY("alr", cc_dep1, cc_dep2); case S390_CC_OP_UNSIGNED_ADDC_64: return S390_CC_FOR_TERNARY_ADDC("alcgr", cc_dep1, cc_dep2, cc_ndep); case S390_CC_OP_UNSIGNED_ADDC_32: return S390_CC_FOR_TERNARY_ADDC("alcr", cc_dep1, cc_dep2, cc_ndep); case S390_CC_OP_UNSIGNED_SUB_64: return S390_CC_FOR_BINARY("slgr", cc_dep1, cc_dep2); case S390_CC_OP_UNSIGNED_SUB_32: return S390_CC_FOR_BINARY("slr", cc_dep1, cc_dep2); case S390_CC_OP_UNSIGNED_SUBB_64: return S390_CC_FOR_TERNARY_SUBB("slbgr", cc_dep1, cc_dep2, cc_ndep); case S390_CC_OP_UNSIGNED_SUBB_32: return S390_CC_FOR_TERNARY_SUBB("slbr", cc_dep1, cc_dep2, cc_ndep); case S390_CC_OP_LOAD_AND_TEST: /* Like signed comparison with 0 */ return S390_CC_FOR_BINARY("cgr", cc_dep1, (Long)0); case S390_CC_OP_TEST_AND_SET: /* Shift the sign bit into the LSB. Note, that the tested value is an 8-bit value which has been zero-extended to 32/64 bit. */ return cc_dep1 >> 7; case S390_CC_OP_LOAD_POSITIVE_32: __asm__ volatile ( "lpr %[result],%[op]\n\t" "ipm %[psw]\n\t" : [psw] "=d"(psw), [result] "=d"(cc_dep1) : [op] "d"(cc_dep1) : "cc"); return psw >> 28; /* cc */ case S390_CC_OP_LOAD_POSITIVE_64: __asm__ volatile ( "lpgr %[result],%[op]\n\t" "ipm %[psw]\n\t" : [psw] "=d"(psw), [result] "=d"(cc_dep1) : [op] "d"(cc_dep1) : "cc"); return psw >> 28; /* cc */ case S390_CC_OP_TEST_UNDER_MASK_8: { UChar value = cc_dep1; UChar mask = cc_dep2; __asm__ volatile ( "bras %%r2,1f\n\t" /* %r2 = address of next insn */ "tm %[value],0\n\t" /* this is skipped, then EXecuted */ "1: ex %[mask],0(%%r2)\n\t" /* EXecute TM after modifying mask */ "ipm %[psw]\n\t" : [psw] "=d"(psw) : [value] "m"(value), [mask] "a"(mask) : "r2", "cc"); return psw >> 28; /* cc */ } case S390_CC_OP_TEST_UNDER_MASK_16: { /* Create a TMLL insn with the mask as given by cc_dep2 */ UInt insn = (0xA701 << 16) | cc_dep2; UInt value = cc_dep1; __asm__ volatile ( "lr 1,%[value]\n\t" "lhi 2,0x10\n\t" "ex 2,%[insn]\n\t" "ipm %[psw]\n\t" : [psw] "=d"(psw) : [value] "d"(value), [insn] "m"(insn) : "r1", "r2", "cc"); return psw >> 28; /* cc */ } case S390_CC_OP_SHIFT_LEFT_32: __asm__ volatile ( "sla %[op],0(%[amount])\n\t" "ipm %[psw]\n\t" : [psw] "=d"(psw), [op] "+d"(cc_dep1) : [amount] "a"(cc_dep2) : "cc"); return psw >> 28; /* cc */ case S390_CC_OP_SHIFT_LEFT_64: { Int high = (Int)(cc_dep1 >> 32); Int low = (Int)(cc_dep1 & 0xFFFFFFFF); __asm__ volatile ( "lr 2,%[high]\n\t" "lr 3,%[low]\n\t" "slda 2,0(%[amount])\n\t" "ipm %[psw]\n\t" : [psw] "=d"(psw), [high] "+d"(high), [low] "+d"(low) : [amount] "a"(cc_dep2) : "cc", "r2", "r3"); return psw >> 28; /* cc */ } case S390_CC_OP_INSERT_CHAR_MASK_32: { Int inserted = 0; Int msb = 0; if (cc_dep2 & 1) { inserted |= cc_dep1 & 0xff; msb = 0x80; } if (cc_dep2 & 2) { inserted |= cc_dep1 & 0xff00; msb = 0x8000; } if (cc_dep2 & 4) { inserted |= cc_dep1 & 0xff0000; msb = 0x800000; } if (cc_dep2 & 8) { inserted |= cc_dep1 & 0xff000000; msb = 0x80000000; } if (inserted & msb) // MSB is 1 return 1; if (inserted > 0) return 2; return 0; } case S390_CC_OP_BFP_RESULT_32: return S390_CC_FOR_BFP_RESULT("ltebr", cc_dep1); case S390_CC_OP_BFP_RESULT_64: return S390_CC_FOR_BFP_RESULT("ltdbr", cc_dep1); case S390_CC_OP_BFP_RESULT_128: return S390_CC_FOR_BFP128_RESULT(cc_dep1, cc_dep2); case S390_CC_OP_BFP_32_TO_INT_32: return S390_CC_FOR_BFP_CONVERT("cfebr", cc_dep1); case S390_CC_OP_BFP_64_TO_INT_32: return S390_CC_FOR_BFP_CONVERT("cfdbr", cc_dep1); case S390_CC_OP_BFP_128_TO_INT_32: return S390_CC_FOR_BFP128_CONVERT("cfxbr", cc_dep1, cc_dep2); case S390_CC_OP_BFP_32_TO_INT_64: return S390_CC_FOR_BFP_CONVERT("cgebr", cc_dep1); case S390_CC_OP_BFP_64_TO_INT_64: return S390_CC_FOR_BFP_CONVERT("cgdbr", cc_dep1); case S390_CC_OP_BFP_128_TO_INT_64: return S390_CC_FOR_BFP128_CONVERT("cgxbr", cc_dep1, cc_dep2); case S390_CC_OP_BFP_TDC_32: return S390_CC_FOR_BFP_TDC("tceb", cc_dep1, cc_dep2); case S390_CC_OP_BFP_TDC_64: return S390_CC_FOR_BFP_TDC("tcdb", cc_dep1, cc_dep2); case S390_CC_OP_BFP_TDC_128: return S390_CC_FOR_BFP128_TDC(cc_dep1, cc_dep2, cc_ndep); case S390_CC_OP_SET: return cc_dep1; default: break; } #endif vpanic("s390_calculate_cc"); } UInt s390_calculate_icc(ULong op, ULong dep1, ULong dep2) { return s390_calculate_cc(op, dep1, dep2, 0 /* unused */); } /* Note that this does *not* return a Boolean value. The result needs to be explicitly tested against zero. */ UInt s390_calculate_cond(ULong mask, ULong op, ULong dep1, ULong dep2, ULong ndep) { UInt cc = s390_calculate_cc(op, dep1, dep2, ndep); return ((mask << cc) & 0x8); } /*------------------------------------------------------------*/ /*--- spechelper for performance ---*/ /*------------------------------------------------------------*/ /* Convenience macros */ #define unop(op,a1) IRExpr_Unop((op),(a1)) #define binop(op,a1,a2) IRExpr_Binop((op),(a1),(a2)) #define mkU64(v) IRExpr_Const(IRConst_U64(v)) #define mkU32(v) IRExpr_Const(IRConst_U32(v)) #define mkU8(v) IRExpr_Const(IRConst_U8(v)) static inline Bool isC64(IRExpr *expr) { return expr->tag == Iex_Const && expr->Iex.Const.con->tag == Ico_U64; } /* The returned expression is NULL if no specialization was found. In that case the helper function will be called. Otherwise, the expression has type Ity_I32 and a Boolean value. */ IRExpr * guest_s390x_spechelper(HChar *function_name, IRExpr **args, IRStmt **precedingStmts, Int n_precedingStmts) { UInt i, arity = 0; for (i = 0; args[i]; i++) arity++; # if 0 vex_printf("spec request:\n"); vex_printf(" %s ", function_name); for (i = 0; i < arity; i++) { vex_printf(" "); ppIRExpr(args[i]); } vex_printf("\n"); # endif /* --------- Specialising "s390_calculate_cond" --------- */ if (vex_streq(function_name, "s390_calculate_cond")) { IRExpr *cond_expr, *cc_op_expr, *cc_dep1, *cc_dep2; ULong cond, cc_op; vassert(arity == 5); cond_expr = args[0]; cc_op_expr = args[1]; /* The necessary requirement for all optimizations here is that the condition and the cc_op are constant. So check that upfront. */ if (! isC64(cond_expr)) return NULL; if (! isC64(cc_op_expr)) return NULL; cond = cond_expr->Iex.Const.con->Ico.U64; cc_op = cc_op_expr->Iex.Const.con->Ico.U64; vassert(cond <= 15); /* +------+---+---+---+---+ | cc | 0 | 1 | 2 | 3 | | cond | 8 | 4 | 2 | 1 | +------+---+---+---+---+ */ cc_dep1 = args[2]; cc_dep2 = args[3]; /* S390_CC_OP_SIGNED_COMPARE */ if (cc_op == S390_CC_OP_SIGNED_COMPARE) { /* cc == 0 --> cc_dep1 == cc_dep2 (cond == 8) cc == 1 --> cc_dep1 < cc_dep2 (cond == 4) cc == 2 --> cc_dep1 > cc_dep2 (cond == 2) Because cc == 3 cannot occur the rightmost bit of cond is a don't care. */ if (cond == 8 || cond == 8 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpEQ64, cc_dep1, cc_dep2)); } if (cond == 4 + 2 || cond == 4 + 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpNE64, cc_dep1, cc_dep2)); } if (cond == 4 || cond == 4 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLT64S, cc_dep1, cc_dep2)); } if (cond == 8 + 4 || cond == 8 + 4 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLE64S, cc_dep1, cc_dep2)); } /* cc_dep1 > cc_dep2 ----> cc_dep2 < cc_dep1 */ if (cond == 2 || cond == 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLT64S, cc_dep2, cc_dep1)); } if (cond == 8 + 2 || cond == 8 + 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLE64S, cc_dep2, cc_dep1)); } if (cond == 8 + 4 + 2 || cond == 8 + 4 + 2 + 1) { return mkU32(1); } /* Remaining case */ return mkU32(0); } /* S390_CC_OP_UNSIGNED_COMPARE */ if (cc_op == S390_CC_OP_UNSIGNED_COMPARE) { /* cc == 0 --> cc_dep1 == cc_dep2 (cond == 8) cc == 1 --> cc_dep1 < cc_dep2 (cond == 4) cc == 2 --> cc_dep1 > cc_dep2 (cond == 2) Because cc == 3 cannot occur the rightmost bit of cond is a don't care. */ if (cond == 8 || cond == 8 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpEQ64, cc_dep1, cc_dep2)); } if (cond == 4 + 2 || cond == 4 + 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpNE64, cc_dep1, cc_dep2)); } if (cond == 4 || cond == 4 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLT64U, cc_dep1, cc_dep2)); } if (cond == 8 + 4 || cond == 8 + 4 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLE64U, cc_dep1, cc_dep2)); } /* cc_dep1 > cc_dep2 ----> cc_dep2 < cc_dep1 */ if (cond == 2 || cond == 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLT64U, cc_dep2, cc_dep1)); } if (cond == 8 + 2 || cond == 8 + 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLE64U, cc_dep2, cc_dep1)); } if (cond == 8 + 4 + 2 || cond == 8 + 4 + 2 + 1) { return mkU32(1); } /* Remaining case */ return mkU32(0); } /* S390_CC_OP_LOAD_AND_TEST */ if (cc_op == S390_CC_OP_LOAD_AND_TEST) { /* cc == 0 --> cc_dep1 == 0 (cond == 8) cc == 1 --> cc_dep1 < 0 (cond == 4) cc == 2 --> cc_dep1 > 0 (cond == 2) Because cc == 3 cannot occur the rightmost bit of cond is a don't care. */ if (cond == 8 || cond == 8 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpEQ64, cc_dep1, mkU64(0))); } if (cond == 4 + 2 || cond == 4 + 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpNE64, cc_dep1, mkU64(0))); } if (cond == 4 || cond == 4 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLT64S, cc_dep1, mkU64(0))); } if (cond == 8 + 4 || cond == 8 + 4 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLE64S, cc_dep1, mkU64(0))); } /* cc_dep1 > 0 ----> 0 < cc_dep1 */ if (cond == 2 || cond == 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLT64S, mkU64(0), cc_dep1)); } if (cond == 8 + 2 || cond == 8 + 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLE64S, mkU64(0), cc_dep1)); } if (cond == 8 + 4 + 2 || cond == 8 + 4 + 2 + 1) { return mkU32(1); } /* Remaining case */ return mkU32(0); } /* S390_CC_OP_BITWISE */ if (cc_op == S390_CC_OP_BITWISE) { /* cc_dep1 is the result of the boolean operation. cc == 0 --> cc_dep1 == 0 (cond == 8) cc == 1 --> cc_dep1 != 0 (cond == 4) Because cc == 2 and cc == 3 cannot occur the two rightmost bits of cond are don't cares. Therefore: cond == 00xx -> always false cond == 01xx -> not equal cond == 10xx -> equal cond == 11xx -> always true */ if ((cond & (8 + 4)) == 8 + 4) { return mkU32(1); } if (cond & 8) { return unop(Iop_1Uto32, binop(Iop_CmpEQ64, cc_dep1, mkU64(0))); } if (cond & 4) { return unop(Iop_1Uto32, binop(Iop_CmpNE64, cc_dep1, mkU64(0))); } /* Remaining case */ return mkU32(0); } /* S390_CC_OP_INSERT_CHAR_MASK_32 Since the mask comes from an immediate field in the opcode, we expect the mask to be a constant here. That simplifies matters. */ if (cc_op == S390_CC_OP_INSERT_CHAR_MASK_32) { ULong mask; UInt imask = 0, shift = 0; IRExpr *word; if (! isC64(cc_dep2)) goto missed; mask = cc_dep2->Iex.Const.con->Ico.U64; /* Extract the 32-bit value from the thunk */ word = unop(Iop_64to32, cc_dep1); switch (mask) { case 0: shift = 0; imask = 0x00000000; break; case 1: shift = 24; imask = 0x000000FF; break; case 2: shift = 16; imask = 0x0000FF00; break; case 3: shift = 16; imask = 0x0000FFFF; break; case 4: shift = 8; imask = 0x00FF0000; break; case 5: shift = 8; imask = 0x00FF00FF; break; case 6: shift = 8; imask = 0x00FFFF00; break; case 7: shift = 8; imask = 0x00FFFFFF; break; case 8: shift = 0; imask = 0xFF000000; break; case 9: shift = 0; imask = 0xFF0000FF; break; case 10: shift = 0; imask = 0xFF00FF00; break; case 11: shift = 0; imask = 0xFF00FFFF; break; case 12: shift = 0; imask = 0xFFFF0000; break; case 13: shift = 0; imask = 0xFFFF00FF; break; case 14: shift = 0; imask = 0xFFFFFF00; break; case 15: shift = 0; imask = 0xFFFFFFFF; break; } /* Select the bits that were inserted */ word = binop(Iop_And32, word, mkU32(imask)); /* cc == 0 --> all inserted bits zero or mask == 0 (cond == 8) cc == 1 --> leftmost inserted bit is one (cond == 4) cc == 2 --> leftmost inserted bit is zero and not (cond == 2) all inserted bits are zero Because cc == 0,1,2 the rightmost bit of the mask is a don't care */ if (cond == 8 || cond == 8 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpEQ32, word, mkU32(0))); } if (cond == 4 + 2 || cond == 4 + 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpNE32, word, mkU32(0))); } /* Sign extend */ if (shift != 0) { word = binop(Iop_Sar32, binop(Iop_Shl32, word, mkU8(shift)), mkU8(shift)); } if (cond == 4 || cond == 4 + 1) { /* word < 0 */ return unop(Iop_1Uto32, binop(Iop_CmpLT32S, word, mkU32(0))); } if (cond == 2 || cond == 2 + 1) { /* word > 0 */ return unop(Iop_1Uto32, binop(Iop_CmpLT32S, mkU32(0), word)); } if (cond == 8 + 4 || cond == 8 + 4 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLE32S, word, mkU32(0))); } if (cond == 8 + 2 || cond == 8 + 2 + 1) { return unop(Iop_1Uto32, binop(Iop_CmpLE32S, mkU32(0), word)); } if (cond == 8 + 4 + 2 || cond == 8 + 4 + 2 + 1) { return mkU32(1); } /* Remaining case */ return mkU32(0); } /* S390_CC_OP_TEST_UNDER_MASK_8 Since the mask comes from an immediate field in the opcode, we expect the mask to be a constant here. That simplifies matters. */ if (cc_op == S390_CC_OP_TEST_UNDER_MASK_8) { ULong mask16; if (! isC64(cc_dep2)) goto missed; mask16 = cc_dep2->Iex.Const.con->Ico.U64; /* Get rid of the mask16 == 0 case first. Some of the simplifications below (e.g. for OVFL) only hold if mask16 == 0. */ if (mask16 == 0) { /* cc == 0 */ if (cond & 0x8) return mkU32(1); return mkU32(0); } /* cc == 2 is a don't care */ if (cond == 8 || cond == 8 + 2) { return unop(Iop_1Uto32, binop(Iop_CmpEQ64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 7 || cond == 7 - 2) { return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 1 || cond == 1 + 2) { return unop(Iop_1Uto32, binop(Iop_CmpEQ64, binop(Iop_And64, cc_dep1, cc_dep2), cc_dep2)); } if (cond == 14 || cond == 14 - 2) { /* ! OVFL */ return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, cc_dep2), cc_dep2)); } goto missed; } /* S390_CC_OP_TEST_UNDER_MASK_16 Since the mask comes from an immediate field in the opcode, we expect the mask to be a constant here. That simplifies matters. */ if (cc_op == S390_CC_OP_TEST_UNDER_MASK_16) { ULong mask16; UInt msb; if (! isC64(cc_dep2)) goto missed; mask16 = cc_dep2->Iex.Const.con->Ico.U64; /* Get rid of the mask16 == 0 case first. Some of the simplifications below (e.g. for OVFL) only hold if mask16 == 0. */ if (mask16 == 0) { /* cc == 0 */ if (cond & 0x8) return mkU32(1); return mkU32(0); } if (cond == 8) { return unop(Iop_1Uto32, binop(Iop_CmpEQ64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 7) { return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 1) { return unop(Iop_1Uto32, binop(Iop_CmpEQ64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(mask16))); } if (cond == 14) { /* ! OVFL */ return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(mask16))); } /* Find MSB in mask */ msb = 0x8000; while (msb > mask16) msb >>= 1; if (cond == 2) { /* cc == 2 */ IRExpr *c1, *c2; /* (cc_dep & msb) != 0 && (cc_dep & mask16) != mask16 */ c1 = binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, mkU64(msb)), mkU64(0)); c2 = binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(mask16)); return binop(Iop_And32, unop(Iop_1Uto32, c1), unop(Iop_1Uto32, c2)); } if (cond == 4) { /* cc == 1 */ IRExpr *c1, *c2; /* (cc_dep & msb) == 0 && (cc_dep & mask16) != 0 */ c1 = binop(Iop_CmpEQ64, binop(Iop_And64, cc_dep1, mkU64(msb)), mkU64(0)); c2 = binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(0)); return binop(Iop_And32, unop(Iop_1Uto32, c1), unop(Iop_1Uto32, c2)); } if (cond == 11) { /* cc == 0,2,3 */ IRExpr *c1, *c2; c1 = binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, mkU64(msb)), mkU64(0)); c2 = binop(Iop_CmpEQ64, binop(Iop_And64, cc_dep1, cc_dep2), mkU64(0)); return binop(Iop_Or32, unop(Iop_1Uto32, c1), unop(Iop_1Uto32, c2)); } if (cond == 3) { /* cc == 2 || cc == 3 */ return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_And64, cc_dep1, mkU64(msb)), mkU64(0))); } if (cond == 12) { /* cc == 0 || cc == 1 */ return unop(Iop_1Uto32, binop(Iop_CmpEQ64, binop(Iop_And64, cc_dep1, mkU64(msb)), mkU64(0))); } // vex_printf("TUM mask = 0x%llx\n", mask16); goto missed; } /* S390_CC_OP_UNSIGNED_SUB_64/32 */ if (cc_op == S390_CC_OP_UNSIGNED_SUB_64 || cc_op == S390_CC_OP_UNSIGNED_SUB_32) { /* cc_dep1, cc_dep2 are the zero extended left and right operands cc == 1 --> result != 0, borrow (cond == 4) cc == 2 --> result == 0, no borrow (cond == 2) cc == 3 --> result != 0, no borrow (cond == 1) cc = (cc_dep1 == cc_dep2) ? 2 : (cc_dep1 > cc_dep2) ? 3 : 1; Because cc == 0 cannot occur the leftmost bit of cond is a don't care. */ if (cond == 1 || cond == 1 + 8) { /* cc == 3 op2 < op1 */ return unop(Iop_1Uto32, binop(Iop_CmpLT64U, cc_dep2, cc_dep1)); } if (cond == 2 || cond == 2 + 8) { /* cc == 2 */ return unop(Iop_1Uto32, binop(Iop_CmpEQ64, cc_dep1, cc_dep2)); } if (cond == 4 || cond == 4 + 8) { /* cc == 1 */ return unop(Iop_1Uto32, binop(Iop_CmpLT64U, cc_dep1, cc_dep2)); } if (cond == 3 || cond == 3 + 8) { /* cc == 2 || cc == 3 */ return unop(Iop_1Uto32, binop(Iop_CmpLE64U, cc_dep2, cc_dep1)); } if (cond == 6 || cond == 6 + 8) { /* cc == 2 || cc == 1 */ return unop(Iop_1Uto32, binop(Iop_CmpLE64U, cc_dep1, cc_dep2)); } if (cond == 5 || cond == 5 + 8) { /* cc == 3 || cc == 1 */ return unop(Iop_1Uto32, binop(Iop_CmpNE64, cc_dep1, cc_dep2)); } if (cond == 7 || cond == 7 + 8) { return mkU32(1); } /* Remaining case */ return mkU32(0); } /* S390_CC_OP_UNSIGNED_ADD_64 */ if (cc_op == S390_CC_OP_UNSIGNED_ADD_64) { /* cc_dep1, cc_dep2 are the zero extended left and right operands cc == 0 --> result == 0, no carry (cond == 8) cc == 1 --> result != 0, no carry (cond == 4) cc == 2 --> result == 0, carry (cond == 2) cc == 3 --> result != 0, carry (cond == 1) */ if (cond == 8) { /* cc == 0 */ /* Both inputs are 0 */ return unop(Iop_1Uto32, binop(Iop_CmpEQ64, binop(Iop_Or64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 7) { /* cc == 1,2,3 */ /* Not both inputs are 0 */ return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_Or64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 8 + 2) { /* cc == 0,2 -> result is zero */ return unop(Iop_1Uto32, binop(Iop_CmpEQ64, binop(Iop_Add64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 4 + 1) { /* cc == 1,3 -> result is not zero */ return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_Add64, cc_dep1, cc_dep2), mkU64(0))); } goto missed; } /* S390_CC_OP_UNSIGNED_ADD_32 */ if (cc_op == S390_CC_OP_UNSIGNED_ADD_32) { /* cc_dep1, cc_dep2 are the zero extended left and right operands cc == 0 --> result == 0, no carry (cond == 8) cc == 1 --> result != 0, no carry (cond == 4) cc == 2 --> result == 0, carry (cond == 2) cc == 3 --> result != 0, carry (cond == 1) */ if (cond == 8) { /* cc == 0 */ /* Both inputs are 0 */ return unop(Iop_1Uto32, binop(Iop_CmpEQ64, binop(Iop_Or64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 7) { /* cc == 1,2,3 */ /* Not both inputs are 0 */ return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_Or64, cc_dep1, cc_dep2), mkU64(0))); } if (cond == 8 + 2) { /* cc == 0,2 -> result is zero */ return unop(Iop_1Uto32, binop(Iop_CmpEQ32, binop(Iop_Add32, unop(Iop_64to32, cc_dep1), unop(Iop_64to32, cc_dep2)), mkU32(0))); } if (cond == 4 + 1) { /* cc == 1,3 -> result is not zero */ return unop(Iop_1Uto32, binop(Iop_CmpNE32, binop(Iop_Add32, unop(Iop_64to32, cc_dep1), unop(Iop_64to32, cc_dep2)), mkU32(0))); } goto missed; } /* S390_CC_OP_SET */ if (cc_op == S390_CC_OP_SET) { /* cc_dep1 is the condition code Return 1, if ((cond << cc_dep1) & 0x8) != 0 */ return unop(Iop_1Uto32, binop(Iop_CmpNE64, binop(Iop_And64, binop(Iop_Shl64, cond_expr, unop(Iop_64to8, cc_dep1)), mkU64(8)), mkU64(0))); } /* S390_CC_OP_TEST_AND_SET */ if (cc_op == S390_CC_OP_TEST_AND_SET) { /* cc_dep1 is the zero-extended loaded value cc == 0 --> leftmost bit is zero (cond == 8) cc == 1 --> leftmost bit is one (cond == 4) As cc is either 0 or 1, only the two leftmost bits of the mask are relevant. */ IRExpr *bit = binop(Iop_Shr64, cc_dep1, mkU8(7)); switch (cond & (8 + 4)) { case 0: return mkU32(0); case 4: return unop(Iop_1Uto32, binop(Iop_CmpNE64, bit, mkU64(0))); case 8: return unop(Iop_1Uto32, binop(Iop_CmpEQ64, bit, mkU64(0))); case 8 + 4: return mkU32(1); } /* not reached */ } missed: ; } return NULL; } /*---------------------------------------------------------------*/ /*--- end guest_s390_helpers.c ---*/ /*---------------------------------------------------------------*/