blob: 65cd0e98115fc0bf20b879e3aeebf5552bd55814 [file] [log] [blame]
/* -*- mode: C; c-basic-offset: 3; -*- */
/*---------------------------------------------------------------*/
/*--- begin guest_s390_toIR.c ---*/
/*---------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright IBM Corp. 2010-2013
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 and Christian Borntraeger */
/* Translates s390 code to IR. */
#include "libvex_basictypes.h"
#include "libvex_ir.h"
#include "libvex_emnote.h"
#include "libvex_s390x_common.h"
#include "main_util.h" /* vassert */
#include "main_globals.h" /* vex_traceflags */
#include "guest_generic_bb_to_IR.h" /* DisResult */
#include "guest_s390_defs.h" /* prototypes for this file's functions */
#include "s390_disasm.h"
#include "s390_defs.h" /* S390_BFP_ROUND_xyzzy */
#include "host_s390_defs.h" /* s390_host_has_xyzzy */
/*------------------------------------------------------------*/
/*--- Forward declarations ---*/
/*------------------------------------------------------------*/
static UInt s390_decode_and_irgen(UChar *, UInt, DisResult *);
static void s390_irgen_xonc(IROp, IRTemp, IRTemp, IRTemp);
static void s390_irgen_CLC_EX(IRTemp, IRTemp, IRTemp);
/*------------------------------------------------------------*/
/*--- Globals ---*/
/*------------------------------------------------------------*/
/* The IRSB* into which we're generating code. */
static IRSB *irsb;
/* The guest address for the instruction currently being
translated. */
static Addr64 guest_IA_curr_instr;
/* The guest address for the instruction following the current instruction. */
static Addr64 guest_IA_next_instr;
/* Result of disassembly step. */
static DisResult *dis_res;
/* Resteer function and callback data */
static Bool (*resteer_fn)(void *, Addr64);
static void *resteer_data;
/* Whether to print diagnostics for illegal instructions. */
static Bool sigill_diag;
/* The last seen execute target instruction */
ULong last_execute_target;
/* The possible outcomes of a decoding operation */
typedef enum {
S390_DECODE_OK,
S390_DECODE_UNKNOWN_INSN,
S390_DECODE_UNIMPLEMENTED_INSN,
S390_DECODE_UNKNOWN_SPECIAL_INSN,
S390_DECODE_ERROR
} s390_decode_t;
/*------------------------------------------------------------*/
/*--- Helpers for constructing IR. ---*/
/*------------------------------------------------------------*/
/* Sign extend a value with the given number of bits. This is a
macro because it allows us to overload the type of the value.
Note that VALUE must have a signed type! */
#undef sign_extend
#define sign_extend(value,num_bits) \
(((value) << (sizeof(__typeof__(value)) * 8 - (num_bits))) >> \
(sizeof(__typeof__(value)) * 8 - (num_bits)))
/* Add a statement to the current irsb. */
static __inline__ void
stmt(IRStmt *st)
{
addStmtToIRSB(irsb, st);
}
/* Allocate a new temporary of the given type. */
static __inline__ IRTemp
newTemp(IRType type)
{
vassert(isPlausibleIRType(type));
return newIRTemp(irsb->tyenv, type);
}
/* Create an expression node for a temporary */
static __inline__ IRExpr *
mkexpr(IRTemp tmp)
{
return IRExpr_RdTmp(tmp);
}
/* Generate an expression node for an address. */
static __inline__ IRExpr *
mkaddr_expr(Addr64 addr)
{
return IRExpr_Const(IRConst_U64(addr));
}
/* Add a statement that assigns to a temporary */
static __inline__ void
assign(IRTemp dst, IRExpr *expr)
{
stmt(IRStmt_WrTmp(dst, expr));
}
/* Write an address into the guest_IA */
static __inline__ void
put_IA(IRExpr *address)
{
stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_IA), address));
}
/* Create a temporary of the given type and assign the expression to it */
static __inline__ IRTemp
mktemp(IRType type, IRExpr *expr)
{
IRTemp temp = newTemp(type);
assign(temp, expr);
return temp;
}
/* Create a unary expression */
static __inline__ IRExpr *
unop(IROp kind, IRExpr *op)
{
return IRExpr_Unop(kind, op);
}
/* Create a binary expression */
static __inline__ IRExpr *
binop(IROp kind, IRExpr *op1, IRExpr *op2)
{
return IRExpr_Binop(kind, op1, op2);
}
/* Create a ternary expression */
static __inline__ IRExpr *
triop(IROp kind, IRExpr *op1, IRExpr *op2, IRExpr *op3)
{
return IRExpr_Triop(kind, op1, op2, op3);
}
/* Create a quaternary expression */
static __inline__ IRExpr *
qop(IROp kind, IRExpr *op1, IRExpr *op2, IRExpr *op3, IRExpr *op4)
{
return IRExpr_Qop(kind, op1, op2, op3, op4);
}
/* Create an expression node for an 8-bit integer constant */
static __inline__ IRExpr *
mkU8(UInt value)
{
vassert(value < 256);
return IRExpr_Const(IRConst_U8((UChar)value));
}
/* Create an expression node for a 16-bit integer constant */
static __inline__ IRExpr *
mkU16(UInt value)
{
vassert(value < 65536);
return IRExpr_Const(IRConst_U16((UShort)value));
}
/* Create an expression node for a 32-bit integer constant */
static __inline__ IRExpr *
mkU32(UInt value)
{
return IRExpr_Const(IRConst_U32(value));
}
/* Create an expression node for a 64-bit integer constant */
static __inline__ IRExpr *
mkU64(ULong value)
{
return IRExpr_Const(IRConst_U64(value));
}
/* Create an expression node for a 32-bit floating point constant
whose value is given by a bit pattern. */
static __inline__ IRExpr *
mkF32i(UInt value)
{
return IRExpr_Const(IRConst_F32i(value));
}
/* Create an expression node for a 32-bit floating point constant
whose value is given by a bit pattern. */
static __inline__ IRExpr *
mkF64i(ULong value)
{
return IRExpr_Const(IRConst_F64i(value));
}
/* Little helper function for my sanity. ITE = if-then-else */
static IRExpr *
mkite(IRExpr *condition, IRExpr *iftrue, IRExpr *iffalse)
{
vassert(typeOfIRExpr(irsb->tyenv, condition) == Ity_I1);
return IRExpr_ITE(condition, iftrue, iffalse);
}
/* Add a statement that stores DATA at ADDR. This is a big-endian machine. */
static void __inline__
store(IRExpr *addr, IRExpr *data)
{
stmt(IRStmt_Store(Iend_BE, addr, data));
}
/* Create an expression that loads a TYPE sized value from ADDR.
This is a big-endian machine. */
static __inline__ IRExpr *
load(IRType type, IRExpr *addr)
{
return IRExpr_Load(Iend_BE, type, addr);
}
/* Function call */
static void
call_function(IRExpr *callee_address)
{
put_IA(callee_address);
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_Call;
}
/* Function call with known target. */
static void
call_function_and_chase(Addr64 callee_address)
{
if (resteer_fn(resteer_data, callee_address)) {
dis_res->whatNext = Dis_ResteerU;
dis_res->continueAt = callee_address;
} else {
put_IA(mkaddr_expr(callee_address));
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_Call;
}
}
/* Function return sequence */
static void
return_from_function(IRExpr *return_address)
{
put_IA(return_address);
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_Ret;
}
/* A conditional branch whose target is not known at instrumentation time.
if (condition) goto computed_target;
Needs to be represented as:
if (! condition) goto next_instruction;
goto computed_target;
*/
static void
if_condition_goto_computed(IRExpr *condition, IRExpr *target)
{
vassert(typeOfIRExpr(irsb->tyenv, condition) == Ity_I1);
condition = unop(Iop_Not1, condition);
stmt(IRStmt_Exit(condition, Ijk_Boring, IRConst_U64(guest_IA_next_instr),
S390X_GUEST_OFFSET(guest_IA)));
put_IA(target);
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_Boring;
}
/* A conditional branch whose target is known at instrumentation time. */
static void
if_condition_goto(IRExpr *condition, Addr64 target)
{
vassert(typeOfIRExpr(irsb->tyenv, condition) == Ity_I1);
stmt(IRStmt_Exit(condition, Ijk_Boring, IRConst_U64(target),
S390X_GUEST_OFFSET(guest_IA)));
put_IA(mkaddr_expr(guest_IA_next_instr));
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_Boring;
}
/* An unconditional branch. Target may or may not be known at instrumentation
time. */
static void
always_goto(IRExpr *target)
{
put_IA(target);
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_Boring;
}
/* An unconditional branch to a known target. */
static void
always_goto_and_chase(Addr64 target)
{
if (resteer_fn(resteer_data, target)) {
/* Follow into the target */
dis_res->whatNext = Dis_ResteerU;
dis_res->continueAt = target;
} else {
put_IA(mkaddr_expr(target));
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_Boring;
}
}
/* A system call */
static void
system_call(IRExpr *sysno)
{
/* Store the system call number in the pseudo register. */
stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_SYSNO), sysno));
/* Store the current IA into guest_IP_AT_SYSCALL. libvex_ir.h says so. */
stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_IP_AT_SYSCALL),
mkU64(guest_IA_curr_instr)));
put_IA(mkaddr_expr(guest_IA_next_instr));
/* It's important that all ArchRegs carry their up-to-date value
at this point. So we declare an end-of-block here, which
forces any TempRegs caching ArchRegs to be flushed. */
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_Sys_syscall;
}
/* A side exit that branches back to the current insn if CONDITION is
true. Does not set DisResult. */
static void
iterate_if(IRExpr *condition)
{
vassert(typeOfIRExpr(irsb->tyenv, condition) == Ity_I1);
stmt(IRStmt_Exit(condition, Ijk_Boring, IRConst_U64(guest_IA_curr_instr),
S390X_GUEST_OFFSET(guest_IA)));
}
/* A side exit that branches back to the current insn.
Does not set DisResult. */
static __inline__ void
iterate(void)
{
iterate_if(IRExpr_Const(IRConst_U1(True)));
}
/* A side exit that branches back to the insn immediately following the
current insn if CONDITION is true. Does not set DisResult. */
static void
next_insn_if(IRExpr *condition)
{
vassert(typeOfIRExpr(irsb->tyenv, condition) == Ity_I1);
stmt(IRStmt_Exit(condition, Ijk_Boring, IRConst_U64(guest_IA_next_instr),
S390X_GUEST_OFFSET(guest_IA)));
}
/* Convenience function to restart the current insn */
static void
restart_if(IRExpr *condition)
{
vassert(typeOfIRExpr(irsb->tyenv, condition) == Ity_I1);
stmt(IRStmt_Exit(condition, Ijk_InvalICache,
IRConst_U64(guest_IA_curr_instr),
S390X_GUEST_OFFSET(guest_IA)));
}
/* Convenience function to yield to thread scheduler */
static void
yield_if(IRExpr *condition)
{
stmt(IRStmt_Exit(condition, Ijk_Yield, IRConst_U64(guest_IA_next_instr),
S390X_GUEST_OFFSET(guest_IA)));
}
static __inline__ IRExpr *get_fpr_dw0(UInt);
static __inline__ void put_fpr_dw0(UInt, IRExpr *);
static __inline__ IRExpr *get_dpr_dw0(UInt);
static __inline__ void put_dpr_dw0(UInt, IRExpr *);
/* Read a floating point register pair and combine their contents into a
128-bit value */
static IRExpr *
get_fpr_pair(UInt archreg)
{
IRExpr *high = get_fpr_dw0(archreg);
IRExpr *low = get_fpr_dw0(archreg + 2);
return binop(Iop_F64HLtoF128, high, low);
}
/* Write a 128-bit floating point value into a register pair. */
static void
put_fpr_pair(UInt archreg, IRExpr *expr)
{
IRExpr *high = unop(Iop_F128HItoF64, expr);
IRExpr *low = unop(Iop_F128LOtoF64, expr);
put_fpr_dw0(archreg, high);
put_fpr_dw0(archreg + 2, low);
}
/* Read a floating point register pair cointaining DFP value
and combine their contents into a 128-bit value */
static IRExpr *
get_dpr_pair(UInt archreg)
{
IRExpr *high = get_dpr_dw0(archreg);
IRExpr *low = get_dpr_dw0(archreg + 2);
return binop(Iop_D64HLtoD128, high, low);
}
/* Write a 128-bit decimal floating point value into a register pair. */
static void
put_dpr_pair(UInt archreg, IRExpr *expr)
{
IRExpr *high = unop(Iop_D128HItoD64, expr);
IRExpr *low = unop(Iop_D128LOtoD64, expr);
put_dpr_dw0(archreg, high);
put_dpr_dw0(archreg + 2, low);
}
/* Terminate the current IRSB with an emulation failure. */
static void
emulation_failure_with_expr(IRExpr *emfailure)
{
vassert(typeOfIRExpr(irsb->tyenv, emfailure) == Ity_I32);
stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_EMNOTE), emfailure));
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_EmFail;
}
static void
emulation_failure(VexEmNote fail_kind)
{
emulation_failure_with_expr(mkU32(fail_kind));
}
/* Terminate the current IRSB with an emulation warning. */
static void
emulation_warning_with_expr(IRExpr *emwarning)
{
vassert(typeOfIRExpr(irsb->tyenv, emwarning) == Ity_I32);
stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_EMNOTE), emwarning));
dis_res->whatNext = Dis_StopHere;
dis_res->jk_StopHere = Ijk_EmWarn;
}
static void
emulation_warning(VexEmNote warn_kind)
{
emulation_warning_with_expr(mkU32(warn_kind));
}
/*------------------------------------------------------------*/
/*--- IR Debugging aids. ---*/
/*------------------------------------------------------------*/
#if 0
static ULong
s390_do_print(HChar *text, ULong value)
{
vex_printf("%s %llu\n", text, value);
return 0;
}
static void
s390_print(HChar *text, IRExpr *value)
{
IRDirty *d;
d = unsafeIRDirty_0_N(0 /* regparms */, "s390_do_print", &s390_do_print,
mkIRExprVec_2(mkU64((ULong)text), value));
stmt(IRStmt_Dirty(d));
}
#endif
/*------------------------------------------------------------*/
/*--- Build the flags thunk. ---*/
/*------------------------------------------------------------*/
/* Completely fill the flags thunk. We're always filling all fields.
Apparently, that is better for redundant PUT elimination. */
static void
s390_cc_thunk_fill(IRExpr *op, IRExpr *dep1, IRExpr *dep2, IRExpr *ndep)
{
UInt op_off, dep1_off, dep2_off, ndep_off;
op_off = S390X_GUEST_OFFSET(guest_CC_OP);
dep1_off = S390X_GUEST_OFFSET(guest_CC_DEP1);
dep2_off = S390X_GUEST_OFFSET(guest_CC_DEP2);
ndep_off = S390X_GUEST_OFFSET(guest_CC_NDEP);
stmt(IRStmt_Put(op_off, op));
stmt(IRStmt_Put(dep1_off, dep1));
stmt(IRStmt_Put(dep2_off, dep2));
stmt(IRStmt_Put(ndep_off, ndep));
}
/* Create an expression for V and widen the result to 64 bit. */
static IRExpr *
s390_cc_widen(IRTemp v, Bool sign_extend)
{
IRExpr *expr;
expr = mkexpr(v);
switch (typeOfIRTemp(irsb->tyenv, v)) {
case Ity_I64:
break;
case Ity_I32:
expr = unop(sign_extend ? Iop_32Sto64 : Iop_32Uto64, expr);
break;
case Ity_I16:
expr = unop(sign_extend ? Iop_16Sto64 : Iop_16Uto64, expr);
break;
case Ity_I8:
expr = unop(sign_extend ? Iop_8Sto64 : Iop_8Uto64, expr);
break;
default:
vpanic("s390_cc_widen");
}
return expr;
}
static void
s390_cc_thunk_put1(UInt opc, IRTemp d1, Bool sign_extend)
{
IRExpr *op, *dep1, *dep2, *ndep;
op = mkU64(opc);
dep1 = s390_cc_widen(d1, sign_extend);
dep2 = mkU64(0);
ndep = mkU64(0);
s390_cc_thunk_fill(op, dep1, dep2, ndep);
}
static void
s390_cc_thunk_put2(UInt opc, IRTemp d1, IRTemp d2, Bool sign_extend)
{
IRExpr *op, *dep1, *dep2, *ndep;
op = mkU64(opc);
dep1 = s390_cc_widen(d1, sign_extend);
dep2 = s390_cc_widen(d2, sign_extend);
ndep = mkU64(0);
s390_cc_thunk_fill(op, dep1, dep2, ndep);
}
/* memcheck believes that the NDEP field in the flags thunk is always
defined. But for some flag computations (e.g. add with carry) that is
just not true. We therefore need to convey to memcheck that the value
of the ndep field does matter and therefore we make the DEP2 field
depend on it:
DEP2 = original_DEP2 ^ NDEP
In s390_calculate_cc we exploit that (a^b)^b == a
I.e. we xor the DEP2 value with the NDEP value to recover the
original_DEP2 value. */
static void
s390_cc_thunk_put3(UInt opc, IRTemp d1, IRTemp d2, IRTemp nd, Bool sign_extend)
{
IRExpr *op, *dep1, *dep2, *ndep, *dep2x;
op = mkU64(opc);
dep1 = s390_cc_widen(d1, sign_extend);
dep2 = s390_cc_widen(d2, sign_extend);
ndep = s390_cc_widen(nd, sign_extend);
dep2x = binop(Iop_Xor64, dep2, ndep);
s390_cc_thunk_fill(op, dep1, dep2x, ndep);
}
/* Write one floating point value into the flags thunk */
static void
s390_cc_thunk_put1f(UInt opc, IRTemp d1)
{
IRExpr *op, *dep1, *dep2, *ndep;
/* Make the CC_DEP1 slot appear completely defined.
Otherwise, assigning a 32-bit value will cause memcheck
to trigger an undefinedness error.
*/
if (sizeofIRType(typeOfIRTemp(irsb->tyenv, d1)) == 4) {
UInt dep1_off = S390X_GUEST_OFFSET(guest_CC_DEP1);
stmt(IRStmt_Put(dep1_off, mkU64(0)));
}
op = mkU64(opc);
dep1 = mkexpr(d1);
dep2 = mkU64(0);
ndep = mkU64(0);
s390_cc_thunk_fill(op, dep1, dep2, ndep);
}
/* Write a floating point value and an integer into the flags thunk. The
integer value is zero-extended first. */
static void
s390_cc_thunk_putFZ(UInt opc, IRTemp d1, IRTemp d2)
{
IRExpr *op, *dep1, *dep2, *ndep;
/* Make the CC_DEP1 slot appear completely defined.
Otherwise, assigning a 32-bit value will cause memcheck
to trigger an undefinedness error.
*/
if (sizeofIRType(typeOfIRTemp(irsb->tyenv, d1)) == 4) {
UInt dep1_off = S390X_GUEST_OFFSET(guest_CC_DEP1);
stmt(IRStmt_Put(dep1_off, mkU64(0)));
}
op = mkU64(opc);
dep1 = mkexpr(d1);
dep2 = s390_cc_widen(d2, False);
ndep = mkU64(0);
s390_cc_thunk_fill(op, dep1, dep2, ndep);
}
/* Write a 128-bit floating point value into the flags thunk. This is
done by splitting the value into two 64-bits values. */
static void
s390_cc_thunk_put1f128(UInt opc, IRTemp d1)
{
IRExpr *op, *hi, *lo, *ndep;
op = mkU64(opc);
hi = unop(Iop_F128HItoF64, mkexpr(d1));
lo = unop(Iop_F128LOtoF64, mkexpr(d1));
ndep = mkU64(0);
s390_cc_thunk_fill(op, hi, lo, ndep);
}
/* Write a 128-bit floating point value and an integer into the flags thunk.
The integer value is zero-extended first. */
static void
s390_cc_thunk_put1f128Z(UInt opc, IRTemp d1, IRTemp nd)
{
IRExpr *op, *hi, *lo, *lox, *ndep;
op = mkU64(opc);
hi = unop(Iop_F128HItoF64, mkexpr(d1));
lo = unop(Iop_ReinterpF64asI64, unop(Iop_F128LOtoF64, mkexpr(d1)));
ndep = s390_cc_widen(nd, False);
lox = binop(Iop_Xor64, lo, ndep); /* convey dependency */
s390_cc_thunk_fill(op, hi, lox, ndep);
}
/* Write a 128-bit decimal floating point value into the flags thunk.
This is done by splitting the value into two 64-bits values. */
static void
s390_cc_thunk_put1d128(UInt opc, IRTemp d1)
{
IRExpr *op, *hi, *lo, *ndep;
op = mkU64(opc);
hi = unop(Iop_D128HItoD64, mkexpr(d1));
lo = unop(Iop_D128LOtoD64, mkexpr(d1));
ndep = mkU64(0);
s390_cc_thunk_fill(op, hi, lo, ndep);
}
/* Write a 128-bit decimal floating point value and an integer into the flags
thunk. The integer value is zero-extended first. */
static void
s390_cc_thunk_put1d128Z(UInt opc, IRTemp d1, IRTemp nd)
{
IRExpr *op, *hi, *lo, *lox, *ndep;
op = mkU64(opc);
hi = unop(Iop_D128HItoD64, mkexpr(d1));
lo = unop(Iop_ReinterpD64asI64, unop(Iop_D128LOtoD64, mkexpr(d1)));
ndep = s390_cc_widen(nd, False);
lox = binop(Iop_Xor64, lo, ndep); /* convey dependency */
s390_cc_thunk_fill(op, hi, lox, ndep);
}
static void
s390_cc_set(UInt val)
{
s390_cc_thunk_fill(mkU64(S390_CC_OP_SET),
mkU64(val), mkU64(0), mkU64(0));
}
/* Build IR to calculate the condition code from flags thunk.
Returns an expression of type Ity_I32 */
static IRExpr *
s390_call_calculate_cc(void)
{
IRExpr **args, *call, *op, *dep1, *dep2, *ndep;
op = IRExpr_Get(S390X_GUEST_OFFSET(guest_CC_OP), Ity_I64);
dep1 = IRExpr_Get(S390X_GUEST_OFFSET(guest_CC_DEP1), Ity_I64);
dep2 = IRExpr_Get(S390X_GUEST_OFFSET(guest_CC_DEP2), Ity_I64);
ndep = IRExpr_Get(S390X_GUEST_OFFSET(guest_CC_NDEP), Ity_I64);
args = mkIRExprVec_4(op, dep1, dep2, ndep);
call = mkIRExprCCall(Ity_I32, 0 /*regparm*/,
"s390_calculate_cc", &s390_calculate_cc, args);
/* Exclude OP and NDEP from definedness checking. We're only
interested in DEP1 and DEP2. */
call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<3);
return call;
}
/* Build IR to calculate the internal condition code for a "compare and branch"
insn. Returns an expression of type Ity_I32 */
static IRExpr *
s390_call_calculate_icc(UInt m, UInt opc, IRTemp op1, IRTemp op2)
{
IRExpr **args, *call, *op, *dep1, *dep2, *mask;
switch (opc) {
case S390_CC_OP_SIGNED_COMPARE:
dep1 = s390_cc_widen(op1, True);
dep2 = s390_cc_widen(op2, True);
break;
case S390_CC_OP_UNSIGNED_COMPARE:
dep1 = s390_cc_widen(op1, False);
dep2 = s390_cc_widen(op2, False);
break;
default:
vpanic("s390_call_calculate_icc");
}
mask = mkU64(m);
op = mkU64(opc);
args = mkIRExprVec_5(mask, op, dep1, dep2, mkU64(0) /* unused */);
call = mkIRExprCCall(Ity_I32, 0 /*regparm*/,
"s390_calculate_cond", &s390_calculate_cond, args);
/* Exclude the requested condition, OP and NDEP from definedness
checking. We're only interested in DEP1 and DEP2. */
call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<1) | (1<<4);
return call;
}
/* Build IR to calculate the condition code from flags thunk.
Returns an expression of type Ity_I32 */
static IRExpr *
s390_call_calculate_cond(UInt m)
{
IRExpr **args, *call, *op, *dep1, *dep2, *ndep, *mask;
mask = mkU64(m);
op = IRExpr_Get(S390X_GUEST_OFFSET(guest_CC_OP), Ity_I64);
dep1 = IRExpr_Get(S390X_GUEST_OFFSET(guest_CC_DEP1), Ity_I64);
dep2 = IRExpr_Get(S390X_GUEST_OFFSET(guest_CC_DEP2), Ity_I64);
ndep = IRExpr_Get(S390X_GUEST_OFFSET(guest_CC_NDEP), Ity_I64);
args = mkIRExprVec_5(mask, op, dep1, dep2, ndep);
call = mkIRExprCCall(Ity_I32, 0 /*regparm*/,
"s390_calculate_cond", &s390_calculate_cond, args);
/* Exclude the requested condition, OP and NDEP from definedness
checking. We're only interested in DEP1 and DEP2. */
call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<1) | (1<<4);
return call;
}
#define s390_cc_thunk_putZ(op,dep1) s390_cc_thunk_put1(op,dep1,False)
#define s390_cc_thunk_putS(op,dep1) s390_cc_thunk_put1(op,dep1,True)
#define s390_cc_thunk_putF(op,dep1) s390_cc_thunk_put1f(op,dep1)
#define s390_cc_thunk_putZZ(op,dep1,dep2) s390_cc_thunk_put2(op,dep1,dep2,False)
#define s390_cc_thunk_putSS(op,dep1,dep2) s390_cc_thunk_put2(op,dep1,dep2,True)
#define s390_cc_thunk_putFF(op,dep1,dep2) s390_cc_thunk_put2f(op,dep1,dep2)
#define s390_cc_thunk_putZZZ(op,dep1,dep2,ndep) \
s390_cc_thunk_put3(op,dep1,dep2,ndep,False)
#define s390_cc_thunk_putSSS(op,dep1,dep2,ndep) \
s390_cc_thunk_put3(op,dep1,dep2,ndep,True)
/*------------------------------------------------------------*/
/*--- Guest register access ---*/
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/*--- ar registers ---*/
/*------------------------------------------------------------*/
/* Return the guest state offset of a ar register. */
static UInt
ar_offset(UInt archreg)
{
static const UInt offset[16] = {
S390X_GUEST_OFFSET(guest_a0),
S390X_GUEST_OFFSET(guest_a1),
S390X_GUEST_OFFSET(guest_a2),
S390X_GUEST_OFFSET(guest_a3),
S390X_GUEST_OFFSET(guest_a4),
S390X_GUEST_OFFSET(guest_a5),
S390X_GUEST_OFFSET(guest_a6),
S390X_GUEST_OFFSET(guest_a7),
S390X_GUEST_OFFSET(guest_a8),
S390X_GUEST_OFFSET(guest_a9),
S390X_GUEST_OFFSET(guest_a10),
S390X_GUEST_OFFSET(guest_a11),
S390X_GUEST_OFFSET(guest_a12),
S390X_GUEST_OFFSET(guest_a13),
S390X_GUEST_OFFSET(guest_a14),
S390X_GUEST_OFFSET(guest_a15),
};
vassert(archreg < 16);
return offset[archreg];
}
/* Return the guest state offset of word #0 of a ar register. */
static __inline__ UInt
ar_w0_offset(UInt archreg)
{
return ar_offset(archreg) + 0;
}
/* Write word #0 of a ar to the guest state. */
static __inline__ void
put_ar_w0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I32);
stmt(IRStmt_Put(ar_w0_offset(archreg), expr));
}
/* Read word #0 of a ar register. */
static __inline__ IRExpr *
get_ar_w0(UInt archreg)
{
return IRExpr_Get(ar_w0_offset(archreg), Ity_I32);
}
/*------------------------------------------------------------*/
/*--- fpr registers ---*/
/*------------------------------------------------------------*/
/* Return the guest state offset of a fpr register. */
static UInt
fpr_offset(UInt archreg)
{
static const UInt offset[16] = {
S390X_GUEST_OFFSET(guest_f0),
S390X_GUEST_OFFSET(guest_f1),
S390X_GUEST_OFFSET(guest_f2),
S390X_GUEST_OFFSET(guest_f3),
S390X_GUEST_OFFSET(guest_f4),
S390X_GUEST_OFFSET(guest_f5),
S390X_GUEST_OFFSET(guest_f6),
S390X_GUEST_OFFSET(guest_f7),
S390X_GUEST_OFFSET(guest_f8),
S390X_GUEST_OFFSET(guest_f9),
S390X_GUEST_OFFSET(guest_f10),
S390X_GUEST_OFFSET(guest_f11),
S390X_GUEST_OFFSET(guest_f12),
S390X_GUEST_OFFSET(guest_f13),
S390X_GUEST_OFFSET(guest_f14),
S390X_GUEST_OFFSET(guest_f15),
};
vassert(archreg < 16);
return offset[archreg];
}
/* Return the guest state offset of word #0 of a fpr register. */
static __inline__ UInt
fpr_w0_offset(UInt archreg)
{
return fpr_offset(archreg) + 0;
}
/* Write word #0 of a fpr to the guest state. */
static __inline__ void
put_fpr_w0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_F32);
stmt(IRStmt_Put(fpr_w0_offset(archreg), expr));
}
/* Read word #0 of a fpr register. */
static __inline__ IRExpr *
get_fpr_w0(UInt archreg)
{
return IRExpr_Get(fpr_w0_offset(archreg), Ity_F32);
}
/* Return the guest state offset of double word #0 of a fpr register. */
static __inline__ UInt
fpr_dw0_offset(UInt archreg)
{
return fpr_offset(archreg) + 0;
}
/* Write double word #0 of a fpr to the guest state. */
static __inline__ void
put_fpr_dw0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_F64);
stmt(IRStmt_Put(fpr_dw0_offset(archreg), expr));
}
/* Read double word #0 of a fpr register. */
static __inline__ IRExpr *
get_fpr_dw0(UInt archreg)
{
return IRExpr_Get(fpr_dw0_offset(archreg), Ity_F64);
}
/* Write word #0 of a dpr to the guest state. */
static __inline__ void
put_dpr_w0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_D32);
stmt(IRStmt_Put(fpr_w0_offset(archreg), expr));
}
/* Read word #0 of a dpr register. */
static __inline__ IRExpr *
get_dpr_w0(UInt archreg)
{
return IRExpr_Get(fpr_w0_offset(archreg), Ity_D32);
}
/* Write double word #0 of a fpr containg DFP value to the guest state. */
static __inline__ void
put_dpr_dw0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_D64);
stmt(IRStmt_Put(fpr_dw0_offset(archreg), expr));
}
/* Read double word #0 of a fpr register containing DFP value. */
static __inline__ IRExpr *
get_dpr_dw0(UInt archreg)
{
return IRExpr_Get(fpr_dw0_offset(archreg), Ity_D64);
}
/*------------------------------------------------------------*/
/*--- gpr registers ---*/
/*------------------------------------------------------------*/
/* Return the guest state offset of a gpr register. */
static UInt
gpr_offset(UInt archreg)
{
static const UInt offset[16] = {
S390X_GUEST_OFFSET(guest_r0),
S390X_GUEST_OFFSET(guest_r1),
S390X_GUEST_OFFSET(guest_r2),
S390X_GUEST_OFFSET(guest_r3),
S390X_GUEST_OFFSET(guest_r4),
S390X_GUEST_OFFSET(guest_r5),
S390X_GUEST_OFFSET(guest_r6),
S390X_GUEST_OFFSET(guest_r7),
S390X_GUEST_OFFSET(guest_r8),
S390X_GUEST_OFFSET(guest_r9),
S390X_GUEST_OFFSET(guest_r10),
S390X_GUEST_OFFSET(guest_r11),
S390X_GUEST_OFFSET(guest_r12),
S390X_GUEST_OFFSET(guest_r13),
S390X_GUEST_OFFSET(guest_r14),
S390X_GUEST_OFFSET(guest_r15),
};
vassert(archreg < 16);
return offset[archreg];
}
/* Return the guest state offset of word #0 of a gpr register. */
static __inline__ UInt
gpr_w0_offset(UInt archreg)
{
return gpr_offset(archreg) + 0;
}
/* Write word #0 of a gpr to the guest state. */
static __inline__ void
put_gpr_w0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I32);
stmt(IRStmt_Put(gpr_w0_offset(archreg), expr));
}
/* Read word #0 of a gpr register. */
static __inline__ IRExpr *
get_gpr_w0(UInt archreg)
{
return IRExpr_Get(gpr_w0_offset(archreg), Ity_I32);
}
/* Return the guest state offset of double word #0 of a gpr register. */
static __inline__ UInt
gpr_dw0_offset(UInt archreg)
{
return gpr_offset(archreg) + 0;
}
/* Write double word #0 of a gpr to the guest state. */
static __inline__ void
put_gpr_dw0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I64);
stmt(IRStmt_Put(gpr_dw0_offset(archreg), expr));
}
/* Read double word #0 of a gpr register. */
static __inline__ IRExpr *
get_gpr_dw0(UInt archreg)
{
return IRExpr_Get(gpr_dw0_offset(archreg), Ity_I64);
}
/* Return the guest state offset of half word #1 of a gpr register. */
static __inline__ UInt
gpr_hw1_offset(UInt archreg)
{
return gpr_offset(archreg) + 2;
}
/* Write half word #1 of a gpr to the guest state. */
static __inline__ void
put_gpr_hw1(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I16);
stmt(IRStmt_Put(gpr_hw1_offset(archreg), expr));
}
/* Read half word #1 of a gpr register. */
static __inline__ IRExpr *
get_gpr_hw1(UInt archreg)
{
return IRExpr_Get(gpr_hw1_offset(archreg), Ity_I16);
}
/* Return the guest state offset of byte #6 of a gpr register. */
static __inline__ UInt
gpr_b6_offset(UInt archreg)
{
return gpr_offset(archreg) + 6;
}
/* Write byte #6 of a gpr to the guest state. */
static __inline__ void
put_gpr_b6(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I8);
stmt(IRStmt_Put(gpr_b6_offset(archreg), expr));
}
/* Read byte #6 of a gpr register. */
static __inline__ IRExpr *
get_gpr_b6(UInt archreg)
{
return IRExpr_Get(gpr_b6_offset(archreg), Ity_I8);
}
/* Return the guest state offset of byte #3 of a gpr register. */
static __inline__ UInt
gpr_b3_offset(UInt archreg)
{
return gpr_offset(archreg) + 3;
}
/* Write byte #3 of a gpr to the guest state. */
static __inline__ void
put_gpr_b3(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I8);
stmt(IRStmt_Put(gpr_b3_offset(archreg), expr));
}
/* Read byte #3 of a gpr register. */
static __inline__ IRExpr *
get_gpr_b3(UInt archreg)
{
return IRExpr_Get(gpr_b3_offset(archreg), Ity_I8);
}
/* Return the guest state offset of byte #0 of a gpr register. */
static __inline__ UInt
gpr_b0_offset(UInt archreg)
{
return gpr_offset(archreg) + 0;
}
/* Write byte #0 of a gpr to the guest state. */
static __inline__ void
put_gpr_b0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I8);
stmt(IRStmt_Put(gpr_b0_offset(archreg), expr));
}
/* Read byte #0 of a gpr register. */
static __inline__ IRExpr *
get_gpr_b0(UInt archreg)
{
return IRExpr_Get(gpr_b0_offset(archreg), Ity_I8);
}
/* Return the guest state offset of word #1 of a gpr register. */
static __inline__ UInt
gpr_w1_offset(UInt archreg)
{
return gpr_offset(archreg) + 4;
}
/* Write word #1 of a gpr to the guest state. */
static __inline__ void
put_gpr_w1(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I32);
stmt(IRStmt_Put(gpr_w1_offset(archreg), expr));
}
/* Read word #1 of a gpr register. */
static __inline__ IRExpr *
get_gpr_w1(UInt archreg)
{
return IRExpr_Get(gpr_w1_offset(archreg), Ity_I32);
}
/* Return the guest state offset of half word #3 of a gpr register. */
static __inline__ UInt
gpr_hw3_offset(UInt archreg)
{
return gpr_offset(archreg) + 6;
}
/* Write half word #3 of a gpr to the guest state. */
static __inline__ void
put_gpr_hw3(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I16);
stmt(IRStmt_Put(gpr_hw3_offset(archreg), expr));
}
/* Read half word #3 of a gpr register. */
static __inline__ IRExpr *
get_gpr_hw3(UInt archreg)
{
return IRExpr_Get(gpr_hw3_offset(archreg), Ity_I16);
}
/* Return the guest state offset of byte #7 of a gpr register. */
static __inline__ UInt
gpr_b7_offset(UInt archreg)
{
return gpr_offset(archreg) + 7;
}
/* Write byte #7 of a gpr to the guest state. */
static __inline__ void
put_gpr_b7(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I8);
stmt(IRStmt_Put(gpr_b7_offset(archreg), expr));
}
/* Read byte #7 of a gpr register. */
static __inline__ IRExpr *
get_gpr_b7(UInt archreg)
{
return IRExpr_Get(gpr_b7_offset(archreg), Ity_I8);
}
/* Return the guest state offset of half word #0 of a gpr register. */
static __inline__ UInt
gpr_hw0_offset(UInt archreg)
{
return gpr_offset(archreg) + 0;
}
/* Write half word #0 of a gpr to the guest state. */
static __inline__ void
put_gpr_hw0(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I16);
stmt(IRStmt_Put(gpr_hw0_offset(archreg), expr));
}
/* Read half word #0 of a gpr register. */
static __inline__ IRExpr *
get_gpr_hw0(UInt archreg)
{
return IRExpr_Get(gpr_hw0_offset(archreg), Ity_I16);
}
/* Return the guest state offset of byte #4 of a gpr register. */
static __inline__ UInt
gpr_b4_offset(UInt archreg)
{
return gpr_offset(archreg) + 4;
}
/* Write byte #4 of a gpr to the guest state. */
static __inline__ void
put_gpr_b4(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I8);
stmt(IRStmt_Put(gpr_b4_offset(archreg), expr));
}
/* Read byte #4 of a gpr register. */
static __inline__ IRExpr *
get_gpr_b4(UInt archreg)
{
return IRExpr_Get(gpr_b4_offset(archreg), Ity_I8);
}
/* Return the guest state offset of byte #1 of a gpr register. */
static __inline__ UInt
gpr_b1_offset(UInt archreg)
{
return gpr_offset(archreg) + 1;
}
/* Write byte #1 of a gpr to the guest state. */
static __inline__ void
put_gpr_b1(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I8);
stmt(IRStmt_Put(gpr_b1_offset(archreg), expr));
}
/* Read byte #1 of a gpr register. */
static __inline__ IRExpr *
get_gpr_b1(UInt archreg)
{
return IRExpr_Get(gpr_b1_offset(archreg), Ity_I8);
}
/* Return the guest state offset of half word #2 of a gpr register. */
static __inline__ UInt
gpr_hw2_offset(UInt archreg)
{
return gpr_offset(archreg) + 4;
}
/* Write half word #2 of a gpr to the guest state. */
static __inline__ void
put_gpr_hw2(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I16);
stmt(IRStmt_Put(gpr_hw2_offset(archreg), expr));
}
/* Read half word #2 of a gpr register. */
static __inline__ IRExpr *
get_gpr_hw2(UInt archreg)
{
return IRExpr_Get(gpr_hw2_offset(archreg), Ity_I16);
}
/* Return the guest state offset of byte #5 of a gpr register. */
static __inline__ UInt
gpr_b5_offset(UInt archreg)
{
return gpr_offset(archreg) + 5;
}
/* Write byte #5 of a gpr to the guest state. */
static __inline__ void
put_gpr_b5(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I8);
stmt(IRStmt_Put(gpr_b5_offset(archreg), expr));
}
/* Read byte #5 of a gpr register. */
static __inline__ IRExpr *
get_gpr_b5(UInt archreg)
{
return IRExpr_Get(gpr_b5_offset(archreg), Ity_I8);
}
/* Return the guest state offset of byte #2 of a gpr register. */
static __inline__ UInt
gpr_b2_offset(UInt archreg)
{
return gpr_offset(archreg) + 2;
}
/* Write byte #2 of a gpr to the guest state. */
static __inline__ void
put_gpr_b2(UInt archreg, IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I8);
stmt(IRStmt_Put(gpr_b2_offset(archreg), expr));
}
/* Read byte #2 of a gpr register. */
static __inline__ IRExpr *
get_gpr_b2(UInt archreg)
{
return IRExpr_Get(gpr_b2_offset(archreg), Ity_I8);
}
/* Return the guest state offset of the counter register. */
static UInt
counter_offset(void)
{
return S390X_GUEST_OFFSET(guest_counter);
}
/* Return the guest state offset of double word #0 of the counter register. */
static __inline__ UInt
counter_dw0_offset(void)
{
return counter_offset() + 0;
}
/* Write double word #0 of the counter to the guest state. */
static __inline__ void
put_counter_dw0(IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I64);
stmt(IRStmt_Put(counter_dw0_offset(), expr));
}
/* Read double word #0 of the counter register. */
static __inline__ IRExpr *
get_counter_dw0(void)
{
return IRExpr_Get(counter_dw0_offset(), Ity_I64);
}
/* Return the guest state offset of word #0 of the counter register. */
static __inline__ UInt
counter_w0_offset(void)
{
return counter_offset() + 0;
}
/* Return the guest state offset of word #1 of the counter register. */
static __inline__ UInt
counter_w1_offset(void)
{
return counter_offset() + 4;
}
/* Write word #0 of the counter to the guest state. */
static __inline__ void
put_counter_w0(IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I32);
stmt(IRStmt_Put(counter_w0_offset(), expr));
}
/* Read word #0 of the counter register. */
static __inline__ IRExpr *
get_counter_w0(void)
{
return IRExpr_Get(counter_w0_offset(), Ity_I32);
}
/* Write word #1 of the counter to the guest state. */
static __inline__ void
put_counter_w1(IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I32);
stmt(IRStmt_Put(counter_w1_offset(), expr));
}
/* Read word #1 of the counter register. */
static __inline__ IRExpr *
get_counter_w1(void)
{
return IRExpr_Get(counter_w1_offset(), Ity_I32);
}
/* Return the guest state offset of the fpc register. */
static UInt
fpc_offset(void)
{
return S390X_GUEST_OFFSET(guest_fpc);
}
/* Return the guest state offset of word #0 of the fpc register. */
static __inline__ UInt
fpc_w0_offset(void)
{
return fpc_offset() + 0;
}
/* Write word #0 of the fpc to the guest state. */
static __inline__ void
put_fpc_w0(IRExpr *expr)
{
vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_I32);
stmt(IRStmt_Put(fpc_w0_offset(), expr));
}
/* Read word #0 of the fpc register. */
static __inline__ IRExpr *
get_fpc_w0(void)
{
return IRExpr_Get(fpc_w0_offset(), Ity_I32);
}
/*------------------------------------------------------------*/
/*--- Rounding modes ---*/
/*------------------------------------------------------------*/
/* Extract the bfp rounding mode from the guest FPC reg and encode it as an
IRRoundingMode:
rounding mode | s390 | IR
-------------------------
to nearest | 00 | 00
to zero | 01 | 11
to +infinity | 10 | 10
to -infinity | 11 | 01
So: IR = (4 - s390) & 3
*/
static IRExpr *
get_bfp_rounding_mode_from_fpc(void)
{
IRTemp fpc_bits = newTemp(Ity_I32);
/* For z196 and later the bfp rounding mode is stored in bits [29:31].
Prior to that bits [30:31] contained the bfp rounding mode with
bit 29 being unused and having a value of 0. So we can always
extract the least significant 3 bits. */
assign(fpc_bits, binop(Iop_And32, get_fpc_w0(), mkU32(7)));
/* fixs390:
if (! s390_host_has_fpext && rounding_mode > 3) {
emulation warning @ runtime and
set fpc to round nearest
}
*/
/* For now silently adjust an unsupported rounding mode to "nearest" */
IRExpr *rm_s390 = mkite(binop(Iop_CmpLE32S, mkexpr(fpc_bits), mkU32(3)),
mkexpr(fpc_bits),
mkU32(S390_FPC_BFP_ROUND_NEAREST_EVEN));
// rm_IR = (4 - rm_s390) & 3;
return binop(Iop_And32, binop(Iop_Sub32, mkU32(4), rm_s390), mkU32(3));
}
/* Encode the s390 rounding mode as it appears in the m3 field of certain
instructions to VEX's IRRoundingMode. Rounding modes that cannot be
represented in VEX are converted to Irrm_NEAREST. The rationale is, that
Irrm_NEAREST refers to IEEE 754's roundTiesToEven which the standard
considers the default rounding mode (4.3.3). */
static IRTemp
encode_bfp_rounding_mode(UChar mode)
{
IRExpr *rm;
switch (mode) {
case S390_BFP_ROUND_PER_FPC:
rm = get_bfp_rounding_mode_from_fpc();
break;
case S390_BFP_ROUND_NEAREST_AWAY: /* not supported */
case S390_BFP_ROUND_PREPARE_SHORT: /* not supported */
case S390_BFP_ROUND_NEAREST_EVEN: rm = mkU32(Irrm_NEAREST); break;
case S390_BFP_ROUND_ZERO: rm = mkU32(Irrm_ZERO); break;
case S390_BFP_ROUND_POSINF: rm = mkU32(Irrm_PosINF); break;
case S390_BFP_ROUND_NEGINF: rm = mkU32(Irrm_NegINF); break;
default:
vpanic("encode_bfp_rounding_mode");
}
return mktemp(Ity_I32, rm);
}
/* Extract the DFP rounding mode from the guest FPC reg and encode it as an
IRRoundingMode:
rounding mode | s390 | IR
------------------------------------------------
to nearest, ties to even | 000 | 000
to zero | 001 | 011
to +infinity | 010 | 010
to -infinity | 011 | 001
to nearest, ties away from 0 | 100 | 100
to nearest, ties toward 0 | 101 | 111
to away from 0 | 110 | 110
to prepare for shorter precision | 111 | 101
So: IR = (s390 ^ ((s390 << 1) & 2))
*/
static IRExpr *
get_dfp_rounding_mode_from_fpc(void)
{
IRTemp fpc_bits = newTemp(Ity_I32);
/* The dfp rounding mode is stored in bits [25:27].
extract the bits at 25:27 and right shift 4 times. */
assign(fpc_bits, binop(Iop_Shr32,
binop(Iop_And32, get_fpc_w0(), mkU32(0x70)),
mkU8(4)));
IRExpr *rm_s390 = mkexpr(fpc_bits);
// rm_IR = (rm_s390 ^ ((rm_s390 << 1) & 2));
return binop(Iop_Xor32, rm_s390,
binop( Iop_And32,
binop(Iop_Shl32, rm_s390, mkU8(1)),
mkU32(2)));
}
/* Encode the s390 rounding mode as it appears in the m3 field of certain
instructions to VEX's IRRoundingMode. */
static IRTemp
encode_dfp_rounding_mode(UChar mode)
{
IRExpr *rm;
switch (mode) {
case S390_DFP_ROUND_PER_FPC_0:
case S390_DFP_ROUND_PER_FPC_2:
rm = get_dfp_rounding_mode_from_fpc(); break;
case S390_DFP_ROUND_NEAREST_EVEN_4:
case S390_DFP_ROUND_NEAREST_EVEN_8:
rm = mkU32(Irrm_NEAREST); break;
case S390_DFP_ROUND_NEAREST_TIE_AWAY_0_1:
case S390_DFP_ROUND_NEAREST_TIE_AWAY_0_12:
rm = mkU32(Irrm_NEAREST_TIE_AWAY_0); break;
case S390_DFP_ROUND_PREPARE_SHORT_3:
case S390_DFP_ROUND_PREPARE_SHORT_15:
rm = mkU32(Irrm_PREPARE_SHORTER); break;
case S390_DFP_ROUND_ZERO_5:
case S390_DFP_ROUND_ZERO_9:
rm = mkU32(Irrm_ZERO ); break;
case S390_DFP_ROUND_POSINF_6:
case S390_DFP_ROUND_POSINF_10:
rm = mkU32(Irrm_PosINF); break;
case S390_DFP_ROUND_NEGINF_7:
case S390_DFP_ROUND_NEGINF_11:
rm = mkU32(Irrm_NegINF); break;
case S390_DFP_ROUND_NEAREST_TIE_TOWARD_0:
rm = mkU32(Irrm_NEAREST_TIE_TOWARD_0); break;
case S390_DFP_ROUND_AWAY_0:
rm = mkU32(Irrm_AWAY_FROM_ZERO); break;
default:
vpanic("encode_dfp_rounding_mode");
}
return mktemp(Ity_I32, rm);
}
/*------------------------------------------------------------*/
/*--- Condition code helpers ---*/
/*------------------------------------------------------------*/
/* The result of a Iop_CmpFxx operation is a condition code. It is
encoded using the values defined in type IRCmpFxxResult.
Before we can store the condition code into the guest state (or do
anything else with it for that matter) we need to convert it to
the encoding that s390 uses. This is what this function does.
s390 VEX b6 b2 b0 cc.1 cc.0
0 0x40 EQ 1 0 0 0 0
1 0x01 LT 0 0 1 0 1
2 0x00 GT 0 0 0 1 0
3 0x45 Unordered 1 1 1 1 1
The following bits from the VEX encoding are interesting:
b0, b2, b6 with b0 being the LSB. We observe:
cc.0 = b0;
cc.1 = b2 | (~b0 & ~b6)
with cc being the s390 condition code.
*/
static IRExpr *
convert_vex_bfpcc_to_s390(IRTemp vex_cc)
{
IRTemp cc0 = newTemp(Ity_I32);
IRTemp cc1 = newTemp(Ity_I32);
IRTemp b0 = newTemp(Ity_I32);
IRTemp b2 = newTemp(Ity_I32);
IRTemp b6 = newTemp(Ity_I32);
assign(b0, binop(Iop_And32, mkexpr(vex_cc), mkU32(1)));
assign(b2, binop(Iop_And32, binop(Iop_Shr32, mkexpr(vex_cc), mkU8(2)),
mkU32(1)));
assign(b6, binop(Iop_And32, binop(Iop_Shr32, mkexpr(vex_cc), mkU8(6)),
mkU32(1)));
assign(cc0, mkexpr(b0));
assign(cc1, binop(Iop_Or32, mkexpr(b2),
binop(Iop_And32,
binop(Iop_Sub32, mkU32(1), mkexpr(b0)), /* ~b0 */
binop(Iop_Sub32, mkU32(1), mkexpr(b6)) /* ~b6 */
)));
return binop(Iop_Or32, mkexpr(cc0), binop(Iop_Shl32, mkexpr(cc1), mkU8(1)));
}
/* The result of a Iop_CmpDxx operation is a condition code. It is
encoded using the values defined in type IRCmpDxxResult.
Before we can store the condition code into the guest state (or do
anything else with it for that matter) we need to convert it to
the encoding that s390 uses. This is what this function does. */
static IRExpr *
convert_vex_dfpcc_to_s390(IRTemp vex_cc)
{
/* The VEX encodings for IRCmpDxxResult and IRCmpFxxResult are the
same. currently. */
return convert_vex_bfpcc_to_s390(vex_cc);
}
/*------------------------------------------------------------*/
/*--- Build IR for formats ---*/
/*------------------------------------------------------------*/
static void
s390_format_I(const HChar *(*irgen)(UChar i),
UChar i)
{
const HChar *mnm = irgen(i);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC2(MNM, UINT), mnm, i);
}
static void
s390_format_E(const HChar *(*irgen)(void))
{
const HChar *mnm = irgen();
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC1(MNM), mnm);
}
static void
s390_format_RI(const HChar *(*irgen)(UChar r1, UShort i2),
UChar r1, UShort i2)
{
irgen(r1, i2);
}
static void
s390_format_RI_RU(const HChar *(*irgen)(UChar r1, UShort i2),
UChar r1, UShort i2)
{
const HChar *mnm = irgen(r1, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, UINT), mnm, r1, i2);
}
static void
s390_format_RI_RI(const HChar *(*irgen)(UChar r1, UShort i2),
UChar r1, UShort i2)
{
const HChar *mnm = irgen(r1, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, INT), mnm, r1, (Int)(Short)i2);
}
static void
s390_format_RI_RP(const HChar *(*irgen)(UChar r1, UShort i2),
UChar r1, UShort i2)
{
const HChar *mnm = irgen(r1, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, PCREL), mnm, r1, (Int)(Short)i2);
}
static void
s390_format_RIE_RRP(const HChar *(*irgen)(UChar r1, UChar r3, UShort i2),
UChar r1, UChar r3, UShort i2)
{
const HChar *mnm = irgen(r1, r3, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, GPR, PCREL), mnm, r1, r3, (Int)(Short)i2);
}
static void
s390_format_RIE_RRI0(const HChar *(*irgen)(UChar r1, UChar r3, UShort i2),
UChar r1, UChar r3, UShort i2)
{
const HChar *mnm = irgen(r1, r3, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, GPR, INT), mnm, r1, r3, (Int)(Short)i2);
}
static void
s390_format_RIE_RRUUU(const HChar *(*irgen)(UChar r1, UChar r2, UChar i3,
UChar i4, UChar i5),
UChar r1, UChar r2, UChar i3, UChar i4, UChar i5)
{
const HChar *mnm = irgen(r1, r2, i3, i4, i5);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC6(MNM, GPR, GPR, UINT, UINT, UINT), mnm, r1, r2, i3, i4,
i5);
}
static void
s390_format_RIE_RRPU(const HChar *(*irgen)(UChar r1, UChar r2, UShort i4,
UChar m3),
UChar r1, UChar r2, UShort i4, UChar m3)
{
const HChar *mnm = irgen(r1, r2, i4, m3);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(XMNM, GPR, GPR, CABM, PCREL), S390_XMNM_CAB, mnm, m3, r1,
r2, m3, (Int)(Short)i4);
}
static void
s390_format_RIE_RUPU(const HChar *(*irgen)(UChar r1, UChar m3, UShort i4,
UChar i2),
UChar r1, UChar m3, UShort i4, UChar i2)
{
const HChar *mnm = irgen(r1, m3, i4, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(XMNM, GPR, UINT, CABM, PCREL), S390_XMNM_CAB, mnm, m3,
r1, i2, m3, (Int)(Short)i4);
}
static void
s390_format_RIE_RUPI(const HChar *(*irgen)(UChar r1, UChar m3, UShort i4,
UChar i2),
UChar r1, UChar m3, UShort i4, UChar i2)
{
const HChar *mnm = irgen(r1, m3, i4, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(XMNM, GPR, INT, CABM, PCREL), S390_XMNM_CAB, 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)
{
irgen(r1, i2);
}
static void
s390_format_RIL_RU(const HChar *(*irgen)(UChar r1, UInt i2),
UChar r1, UInt i2)
{
const HChar *mnm = irgen(r1, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, UINT), mnm, r1, i2);
}
static void
s390_format_RIL_RI(const HChar *(*irgen)(UChar r1, UInt i2),
UChar r1, UInt i2)
{
const HChar *mnm = irgen(r1, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, INT), mnm, r1, i2);
}
static void
s390_format_RIL_RP(const HChar *(*irgen)(UChar r1, UInt i2),
UChar r1, UInt i2)
{
const HChar *mnm = irgen(r1, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, PCREL), mnm, r1, i2);
}
static void
s390_format_RIL_UP(const HChar *(*irgen)(void),
UChar r1, UInt i2)
{
const HChar *mnm = irgen();
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, UINT, PCREL), mnm, r1, i2);
}
static void
s390_format_RIS_RURDI(const HChar *(*irgen)(UChar r1, UChar m3, UChar i2,
IRTemp op4addr),
UChar r1, UChar m3, UChar b4, UShort d4, UChar i2)
{
const HChar *mnm;
IRTemp op4addr = newTemp(Ity_I64);
assign(op4addr, binop(Iop_Add64, mkU64(d4), b4 != 0 ? get_gpr_dw0(b4) :
mkU64(0)));
mnm = irgen(r1, m3, i2, op4addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(XMNM, GPR, INT, CABM, UDXB), S390_XMNM_CAB, mnm, m3, r1,
(Int)(Char)i2, m3, d4, 0, b4);
}
static void
s390_format_RIS_RURDU(const HChar *(*irgen)(UChar r1, UChar m3, UChar i2,
IRTemp op4addr),
UChar r1, UChar m3, UChar b4, UShort d4, UChar i2)
{
const HChar *mnm;
IRTemp op4addr = newTemp(Ity_I64);
assign(op4addr, binop(Iop_Add64, mkU64(d4), b4 != 0 ? get_gpr_dw0(b4) :
mkU64(0)));
mnm = irgen(r1, m3, i2, op4addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(XMNM, GPR, UINT, CABM, UDXB), S390_XMNM_CAB, mnm, m3, r1,
i2, m3, d4, 0, b4);
}
static void
s390_format_RR(const HChar *(*irgen)(UChar r1, UChar r2),
UChar r1, UChar r2)
{
irgen(r1, r2);
}
static void
s390_format_RR_RR(const HChar *(*irgen)(UChar r1, UChar r2),
UChar r1, UChar r2)
{
const HChar *mnm = irgen(r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, GPR), mnm, r1, r2);
}
static void
s390_format_RR_FF(const HChar *(*irgen)(UChar r1, UChar r2),
UChar r1, UChar r2)
{
const HChar *mnm = irgen(r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, FPR, FPR), mnm, r1, r2);
}
static void
s390_format_RRE(const HChar *(*irgen)(UChar r1, UChar r2),
UChar r1, UChar r2)
{
irgen(r1, r2);
}
static void
s390_format_RRE_RR(const HChar *(*irgen)(UChar r1, UChar r2),
UChar r1, UChar r2)
{
const HChar *mnm = irgen(r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, GPR), mnm, r1, r2);
}
static void
s390_format_RRE_FF(const HChar *(*irgen)(UChar r1, UChar r2),
UChar r1, UChar r2)
{
const HChar *mnm = irgen(r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, FPR, FPR), mnm, r1, r2);
}
static void
s390_format_RRE_RF(const HChar *(*irgen)(UChar, UChar),
UChar r1, UChar r2)
{
const HChar *mnm = irgen(r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, FPR), mnm, r1, r2);
}
static void
s390_format_RRE_FR(const HChar *(*irgen)(UChar r1, UChar r2),
UChar r1, UChar r2)
{
const HChar *mnm = irgen(r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, FPR, GPR), mnm, r1, r2);
}
static void
s390_format_RRE_R0(const HChar *(*irgen)(UChar r1),
UChar r1)
{
const HChar *mnm = irgen(r1);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC2(MNM, GPR), mnm, r1);
}
static void
s390_format_RRE_F0(const HChar *(*irgen)(UChar r1),
UChar r1)
{
const HChar *mnm = irgen(r1);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC2(MNM, FPR), mnm, r1);
}
static void
s390_format_RRF_M0RERE(const HChar *(*irgen)(UChar m3, UChar r1, UChar r2),
UChar m3, UChar r1, UChar r2)
{
const HChar *mnm = irgen(m3, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, GPR, UINT), mnm, r1, r2, m3);
}
static void
s390_format_RRF_F0FF(const HChar *(*irgen)(UChar, UChar, UChar),
UChar r1, UChar r3, UChar r2)
{
const HChar *mnm = irgen(r1, r3, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, FPR, FPR, FPR), mnm, r1, r3, r2);
}
static void
s390_format_RRF_F0FR(const HChar *(*irgen)(UChar, UChar, UChar),
UChar r3, UChar r1, UChar r2)
{
const HChar *mnm = irgen(r3, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, FPR, FPR, GPR), mnm, r1, r3, r2);
}
static void
s390_format_RRF_UUFF(const HChar *(*irgen)(UChar m3, UChar m4, UChar r1,
UChar r2),
UChar m3, UChar m4, UChar r1, UChar r2)
{
const HChar *mnm = irgen(m3, m4, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(MNM, FPR, UINT, FPR, UINT), mnm, r1, m3, r2, m4);
}
static void
s390_format_RRF_0UFF(const HChar *(*irgen)(UChar m4, UChar r1, UChar r2),
UChar m4, UChar r1, UChar r2)
{
const HChar *mnm = irgen(m4, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, FPR, FPR, UINT), mnm, r1, r2, m4);
}
static void
s390_format_RRF_UUFR(const HChar *(*irgen)(UChar m3, UChar m4, UChar r1,
UChar r2),
UChar m3, UChar m4, UChar r1, UChar r2)
{
const HChar *mnm = irgen(m3, m4, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(MNM, FPR, UINT, GPR, UINT), mnm, r1, m3, r2, m4);
}
static void
s390_format_RRF_UURF(const HChar *(*irgen)(UChar m3, UChar m4, UChar r1,
UChar r2),
UChar m3, UChar m4, UChar r1, UChar r2)
{
const HChar *mnm = irgen(m3, m4, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(MNM, GPR, UINT, FPR, UINT), mnm, r1, m3, r2, m4);
}
static void
s390_format_RRF_U0RR(const HChar *(*irgen)(UChar m3, UChar r1, UChar r2),
UChar m3, UChar r1, UChar r2, Int xmnm_kind)
{
irgen(m3, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(XMNM, GPR, GPR), xmnm_kind, m3, r1, r2);
}
static void
s390_format_RRF_F0FF2(const HChar *(*irgen)(UChar, UChar, UChar),
UChar r3, UChar r1, UChar r2)
{
const HChar *mnm = irgen(r3, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, FPR, FPR, FPR), mnm, r1, r3, r2);
}
static void
s390_format_RRF_FFRU(const HChar *(*irgen)(UChar, UChar, UChar, UChar),
UChar r3, UChar m4, UChar r1, UChar r2)
{
const HChar *mnm = irgen(r3, m4, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(MNM, FPR, FPR, GPR, UINT), mnm, r1, r3, r2, m4);
}
static void
s390_format_RRF_FUFF(const HChar *(*irgen)(UChar, UChar, UChar, UChar),
UChar r3, UChar m4, UChar r1, UChar r2)
{
const HChar *mnm = irgen(r3, m4, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), mnm, r1, r3, r2, m4);
}
static void
s390_format_RRF_FUFF2(const HChar *(*irgen)(UChar, UChar, UChar, UChar),
UChar r3, UChar m4, UChar r1, UChar r2)
{
const HChar *mnm = irgen(r3, m4, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), mnm, r1, r2, r3, m4);
}
static void
s390_format_RRF_R0RR2(const HChar *(*irgen)(UChar r3, UChar r1, UChar r2),
UChar r3, UChar r1, UChar r2)
{
const HChar *mnm = irgen(r3, r1, r2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, GPR, GPR), mnm, r1, r2, r3);
}
static void
s390_format_RRS(const HChar *(*irgen)(UChar r1, UChar r2, UChar m3,
IRTemp op4addr),
UChar r1, UChar r2, UChar b4, UShort d4, UChar m3)
{
const HChar *mnm;
IRTemp op4addr = newTemp(Ity_I64);
assign(op4addr, binop(Iop_Add64, mkU64(d4), b4 != 0 ? get_gpr_dw0(b4) :
mkU64(0)));
mnm = irgen(r1, r2, m3, op4addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC5(XMNM, GPR, GPR, CABM, UDXB), S390_XMNM_CAB, mnm, m3, r1,
r2, m3, d4, 0, b4);
}
static void
s390_format_RS_R0RD(const HChar *(*irgen)(UChar r1, IRTemp op2addr),
UChar r1, UChar b2, UShort d2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(r1, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, UDXB), mnm, r1, d2, 0, b2);
}
static void
s390_format_RS_RRRD(const HChar *(*irgen)(UChar r1, UChar r3, IRTemp op2addr),
UChar r1, UChar r3, UChar b2, UShort d2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(r1, r3, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, GPR, UDXB), mnm, r1, r3, d2, 0, b2);
}
static void
s390_format_RS_RURD(const HChar *(*irgen)(UChar r1, UChar r3, IRTemp op2addr),
UChar r1, UChar r3, UChar b2, UShort d2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(r1, r3, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, UINT, UDXB), mnm, r1, r3, d2, 0, b2);
}
static void
s390_format_RS_AARD(const HChar *(*irgen)(UChar, UChar, IRTemp),
UChar r1, UChar r3, UChar b2, UShort d2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(r1, r3, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, AR, AR, UDXB), mnm, r1, r3, d2, 0, b2);
}
static void
s390_format_RSI_RRP(const HChar *(*irgen)(UChar r1, UChar r3, UShort i2),
UChar r1, UChar r3, UShort i2)
{
const HChar *mnm = irgen(r1, r3, i2);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, GPR, PCREL), mnm, r1, r3, (Int)(Short)i2);
}
static void
s390_format_RSY_RRRD(const HChar *(*irgen)(UChar r1, UChar r3, IRTemp op2addr),
UChar r1, UChar r3, UChar b2, UShort dl2, UChar dh2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
IRTemp d2 = newTemp(Ity_I64);
assign(d2, mkU64(((ULong)(Long)(Char)dh2 << 12) | ((ULong)dl2)));
assign(op2addr, binop(Iop_Add64, mkexpr(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(r1, r3, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, GPR, SDXB), mnm, r1, r3, dh2, dl2, 0, b2);
}
static void
s390_format_RSY_AARD(const HChar *(*irgen)(UChar, UChar, IRTemp),
UChar r1, UChar r3, UChar b2, UShort dl2, UChar dh2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
IRTemp d2 = newTemp(Ity_I64);
assign(d2, mkU64(((ULong)(Long)(Char)dh2 << 12) | ((ULong)dl2)));
assign(op2addr, binop(Iop_Add64, mkexpr(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(r1, r3, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, AR, AR, SDXB), mnm, r1, r3, dh2, dl2, 0, b2);
}
static void
s390_format_RSY_RURD(const HChar *(*irgen)(UChar r1, UChar r3, IRTemp op2addr),
UChar r1, UChar r3, UChar b2, UShort dl2, UChar dh2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
IRTemp d2 = newTemp(Ity_I64);
assign(d2, mkU64(((ULong)(Long)(Char)dh2 << 12) | ((ULong)dl2)));
assign(op2addr, binop(Iop_Add64, mkexpr(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(r1, r3, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, GPR, UINT, SDXB), mnm, r1, r3, dh2, dl2, 0, b2);
}
static void
s390_format_RSY_RDRM(const HChar *(*irgen)(UChar r1, IRTemp op2addr),
UChar r1, UChar m3, UChar b2, UShort dl2, UChar dh2,
Int xmnm_kind)
{
IRTemp op2addr = newTemp(Ity_I64);
IRTemp d2 = newTemp(Ity_I64);
next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0)));
assign(d2, mkU64(((ULong)(Long)(Char)dh2 << 12) | ((ULong)dl2)));
assign(op2addr, binop(Iop_Add64, mkexpr(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
irgen(r1, op2addr);
vassert(dis_res->whatNext == Dis_Continue);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(XMNM, GPR, SDXB), xmnm_kind, m3, r1, dh2, dl2, 0, b2);
}
static void
s390_format_RX(const HChar *(*irgen)(UChar r1, UChar x2, UChar b2, UShort d2,
IRTemp op2addr),
UChar r1, UChar x2, UChar b2, UShort d2)
{
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)));
irgen(r1, x2, b2, d2, op2addr);
}
static void
s390_format_RX_RRRD(const HChar *(*irgen)(UChar r1, IRTemp op2addr),
UChar r1, UChar x2, UChar b2, UShort d2)
{
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);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, UDXB), mnm, r1, d2, x2, b2);
}
static void
s390_format_RX_FRRD(const HChar *(*irgen)(UChar r1, IRTemp op2addr),
UChar r1, UChar x2, UChar b2, UShort d2)
{
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);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, FPR, UDXB), mnm, r1, d2, x2, b2);
}
static void
s390_format_RXE_FRRD(const HChar *(*irgen)(UChar r1, IRTemp op2addr),
UChar r1, UChar x2, UChar b2, UShort d2)
{
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);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, FPR, 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)
{
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(r3, op2addr, r1);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC4(MNM, FPR, FPR, UDXB), mnm, r1, r3, d2, x2, b2);
}
static void
s390_format_RXY_RRRD(const HChar *(*irgen)(UChar r1, IRTemp op2addr),
UChar r1, UChar x2, UChar b2, UShort dl2, UChar dh2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
IRTemp d2 = newTemp(Ity_I64);
assign(d2, mkU64(((ULong)(Long)(Char)dh2 << 12) | ((ULong)dl2)));
assign(op2addr, binop(Iop_Add64, binop(Iop_Add64, mkexpr(d2),
b2 != 0 ? get_gpr_dw0(b2) : mkU64(0)), x2 != 0 ? get_gpr_dw0(x2) :
mkU64(0)));
mnm = irgen(r1, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, GPR, SDXB), mnm, r1, dh2, dl2, x2, b2);
}
static void
s390_format_RXY_FRRD(const HChar *(*irgen)(UChar r1, IRTemp op2addr),
UChar r1, UChar x2, UChar b2, UShort dl2, UChar dh2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
IRTemp d2 = newTemp(Ity_I64);
assign(d2, mkU64(((ULong)(Long)(Char)dh2 << 12) | ((ULong)dl2)));
assign(op2addr, binop(Iop_Add64, binop(Iop_Add64, mkexpr(d2),
b2 != 0 ? get_gpr_dw0(b2) : mkU64(0)), x2 != 0 ? get_gpr_dw0(x2) :
mkU64(0)));
mnm = irgen(r1, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, FPR, SDXB), mnm, r1, dh2, dl2, x2, b2);
}
static void
s390_format_RXY_URRD(const HChar *(*irgen)(void),
UChar r1, UChar x2, UChar b2, UShort dl2, UChar dh2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
IRTemp d2 = newTemp(Ity_I64);
assign(d2, mkU64(((ULong)(Long)(Char)dh2 << 12) | ((ULong)dl2)));
assign(op2addr, binop(Iop_Add64, binop(Iop_Add64, mkexpr(d2),
b2 != 0 ? get_gpr_dw0(b2) : mkU64(0)), x2 != 0 ? get_gpr_dw0(x2) :
mkU64(0)));
mnm = irgen();
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, UINT, SDXB), mnm, r1, dh2, dl2, x2, b2);
}
static void
s390_format_S_RD(const HChar *(*irgen)(IRTemp op2addr),
UChar b2, UShort d2)
{
const HChar *mnm;
IRTemp op2addr = newTemp(Ity_I64);
assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC2(MNM, UDXB), mnm, d2, 0, b2);
}
static void
s390_format_SI_URD(const HChar *(*irgen)(UChar i2, IRTemp op1addr),
UChar i2, UChar b1, UShort d1)
{
const HChar *mnm;
IRTemp op1addr = newTemp(Ity_I64);
assign(op1addr, binop(Iop_Add64, mkU64(d1), b1 != 0 ? get_gpr_dw0(b1) :
mkU64(0)));
mnm = irgen(i2, op1addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, UDXB, UINT), mnm, d1, 0, b1, i2);
}
static void
s390_format_SIY_URD(const HChar *(*irgen)(UChar i2, IRTemp op1addr),
UChar i2, UChar b1, UShort dl1, UChar dh1)
{
const HChar *mnm;
IRTemp op1addr = newTemp(Ity_I64);
IRTemp d1 = newTemp(Ity_I64);
assign(d1, mkU64(((ULong)(Long)(Char)dh1 << 12) | ((ULong)dl1)));
assign(op1addr, binop(Iop_Add64, mkexpr(d1), b1 != 0 ? get_gpr_dw0(b1) :
mkU64(0)));
mnm = irgen(i2, op1addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, SDXB, UINT), mnm, dh1, dl1, 0, b1, i2);
}
static void
s390_format_SIY_IRD(const HChar *(*irgen)(UChar i2, IRTemp op1addr),
UChar i2, UChar b1, UShort dl1, UChar dh1)
{
const HChar *mnm;
IRTemp op1addr = newTemp(Ity_I64);
IRTemp d1 = newTemp(Ity_I64);
assign(d1, mkU64(((ULong)(Long)(Char)dh1 << 12) | ((ULong)dl1)));
assign(op1addr, binop(Iop_Add64, mkexpr(d1), b1 != 0 ? get_gpr_dw0(b1) :
mkU64(0)));
mnm = irgen(i2, op1addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, SDXB, INT), mnm, dh1, dl1, 0, b1, (Int)(Char)i2);
}
static void
s390_format_SS_L0RDRD(const HChar *(*irgen)(UChar, IRTemp, IRTemp),
UChar l, UChar b1, UShort d1, UChar b2, UShort d2)
{
const HChar *mnm;
IRTemp op1addr = newTemp(Ity_I64);
IRTemp op2addr = newTemp(Ity_I64);
assign(op1addr, binop(Iop_Add64, mkU64(d1), b1 != 0 ? get_gpr_dw0(b1) :
mkU64(0)));
assign(op2addr, binop(Iop_Add64, mkU64(d2), b2 != 0 ? get_gpr_dw0(b2) :
mkU64(0)));
mnm = irgen(l, op1addr, op2addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, UDLB, UDXB), mnm, d1, l, b1, d2, 0, b2);
}
static void
s390_format_SIL_RDI(const HChar *(*irgen)(UShort i2, IRTemp op1addr),
UChar b1, UShort d1, UShort i2)
{
const HChar *mnm;
IRTemp op1addr = newTemp(Ity_I64);
assign(op1addr, binop(Iop_Add64, mkU64(d1), b1 != 0 ? get_gpr_dw0(b1) :
mkU64(0)));
mnm = irgen(i2, op1addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, UDXB, INT), mnm, d1, 0, b1, (Int)(Short)i2);
}
static void
s390_format_SIL_RDU(const HChar *(*irgen)(UShort i2, IRTemp op1addr),
UChar b1, UShort d1, UShort i2)
{
const HChar *mnm;
IRTemp op1addr = newTemp(Ity_I64);
assign(op1addr, binop(Iop_Add64, mkU64(d1), b1 != 0 ? get_gpr_dw0(b1) :
mkU64(0)));
mnm = irgen(i2, op1addr);
if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
s390_disasm(ENC3(MNM, UDXB, UINT), mnm, d1, 0, b1, i2);
}
/*------------------------------------------------------------*/
/*--- Build IR for opcodes ---*/
/*------------------------------------------------------------*/
static const HChar *
s390_irgen_AR(UChar r1, UChar r2)
{
IRTemp op1 = newTemp(Ity_I32);
IRTemp op2 = newTemp(Ity_I32);
IRTemp result = newTemp(Ity_I32);
assign(op1, get_gpr_w1(r1));
assign(op2, get_gpr_w1(r2));
assign(result, binop(Iop_Add32, mkexpr(op1), mkexpr(op2)));
s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_32, op1, op2);
put_gpr_w1(r1, mkexpr(result));
return "ar";
}
static const HChar *
s390_irgen_AGR(UChar r1, UChar r2)
{
IRTemp op1 = newTemp(Ity_I64);
IRTemp op2 = newTemp(Ity_I64);
IRTemp result = newTemp(Ity_I64);
assign(op1, get_gpr_dw0(r1));
assign(op2, get_gpr_dw0(r2));
assign(result, binop(Iop_Add64, mkexpr(op1), mkexpr(op2)));
s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op1, op2);
put_gpr_dw0(r1, mkexpr(result));
return "agr";
}
static const HChar *
s390_irgen_AGFR(UChar r1, UChar r2)
{
IRTemp op1 = newTemp(Ity_I64);
IRTemp op2 = newTemp(Ity_I64);
IRTemp result = newTemp(Ity_I64);
assign(op1, get_gpr_dw0(r1));
assign(op2, unop(Iop_32Sto64, get_gpr_w1(r2)));
assign(result, binop(Iop_Add64, mkexpr(op1), mkexpr(op2)));
s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op1, op2);
put_gpr_dw0(r1, mkexpr(result));
return "agfr";
}
static const HChar *
s390_irgen_ARK(UChar r3, UChar r1, UChar r2)
{
IRTemp op2 = newTemp(Ity_I32);
IRTemp op3 = newTemp(Ity_I32);
IRTemp result = newTemp(Ity_I32);
assign(op2, get_gpr_w1(r2));
assign(op3, get_gpr_w1(r3));
assign(result, binop(Iop_Add32, mkexpr(op2), mkexpr(op3)));
s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_32, op2, op3);
put_gpr_w1(r1, mkexpr(result));
return "ark";
}
static const HChar *
s390_irgen_AGRK(UChar r3, UChar r1, UChar r2)
{
IRTemp op2 = newTemp(Ity_I64);
IRTemp op3 = newTemp(Ity_I64);
IRTemp result = newTemp(Ity_I64);
assign(op2, get_gpr_dw0(r2));
assign(op3, get_gpr_dw0(r3));
assign(result, binop(Iop_Add64, mkexpr(op2), mkexpr(op3)));
s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_64, op2, op3);
put_gpr_dw0(r1, mkexpr(result));
return "agrk";
}
static const HChar *
s390_irgen_A(UChar r1, IRTemp op2addr)
{
IRTemp op1 = newTemp(Ity_I32);
IRTemp op2 = newTemp(Ity_I32);
IRTemp result = newTemp(Ity_I32);
assign(op1, get_gpr_w1(r1));
assign(op2, load(Ity_I32, mkexpr(op2addr)));
assign(result, binop(Iop_Add32, mkexpr(op1), mkexpr(op2)));
s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_32, op1, op2);
put_gpr_w1(r1, mkexpr(result));
return "a";
}
static const HChar *
s390_irgen_AY(UChar r1, IRTemp op2addr)
{