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 ========================*/