| /* -*- 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; |
| state->host_EvC_COUNTER = 0; |
| state->host_EvC_FAILADDR = 0; |
| |
| /*------------------------------------------------------------*/ |
| /*--- Initialise thunk ---*/ |
| /*------------------------------------------------------------*/ |
| |
| state->guest_CC_OP = 0; |
| state->guest_CC_DEP1 = 0; |
| state->guest_CC_DEP2 = 0; |
| state->guest_CC_NDEP = 0; |
| |
| __builtin_memset(state->padding, 0x0, sizeof(state->padding)); |
| } |
| |
| |
| /* 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_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"); |
| } |
| |
| |
| /* 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))); |
| } |
| |
| missed: |
| ; |
| } |
| |
| return NULL; |
| } |
| |
| /*---------------------------------------------------------------*/ |
| /*--- end guest_s390_helpers.c ---*/ |
| /*---------------------------------------------------------------*/ |