src/mono/mono/mini/mini-s390x.c (5,517 lines of code) (raw):

/** * @file * @author - Neale Ferguson (Neale.Ferguson@SoftwareAG-usa.com) * * @section description * Function - S/390 backend for the Mono code generator. * * Date - January, 2004 * * Derivation - From mini-x86 & mini-ppc by - * Paolo Molaro (lupus@ximian.com) * Dietmar Maurer (dietmar@ximian.com) * */ /*------------------------------------------------------------------*/ /* D e f i n e s */ /*------------------------------------------------------------------*/ #define MAX_ARCH_DELEGATE_PARAMS 10 #define EMIT_COND_BRANCH(ins,cond) \ { \ if (ins->inst_true_bb->native_offset) { \ int displace; \ displace = ((cfg->native_code + \ ins->inst_true_bb->native_offset) - code) / 2; \ if (s390_is_imm16(displace)) { \ s390_brc (code, cond, displace); \ } else { \ s390_jcl (code, cond, displace); \ } \ } else { \ mono_add_patch_info_rel (cfg, code - cfg->native_code, \ MONO_PATCH_INFO_BB, ins->inst_true_bb, \ MONO_R_S390_RELINS); \ s390_jcl (code, cond, 0); \ } \ } #define EMIT_UNCOND_BRANCH(ins) \ { \ if (ins->inst_target_bb->native_offset) { \ int displace; \ displace = ((cfg->native_code + \ ins->inst_target_bb->native_offset) - code) / 2; \ if (s390_is_imm16(displace)) { \ s390_brc (code, S390_CC_UN, displace); \ } else { \ s390_jcl (code, S390_CC_UN, displace); \ } \ } else { \ mono_add_patch_info_rel (cfg, code - cfg->native_code, \ MONO_PATCH_INFO_BB, ins->inst_target_bb, \ MONO_R_S390_RELINS); \ s390_jcl (code, S390_CC_UN, 0); \ } \ } #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) \ do { \ mono_add_patch_info (cfg, code - cfg->native_code, \ MONO_PATCH_INFO_EXC, exc_name); \ s390_jcl (code, cond, 0); \ } while (0); #define EMIT_COMP_AND_BRANCH(ins, cab, cmp) \ { \ if (ins->inst_true_bb->native_offset) { \ int displace; \ displace = ((cfg->native_code + \ ins->inst_true_bb->native_offset) - code) / 2; \ if (s390_is_imm16(displace)) { \ s390_##cab (code, ins->sreg1, ins->sreg2, \ ins->sreg3, displace); \ } else { \ s390_##cmp (code, ins->sreg1, ins->sreg2); \ displace = ((cfg->native_code + \ ins->inst_true_bb->native_offset) - code) / 2; \ s390_jcl (code, ins->sreg3, displace); \ } \ } else { \ s390_##cmp (code, ins->sreg1, ins->sreg2); \ mono_add_patch_info_rel (cfg, code - cfg->native_code, \ MONO_PATCH_INFO_BB, ins->inst_true_bb, \ MONO_R_S390_RELINS); \ s390_jcl (code, ins->sreg3, 0); \ } \ } #define EMIT_COMP_AND_BRANCH_IMM(ins, cab, cmp, lat, logical) \ { \ if (ins->inst_true_bb->native_offset) { \ int displace; \ if ((ins->backend.data == 0) && (!logical)) { \ s390_##lat (code, ins->sreg1, ins->sreg1); \ displace = ((cfg->native_code + \ ins->inst_true_bb->native_offset) - code) / 2; \ if (s390_is_imm16(displace)) { \ s390_brc (code, ins->sreg3, displace); \ } else { \ s390_jcl (code, ins->sreg3, displace); \ } \ } else { \ S390_SET (code, s390_r0, ins->backend.data); \ displace = ((cfg->native_code + \ ins->inst_true_bb->native_offset) - code) / 2; \ if (s390_is_imm16(displace)) { \ s390_##cab (code, ins->sreg1, s390_r0, \ ins->sreg3, displace); \ } else { \ s390_##cmp (code, ins->sreg1, s390_r0); \ displace = ((cfg->native_code + \ ins->inst_true_bb->native_offset) - code) / 2; \ s390_jcl (code, ins->sreg3, displace); \ } \ } \ } else { \ if ((ins->backend.data == 0) && (!logical)) { \ s390_##lat (code, ins->sreg1, ins->sreg1); \ } else { \ S390_SET (code, s390_r0, ins->backend.data); \ s390_##cmp (code, ins->sreg1, s390_r0); \ } \ mono_add_patch_info_rel (cfg, code - cfg->native_code, \ MONO_PATCH_INFO_BB, ins->inst_true_bb, \ MONO_R_S390_RELINS); \ s390_jcl (code, ins->sreg3, 0); \ } \ } #define CHECK_SRCDST_COM \ if (ins->dreg == ins->sreg2) { \ src2 = ins->sreg1; \ } else { \ src2 = ins->sreg2; \ if (ins->dreg != ins->sreg1) { \ s390_lgr (code, ins->dreg, ins->sreg1); \ } \ } #define CHECK_SRCDST_NCOM \ if (ins->dreg == ins->sreg2) { \ src2 = s390_r13; \ s390_lgr (code, s390_r13, ins->sreg2); \ } else { \ src2 = ins->sreg2; \ } \ if (ins->dreg != ins->sreg1) { \ s390_lgr (code, ins->dreg, ins->sreg1); \ } #define CHECK_SRCDST_COM_I \ if (ins->dreg == ins->sreg2) { \ src2 = ins->sreg1; \ } else { \ src2 = ins->sreg2; \ if (ins->dreg != ins->sreg1) { \ s390_lgfr (code, ins->dreg, ins->sreg1); \ } \ } #define CHECK_SRCDST_NCOM_I \ if (ins->dreg == ins->sreg2) { \ src2 = s390_r13; \ s390_lgfr (code, s390_r13, ins->sreg2); \ } else { \ src2 = ins->sreg2; \ } \ if (ins->dreg != ins->sreg1) { \ s390_lgfr (code, ins->dreg, ins->sreg1); \ } #define CHECK_SRCDST_COM_F \ if (ins->dreg == ins->sreg2) { \ src2 = ins->sreg1; \ } else { \ src2 = ins->sreg2; \ if (ins->dreg != ins->sreg1) { \ s390_ldr (code, ins->dreg, ins->sreg1); \ } \ } #define CHECK_SRCDST_NCOM_F(op) \ if (ins->dreg == ins->sreg2) { \ s390_lgdr (code, s390_r0, s390_f15); \ s390_ldr (code, s390_f15, ins->sreg2); \ if (ins->dreg != ins->sreg1) { \ s390_ldr (code, ins->dreg, ins->sreg1); \ } \ s390_ ## op (code, ins->dreg, s390_f15); \ s390_ldgr (code, s390_f15, s390_r0); \ } else { \ if (ins->dreg != ins->sreg1) { \ s390_ldr (code, ins->dreg, ins->sreg1); \ } \ s390_ ## op (code, ins->dreg, ins->sreg2); \ } #define CHECK_SRCDST_NCOM_FR(op, m) \ s390_lgdr (code, s390_r1, s390_f14); \ if (ins->dreg == ins->sreg2) { \ s390_lgdr (code, s390_r0, s390_f15); \ s390_ldr (code, s390_f15, ins->sreg2); \ if (ins->dreg != ins->sreg1) { \ s390_ldr (code, ins->dreg, ins->sreg1); \ } \ s390_ ## op (code, ins->dreg, s390_f15, m, s390_f14); \ s390_ldgr (code, s390_f15, s390_r0); \ } else { \ if (ins->dreg != ins->sreg1) { \ s390_ldr (code, ins->dreg, ins->sreg1); \ } \ s390_ ## op (code, ins->dreg, ins->sreg2, m, s390_f14); \ } \ s390_ldgr (code, s390_f14, s390_r1); #undef DEBUG #define DEBUG(a) if (cfg->verbose_level > 1) a #define MAX_EXC 16 #define S390_TRACE_STACK_SIZE (5*sizeof(gpointer)+4*sizeof(gdouble)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) /* * imt trampoline size values */ #define CMP_SIZE 24 #define LOADCON_SIZE 20 #define LOAD_SIZE 6 #define BR_SIZE 2 #define JUMP_SIZE 6 #define ENABLE_WRONG_METHOD_CHECK 0 /*========================= End of Defines =========================*/ /*------------------------------------------------------------------*/ /* I n c l u d e s */ /*------------------------------------------------------------------*/ #include "mini.h" #include <string.h> #include <sys/types.h> #include <unistd.h> #include <mono/metadata/abi-details.h> #include <mono/metadata/appdomain.h> #include <mono/metadata/debug-helpers.h> #include <mono/metadata/profiler-private.h> #include <mono/utils/mono-error.h> #include <mono/utils/mono-error-internals.h> #include <mono/utils/mono-math.h> #include <mono/utils/mono-mmap.h> #include <mono/utils/mono-hwcap.h> #include <mono/utils/mono-threads.h> #include <mono/utils/unlocked.h> #include "mini-s390x.h" #include "cpu-s390x.h" #include "jit-icalls.h" #include "ir-emit.h" #include "mini-gc.h" #include "aot-runtime.h" #include "mini-runtime.h" /*========================= End of Includes ========================*/ /*------------------------------------------------------------------*/ /* T y p e d e f s */ /*------------------------------------------------------------------*/ /** * Track stack use */ typedef struct { guint stack_size, code_size, parm_size, retStruct; } size_data; /** * ABI - register use in calls etc. */ typedef enum { RegTypeGeneral, RegTypeBase, RegTypeFP, RegTypeFPR4, RegTypeStructByVal, RegTypeStructByValInFP, RegTypeStructByAddr } ArgStorage; /** * Track method arguments */ typedef struct { gint32 offset; /* offset from caller's stack */ guint16 vtsize; /* in param area */ guint8 reg; ArgStorage regtype; guint32 size; /* Size of structure used by RegTypeStructByVal */ gint32 type; /* Data type of argument */ } ArgInfo; /** * Call information - parameters and stack use for s390x ABI */ struct CallInfo { int nargs; int lastgr; guint32 stack_usage; guint32 struct_ret; ArgInfo ret; ArgInfo sigCookie; size_data sz; int vret_arg_index; MonoMethodSignature *sig; ArgInfo args [1]; }; /** * Registers used in parameter passing */ typedef struct { gint64 gr[5]; /* R2-R6 */ gdouble fp[3]; /* F0-F2 */ } __attribute__ ((__packed__)) RegParm; /*========================= End of Typedefs ========================*/ /*------------------------------------------------------------------*/ /* P r o t o t y p e s */ /*------------------------------------------------------------------*/ static guint8 * backUpStackPtr(MonoCompile *, guint8 *); static void add_general (guint *, size_data *, ArgInfo *); static void add_stackParm (guint *, size_data *, ArgInfo *, gint, ArgStorage); static void add_float (guint *, size_data *, ArgInfo *, gboolean); static CallInfo * get_call_info (MonoMemPool *, MonoMethodSignature *); static guchar * emit_float_to_int (MonoCompile *, guchar *, int, int, int, gboolean); static __inline__ void emit_unwind_regs(MonoCompile *, guint8 *, int, int, long); static void compare_and_branch(MonoBasicBlock *, MonoInst *, int, gboolean); static __inline__ guint8 * emit_call(MonoCompile *, guint8 *, MonoJumpInfoType, gconstpointer); static guint8 * emit_thunk(guint8 *, gconstpointer); static void create_thunk(MonoCompile *, guint8 *, guint8 *, gpointer); static void update_thunk(MonoCompile *, guint8 *, gpointer); static void emit_patch_full (MonoCompile *, MonoJumpInfo *, guint8 *, gpointer, int); /*========================= End of Prototypes ======================*/ /*------------------------------------------------------------------*/ /* G l o b a l V a r i a b l e s */ /*------------------------------------------------------------------*/ /** * The single-step trampoline */ static gpointer ss_trampoline; /** * The breakpoint trampoline */ static gpointer bp_trampoline; /** * Constants used in debugging - map general register names */ static const char * grNames[] = { "s390_r0", "s390_sp", "s390_r2", "s390_r3", "s390_r4", "s390_r5", "s390_r6", "s390_r7", "s390_r8", "s390_r9", "s390_r10", "s390_r11", "s390_r12", "s390_r13", "s390_r14", "s390_r15" }; /** * Constants used in debugging - map floating point register names */ static const char * fpNames[] = { "s390_f0", "s390_f1", "s390_f2", "s390_f3", "s390_f4", "s390_f5", "s390_f6", "s390_f7", "s390_f8", "s390_f9", "s390_f10", "s390_f11", "s390_f12", "s390_f13", "s390_f14", "s390_f15" }; /** * Constants used in debugging - map vector register names */ static const char * vrNames[] = { "vr0", "vr1", "vr2", "vr3", "vr4", "vr5", "vr6", "vr7", "vr8", "vr9", "vr10", "vr11", "vr12", "vr13", "vr14", "vr15", "vr16", "vr17", "vr18", "vr19", "vr20", "vr21", "vr22", "vr23", "vr24", "vr25", "vr26", "vr27", "vr28", "vr29", "vr30", "vr31" }; #if 0 /** * Constants used in debugging - ABI register types */ static const char *typeParm[] = { "General", "Base", "FPR8", "FPR4", "StructByVal", "StructByValInFP", "ByAddr"}; #endif /*====================== End of Global Variables ===================*/ static GENERATE_TRY_GET_CLASS_WITH_CACHE (math, "System", "Math") static GENERATE_TRY_GET_CLASS_WITH_CACHE (mathf, "System", "MathF") /** * * @brief Return general register name * * @param[in] register number * @returns Name of register * * Returns the name of the general register specified by the input parameter. */ const char* mono_arch_regname (int reg) { if (reg >= 0 && reg < 16) return grNames [reg]; else return "unknown"; } /*========================= End of Function ========================*/ /** * * @brief Return floating point register name * * @param[in] register number * @returns Name of register * * Returns the name of the FP register specified by the input parameter. */ const char* mono_arch_fregname (int reg) { if (reg >= 0 && reg < 16) return fpNames [reg]; else return "unknown"; } /*========================= End of Function ========================*/ /** * * @brief Return vector register name * * @param[in] register number * @returns Name of register * * Returns the name of the vector register specified by the input parameter. */ const char * mono_arch_xregname (int reg) { if (reg >= 0 && reg < s390_VR_NREG) return vrNames [reg]; else return "unknown"; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific return argument information * * @param[in] @csig - Method signature * @param[in] @param_count - Number of parameters to consider * @param[out] @arg_info - An array in which to store results * @returns Size of the activation frame * * Gathers information on parameters such as size, alignment, and padding. * arg_info should be large * enough to hold param_count + 1 entries. */ int mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info) { int k, frame_size = 0; int size, align, pad; int offset = 8; if (MONO_TYPE_ISSTRUCT (csig->ret)) { frame_size += sizeof (target_mgreg_t); offset += 8; } arg_info [0].offset = offset; if (csig->hasthis) { frame_size += sizeof (target_mgreg_t); offset += 8; } arg_info [0].size = frame_size; for (k = 0; k < param_count; k++) { if (csig->pinvoke && !csig->marshalling_disabled) size = mono_type_native_stack_size (csig->params [k], (guint32 *) &align); else size = mini_type_stack_size (csig->params [k], &align); frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); arg_info [k].pad = pad; frame_size += size; arg_info [k + 1].pad = 0; arg_info [k + 1].size = size; offset += pad; arg_info [k + 1].offset = offset; offset += size; } align = MONO_ARCH_FRAME_ALIGNMENT; frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1); arg_info [k].pad = pad; return frame_size; } /*========================= End of Function ========================*/ /** * * @brief Emit an s390x move operation * * @param[in] @cfg - MonoCompile control block * @param[in] @dr - Destination register * @param[in] @ins - Current instruction * @param[in] @src - Instruction representing the source of move * * Emit a move instruction for VT parameters */ static void __inline__ emit_new_move(MonoCompile *cfg, int dr, MonoInst *ins, MonoInst *src) { MonoCallInst *call = (MonoCallInst *) ins->inst_p0; ArgInfo *ainfo = (ArgInfo *) ins->inst_p1; MonoInst *vtcopy = mono_compile_create_var (cfg, m_class_get_byval_arg (src->klass), OP_LOCAL); MonoInst *load; MonoInst *move; int size; if (call->signature->pinvoke && !call->signature->marshalling_disabled) { size = mono_type_native_stack_size (m_class_get_byval_arg (src->klass), NULL); vtcopy->backend.is_pinvoke = 1; } else { size = ins->backend.size; } EMIT_NEW_VARLOADA (cfg, load, vtcopy, vtcopy->inst_vtype); MONO_INST_NEW (cfg, move, OP_S390_MOVE); move->sreg2 = load->dreg; move->inst_offset = 0; move->sreg1 = src->dreg; move->inst_imm = 0; move->backend.size = size; MONO_ADD_INS (cfg->cbb, move); if (dr != 0) MONO_EMIT_NEW_UNALU(cfg, OP_MOVE, dr, load->dreg); else MONO_EMIT_NEW_STORE_MEMBASE(cfg, OP_STORE_MEMBASE_REG, ainfo->reg, ainfo->offset, load->dreg); } /*========================= End of Function ========================*/ /** * * @brief Generate output sequence for VT register parameters * * @param[in] @cfg - MonoCompile control block * @param[in] @dr - Destination register * @param[in] @ins - Current instruction * @param[in] @src - Instruction representing the source * * Emit the output of structures for calls whose address is placed in a register. */ static void __inline__ emit_outarg_vtr(MonoCompile *cfg, MonoInst *ins, MonoInst *src) { MonoCallInst *call = (MonoCallInst *) ins->inst_p0; ArgInfo *ainfo = (ArgInfo *) ins->inst_p1; int reg = mono_alloc_preg (cfg); switch (ins->backend.size) { case 0: MONO_EMIT_NEW_ICONST(cfg, reg, 0); break; case 1: MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg, OP_LOADU1_MEMBASE, reg, src->dreg, 0); break; case 2: MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg, OP_LOADU2_MEMBASE, reg, src->dreg, 0); break; case 4: MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg, OP_LOADI4_MEMBASE, reg, src->dreg, 0); break; case 8: MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg, OP_LOADI8_MEMBASE, reg, src->dreg, 0); break; default: emit_new_move (cfg, reg, ins, src); } mono_call_inst_add_outarg_reg(cfg, call, reg, ainfo->reg, FALSE); } /*========================= End of Function ========================*/ /** * * @brief Generate output sequence for VT stack parameters * * @param[in] @cfg - MonoCompile control block * @param[in] @dr - Destination register * @param[in] @ins - Current instruction * @param[in] @src - Instruction representing the source * * Emit the output of structures for calls whose address is placed on the stack */ static void __inline__ emit_outarg_vts(MonoCompile *cfg, MonoInst *ins, MonoInst *src) { ArgInfo *ainfo = (ArgInfo *) ins->inst_p1; int tmpr = mono_alloc_preg (cfg); switch (ins->backend.size) { case 0: MONO_EMIT_NEW_ICONST(cfg, tmpr, 0); MONO_EMIT_NEW_STORE_MEMBASE(cfg, OP_STORE_MEMBASE_REG, ainfo->reg, ainfo->offset, tmpr); break; case 1: MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg, OP_LOADU1_MEMBASE, tmpr, src->dreg, 0); MONO_EMIT_NEW_STORE_MEMBASE(cfg, OP_STORE_MEMBASE_REG, ainfo->reg, ainfo->offset, tmpr); break; case 2: MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg, OP_LOADU2_MEMBASE, tmpr, src->dreg, 0); MONO_EMIT_NEW_STORE_MEMBASE(cfg, OP_STORE_MEMBASE_REG, ainfo->reg, ainfo->offset, tmpr); break; case 4: MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg, OP_LOADI4_MEMBASE, tmpr, src->dreg, 0); MONO_EMIT_NEW_STORE_MEMBASE(cfg, OP_STORE_MEMBASE_REG, ainfo->reg, ainfo->offset, tmpr); break; case 8: MONO_EMIT_NEW_LOAD_MEMBASE_OP(cfg, OP_LOADI8_MEMBASE, tmpr, src->dreg, 0); MONO_EMIT_NEW_STORE_MEMBASE(cfg, OP_STORE_MEMBASE_REG, ainfo->reg, ainfo->offset, tmpr); break; default: { emit_new_move (cfg, 0, ins, src); } } } /*========================= End of Function ========================*/ /** * * @brief Generate unwind information for range of registers * * @param[in] @cfg - MonoCompile control block * @param[in] @code - Location of code * @param[in] @start - Starting register * @param[in] @end - Ending register * @param[in] @offset - Offset in stack * * Emit unwind information for a range of registers. */ static void __inline__ emit_unwind_regs(MonoCompile *cfg, guint8 *code, int start, int end, long offset) { int i; for (i = start; i <= end; i++) { mono_emit_unwind_op_offset (cfg, code, i, offset); mini_gc_set_slot_type_from_cfa (cfg, offset, SLOT_NOREF); offset += sizeof(gulong); } } /*========================= End of Function ========================*/ /** * * @brief Get previous stack frame pointer * * @param[in] @cfg - MonoCompile control block * @param[in] @code - Location of code * @returns Previous stack pointer * * Retrieve the stack pointer of the previous frame */ static guint8 * backUpStackPtr(MonoCompile *cfg, guint8 *code) { int stackSize = cfg->stack_usage; if (cfg->flags & MONO_CFG_HAS_ALLOCA) { s390_lg (code, STK_BASE, 0, STK_BASE, 0); } else { if (cfg->frame_reg != STK_BASE) s390_lgr (code, STK_BASE, cfg->frame_reg); if (s390_is_imm16 (stackSize)) { s390_aghi (code, STK_BASE, stackSize); } else if (s390_is_imm32 (stackSize)) { s390_agfi (code, STK_BASE, stackSize); } else { while (stackSize > INT_MAX) { s390_aghi (code, STK_BASE, INT_MAX); stackSize -= INT_MAX; } s390_agfi (code, STK_BASE, stackSize); } } return (code); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific CPU initialization * * Perform CPU specific initialization to execute managed code. */ void mono_arch_cpu_init (void) { } /*========================= End of Function ========================*/ /** * * @brief Architecture specific initialization * * * Initialize architecture specific code: * - Define trigger pages for debugger * - Generate breakpoint code stub */ void mono_arch_init (void) { mono_set_partial_sharing_supported (FALSE); if (!mono_aot_only) bp_trampoline = mini_get_breakpoint_trampoline(); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific cleaup code * * * Clean up before termination: * - Free the trigger pages */ void mono_arch_cleanup (void) { } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific check for fast TLS access * * @returns True * * Returns whether we use fast inlined thread local storage managed access, * instead of falling back to native code. */ gboolean mono_arch_have_fast_tls (void) { return TRUE; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific check of mono optimizations * * @param[out] @exclude_mask - Optimization exclusion mask * @returns Optimizations supported on this CPU * * Returns the optimizations supported on this CPU */ guint32 mono_arch_cpu_optimizations (guint32 *exclude_mask) { guint32 opts = 0; /* * No s390-specific optimizations yet */ *exclude_mask = 0; return opts; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific allocation of integer variables * * @param[in] @cfg - MonoCompile control block * @returns A list of integer variables * * Returns a list of allocatable integer variables */ GList * mono_arch_get_allocatable_int_vars (MonoCompile *cfg) { GList *vars = NULL; int i; for (i = 0; i < cfg->num_varinfo; i++) { MonoInst *ins = cfg->varinfo [i]; MonoMethodVar *vmv = MONO_VARINFO (cfg, i); /* unused vars */ if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos) continue; if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG)) continue; /* we can only allocate 32 bit values */ if (mono_is_regsize_var(ins->inst_vtype)) { g_assert (MONO_VARINFO (cfg, i)->reg == -1); g_assert (i == vmv->idx); vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE); } } return vars; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific determination of usable integer registers * * @param[in] @cfg - MonoCompile control block * @returns A list of allocatable registers * * Returns a list of usable integer registers */ GList * mono_arch_get_global_int_regs (MonoCompile *cfg) { GList *regs = NULL; MonoMethodHeader *header; int i, top = 13; header = cfg->header; if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses) cfg->frame_reg = s390_r11; /* FIXME: s390_r12 is reserved for bkchain_reg. Only reserve it if needed */ top = 12; for (i = 8; i < top; ++i) { if ((cfg->frame_reg != i) && //!((cfg->uses_rgctx_reg) && (i == MONO_ARCH_IMT_REG))) (i != MONO_ARCH_IMT_REG)) regs = g_list_prepend (regs, GUINT_TO_POINTER (i)); } return regs; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific flush of instruction cache * * @param[in] @code - Start of code * @param[in] @size - Amount to be flushed * * Flush the CPU icache. */ void mono_arch_flush_icache (guint8 *code, gint size) { } /*========================= End of Function ========================*/ /** * * @brief Add an integer register parameter * * @param[in] @gr - Address of current register number * @param[in] @sz - Stack size data * @param[in] @ainfo - Parameter information * * Assign a parameter to a general register or spill it onto the stack */ static void inline add_general (guint *gr, size_data *sz, ArgInfo *ainfo) { if (*gr > S390_LAST_ARG_REG) { sz->stack_size = S390_ALIGN(sz->stack_size, sizeof(long)); ainfo->offset = sz->stack_size; ainfo->reg = STK_BASE; ainfo->regtype = RegTypeBase; sz->stack_size += sizeof(long); sz->code_size += 12; } else { ainfo->reg = *gr; ainfo->regtype = RegTypeGeneral; sz->code_size += 8; } (*gr) ++; } /*========================= End of Function ========================*/ /** * * @brief Add a structure variable to parameter list * * @param[in] @gr - Address of current register number * @param[in] @sz - Stack size data * @param[in] @ainfo - Parameter information * @param[in] @size - Size of parameter * @param[in] @type - Type of stack parameter (reference or value) * * Assign a structure address to a register or spill it onto the stack */ static void inline add_stackParm (guint *gr, size_data *sz, ArgInfo *ainfo, gint size, ArgStorage type) { if (*gr > S390_LAST_ARG_REG) { sz->stack_size = S390_ALIGN(sz->stack_size, sizeof(long)); ainfo->reg = STK_BASE; ainfo->offset = sz->stack_size; sz->stack_size += sizeof (target_mgreg_t); sz->parm_size += sizeof(gpointer); } else { ainfo->reg = *gr; } (*gr) ++; ainfo->regtype = type; ainfo->size = size; ainfo->vtsize = size; sz->parm_size += size; } /*========================= End of Function ========================*/ /** * * @brief Add a floating point register parameter * * @param[in] @fr - Address of current register number * @param[in] @sz - Stack size data * @param[in] @ainfo - Parameter information * @param[in] @isDouble - Precision of parameter * * Assign a parameter to a FP register or spill it onto the stack */ static void inline add_float (guint *fr, size_data *sz, ArgInfo *ainfo, gboolean isDouble) { if ((*fr) <= S390_LAST_FPARG_REG) { if (isDouble) ainfo->regtype = RegTypeFP; else ainfo->regtype = RegTypeFPR4; ainfo->reg = *fr; sz->code_size += 4; (*fr) += 2; } else { ainfo->offset = sz->stack_size; ainfo->reg = STK_BASE; sz->code_size += 4; sz->stack_size += sizeof(double); ainfo->regtype = RegTypeBase; } } /*========================= End of Function ========================*/ /** * * @brief Extract information about call parameters and stack use * * @param[in] @mp - Mono Memory Pool * @param[in] @sig - Mono Method Signature * @returns Information about the parameters and stack usage for a call * * Determine the amount of space required for code and stack. In addition * determine starting points for stack-based parameters, and area for * structures being returned on the stack. */ static CallInfo * get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) { guint i, fr, gr, size, pstart; int nParm = sig->hasthis + sig->param_count; MonoType *ret_type; guint32 simpleType, align; gboolean is_pinvoke = sig->pinvoke; CallInfo *cinfo; size_data *sz; if (mp) cinfo = (CallInfo *) mono_mempool_alloc0 (mp, sizeof (CallInfo) + sizeof (ArgInfo) * nParm); else cinfo = (CallInfo *) g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * nParm); fr = 0; gr = s390_r2; nParm = 0; cinfo->struct_ret = 0; cinfo->sig = sig; sz = &cinfo->sz; sz->retStruct = 0; sz->stack_size = S390_MINIMAL_STACK_SIZE; sz->code_size = 0; sz->parm_size = 0; align = 0; size = 0; /*----------------------------------------------------------*/ /* We determine the size of the return code/stack in case we*/ /* need to reserve a register to be used to address a stack */ /* area that the callee will use. */ /*----------------------------------------------------------*/ ret_type = mini_get_underlying_type (sig->ret); simpleType = ret_type->type; enum_retvalue: switch (simpleType) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_I4: case MONO_TYPE_U4: case MONO_TYPE_I: case MONO_TYPE_U: case MONO_TYPE_OBJECT: case MONO_TYPE_PTR: case MONO_TYPE_FNPTR: case MONO_TYPE_CLASS: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: case MONO_TYPE_STRING: cinfo->ret.reg = s390_r2; sz->code_size += 4; break; case MONO_TYPE_R4: case MONO_TYPE_R8: cinfo->ret.reg = s390_f0; sz->code_size += 4; break; case MONO_TYPE_I8: case MONO_TYPE_U8: cinfo->ret.reg = s390_r2; sz->code_size += 4; break; case MONO_TYPE_GENERICINST: if (!mono_type_generic_inst_is_valuetype (sig->ret)) { cinfo->ret.reg = s390_r2; sz->code_size += 4; break; } /* Fall through */ case MONO_TYPE_VALUETYPE: { MonoClass *klass = mono_class_from_mono_type_internal (sig->ret); if (m_class_is_enumtype (klass)) { simpleType = mono_class_enum_basetype_internal (klass)->type; goto enum_retvalue; } size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke && !sig->marshalling_disabled); cinfo->struct_ret = 1; cinfo->ret.size = size; cinfo->ret.vtsize = size; break; } case MONO_TYPE_TYPEDBYREF: { MonoClass *klass = mono_class_from_mono_type_internal (sig->ret); size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke && !sig->marshalling_disabled); cinfo->struct_ret = 1; cinfo->ret.size = size; cinfo->ret.vtsize = size; } break; case MONO_TYPE_VOID: break; default: g_error ("Can't handle as return value 0x%x", sig->ret->type); } pstart = 0; /* * To simplify get_this_arg_reg () and LLVM integration, emit the vret arg after * the first argument, allowing 'this' to be always passed in the first arg reg. * Also do this if the first argument is a reference type, since virtual calls * are sometimes made using calli without sig->hasthis set, like in the delegate * invoke wrappers. */ if (cinfo->struct_ret && !is_pinvoke && (sig->hasthis || (sig->param_count > 0 && MONO_TYPE_IS_REFERENCE (mini_get_underlying_type (sig->params [0]))))) { if (sig->hasthis) { cinfo->args[nParm].size = sizeof (target_mgreg_t); add_general (&gr, sz, cinfo->args + nParm); } else { cinfo->args[nParm].size = sizeof (target_mgreg_t); add_general (&gr, sz, &cinfo->args [sig->hasthis + nParm]); pstart = 1; } nParm ++; cinfo->vret_arg_index = 1; cinfo->ret.reg = gr; gr ++; } else { /* this */ if (sig->hasthis) { cinfo->args[nParm].size = sizeof (target_mgreg_t); add_general (&gr, sz, cinfo->args + nParm); nParm ++; } if (cinfo->struct_ret) { cinfo->ret.reg = gr; gr++; } } if ((sig->call_convention == MONO_CALL_VARARG) && (sig->param_count == 0)) { gr = S390_LAST_ARG_REG + 1; fr = S390_LAST_FPARG_REG + 1; /* Emit the signature cookie just before the implicit arguments */ add_general (&gr, sz, &cinfo->sigCookie); } /*----------------------------------------------------------*/ /* We determine the size of the parameter code and stack */ /* requirements by checking the types and sizes of the */ /* parameters. */ /*----------------------------------------------------------*/ for (i = pstart; i < sig->param_count; ++i) { MonoType *ptype; /*--------------------------------------------------*/ /* Handle vararg type calls. All args are put on */ /* the stack. */ /*--------------------------------------------------*/ if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) { gr = S390_LAST_ARG_REG + 1; fr = S390_LAST_FPARG_REG + 1; add_general (&gr, sz, &cinfo->sigCookie); } if (m_type_is_byref (sig->params [i])) { add_general (&gr, sz, cinfo->args+nParm); cinfo->args[nParm].size = sizeof(gpointer); nParm++; continue; } ptype = mini_get_underlying_type (sig->params [i]); simpleType = ptype->type; cinfo->args[nParm].type = simpleType; switch (simpleType) { case MONO_TYPE_I1: case MONO_TYPE_U1: cinfo->args[nParm].size = sizeof(char); add_general (&gr, sz, cinfo->args+nParm); nParm++; break; case MONO_TYPE_I2: case MONO_TYPE_U2: cinfo->args[nParm].size = sizeof(short); add_general (&gr, sz, cinfo->args+nParm); nParm++; break; case MONO_TYPE_I4: case MONO_TYPE_U4: cinfo->args[nParm].size = sizeof(int); add_general (&gr, sz, cinfo->args+nParm); nParm++; break; case MONO_TYPE_I: case MONO_TYPE_U: case MONO_TYPE_PTR: case MONO_TYPE_FNPTR: case MONO_TYPE_OBJECT: case MONO_TYPE_STRING: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: cinfo->args[nParm].size = sizeof(gpointer); add_general (&gr, sz, cinfo->args+nParm); nParm++; break; case MONO_TYPE_I8: case MONO_TYPE_U8: cinfo->args[nParm].size = sizeof(long long); add_general (&gr, sz, cinfo->args+nParm); nParm++; break; case MONO_TYPE_R4: cinfo->args[nParm].size = sizeof(float); add_float (&fr, sz, cinfo->args+nParm, FALSE); nParm++; break; case MONO_TYPE_R8: cinfo->args[nParm].size = sizeof(double); add_float (&fr, sz, cinfo->args+nParm, TRUE); nParm++; break; case MONO_TYPE_GENERICINST: if (!mono_type_generic_inst_is_valuetype (ptype)) { cinfo->args[nParm].size = sizeof(gpointer); add_general (&gr, sz, cinfo->args+nParm); nParm++; break; } /* Fall through */ case MONO_TYPE_VALUETYPE: { MonoMarshalType *info; MonoClass *klass = mono_class_from_mono_type_internal (ptype); if (sig->pinvoke && !sig->marshalling_disabled) size = mono_class_native_size(klass, NULL); else size = mono_class_value_size(klass, NULL); if (simpleType != MONO_TYPE_GENERICINST) { info = mono_marshal_load_type_info(klass); if ((info->native_size == sizeof(float)) && (info->num_fields == 1) && (info->fields[0].field->type->type == MONO_TYPE_R4)) { cinfo->args[nParm].size = sizeof(float); add_float(&fr, sz, cinfo->args+nParm, FALSE); nParm ++; break; } if ((info->native_size == sizeof(double)) && (info->num_fields == 1) && (info->fields[0].field->type->type == MONO_TYPE_R8)) { cinfo->args[nParm].size = sizeof(double); add_float(&fr, sz, cinfo->args+nParm, TRUE); nParm ++; break; } } cinfo->args[nParm].vtsize = 0; cinfo->args[nParm].size = 0; switch (size) { /*----------------------------------*/ /* On S/390, structures of size 1, */ /* 2, 4, and 8 bytes are passed in */ /* (a) register(s). */ /*----------------------------------*/ case 0: case 1: case 2: case 4: case 8: add_general(&gr, sz, cinfo->args+nParm); cinfo->args[nParm].size = size; cinfo->args[nParm].regtype = RegTypeStructByVal; nParm++; break; default: add_stackParm(&gr, sz, cinfo->args+nParm, size, RegTypeStructByVal); nParm++; } } break; case MONO_TYPE_TYPEDBYREF: { add_stackParm(&gr, sz, cinfo->args+nParm, sizeof(uintptr_t), RegTypeStructByAddr); nParm++; } break; default: g_error ("Can't trampoline 0x%x", ptype); } } /*----------------------------------------------------------*/ /* Handle the case where there are no implicit arguments */ /*----------------------------------------------------------*/ if ((sig->call_convention == MONO_CALL_VARARG) && (nParm > 0) && (!sig->pinvoke) && (sig->param_count == sig->sentinelpos)) { gr = S390_LAST_ARG_REG + 1; fr = S390_LAST_FPARG_REG + 1; add_general (&gr, sz, &cinfo->sigCookie); } /* * If we are passing a structure back then we make room at * the end of the parameters that may have been placed on * the stack */ if (cinfo->struct_ret) { cinfo->ret.offset = sz->stack_size; sz->stack_size += S390_ALIGN(cinfo->ret.size, align); } cinfo->lastgr = gr; sz->stack_size = sz->stack_size + sz->parm_size; sz->stack_size = S390_ALIGN(sz->stack_size, sizeof(long)); return (cinfo); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific allocation of variables * * @param[in] @cfg - Compile control block * * Set var information according to the calling convention for s390x. * */ void mono_arch_allocate_vars (MonoCompile *cfg) { MonoMethodSignature *sig; MonoMethodHeader *header; MonoInst *inst; CallInfo *cinfo; int iParm, iVar, offset, align, size, curinst; int frame_reg = STK_BASE; int sArg, eArg; header = cfg->header; cfg->flags |= MONO_CFG_HAS_SPILLUP; /*---------------------------------------------------------*/ /* We use the frame register also for any method that has */ /* filter clauses. This way, when the handlers are called, */ /* the code will reference local variables using the frame */ /* reg instead of the stack pointer: if we had to restore */ /* the stack pointer, we'd corrupt the method frames that */ /* are already on the stack (since filters get called */ /* before stack unwinding happens) when the filter code */ /* would call any method. */ /*---------------------------------------------------------*/ if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses) frame_reg = s390_r11; cfg->frame_reg = frame_reg; cfg->arch.bkchain_reg = -1; if (frame_reg != STK_BASE) cfg->used_int_regs |= (1LL << frame_reg); sig = mono_method_signature_internal (cfg->method); if (!cfg->arch.cinfo) cfg->arch.cinfo = get_call_info (cfg->mempool, sig); cinfo = cfg->arch.cinfo; /*--------------------------------------------------------------*/ /* local vars are at a positive offset from the stack pointer */ /* also note that if the function uses alloca, we use s390_r11 */ /* to point at the local variables. */ /* add parameter area size for called functions */ /*--------------------------------------------------------------*/ if (cfg->param_area == 0) offset = S390_MINIMAL_STACK_SIZE; else offset = cfg->param_area; cfg->sig_cookie = 0; if (MONO_TYPE_ISSTRUCT(sig->ret)) { cfg->ret->opcode = OP_REGVAR; cfg->ret->inst_c0 = cfg->ret->dreg = cinfo->ret.reg; } else { switch (mini_get_underlying_type (sig->ret)->type) { case MONO_TYPE_VOID: break; default: cfg->ret->opcode = OP_REGVAR; cfg->ret->inst_c0 = cfg->ret->dreg = cinfo->ret.reg; } } if (sig->hasthis) { inst = cfg->args [0]; if (inst->opcode != OP_REGVAR) { inst->opcode = OP_REGOFFSET; inst->inst_basereg = frame_reg; offset = S390_ALIGN(offset, sizeof(gpointer)); inst->inst_offset = offset; offset += sizeof (target_mgreg_t); } curinst = sArg = 1; } else { curinst = sArg = 0; } eArg = sig->param_count + sArg; if (sig->call_convention == MONO_CALL_VARARG) cfg->sig_cookie += S390_MINIMAL_STACK_SIZE; for (iParm = sArg; iParm < eArg; ++iParm) { inst = cfg->args [curinst]; if (inst->opcode != OP_REGVAR) { switch (cinfo->args[iParm].regtype) { case RegTypeStructByAddr : { MonoInst *indir; size = sizeof (target_mgreg_t); if (cinfo->args [iParm].reg == STK_BASE) { /* Similar to the == STK_BASE case below */ cfg->arch.bkchain_reg = s390_r12; cfg->used_int_regs |= 1 << cfg->arch.bkchain_reg; inst->opcode = OP_REGOFFSET; inst->dreg = mono_alloc_preg (cfg); inst->inst_basereg = cfg->arch.bkchain_reg; inst->inst_offset = cinfo->args [iParm].offset; } else { inst->opcode = OP_REGOFFSET; inst->dreg = cinfo->args [iParm].reg; inst->opcode = OP_REGOFFSET; inst->dreg = mono_alloc_preg (cfg); inst->inst_basereg = cfg->frame_reg; // inst->inst_offset = cinfo->args [iParm].offset; inst->inst_offset = offset; } /* Add a level of indirection */ MONO_INST_NEW (cfg, indir, 0); *indir = *inst; inst->opcode = OP_VTARG_ADDR; inst->inst_left = indir; } break; case RegTypeStructByVal : { MonoInst *indir; cfg->arch.bkchain_reg = s390_r12; cfg->used_int_regs |= 1 << cfg->arch.bkchain_reg; size = cinfo->args[iParm].size; if (cinfo->args [iParm].reg == STK_BASE) { int offStruct = 0; switch(size) { case 0: case 1: case 2: case 4: case 8: offStruct = (size < 8 ? sizeof(uintptr_t) - size : 0); default: inst->opcode = OP_REGOFFSET; inst->dreg = mono_alloc_preg (cfg); inst->inst_basereg = cfg->arch.bkchain_reg; inst->inst_offset = cinfo->args [iParm].offset + offStruct; } } else { offset = S390_ALIGN(offset, sizeof(uintptr_t)); inst->opcode = OP_REGOFFSET; inst->inst_basereg = cfg->frame_reg; inst->inst_offset = offset; } switch (size) { case 0 : case 1 : case 2 : case 4 : case 8 : break; default : /* Add a level of indirection */ MONO_INST_NEW (cfg, indir, 0); *indir = *inst; inst->opcode = OP_VTARG_ADDR; inst->inst_left = indir; } } break; default : if (cinfo->args [iParm].reg == STK_BASE) { /* * These arguments are in the previous frame, so we can't * compute their offset from the current frame pointer right * now, since cfg->stack_offset is not yet known, so dedicate a * register holding the previous frame pointer. */ cfg->arch.bkchain_reg = s390_r12; cfg->used_int_regs |= 1 << cfg->arch.bkchain_reg; inst->opcode = OP_REGOFFSET; inst->inst_basereg = cfg->arch.bkchain_reg; size = (cinfo->args[iParm].size < 8 ? 8 - cinfo->args[iParm].size : 0); inst->inst_offset = cinfo->args [iParm].offset + size; size = sizeof (long); } else { inst->opcode = OP_REGOFFSET; inst->inst_basereg = frame_reg; size = (cinfo->args[iParm].size < 8 ? sizeof(int) : sizeof(long)); offset = S390_ALIGN(offset, size); if (cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) inst->inst_offset = offset; else inst->inst_offset = offset + (8 - size); } } offset += 8; } curinst++; } cfg->locals_min_stack_offset = offset; curinst = cfg->locals_start; for (iVar = curinst; iVar < cfg->num_varinfo; ++iVar) { inst = cfg->varinfo [iVar]; if ((inst->flags & MONO_INST_IS_DEAD) || (inst->opcode == OP_REGVAR)) continue; /*--------------------------------------------------*/ /* inst->backend.is_pinvoke indicates native sized */ /* value types this is used by the pinvoke wrappers */ /* when they call functions returning structure */ /*--------------------------------------------------*/ if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype)) size = mono_class_native_size (mono_class_from_mono_type_internal (inst->inst_vtype), (guint32 *) &align); else size = mono_type_size (inst->inst_vtype, &align); offset = S390_ALIGN(offset, align); inst->inst_offset = offset; inst->opcode = OP_REGOFFSET; inst->inst_basereg = frame_reg; offset += size; DEBUG (g_print("allocating local %d to %ld, size: %d\n", iVar, inst->inst_offset, size)); } offset = S390_ALIGN(offset, sizeof(uintptr_t)); cfg->locals_max_stack_offset = offset; /*------------------------------------------------------*/ /* Reserve space to save LMF and caller saved registers */ /*------------------------------------------------------*/ if (cfg->method->save_lmf) offset += sizeof (MonoLMF); /*------------------------------------------------------*/ /* align the offset */ /*------------------------------------------------------*/ cfg->stack_offset = S390_ALIGN(offset, S390_STACK_ALIGNMENT); /*------------------------------------------------------*/ /* Fix offsets for args whose value is in parent frame */ /*------------------------------------------------------*/ for (iParm = sArg; iParm < eArg; ++iParm) { inst = cfg->args [iParm]; if (inst->opcode == OP_S390_STKARG) { inst->opcode = OP_REGOFFSET; inst->inst_offset += cfg->stack_offset; } } } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific creation of variables * * @param[in] @cfg - Compile control block * * Create variables for the method. * */ void mono_arch_create_vars (MonoCompile *cfg) { MonoMethodSignature *sig = mono_method_signature_internal (cfg->method); CallInfo *cinfo; if (!cfg->arch.cinfo) cfg->arch.cinfo = get_call_info (cfg->mempool, sig); cinfo = cfg->arch.cinfo; if (cinfo->struct_ret) { cfg->vret_addr = mono_compile_create_var (cfg, mono_get_int_type (), OP_ARG); if (G_UNLIKELY (cfg->verbose_level > 1)) { printf ("vret_addr = "); mono_print_ins (cfg->vret_addr); } } if (cfg->gen_sdb_seq_points) { MonoInst *ins; ins = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL); ins->flags |= MONO_INST_VOLATILE; cfg->arch.ss_tramp_var = ins; ins = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL); ins->flags |= MONO_INST_VOLATILE; cfg->arch.bp_tramp_var = ins; } } /*========================= End of Function ========================*/ /** * * @brief Add a register to the call operation * * @param[in] @cfg - Compile control block * @param[in] @call - Call Instruction * @param[in] @storage - Register use type * @param[in] @reg - Register number * @param[in] @tree - Call arguments * * Add register use information to the call sequence */ static void add_outarg_reg2 (MonoCompile *cfg, MonoCallInst *call, ArgStorage storage, int reg, MonoInst *tree) { MonoInst *ins; switch (storage) { case RegTypeGeneral: MONO_INST_NEW (cfg, ins, OP_MOVE); ins->dreg = mono_alloc_ireg (cfg); ins->sreg1 = tree->dreg; MONO_ADD_INS (cfg->cbb, ins); mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, FALSE); break; case RegTypeFP: MONO_INST_NEW (cfg, ins, OP_FMOVE); ins->dreg = mono_alloc_freg (cfg); ins->sreg1 = tree->dreg; MONO_ADD_INS (cfg->cbb, ins); mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, TRUE); break; case RegTypeFPR4: MONO_INST_NEW (cfg, ins, OP_S390_SETF4RET); ins->dreg = mono_alloc_freg (cfg); ins->sreg1 = tree->dreg; MONO_ADD_INS (cfg->cbb, ins); mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, TRUE); break; default: g_assert_not_reached (); } } /*========================= End of Function ========================*/ /** * * @brief Emit a signature cookine * * @param[in] @cfg - Compile control block * @param[in] @call - Call Instruction * @param[in] @cinfo - Call Information * * Emit the signature cooke as a parameter */ static void emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo) { MonoMethodSignature *tmpSig; MonoInst *sig_arg; cfg->disable_aot = TRUE; /* * mono_ArgIterator_Setup assumes the signature cookie is * passed first and all the arguments which were before it * passed on the stack after the signature. So compensate * by passing a different signature. */ tmpSig = mono_metadata_signature_dup (call->signature); tmpSig->param_count -= call->signature->sentinelpos; tmpSig->sentinelpos = 0; if (tmpSig->param_count > 0) memcpy (tmpSig->params, call->signature->params + call->signature->sentinelpos, tmpSig->param_count * sizeof(MonoType *)); MONO_INST_NEW (cfg, sig_arg, OP_ICONST); sig_arg->dreg = mono_alloc_ireg (cfg); sig_arg->inst_p0 = tmpSig; MONO_ADD_INS (cfg->cbb, sig_arg); MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, STK_BASE, cinfo->sigCookie.offset, sig_arg->dreg); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific emission of a call operation * * @param[in] @cfg - Compile control block * @param[in] @call - Call Instruction * * Process all parameters for a call and generate the sequence of * operations to perform the call according to the s390x ABI. */ void mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) { MonoInst *in; MonoMethodSignature *sig; MonoInst *ins; int i, n, lParamArea; CallInfo *cinfo; ArgInfo *ainfo = NULL; int stackSize; sig = call->signature; n = sig->param_count + sig->hasthis; DEBUG (g_print ("Call requires: %d parameters\n",n)); cinfo = get_call_info (cfg->mempool, sig); stackSize = cinfo->sz.stack_size + cinfo->sz.parm_size; call->stack_usage = MAX(stackSize, call->stack_usage); lParamArea = MAX((call->stack_usage-S390_MINIMAL_STACK_SIZE-cinfo->sz.parm_size), 0); cfg->param_area = MAX(((signed) cfg->param_area), lParamArea); /* FIXME */ cfg->flags |= MONO_CFG_HAS_CALLS; if (cinfo->struct_ret) { MONO_INST_NEW (cfg, ins, OP_MOVE); ins->sreg1 = call->vret_var->dreg; ins->dreg = mono_alloc_preg (cfg); MONO_ADD_INS (cfg->cbb, ins); mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, cinfo->ret.reg, FALSE); } for (i = 0; i < n; ++i) { MonoType *t; ainfo = cinfo->args + i; if (i >= sig->hasthis) t = sig->params [i - sig->hasthis]; else t = mono_get_int_type (); t = mini_get_underlying_type (t); in = call->args [i]; if ((sig->call_convention == MONO_CALL_VARARG) && (!sig->pinvoke) && (i == sig->sentinelpos)) { emit_sig_cookie (cfg, call, cinfo); } switch (ainfo->regtype) { case RegTypeGeneral : add_outarg_reg2 (cfg, call, ainfo->regtype, ainfo->reg, in); break; case RegTypeFP : case RegTypeFPR4 : if (MONO_TYPE_ISSTRUCT (t)) { /* Valuetype passed in one fp register */ ainfo->regtype = RegTypeStructByValInFP; /* Fall through */ } else { add_outarg_reg2 (cfg, call, ainfo->regtype, ainfo->reg, in); break; } case RegTypeStructByVal : case RegTypeStructByAddr : { g_assert (in->klass); MONO_INST_NEW (cfg, ins, OP_OUTARG_VT); ins->sreg1 = in->dreg; ins->klass = in->klass; ins->backend.size = ainfo->size; ins->inst_p0 = call; ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo)); memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo)); MONO_ADD_INS (cfg->cbb, ins); break; } case RegTypeBase : if (!m_type_is_byref (t) && t->type == MONO_TYPE_R4) { MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, STK_BASE, ainfo->offset + 4, in->dreg); } else if (!m_type_is_byref (t) && (t->type == MONO_TYPE_R8)) { MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, STK_BASE, ainfo->offset, in->dreg); } else { MONO_INST_NEW (cfg, ins, OP_STORE_MEMBASE_REG); ins->inst_destbasereg = STK_BASE; ins->inst_offset = ainfo->offset; ins->sreg1 = in->dreg; MONO_ADD_INS (cfg->cbb, ins); } break; default: g_assert_not_reached (); break; } } /* * Handle the case where there are no implicit arguments */ if ((sig->call_convention == MONO_CALL_VARARG) && (!sig->pinvoke) && (i == sig->sentinelpos)) { emit_sig_cookie (cfg, call, cinfo); } } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific Value Type parameter processing * * @param[in] @cfg - Compile control block * @param[in] @call - Call Instruction * @param[in] @src - Source parameter * * Process value type parameters for a call operation */ void mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) { MonoCallInst *call = (MonoCallInst*) ins->inst_p0; ArgInfo *ainfo = (ArgInfo *) ins->inst_p1; if (ainfo->regtype == RegTypeStructByVal) { if (ainfo->reg != STK_BASE) { emit_outarg_vtr (cfg, ins, src); } else { emit_outarg_vts (cfg, ins, src); } } else if (ainfo->regtype == RegTypeStructByValInFP) { int dreg = mono_alloc_freg (cfg); if (ainfo->size == 4) { MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADR4_MEMBASE, dreg, src->dreg, 0); MONO_EMIT_NEW_UNALU (cfg, OP_S390_SETF4RET, dreg, dreg); } else { g_assert (ainfo->size == 8); MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADR8_MEMBASE, dreg, src->dreg, 0); } mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg, TRUE); } else { ERROR_DECL (error); MonoMethodHeader *header; MonoInst *vtcopy = mono_compile_create_var (cfg, m_class_get_byval_arg (src->klass), OP_LOCAL); MonoInst *load; int ovf_size = ainfo->vtsize, srcReg; guint32 size; /* FIXME: alignment? */ if (call->signature->pinvoke && !call->signature->marshalling_disabled) { size = mono_type_native_stack_size (m_class_get_byval_arg (src->klass), NULL); vtcopy->backend.is_pinvoke = 1; } else { size = mini_type_stack_size (m_class_get_byval_arg (src->klass), NULL); } if (size > 0) g_assert (ovf_size > 0); header = mono_method_get_header_checked (cfg->method, error); mono_error_assert_ok (error); /* FIXME don't swallow the error */ if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses) srcReg = s390_r11; else srcReg = STK_BASE; EMIT_NEW_VARLOADA (cfg, load, vtcopy, vtcopy->inst_vtype); mini_emit_memcpy (cfg, load->dreg, 0, src->dreg, 0, size, TARGET_SIZEOF_VOID_P); if (ainfo->reg == STK_BASE) { MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, srcReg, ainfo->offset, load->dreg); if (cfg->compute_gc_maps) { MonoInst *def; EMIT_NEW_GC_PARAM_SLOT_LIVENESS_DEF (cfg, def, ainfo->offset, m_class_get_byval_arg (ins->klass)); } } else mono_call_inst_add_outarg_reg (cfg, call, load->dreg, ainfo->reg, FALSE); } } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific call value return processing * * @param[in] @cfg - Compile control block * @param[in] @method - Method * @param[in] @val - Instruction representing the result returned to method * * Create the sequence to unload the value returned from a call */ void mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val) { MonoType *ret = mini_get_underlying_type (mono_method_signature_internal (method)->ret); if (!m_type_is_byref (ret)) { if (ret->type == MONO_TYPE_R4) { MONO_EMIT_NEW_UNALU (cfg, OP_S390_SETF4RET, s390_f0, val->dreg); return; } else if (ret->type == MONO_TYPE_R8) { MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, s390_f0, val->dreg); return; } } MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg); } /*========================= End of Function ========================*/ /** * * @brief Replace compound compare/branch operations with single operation * * @param[in] @bb - Basic block * @param[in] @ins - Current instruction * @param[in] @cc - Condition code of branch * @param[in] @logical - Whether comparison is signed or logical * * Form a peephole pass at the code looking for simple optimizations * that will combine compare/branch instructions into a single operation. */ static void compare_and_branch(MonoBasicBlock *bb, MonoInst *ins, int cc, gboolean logical) { MonoInst *last; if (mono_hwcap_s390x_has_gie) { last = mono_inst_prev (ins, FILTER_IL_SEQ_POINT); ins->sreg1 = last->sreg1; ins->sreg2 = last->sreg2; ins->sreg3 = cc; switch(last->opcode) { case OP_ICOMPARE: if (logical) ins->opcode = OP_S390_CLRJ; else ins->opcode = OP_S390_CRJ; MONO_DELETE_INS(bb, last); break; case OP_COMPARE: case OP_LCOMPARE: if (logical) ins->opcode = OP_S390_CLGRJ; else ins->opcode = OP_S390_CGRJ; MONO_DELETE_INS(bb, last); break; case OP_ICOMPARE_IMM: ins->backend.data = (gpointer) last->inst_imm; if (logical) ins->opcode = OP_S390_CLIJ; else ins->opcode = OP_S390_CIJ; MONO_DELETE_INS(bb, last); break; case OP_COMPARE_IMM: case OP_LCOMPARE_IMM: ins->backend.data = (gpointer) last->inst_imm; if (logical) ins->opcode = OP_S390_CLGIJ; else ins->opcode = OP_S390_CGIJ; MONO_DELETE_INS(bb, last); break; } } } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific peephole pass 1 processing * * @param[in] @cfg - Compile control block * @param[in] @bb - Basic block * * Form a peephole pass at the code looking for compare and branch * optimizations. */ void mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb) { MonoInst *ins, *n; MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) { switch (ins->opcode) { case OP_IBEQ: case OP_LBEQ: compare_and_branch(bb, ins, S390_CC_EQ, FALSE); break; case OP_LBNE_UN: case OP_IBNE_UN: compare_and_branch(bb, ins, S390_CC_NE, TRUE); break; case OP_LBLT: case OP_IBLT: compare_and_branch(bb, ins, S390_CC_LT, FALSE); break; case OP_LBLT_UN: case OP_IBLT_UN: compare_and_branch(bb, ins, S390_CC_LT, TRUE); break; case OP_LBGT: case OP_IBGT: compare_and_branch(bb, ins, S390_CC_GT, FALSE); break; case OP_LBGT_UN: case OP_IBGT_UN: compare_and_branch(bb, ins, S390_CC_GT, TRUE); break; case OP_LBGE: case OP_IBGE: compare_and_branch(bb, ins, S390_CC_GE, FALSE); break; case OP_LBGE_UN: case OP_IBGE_UN: compare_and_branch(bb, ins, S390_CC_GE, TRUE); break; case OP_LBLE: case OP_IBLE: compare_and_branch(bb, ins, S390_CC_LE, FALSE); break; case OP_LBLE_UN: case OP_IBLE_UN: compare_and_branch(bb, ins, S390_CC_LE, TRUE); break; // default: // mono_peephole_ins (bb, ins); } } } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific peephole pass 2 processing * * @param[in] @cfg - Compile control block * @param[in] @bb - Basic block * * Form a peephole pass at the code looking for simple optimizations. */ void mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb) { MonoInst *ins, *n, *last_ins = NULL; MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) { switch (ins->opcode) { case OP_LOADU4_MEMBASE: case OP_LOADI4_MEMBASE: if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG) && ins->inst_basereg == last_ins->inst_destbasereg && ins->inst_offset == last_ins->inst_offset) { ins->opcode = (ins->opcode == OP_LOADI4_MEMBASE) ? OP_ICONV_TO_I4 : OP_ICONV_TO_U4; ins->sreg1 = last_ins->sreg1; } break; } mono_peephole_ins (bb, ins); } } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific lowering pass processing * * @param[in] @cfg - Compile control block * @param[in] @bb - Basic block * * Form a lowering pass at the code looking for simple optimizations. */ void mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) { MonoInst *ins, *next; MONO_BB_FOR_EACH_INS_SAFE (bb, next, ins) { switch (ins->opcode) { case OP_DIV_IMM: case OP_REM_IMM: case OP_IDIV_IMM: case OP_IREM_IMM: case OP_IDIV_UN_IMM: case OP_IREM_UN_IMM: case OP_LAND_IMM: case OP_LOR_IMM: case OP_LREM_IMM: case OP_LXOR_IMM: case OP_LOCALLOC_IMM: mono_decompose_op_imm (cfg, bb, ins); break; case OP_LADD_IMM: if (!s390_is_imm16 (ins->inst_imm)) /* This is created by the memcpy code which ignores is_inst_imm */ mono_decompose_op_imm (cfg, bb, ins); break; default: break; } } bb->max_vreg = cfg->next_vreg; } /*========================= End of Function ========================*/ /** * * @brief Emit float-to-int sequence * * @param[in] @cfg - Compile control block * @param[in] @code - Current instruction area * @param[in] @dreg - Destination general register * @param[in] @sreg - Source floating point register * @param[in] @size - Size of destination * @param[in] @is_signed - Destination is signed/unsigned * @returns Next instruction location * * Emit instructions to convert a single precision floating point value to an integer */ static guchar * emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed) { /* sreg is a float, dreg is an integer reg. */ if (is_signed) { s390_cgebr (code, dreg, 5, sreg); switch (size) { case 1: s390_ltgr (code, dreg, dreg); s390_jnl (code, 4); s390_oill (code, dreg, 0x80); s390_lghi (code, s390_r0, 0xff); s390_ngr (code, dreg, s390_r0); break; case 2: s390_ltgr (code, dreg, dreg); s390_jnl (code, 4); s390_oill (code, dreg, 0x8000); s390_llill(code, s390_r0, 0xffff); s390_ngr (code, dreg, s390_r0); break; } } else { short *o[1]; s390_lgdr (code, s390_r14, s390_f14); s390_lgdr (code, s390_r13, s390_f15); S390_SET (code, s390_r0, 0x4f000000u); s390_ldgr (code, s390_f14, s390_r0); s390_ler (code, s390_f15, sreg); s390_cebr (code, s390_f15, s390_f14); s390_jl (code, 0); CODEPTR (code, o[0]); S390_SET (code, s390_r0, 0x4f800000u); s390_ldgr (code, s390_f14, s390_r0); s390_sebr (code, s390_f15, s390_f14); s390_cfebr (code, dreg, 7, s390_f15); s390_j (code, 4); PTRSLOT (code, o[0]); s390_cfebr (code, dreg, 5, sreg); switch (size) { case 1: s390_lghi (code, s390_r0, 0xff); s390_ngr (code, dreg, s390_r0); break; case 2: s390_llill(code, s390_r0, 0xffff); s390_ngr (code, dreg, s390_r0); break; } s390_ldgr (code, s390_f14, s390_r14); s390_ldgr (code, s390_f15, s390_r13); } return code; } /*========================= End of Function ========================*/ /** * * @brief Emit double-to-int sequence * * @param[in] @cfg - Compile control block * @param[in] @code - Current instruction area * @param[in] @dreg - Destination general register * @param[in] @sreg - Source floating point register * @param[in] @size - Size of destination * @param[in] @is_signed - Destination is signed/unsigned * @returns Next instruction location * * Emit instructions to convert a single precision floating point value to an integer */ static guchar* emit_double_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed) { /* sreg is a float, dreg is an integer reg. */ if (is_signed) { s390_cgdbr (code, dreg, 5, sreg); switch (size) { case 1: s390_ltgr (code, dreg, dreg); s390_jnl (code, 4); s390_oill (code, dreg, 0x80); s390_lghi (code, s390_r0, 0xff); s390_ngr (code, dreg, s390_r0); break; case 2: s390_ltgr (code, dreg, dreg); s390_jnl (code, 4); s390_oill (code, dreg, 0x8000); s390_llill(code, s390_r0, 0xffff); s390_ngr (code, dreg, s390_r0); break; } } else { short *o[1]; s390_lgdr (code, s390_r14, s390_f14); s390_lgdr (code, s390_r13, s390_f15); S390_SET (code, s390_r0, 0x41e0000000000000llu); s390_ldgr (code, s390_f14, s390_r0); s390_ldr (code, s390_f15, sreg); s390_cdbr (code, s390_f15, s390_f14); s390_jl (code, 0); CODEPTR (code, o[0]); S390_SET (code, s390_r0, 0x41f0000000000000llu); s390_ldgr (code, s390_f14, s390_r0); s390_sdbr (code, s390_f15, s390_f14); s390_cfdbr (code, dreg, 7, s390_f15); s390_j (code, 4); PTRSLOT (code, o[0]); s390_cfdbr (code, dreg, 5, sreg); switch (size) { case 1: s390_lghi (code, s390_r0, 0xff); s390_ngr (code, dreg, s390_r0); break; case 2: s390_llill(code, s390_r0, 0xffff); s390_ngr (code, dreg, s390_r0); break; } s390_ldgr (code, s390_f14, s390_r14); s390_ldgr (code, s390_f15, s390_r13); } return code; } /*========================= End of Function ========================*/ /** * * @brief Check if branch is for unsigned comparison * * @param[in] @next - Next instruction * @returns True if the branch is for an unsigned comparison * * Determine if next instruction is a branch for an unsigned comparison */ static gboolean is_unsigned (MonoInst *next) { if ((next) && (((next->opcode >= OP_IBNE_UN) && (next->opcode <= OP_IBLT_UN)) || ((next->opcode >= OP_LBNE_UN) && (next->opcode <= OP_LBLT_UN)) || ((next->opcode >= OP_COND_EXC_NE_UN) && (next->opcode <= OP_COND_EXC_LT_UN)) || ((next->opcode >= OP_COND_EXC_INE_UN) && (next->opcode <= OP_COND_EXC_ILT_UN)) || ((next->opcode == OP_CLT_UN) || (next->opcode == OP_CGT_UN) || (next->opcode == OP_ICGE_UN) || (next->opcode == OP_ICLE_UN)) || ((next->opcode == OP_ICLT_UN) || (next->opcode == OP_ICGT_UN) || (next->opcode == OP_LCLT_UN) || (next->opcode == OP_LCGT_UN)))) return TRUE; else return FALSE; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific processing of a basic block * * @param[in] @cfg - Compile control block * @param[in] @bb - Basic block * * Process instructions within basic block emitting s390x instructions * based on the VM operation codes */ void mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) { MonoInst *ins; MonoCallInst *call; guint8 *code = cfg->native_code + cfg->code_len; int src2; /* we don't align basic blocks of loops on s390 */ if (cfg->verbose_level > 2) g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset); MONO_BB_FOR_EACH_INS (bb, ins) { const guint offset = code - cfg->native_code; set_code_cursor (cfg, code); int max_len = ins_get_size (ins->opcode); code = realloc_code (cfg, max_len); mono_debug_record_line_number (cfg, ins, offset); switch (ins->opcode) { case OP_STOREI1_MEMBASE_IMM: { s390_lghi (code, s390_r0, ins->inst_imm); S390_LONG (code, stcy, stc, s390_r0, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_STOREI2_MEMBASE_IMM: { s390_lghi (code, s390_r0, ins->inst_imm); S390_LONG (code, sthy, sth, s390_r0, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_STOREI4_MEMBASE_IMM: { s390_lgfi (code, s390_r0, ins->inst_imm); S390_LONG (code, sty, st, s390_r0, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_STORE_MEMBASE_IMM: case OP_STOREI8_MEMBASE_IMM: { S390_SET (code, s390_r0, ins->inst_imm); S390_LONG (code, stg, stg, s390_r0, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_STOREI1_MEMBASE_REG: { S390_LONG (code, stcy, stc, ins->sreg1, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_STOREI2_MEMBASE_REG: { S390_LONG (code, sthy, sth, ins->sreg1, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_STOREI4_MEMBASE_REG: { S390_LONG (code, sty, st, ins->sreg1, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_STORE_MEMBASE_REG: case OP_STOREI8_MEMBASE_REG: { S390_LONG (code, stg, stg, ins->sreg1, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_LOADU4_MEM: g_assert_not_reached (); break; case OP_LOAD_MEMBASE: case OP_LOADI8_MEMBASE: { S390_LONG (code, lg, lg, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_LOADI4_MEMBASE: { S390_LONG (code, lgf, lgf, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_LOADU4_MEMBASE: { S390_LONG (code, llgf, llgf, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_LOADU1_MEMBASE: { S390_LONG (code, llgc, llgc, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_LOADI1_MEMBASE: { S390_LONG (code, lgb, lgb, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_LOADU2_MEMBASE: { S390_LONG (code, llgh, llgh, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_LOADI2_MEMBASE: { S390_LONG (code, lgh, lgh, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_LCONV_TO_I1: { s390_lgbr (code, ins->dreg, ins->sreg1); } break; case OP_LCONV_TO_I2: { s390_lghr (code, ins->dreg, ins->sreg1); } break; case OP_LCONV_TO_U1: { s390_llgcr (code, ins->dreg, ins->sreg1); } break; case OP_LCONV_TO_U2: { s390_llghr (code, ins->dreg, ins->sreg1); } break; case OP_ICONV_TO_I1: { s390_lgbr (code, ins->dreg, ins->sreg1); } break; case OP_ICONV_TO_I2: { s390_lghr (code, ins->dreg, ins->sreg1); } break; case OP_ICONV_TO_U1: { s390_llgcr (code, ins->dreg, ins->sreg1); } break; case OP_ICONV_TO_U2: { s390_llghr (code, ins->dreg, ins->sreg1); } break; case OP_ICONV_TO_U4: { s390_llgfr (code, ins->dreg, ins->sreg1); } break; case OP_ICONV_TO_I4: { s390_lgfr (code, ins->dreg, ins->sreg1); } break; case OP_COMPARE: case OP_LCOMPARE: { if (is_unsigned (ins->next)) s390_clgr (code, ins->sreg1, ins->sreg2); else s390_cgr (code, ins->sreg1, ins->sreg2); } break; case OP_ICOMPARE: { if (is_unsigned (ins->next)) s390_clr (code, ins->sreg1, ins->sreg2); else s390_cr (code, ins->sreg1, ins->sreg2); } break; case OP_COMPARE_IMM: case OP_LCOMPARE_IMM: { gboolean branchUn = is_unsigned (ins->next); if ((ins->inst_imm == 0) && (!branchUn)) { s390_ltgr (code, ins->sreg1, ins->sreg1); } else { S390_SET (code, s390_r0, ins->inst_imm); if (branchUn) s390_clgr (code, ins->sreg1, s390_r0); else s390_cgr (code, ins->sreg1, s390_r0); } } break; case OP_ICOMPARE_IMM: { gboolean branchUn = is_unsigned (ins->next); if ((ins->inst_imm == 0) && (!branchUn)) { s390_ltr (code, ins->sreg1, ins->sreg1); } else { S390_SET (code, s390_r0, ins->inst_imm); if (branchUn) s390_clr (code, ins->sreg1, s390_r0); else s390_cr (code, ins->sreg1, s390_r0); } } break; case OP_BREAK: { code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_break)); } break; case OP_ADDCC: { if (mono_hwcap_s390x_has_mlt) { s390_agrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_COM; s390_agr (code, ins->dreg, src2); } } break; case OP_LADD: { if (mono_hwcap_s390x_has_mlt) { s390_agrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_COM; s390_agr (code, ins->dreg, src2); } } break; case OP_ADC: { CHECK_SRCDST_COM; s390_alcgr (code, ins->dreg, src2); } break; case OP_ADD_IMM: { if (mono_hwcap_s390x_has_mlt) { if (s390_is_imm16 (ins->inst_imm)) { s390_aghik(code, ins->dreg, ins->sreg1, ins->inst_imm); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_agrk (code, ins->dreg, ins->sreg1, s390_r0); } } else { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (ins->inst_imm)) { s390_aghi (code, ins->dreg, ins->inst_imm); } else if (s390_is_imm32 (ins->inst_imm)) { s390_agfi (code, ins->dreg, ins->inst_imm); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_agr (code, ins->dreg, s390_r0); } } } break; case OP_LADD_IMM: { if (mono_hwcap_s390x_has_mlt) { if (s390_is_imm16 (ins->inst_imm)) { s390_aghik(code, ins->dreg, ins->sreg1, ins->inst_imm); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_agrk (code, ins->dreg, ins->sreg1, s390_r0); } } else { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } if (s390_is_imm32 (ins->inst_imm)) { s390_agfi (code, ins->dreg, ins->inst_imm); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_agr (code, ins->dreg, s390_r0); } } } break; case OP_ADC_IMM: { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (ins->inst_imm)) { s390_lghi (code, s390_r0, ins->inst_imm); s390_alcgr (code, ins->dreg, s390_r0); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_alcgr (code, ins->dreg, s390_r0); } } break; case OP_IADD_OVF: case OP_S390_IADD_OVF: { CHECK_SRCDST_COM; s390_ar (code, ins->dreg, src2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); s390_lgfr (code, ins->dreg, ins->dreg); } break; case OP_IADD_OVF_UN: case OP_S390_IADD_OVF_UN: { CHECK_SRCDST_COM; s390_alr (code, ins->dreg, src2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_CY, "OverflowException"); s390_llgfr (code, ins->dreg, ins->dreg); } break; case OP_ADD_OVF_CARRY: { CHECK_SRCDST_COM; s390_lghi (code, s390_r0, 0); s390_lgr (code, s390_r1, s390_r0); s390_alcgr (code, s390_r0, s390_r1); s390_agr (code, ins->dreg, src2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); s390_agr (code, ins->dreg, s390_r0); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); } break; case OP_ADD_OVF_UN_CARRY: { CHECK_SRCDST_COM; s390_alcgr (code, ins->dreg, src2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_CY, "OverflowException"); } break; case OP_SUBCC: { if (mono_hwcap_s390x_has_mlt) { s390_sgrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_NCOM; s390_sgr (code, ins->dreg, src2); } } break; case OP_LSUB: { if (mono_hwcap_s390x_has_mlt) { s390_sgrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_NCOM; s390_sgr (code, ins->dreg, src2); } } break; case OP_SBB: { CHECK_SRCDST_NCOM; s390_slbgr(code, ins->dreg, src2); } break; case OP_SUB_IMM: { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (-ins->inst_imm)) { s390_aghi (code, ins->dreg, -ins->inst_imm); } else if (s390_is_imm32 (-ins->inst_imm)) { s390_slgfi (code, ins->dreg, ins->inst_imm); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_slgr (code, ins->dreg, s390_r0); } } break; case OP_LSUB_IMM: { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (-ins->inst_imm)) { s390_aghi (code, ins->dreg, -ins->inst_imm); } else if (s390_is_imm32 (-ins->inst_imm)) { s390_slgfi (code, ins->dreg, ins->inst_imm); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_slgr (code, ins->dreg, s390_r0); } } break; case OP_SBB_IMM: { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (-ins->inst_imm)) { s390_lghi (code, s390_r0, ins->inst_imm); s390_slbgr (code, ins->dreg, s390_r0); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_slbgr(code, ins->dreg, s390_r0); } } break; case OP_SUB_OVF_CARRY: { CHECK_SRCDST_NCOM; s390_lghi (code, s390_r0, 0); s390_lgr (code, s390_r1, s390_r0); s390_slbgr (code, s390_r0, s390_r1); s390_sgr (code, ins->dreg, src2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); s390_agr (code, ins->dreg, s390_r0); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); } break; case OP_SUB_OVF_UN_CARRY: { CHECK_SRCDST_NCOM; s390_slbgr (code, ins->dreg, src2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NC, "OverflowException"); } break; case OP_LAND: { if (mono_hwcap_s390x_has_mlt) { s390_ngrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { if (ins->sreg1 == ins->dreg) { s390_ngr (code, ins->dreg, ins->sreg2); } else { if (ins->sreg2 == ins->dreg) { s390_ngr (code, ins->dreg, ins->sreg1); } else { s390_lgr (code, ins->dreg, ins->sreg1); s390_ngr (code, ins->dreg, ins->sreg2); } } } } break; case OP_AND_IMM: { S390_SET_MASK (code, s390_r0, ins->inst_imm); if (mono_hwcap_s390x_has_mlt) { s390_ngrk (code, ins->dreg, ins->sreg1, s390_r0); } else { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } s390_ngr (code, ins->dreg, s390_r0); } } break; case OP_LDIV: { s390_lgr (code, s390_r1, ins->sreg1); s390_dsgr (code, s390_r0, ins->sreg2); s390_lgr (code, ins->dreg, s390_r1); } break; case OP_LDIV_UN: { s390_lgr (code, s390_r1, ins->sreg1); s390_lghi (code, s390_r0, 0); s390_dlgr (code, s390_r0, ins->sreg2); s390_lgr (code, ins->dreg, s390_r1); } break; case OP_LREM: { s390_lgr (code, s390_r1, ins->sreg1); s390_dsgr (code, s390_r0, ins->sreg2); s390_lgr (code, ins->dreg, s390_r0); break; } case OP_LREM_IMM: { if (s390_is_imm16 (ins->inst_imm)) { s390_lghi (code, s390_r13, ins->inst_imm); } else { s390_lgfi (code, s390_r13, ins->inst_imm); } s390_lgr (code, s390_r0, ins->sreg1); s390_dsgr (code, s390_r0, s390_r13); s390_lgfr (code, ins->dreg, s390_r0); } break; case OP_LREM_UN: { s390_lgr (code, s390_r1, ins->sreg1); s390_lghi (code, s390_r0, 0); s390_dlgr (code, s390_r0, ins->sreg2); s390_lgr (code, ins->dreg, s390_r0); } break; case OP_LOR: { if (mono_hwcap_s390x_has_mlt) { s390_ogrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { if (ins->sreg1 == ins->dreg) { s390_ogr (code, ins->dreg, ins->sreg2); } else { if (ins->sreg2 == ins->dreg) { s390_ogr (code, ins->dreg, ins->sreg1); } else { s390_lgr (code, ins->dreg, ins->sreg1); s390_ogr (code, ins->dreg, ins->sreg2); } } } } break; case OP_OR_IMM: { S390_SET_MASK(code, s390_r0, ins->inst_imm); if (mono_hwcap_s390x_has_mlt) { s390_ogrk (code, ins->dreg, ins->sreg1, s390_r0); } else { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } s390_ogr (code, ins->dreg, s390_r0); } } break; case OP_LXOR: { if (mono_hwcap_s390x_has_mlt) { s390_xgrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { if (ins->sreg1 == ins->dreg) { s390_xgr (code, ins->dreg, ins->sreg2); } else { if (ins->sreg2 == ins->dreg) { s390_xgr (code, ins->dreg, ins->sreg1); } else { s390_lgr (code, ins->dreg, ins->sreg1); s390_xgr (code, ins->dreg, ins->sreg2); } } } } break; case OP_XOR_IMM: { S390_SET_MASK(code, s390_r0, ins->inst_imm); if (mono_hwcap_s390x_has_mlt) { s390_xgrk (code, ins->dreg, ins->sreg1, s390_r0); } else { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } s390_xgr (code, ins->dreg, s390_r0); } } break; case OP_LSHL: { CHECK_SRCDST_NCOM; s390_sllg (code, ins->dreg, ins->dreg, src2, 0); } break; case OP_SHL_IMM: case OP_LSHL_IMM: { if (ins->sreg1 != ins->dreg) { s390_lgr (code, ins->dreg, ins->sreg1); } s390_sllg (code, ins->dreg, ins->dreg, 0, (ins->inst_imm & 0x3f)); } break; case OP_LSHR: { CHECK_SRCDST_NCOM; s390_srag (code, ins->dreg, ins->dreg, src2, 0); } break; case OP_SHR_IMM: case OP_LSHR_IMM: { if (ins->sreg1 != ins->dreg) { s390_lgr (code, ins->dreg, ins->sreg1); } s390_srag (code, ins->dreg, ins->dreg, 0, (ins->inst_imm & 0x3f)); } break; case OP_SHR_UN_IMM: case OP_LSHR_UN_IMM: { if (ins->sreg1 != ins->dreg) { s390_lgr (code, ins->dreg, ins->sreg1); } s390_srlg (code, ins->dreg, ins->dreg, 0, (ins->inst_imm & 0x3f)); } break; case OP_LSHR_UN: { CHECK_SRCDST_NCOM; s390_srlg (code, ins->dreg, ins->dreg, src2, 0); } break; case OP_LNOT: { if (ins->sreg1 != ins->dreg) { s390_lgr (code, ins->dreg, ins->sreg1); } s390_lghi (code, s390_r0, -1); s390_xgr (code, ins->dreg, s390_r0); } break; case OP_LNEG: { s390_lcgr (code, ins->dreg, ins->sreg1); } break; case OP_LMUL: { CHECK_SRCDST_COM; s390_msgr (code, ins->dreg, src2); } break; case OP_MUL_IMM: case OP_LMUL_IMM: { if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } if ((mono_hwcap_s390x_has_gie) && (s390_is_imm32 (ins->inst_imm))) { s390_msgfi (code, ins->dreg, ins->inst_imm); } else { if (s390_is_imm16 (ins->inst_imm)) { s390_lghi (code, s390_r13, ins->inst_imm); } else if (s390_is_imm32 (ins->inst_imm)) { s390_lgfi (code, s390_r13, ins->inst_imm); } else { S390_SET (code, s390_r13, ins->inst_imm); } s390_msgr (code, ins->dreg, s390_r13); } } break; case OP_LMUL_OVF: { short int *o[2]; if (mono_hwcap_s390x_has_mie2) { s390_msgrkc (code, ins->dreg, ins->sreg1, ins->sreg2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); } else { s390_ltgr (code, s390_r1, ins->sreg1); s390_jz (code, 0); CODEPTR(code, o[0]); s390_ltgr (code, s390_r0, ins->sreg2); s390_jnz (code, 6); s390_lghi (code, s390_r1, 0); s390_j (code, 0); CODEPTR(code, o[1]); s390_xgr (code, s390_r0, s390_r1); s390_msgr (code, s390_r1, ins->sreg2); s390_xgr (code, s390_r0, s390_r1); s390_srlg (code, s390_r0, s390_r0, 0, 63); s390_ltgr (code, s390_r0, s390_r0); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NZ, "OverflowException"); PTRSLOT (code, o[0]); PTRSLOT (code, o[1]); s390_lgr (code, ins->dreg, s390_r1); } } break; case OP_LMUL_OVF_UN: { s390_lghi (code, s390_r0, 0); s390_lgr (code, s390_r1, ins->sreg1); s390_mlgr (code, s390_r0, ins->sreg2); s390_ltgr (code, s390_r0, s390_r0); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NZ, "OverflowException"); s390_lgr (code, ins->dreg, s390_r1); } break; case OP_IADDCC: { g_assert_not_reached (); CHECK_SRCDST_COM_I; s390_algr (code, ins->dreg, src2); } break; case OP_IADD: { CHECK_SRCDST_COM_I; s390_agr (code, ins->dreg, src2); } break; case OP_IADC: { g_assert_not_reached (); CHECK_SRCDST_COM_I; s390_alcgr (code, ins->dreg, src2); } break; case OP_IADD_IMM: { if (ins->dreg != ins->sreg1) { s390_lgfr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (ins->inst_imm)) { s390_aghi (code, ins->dreg, ins->inst_imm); } else { s390_afi (code, ins->dreg, ins->inst_imm); } } break; case OP_IADC_IMM: { if (ins->dreg != ins->sreg1) { s390_lgfr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (ins->inst_imm)) { s390_lghi (code, s390_r0, ins->inst_imm); s390_alcgr (code, ins->dreg, s390_r0); } else { S390_SET (code, s390_r0, ins->inst_imm); s390_alcgr (code, ins->dreg, s390_r0); } } break; case OP_LADD_OVF: case OP_S390_LADD_OVF: { if (mono_hwcap_s390x_has_mlt) { s390_agrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_COM; s390_agr (code, ins->dreg, src2); } EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); } break; case OP_LADD_OVF_UN: case OP_S390_LADD_OVF_UN: { if (mono_hwcap_s390x_has_mlt) { s390_algrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_COM; s390_algr (code, ins->dreg, src2); } EMIT_COND_SYSTEM_EXCEPTION (S390_CC_CY, "OverflowException"); } break; case OP_ISUBCC: { if (mono_hwcap_s390x_has_mlt) { s390_slgrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_NCOM_I; s390_slgr (code, ins->dreg, src2); } } break; case OP_ISUB: { if (mono_hwcap_s390x_has_mlt) { s390_sgrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_NCOM_I; s390_sgr (code, ins->dreg, src2); } } break; case OP_ISBB: { CHECK_SRCDST_NCOM_I; s390_slbgr (code, ins->dreg, src2); } break; case OP_ISUB_IMM: { if (ins->dreg != ins->sreg1) { s390_lgfr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (-ins->inst_imm)) { s390_aghi (code, ins->dreg, -ins->inst_imm); } else { s390_agfi (code, ins->dreg, -ins->inst_imm); } } break; case OP_ISBB_IMM: { S390_SET (code, s390_r0, ins->inst_imm); s390_slgfr (code, ins->dreg, s390_r0); } break; case OP_ISUB_OVF: case OP_S390_ISUB_OVF: { if (mono_hwcap_s390x_has_mlt) { s390_srk (code, ins->dreg, ins->sreg1, ins->sreg2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); } else { CHECK_SRCDST_NCOM; s390_sr (code, ins->dreg, src2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); s390_lgfr (code, ins->dreg, ins->dreg); } } break; case OP_ISUB_OVF_UN: case OP_S390_ISUB_OVF_UN: { if (mono_hwcap_s390x_has_mlt) { s390_slrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_NCOM; s390_slr (code, ins->dreg, src2); } EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NC, "OverflowException"); s390_llgfr(code, ins->dreg, ins->dreg); } break; case OP_LSUB_OVF: case OP_S390_LSUB_OVF: { if (mono_hwcap_s390x_has_mlt) { s390_sgrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_NCOM; s390_sgr (code, ins->dreg, src2); } EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); } break; case OP_LSUB_OVF_UN: case OP_S390_LSUB_OVF_UN: { CHECK_SRCDST_NCOM; s390_slgr (code, ins->dreg, src2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NC, "OverflowException"); } break; case OP_IAND: { if (mono_hwcap_s390x_has_mlt) { s390_ngrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_NCOM_I; s390_ngr (code, ins->dreg, src2); } } break; case OP_IAND_IMM: { S390_SET_MASK (code, s390_r0, ins->inst_imm); if (mono_hwcap_s390x_has_mlt) { s390_ngrk (code, ins->dreg, ins->sreg1, s390_r0); } else { if (ins->dreg != ins->sreg1) { s390_lgfr (code, ins->dreg, ins->sreg1); } s390_ngr (code, ins->dreg, s390_r0); } } break; case OP_IDIV: { s390_lgfr (code, s390_r0, ins->sreg1); s390_srda (code, s390_r0, 0, 32); s390_dr (code, s390_r0, ins->sreg2); s390_lgfr (code, ins->dreg, s390_r1); } break; case OP_IDIV_UN: { s390_lgfr (code, s390_r0, ins->sreg1); s390_srdl (code, s390_r0, 0, 32); s390_dlr (code, s390_r0, ins->sreg2); s390_lgfr (code, ins->dreg, s390_r1); } break; case OP_IDIV_IMM: { if (s390_is_imm16 (ins->inst_imm)) { s390_lghi (code, s390_r13, ins->inst_imm); } else { s390_lgfi (code, s390_r13, ins->inst_imm); } s390_lgfr (code, s390_r0, ins->sreg1); s390_srda (code, s390_r0, 0, 32); s390_dr (code, s390_r0, ins->sreg2); s390_lgfr (code, ins->dreg, s390_r1); } break; case OP_IREM: { s390_lgfr (code, s390_r0, ins->sreg1); s390_srda (code, s390_r0, 0, 32); s390_dr (code, s390_r0, ins->sreg2); s390_lgfr (code, ins->dreg, s390_r0); break; case OP_IREM_UN: s390_lgfr (code, s390_r0, ins->sreg1); s390_srdl (code, s390_r0, 0, 32); s390_dlr (code, s390_r0, ins->sreg2); s390_lgfr (code, ins->dreg, s390_r0); } break; case OP_IREM_IMM: { if (s390_is_imm16 (ins->inst_imm)) { s390_lghi (code, s390_r13, ins->inst_imm); } else { s390_lgfi (code, s390_r13, ins->inst_imm); } s390_lgfr (code, s390_r0, ins->sreg1); s390_srda (code, s390_r0, 0, 32); s390_dr (code, s390_r0, ins->sreg2); s390_lgfr (code, ins->dreg, s390_r0); } break; case OP_IOR: { if (mono_hwcap_s390x_has_mlt) { s390_ogrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_COM_I; s390_ogr (code, ins->dreg, src2); } } break; case OP_IOR_IMM: { S390_SET_MASK (code, s390_r0, ins->inst_imm); if (mono_hwcap_s390x_has_mlt) { s390_ogrk (code, ins->dreg, ins->sreg1, s390_r0); } else { if (ins->dreg != ins->sreg1) { s390_lgfr (code, ins->dreg, ins->sreg1); } s390_ogr (code, ins->dreg, s390_r0); } } break; case OP_IXOR: { if (mono_hwcap_s390x_has_mlt) { s390_xgrk (code, ins->dreg, ins->sreg1, ins->sreg2); } else { CHECK_SRCDST_COM_I; s390_xgr (code, ins->dreg, src2); } } break; case OP_IXOR_IMM: { S390_SET_MASK (code, s390_r0, ins->inst_imm); if (mono_hwcap_s390x_has_mlt) { s390_xgrk (code, ins->dreg, ins->sreg1, s390_r0); } else { if (ins->dreg != ins->sreg1) { s390_lgfr (code, ins->dreg, ins->sreg1); } s390_xgr (code, ins->dreg, s390_r0); } } break; case OP_ISHL: { CHECK_SRCDST_NCOM; s390_sll (code, ins->dreg, src2, 0); } break; case OP_ISHL_IMM: { if (ins->sreg1 != ins->dreg) { s390_lgfr (code, ins->dreg, ins->sreg1); } s390_sll (code, ins->dreg, 0, (ins->inst_imm & 0x1f)); } break; case OP_ISHR: { CHECK_SRCDST_NCOM; s390_sra (code, ins->dreg, src2, 0); } break; case OP_ISHR_IMM: { if (ins->sreg1 != ins->dreg) { s390_lgfr (code, ins->dreg, ins->sreg1); } s390_sra (code, ins->dreg, 0, (ins->inst_imm & 0x1f)); } break; case OP_ISHR_UN_IMM: { if (ins->sreg1 != ins->dreg) { s390_lgfr (code, ins->dreg, ins->sreg1); } s390_srl (code, ins->dreg, 0, (ins->inst_imm & 0x1f)); } break; case OP_ISHR_UN: { CHECK_SRCDST_NCOM; s390_srl (code, ins->dreg, src2, 0); } break; case OP_INOT: { if (ins->sreg1 != ins->dreg) { s390_lgfr (code, ins->dreg, ins->sreg1); } s390_lghi (code, s390_r0, -1); s390_xgr (code, ins->dreg, s390_r0); } break; case OP_INEG: { s390_lcgr (code, ins->dreg, ins->sreg1); } break; case OP_IMUL: { CHECK_SRCDST_COM_I; s390_msr (code, ins->dreg, src2); } break; case OP_IMUL_IMM: { if (ins->dreg != ins->sreg1) { s390_lgfr (code, ins->dreg, ins->sreg1); } if (s390_is_imm16 (ins->inst_imm)) { s390_lghi (code, s390_r0, ins->inst_imm); } else { s390_lgfi (code, s390_r0, ins->inst_imm); } s390_msr (code, ins->dreg, s390_r0); } break; case OP_IMUL_OVF: { short int *o[2]; if (mono_hwcap_s390x_has_mie2) { s390_msrkc (code, ins->dreg, ins->sreg1, ins->sreg2); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException"); s390_lgfr (code, ins->dreg, ins->dreg); } else { s390_ltr (code, s390_r1, ins->sreg1); s390_jz (code, 0); CODEPTR(code, o[0]); s390_ltr (code, s390_r0, ins->sreg2); s390_jnz (code, 6); s390_lhi (code, s390_r1, 0); s390_j (code, 0); CODEPTR(code, o[1]); s390_xr (code, s390_r0, s390_r1); s390_msr (code, s390_r1, ins->sreg2); s390_xr (code, s390_r0, s390_r1); s390_srl (code, s390_r0, 0, 31); s390_ltr (code, s390_r0, s390_r0); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NZ, "OverflowException"); PTRSLOT (code, o[0]); PTRSLOT (code, o[1]); s390_lgfr (code, ins->dreg, s390_r1); } } break; case OP_IMUL_OVF_UN: { s390_lhi (code, s390_r0, 0); s390_lr (code, s390_r1, ins->sreg1); s390_mlr (code, s390_r0, ins->sreg2); s390_ltr (code, s390_r0, s390_r0); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NZ, "OverflowException"); s390_lgfr (code, ins->dreg, s390_r1); } break; case OP_ICONST: case OP_I8CONST: { S390_SET (code, ins->dreg, ins->inst_c0); } break; case OP_AOTCONST: { mono_add_patch_info (cfg, code - cfg->native_code, (MonoJumpInfoType)(gsize)ins->inst_i1, ins->inst_p0); S390_LOAD_TEMPLATE (code, ins->dreg); } break; case OP_JUMP_TABLE: { mono_add_patch_info (cfg, code - cfg->native_code, (MonoJumpInfoType)(gsize)ins->inst_i1, ins->inst_p0); S390_LOAD_TEMPLATE (code, ins->dreg); } break; case OP_MOVE: if (ins->dreg != ins->sreg1) { s390_lgr (code, ins->dreg, ins->sreg1); } break; case OP_LCONV_TO_I: case OP_LCONV_TO_I8: case OP_SEXT_I4: s390_lgfr (code, ins->dreg, ins->sreg1); break; case OP_LCONV_TO_I4: s390_lgfr (code, ins->dreg, ins->sreg1); break; case OP_LCONV_TO_U: case OP_LCONV_TO_U8: case OP_LCONV_TO_U4: case OP_ZEXT_I4: s390_llgfr (code, ins->dreg, ins->sreg1); break; case OP_LCONV_TO_OVF_U4: S390_SET (code, s390_r0, 4294967295); s390_clgr (code, ins->sreg1, s390_r0); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_GT, "OverflowException"); s390_ltgr (code, ins->sreg1, ins->sreg1); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_LT, "OverflowException"); s390_llgfr(code, ins->dreg, ins->sreg1); break; case OP_LCONV_TO_OVF_I4_UN: S390_SET (code, s390_r0, 2147483647); s390_cgr (code, ins->sreg1, s390_r0); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_GT, "OverflowException"); s390_ltgr (code, ins->sreg1, ins->sreg1); EMIT_COND_SYSTEM_EXCEPTION (S390_CC_LT, "OverflowException"); s390_lgfr (code, ins->dreg, ins->sreg1); break; case OP_RCONV_TO_R4: if (ins->dreg != ins->sreg1) s390_ler (code, ins->dreg, ins->sreg1); break; case OP_RCONV_TO_R8: s390_ldebr (code, ins->dreg, ins->sreg1); break; case OP_FMOVE: if (ins->dreg != ins->sreg1) s390_ldr (code, ins->dreg, ins->sreg1); break; case OP_RMOVE: if (ins->dreg != ins->sreg1) s390_ldr (code, ins->dreg, ins->sreg1); break; case OP_MOVE_F_TO_I8: s390_lgdr (code, ins->dreg, ins->sreg1); break; case OP_MOVE_I8_TO_F: s390_ldgr (code, ins->dreg, ins->sreg1); break; case OP_MOVE_F_TO_I4: s390_lgdr (code, ins->dreg, ins->sreg1); s390_srag (code, ins->dreg, ins->dreg, 0, 32); break; case OP_MOVE_I4_TO_F: s390_slag (code, s390_r0, ins->sreg1, 0, 32); s390_ldgr (code, ins->dreg, s390_r0); break; case OP_FCONV_TO_R4: s390_ledbr (code, ins->dreg, ins->sreg1); break; case OP_S390_SETF4RET: s390_ldr (code, ins->dreg, ins->sreg1); break; case OP_TLS_GET: { if (s390_is_imm16 (ins->inst_offset)) { s390_lghi (code, s390_r13, ins->inst_offset); } else if (s390_is_imm32 (ins->inst_offset)) { s390_lgfi (code, s390_r13, ins->inst_offset); } else { S390_SET (code, s390_r13, ins->inst_offset); } s390_ear (code, s390_r1, 0); s390_sllg(code, s390_r1, s390_r1, 0, 32); s390_ear (code, s390_r1, 1); s390_lg (code, ins->dreg, s390_r13, s390_r1, 0); } break; case OP_TLS_SET: { if (s390_is_imm16 (ins->inst_offset)) { s390_lghi (code, s390_r13, ins->inst_offset); } else if (s390_is_imm32 (ins->inst_offset)) { s390_lgfi (code, s390_r13, ins->inst_offset); } else { S390_SET (code, s390_r13, ins->inst_offset); } s390_ear (code, s390_r1, 0); s390_sllg(code, s390_r1, s390_r1, 0, 32); s390_ear (code, s390_r1, 1); s390_stg (code, ins->sreg1, s390_r13, s390_r1, 0); } break; case OP_TAILCALL_PARAMETER : // This opcode helps compute sizes, i.e. // of the subsequent OP_TAILCALL, but contributes no code. g_assert (ins->next); break; case OP_TAILCALL : case OP_TAILCALL_REG : case OP_TAILCALL_MEMBASE : { call = (MonoCallInst *) ins; /* * Restore SP to caller's SP */ code = backUpStackPtr(cfg, code); /* * If the destination is specified as a register or membase then * save destination so it doesn't get overwritten by the restores */ if (ins->opcode != OP_TAILCALL) s390_lgr (code, s390_r1, ins->sreg1); /* * We have to restore R6, so it cannot be used as argument register. * This is ensured by mono_arch_tailcall_supported, but verify here. */ g_assert (!(call->used_iregs & (1 << S390_LAST_ARG_REG))); /* * Likewise for the IMT/RGCTX register */ g_assert (!(call->used_iregs & (1 << MONO_ARCH_RGCTX_REG))); g_assert (!(call->rgctx_reg)); /* * Restore all general registers */ s390_lmg (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET); /* * Restore any FP registers that have been altered */ if (cfg->arch.fpSize != 0) { int fpOffset = -cfg->arch.fpSize; for (int i = 8; i < 16; i++) { if (cfg->arch.used_fp_regs & (1 << i)) { s390_ldy (code, i, 0, STK_BASE, fpOffset); fpOffset += sizeof(double); } } } if (ins->opcode == OP_TAILCALL_REG) { s390_br (code, s390_r1); } else { if (ins->opcode == OP_TAILCALL_MEMBASE) { if (mono_hwcap_s390x_has_mie2) { s390_bi (code, 0, s390_r1, ins->inst_offset); } else { s390_lg (code, s390_r1, 0, s390_r1, ins->inst_offset); s390_br (code, s390_r1); } } else { mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, call->method, MONO_R_S390_THUNKED); S390_BR_TEMPLATE (code, s390_r1); cfg->thunk_area += THUNK_SIZE; } } } break; case OP_CHECK_THIS: { /* ensure ins->sreg1 is not NULL */ s390_llgc (code, s390_r0, 0, ins->sreg1, 0); } break; case OP_ARGLIST: { const int offset = cfg->sig_cookie + cfg->stack_usage; S390_SET (code, s390_r0, offset); s390_agr (code, s390_r0, cfg->frame_reg); s390_stg (code, s390_r0, 0, ins->sreg1, 0); } break; case OP_FCALL: { call = (MonoCallInst *) ins; const MonoJumpInfoTarget patch = mono_call_to_patch (call); code = emit_call (cfg, code, patch.type, patch.target); } break; case OP_RCALL: { call = (MonoCallInst *) ins; const MonoJumpInfoTarget patch = mono_call_to_patch (call); code = emit_call (cfg, code, patch.type, patch.target); if (ins->dreg != s390_f0) s390_ldr (code, ins->dreg, s390_f0); break; } case OP_LCALL: case OP_VCALL: case OP_VCALL2: case OP_VOIDCALL: case OP_CALL: { call = (MonoCallInst *) ins; const MonoJumpInfoTarget patch = mono_call_to_patch (call); code = emit_call (cfg, code, patch.type, patch.target); } break; case OP_FCALL_REG: call = (MonoCallInst*)ins; s390_lgr (code, s390_r1, ins->sreg1); s390_basr (code, s390_r14, s390_r1); break; case OP_RCALL_REG: call = (MonoCallInst*)ins; s390_lgr (code, s390_r1, ins->sreg1); s390_basr (code, s390_r14, s390_r1); if (ins->dreg != s390_f0) s390_ldr (code, ins->dreg, s390_f0); break; case OP_LCALL_REG: case OP_VCALL_REG: case OP_VCALL2_REG: case OP_VOIDCALL_REG: case OP_CALL_REG: { s390_lgr (code, s390_r1, ins->sreg1); s390_basr (code, s390_r14, s390_r1); } break; case OP_FCALL_MEMBASE: call = (MonoCallInst*)ins; s390_lg (code, s390_r1, 0, ins->sreg1, ins->inst_offset); s390_basr (code, s390_r14, s390_r1); break; case OP_RCALL_MEMBASE: call = (MonoCallInst*)ins; s390_lg (code, s390_r1, 0, ins->sreg1, ins->inst_offset); s390_basr (code, s390_r14, s390_r1); if (ins->dreg != s390_f0) s390_ldr (code, ins->dreg, s390_f0); break; case OP_LCALL_MEMBASE: case OP_VCALL_MEMBASE: case OP_VCALL2_MEMBASE: case OP_VOIDCALL_MEMBASE: case OP_CALL_MEMBASE: { s390_lg (code, s390_r1, 0, ins->sreg1, ins->inst_offset); s390_basr (code, s390_r14, s390_r1); } break; case OP_LOCALLOC: { int area_offset; if (cfg->param_area == 0) area_offset = S390_MINIMAL_STACK_SIZE; else area_offset = cfg->param_area; area_offset = S390_ALIGN(area_offset, S390_STACK_ALIGNMENT); /* Get current backchain pointer */ s390_lg (code, s390_r13, 0, STK_BASE, 0); /* * Round object size to doubleword */ s390_lgr (code, s390_r1, ins->sreg1); s390_aghi (code, s390_r1, 7); s390_srlg (code, s390_r1, s390_r1, 0, 3); s390_sllg (code, s390_r1, s390_r1, 0, 3); if (mono_hwcap_s390x_has_gie) { if (ins->flags & MONO_INST_INIT) s390_lgr (code, s390_r0, s390_r1); s390_risbg (code, ins->dreg, s390_r1, 0, 0xb3, 0); s390_sgrk (code, ins->dreg, STK_BASE, ins->dreg); s390_cgr (code, STK_BASE, ins->dreg); /* L0: */ s390_je (code, 9); /* je L1 */ s390_aghi (code, STK_BASE, -4096); s390_mvghi (code, s390_r15, 0, 0); s390_j (code, -9); /* j L0 */ s390_risbg (code, ins->dreg, s390_r1, 0x34, 0xbf, 0); /* L1: */ s390_ltgr (code, ins->dreg, ins->dreg); s390_jz (code, 13); /* jz L2: */ s390_sgr (code, STK_BASE, ins->dreg); s390_risbg (code, s390_r1, s390_r1, 0x34, 0xbf, 0); s390_lay (code, s390_r1, s390_r1, STK_BASE, -8); s390_mvghi (code, s390_r1, 0, 0); /* L2: */ } else { s390_lgr (code, ins->dreg, s390_r1); s390_nill (code, ins->dreg, 0xf000); s390_lgr (code, s390_r0, STK_BASE); s390_sgr (code, s390_r0, ins->dreg); s390_lgr (code, ins->dreg, s390_r0); s390_cgr (code, STK_BASE, ins->dreg); /* L0: */ s390_je (code, 11); /* je L1 */ s390_aghi (code, STK_BASE, -4096); s390_lghi (code, s390_r0, 0); s390_stg (code, s390_r0, 0, STK_BASE, 4088); s390_j (code, -11); /* j L0 */ s390_lghi (code, ins->dreg, 4095); /* L1: */ s390_ngr (code, ins->dreg, s390_r1); s390_ltgr (code, ins->dreg, ins->dreg); s390_jz (code, 7); /* jz L2 */ s390_sgr (code, STK_BASE, ins->dreg); s390_stg (code, ins->dreg, s390_r1, STK_BASE, -8); /* L2: */ if (ins->flags & MONO_INST_INIT) s390_lgr (code, s390_r0, s390_r1); } /* * Compute address of localloc'd object */ s390_lgr (code, s390_r1, STK_BASE); if (s390_is_imm16(area_offset)) s390_aghi (code, s390_r1, area_offset); else s390_agfi (code, s390_r1, area_offset); s390_aghi (code, s390_r1, 7); s390_srlg (code, s390_r1, s390_r1, 0, 3); s390_sllg (code, s390_r1, s390_r1, 0, 3); s390_lgr (code, ins->dreg, s390_r1); /* Save backchain pointer */ s390_stg (code, s390_r13, 0, STK_BASE, 0); /* * If we need to zero the area then clear from localloc start * using the length we saved earlier */ if (ins->flags & MONO_INST_INIT) { s390_lgr (code, s390_r1, s390_r0); s390_lgr (code, s390_r0, ins->dreg); s390_lgr (code, s390_r14, s390_r12); s390_lghi (code, s390_r13, 0); s390_mvcle(code, s390_r0, s390_r12, 0, 0); s390_jo (code, -2); s390_lgr (code, s390_r12, s390_r14); } /* * If we have an LMF then we have to adjust its BP */ if (cfg->method->save_lmf) { int lmfOffset = cfg->stack_usage - sizeof(MonoLMF); if (s390_is_imm16(lmfOffset)) { s390_lghi (code, s390_r13, lmfOffset); } else if (s390_is_imm32(lmfOffset)) { s390_lgfi (code, s390_r13, lmfOffset); } else { S390_SET (code, s390_r13, lmfOffset); } s390_stg (code, s390_r15, s390_r13, cfg->frame_reg, MONO_STRUCT_OFFSET(MonoLMF, ebp)); } } break; case OP_THROW: { s390_lgr (code, s390_r2, ins->sreg1); code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_arch_throw_exception)); } break; case OP_RETHROW: { s390_lgr (code, s390_r2, ins->sreg1); code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_arch_rethrow_exception)); } break; case OP_START_HANDLER: { MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region); S390_LONG (code, stg, stg, s390_r14, 0, spvar->inst_basereg, spvar->inst_offset); } break; case OP_ENDFILTER: { MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region); if (ins->sreg1 != s390_r2) s390_lgr(code, s390_r2, ins->sreg1); S390_LONG (code, lg, lg, s390_r14, 0, spvar->inst_basereg, spvar->inst_offset); s390_br (code, s390_r14); } break; case OP_ENDFINALLY: { MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region); S390_LONG (code, lg, lg, s390_r14, 0, spvar->inst_basereg, spvar->inst_offset); s390_br (code, s390_r14); } break; case OP_CALL_HANDLER: { mono_add_patch_info_rel (cfg, code-cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb, MONO_R_S390_DIRECT); s390_brasl (code, s390_r14, 0); for (GList *tmp = ins->inst_eh_blocks; tmp != bb->clause_holes; tmp = tmp->prev) mono_cfg_add_try_hole (cfg, ((MonoLeaveClause *) tmp->data)->clause, code, bb); } break; case OP_LABEL: { ins->inst_c0 = code - cfg->native_code; } break; case OP_RELAXED_NOP: case OP_NOP: case OP_DUMMY_USE: case OP_DUMMY_ICONST: case OP_DUMMY_I8CONST: case OP_DUMMY_R8CONST: case OP_DUMMY_R4CONST: case OP_NOT_REACHED: case OP_NOT_NULL: { } break; case OP_IL_SEQ_POINT: mono_add_seq_point (cfg, bb, ins, code - cfg->native_code); break; case OP_SEQ_POINT: { MonoInst *var; RI_Format *o[2]; guint16 displace; if (cfg->compile_aot) NOT_IMPLEMENTED; if (ins->flags & MONO_INST_SINGLE_STEP_LOC) { var = cfg->arch.ss_tramp_var; s390_lg (code, s390_r1, 0, var->inst_basereg, var->inst_offset); if (mono_hwcap_s390x_has_eif) { s390_ltg (code, s390_r14, 0, s390_r1, 0); } else { s390_lg (code, s390_r14, 0, s390_r1, 0); s390_ltgr (code, s390_r14, s390_r14); } o[0] = (RI_Format *) code; s390_jz (code, 4); s390_lgr (code, s390_r1, cfg->frame_reg); s390_basr (code, s390_r14, s390_r14); displace = ((uintptr_t) code - (uintptr_t) o[0]) / 2; o[0]->i2 = displace; } /* * This is the address which is saved in seq points, */ mono_add_seq_point (cfg, bb, ins, code - cfg->native_code); var = cfg->arch.bp_tramp_var; s390_lghi (code, s390_r1, 0); s390_ltgr (code, s390_r1, s390_r1); o[0] = (RI_Format *) code; s390_jz (code, 0); s390_lg (code, s390_r1, 0, var->inst_basereg, var->inst_offset); if (mono_hwcap_s390x_has_eif) { s390_ltg (code, s390_r14, 0, s390_r1, 0); } else { s390_lg (code, s390_r1, 0, s390_r1, 0); s390_ltgr (code, s390_r14, s390_r1); } o[1] = (RI_Format *) code; s390_jz (code, 4); s390_lgr (code, s390_r1, cfg->frame_reg); s390_basr (code, s390_r14, s390_r14); displace = ((uintptr_t) code - (uintptr_t) o[0]) / 2; o[0]->i2 = displace; displace = ((uintptr_t) code - (uintptr_t) o[1]) / 2; o[1]->i2 = displace; /* * Add an additional nop so skipping the bp doesn't cause the ip to point * to another IL offset. */ s390_nop (code); break; } case OP_GENERIC_CLASS_INIT: { static int byte_offset = -1; static guint8 bitmask; short int *jump; g_assert (ins->sreg1 == S390_FIRST_ARG_REG); if (byte_offset < 0) mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask); s390_tm (code, ins->sreg1, byte_offset, bitmask); s390_jo (code, 0); CODEPTR(code, jump); code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_generic_class_init)); PTRSLOT (code, jump); ins->flags |= MONO_INST_GC_CALLSITE; ins->backend.pc_offset = code - cfg->native_code; break; } case OP_BR: EMIT_UNCOND_BRANCH(ins); break; case OP_BR_REG: { s390_br (code, ins->sreg1); } break; case OP_CEQ: case OP_ICEQ: case OP_LCEQ: { if (mono_hwcap_s390x_has_lsoc2) { s390_lghi (code, ins->dreg, 0); s390_locghiz(code, ins->dreg, 1); } else if (mono_hwcap_s390x_has_mlt) { s390_lghi (code, ins->dreg, 0); s390_lghi (code, s390_r13, 1); s390_locgrz(code, ins->dreg, s390_r13); } else { s390_lghi(code, ins->dreg, 1); s390_jz (code, 4); s390_lghi(code, ins->dreg, 0); } } break; case OP_CLT: case OP_ICLT: case OP_LCLT: { if (mono_hwcap_s390x_has_lsoc2) { s390_lghi (code, ins->dreg, 0); s390_locghil(code, ins->dreg, 1); } else if (mono_hwcap_s390x_has_mlt) { s390_lghi (code, ins->dreg, 0); s390_lghi (code, s390_r13, 1); s390_locgrl(code, ins->dreg, s390_r13); } else { s390_lghi(code, ins->dreg, 1); s390_jl (code, 4); s390_lghi(code, ins->dreg, 0); } } break; case OP_CLT_UN: case OP_ICLT_UN: case OP_LCLT_UN: { if (mono_hwcap_s390x_has_lsoc2) { s390_lghi (code, ins->dreg, 0); s390_locghilo(code, ins->dreg, 1); } else if (mono_hwcap_s390x_has_mlt) { s390_lghi (code, ins->dreg, 0); s390_lghi (code, s390_r13, 1); s390_locgrlo(code, ins->dreg, s390_r13); } else { s390_lghi(code, ins->dreg, 1); s390_jlo (code, 4); s390_lghi(code, ins->dreg, 0); } } break; case OP_CGT: case OP_ICGT: case OP_LCGT: { if (mono_hwcap_s390x_has_lsoc2) { s390_lghi (code, ins->dreg, 0); s390_locghih(code, ins->dreg, 1); } else if (mono_hwcap_s390x_has_mlt) { s390_lghi (code, ins->dreg, 0); s390_lghi (code, s390_r13, 1); s390_locgrh(code, ins->dreg, s390_r13); } else { s390_lghi(code, ins->dreg, 1); s390_jh (code, 4); s390_lghi(code, ins->dreg, 0); } } break; case OP_CGT_UN: case OP_ICGT_UN: case OP_LCGT_UN: { if (mono_hwcap_s390x_has_lsoc2) { s390_lghi (code, ins->dreg, 0); s390_locghiho(code, ins->dreg, 1); } else if (mono_hwcap_s390x_has_mlt) { s390_lghi (code, ins->dreg, 0); s390_lghi (code, s390_r13, 1); s390_locgrho(code, ins->dreg, s390_r13); } else { s390_lghi(code, ins->dreg, 1); s390_jho (code, 4); s390_lghi(code, ins->dreg, 0); } } break; case OP_ICNEQ: { if (mono_hwcap_s390x_has_lsoc2) { s390_lghi (code, ins->dreg, 0); s390_locghine(code, ins->dreg, 1); } else if (mono_hwcap_s390x_has_mlt) { s390_lghi (code, ins->dreg, 0); s390_lghi (code, s390_r13, 1); s390_locgrne(code, ins->dreg, s390_r13); } else { s390_lghi(code, ins->dreg, 1); s390_jne (code, 4); s390_lghi(code, ins->dreg, 0); } } break; case OP_ICLE_UN: case OP_ICLE: { if (mono_hwcap_s390x_has_lsoc2) { s390_lghi (code, ins->dreg, 0); s390_locghile(code, ins->dreg, 1); } else if (mono_hwcap_s390x_has_mlt) { s390_lghi (code, ins->dreg, 0); s390_lghi (code, s390_r13, 1); s390_locgrle(code, ins->dreg, s390_r13); } else { s390_lghi(code, ins->dreg, 1); s390_jle (code, 4); s390_lghi(code, ins->dreg, 0); } } break; case OP_ICGE: case OP_ICGE_UN: { if (mono_hwcap_s390x_has_lsoc2) { s390_lghi (code, ins->dreg, 0); s390_locghihe(code, ins->dreg, 1); } else if (mono_hwcap_s390x_has_mlt) { s390_lghi (code, ins->dreg, 0); s390_lghi (code, s390_r13, 1); s390_locgrhe(code, ins->dreg, s390_r13); } else { s390_lghi(code, ins->dreg, 1); s390_jhe (code, 4); s390_lghi(code, ins->dreg, 0); } } break; case OP_COND_EXC_EQ: case OP_COND_EXC_IEQ: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_EQ, ins->inst_p1); break; case OP_COND_EXC_NE_UN: case OP_COND_EXC_INE_UN: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NE, ins->inst_p1); break; case OP_COND_EXC_LT: case OP_COND_EXC_ILT: case OP_COND_EXC_LT_UN: case OP_COND_EXC_ILT_UN: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_LT, ins->inst_p1); break; case OP_COND_EXC_GT: case OP_COND_EXC_IGT: case OP_COND_EXC_GT_UN: case OP_COND_EXC_IGT_UN: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_GT, ins->inst_p1); break; case OP_COND_EXC_GE: case OP_COND_EXC_IGE: case OP_COND_EXC_GE_UN: case OP_COND_EXC_IGE_UN: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_GE, ins->inst_p1); break; case OP_COND_EXC_LE: case OP_COND_EXC_ILE: case OP_COND_EXC_LE_UN: case OP_COND_EXC_ILE_UN: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_LE, ins->inst_p1); break; case OP_COND_EXC_OV: case OP_COND_EXC_IOV: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, ins->inst_p1); break; case OP_COND_EXC_NO: case OP_COND_EXC_INO: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NO, ins->inst_p1); break; case OP_COND_EXC_C: case OP_COND_EXC_IC: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_CY, ins->inst_p1); break; case OP_COND_EXC_NC: case OP_COND_EXC_INC: EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NC, ins->inst_p1); break; case OP_LBEQ: case OP_IBEQ: EMIT_COND_BRANCH (ins, S390_CC_EQ); break; case OP_LBNE_UN: case OP_IBNE_UN: EMIT_COND_BRANCH (ins, S390_CC_NE); break; case OP_LBLT: case OP_LBLT_UN: case OP_IBLT: case OP_IBLT_UN: EMIT_COND_BRANCH (ins, S390_CC_LT); break; case OP_LBGT: case OP_LBGT_UN: case OP_IBGT: case OP_IBGT_UN: EMIT_COND_BRANCH (ins, S390_CC_GT); break; case OP_LBGE: case OP_LBGE_UN: case OP_IBGE: case OP_IBGE_UN: EMIT_COND_BRANCH (ins, S390_CC_GE); break; case OP_LBLE: case OP_LBLE_UN: case OP_IBLE: case OP_IBLE_UN: EMIT_COND_BRANCH (ins, S390_CC_LE); break; case OP_S390_CRJ: EMIT_COMP_AND_BRANCH(ins, crj, cr); break; case OP_S390_CLRJ: EMIT_COMP_AND_BRANCH(ins, clrj, clr); break; case OP_S390_CGRJ: EMIT_COMP_AND_BRANCH(ins, cgrj, cgr); break; case OP_S390_CLGRJ: EMIT_COMP_AND_BRANCH(ins, clgrj, clgr); break; case OP_S390_CIJ: EMIT_COMP_AND_BRANCH_IMM(ins, crj, cr, ltr, FALSE); break; case OP_S390_CLIJ: EMIT_COMP_AND_BRANCH_IMM(ins, clrj, clr, ltr, TRUE); break; case OP_S390_CGIJ: EMIT_COMP_AND_BRANCH_IMM(ins, cgrj, cgr, ltgr, FALSE); break; case OP_S390_CLGIJ: EMIT_COMP_AND_BRANCH_IMM(ins, clgrj, clgr, ltgr, TRUE); break; /* floating point opcodes */ case OP_R8CONST: { double d = *(double *) ins->inst_p0; if (d == 0) { s390_lzdr (code, ins->dreg); if (mono_signbit (d) != 0) s390_lndbr (code, ins->dreg, ins->dreg); } else { S390_SET (code, s390_r13, ins->inst_p0); s390_ld (code, ins->dreg, 0, s390_r13, 0); } } break; case OP_R4CONST: { float f = *(float *) ins->inst_p0; if (f == 0) { s390_lzer (code, ins->dreg); if (mono_signbit (f) != 0) s390_lnebr (code, ins->dreg, ins->dreg); } else { S390_SET (code, s390_r13, ins->inst_p0); s390_le (code, ins->dreg, 0, s390_r13, 0); s390_le (code, ins->dreg, 0, s390_r13, 0); } } break; case OP_STORER8_MEMBASE_REG: { S390_LONG (code, stdy, std, ins->sreg1, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_LOADR8_MEMBASE: { S390_LONG (code, ldy, ld, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_STORER4_MEMBASE_REG: { S390_LONG (code, stey, ste, ins->sreg1, 0, ins->inst_destbasereg, ins->inst_offset); } break; case OP_LOADR4_MEMBASE: { S390_LONG (code, ley, le, ins->dreg, 0, ins->inst_basereg, ins->inst_offset); } break; case OP_ICONV_TO_R_UN: { if (mono_hwcap_s390x_has_fpe) { s390_cdlfbr (code, ins->dreg, 5, ins->sreg1, 0); } else { s390_llgfr (code, s390_r0, ins->sreg1); s390_cdgbr (code, ins->dreg, s390_r0); } } break; case OP_LCONV_TO_R_UN: { if (mono_hwcap_s390x_has_fpe) { s390_cdlgbr (code, ins->dreg, 6, ins->sreg1, 0); } else { short int *jump; s390_lgdr (code, s390_r0, s390_r15); s390_lgdr (code, s390_r1, s390_r13); s390_lgdr (code, s390_r14, s390_r12); s390_cxgbr (code, s390_f12, ins->sreg1); s390_ltgr (code, ins->sreg1, ins->sreg1); s390_jnl (code, 0); CODEPTR(code, jump); S390_SET (code, s390_r13, 0x403f000000000000llu); s390_lgdr (code, s390_f13, s390_r13); s390_lzdr (code, s390_f15); s390_axbr (code, s390_f12, s390_f13); PTRSLOT(code, jump); s390_ldxbr (code, s390_f13, s390_f12); s390_ldr (code, ins->dreg, s390_f13); s390_ldgr (code, s390_f12, s390_r14); s390_ldgr (code, s390_f13, s390_r1); s390_ldgr (code, s390_f15, s390_r0); } } break; case OP_ICONV_TO_R4: s390_cefbr (code, ins->dreg, ins->sreg1); break; case OP_LCONV_TO_R4: s390_cegbr (code, ins->dreg, ins->sreg1); break; case OP_ICONV_TO_R8: s390_cdfbr (code, ins->dreg, ins->sreg1); break; case OP_LCONV_TO_R8: s390_cdgbr (code, ins->dreg, ins->sreg1); break; case OP_FCONV_TO_I1: s390_cgdbr (code, ins->dreg, 5, ins->sreg1); s390_ltgr (code, ins->dreg, ins->dreg); s390_jnl (code, 4); s390_oill (code, ins->dreg, 0x80); s390_lghi (code, s390_r0, 0xff); s390_ngr (code, ins->dreg, s390_r0); break; case OP_FCONV_TO_U1: if (mono_hwcap_s390x_has_fpe) { s390_clgdbr (code, ins->dreg, 5, ins->sreg1, 0); s390_lghi (code, s390_r0, 0xff); s390_ngr (code, ins->dreg, s390_r0); } else { code = emit_double_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE); } break; case OP_FCONV_TO_I2: s390_cgdbr (code, ins->dreg, 5, ins->sreg1); s390_ltgr (code, ins->dreg, ins->dreg); s390_jnl (code, 4); s390_oill (code, ins->dreg, 0x8000); s390_llill (code, s390_r0, 0xffff); s390_ngr (code, ins->dreg, s390_r0); break; case OP_FCONV_TO_U2: if (mono_hwcap_s390x_has_fpe) { s390_clgdbr (code, ins->dreg, 5, ins->sreg1, 0); s390_llill (code, s390_r0, 0xffff); s390_ngr (code, ins->dreg, s390_r0); } else { code = emit_double_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE); } break; case OP_FCONV_TO_I4: s390_cfdbr (code, ins->dreg, 5, ins->sreg1); break; case OP_FCONV_TO_U4: if (mono_hwcap_s390x_has_fpe) { s390_clgdbr (code, ins->dreg, 5, ins->sreg1, 0); } else { code = emit_double_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE); } break; case OP_FCONV_TO_I8: s390_cgdbr (code, ins->dreg, 5, ins->sreg1); break; case OP_FCONV_TO_U8: if (mono_hwcap_s390x_has_fpe) { s390_clgdbr (code, ins->dreg, 5, ins->sreg1, 0); } else { code = emit_double_to_int (cfg, code, ins->dreg, ins->sreg1, 8, FALSE); } break; case OP_RCONV_TO_I1: s390_cgebr (code, ins->dreg, 5, ins->sreg1); s390_ltgr (code, ins->dreg, ins->dreg); s390_jnl (code, 4); s390_oill (code, ins->dreg, 0x80); s390_lghi (code, s390_r0, 0xff); s390_ngr (code, ins->dreg, s390_r0); break; case OP_RCONV_TO_U1: if (mono_hwcap_s390x_has_fpe) { s390_clgebr (code, ins->dreg, 5, ins->sreg1, 0); s390_lghi (code, s390_r0, 0xff); s390_ngr (code, ins->dreg, s390_r0); } else { code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE); } break; case OP_RCONV_TO_I2: s390_cgebr (code, ins->dreg, 5, ins->sreg1); s390_ltgr (code, ins->dreg, ins->dreg); s390_jnl (code, 4); s390_oill (code, ins->dreg, 0x8000); s390_llill (code, s390_r0, 0xffff); s390_ngr (code, ins->dreg, s390_r0); break; case OP_RCONV_TO_U2: if (mono_hwcap_s390x_has_fpe) { s390_clgebr (code, ins->dreg, 5, ins->sreg1, 0); s390_llill (code, s390_r0, 0xffff); s390_ngr (code, ins->dreg, s390_r0); } else { code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE); } break; case OP_RCONV_TO_I4: s390_cfebr (code, ins->dreg, 5, ins->sreg1); break; case OP_RCONV_TO_U4: if (mono_hwcap_s390x_has_fpe) { s390_clfebr (code, ins->dreg, 5, ins->sreg1, 0); } else { code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE); } break; case OP_RCONV_TO_I8: s390_cgebr (code, ins->dreg, 5, ins->sreg1); break; case OP_RCONV_TO_U8: if (mono_hwcap_s390x_has_fpe) { s390_clgebr (code, ins->dreg, 5, ins->sreg1, 0); } else { code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 8, FALSE); } break; case OP_LCONV_TO_OVF_I: { /* Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000 */ short int *o[5]; s390_ltgr (code, ins->sreg2, ins->sreg2); s390_jnl (code, 0); CODEPTR(code, o[0]); s390_ltgr (code, ins->sreg1, ins->sreg1); s390_jnl (code, 0); CODEPTR(code, o[1]); s390_lhi (code, s390_r13, -1); s390_cgr (code, ins->sreg1, s390_r13); s390_jnz (code, 0); CODEPTR(code, o[2]); if (ins->dreg != ins->sreg2) s390_lgr (code, ins->dreg, ins->sreg2); s390_j (code, 0); CODEPTR(code, o[3]); PTRSLOT(code, o[0]); s390_jz (code, 0); CODEPTR(code, o[4]); PTRSLOT(code, o[1]); PTRSLOT(code, o[2]); mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "OverflowException"); s390_brasl (code, s390_r14, 0); PTRSLOT(code, o[3]); PTRSLOT(code, o[4]); } break; case OP_ABS: s390_lpdbr (code, ins->dreg, ins->sreg1); break; case OP_ABSF: s390_lpebr (code, ins->dreg, ins->sreg1); break; case OP_CEIL: s390_fidbra (code, ins->dreg, 6, ins->sreg1, 4); break; case OP_CEILF: s390_fiebra (code, ins->dreg, 6, ins->sreg1, 4); break; case OP_FLOOR: s390_fidbra (code, ins->dreg, 7, ins->sreg1, 4); break; case OP_FLOORF: s390_fiebra (code, ins->dreg, 7, ins->sreg1, 4); break; case OP_FCOPYSIGN: s390_cpsdr (code, ins->dreg, ins->sreg2, ins->sreg1); break; case OP_ROUND: s390_fidbra (code, ins->dreg, 4, ins->sreg1, 4); break; case OP_SQRT: s390_sqdbr (code, ins->dreg, ins->sreg1); break; case OP_SQRTF: s390_sqebr (code, ins->dreg, ins->sreg1); break; case OP_TRUNC: s390_fidbra (code, ins->dreg, 5, ins->sreg1, 4); break; case OP_TRUNCF: s390_fiebra (code, ins->dreg, 5, ins->sreg1, 4); break; case OP_FADD: { CHECK_SRCDST_COM_F; s390_adbr (code, ins->dreg, src2); } break; case OP_RADD: { CHECK_SRCDST_COM_F; s390_aebr (code, ins->dreg, src2); } break; case OP_FSUB: { CHECK_SRCDST_NCOM_F(sdbr); } break; case OP_RSUB: { CHECK_SRCDST_NCOM_F(sebr); } break; case OP_FMUL: { CHECK_SRCDST_COM_F; s390_mdbr (code, ins->dreg, src2); } break; case OP_RMUL: { CHECK_SRCDST_COM_F; s390_meer (code, ins->dreg, src2); } break; case OP_FDIV: { CHECK_SRCDST_NCOM_F(ddbr); } break; case OP_RDIV: { CHECK_SRCDST_NCOM_F(debr); } break; case OP_FNEG: { s390_lcdbr (code, ins->dreg, ins->sreg1); } break; case OP_RNEG: { s390_lcebr (code, ins->dreg, ins->sreg1); } break; case OP_FREM: { CHECK_SRCDST_NCOM_FR(didbr, 5); } break; case OP_RREM: { CHECK_SRCDST_NCOM_FR(diebr, 5); } break; case OP_FCOMPARE: { s390_cdbr (code, ins->sreg1, ins->sreg2); } break; case OP_RCOMPARE: { s390_cebr (code, ins->sreg1, ins->sreg2); } break; case OP_FCEQ: { s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_je (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_FCLT: { s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jl (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_FCLT_UN: { s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jlo (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_FCGT: { s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jh (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_FCGT_UN: { s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jho (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_FCNEQ: { s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jne (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_FCGE: { s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jhe (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_FCLE: { s390_cdbr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jle (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCEQ: { s390_cebr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_je (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCLT: { s390_cebr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jl (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCLT_UN: { s390_cebr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jlo (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCGT: { s390_cebr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jh (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCGT_UN: { s390_cebr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jho (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCNEQ: { s390_cebr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jne (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCGE: { s390_cebr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jhe (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_RCLE: { s390_cebr (code, ins->sreg1, ins->sreg2); s390_lghi (code, ins->dreg, 1); s390_jle (code, 4); s390_lghi (code, ins->dreg, 0); } break; case OP_FBEQ: { short *o; s390_jo (code, 0); CODEPTR(code, o); EMIT_COND_BRANCH (ins, S390_CC_EQ); PTRSLOT (code, o); } break; case OP_FBNE_UN: EMIT_COND_BRANCH (ins, S390_CC_NE|S390_CC_OV); break; case OP_FBLT: { short *o; s390_jo (code, 0); CODEPTR(code, o); EMIT_COND_BRANCH (ins, S390_CC_LT); PTRSLOT (code, o); } break; case OP_FBLT_UN: EMIT_COND_BRANCH (ins, S390_CC_LT|S390_CC_OV); break; case OP_FBGT: { short *o; s390_jo (code, 0); CODEPTR(code, o); EMIT_COND_BRANCH (ins, S390_CC_GT); PTRSLOT (code, o); } break; case OP_FBGT_UN: EMIT_COND_BRANCH (ins, S390_CC_GT|S390_CC_OV); break; case OP_FBGE: { short *o; s390_jo (code, 0); CODEPTR(code, o); EMIT_COND_BRANCH (ins, S390_CC_GE); PTRSLOT (code, o); } break; case OP_FBGE_UN: EMIT_COND_BRANCH (ins, S390_CC_GE|S390_CC_OV); break; case OP_FBLE: { short *o; s390_jo (code, 0); CODEPTR(code, o); EMIT_COND_BRANCH (ins, S390_CC_LE); PTRSLOT (code, o); } break; case OP_FBLE_UN: EMIT_COND_BRANCH (ins, S390_CC_LE|S390_CC_OV); break; case OP_CKFINITE: { short *o; s390_lhi (code, s390_r13, 0x7f); s390_tcdb (code, ins->sreg1, 0, s390_r13, 0); s390_jz (code, 0); CODEPTR(code, o); mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, "OverflowException"); s390_brasl (code, s390_r14,0); PTRSLOT(code, o); } break; case OP_S390_MOVE: { if (ins->backend.size > 0) { if (ins->backend.size <= 256) { s390_mvc (code, ins->backend.size, ins->sreg2, ins->inst_offset, ins->sreg1, ins->inst_imm); } else { s390_lgr (code, s390_r0, ins->sreg2); if (ins->inst_offset > 0) { if (s390_is_imm16 (ins->inst_offset)) { s390_aghi (code, s390_r0, ins->inst_offset); } else if (s390_is_imm32 (ins->inst_offset)) { s390_agfi (code, s390_r0, ins->inst_offset); } else { S390_SET (code, s390_r13, ins->inst_offset); s390_agr (code, s390_r0, s390_r13); } } s390_lgr (code, s390_r12, ins->sreg1); if (ins->inst_imm > 0) { if (s390_is_imm16 (ins->inst_imm)) { s390_aghi (code, s390_r12, ins->inst_imm); } else if (s390_is_imm32 (ins->inst_imm)) { s390_agfi (code, s390_r12, ins->inst_imm); } else { S390_SET (code, s390_r13, ins->inst_imm); s390_agr (code, s390_r12, s390_r13); } } if (s390_is_imm16 (ins->backend.size)) { s390_lghi (code, s390_r1, ins->backend.size); } else if (s390_is_imm32 (ins->inst_offset)) { s390_agfi (code, s390_r1, ins->backend.size); } else { S390_SET (code, s390_r13, ins->backend.size); s390_agr (code, s390_r1, s390_r13); } s390_lgr (code, s390_r13, s390_r1); s390_mvcle(code, s390_r0, s390_r12, 0, 0); s390_jo (code, -2); } } } break; case OP_ATOMIC_ADD_I8: { if (mono_hwcap_s390x_has_ia) { s390_laag(code, s390_r0, ins->sreg2, ins->inst_basereg, ins->inst_offset); if (mono_hwcap_s390x_has_mlt) { s390_agrk(code, ins->dreg, s390_r0, ins->sreg2); } else { s390_agr (code, s390_r0, ins->sreg2); s390_lgr (code, ins->dreg, s390_r0); } } else { s390_lgr (code, s390_r1, ins->sreg2); s390_lg (code, s390_r0, 0, ins->inst_basereg, ins->inst_offset); s390_agr (code, s390_r1, s390_r0); s390_csg (code, s390_r0, s390_r1, ins->inst_basereg, ins->inst_offset); s390_jnz (code, -10); s390_lgr (code, ins->dreg, s390_r1); } } break; case OP_ATOMIC_EXCHANGE_I8: { s390_lg (code, s390_r0, 0, ins->inst_basereg, ins->inst_offset); s390_csg (code, s390_r0, ins->sreg2, ins->inst_basereg, ins->inst_offset); s390_jnz (code, -6); s390_lgr (code, ins->dreg, s390_r0); } break; case OP_ATOMIC_ADD_I4: { if (mono_hwcap_s390x_has_ia) { s390_laa (code, s390_r0, ins->sreg2, ins->inst_basereg, ins->inst_offset); s390_ar (code, s390_r0, ins->sreg2); s390_lgfr(code, ins->dreg, s390_r0); } else { s390_lgfr(code, s390_r1, ins->sreg2); s390_lgf (code, s390_r0, 0, ins->inst_basereg, ins->inst_offset); s390_agr (code, s390_r1, s390_r0); s390_cs (code, s390_r0, s390_r1, ins->inst_basereg, ins->inst_offset); s390_jnz (code, -9); s390_lgfr(code, ins->dreg, s390_r1); } } break; case OP_ATOMIC_EXCHANGE_I4: { s390_l (code, s390_r0, 0, ins->inst_basereg, ins->inst_offset); s390_cs (code, s390_r0, ins->sreg2, ins->inst_basereg, ins->inst_offset); s390_jnz (code, -4); s390_lgfr(code, ins->dreg, s390_r0); } break; case OP_S390_BKCHAIN: { s390_lgr (code, ins->dreg, ins->sreg1); if (s390_is_imm16 (cfg->stack_offset)) { s390_aghi (code, ins->dreg, cfg->stack_offset); } else if (s390_is_imm32 (cfg->stack_offset)) { s390_agfi (code, ins->dreg, cfg->stack_offset); } else { S390_SET (code, s390_r13, cfg->stack_offset); s390_agr (code, ins->dreg, s390_r13); } } break; case OP_MEMORY_BARRIER: s390_mem (code); break; case OP_POPCNT32: s390_llgfr (code, s390_r1, ins->sreg1); if (mono_hwcap_s390x_has_mie3) { s390_popcnt (code, ins->dreg, 0x80, s390_r1); } else { s390_popcnt (code, s390_r0, 0, s390_r1); s390_ahhlr (code, s390_r0, s390_r0, s390_r0); s390_sllg (code, s390_r1, s390_r0, 0, 16); s390_algr (code, s390_r0, s390_r1); s390_sllg (code, s390_r1, s390_r0, 0, 8); s390_algr (code, s390_r0, s390_r1); s390_srlg (code, ins->dreg, s390_r0, 0, 56); } break; case OP_POPCNT64: if (mono_hwcap_s390x_has_mie3) { s390_popcnt (code, ins->dreg, 0x80, ins->sreg1); } else { s390_ahhlr (code, s390_r0, s390_r0, s390_r0); s390_sllg (code, s390_r1, s390_r0, 0, 16); s390_algr (code, s390_r0, s390_r1); s390_sllg (code, s390_r1, s390_r0, 0, 8); s390_algr (code, s390_r0, s390_r1); s390_srlg (code, ins->dreg, s390_r0, 0, 56); } break; case OP_LIVERANGE_START: { if (cfg->verbose_level > 1) printf ("R%d START=0x%x\n", MONO_VARINFO (cfg, ins->inst_c0)->vreg, (int)(code - cfg->native_code)); MONO_VARINFO (cfg, ins->inst_c0)->live_range_start = code - cfg->native_code; break; } case OP_LIVERANGE_END: { if (cfg->verbose_level > 1) printf ("R%d END=0x%x\n", MONO_VARINFO (cfg, ins->inst_c0)->vreg, (int)(code - cfg->native_code)); MONO_VARINFO (cfg, ins->inst_c0)->live_range_end = code - cfg->native_code; break; } case OP_GC_SAFE_POINT: { short *br; s390_ltg (code, s390_r0, 0, ins->sreg1, 0); s390_jz (code, 0); CODEPTR(code, br); code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_threads_state_poll)); PTRSLOT (code, br); break; } case OP_GC_LIVENESS_DEF: case OP_GC_LIVENESS_USE: case OP_GC_PARAM_SLOT_LIVENESS_DEF: ins->backend.pc_offset = code - cfg->native_code; break; case OP_GC_SPILL_SLOT_LIVENESS_DEF: ins->backend.pc_offset = code - cfg->native_code; bb->spill_slot_defs = g_slist_prepend_mempool (cfg->mempool, bb->spill_slot_defs, ins); break; #ifdef MONO_ARCH_SIMD_INTRINSICS case OP_ADDPS: s390x_addps (code, ins->sreg1, ins->sreg2); break; case OP_DIVPS: s390x_divps (code, ins->sreg1, ins->sreg2); break; case OP_MULPS: s390x_mulps (code, ins->sreg1, ins->sreg2); break; case OP_SUBPS: s390x_subps (code, ins->sreg1, ins->sreg2); break; case OP_MAXPS: s390x_maxps (code, ins->sreg1, ins->sreg2); break; case OP_MINPS: s390x_minps (code, ins->sreg1, ins->sreg2); break; case OP_COMPPS: g_assert (ins->inst_c0 >= 0 && ins->inst_c0 <= 7); s390x_cmpps_imm (code, ins->sreg1, ins->sreg2, ins->inst_c0); break; case OP_ANDPS: s390x_andps (code, ins->sreg1, ins->sreg2); break; case OP_ANDNPS: s390x_andnps (code, ins->sreg1, ins->sreg2); break; case OP_ORPS: s390x_orps (code, ins->sreg1, ins->sreg2); break; case OP_XORPS: s390x_xorps (code, ins->sreg1, ins->sreg2); break; case OP_SQRTPS: s390x_sqrtps (code, ins->dreg, ins->sreg1); break; case OP_RSQRTPS: s390x_rsqrtps (code, ins->dreg, ins->sreg1); break; case OP_RCPPS: s390x_rcpps (code, ins->dreg, ins->sreg1); break; case OP_ADDSUBPS: s390x_addsubps (code, ins->sreg1, ins->sreg2); break; case OP_HADDPS: s390x_haddps (code, ins->sreg1, ins->sreg2); break; case OP_HSUBPS: s390x_hsubps (code, ins->sreg1, ins->sreg2); break; case OP_DUPPS_HIGH: s390x_movshdup (code, ins->dreg, ins->sreg1); break; case OP_DUPPS_LOW: s390x_movsldup (code, ins->dreg, ins->sreg1); break; case OP_PSHUFLEW_HIGH: g_assert (ins->inst_c0 >= 0 && ins->inst_c0 <= 0xFF); s390x_pshufhw_imm (code, ins->dreg, ins->sreg1, ins->inst_c0); break; case OP_PSHUFLEW_LOW: g_assert (ins->inst_c0 >= 0 && ins->inst_c0 <= 0xFF); s390x_pshuflw_imm (code, ins->dreg, ins->sreg1, ins->inst_c0); break; case OP_PSHUFLED: g_assert (ins->inst_c0 >= 0 && ins->inst_c0 <= 0xFF); s390x_pshufd_imm (code, ins->dreg, ins->sreg1, ins->inst_c0); break; case OP_SHUFPS: g_assert (ins->inst_c0 >= 0 && ins->inst_c0 <= 0xFF); s390x_shufps_imm (code, ins->sreg1, ins->sreg2, ins->inst_c0); break; case OP_SHUFPD: g_assert (ins->inst_c0 >= 0 && ins->inst_c0 <= 0x3); s390x_shufpd_imm (code, ins->sreg1, ins->sreg2, ins->inst_c0); break; case OP_ADDPD: s390x_addpd (code, ins->sreg1, ins->sreg2); break; case OP_DIVPD: s390x_divpd (code, ins->sreg1, ins->sreg2); break; case OP_MULPD: s390x_mulpd (code, ins->sreg1, ins->sreg2); break; case OP_SUBPD: s390x_subpd (code, ins->sreg1, ins->sreg2); break; case OP_MAXPD: s390x_maxpd (code, ins->sreg1, ins->sreg2); break; case OP_MINPD: s390x_minpd (code, ins->sreg1, ins->sreg2); break; case OP_COMPPD: g_assert (ins->inst_c0 >= 0 && ins->inst_c0 <= 7); s390x_cmppd_imm (code, ins->sreg1, ins->sreg2, ins->inst_c0); break; case OP_ANDPD: s390x_andpd (code, ins->sreg1, ins->sreg2); break; case OP_ANDNPD: s390x_andnpd (code, ins->sreg1, ins->sreg2); break; case OP_ORPD: s390x_orpd (code, ins->sreg1, ins->sreg2); break; case OP_XORPD: s390x_xorpd (code, ins->sreg1, ins->sreg2); break; case OP_SQRTPD: s390x_sqrtpd (code, ins->dreg, ins->sreg1); break; case OP_ADDSUBPD: s390x_addsubpd (code, ins->sreg1, ins->sreg2); break; case OP_HADDPD: s390x_haddpd (code, ins->sreg1, ins->sreg2); break; case OP_HSUBPD: s390x_hsubpd (code, ins->sreg1, ins->sreg2); break; case OP_DUPPD: s390x_movddup (code, ins->dreg, ins->sreg1); break; case OP_EXTRACT_MASK: s390x_pmovmskb (code, ins->dreg, ins->sreg1); break; case OP_PAND: s390x_pand (code, ins->sreg1, ins->sreg2); break; case OP_POR: s390x_por (code, ins->sreg1, ins->sreg2); break; case OP_PXOR: s390x_pxor (code, ins->sreg1, ins->sreg2); break; case OP_PADDB: s390x_paddb (code, ins->sreg1, ins->sreg2); break; case OP_PADDW: s390x_paddw (code, ins->sreg1, ins->sreg2); break; case OP_PADDD: s390x_paddd (code, ins->sreg1, ins->sreg2); break; case OP_PADDQ: s390x_paddq (code, ins->sreg1, ins->sreg2); break; case OP_PSUBB: s390x_psubb (code, ins->sreg1, ins->sreg2); break; case OP_PSUBW: s390x_psubw (code, ins->sreg1, ins->sreg2); break; case OP_PSUBD: s390x_psubd (code, ins->sreg1, ins->sreg2); break; case OP_PSUBQ: s390x_psubq (code, ins->sreg1, ins->sreg2); break; case OP_PMAXB_UN: s390x_pmaxub (code, ins->sreg1, ins->sreg2); break; case OP_PMAXW_UN: s390x_pmaxuw (code, ins->sreg1, ins->sreg2); break; case OP_PMAXD_UN: s390x_pmaxud (code, ins->sreg1, ins->sreg2); break; case OP_PMAXB: s390x_pmaxsb (code, ins->sreg1, ins->sreg2); break; case OP_PMAXW: s390x_pmaxsw (code, ins->sreg1, ins->sreg2); break; case OP_PMAXD: s390x_pmaxsd (code, ins->sreg1, ins->sreg2); break; case OP_PAVGB_UN: s390x_pavgb (code, ins->sreg1, ins->sreg2); break; case OP_PAVGW_UN: s390x_pavgw (code, ins->sreg1, ins->sreg2); break; case OP_PMINB_UN: s390x_pminub (code, ins->sreg1, ins->sreg2); break; case OP_PMINW_UN: s390x_pminuw (code, ins->sreg1, ins->sreg2); break; case OP_PMIND_UN: s390x_pminud (code, ins->sreg1, ins->sreg2); break; case OP_PMINB: s390x_pminsb (code, ins->sreg1, ins->sreg2); break; case OP_PMINW: s390x_pminsw (code, ins->sreg1, ins->sreg2); break; case OP_PMIND: s390x_pminsd (code, ins->sreg1, ins->sreg2); break; case OP_PCMPEQB: s390x_pcmpeqb (code, ins->sreg1, ins->sreg2); break; case OP_PCMPEQW: s390x_pcmpeqw (code, ins->sreg1, ins->sreg2); break; case OP_PCMPEQD: s390x_pcmpeqd (code, ins->sreg1, ins->sreg2); break; case OP_PCMPEQQ: s390x_pcmpeqq (code, ins->sreg1, ins->sreg2); break; case OP_PCMPGTB: s390x_pcmpgtb (code, ins->sreg1, ins->sreg2); break; case OP_PCMPGTW: s390x_pcmpgtw (code, ins->sreg1, ins->sreg2); break; case OP_PCMPGTD: s390x_pcmpgtd (code, ins->sreg1, ins->sreg2); break; case OP_PCMPGTQ: s390x_pcmpgtq (code, ins->sreg1, ins->sreg2); break; case OP_PSUM_ABS_DIFF: s390x_psadbw (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_LOWB: s390x_punpcklbw (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_LOWW: s390x_punpcklwd (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_LOWD: s390x_punpckldq (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_LOWQ: s390x_punpcklqdq (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_LOWPS: s390x_unpcklps (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_LOWPD: s390x_unpcklpd (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_HIGHB: s390x_punpckhbw (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_HIGHW: s390x_punpckhwd (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_HIGHD: s390x_punpckhdq (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_HIGHQ: s390x_punpckhqdq (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_HIGHPS: s390x_unpckhps (code, ins->sreg1, ins->sreg2); break; case OP_UNPACK_HIGHPD: s390x_unpckhpd (code, ins->sreg1, ins->sreg2); break; case OP_PACKW: s390x_packsswb (code, ins->sreg1, ins->sreg2); break; case OP_PACKD: s390x_packssdw (code, ins->sreg1, ins->sreg2); break; case OP_PACKW_UN: s390x_packuswb (code, ins->sreg1, ins->sreg2); break; case OP_PACKD_UN: s390x_packusdw (code, ins->sreg1, ins->sreg2); break; case OP_PADDB_SAT_UN: s390x_paddusb (code, ins->sreg1, ins->sreg2); break; case OP_PSUBB_SAT_UN: s390x_psubusb (code, ins->sreg1, ins->sreg2); break; case OP_PADDW_SAT_UN: s390x_paddusw (code, ins->sreg1, ins->sreg2); break; case OP_PSUBW_SAT_UN: s390x_psubusw (code, ins->sreg1, ins->sreg2); break; case OP_PADDB_SAT: s390x_paddsb (code, ins->sreg1, ins->sreg2); break; case OP_PSUBB_SAT: s390x_psubsb (code, ins->sreg1, ins->sreg2); break; case OP_PADDW_SAT: s390x_paddsw (code, ins->sreg1, ins->sreg2); break; case OP_PSUBW_SAT: s390x_psubsw (code, ins->sreg1, ins->sreg2); break; case OP_PMULW: s390x_pmullw (code, ins->sreg1, ins->sreg2); break; case OP_PMULD: s390x_pmulld (code, ins->sreg1, ins->sreg2); break; case OP_PMULQ: s390x_pmuludq (code, ins->sreg1, ins->sreg2); break; case OP_PMULW_HIGH_UN: s390x_pmulhuw (code, ins->sreg1, ins->sreg2); break; case OP_PMULW_HIGH: s390x_pmulhw (code, ins->sreg1, ins->sreg2); break; case OP_PSHRW: s390x_psrlw_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSHRW_REG: s390x_psrlw (code, ins->dreg, ins->sreg2); break; case OP_PSARW: s390x_psraw_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSARW_REG: s390x_psraw (code, ins->dreg, ins->sreg2); break; case OP_PSHLW: s390x_psllw_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSHLW_REG: s390x_psllw (code, ins->dreg, ins->sreg2); break; case OP_PSHRD: s390x_psrld_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSHRD_REG: s390x_psrld (code, ins->dreg, ins->sreg2); break; case OP_PSARD: s390x_psrad_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSARD_REG: s390x_psrad (code, ins->dreg, ins->sreg2); break; case OP_PSHLD: s390x_pslld_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSHLD_REG: s390x_pslld (code, ins->dreg, ins->sreg2); break; case OP_PSHRQ: s390x_psrlq_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSHRQ_REG: s390x_psrlq (code, ins->dreg, ins->sreg2); break; /*TODO: This is appart of the sse spec but not added case OP_PSARQ: s390x_psraq_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSARQ_REG: s390x_psraq (code, ins->dreg, ins->sreg2); break; */ case OP_PSHLQ: s390x_psllq_reg_imm (code, ins->dreg, ins->inst_imm); break; case OP_PSHLQ_REG: s390x_psllq (code, ins->dreg, ins->sreg2); break; case OP_CVTDQ2PD: s390x_cvtdq2pd (code, ins->dreg, ins->sreg1); break; case OP_CVTDQ2PS: s390x_cvtdq2ps (code, ins->dreg, ins->sreg1); break; case OP_CVTPD2DQ: s390x_cvtpd2dq (code, ins->dreg, ins->sreg1); break; case OP_CVTPD2PS: s390x_cvtpd2ps (code, ins->dreg, ins->sreg1); break; case OP_CVTPS2DQ: s390x_cvtps2dq (code, ins->dreg, ins->sreg1); break; case OP_CVTPS2PD: s390x_cvtps2pd (code, ins->dreg, ins->sreg1); break; case OP_CVTTPD2DQ: s390x_cvttpd2dq (code, ins->dreg, ins->sreg1); break; case OP_CVTTPS2DQ: s390x_cvttps2dq (code, ins->dreg, ins->sreg1); break; case OP_ICONV_TO_X: amd64_movd_xreg_reg_size (code, ins->dreg, ins->sreg1, 4); break; case OP_EXTRACT_I4: amd64_movd_reg_xreg_size (code, ins->dreg, ins->sreg1, 4); break; case OP_EXTRACT_I8: if (ins->inst_c0) { amd64_movhlps (code, MONO_ARCH_FP_SCRATCH_REG, ins->sreg1); amd64_movd_reg_xreg_size (code, ins->dreg, MONO_ARCH_FP_SCRATCH_REG, 8); } else { amd64_movd_reg_xreg_size (code, ins->dreg, ins->sreg1, 8); } break; case OP_EXTRACT_I1: case OP_EXTRACT_U1: amd64_movd_reg_xreg_size (code, ins->dreg, ins->sreg1, 4); if (ins->inst_c0) amd64_shift_reg_imm (code, X86_SHR, ins->dreg, ins->inst_c0 * 8); amd64_widen_reg (code, ins->dreg, ins->dreg, ins->inst_c1 == OP_EXTRACT_I1, FALSE); break; case OP_EXTRACT_I2: case OP_EXTRACT_U2: /*amd64_movd_reg_xreg_size (code, ins->dreg, ins->sreg1, 4); if (ins->inst_c0) amd64_shift_reg_imm_size (code, X86_SHR, ins->dreg, 16, 4);*/ s390x_pextrw_imm (code, ins->dreg, ins->sreg1, ins->inst_c0); amd64_widen_reg_size (code, ins->dreg, ins->dreg, ins->inst_c1 == OP_EXTRACT_I2, TRUE, 4); break; case OP_EXTRACT_R8: if (ins->inst_c0) amd64_movhlps (code, ins->dreg, ins->sreg1); else s390x_movsd (code, ins->dreg, ins->sreg1); break; case OP_INSERT_I2: s390x_pinsrw_imm (code, ins->sreg1, ins->sreg2, ins->inst_c0); break; case OP_EXTRACTX_U2: s390x_pextrw_imm (code, ins->dreg, ins->sreg1, ins->inst_c0); break; case OP_INSERTX_U1_SLOW: /*sreg1 is the extracted ireg (scratch) /sreg2 is the to be inserted ireg (scratch) /dreg is the xreg to receive the value*/ /*clear the bits from the extracted word*/ amd64_alu_reg_imm (code, X86_AND, ins->sreg1, ins->inst_c0 & 1 ? 0x00FF : 0xFF00); /*shift the value to insert if needed*/ if (ins->inst_c0 & 1) amd64_shift_reg_imm_size (code, X86_SHL, ins->sreg2, 8, 4); /*join them together*/ amd64_alu (code, X86_OR, ins->sreg1, ins->sreg2); s390x_pinsrw_imm (code, ins->dreg, ins->sreg1, ins->inst_c0 / 2); break; case OP_INSERTX_I4_SLOW: s390x_pinsrw_imm (code, ins->dreg, ins->sreg2, ins->inst_c0 * 2); amd64_shift_reg_imm (code, X86_SHR, ins->sreg2, 16); s390x_pinsrw_imm (code, ins->dreg, ins->sreg2, ins->inst_c0 * 2 + 1); break; case OP_INSERTX_I8_SLOW: amd64_movd_xreg_reg_size(code, MONO_ARCH_FP_SCRATCH_REG, ins->sreg2, 8); if (ins->inst_c0) amd64_movlhps (code, ins->dreg, MONO_ARCH_FP_SCRATCH_REG); else s390x_movsd (code, ins->dreg, MONO_ARCH_FP_SCRATCH_REG); break; case OP_INSERTX_R4_SLOW: switch (ins->inst_c0) { case 0: s390x_movss (code, ins->dreg, ins->sreg2); break; case 1: s390x_pshufd_imm (code, ins->dreg, ins->dreg, mono_simd_shuffle_mask(1, 0, 2, 3)); s390x_movss (code, ins->dreg, ins->sreg2); s390x_pshufd_imm (code, ins->dreg, ins->dreg, mono_simd_shuffle_mask(1, 0, 2, 3)); break; case 2: s390x_pshufd_imm (code, ins->dreg, ins->dreg, mono_simd_shuffle_mask(2, 1, 0, 3)); s390x_movss (code, ins->dreg, ins->sreg2); s390x_pshufd_imm (code, ins->dreg, ins->dreg, mono_simd_shuffle_mask(2, 1, 0, 3)); break; case 3: s390x_pshufd_imm (code, ins->dreg, ins->dreg, mono_simd_shuffle_mask(3, 1, 2, 0)); s390x_movss (code, ins->dreg, ins->sreg2); s390x_pshufd_imm (code, ins->dreg, ins->dreg, mono_simd_shuffle_mask(3, 1, 2, 0)); break; } break; case OP_INSERTX_R8_SLOW: if (ins->inst_c0) amd64_movlhps (code, ins->dreg, ins->sreg2); else s390x_movsd (code, ins->dreg, ins->sreg2); break; case OP_STOREX_MEMBASE_REG: case OP_STOREX_MEMBASE: s390x_movups_membase_reg (code, ins->dreg, ins->inst_offset, ins->sreg1); break; case OP_LOADX_MEMBASE: s390x_movups_reg_membase (code, ins->dreg, ins->sreg1, ins->inst_offset); break; case OP_LOADX_ALIGNED_MEMBASE: s390x_movaps_reg_membase (code, ins->dreg, ins->sreg1, ins->inst_offset); break; case OP_STOREX_ALIGNED_MEMBASE_REG: s390x_movaps_membase_reg (code, ins->dreg, ins->inst_offset, ins->sreg1); break; case OP_STOREX_NTA_MEMBASE_REG: s390x_movntps_reg_membase (code, ins->dreg, ins->sreg1, ins->inst_offset); break; case OP_PREFETCH_MEMBASE: s390x_prefetch_reg_membase (code, ins->backend.arg_info, ins->sreg1, ins->inst_offset); break; case OP_XMOVE: /*FIXME the peephole pass should have killed this*/ if (ins->dreg != ins->sreg1) s390x_movaps (code, ins->dreg, ins->sreg1); break; case OP_XZERO: s390x_pxor (code, ins->dreg, ins->dreg); break; case OP_ICONV_TO_R4_RAW: amd64_movd_xreg_reg_size (code, ins->dreg, ins->sreg1, 4); break; case OP_FCONV_TO_R8_X: s390x_movsd (code, ins->dreg, ins->sreg1); break; case OP_XCONV_R8_TO_I4: s390x_cvttsd2si_reg_xreg_size (code, ins->dreg, ins->sreg1, 4); switch (ins->backend.source_opcode) { case OP_FCONV_TO_I1: amd64_widen_reg (code, ins->dreg, ins->dreg, TRUE, FALSE); break; case OP_FCONV_TO_U1: amd64_widen_reg (code, ins->dreg, ins->dreg, FALSE, FALSE); break; case OP_FCONV_TO_I2: amd64_widen_reg (code, ins->dreg, ins->dreg, TRUE, TRUE); break; case OP_FCONV_TO_U2: amd64_widen_reg (code, ins->dreg, ins->dreg, FALSE, TRUE); break; } break; case OP_EXPAND_I2: s390x_pinsrw_imm (code, ins->dreg, ins->sreg1, 0); s390x_pinsrw_imm (code, ins->dreg, ins->sreg1, 1); s390x_pshufd_imm (code, ins->dreg, ins->dreg, 0); break; case OP_EXPAND_I4: amd64_movd_xreg_reg_size (code, ins->dreg, ins->sreg1, 4); s390x_pshufd_imm (code, ins->dreg, ins->dreg, 0); break; case OP_EXPAND_I8: amd64_movd_xreg_reg_size (code, ins->dreg, ins->sreg1, 8); s390x_pshufd_imm (code, ins->dreg, ins->dreg, 0x44); break; case OP_EXPAND_R4: s390x_movsd (code, ins->dreg, ins->sreg1); s390x_pshufd_imm (code, ins->dreg, ins->dreg, 0); break; case OP_EXPAND_R8: s390x_movsd (code, ins->dreg, ins->sreg1); s390x_pshufd_imm (code, ins->dreg, ins->dreg, 0x44); break; #endif default: g_warning ("unknown opcode " M_PRI_INST " in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__); g_assert_not_reached (); } if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) { g_warning ("wrong maximal instruction length of instruction " M_PRI_INST " (expected %d, got %ld)", mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset); g_assert_not_reached (); } } set_code_cursor (cfg, code); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific registration of lowlevel calls * * Register routines to register optimized lowlevel operations */ void mono_arch_register_lowlevel_calls (void) { } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific patching * @param[in] @cfg - Compilation control block * @param[in] @code - Start of code * @param[in] @target - Target of patch * @param[in] @relo - Relocation type * * Perform patching action */ static void emit_patch_full (MonoCompile *cfg, MonoJumpInfo *ji, guint8 *code, gpointer target, int relo) { guint8 *ip = ji->ip.i + code; switch (relo) { case MONO_R_S390_RELINS : target = S390_RELATIVE(target, ip); ip += 2; s390_patch_rel (ip, (guint64) target); break; case MONO_R_S390_THUNKED : if (cfg) create_thunk(cfg, ip, code, target); else update_thunk(cfg, code, target); break; case MONO_R_S390_DIRECT : S390_EMIT_CALL (ip, target); break; case MONO_R_S390_ADDR : s390_patch_addr (ip, (guint64) target); break; case MONO_R_S390_SWITCH : S390_EMIT_LOAD (ip, target); break; case MONO_R_S390_REL : target = S390_RELATIVE(target, ip); s390_patch_rel (ip, (guint64) target); break; default : g_assert_not_reached(); } } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific patching of instructions and data * * @param[in] @cfg - Compile control block * @param[in] @method - Current method * @param[in] @code - Current code block * @param[in] @ji - Jump information * @param[in] @target - Target of patch * * Process the patch data created during the instruction build process. * This resolves jumps, calls, variables etc. */ void mono_arch_patch_code_new (MonoCompile *cfg, guint8 *code, MonoJumpInfo *ji, gpointer target) { switch (ji->type) { case MONO_PATCH_INFO_IP: case MONO_PATCH_INFO_LDSTR: case MONO_PATCH_INFO_TYPE_FROM_HANDLE: case MONO_PATCH_INFO_LDTOKEN: case MONO_PATCH_INFO_EXC: emit_patch_full (cfg, ji, code, target, MONO_R_S390_ADDR); break; case MONO_PATCH_INFO_BB: case MONO_PATCH_INFO_JIT_ICALL_ADDR: case MONO_PATCH_INFO_JIT_ICALL_ID: case MONO_PATCH_INFO_METHOD: emit_patch_full (cfg, ji, code, target, ji->relocation); break; case MONO_PATCH_INFO_METHOD_JUMP: case MONO_PATCH_INFO_RGCTX_FETCH: case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR: case MONO_PATCH_INFO_ABS: emit_patch_full (cfg, ji, code, target, MONO_R_S390_THUNKED); break; case MONO_PATCH_INFO_SWITCH: emit_patch_full(cfg, ji, code, target, MONO_R_S390_SWITCH); break; case MONO_PATCH_INFO_METHODCONST: case MONO_PATCH_INFO_CLASS: case MONO_PATCH_INFO_IMAGE: case MONO_PATCH_INFO_FIELD: case MONO_PATCH_INFO_IID: case MONO_PATCH_INFO_EXC_NAME: emit_patch_full(cfg, ji, code, target, MONO_R_S390_REL); break; case MONO_PATCH_INFO_NONE: break; default: emit_patch_full (cfg, ji, code, target, MONO_R_S390_RELINS); } } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific prolog generation * * @param[in] @cfg - Compile control block * @returns Location of code code generated * * Create the instruction sequence for entry into a method: * - Determine stack size * - Save preserved registers * - Unload parameters * - Determine if LMF needs saving and generate that sequence */ guint8 * mono_arch_emit_prolog (MonoCompile *cfg) { MonoMethod *method = cfg->method; MonoBasicBlock *bb; MonoMethodSignature *sig; MonoInst *inst; long alloc_size, pos, max_offset, i, cfa_offset = 0; guint8 *code; guint32 size; CallInfo *cinfo; int argsClobbered = 0, lmfOffset, fpOffset = 0; cfg->code_size = 512; if (method->save_lmf) cfg->code_size += 200; cfg->native_code = code = (guint8 *) g_malloc (cfg->code_size); /** * Create unwind information */ mono_emit_unwind_op_def_cfa (cfg, code, STK_BASE, S390_CFA_OFFSET); s390_stmg (code, s390_r6, s390_r15, STK_BASE, S390_REG_SAVE_OFFSET); emit_unwind_regs(cfg, code, s390_r6, s390_r15, S390_REG_SAVE_OFFSET - S390_CFA_OFFSET); if (cfg->arch.bkchain_reg != -1) s390_lgr (code, cfg->arch.bkchain_reg, STK_BASE); /* * If there are local allocations the R11 becomes the frame register */ if (cfg->flags & MONO_CFG_HAS_ALLOCA) { cfg->used_int_regs |= 1 << s390_r11; } /* * Check if FP registers need preserving */ if ((cfg->arch.used_fp_regs & S390_FP_SAVE_MASK) != 0) { for (int i = s390_f8; i <= s390_f15; i++) { if (cfg->arch.used_fp_regs & (1 << i)) fpOffset += sizeof(double); } fpOffset = S390_ALIGN(fpOffset, sizeof(double)); } cfg->arch.fpSize = fpOffset; /* * Calculate stack requirements */ alloc_size = cfg->stack_offset + fpOffset; cfg->stack_usage = cfa_offset = alloc_size; s390_lgr (code, s390_r11, STK_BASE); if (s390_is_imm16 (alloc_size)) { s390_aghi (code, STK_BASE, -alloc_size); } else if (s390_is_imm32 (alloc_size)) { s390_agfi (code, STK_BASE, -alloc_size); } else { int stackSize = alloc_size; while (stackSize > INT_MAX) { s390_agfi (code, STK_BASE, -INT_MAX); stackSize -= INT_MAX; } s390_agfi (code, STK_BASE, -stackSize); } mono_emit_unwind_op_def_cfa_offset (cfg, code, alloc_size + S390_CFA_OFFSET); s390_stg (code, s390_r11, 0, STK_BASE, 0); if (fpOffset > 0) { int stkOffset = 0; s390_lgr (code, s390_r1, s390_r11); s390_aghi (code, s390_r1, -fpOffset); for (int i = s390_f8; i <= s390_f15; i++) { if (cfg->arch.used_fp_regs & (1 << i)) { s390_std (code, i, 0, s390_r1, stkOffset); emit_unwind_regs(cfg, code, 16+i, 16+i, stkOffset+fpOffset - S390_CFA_OFFSET); stkOffset += sizeof(double); } } } if (cfg->frame_reg != STK_BASE) { s390_lgr (code, s390_r11, STK_BASE); mono_emit_unwind_op_def_cfa_reg (cfg, code, cfg->frame_reg); } /* store runtime generic context */ if (cfg->rgctx_var) { g_assert (cfg->rgctx_var->opcode == OP_REGOFFSET); s390_stg (code, MONO_ARCH_RGCTX_REG, 0, cfg->rgctx_var->inst_basereg, cfg->rgctx_var->inst_offset); } #if 0 char *methodName = getenv("MONO_TRACE_METHOD"); if (methodName != NULL) { printf("ns: %s k: %s m: %s\n",method->klass->name_space,method->klass->name,method->name);fflush(stdout); // Tests:set_ip //if ((strcmp(method->klass->name_space,"") == 0) && // (strcmp(method->klass->name,"Tests") == 0) && // (strcmp(method->name, "set_ip") == 0)) { // (strcmp("CancellationToken,TaskCreationOptions,TaskContinuationOptions,TaskScheduler",mono_signature_get_desc(method->signature, FALSE)) != 0)) { if ((strcmp(method->name, methodName) == 0)) { printf("SIGNATURE: %s\n",mono_signature_get_desc(method->signature, FALSE)); fflush(stdout); s390_j (code, 0); } } #endif /* compute max_offset in order to use short forward jumps * we always do it on s390 because the immediate displacement * for jumps is too small */ max_offset = 0; for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { MonoInst *ins; bb->max_offset = max_offset; MONO_BB_FOR_EACH_INS (bb, ins) max_offset += ins_get_size (ins->opcode); } /* load arguments allocated to register from the stack */ sig = mono_method_signature_internal (method); pos = 0; cinfo = cfg->arch.cinfo; if (cinfo->struct_ret) { ArgInfo *ainfo = &cinfo->ret; inst = cfg->vret_addr; inst->backend.size = ainfo->vtsize; if (inst->opcode == OP_REGVAR) s390_lgr (code, inst->dreg, ainfo->reg); else s390_stg (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); } /** * Process the arguments passed to the method */ for (i = 0; i < sig->param_count + sig->hasthis; ++i) { ArgInfo *ainfo = cinfo->args + i; inst = cfg->args [pos]; if (inst->opcode == OP_VTARG_ADDR) inst = inst->inst_left; if (inst->opcode == OP_REGVAR) { if (ainfo->regtype == RegTypeGeneral) s390_lgr (code, inst->dreg, ainfo->reg); else if (ainfo->regtype == RegTypeFP) { if (inst->dreg != ainfo->reg) { s390_ldr (code, inst->dreg, ainfo->reg); } } else if (ainfo->regtype == RegTypeFPR4) { } else if (ainfo->regtype == RegTypeBase) { s390_lgr (code, s390_r13, STK_BASE); s390_aghi (code, s390_r13, alloc_size); s390_lg (code, inst->dreg, 0, s390_r13, ainfo->offset); } else g_assert_not_reached (); if (cfg->verbose_level > 2) g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg)); } else { if (ainfo->regtype == RegTypeGeneral) { if (!((ainfo->reg >= 2) && (ainfo->reg <= 6))) g_assert_not_reached(); switch (ainfo->size) { case 1: s390_stc (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); break; case 2: s390_sth (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); break; case 4: s390_st (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); break; case 8: s390_stg (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); break; } } else if (ainfo->regtype == RegTypeBase) { } else if (ainfo->regtype == RegTypeFP) { s390_std (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); } else if (ainfo->regtype == RegTypeFPR4) { s390_ste (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); } else if (ainfo->regtype == RegTypeStructByVal) { int doffset = inst->inst_offset; size = (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && sig->pinvoke && !sig->marshalling_disabled ? mono_class_native_size(mono_class_from_mono_type_internal (inst->inst_vtype), NULL) : ainfo->size); switch (size) { case 1: if (ainfo->reg != STK_BASE) s390_stc (code, ainfo->reg, 0, inst->inst_basereg, doffset); break; case 2: if (ainfo->reg != STK_BASE) s390_sth (code, ainfo->reg, 0, inst->inst_basereg, doffset); break; case 4: if (ainfo->reg != STK_BASE) s390_st (code, ainfo->reg, 0, inst->inst_basereg, doffset); break; case 8: if (ainfo->reg != STK_BASE) s390_stg (code, ainfo->reg, 0, inst->inst_basereg, doffset); break; default: if (ainfo->reg != STK_BASE) s390_stg (code, ainfo->reg, 0, STK_BASE, doffset); } } else if (ainfo->regtype == RegTypeStructByAddr) { s390_stg (code, ainfo->reg, 0, inst->inst_basereg, inst->inst_offset); } else g_assert_not_reached (); } pos++; } if (method->save_lmf) { /** * Build the MonoLMF structure on the stack - see mini-s390x.h */ lmfOffset = alloc_size - sizeof(MonoLMF); s390_lgr (code, s390_r13, cfg->frame_reg); s390_aghi (code, s390_r13, lmfOffset); /* * Preserve the parameter registers while we fix up the lmf */ s390_stmg (code, s390_r2, s390_r6, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, pregs)); for (i = 0; i < 5; i++) mini_gc_set_slot_type_from_fp (cfg, lmfOffset + MONO_STRUCT_OFFSET (MonoLMF, pregs) + i * sizeof(gulong), SLOT_NOREF); /* * On return from this call r2 have the address of the &lmf */ code = emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_tls_get_lmf_addr_extern)); /* * Set lmf.lmf_addr = jit_tls->lmf */ s390_stg (code, s390_r2, 0, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, lmf_addr)); mini_gc_set_slot_type_from_fp (cfg, lmfOffset + MONO_STRUCT_OFFSET (MonoLMF, lmf_addr), SLOT_NOREF); /* * Get current lmf */ s390_lg (code, s390_r0, 0, s390_r2, 0); /* * Set our lmf as the current lmf */ s390_stg (code, s390_r13, 0, s390_r2, 0); /* * Have our lmf.previous_lmf point to the last lmf */ s390_stg (code, s390_r0, 0, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, previous_lmf)); mini_gc_set_slot_type_from_fp (cfg, lmfOffset + MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), SLOT_NOREF); /* * Save method info */ S390_SET (code, s390_r1, method); s390_stg (code, s390_r1, 0, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, method)); mini_gc_set_slot_type_from_fp (cfg, lmfOffset + MONO_STRUCT_OFFSET (MonoLMF, method), SLOT_NOREF); /* * Save the current IP */ s390_stg (code, STK_BASE, 0, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, ebp)); s390_basr (code, s390_r1, 0); s390_stg (code, s390_r1, 0, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, eip)); mini_gc_set_slot_type_from_fp (cfg, lmfOffset + MONO_STRUCT_OFFSET (MonoLMF, ebp), SLOT_NOREF); mini_gc_set_slot_type_from_fp (cfg, lmfOffset + MONO_STRUCT_OFFSET (MonoLMF, eip), SLOT_NOREF); /* * Save general and floating point registers */ s390_stmg (code, s390_r2, s390_r12, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, gregs) + 2 * sizeof(gulong)); for (i = 0; i < 11; i++) mini_gc_set_slot_type_from_fp (cfg, lmfOffset + MONO_STRUCT_OFFSET (MonoLMF, gregs) + i * sizeof(gulong), SLOT_NOREF); fpOffset = lmfOffset + MONO_STRUCT_OFFSET (MonoLMF, fregs); for (i = 0; i < 16; i++) { s390_std (code, i, 0, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, fregs) + i * sizeof(gulong)); mini_gc_set_slot_type_from_fp (cfg, fpOffset, SLOT_NOREF); fpOffset += sizeof(double); } /* * Restore the parameter registers now that we've set up the lmf */ s390_lmg (code, s390_r2, s390_r6, s390_r13, MONO_STRUCT_OFFSET(MonoLMF, pregs)); } if (cfg->method->save_lmf) argsClobbered = TRUE; /* * Optimize the common case of the first bblock making a call with the same * arguments as the method. This works because the arguments are still in their * original argument registers. */ if (!argsClobbered) { MonoBasicBlock *first_bb = cfg->bb_entry; MonoInst *next; int filter = FILTER_IL_SEQ_POINT; next = mono_bb_first_inst (first_bb, filter); if (!next && first_bb->next_bb) { first_bb = first_bb->next_bb; next = mono_bb_first_inst (first_bb, filter); } if (first_bb->in_count > 1) next = NULL; for (i = 0; next && i < sig->param_count + sig->hasthis; ++i) { ArgInfo *ainfo = cinfo->args + i; gboolean match = FALSE; inst = cfg->args [i]; if (inst->opcode != OP_REGVAR) { switch (ainfo->regtype) { case RegTypeGeneral: { if (((next->opcode == OP_LOAD_MEMBASE) || (next->opcode == OP_LOADI4_MEMBASE)) && next->inst_basereg == inst->inst_basereg && next->inst_offset == inst->inst_offset) { if (next->dreg == ainfo->reg) { NULLIFY_INS (next); match = TRUE; } else { next->opcode = OP_MOVE; next->sreg1 = ainfo->reg; /* Only continue if the instruction doesn't change argument regs */ if (next->dreg == ainfo->reg) match = TRUE; } } break; } default: break; } } else { /* Argument allocated to (non-volatile) register */ switch (ainfo->regtype) { case RegTypeGeneral: if (next->opcode == OP_MOVE && next->sreg1 == inst->dreg && next->dreg == ainfo->reg) { NULLIFY_INS (next); match = TRUE; } break; default: break; } } if (match) { next = mono_inst_next (next, filter); if (!next) break; } } } if (cfg->gen_sdb_seq_points) { MonoInst *seq; /* Initialize ss_tramp_var */ seq = cfg->arch.ss_tramp_var; g_assert (seq->opcode == OP_REGOFFSET); S390_SET (code, s390_r1, (guint64) &ss_trampoline); s390_stg (code, s390_r1, 0, seq->inst_basereg, seq->inst_offset); /* Initialize bp_tramp_var */ seq = cfg->arch.bp_tramp_var; g_assert (seq->opcode == OP_REGOFFSET); S390_SET (code, s390_r1, (guint64) &bp_trampoline); s390_stg (code, s390_r1, 0, seq->inst_basereg, seq->inst_offset); } set_code_cursor (cfg, code); return code; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific epilog generation * * @param[in] @cfg - Compile control block * * Create the instruction sequence for exit from a method */ void mono_arch_emit_epilog (MonoCompile *cfg) { MonoMethod *method = cfg->method; guint8 *code; int max_epilog_size = 96, i; int fpOffset = 0; if (cfg->method->save_lmf) max_epilog_size += 128; code = realloc_code (cfg, max_epilog_size); cfg->has_unwind_info_for_epilog = TRUE; /* Mark the start of the epilog */ mono_emit_unwind_op_mark_loc (cfg, code, 0); /* Save the uwind state which is needed by the out-of-line code */ mono_emit_unwind_op_remember_state (cfg, code); if (method->save_lmf) restoreLMF(code, cfg->frame_reg, cfg->stack_usage); code = backUpStackPtr(cfg, code); mono_emit_unwind_op_def_cfa (cfg, code, STK_BASE, S390_CFA_OFFSET); mono_emit_unwind_op_same_value (cfg, code, STK_BASE); if (cfg->arch.fpSize != 0) { fpOffset = -cfg->arch.fpSize; for (int i=8; i<16; i++) { if (cfg->arch.used_fp_regs & (1 << i)) { s390_ldy (code, i, 0, STK_BASE, fpOffset); mono_emit_unwind_op_same_value (cfg, code, 16+i); fpOffset += sizeof(double); } } } s390_lmg (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET); for (i = s390_r6; i < s390_r15; i++) mono_emit_unwind_op_same_value (cfg, code, i); s390_br (code, s390_r14); /* Restore the unwind state to be the same as before the epilog */ mono_emit_unwind_op_restore_state (cfg, code); /* Round up for start of any thunk entries */ code = (guint8 *) ((((uintptr_t) code + 7) >> 3) << 3); set_code_cursor (cfg, code); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific exception emission * * @param[in] @cfg - Compile control block * * Create the instruction sequence for exception handling */ void mono_arch_emit_exceptions (MonoCompile *cfg) { MonoJumpInfo *patch_info; guint8 *code; int nThrows = 0, exc_count = 0, iExc; guint32 code_size; MonoClass *exc_classes [MAX_EXC]; guint8 *exc_throw_start [MAX_EXC]; for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { if (patch_info->type == MONO_PATCH_INFO_EXC) exc_count++; } code_size = exc_count * 48; code = realloc_code (cfg, code_size); /* * Add code to raise exceptions */ for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { switch (patch_info->type) { case MONO_PATCH_INFO_EXC: { guint8 *ip = patch_info->ip.i + cfg->native_code; MonoClass *exc_class; /* * Patch the branch in epilog to come here */ s390_patch_rel (ip + 2, (guint64) S390_RELATIVE(code,ip)); exc_class = mono_class_load_from_name (mono_defaults.corlib, "System", patch_info->data.name); for (iExc = 0; iExc < nThrows; ++iExc) if (exc_classes [iExc] == exc_class) break; if (iExc < nThrows) { s390_jcl (code, S390_CC_UN, (guint64) exc_throw_start [iExc]); patch_info->type = MONO_PATCH_INFO_NONE; } else { if (nThrows < MAX_EXC) { exc_classes [nThrows] = exc_class; exc_throw_start [nThrows] = code; } /* * Patch the parameter passed to the handler */ S390_SET (code, s390_r2, m_class_get_type_token (exc_class)); /* * Load return address & parameter register */ s390_larl (code, s390_r14, (guint64)S390_RELATIVE((patch_info->ip.i + cfg->native_code + 8), code)); /* * Reuse the current patch to set the jump */ patch_info->type = MONO_PATCH_INFO_JIT_ICALL_ID; patch_info->data.jit_icall_id = MONO_JIT_ICALL_mono_arch_throw_corlib_exception; patch_info->ip.i = code - cfg->native_code; patch_info->relocation = MONO_R_S390_THUNKED; S390_BR_TEMPLATE (code, s390_r1); cfg->thunk_area += THUNK_SIZE; } break; } default: /* do nothing */ break; } } /* Round up for start of any thunk entries */ code = (guint8 *) ((((uintptr_t) code + 7) >> 3) << 3); set_code_cursor (cfg, code); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific finishing of initialization * * Perform any architectural-specific operations at the conclusion of * the initialization phase */ void mono_arch_finish_init (void) { } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific instruction emission for method * * @param[in] @cfg - Compile Control block * @param[in] @cmethod - Current method * @param[in] @fsig - Method signature * @param[in] @args - Arguments to method * @returns Instruction(s) required for architecture * * Provide any architectural shortcuts for specific methods. */ MonoInst * mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) { MonoInst *ins = NULL; int opcode = 0; MonoStackType stack_type = STACK_R8; if (cmethod->klass == mono_class_try_get_math_class ()) { // unary double if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R8) { if (strcmp (cmethod->name, "Abs") == 0) { opcode = OP_ABS; } else if (strcmp (cmethod->name, "Ceiling") == 0) { opcode = OP_CEIL; } else if (strcmp (cmethod->name, "Floor") == 0) { opcode = OP_FLOOR; } else if (strcmp (cmethod->name, "Round") == 0) { opcode = OP_ROUND; } else if (strcmp (cmethod->name, "Sqrt") == 0) { opcode = OP_SQRT; } else if (strcmp (cmethod->name, "Truncate") == 0) { opcode = OP_TRUNC; } } // unary float (overloaded) else if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R4) { if (strcmp (cmethod->name, "Abs") == 0) { opcode = OP_ABSF; stack_type = STACK_R4; } } // binary double else if (fsig->param_count == 2 && fsig->params [0]->type == MONO_TYPE_R8 && fsig->params [1]->type == MONO_TYPE_R8) { if (strcmp (cmethod->name, "CopySign") == 0) { opcode = OP_FCOPYSIGN; } } } else if (cmethod->klass == mono_class_try_get_mathf_class ()) { if (fsig->param_count == 1) { stack_type = STACK_R4; if (strcmp (cmethod->name, "Abs") == 0) { opcode = OP_ABSF; stack_type = STACK_R4; } else if (strcmp (cmethod->name, "Ceiling") == 0) { opcode = OP_CEILF; stack_type = STACK_R4; } else if (strcmp (cmethod->name, "Floor") == 0) { opcode = OP_FLOORF; stack_type = STACK_R4; } else if (strcmp (cmethod->name, "Sqrt") == 0) { opcode = OP_SQRTF; stack_type = STACK_R4; } else if (strcmp (cmethod->name, "Truncate") == 0) { opcode = OP_TRUNCF; stack_type = STACK_R4; } } } if (opcode) { MONO_INST_NEW (cfg, ins, opcode); ins->type = stack_type; ins->dreg = mono_alloc_freg (cfg); ins->sreg1 = args [0]->dreg; if (fsig->param_count > 1) { ins->sreg2 = args [1]->dreg; } g_assert (fsig->param_count <= 2); MONO_ADD_INS (cfg->cbb, ins); } return ins; } /*========================= End of Function ========================*/ /** * * @brief Decompose opcode into a System z operation * * @param[in] @cfg - Compile Control block * @param[in] @ins - Mono Instruction * * Substitute a System z instruction for a Mono operation. */ void mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins) { /* * Have to rename these to avoid being decomposed normally, since the normal * decomposition does not work on S390. */ switch (ins->opcode) { case OP_ISUB_OVF: ins->opcode = OP_S390_ISUB_OVF; break; case OP_ISUB_OVF_UN: ins->opcode = OP_S390_ISUB_OVF_UN; break; case OP_IADD_OVF: ins->opcode = OP_S390_IADD_OVF; break; case OP_IADD_OVF_UN: ins->opcode = OP_S390_IADD_OVF_UN; break; case OP_LADD_OVF: ins->opcode = OP_S390_LADD_OVF; break; case OP_LADD_OVF_UN: ins->opcode = OP_S390_LADD_OVF_UN; break; case OP_LSUB_OVF: ins->opcode = OP_S390_LSUB_OVF; break; case OP_LSUB_OVF_UN: ins->opcode = OP_S390_LSUB_OVF_UN; break; default: break; } } /*========================= End of Function ========================*/ /** * * @brief Determine the cost of allocation a variable * * @param[in] @cfg - Compile Control block * @param[in] @vmv - Mono Method Variable * @returns Cost (hardcoded on s390x to 2) * * Determine the cost, in the number of memory references, of the action * of allocating the variable VMV into a register during global register * allocation. * */ guint32 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv) { /* FIXME: */ return 2; } /*========================= End of Function ========================*/ /** * * @brief Architectural specific register window flushing * * Not applicable for s390x so we just do nothing * */ void mono_arch_flush_register_windows (void) { } /*========================= End of Function ========================*/ /** * * @brief Architectural specific check if value may be immediate * * @param[in] @opcode - Operation code * @param[in] @imm_opcode - Immediate operation code * @param[in] @imm - Value to be examined * @returns True if it is a valid immediate value * * Determine if operand qualifies as an immediate value. For s390x * this is a value in the range -2**32/2**32-1 * */ gboolean mono_arch_is_inst_imm (int opcode, int imm_opcode, gint64 imm) { return s390_is_imm32 (imm); } /*========================= End of Function ========================*/ /** * * @brief Architectural specific patch offset value for AOT * * @param[in] @code - Location of code to check * @returns Offset * * Dummy entry point if/when s390x supports AOT. */ guint32 mono_arch_get_patch_offset (guint8 *code) { return 0; } /*========================= End of Function ========================*/ /** * * @brief Architectural specific returning of register from context * * @param[in] @ctx - Mono context * @param[in] @reg - Register number to be returned * @returns Contents of the register from the context * * Return a register from the context. */ host_mgreg_t mono_arch_context_get_int_reg (MonoContext *ctx, int reg) { return ctx->uc_mcontext.gregs[reg]; } host_mgreg_t* mono_arch_context_get_int_reg_address (MonoContext *ctx, int reg) { return &ctx->uc_mcontext.gregs[reg]; } /*========================= End of Function ========================*/ /** * * @brief Architectural specific setting of a register in the context * * @param[in] @ctx - Mono context * @param[in] @reg - Register number to be returned * @param[in] @val - Value to be set * * Set the specified register in the context with the value passed */ void mono_arch_context_set_int_reg (MonoContext *ctx, int reg, host_mgreg_t val) { ctx->uc_mcontext.gregs[reg] = val; } /*========================= End of Function ========================*/ /** * * @brief Architectural specific returning of the "this" value from context * * @param[in] @ctx - Mono context * @param[in] @code - Current location * @returns Pointer to the "this" object * * Extract register 2 from the context as for s390x this is where the * this parameter is passed */ gpointer mono_arch_get_this_arg_from_call (host_mgreg_t *regs, guint8 *code) { return (gpointer) regs [s390_r2]; } /*========================= End of Function ========================*/ /** * * @brief Delegation trampoline processing * * @param[in] @info - Trampoline information * @param[in] @has_target - Use target from delegation * @param[in] @param_count - Count of parameters * @param[in] @aot - AOT indicator * @returns Next instruction location * * Process the delegation trampolines */ static guint8 * get_delegate_invoke_impl (MonoTrampInfo **info, gboolean has_target, MonoMethodSignature *sig, gboolean aot) { guint8 *code, *start; if (has_target) { int size = 32; start = code = (guint8 *) mono_global_codeman_reserve (size); /* Replace the this argument with the target */ s390_lg (code, s390_r1, 0, s390_r2, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr)); s390_lg (code, s390_r2, 0, s390_r2, MONO_STRUCT_OFFSET (MonoDelegate, target)); s390_br (code, s390_r1); g_assert ((code - start) <= size); mono_arch_flush_icache (start, size); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE, NULL)); } else { int size, i, offset = S390_MINIMAL_STACK_SIZE, iReg = s390_r2; CallInfo *cinfo = get_call_info (NULL, sig); size = 32 + sig->param_count * 8; start = code = (guint8 *) mono_global_codeman_reserve (size); s390_lg (code, s390_r1, 0, s390_r2, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr)); /* slide down the arguments */ for (i = 0; i < sig->param_count; ++i) { switch(cinfo->args[i].regtype) { case RegTypeGeneral : if (iReg < S390_LAST_ARG_REG) { s390_lgr (code, iReg, (iReg + 1)); } else { s390_lg (code, iReg, 0, STK_BASE, offset); } iReg++; break; default : s390_mvc (code, sizeof(uintptr_t), STK_BASE, offset, STK_BASE, offset+sizeof(uintptr_t)); offset += sizeof(uintptr_t); } } s390_br (code, s390_r1); g_free (cinfo); g_assert ((code - start) <= size); mono_arch_flush_icache (start, size); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE, NULL)); } if (has_target) { *info = mono_tramp_info_create ("delegate_invoke_impl_has_target", start, code - start, NULL, NULL); } else { char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count); *info = mono_tramp_info_create (name, start, code - start, NULL, NULL); g_free (name); } return start; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific delegation trampolines processing * * @returns List of trampolines * * Return a list of MonoTrampInfo structures for the delegate invoke impl trampolines. */ GSList* mono_arch_get_delegate_invoke_impls (void) { GSList *res = NULL; MonoTrampInfo *info; get_delegate_invoke_impl (&info, TRUE, 0, TRUE); res = g_slist_prepend (res, info); #if 0 for (int i = 0; i <= MAX_ARCH_DELEGATE_PARAMS; ++i) { get_delegate_invoke_impl (&info, FALSE, NULL, TRUE); res = g_slist_prepend (res, info); } #endif return res; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific delegation trampoline processing * * @param[in] @sig - Method signature * @param[in] @has_target - Whether delegation contains a target * @returns Trampoline * * Return a pointer to a delegation trampoline */ gpointer mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target) { guint8 *code, *start; if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS) return NULL; /* FIXME: Support more cases */ if (MONO_TYPE_ISSTRUCT (mini_get_underlying_type (sig->ret))) return NULL; if (has_target) { static guint8* cached = NULL; if (cached) return cached; if (mono_ee_features.use_aot_trampolines) { start = (guint8 *) mono_aot_get_trampoline ("delegate_invoke_impl_has_target"); } else { MonoTrampInfo *info; start = get_delegate_invoke_impl (&info, TRUE, sig, FALSE); mono_tramp_info_register (info, NULL); } mono_memory_barrier (); cached = start; } else { static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL}; int i; if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS) return NULL; for (i = 0; i < sig->param_count; ++i) if (!mono_is_regsize_var (sig->params [i])) return NULL; code = cache [sig->param_count]; if (code) return code; if (mono_ee_features.use_aot_trampolines) { char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count); start = (guint8 *) mono_aot_get_trampoline (name); g_free (name); } else { MonoTrampInfo *info; start = get_delegate_invoke_impl (&info, FALSE, sig, FALSE); mono_tramp_info_register (info, NULL); } mono_memory_barrier (); cache [sig->param_count] = start; } return start; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific delegation virtual trampoline processing * * @param[in] @sig - Method signature * @param[in] @method - Method * @param[in] @offset - Offset into vtable * @param[in] @load_imt_reg - Whether to load the LMT register * @returns Trampoline * * Return a pointer to a delegation virtual trampoline */ gpointer mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg) { guint8 *code, *start; int size = 40; start = code = (guint8 *) mono_global_codeman_reserve (size); /* * Replace the "this" argument with the target */ s390_lgr (code, s390_r1, s390_r2); s390_lg (code, s390_r2, 0, s390_r1, MONO_STRUCT_OFFSET(MonoDelegate, target)); /* * Load the IMT register, if needed */ if (load_imt_reg) { s390_lg (code, MONO_ARCH_IMT_REG, 0, s390_r1, MONO_STRUCT_OFFSET(MonoDelegate, method)); } /* * Load the vTable */ s390_lg (code, s390_r1, 0, s390_r2, MONO_STRUCT_OFFSET(MonoObject, vtable)); if (offset != 0) { s390_agfi(code, s390_r1, offset); } s390_lg (code, s390_r1, 0, s390_r1, 0); s390_br (code, s390_r1); mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE, NULL)); return(start); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific build of IMT trampoline * * @param[in] @vtable - Mono VTable * @param[in] @domain - Mono Domain * @param[in] @imt_entries - List of IMT check items * @param[in] @count - Count of items * @param[in] @fail_tramp - Pointer to a failure trampoline * @returns Trampoline * * Return a pointer to an IMT trampoline */ gpointer mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp) { int i; int size = 0; guchar *code, *start; MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); for (i = 0; i < count; ++i) { MonoIMTCheckItem *item = imt_entries [i]; if (item->is_equals) { if (item->check_target_idx) { if (!item->compare_done) item->chunk_size += CMP_SIZE + JUMP_SIZE; if (item->has_target_code) item->chunk_size += BR_SIZE + JUMP_SIZE + LOADCON_SIZE; else item->chunk_size += BR_SIZE + JUMP_SIZE + LOADCON_SIZE + LOAD_SIZE; } else { if (fail_tramp) { item->chunk_size += CMP_SIZE + 2 * BR_SIZE + JUMP_SIZE + 2 * LOADCON_SIZE; if (!item->has_target_code) item->chunk_size += LOAD_SIZE; } else { item->chunk_size += LOADCON_SIZE + LOAD_SIZE + BR_SIZE; #if ENABLE_WRONG_METHOD_CHECK item->chunk_size += CMP_SIZE + JUMP_SIZE; #endif } } } else { item->chunk_size += CMP_SIZE + JUMP_SIZE; imt_entries [item->check_target_idx]->compare_done = TRUE; } size += item->chunk_size; } if (fail_tramp) { code = (guint8 *)mini_alloc_generic_virtual_trampoline (vtable, size); } else { code = mono_mem_manager_code_reserve (mem_manager, size); } start = code; for (i = 0; i < count; ++i) { MonoIMTCheckItem *item = imt_entries [i]; item->code_target = (guint8 *) code; if (item->is_equals) { if (item->check_target_idx) { if (!item->compare_done) { S390_SET (code, s390_r0, item->key); s390_cgr (code, s390_r0, MONO_ARCH_IMT_REG); } item->jmp_code = (guint8*) code; s390_jcl (code, S390_CC_NE, 0); if (item->has_target_code) { S390_SET (code, s390_r1, item->value.target_code); } else { S390_SET (code, s390_r1, (&(vtable->vtable [item->value.vtable_slot]))); s390_lg (code, s390_r1, 0, s390_r1, 0); } s390_br (code, s390_r1); } else { if (fail_tramp) { gint64 target; S390_SET (code, s390_r0, item->key); s390_cgr (code, s390_r0, MONO_ARCH_IMT_REG); item->jmp_code = (guint8*) code; s390_jcl (code, S390_CC_NE, 0); if (item->has_target_code) { S390_SET (code, s390_r1, item->value.target_code); } else { g_assert (vtable); S390_SET (code, s390_r1, (&(vtable->vtable [item->value.vtable_slot]))); s390_lg (code, s390_r1, 0, s390_r1, 0); } s390_br (code, s390_r1); target = (gint64) S390_RELATIVE(code, item->jmp_code); s390_patch_rel(item->jmp_code+2, target); S390_SET (code, s390_r1, fail_tramp); s390_br (code, s390_r1); item->jmp_code = NULL; } else { /* enable the commented code to assert on wrong method */ #if ENABLE_WRONG_METHOD_CHECK g_assert_not_reached (); #endif S390_SET (code, s390_r1, (&(vtable->vtable [item->value.vtable_slot]))); s390_lg (code, s390_r1, 0, s390_r1, 0); s390_br (code, s390_r1); } } } else { S390_SET (code, s390_r0, item->key); s390_cgr (code, MONO_ARCH_IMT_REG, s390_r0); item->jmp_code = (guint8 *) code; s390_jcl (code, S390_CC_GE, 0); } } /* * patch the branches to get to the target items */ for (i = 0; i < count; ++i) { MonoIMTCheckItem *item = imt_entries [i]; if (item->jmp_code) { if (item->check_target_idx) { gint64 offset; offset = (gint64) S390_RELATIVE(imt_entries [item->check_target_idx]->code_target, item->jmp_code); s390_patch_rel ((guchar *) item->jmp_code + 2, (guint64) offset); } } } mono_arch_flush_icache ((guint8*)start, (code - start)); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE, NULL)); if (!fail_tramp) UnlockedAdd (&mono_stats.imt_trampolines_size, code - start); g_assert (code - start <= size); mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), mem_manager); return (start); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific return of pointer to IMT method * * @param[in] @regs - Context registers * @param[in] @code - Current location * @returns Pointer to IMT method * * Extract the value of the IMT register from the context */ MonoMethod* mono_arch_find_imt_method (host_mgreg_t *regs, guint8 *code) { return ((MonoMethod *) regs [MONO_ARCH_IMT_REG]); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific return of pointer static call vtable. * * @param[in] @regs - Context registers * @param[in] @code - Current location * @returns Pointer to static call vtable * * Extract the value of the RGCTX register from the context which * points to the static call vtable. */ MonoVTable* mono_arch_find_static_call_vtable (host_mgreg_t *regs, guint8 *code) { return (MonoVTable*)(gsize) regs [MONO_ARCH_RGCTX_REG]; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific return of unwind bytecode for DWARF CIE * * @returns Unwind byte code * * Returns the unwind bytecode for DWARF CIE */ GSList* mono_arch_get_cie_program (void) { GSList *l = NULL; mono_add_unwind_op_def_cfa (l, 0, 0, STK_BASE, S390_CFA_OFFSET); return(l); } /*========================= End of Function ========================*/ #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED /** * * @brief Architecture-specific setting of a breakpoint * * @param[in] @ji - Mono JIT Information * @param[in] @ip - Insruction pointer * * Set a breakpoint at the native code corresponding to JI at NATIVE_OFFSET. * The location should contain code emitted by OP_SEQ_POINT. */ void mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip) { guint8 *bp = ip; /* IP should point to a LGHI R1,0 */ g_assert (bp[0] == 0xa7); /* Replace it with a LGHI R1,1 */ s390_lghi (bp, s390_r1, 1); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific clearing of a breakpoint * * @param[in] @ji - Mono JIT Information * @param[in] @ip - Insruction pointer * * Replace the breakpoint with a no-operation. */ void mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip) { guint8 *bp = ip; /* IP should point to a LGHI R1,1 */ g_assert (bp[0] == 0xa7); /* Replace it with a LGHI R1,0 */ s390_lghi (bp, s390_r1, 0); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific check if this is a breakpoint event * * @param[in] @info - Signal information * @param[in] @sigctx - Signal context * @returns True if this is a breakpoint event * * We use soft breakpoints so always return FALSE */ gboolean mono_arch_is_breakpoint_event (void *info, void *sigctx) { /* We use soft breakpoints on s390x */ return FALSE; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific skip of a breakpoint * * @param[in] @ctx - Mono Context * @param[in] @ji - Mono JIT information * * We use soft breakpoints so this is a no-op */ void mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji) { g_assert_not_reached (); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific start of single stepping * * Unprotect the trigger page to enable single stepping */ void mono_arch_start_single_stepping (void) { ss_trampoline = mini_get_single_step_trampoline(); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific stop of single stepping * * Write-protect the trigger page to disable single stepping */ void mono_arch_stop_single_stepping (void) { ss_trampoline = NULL; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific check if single stepping event * * @param[in] @info - Signal information * @param[in] @sigctx - Signal context * @returns True if this is a single stepping event * * Return whether the machine state in sigctx corresponds to a single step event. * On s390x we use soft breakpoints so return FALSE */ gboolean mono_arch_is_single_step_event (void *info, void *sigctx) { /* We use soft breakpoints on s390x */ return FALSE; } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific skip of a single stepping event * * @param[in] @ctx - Mono Context * * Modify the ctx so the IP is placed after the single step trigger * instruction, so that the instruction is not executed again. * On s390x we use soft breakpoints so we shouldn't get here */ void mono_arch_skip_single_step (MonoContext *ctx) { g_assert_not_reached(); } /*========================= End of Function ========================*/ /** * * @brief Architecture-specific creation of sequence point information * * @param[in] @domain - Mono Domain * @param[in] @code - Current location pointer * @returns Sequence Point Information * * Return a pointer to a data struction which is used by the sequence * point implementation in AOTed code. A no-op on s390x until AOT is * ever supported. */ SeqPointInfo * mono_arch_get_seq_point_info (guint8 *code) { SeqPointInfo *info; MonoJitInfo *ji; MonoJitMemoryManager *jit_mm; jit_mm = get_default_jit_mm (); jit_mm_lock (jit_mm); info = (SeqPointInfo *)g_hash_table_lookup (jit_mm->arch_seq_points, code); jit_mm_unlock (jit_mm); if (!info) { ji = mini_jit_info_table_find (code); g_assert (ji); // FIXME: Optimize the size info = (SeqPointInfo *)g_malloc0 (sizeof (SeqPointInfo) + (ji->code_size * sizeof (gpointer))); info->ss_tramp_addr = &ss_trampoline; jit_mm_lock (jit_mm); g_hash_table_insert (jit_mm->arch_seq_points, code, info); jit_mm_unlock (jit_mm); } return info; } /*========================= End of Function ========================*/ #endif /** * * @brief Architecture-specific check of supported operation codes * * @param[in] @opcode - Operation code to be checked * @returns True if operation code is supported * * Check if a mono operation is supported in hardware. */ gboolean mono_arch_opcode_supported (int opcode) { switch (opcode) { case OP_ATOMIC_ADD_I4: case OP_ATOMIC_ADD_I8: case OP_ATOMIC_EXCHANGE_I4: case OP_ATOMIC_EXCHANGE_I8: return TRUE; default: return FALSE; } } /*========================= End of Function ========================*/ #ifndef DISABLE_JIT /** * * @brief Architecture-specific check of tailcall support * * @param[in] @cfg - Mono Compile control block * @param[in] @caller_sig - Signature of caller * @param[in] @callee_sig - Signature of callee * @param[in] @virtual_ - Whether this a virtual call * @returns True if the tailcall operation is supported * * Check if a tailcall may be made from caller to callee based on a * number of conditions including parameter types and stack sizes */ gboolean mono_arch_tailcall_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig, MonoMethodSignature *callee_sig, gboolean virtual_) { g_assert (caller_sig); g_assert (callee_sig); CallInfo *caller_info = get_call_info (NULL, caller_sig); CallInfo *callee_info = get_call_info (NULL, callee_sig); gboolean res = IS_SUPPORTED_TAILCALL (callee_info->stack_usage <= caller_info->stack_usage); // Any call that would result in parameters being placed on the stack cannot be "tailed" as it may // result in the callers parameter variables being overwritten. ArgInfo const * const ainfo = callee_info->args + callee_sig->hasthis; for (int i = 0; res && i < callee_sig->param_count; ++i) { switch(ainfo[i].regtype) { case RegTypeGeneral : // R6 is both used as argument register and call-saved // This means we cannot use a tail call if R6 is needed if (ainfo[i].reg == S390_LAST_ARG_REG) res = FALSE; else res = TRUE; break; case RegTypeFP : case RegTypeFPR4 : case RegTypeStructByValInFP : res = TRUE; break; case RegTypeBase : res = FALSE; break; case RegTypeStructByAddr : if (ainfo[i].reg == STK_BASE || ainfo[i].reg == S390_LAST_ARG_REG) res = FALSE; else res = TRUE; break; case RegTypeStructByVal : if (ainfo[i].reg == STK_BASE || ainfo[i].reg == S390_LAST_ARG_REG) res = FALSE; else { switch(ainfo[i].size) { case 0: case 1: case 2: case 4: case 8: res = TRUE; break; default: res = FALSE; } } break; } } g_free (caller_info); g_free (callee_info); return(res); } /*========================= End of Function ========================*/ #endif /** * * @brief Architecture-specific load function * * @param[in] @jit_call_id - JIT callee identifier * @returns Pointer to load function trampoline * * A no-operation on s390x until if/when it supports AOT. */ gpointer mono_arch_load_function (MonoJitICallId jit_icall_id) { return NULL; } /*========================= End of Function ========================*/ /** * * @brief Emit call to thunked code * * @param[in] @cfg - configuration data * @param[inout] @code - where to emit call * @param[in] @call - call instruction * @returns Pointer to next code area * */ static __inline__ guint8* emit_call (MonoCompile *cfg, guint8 *code, MonoJumpInfoType type, gconstpointer target) { mono_add_patch_info_rel (cfg, code-cfg->native_code, type, target, MONO_R_S390_THUNKED); S390_CALL_TEMPLATE (code, s390_r14); cfg->thunk_area += THUNK_SIZE; return code; } /*========================= End of Function ========================*/ /** * * @brief Emit thunk for an indirect call * * @param[inout] @code - where to emit thunk * @param[in] @target - thunk target * @returns Pointer to next code area * */ static guint8* emit_thunk (guint8 *code, gconstpointer target) { *(guint64*)code = (guint64)target; code += sizeof (guint64); return code; } /*========================= End of Function ========================*/ /** * * @brief Create thunk * * @param[in] @cfg - Compiler configuration * @param[inout] @code - where to emit thunk * @param[in] @target - thunk target * * Create a new thunk * */ static void create_thunk (MonoCompile *cfg, guint8 *ip, guint8 *code, gpointer target) { guint8 *thunks; int thunks_size; /* * This can be called multiple times during JITting, * save the current position in cfg->arch to avoid * doing a O(n^2) search. */ if (!cfg->arch.thunks) { cfg->arch.thunks = cfg->thunks; cfg->arch.thunks_size = cfg->thunk_area; } thunks = (guint8 *) cfg->arch.thunks; thunks_size = cfg->arch.thunks_size; if (!thunks_size) { g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, mono_method_full_name (cfg->method, TRUE)); g_assert_not_reached (); } g_assert (*(guint64 *)thunks == 0); emit_thunk (thunks, target); cfg->arch.thunks += THUNK_SIZE; cfg->arch.thunks_size -= THUNK_SIZE; S390_EMIT_CALL(ip, thunks); } /*========================= End of Function ========================*/ /** * * @brief Update thunk * * @param[in] @cfg - Compiler configuration * @param[inout] @code - where to emit thunk * @param[in] @target - thunk target * * Update an existing thunk * */ static void update_thunk (MonoCompile *cfg, guint8 *code, gpointer target) { MonoJitInfo *ji; MonoThunkJitInfo *info; guint8 *thunks; guint8 *orig_target; guint8 *target_thunk; int thunks_size; ji = mini_jit_info_table_find ((char*)code); g_assert (ji); info = mono_jit_info_get_thunk_info (ji); g_assert (info); thunks = (guint8*)ji->code_start + info->thunks_offset; thunks_size = info->thunks_size; /* * We're pointing at the start of jump to thunk, * but mono_arch_get_call_target expects we're pointing * after the branch so we adjust */ orig_target = mono_arch_get_call_target (code + 6); target_thunk = NULL; if (orig_target >= thunks && orig_target < thunks + thunks_size) { /* The call already points to a thunk, because of trampolines etc. */ target_thunk = orig_target; } else { g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, cfg ? mono_method_full_name (cfg->method, TRUE) : mono_method_full_name (jinfo_get_method (ji), TRUE)); g_assert_not_reached (); } emit_thunk (target_thunk, target); } /*========================= End of Function ========================*/