merge -r 53370:58178
[mono.git] / mono / mini / mini-s390.c
index 101c5c7ec7625c64e60c107efa415b0f23753f03..6ed6b83abd3d943751fda1d640eff81d70f4f388 100644 (file)
@@ -19,7 +19,7 @@
 /*------------------------------------------------------------------*/
 
 #define NOT_IMPLEMENTED(x) \
-        g_error ("FIXME: %s is not yet implemented. (trampoline)", x);
+        g_error ("FIXME: %s is not yet implemented.", x);
 
 #define EMIT_COND_BRANCH(ins,cond)                                                     \
 {                                                                                      \
@@ -96,23 +96,59 @@ if (ins->flags & MONO_INST_BRLABEL) {                                                       \
                s390_jcl (code, cond, 0);                               \
        } while (0); 
 
+#define CHECK_SRCDST_COM                                               \
+       if (ins->dreg == ins->sreg2) {                                  \
+               src2 = ins->sreg1;                                      \
+       } else {                                                        \
+               src2 = ins->sreg2;                                      \
+               if (ins->dreg != ins->sreg1) {                          \
+                       s390_lr  (code, ins->dreg, ins->sreg1);         \
+               }                                                       \
+       }
+
+#define CHECK_SRCDST_NCOM                                              \
+       if (ins->dreg == ins->sreg2) {                                  \
+               src2 = s390_r13;                                        \
+               s390_lr  (code, s390_r13, ins->sreg2);                  \
+       } else {                                                        \
+               src2 = ins->sreg2;                                      \
+       }                                                               \
+       if (ins->dreg != ins->sreg1) {                                  \
+               s390_lr  (code, ins->dreg, ins->sreg1);                 \
+       }
+
+#define CHECK_SRCDST_NCOM_F                                            \
+       if (ins->dreg == ins->sreg2) {                                  \
+               src2 = s390_f15;                                        \
+               s390_ldr (code, s390_r13, ins->sreg2);                  \
+       } else {                                                        \
+               src2 = ins->sreg2;                                      \
+       }                                                               \
+       if (ins->dreg != ins->sreg1) {                                  \
+               s390_ldr (code, ins->dreg, ins->sreg1);                 \
+       }
+
 #undef DEBUG
 #define DEBUG(a) if (cfg->verbose_level > 1) a
-#define reg_is_freeable(r) ((r) >= 3 && (r) <= 10)
-#define freg_is_freeable(r) ((r) >= 1 && (r) <= 14)
+
+#define MAX_EXC        16
 
 /*----------------------------------------*/
-/* use s390_r3-s390_r10 as temp registers */
+/* use s390_r2-s390_r5 as temp registers  */
 /*----------------------------------------*/
-#define S390_CALLER_REGS  (0x03f8)
+#define S390_CALLER_REGS  (0x10fc)
+#define reg_is_freeable(r) (S390_CALLER_REGS & 1 << (r))
 
 /*----------------------------------------*/
-/* use s390_f2-s390_f14 as temp registers */
+/* use s390_f1/s390_f3-s390_f15 as temps  */
 /*----------------------------------------*/
-#define S390_CALLER_FREGS (0x73f8)
+#define S390_CALLER_FREGS (0xfffa)
+#define freg_is_freeable(r) ((r) >= 1 && (r) <= 14)
 
 #define S390_TRACE_STACK_SIZE (5*sizeof(gint32)+3*sizeof(gdouble))
 
+#define MAX (a, b) ((a) > (b) ? (a) : (b))
+
 /*========================= End of Defines =========================*/
 
 /*------------------------------------------------------------------*/
@@ -193,6 +229,7 @@ typedef struct {
        guint32 stack_usage;
        guint32 struct_ret;
        ArgInfo ret;
+       ArgInfo sigCookie;
        ArgInfo args [1];
 } CallInfo;
 
@@ -207,13 +244,15 @@ typedef struct {
 /*                   P r o t o t y p e s                            */
 /*------------------------------------------------------------------*/
 
-static guint32 *emit_memcpy (guint8 *, int, int, int, int, int);
+static guint32 * emit_memcpy (guint8 *, int, int, int, int, int);
 static void indent (int);
+static guint8 * backUpStackPtr(MonoCompile *, guint8 *);
 static void decodeParm (MonoType *, void *, int);
 static void enter_method (MonoMethod *, RegParm *, char *);
 static void leave_method (MonoMethod *, ...);
 static gboolean is_regsize_var (MonoType *);
-static void add_general (guint *, size_data *, ArgInfo *, gboolean);
+static inline void add_general (guint *, size_data *, ArgInfo *, gboolean);
+static inline void add_float (guint *, size_data *, ArgInfo *);
 static CallInfo * calculate_sizes (MonoMethodSignature *, size_data *, gboolean);
 static void peephole_pass (MonoCompile *, MonoBasicBlock *);
 static int mono_spillvar_offset (MonoCompile *, int);
@@ -245,6 +284,20 @@ static int indent_level = 0;
 
 static const char*const * ins_spec = s390;
 
+static gboolean tls_offset_inited = FALSE;
+
+static int appdomain_tls_offset = -1,
+                  lmf_tls_offset = -1,
+           thread_tls_offset = -1;
+
+#if 0
+
+extern __thread MonoDomain *tls_appdomain;
+extern __thread MonoThread *tls_current_object;
+extern __thread gpointer   mono_lmf_addr;
+               
+#endif
+
 /*====================== End of Global Variables ===================*/
 
 /*------------------------------------------------------------------*/
@@ -386,6 +439,60 @@ mono_arch_get_argument_info (MonoMethodSignature *csig,
 
 /*========================= End of Function ========================*/
 
+/*------------------------------------------------------------------*/
+/*                                                                  */
+/* Name                - retFitsInReg.                                     */
+/*                                                                  */
+/* Function    - Determines if a value can be returned in one or   */
+/*                two registers.                                    */
+/*                                                                  */
+/*------------------------------------------------------------------*/
+
+static inline gboolean
+retFitsInReg(guint32 size)
+{
+       switch (size) {
+               case 0:
+               case 1:
+               case 2:
+               case 4:
+               case 8:
+                       return (TRUE);
+               break;
+               default:
+                       return (FALSE);
+       }
+}
+
+/*========================= End of Function ========================*/
+
+/*------------------------------------------------------------------*/
+/*                                                                  */
+/* Name                - backStackPtr.                                     */
+/*                                                                  */
+/* Function    - Restore Stack Pointer to previous frame.          */
+/*                                                                  */
+/*------------------------------------------------------------------*/
+
+static inline guint8 *
+backUpStackPtr(MonoCompile *cfg, guint8 *code)
+{
+       int stackSize = cfg->stack_usage;
+
+       if (s390_is_uimm16 (cfg->stack_usage)) {
+               s390_ahi  (code, STK_BASE, cfg->stack_usage);
+       } else { 
+               while (stackSize > 32767) {
+                       s390_ahi  (code, STK_BASE, 32767);
+                       stackSize -= 32767;
+               }
+               s390_ahi  (code, STK_BASE, stackSize);
+       }
+       return (code);
+}
+
+/*========================= End of Function ========================*/
+
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* Name                - indent                                            */
@@ -426,7 +533,7 @@ decodeParm(MonoType *type, void *curParm, int size)
        if (type->byref) {
                printf("[BYREF:%p], ", *((char **) curParm));
        } else {
-               simpleType = type->type;
+               simpleType = mono_type_get_underlying_type(type)->type;
 enum_parmtype:
                switch (simpleType) {
                        case MONO_TYPE_I :
@@ -442,22 +549,25 @@ enum_parmtype:
                                printf ("[CHAR:%p], ", *((int *) curParm));
                                break;
                        case MONO_TYPE_I1 :
-                               printf ("[INT1:%d], ", *((int *) curParm+3));
+                               printf ("[INT1:%d], ", *((int *) curParm));
                                break; 
                        case MONO_TYPE_I2 :
-                               printf ("[INT2:%d], ", *((int *) curParm+2));
+                               printf ("[INT2:%d], ", *((int *) curParm));
                                break; 
                        case MONO_TYPE_I4 :
                                printf ("[INT4:%d], ", *((int *) curParm));
                                break; 
                        case MONO_TYPE_U1 :
-                               printf ("[UINT1:%ud], ", *((unsigned int *) curParm));
+                               printf ("[UINT1:%u], ", *((unsigned int *) curParm));
                                break; 
                        case MONO_TYPE_U2 :
-                               printf ("[UINT2:%ud], ", *((unsigned int *) curParm));
+                               printf ("[UINT2:%u], ", *((guint16 *) curParm));
                                break; 
                        case MONO_TYPE_U4 :
-                               printf ("[UINT4:%ud], ", *((unsigned int *) curParm));
+                               printf ("[UINT4:%u], ", *((guint32 *) curParm));
+                               break; 
+                       case MONO_TYPE_U8 :
+                               printf ("[UINT8:%llu], ", *((guint64 *) curParm));
                                break; 
                        case MONO_TYPE_STRING : {
                                MonoString *s = *((MonoString **) curParm);
@@ -473,7 +583,7 @@ enum_parmtype:
                        case MONO_TYPE_OBJECT : {
                                MonoObject *obj = *((MonoObject **) curParm);
                                MonoClass *class;
-                               if (obj) {
+                               if ((obj) && (obj->vtable)) {
                                        printf("[CLASS/OBJ:");
                                        class = obj->vtable->klass;
                                        if (class == mono_defaults.string_class) {
@@ -514,17 +624,44 @@ enum_parmtype:
                                break;
                        case MONO_TYPE_VALUETYPE : {
                                int i;
+                               MonoMarshalType *info;
+
                                if (type->data.klass->enumtype) {
                                        simpleType = type->data.klass->enum_basetype->type;
                                        printf("{VALUETYPE} - ");
                                        goto enum_parmtype;
                                }
+
+                               info = mono_marshal_load_type_info (type->data.klass);
+
+                               if ((info->native_size == sizeof(float)) &&
+                                   (info->num_fields  == 1) &&
+                                   (info->fields[0].field->type->type == MONO_TYPE_R4)) {
+                                       printf("[FLOAT4:%f], ", *((float *) (curParm)));
+                                       break;
+                               }
+
+                               if ((info->native_size == sizeof(double)) &&
+                                   (info->num_fields  == 1) &&
+                                   (info->fields[0].field->type->type == MONO_TYPE_R8)) {
+                                       printf("[FLOAT8:%g], ", *((double *) (curParm)));
+                                       break;
+                               }
+
                                printf("[VALUETYPE:");
                                for (i = 0; i < size; i++)
                                        printf("%02x,", *((guint8 *)curParm+i));
                                printf("]");
                                break;
                        }
+                       case MONO_TYPE_TYPEDBYREF: {
+                               int i;
+                               printf("[TYPEDBYREF:");
+                               for (i = 0; i < size; i++)
+                                       printf("%02x,", *((guint8 *)curParm+i));
+                               printf("]");
+                               break;
+                       }
                        default :
                                printf("[?? - %d], ",simpleType);
                }
@@ -551,6 +688,7 @@ enter_method (MonoMethod *method, RegParm *rParm, char *sp)
        MonoJitArgumentInfo *arg_info;
        MonoMethodSignature *sig;
        char *fname;
+       guint32 ip;
        CallInfo *cinfo;
        ArgInfo *ainfo;
        size_data sz;
@@ -561,12 +699,13 @@ enter_method (MonoMethod *method, RegParm *rParm, char *sp)
        printf ("ENTER: %s(", fname);
        g_free (fname);
 
-       printf (") ip: %p sp: %p\n", __builtin_return_address (1), sp);
+       ip  = (*(guint32 *) (sp+S390_RET_ADDR_OFFSET)) & 0x7fffffff;
+       printf (") ip: %p sp: %p - ", ip, sp); 
 
        if (rParm == NULL)
                return;
        
-       sig = method->signature;
+       sig = mono_method_signature (method);
        
        cinfo = calculate_sizes (sig, &sz, sig->pinvoke);
 
@@ -618,22 +757,26 @@ enter_method (MonoMethod *method, RegParm *rParm, char *sp)
                                else
                                        curParm = sp+ainfo->offset;
 
-                               switch (ainfo->vtsize) {
-                                       case 0:
-                                       case 1:
-                                       case 2:
-                                       case 4:
-                                       case 8:
-                                               decodeParm(sig->params[i], 
+                               if (retFitsInReg (ainfo->vtsize)) 
+                                       decodeParm(sig->params[i], 
                                                   curParm,
                                                   ainfo->size);
-                                               break;
-                                       default:
-                                               decodeParm(sig->params[i], 
+                               else
+                                       decodeParm(sig->params[i], 
                                                   *((char **) curParm),
                                                   ainfo->vtsize);
-                                       }
                                break;
+                       case RegTypeStructByAddr :
+                               if (ainfo->reg != STK_BASE) 
+                                       curParm = &(rParm->gr[ainfo->reg-2]);
+                               else
+                                       curParm = sp+ainfo->offset;
+
+                               decodeParm(sig->params[i], 
+                                          *((char **) curParm),
+                                          ainfo->vtsize);
+                               break;
+                               
                        default :
                                printf("???, ");
                }
@@ -657,6 +800,7 @@ leave_method (MonoMethod *method, ...)
 {
        MonoType *type;
        char *fname;
+       guint32 ip;
        va_list ap;
 
        va_start(ap, method);
@@ -666,7 +810,7 @@ leave_method (MonoMethod *method, ...)
        printf ("LEAVE: %s", fname);
        g_free (fname);
 
-       type = method->signature->ret;
+       type = mono_method_signature (method)->ret;
 
 handle_enum:
        switch (type->type) {
@@ -718,13 +862,13 @@ handle_enum:
        }
        case MONO_TYPE_I: {
                int *val = va_arg (ap, int*);
-               printf ("[INT:%p]", val);
+               printf ("[INT:%d]", val);
                printf("]");
                break;
        }
        case MONO_TYPE_U: {
                int *val = va_arg (ap, int*);
-               printf ("[UINT:%p]", val);
+               printf ("[UINT:%d]", val);
                printf("]");
                break;
        }
@@ -742,7 +886,7 @@ handle_enum:
        case MONO_TYPE_OBJECT: {
                MonoObject *o = va_arg (ap, MonoObject *);
 
-               if (o) {
+               if ((o) && (o->vtable)) {
                        if (o->vtable->klass == mono_defaults.boolean_class) {
                                printf ("[BOOLEAN:%p:%d]", o, *((guint8 *)o + sizeof (MonoObject)));            
                        } else if  (o->vtable->klass == mono_defaults.int32_class) {
@@ -769,18 +913,48 @@ handle_enum:
                printf ("[LONG:%lld]", l);
                break;
        }
+       case MONO_TYPE_U8: {
+               guint64 l =  va_arg (ap, guint64);
+               printf ("[ULONG:%llu]", l);
+               break;
+       }
+       case MONO_TYPE_R4: {
+               double f = va_arg (ap, double);
+               printf ("[FLOAT4:%f]\n", (float) f);
+               break;
+       }
        case MONO_TYPE_R8: {
                double f = va_arg (ap, double);
-               printf ("[FP:%g]\n", f);
+               printf ("[FLOAT8:%g]\n", f);
                break;
        }
-       case MONO_TYPE_VALUETYPE: 
+       case MONO_TYPE_VALUETYPE: {
+               MonoMarshalType *info;
                if (type->data.klass->enumtype) {
                        type = type->data.klass->enum_basetype;
                        goto handle_enum;
                } else {
                        guint8 *p = va_arg (ap, gpointer);
                        int j, size, align;
+
+                       info = mono_marshal_load_type_info (type->data.klass);
+
+                       if ((info->native_size == sizeof(float)) &&
+                           (info->num_fields  == 1) &&
+                           (info->fields[0].field->type->type == MONO_TYPE_R4)) {
+                               double f = va_arg (ap, double);
+                               printf("[FLOAT4:%f]\n", (float) f);
+                               break;
+                       }
+
+                       if ((info->native_size == sizeof(double)) &&
+                           (info->num_fields  == 1) &&
+                           (info->fields[0].field->type->type == MONO_TYPE_R8)) {
+                               double f = va_arg (ap, double);
+                               printf("[FLOAT8:%g]\n", f);
+                               break;
+                       }
+
                        size = mono_type_size (type, &align);
                        printf ("[");
                        for (j = 0; p && j < size; j++)
@@ -788,11 +962,24 @@ handle_enum:
                        printf ("]");
                }
                break;
+       }
+       case MONO_TYPE_TYPEDBYREF: {
+               guint8 *p = va_arg (ap, gpointer);
+               int j, size, align;
+               size = mono_type_size (type, &align);
+               printf ("[");
+               for (j = 0; p && j < size; j++)
+                       printf ("%02x,", p [j]);
+               printf ("]");
+       }
+               break;
        default:
-               printf ("(unknown return type %x)", method->signature->ret->type);
+               printf ("(unknown return type %x)", 
+                       mono_method_signature (method)->ret->type);
        }
 
-       printf (" ip: %p\n", __builtin_return_address (0));
+       ip = ((gint32) __builtin_return_address (0)) & 0x7fffffff;
+       printf (" ip: %p\n", ip);
 }
 
 /*========================= End of Function ========================*/
@@ -809,6 +996,13 @@ handle_enum:
 void
 mono_arch_cpu_init (void)
 {
+       guint mode = 1;
+
+       /*--------------------------------------*/      
+       /* Set default rounding mode for FP     */
+       /*--------------------------------------*/      
+       __asm__ ("SRNM\t%0\n\t"
+               : : "m" (mode));
 }
 
 /*========================= End of Function ========================*/
@@ -826,8 +1020,11 @@ mono_arch_cpu_optimizazions (guint32 *exclude_mask)
 {
        guint32 opts = 0;
 
-       /* no s390-specific optimizations yet */
+       /*----------------------------------------------------------*/
+       /* no s390-specific optimizations yet                       */
+       /*----------------------------------------------------------*/
        *exclude_mask = MONO_OPT_INLINE|MONO_OPT_LINEARS;
+//     *exclude_mask = MONO_OPT_INLINE;
        return opts;
 }
 
@@ -845,7 +1042,7 @@ static gboolean
 is_regsize_var (MonoType *t) {
        if (t->byref)
                return TRUE;
-       switch (t->type) {
+       switch (mono_type_get_underlying_type (t)->type) {
        case MONO_TYPE_I4:
        case MONO_TYPE_U4:
        case MONO_TYPE_I:
@@ -917,10 +1114,17 @@ GList *
 mono_arch_get_global_int_regs (MonoCompile *cfg)
 {
        GList *regs = NULL;
-       int i, top = 12;
+       MonoMethodHeader *header;
+       int i, top = 13;
 
-       for (i = 3; i < top; ++i)
-               regs = g_list_prepend (regs, GUINT_TO_POINTER (i));
+       header = mono_method_get_header (cfg->method);
+       if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
+               cfg->frame_reg = s390_r11;
+
+       for (i = 8; i < top; ++i) {
+               if (cfg->frame_reg != i) 
+                       regs = g_list_prepend (regs, GUINT_TO_POINTER (i));
+       }
 
        return regs;
 }
@@ -985,6 +1189,35 @@ add_general (guint *gr, size_data *sz, ArgInfo *ainfo, gboolean simple)
 
 /*========================= End of Function ========================*/
 
+/*------------------------------------------------------------------*/
+/*                                                                  */
+/* Name                - add_float                                         */
+/*                                                                  */
+/* Function    - Determine code and stack size incremements for a  */
+/*               float parameter.                                  */
+/*                                                                  */
+/*------------------------------------------------------------------*/
+
+static void inline
+add_float (guint *fr,  size_data *sz, ArgInfo *ainfo)
+{
+       if ((*fr) <= S390_LAST_FPARG_REG) {
+               ainfo->regtype = RegTypeFP;
+               ainfo->reg     = *fr;
+               sz->code_size += 4;
+               (*fr) += 2;
+       }
+       else {
+               ainfo->offset   = sz->stack_size;
+               ainfo->reg      = STK_BASE;
+               ainfo->regtype  = RegTypeBase;
+               sz->code_size  += 4;
+               sz->stack_size += ainfo->size;
+       }
+}
+
+/*========================= End of Function ========================*/
+
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* Name                - calculate_sizes                                   */
@@ -1000,36 +1233,29 @@ static CallInfo *
 calculate_sizes (MonoMethodSignature *sig, size_data *sz, 
                 gboolean string_ctor)
 {
-       guint i, fr, gr, size, nWords;
+       guint i, fr, gr, size;
        int nParm = sig->hasthis + sig->param_count;
        guint32 simpletype, align;
        CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * nParm);
 
        fr                = 0;
        gr                = s390_r2;
+       nParm             = 0;
        cinfo->struct_ret = 0;
        sz->retStruct     = 0;
        sz->stack_size    = S390_MINIMAL_STACK_SIZE;
        sz->code_size     = 0;
        sz->local_size    = 0;
 
-       nParm = 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.                           */
        /*----------------------------------------------------------*/
 
-//     if (sig->ret->byref || string_ctor) {
-//             sz->code_size += 8;
-//             add_general (&gr, sz, cinfo->args+nParm, TRUE);
-//             cinfo->args[nParm].size = sizeof(gpointer);
-//             nParm++;
-//     } else {
-       {
-               simpletype = sig->ret->type;
+       simpletype = mono_type_get_underlying_type (sig->ret)->type;
 enum_retvalue:
-               switch (simpletype) {
+       switch (simpletype) {
                case MONO_TYPE_BOOLEAN:
                case MONO_TYPE_I1:
                case MONO_TYPE_U1:
@@ -1045,6 +1271,7 @@ enum_retvalue:
                case MONO_TYPE_SZARRAY:
                case MONO_TYPE_ARRAY:
                case MONO_TYPE_PTR:
+               case MONO_TYPE_FNPTR:
                case MONO_TYPE_STRING:
                        cinfo->ret.reg = s390_r2;
                        sz->code_size += 4;
@@ -1059,15 +1286,17 @@ enum_retvalue:
                        cinfo->ret.reg = s390_r2;
                        sz->code_size += 4;
                        break;
-               case MONO_TYPE_VALUETYPE:
+               case MONO_TYPE_VALUETYPE: {
+                       MonoClass *klass = mono_class_from_mono_type (sig->ret);
                        if (sig->ret->data.klass->enumtype) {
                                simpletype = sig->ret->data.klass->enum_basetype->type;
                                goto enum_retvalue;
                        }
                        if (sig->pinvoke)
-                               size = mono_class_native_size (sig->ret->data.klass, &align);
+                               size = mono_class_native_size (klass, &align);
                        else
-                               size = mono_class_value_size (sig->ret->data.klass, &align);
+                               size = mono_class_value_size (klass, &align);
+       
                        cinfo->ret.reg    = s390_r2;
                        cinfo->struct_ret = 1;
                        cinfo->ret.size   = size;
@@ -1076,11 +1305,21 @@ enum_retvalue:
                        sz->stack_size   += S390_ALIGN(size, align);
                        gr++;
                         break;
+               }
+               case MONO_TYPE_TYPEDBYREF:
+                       size = sizeof (MonoTypedRef);
+                       cinfo->ret.reg    = s390_r2;
+                       cinfo->struct_ret = 1;
+                       cinfo->ret.size   = size;
+                       cinfo->ret.vtsize = size;
+                       cinfo->ret.offset = sz->stack_size;
+                       sz->stack_size   += S390_ALIGN(size, align);
+                       gr++;
+                       break;
                case MONO_TYPE_VOID:
                        break;
                default:
                        g_error ("Can't handle as return value 0x%x", sig->ret->type);
-               }
        }
 
        if (sig->hasthis) {
@@ -1096,14 +1335,23 @@ enum_retvalue:
        /*----------------------------------------------------------*/
 
        for (i = 0; i < sig->param_count; ++i) {
+               /*--------------------------------------------------*/
+               /* 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;
+                       add_general (&gr, sz, &cinfo->sigCookie, TRUE);
+               }
+
                if (sig->params [i]->byref) {
                        add_general (&gr, sz, cinfo->args+nParm, TRUE);
                        cinfo->args[nParm].size = sizeof(gpointer);
                        nParm++;
                        continue;
                }
-               simpletype = sig->params [i]->type;
-       enum_calc_size:
+               simpletype = mono_type_get_underlying_type(sig->params [i])->type;
                switch (simpletype) {
                case MONO_TYPE_BOOLEAN:
                case MONO_TYPE_I1:
@@ -1128,6 +1376,7 @@ enum_retvalue:
                case MONO_TYPE_I:
                case MONO_TYPE_U:
                case MONO_TYPE_PTR:
+               case MONO_TYPE_FNPTR:
                case MONO_TYPE_CLASS:
                case MONO_TYPE_OBJECT:
                case MONO_TYPE_STRING:
@@ -1137,17 +1386,47 @@ enum_retvalue:
                        add_general (&gr, sz, cinfo->args+nParm, TRUE);
                        nParm++;
                        break;
-               case MONO_TYPE_VALUETYPE:
-                       if (sig->params [i]->data.klass->enumtype) {
-                               simpletype = sig->params [i]->data.klass->enum_basetype->type;
-                               goto enum_calc_size;
-                       }
+               case MONO_TYPE_I8:
+               case MONO_TYPE_U8:
+                       cinfo->args[nParm].size = sizeof(long long);
+                       add_general (&gr, sz, cinfo->args+nParm, FALSE);
+                       nParm++;
+                       break;
+               case MONO_TYPE_R4:
+                       cinfo->args[nParm].size = sizeof(float);
+                       add_float (&fr, sz, cinfo->args+nParm);
+                       nParm++;
+                       break;
+               case MONO_TYPE_R8:
+                       cinfo->args[nParm].size = sizeof(double);
+                       add_float (&fr, sz, cinfo->args+nParm);
+                       nParm++;
+                       break;
+               case MONO_TYPE_VALUETYPE: {
+                       MonoMarshalType *info;
+                       MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
                        if (sig->pinvoke)
-                               size = mono_class_native_size (sig->params [i]->data.klass, &align);
+                               size = mono_class_native_size (klass, &align);
                        else
-                               size = mono_class_value_size (sig->params [i]->data.klass, &align);
-                       nWords = (size + sizeof(gpointer) - 1) /
-                                sizeof(gpointer);
+                               size = mono_class_value_size (klass, &align);
+       
+                       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);
+                               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);
+                               break;
+                       }
 
                        cinfo->args[nParm].vtsize  = 0;
                        cinfo->args[nParm].size    = 0;
@@ -1164,78 +1443,79 @@ enum_retvalue:
                                case 2:
                                case 4:
                                        add_general(&gr, sz, cinfo->args+nParm, TRUE);
-                                       cinfo->args[nParm].size    = sizeof(int);
+                                       cinfo->args[nParm].size    = size;
                                        cinfo->args[nParm].regtype = RegTypeStructByVal; 
                                        nParm++;
-                                       sz->local_size += sizeof(long);
+                                       sz->local_size            += sizeof(long);
                                        break;
                                case 8:
                                        add_general(&gr, sz, cinfo->args+nParm, FALSE);
                                        cinfo->args[nParm].size    = sizeof(long long);
                                        cinfo->args[nParm].regtype = RegTypeStructByVal; 
                                        nParm++;
-                                       sz->local_size += sizeof(long);
+                                       sz->local_size            += sizeof(long);
                                        break;
                                default:
                                        add_general(&gr, sz, cinfo->args+nParm, TRUE);
                                        cinfo->args[nParm].size    = sizeof(int);
-                                       cinfo->args[nParm].regtype = RegTypeStructByVal
+                                       cinfo->args[nParm].regtype = RegTypeStructByAddr
                                        cinfo->args[nParm].vtsize  = size;
-                                       sz->code_size  += 40;
-                                       sz->local_size += size;
+                                       sz->code_size             += 40;
+                                       sz->local_size            += size;
                                        if (cinfo->args[nParm].reg == STK_BASE)
                                                sz->local_size += sizeof(gpointer);
                                        nParm++;
                        }
+               }
                        break;
-               case MONO_TYPE_I8:
-               case MONO_TYPE_U8:
-                       cinfo->args[nParm].size = sizeof(long long);
-                       add_general (&gr, sz, cinfo->args+nParm, FALSE);
-                       nParm++;
-                       break;
-               case MONO_TYPE_R4:
-                       cinfo->args[nParm].size = sizeof(float);
-                       if (fr <= S390_LAST_FPARG_REG) {
-                               cinfo->args[nParm].regtype = RegTypeFP;
-                               cinfo->args[nParm].reg     = fr;
-                               sz->code_size += 4;
-                               fr += 2;
-                       }
-                       else {
-                               cinfo->args[nParm].offset  = sz->stack_size;
-                               cinfo->args[nParm].reg     = STK_BASE;
-                               cinfo->args[nParm].regtype = RegTypeBase;
-                               sz->code_size  += 4;
-                               sz->stack_size += sizeof(float);
-                       }
-                       nParm++;
-                       break;
-               case MONO_TYPE_R8:
-                       cinfo->args[nParm].size = sizeof(double);
-                       if (fr <= S390_LAST_FPARG_REG) {
-                               cinfo->args[nParm].regtype = RegTypeFP;
-                               cinfo->args[nParm].reg     = fr;
-                               sz->code_size += 4;
-                               fr += 2;
-                       } else {
-                               sz->stack_size  = S390_ALIGN(sz->stack_size, 
-                                                            S390_STACK_ALIGNMENT);
-                               cinfo->args[nParm].offset  = sz->stack_size;
-                               cinfo->args[nParm].reg     = STK_BASE;
-                               cinfo->args[nParm].regtype = RegTypeBase;
-                               sz->code_size  += 4;
-                               sz->stack_size += sizeof(double);
+               case MONO_TYPE_TYPEDBYREF: {
+                       int size = sizeof (MonoTypedRef);
+
+                       cinfo->args[nParm].vtsize  = 0;
+                       cinfo->args[nParm].size    = 0;
+                       cinfo->args[nParm].offparm = sz->local_size;
+
+                       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:
+                                       add_general(&gr, sz, cinfo->args+nParm, TRUE);
+                                       cinfo->args[nParm].size    = size;
+                                       cinfo->args[nParm].regtype = RegTypeStructByVal; 
+                                       nParm++;
+                                       sz->local_size            += sizeof(long);
+                                       break;
+                               case 8:
+                                       add_general(&gr, sz, cinfo->args+nParm, FALSE);
+                                       cinfo->args[nParm].size    = sizeof(long long);
+                                       cinfo->args[nParm].regtype = RegTypeStructByVal; 
+                                       nParm++;
+                                       sz->local_size            += sizeof(long);
+                                       break;
+                               default:
+                                       add_general(&gr, sz, cinfo->args+nParm, TRUE);
+                                       cinfo->args[nParm].size    = sizeof(int);
+                                       cinfo->args[nParm].regtype = RegTypeStructByAddr; 
+                                       cinfo->args[nParm].vtsize  = size;
+                                       sz->code_size             += 40;
+                                       sz->local_size            += size;
+                                       if (cinfo->args[nParm].reg == STK_BASE)
+                                               sz->local_size += sizeof(gpointer);
+                                       nParm++;
                        }
-                       nParm++;
+               }
                        break;
                default:
                        g_error ("Can't trampoline 0x%x", sig->params [i]->type);
                }
        }
 
-
-       /* align stack size */
        cinfo->stack_usage = S390_ALIGN(sz->stack_size+sz->local_size, 
                                        S390_STACK_ALIGNMENT);
        return (cinfo);
@@ -1256,7 +1536,7 @@ enum_retvalue:
 /*------------------------------------------------------------------*/
 
 void
-mono_arch_allocate_vars (MonoCompile *m)
+mono_arch_allocate_vars (MonoCompile *cfg)
 {
        MonoMethodSignature *sig;
        MonoMethodHeader *header;
@@ -1267,65 +1547,67 @@ mono_arch_allocate_vars (MonoCompile *m)
        int frame_reg = STK_BASE;
        int sArg, eArg;
 
-       header  = ((MonoMethodNormal *)m->method)->header;
-
-       /* 
-        * 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 ((m->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
-       if (m->flags & MONO_CFG_HAS_ALLOCA) 
+       header  = mono_method_get_header (cfg->method);
+
+       /*---------------------------------------------------------*/    
+       /* 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;
 
-       m->frame_reg = frame_reg;
+       cfg->frame_reg = frame_reg;
 
        if (frame_reg != STK_BASE) 
-               m->used_int_regs |= 1 << frame_reg;             
+               cfg->used_int_regs |= 1 << frame_reg;           
 
-       sig     = m->method->signature;
+       sig     = mono_method_signature (cfg->method);
        
        cinfo   = calculate_sizes (sig, &sz, sig->pinvoke);
 
        if (cinfo->struct_ret) {
-               m->ret->opcode = OP_REGVAR;
-               m->ret->inst_c0 = s390_r2;
+               cfg->ret->opcode = OP_REGVAR;
+               cfg->ret->inst_c0 = s390_r2;
        } else {
-               /* FIXME: handle long and FP values */
-               switch (sig->ret->type) {
+               switch (mono_type_get_underlying_type (sig->ret)->type) {
                case MONO_TYPE_VOID:
                        break;
                default:
-                       m->ret->opcode = OP_REGVAR;
-                       m->ret->dreg    = s390_r2;
+                       cfg->ret->opcode = OP_REGVAR;
+                       cfg->ret->dreg   = s390_r2;
                        break;
                }
        }
 
        /*--------------------------------------------------------------*/
        /* 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                 */
        /*--------------------------------------------------------------*/
-       offset = (m->param_area + S390_MINIMAL_STACK_SIZE);
+       offset          = (cfg->param_area + S390_MINIMAL_STACK_SIZE);
+       cfg->sig_cookie = 0;
 
        if (cinfo->struct_ret) {
-               inst               = m->ret;
+               inst               = cfg->ret;
                offset             = S390_ALIGN(offset, sizeof(gpointer));
                inst->inst_offset  = offset;
                inst->opcode       = OP_REGOFFSET;
                inst->inst_basereg = frame_reg;
                offset            += sizeof(gpointer);
+               if ((sig->call_convention == MONO_CALL_VARARG) &&
+                   (!retFitsInReg (cinfo->ret.size)))
+                       cfg->sig_cookie += cinfo->ret.size;
        }
 
        if (sig->hasthis) {
-               inst = m->varinfo [0];
+               inst = cfg->varinfo [0];
                if (inst->opcode != OP_REGVAR) {
                        inst->opcode       = OP_REGOFFSET;
                        inst->inst_basereg = frame_reg;
@@ -1340,24 +1622,28 @@ mono_arch_allocate_vars (MonoCompile *m)
 
        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 = m->varinfo [curinst];
+               inst = cfg->varinfo [curinst];
                if (inst->opcode != OP_REGVAR) {
-                       if (cinfo->args[iParm].regtype == RegTypeStructByVal) {
-                               if (cinfo->args[iParm].vtsize != 0) {
+                       switch (cinfo->args[iParm].regtype) {
+                               case RegTypeStructByAddr :
                                        inst->opcode       = OP_S390_LOADARG;
                                        inst->inst_basereg = frame_reg;
-                                       size               = sizeof(long);
+                                       size               = abs(cinfo->args[iParm].vtsize);
                                        offset             = S390_ALIGN(offset, size);
                                        inst->inst_offset  = offset; 
-                               } else {
+                                       break;
+                               case RegTypeStructByVal :
                                        inst->opcode       = OP_S390_ARGPTR;
                                        inst->inst_basereg = frame_reg;
                                        size               = cinfo->args[iParm].size;
                                        offset             = S390_ALIGN(offset, size);
                                        inst->inst_offset  = offset;
-                               }
-                       } else {
+                                       break;
+                               default :
                                if (cinfo->args[iParm].reg != STK_BASE) {
                                        inst->opcode       = OP_REGOFFSET;
                                        inst->inst_basereg = frame_reg;
@@ -1374,26 +1660,34 @@ mono_arch_allocate_vars (MonoCompile *m)
                                                              : 0);
                                        inst->inst_offset  = cinfo->args[iParm].offset + 
                                                             size;
-//                                     inst->unused       = stackOffset;
                                        inst->unused       = 0;
                                        size               = sizeof(long);
                                } 
                        }
+                       if ((sig->call_convention == MONO_CALL_VARARG) && 
+                           (cinfo->args[iParm].regtype != RegTypeGeneral) &&
+                           (iParm < sig->sentinelpos)) 
+                               cfg->sig_cookie += size;
+
                        offset += size;
                }
                curinst++;
        }
 
-       curinst = m->locals_start;
-       for (iVar = curinst; iVar < m->num_varinfo; ++iVar) {
-               inst = m->varinfo [iVar];
-               if (inst->opcode == OP_REGVAR)
+       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->unused indicates native sized value types, this is used by the
-               * pinvoke wrappers when they call functions returning structure */
+               /*--------------------------------------------------*/
+               /* inst->unused indicates native sized value types, */
+               /* this is used by the pinvoke wrappers when they   */
+               /* call functions returning structure               */
+               /*--------------------------------------------------*/
                if (inst->unused && MONO_TYPE_ISSTRUCT (inst->inst_vtype))
-                       size = mono_class_native_size (inst->inst_vtype->data.klass, &align);
+                       size = mono_class_native_size (mono_class_from_mono_type(inst->inst_vtype), &align);
                else
                        size = mono_type_size (inst->inst_vtype, &align);
 
@@ -1402,31 +1696,25 @@ mono_arch_allocate_vars (MonoCompile *m)
                inst->opcode       = OP_REGOFFSET;
                inst->inst_basereg = frame_reg;
                offset            += size;
-               //DEBUG (g_print("allocating local %d to %d\n", iVar, inst->inst_offset));
+               DEBUG (g_print("allocating local %d to %d\n", iVar, inst->inst_offset));
        }
 
-//     if (sig->hasthis) 
-//             curinst = sArg = 1;
-//     else 
-//             curinst = sArg = 0;
-//
-
        /*------------------------------------------------------*/
        /* Allow space for the trace method stack area if needed*/
        /*------------------------------------------------------*/
-       if (mono_jit_trace_calls != NULL && mono_trace_eval (m)) 
+       if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg)) 
                offset += S390_TRACE_STACK_SIZE;
 
        /*------------------------------------------------------*/
        /* Reserve space to save LMF and caller saved registers */
        /*------------------------------------------------------*/
-       if (m->method->save_lmf)
+       if (cfg->method->save_lmf)
                offset += sizeof (MonoLMF);
 
        /*------------------------------------------------------*/
        /* align the offset                                     */
        /*------------------------------------------------------*/
-       m->stack_offset = S390_ALIGN(offset, S390_STACK_ALIGNMENT);
+       cfg->stack_offset = S390_ALIGN(offset, S390_STACK_ALIGNMENT);
 
 }
 
@@ -1449,7 +1737,8 @@ mono_arch_allocate_vars (MonoCompile *m)
 /*------------------------------------------------------------------*/
 
 MonoCallInst*
-mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) {
+mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, 
+                      MonoCallInst *call, int is_virtual) {
        MonoInst *arg, *in;
        MonoMethodSignature *sig;
        int i, n, lParamArea;
@@ -1464,21 +1753,32 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call,
        cinfo = calculate_sizes (sig, &sz, sig->pinvoke);
 
        call->stack_usage = cinfo->stack_usage;
-       lParamArea        = cinfo->stack_usage - S390_MINIMAL_STACK_SIZE;
-       cfg->param_area   = MAX (cfg->param_area, lParamArea);
+       lParamArea        = MAX((cinfo->stack_usage - S390_MINIMAL_STACK_SIZE), 0);
+       cfg->param_area   = MAX (((signed) cfg->param_area), lParamArea);
        cfg->flags       |= MONO_CFG_HAS_CALLS;
-       /*----------------------------------------------------------*/ 
-       /* should set more info in call, such as the stack space    */
-       /* used by the args that needs to be added back to esp      */
-       /*----------------------------------------------------------*/ 
 
        if (cinfo->struct_ret)
-               call->used_iregs |= 1 << cinfo->struct_ret;
+               call->used_iregs |= 1 << cinfo->ret.reg;
 
        for (i = 0; i < n; ++i) {
                ainfo = cinfo->args + i;
-               DEBUG (g_print ("Parameter %d - Register: %d Type: %d\n",
-                               i+1,ainfo->reg,ainfo->regtype));
+
+               if ((sig->call_convention == MONO_CALL_VARARG) &&
+                   (i == sig->sentinelpos)) {
+                       MonoInst *sigArg;
+                       
+                       cfg->disable_aot = TRUE;
+                       MONO_INST_NEW (cfg, sigArg, OP_ICONST);
+                       sigArg->inst_p0 = call->signature;
+
+                       MONO_INST_NEW (cfg, arg, OP_OUTARG);
+                       arg->inst_imm  = cinfo->sigCookie.offset;
+                       arg->inst_left = sigArg;
+
+                       arg->next      = call->out_args;
+                       call->out_args = arg;
+               }
+
                if (is_virtual && i == 0) {
                        /* the argument will be attached to the call instrucion */
                        in = call->args [i];
@@ -1498,8 +1798,12 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call,
                                if (arg->type == STACK_I8)
                                        call->used_iregs |= 1 << (ainfo->reg + 1);
                        } else if (ainfo->regtype == RegTypeStructByAddr) {
-                               arg->unused = ainfo->reg;
                                call->used_iregs |= 1 << ainfo->reg;
+                               arg->sreg1     = ainfo->reg;
+                               arg->opcode    = OP_OUTARG_VT;
+                               arg->unused    = -ainfo->vtsize;
+                               arg->inst_imm  = ainfo->offset;
+                               arg->sreg2     = ainfo->offparm + S390_MINIMAL_STACK_SIZE;
                        } else if (ainfo->regtype == RegTypeStructByVal) {
                                if (ainfo->reg != STK_BASE) {
                                        switch (ainfo->size) {
@@ -1517,12 +1821,9 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call,
                                                call->used_iregs |= 1 << ainfo->reg;
                                        }
                                } 
-                               arg->sreg1    = ainfo->reg;
-                               arg->opcode   = OP_OUTARG_VT;
-                               if (ainfo->vtsize != 0)
-                                       arg->unused = -ainfo->vtsize;
-                               else
-                                       arg->unused = ainfo->size;
+                               arg->sreg1     = ainfo->reg;
+                               arg->opcode    = OP_OUTARG_VT;
+                               arg->unused    = ainfo->size;
                                arg->inst_imm  = ainfo->offset;
                                arg->sreg2     = ainfo->offparm + S390_MINIMAL_STACK_SIZE;
                        } else if (ainfo->regtype == RegTypeBase) {
@@ -1531,16 +1832,17 @@ mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call,
                                arg->inst_imm = ainfo->offset;
                                call->used_fregs |= 1 << ainfo->reg;
                        } else if (ainfo->regtype == RegTypeFP) {
-                               arg->opcode = OP_OUTARG_R8;
                                arg->unused = ainfo->reg;
                                call->used_fregs |= 1 << ainfo->reg;
                                if (ainfo->size == 4) {
-                                       /* we reduce the precision */
                                        MonoInst *conv;
+                                       arg->opcode     = OP_OUTARG_R4;
                                        MONO_INST_NEW (cfg, conv, OP_FCONV_TO_R4);
                                        conv->inst_left = arg->inst_left;
-                                       arg->inst_left = conv;
+                                       arg->inst_left  = conv;
                                }
+                               else
+                                       arg->opcode = OP_OUTARG_R8;
                        } else {
                                g_assert_not_reached ();
                        }
@@ -1647,7 +1949,7 @@ mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean ena
        int        save_mode = SAVE_NONE,
                   saveOffset;
        MonoMethod *method = cfg->method;
-       int        rtype = method->signature->ret->type;
+       int        rtype = mono_type_get_underlying_type (mono_method_signature (method)->ret)->type;
 
        saveOffset = cfg->stack_usage - S390_TRACE_STACK_SIZE;
        if (method->save_lmf)
@@ -1671,8 +1973,8 @@ handle_enum:
                save_mode = SAVE_FP;
                break;
        case MONO_TYPE_VALUETYPE:
-               if (method->signature->ret->data.klass->enumtype) {
-                       rtype = method->signature->ret->data.klass->enum_basetype->type;
+               if (mono_method_signature (method)->ret->data.klass->enumtype) {
+                       rtype = mono_method_signature (method)->ret->data.klass->enum_basetype->type;
                        goto handle_enum;
                }
                save_mode = SAVE_STRUCT;
@@ -2482,10 +2784,10 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
        while (ins) {
                spec = ins_spec [ins->opcode];
                DEBUG (print_ins (i, ins));
-               if (spec [MONO_INST_CLOB] == 'c') {
-                       MonoCallInst * call = (MonoCallInst*)ins;
-                       int j;
-               }
+//             if (spec [MONO_INST_CLOB] == 'c') {
+//                     MonoCallInst * call = (MonoCallInst*)ins;
+//                     int j;
+//             }
                if (spec [MONO_INST_SRC1]) {
                        if (spec [MONO_INST_SRC1] == 'f')
                                reginfo1 = reginfof;
@@ -2518,7 +2820,7 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                        if (reginfod [ins->dreg].born_in == 0 || reginfod [ins->dreg].born_in > i)
                                reginfod [ins->dreg].born_in = i;
                        if (spec [MONO_INST_DEST] == 'l') {
-                               /* result in eax:edx, the virtual register is allocated sequentially */
+                               /* result in R2/R3, the virtual register is allocated sequentially */
                                reginfod [ins->dreg + 1].prev_use = reginfod [ins->dreg + 1].last_use;
                                reginfod [ins->dreg + 1].last_use = i;
                                if (reginfod [ins->dreg + 1].born_in == 0 || reginfod [ins->dreg + 1].born_in > i)
@@ -2667,7 +2969,10 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                                create_copy_ins (cfg, ins->dreg, s390_r3, ins);
                                        }
                                }
-                               if (reg_is_freeable (val) && hreg >= 0 && (reginfo [hreg].born_in >= i && !(cur_iregs & (1 << val)))) {
+                               if (reg_is_freeable (val) && 
+                                   hreg >= 0 && 
+                                    (reginfo [hreg].born_in >= i && 
+                                     !(cur_iregs & (1 << val)))) {
                                        DEBUG (g_print ("\tfreeable %s (R%d)\n", mono_arch_regname (val), hreg));
                                        mono_regstate_free_int (rs, val);
                                }
@@ -2700,7 +3005,6 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                                /* the register gets spilled after this inst */
                                                spill = -val -1;
                                        }
-                                       //g_assert (val == -1); /* source cannot be spilled */
                                        val = mono_regstate_alloc_float (rs, src1_mask);
                                        if (val < 0)
                                                val = get_float_register_spilling (cfg, tmp, ins, src1_mask, ins->sreg1);
@@ -2825,16 +3129,6 @@ mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                }
                        }
                }
-               /*if (reg_is_freeable (ins->sreg1) && prev_sreg1 >= 0 && reginfo [prev_sreg1].born_in >= i) {
-                       DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg1)));
-                       mono_regstate_free_int (rs, ins->sreg1);
-               }
-               if (reg_is_freeable (ins->sreg2) && prev_sreg2 >= 0 && reginfo [prev_sreg2].born_in >= i) {
-                       DEBUG (g_print ("freeable %s\n", mono_arch_regname (ins->sreg2)));
-                       mono_regstate_free_int (rs, ins->sreg2);
-               }*/
-               
-               //DEBUG (print_ins (i, ins));
                tmp = tmp->next;
        }
 }
@@ -2856,18 +3150,40 @@ emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size,
        /* sreg is a float, dreg is an integer reg. */
        if (is_signed) {
                s390_cfdbr (code, dreg, 5, sreg);
+               switch (size) {
+                       case 1:
+                               s390_lhi  (code, s390_r0, 0);
+                               s390_lhi  (code, s390_r13, 0xff);
+                               s390_ltr  (code, dreg, dreg);
+                               s390_jnl  (code, 4);
+                               s390_lhi  (code, s390_r0, 0x80);
+                               s390_nr   (code, dreg, s390_r13);
+                               s390_or   (code, dreg, s390_r0);
+                               break;
+               }
        } else {
                s390_basr   (code, s390_r13, 0);
                s390_j      (code, 10);
-               s390_double (code, 0x41e0000000000000);
-               s390_double (code, 0x41f0000000000000);
-               s390_ldr    (code, s390_f0, sreg);
-               s390_cdb    (code, s390_f0, 0, s390_r13, 0);
+               s390_llong  (code, 0x41e0000000000000);
+               s390_llong  (code, 0x41f0000000000000);
+               s390_ldr    (code, s390_f15, sreg);
+               s390_cdb    (code, s390_f15, 0, s390_r13, 0);
                s390_jl     (code, 10);
-               s390_sdb    (code, s390_f0, 0, s390_r13, 8);
-               s390_cfdbr  (code, dreg, 7, s390_f0);
-               s390_j      (code, 5);
+               s390_sdb    (code, s390_f15, 0, s390_r13, 8);
+               s390_cfdbr  (code, dreg, 7, s390_f15);
+               s390_j      (code, 4);
                s390_cfdbr  (code, dreg, 5, sreg);
+               switch (size) {
+                       case 1: 
+                               s390_lhi  (code, s390_r0, 0xff);
+                               s390_nr   (code, dreg, s390_r0);
+                               break;
+                       case 2:
+                               s390_lhi  (code, s390_r0, -1);
+                               s390_srl  (code, s390_r0, 0, 16);
+                               s390_nr   (code, dreg, s390_r0);
+                               break;
+               }
        }
        return code;
 }
@@ -2908,8 +3224,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
        guint8 *code = cfg->native_code + cfg->code_len;
        MonoInst *last_ins = NULL;
        guint last_offset = 0;
-       int max_len, cpos;
-guint8 cond;
+       int max_len, cpos, src2;
 
        if (cfg->opt & MONO_OPT_PEEPHOLE)
                peephole_pass (cfg, bb);
@@ -2944,51 +3259,53 @@ guint8 cond;
                        code = cfg->native_code + offset;
                }
 
+               mono_debug_record_line_number (cfg, ins, offset);
+
                switch (ins->opcode) {
                case OP_STOREI1_MEMBASE_IMM: {
-                       s390_lhi (code, s390_r14, ins->inst_imm);
+                       s390_lhi (code, s390_r0, ins->inst_imm);
                        if (s390_is_uimm12(ins->inst_offset))
-                               s390_stc (code, s390_r14, 0, ins->inst_destbasereg, ins->inst_offset);
+                               s390_stc (code, s390_r0, 0, ins->inst_destbasereg, ins->inst_offset);
                        else {
                                s390_basr (code, s390_r13, 0);
                                s390_j    (code, 4);
                                s390_word (code, ins->inst_offset);
                                s390_l    (code, s390_r13, 0, s390_r13, 4);
-                               s390_stc  (code, s390_r14, s390_r13, ins->inst_destbasereg, 0);
+                               s390_stc  (code, s390_r0, s390_r13, ins->inst_destbasereg, 0);
                        }
                }
                        break;
                case OP_STOREI2_MEMBASE_IMM: {
-                       s390_lhi (code, s390_r14, ins->inst_imm);
+                       s390_lhi (code, s390_r0, ins->inst_imm);
                        if (s390_is_uimm12(ins->inst_offset)) {
-                               s390_sth (code, s390_r14, 0, ins->inst_destbasereg, ins->inst_offset);
+                               s390_sth (code, s390_r0, 0, ins->inst_destbasereg, ins->inst_offset);
                        } else {
-                               s390_basr (code, s390_r14, 0);
+                               s390_basr (code, s390_r13, 0);
                                s390_j    (code, 4);
                                s390_word (code, ins->inst_offset);
                                s390_l    (code, s390_r13, 0, s390_r13, 4);
-                               s390_sth  (code, s390_r14, s390_r13, ins->inst_destbasereg, 0);
+                               s390_sth  (code, s390_r0, s390_r13, ins->inst_destbasereg, 0);
                        }
                }
                        break;
                case OP_STORE_MEMBASE_IMM:
                case OP_STOREI4_MEMBASE_IMM: {
-                       if (s390_is_imm16(ins->inst_imm)) {
-                               s390_lhi  (code, s390_r14, ins->inst_imm);
+                       if (s390_is_uimm16(ins->inst_imm)) {
+                               s390_lhi  (code, s390_r0, ins->inst_imm);
                        } else {
                                s390_basr (code, s390_r13, 0);
                                s390_j    (code, 4);
                                s390_word (code, ins->inst_imm);
-                               s390_l    (code, s390_r14, 0, s390_r13, 4);
+                               s390_l    (code, s390_r0, 0, s390_r13, 4);
                        }
                        if (s390_is_uimm12(ins->inst_offset)) {
-                               s390_st  (code, s390_r14, 0, ins->inst_destbasereg, ins->inst_offset);
+                               s390_st  (code, s390_r0, 0, ins->inst_destbasereg, ins->inst_offset);
                        } else {
                                s390_basr (code, s390_r13, 0);
                                s390_j    (code, 4);
                                s390_word (code, ins->inst_offset);
                                s390_l    (code, s390_r13, 0, s390_r13, 4);
-                               s390_st   (code, s390_r14, s390_r13, ins->inst_destbasereg, 0);
+                               s390_st   (code, s390_r0, s390_r13, ins->inst_destbasereg, 0);
                        }
                }
                        break;
@@ -3048,7 +3365,7 @@ guint8 cond;
                        if (s390_is_uimm12(ins->inst_offset))
                                s390_l    (code, ins->dreg, 0, ins->inst_basereg, ins->inst_offset);
                        else {
-                               if (s390_is_imm16(ins->inst_offset)) {
+                               if (s390_is_uimm16(ins->inst_offset)) {
                                        s390_lhi (code, s390_r13, ins->inst_offset);
                                        s390_l   (code, ins->dreg, s390_r13, ins->inst_basereg, 0);
                                } else {
@@ -3129,7 +3446,7 @@ guint8 cond;
                                s390_lr   (code, ins->dreg, ins->sreg1);
                        }
                        s390_nr   (code, s390_r0, ins->sreg1);
-                       s390_jl   (code, 7);
+                       s390_jz   (code, 7);
                        s390_lhi  (code, s390_r13, -1);
                        s390_sll  (code, s390_r13, 0, 8);
                        s390_or   (code, ins->dreg, s390_r13);
@@ -3137,11 +3454,12 @@ guint8 cond;
                        break;
                case CEE_CONV_I2: {
                        s390_lhi  (code, s390_r0, 0x80);
+                       s390_sll  (code, s390_r0, 0, 8);
                        if (ins->dreg != ins->sreg1) {
                                s390_lr   (code, ins->dreg, ins->sreg1);
                        }
                        s390_nr   (code, s390_r0, ins->sreg1);
-                       s390_jl   (code, 7);
+                       s390_jz   (code, 7);
                        s390_lhi  (code, s390_r13, -1);
                        s390_sll  (code, s390_r13, 0, 16);
                        s390_or   (code, ins->dreg, s390_r13);
@@ -3168,18 +3486,26 @@ guint8 cond;
                case OP_COMPARE: {
                        if ((ins->next) && 
                            ((ins->next->opcode >= CEE_BNE_UN) &&
-                            (ins->next->opcode <= CEE_BLT_UN)))
+                            (ins->next->opcode <= CEE_BLT_UN)) || 
+                           ((ins->next->opcode >= OP_COND_EXC_NE_UN) &&
+                            (ins->next->opcode <= OP_COND_EXC_LT_UN)) ||
+                           ((ins->next->opcode == OP_CLT_UN) ||
+                            (ins->next->opcode == OP_CGT_UN)))
                                s390_clr  (code, ins->sreg1, ins->sreg2);
                        else
                                s390_cr   (code, ins->sreg1, ins->sreg2);
                }
                        break;
                case OP_COMPARE_IMM: {
-                       if (s390_is_imm16 (ins->inst_imm)) {
+                       if (s390_is_uimm16 (ins->inst_imm)) {
                                s390_lhi  (code, s390_r0, ins->inst_imm);
                                if ((ins->next) && 
-                                   ((ins->next->opcode >= CEE_BNE_UN) &&
-                                   (ins->next->opcode <= CEE_BLT_UN)))
+                                   ((ins->next->opcode >= CEE_BNE_UN) &&
+                                    (ins->next->opcode <= CEE_BLT_UN)) || 
+                                   ((ins->next->opcode >= OP_COND_EXC_NE_UN) &&
+                                    (ins->next->opcode <= OP_COND_EXC_LT_UN)) ||
+                                   ((ins->next->opcode == OP_CLT_UN) ||
+                                    (ins->next->opcode == OP_CGT_UN)))
                                        s390_clr  (code, ins->sreg1, s390_r0);
                                else
                                        s390_cr   (code, ins->sreg1, s390_r0);
@@ -3189,8 +3515,12 @@ guint8 cond;
                                s390_j    (code, 4);
                                s390_word (code, ins->inst_imm);
                                if ((ins->next) && 
-                                   ((ins->next->opcode >= CEE_BNE_UN) &&
-                                   (ins->next->opcode <= CEE_BLT_UN)))
+                                   ((ins->next->opcode >= CEE_BNE_UN) &&
+                                    (ins->next->opcode <= CEE_BLT_UN)) || 
+                                   ((ins->next->opcode >= OP_COND_EXC_NE_UN) &&
+                                    (ins->next->opcode <= OP_COND_EXC_LT_UN)) ||
+                                   ((ins->next->opcode == OP_CLT_UN) &&
+                                    (ins->next->opcode == OP_CGT_UN)))
                                        s390_cl   (code, ins->sreg1, 0, s390_r13, 4);
                                else
                                        s390_c    (code, ins->sreg1, 0, s390_r13, 4);
@@ -3206,26 +3536,21 @@ guint8 cond;
                }
                        break;
                case OP_ADDCC: {
-                       if (ins->dreg != ins->sreg1) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
-                       }
-                       s390_alr  (code, ins->dreg, ins->sreg2);
+                       CHECK_SRCDST_COM;
+                       s390_alr  (code, ins->dreg, src2);
                }
                        break;
                case CEE_ADD: {
-                       if (ins->dreg != ins->sreg1) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
-                       }
-                       s390_ar   (code, ins->dreg, ins->sreg2);
+                       CHECK_SRCDST_COM;
+                       s390_ar   (code, ins->dreg, src2);
                }
                        break;
                case OP_ADC: {
-                       if (ins->dreg != ins->sreg1) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
-                       }
-                       s390_alcr (code, ins->dreg, ins->sreg2);
+                       CHECK_SRCDST_COM;
+                       s390_alcr (code, ins->dreg, src2);
                }
                        break;
+               case OP_ADDCC_IMM:
                case OP_ADD_IMM: {
                        if ((ins->next) &&
                            (ins->next->opcode == OP_ADC_IMM)) {
@@ -3237,7 +3562,7 @@ guint8 cond;
                                }
                                s390_al   (code, ins->dreg, 0, s390_r13, 4);
                        } else {
-                               if (s390_is_imm16 (ins->inst_imm)) {
+                               if (s390_is_uimm16 (ins->inst_imm)) {
                                        if (ins->dreg != ins->sreg1) {
                                                s390_lr   (code, ins->dreg, ins->sreg1);
                                        }
@@ -3255,7 +3580,7 @@ guint8 cond;
                }
                        break;
                case OP_ADC_IMM: {
-                       if (s390_is_imm16 (ins->inst_imm)) {
+                       if (s390_is_uimm16 (ins->inst_imm)) {
                                if (ins->dreg != ins->sreg1) {
                                        s390_lr   (code, ins->dreg, ins->sreg1);
                                } 
@@ -3270,29 +3595,69 @@ guint8 cond;
                        }
                }
                        break;
-               case OP_SUBCC: {
-                       if (ins->dreg != ins->sreg1) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
-                       }
-                       s390_sr (code, ins->dreg, ins->sreg2);
+               case CEE_ADD_OVF: {
+                       CHECK_SRCDST_COM;
+                       s390_ar   (code, ins->dreg, src2);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException");
+               }
+                       break;
+               case CEE_ADD_OVF_UN: {
+                       CHECK_SRCDST_COM;
+                       s390_alr  (code, ins->dreg, src2);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_CY, "OverflowException");
+               }
+                       break;
+               case OP_ADD_OVF_CARRY: {
+                       CHECK_SRCDST_COM;
+                       s390_lhi  (code, s390_r0, 0);
+                       s390_lr   (code, s390_r1, s390_r0);
+                       s390_alcr (code, s390_r0, s390_r1);
+                       s390_ar   (code, ins->dreg, src2);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException");
+                       s390_ar   (code, ins->dreg, s390_r0);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException");
+               }
+                       break;
+               case OP_ADD_OVF_UN_CARRY: {
+                       CHECK_SRCDST_COM;
+                       s390_alcr (code, ins->dreg, src2);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_CY, "OverflowException");
+               }
+                       break;
+               case OP_SUBCC: {
+                       CHECK_SRCDST_NCOM;
+                       s390_slr (code, ins->dreg, src2);
                }
                        break;
                case CEE_SUB: {
-                       if (ins->dreg != ins->sreg1) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
-                       }
-                       s390_sr   (code, ins->dreg, ins->sreg2);
+                       CHECK_SRCDST_NCOM;
+                       s390_sr   (code, ins->dreg, src2);
                }
                        break;
                case OP_SBB: {
-                       if (ins->dreg != ins->sreg1) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
+                       CHECK_SRCDST_NCOM;
+                       s390_slbr (code, ins->dreg, src2);
+               }
+                       break;
+               case OP_SUBCC_IMM: {
+                       if (s390_is_uimm16 (-ins->inst_imm)) {
+                               if (ins->dreg != ins->sreg1) {
+                                       s390_lr   (code, ins->dreg, ins->sreg1);
+                               }
+                               s390_ahi  (code, ins->dreg, -ins->inst_imm);
+                       } else {
+                               s390_basr (code, s390_r13, 0);
+                               s390_j    (code, 4);
+                               s390_word (code, ins->inst_imm);
+                               if (ins->dreg != ins->sreg1) {
+                                       s390_lr   (code, ins->dreg, ins->sreg1);
+                               }
+                               s390_sl   (code, ins->dreg, 0, s390_r13, 4);
                        }
-                       s390_slbr (code, ins->dreg, ins->sreg2);
                }
                        break;
                case OP_SUB_IMM: {
-                       if (s390_is_imm16 (-ins->inst_imm)) {
+                       if (s390_is_uimm16 (-ins->inst_imm)) {
                                if (ins->dreg != ins->sreg1) {
                                        s390_lr   (code, ins->dreg, ins->sreg1);
                                }
@@ -3309,10 +3674,47 @@ guint8 cond;
                }
                        break;
                case OP_SBB_IMM: {
-                       s390_basr (code, s390_r13, 0);
-                       s390_j    (code, 4);
-                       s390_word (code, ins->inst_imm);
-                       s390_sl   (code, ins->dreg, 0, s390_r13, 4);
+                       if (ins->dreg != ins->sreg1) {
+                               s390_lr    (code, ins->dreg, ins->sreg1);
+                       }
+                       if (s390_is_uimm16 (-ins->inst_imm)) {
+                               s390_lhi   (code, s390_r0, ins->inst_imm);
+                               s390_slbr  (code, ins->dreg, s390_r0);
+                       } else {
+                               s390_basr (code, s390_r13, 0);
+                               s390_j    (code, 4);
+                               s390_word (code, ins->inst_imm);
+                               s390_slb  (code, ins->dreg, 0, s390_r13, 4);
+                       }
+               }
+                       break;
+               case CEE_SUB_OVF: {
+                       CHECK_SRCDST_NCOM;
+                       s390_sr   (code, ins->dreg, src2);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException");
+               }
+                       break;
+               case CEE_SUB_OVF_UN: {
+                       CHECK_SRCDST_NCOM;
+                       s390_slr  (code, ins->dreg, src2);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NC, "OverflowException");
+               }
+                       break;
+               case OP_SUB_OVF_CARRY: {
+                       CHECK_SRCDST_NCOM;
+                       s390_lhi  (code, s390_r0, 0);
+                       s390_lr   (code, s390_r1, s390_r0);
+                       s390_slbr (code, s390_r0, s390_r1);
+                       s390_sr   (code, ins->dreg, src2);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException");
+                       s390_ar   (code, ins->dreg, s390_r0);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_OV, "OverflowException");
+               }
+                       break;
+               case OP_SUB_OVF_UN_CARRY: {
+                       CHECK_SRCDST_NCOM;
+                       s390_slbr (code, ins->dreg, src2);
+                       EMIT_COND_SYSTEM_EXCEPTION (S390_CC_NC, "OverflowException");
                }
                        break;
                case CEE_AND: {
@@ -3331,7 +3733,7 @@ guint8 cond;
                }
                        break;
                case OP_AND_IMM: {
-                       if (s390_is_imm16 (ins->inst_imm)) {
+                       if (s390_is_uimm16 (ins->inst_imm)) {
                                s390_lhi  (code, s390_r0, ins->inst_imm);
                                if (ins->dreg != ins->sreg1) {
                                        s390_lr   (code, ins->dreg, ins->sreg1);
@@ -3363,7 +3765,7 @@ guint8 cond;
                }
                        break;
                case OP_DIV_IMM: {
-                       if (s390_is_imm16 (ins->inst_imm)) {
+                       if (s390_is_uimm16 (ins->inst_imm)) {
                                s390_lhi  (code, s390_r13, ins->inst_imm);
                                s390_lr   (code, s390_r0, ins->sreg1);
                        } else {
@@ -3374,7 +3776,7 @@ guint8 cond;
                                s390_l    (code, s390_r13, 0, s390_r13, 4);
                        }
                        s390_srda (code, s390_r0, 0, 32);
-                       s390_dr   (code, s390_r0, ins->sreg2);
+                       s390_dr   (code, s390_r0, s390_r13);
                        s390_lr   (code, ins->dreg, s390_r1);
                }
                        break;
@@ -3392,7 +3794,7 @@ guint8 cond;
                }
                        break;
                case OP_REM_IMM: {
-                       if (s390_is_imm16 (ins->inst_imm)) {
+                       if (s390_is_uimm16 (ins->inst_imm)) {
                                s390_lhi  (code, s390_r13, ins->inst_imm);
                                s390_lr   (code, s390_r0, ins->sreg1);
                        } else {
@@ -3403,7 +3805,7 @@ guint8 cond;
                                s390_l    (code, s390_r13, 0, s390_r13, 4);
                        }
                        s390_srda (code, s390_r0, 0, 32);
-                       s390_dr   (code, s390_r0, ins->sreg2);
+                       s390_dr   (code, s390_r0, s390_r13);
                        s390_lr   (code, ins->dreg, s390_r0);
                }
                        break;
@@ -3423,19 +3825,20 @@ guint8 cond;
                }
                        break;
                case OP_OR_IMM: {
-                       if (s390_is_imm16 (ins->inst_imm)) {
+                       if (s390_is_uimm16 (ins->inst_imm)) {
                                s390_lhi  (code, s390_r0, ins->inst_imm);
                                if (ins->dreg != ins->sreg1) {
                                        s390_lr   (code, ins->dreg, ins->sreg1);
                                }
                                s390_or   (code, ins->dreg, s390_r0);
                        } else {
-                               s390_bras (code, s390_r13, 4);
+                               s390_basr (code, s390_r13, 0);
+                               s390_j    (code, 4);
                                s390_word (code, ins->inst_imm);
                                if (ins->dreg != ins->sreg1) {
                                        s390_lr   (code, ins->dreg, ins->sreg1);
                                }
-                               s390_o    (code, ins->dreg, 0, s390_r13, 0);
+                               s390_o    (code, ins->dreg, 0, s390_r13, 4);
                        }
                }
                        break;
@@ -3455,7 +3858,7 @@ guint8 cond;
                }
                        break;
                case OP_XOR_IMM: {
-                       if (s390_is_imm16 (ins->inst_imm)) {
+                       if (s390_is_uimm16 (ins->inst_imm)) {
                                s390_lhi  (code, s390_r0, ins->inst_imm);
                                if (ins->dreg != ins->sreg1) {
                                        s390_lr   (code, ins->dreg, ins->sreg1);
@@ -3473,10 +3876,8 @@ guint8 cond;
                }
                        break;
                case CEE_SHL: {
-                       if (ins->sreg1 != ins->dreg) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
-                       }
-                       s390_sll  (code, ins->dreg, ins->sreg2, 0);
+                       CHECK_SRCDST_NCOM;
+                       s390_sll  (code, ins->dreg, src2, 0);
                }
                        break;
                case OP_SHL_IMM: {
@@ -3487,10 +3888,8 @@ guint8 cond;
                }
                        break;
                case CEE_SHR: {
-                       if (ins->sreg1 != ins->dreg) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
-                       }
-                       s390_sra  (code, ins->dreg, ins->sreg2, 0);
+                       CHECK_SRCDST_NCOM;
+                       s390_sra  (code, ins->dreg, src2, 0);
                }
                        break;
                case OP_SHR_IMM: {
@@ -3508,10 +3907,8 @@ guint8 cond;
                }
                        break;
                case CEE_SHR_UN: {
-                       if (ins->sreg1 != ins->dreg) {
-                               s390_lr   (code, ins->dreg, ins->sreg1);
-                       }
-                       s390_srl  (code, ins->dreg, ins->sreg2, 0);
+                       CHECK_SRCDST_NCOM;
+                       s390_srl  (code, ins->dreg, src2, 0);
                }
                        break;
                case CEE_NOT: {
@@ -3542,7 +3939,7 @@ guint8 cond;
                }
                        break;
                case OP_MUL_IMM: {
-                       if (s390_is_imm16 (ins->inst_imm)) {
+                       if (s390_is_uimm16 (ins->inst_imm)) {
                                s390_lhi  (code, s390_r13, ins->inst_imm);
                        } else {
                                s390_basr (code, s390_r13, 0);
@@ -3557,11 +3954,21 @@ guint8 cond;
                }
                        break;
                case CEE_MUL_OVF: {
-                       s390_lhi  (code, s390_r0, 0);
-                       s390_lr   (code, s390_r1, ins->sreg1);
-                       s390_mr   (code, s390_r0, ins->sreg2);
+                       short int *o[2];
+                       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_lr   (code, ins->dreg, s390_r1);
                }
                        break;
@@ -3576,22 +3983,22 @@ guint8 cond;
                        break;
                case OP_LMUL: {
                        s390_l    (code, s390_r0, 0, ins->sreg1, 4);
-                       s390_lr   (code, s390_r14, s390_r0);
                        s390_srda (code, s390_r0, 0, 32);
                        s390_m    (code, s390_r0, 0, ins->sreg2, 4);
-                       s390_srl  (code, s390_r14, 0, 31);
-                       s390_a    (code, s390_r14, 0, ins->sreg1, 0);
+                       s390_l    (code, s390_r0, 0, ins->sreg1, 4);
+                       s390_srl  (code, s390_r0, 0, 31);
+                       s390_a    (code, s390_r0, 0, ins->sreg1, 0);
                        s390_l    (code, s390_r13, 0, ins->sreg2, 0);
                        s390_srl  (code, s390_r13, 0, 31);
                        s390_ms   (code, s390_r13, 0, ins->sreg1, 4);
-                       s390_ar   (code, s390_r14, s390_r13);
-                       s390_st   (code, s390_r14, 0, ins->dreg, 0);
+                       s390_ar   (code, s390_r0, s390_r13);
+                       s390_st   (code, s390_r0, 0, ins->dreg, 0);
                        s390_st   (code, s390_r1, 0, ins->dreg, 4);
                }
                        break;  
                case OP_ICONST:
                case OP_SETREGIMM: {
-                       if (s390_is_imm16(ins->inst_c0)) {
+                       if (s390_is_uimm16(ins->inst_c0)) {
                                s390_lhi  (code, ins->dreg, ins->inst_c0);
                        } else {
                                s390_basr (code, s390_r13, 0);
@@ -3638,19 +4045,91 @@ guint8 cond;
                        }
                }
                        break;
-               case OP_FCONV_TO_R4: {
+               case OP_S390_SETF4RET: {
                        s390_ledbr (code, ins->dreg, ins->sreg1);
                }
                        break;
-               case CEE_JMP:
-                       g_assert_not_reached ();
+               case OP_TLS_GET: {
+                       if (s390_is_uimm16 (ins->inst_offset)) {
+                               s390_lhi (code, s390_r13, ins->inst_offset);
+                       } else {
+                               s390_bras (code, s390_r13, 0);
+                               s390_j    (code, 4);
+                               s390_word (code, ins->inst_offset);
+                               s390_l    (code, s390_r13, 0, s390_r13, 4);
+                       }
+                       s390_ear (code, s390_r1, 0);
+                       s390_l   (code, ins->dreg, s390_r13, s390_r1, 0);
+               }
+                       break;
+               case OP_FCONV_TO_R4: {
+                       if ((ins->next) &&
+                           (ins->next->opcode != OP_STORER4_MEMBASE_REG))
+                               s390_ledbr (code, ins->dreg, ins->sreg1);
+               }
+                       break;
+               case CEE_JMP: {
+                       int iParm, fParm, pOffset;
+                       if (cfg->method->save_lmf)
+                               restoreLMF(code, cfg->frame_reg, cfg->stack_usage);
+
+                       if (cfg->flags & MONO_CFG_HAS_TAIL) {
+                               pOffset = S390_PARM_SAVE_OFFSET;
+                               s390_l  (code, s390_r13, 0, STK_BASE, 0);
+                               for (iParm = s390_r2; 
+                                    iParm <= s390_r5; 
+                                    iParm++, pOffset+sizeof(gint32)) {
+                                       if (cfg->used_int_regs & (1 << iParm))
+                                               s390_l (code, iParm, 0, s390_r13, pOffset);
+                               }
+                               pOffset = S390_FLOAT_SAVE_OFFSET;
+                               for (fParm = 0; 
+                                    fParm < 4; 
+                                    fParm++, pOffset+sizeof(double)) 
+                                       s390_ld (code, fParm, 0, s390_r13, pOffset);
+                       }
+
+                       code = backUpStackPtr(cfg, code);
+                       s390_l   (code, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
+                       mono_add_patch_info (cfg, code - cfg->native_code,
+                                            MONO_PATCH_INFO_METHOD_JUMP,
+                                            ins->inst_p0);
+                       s390_jcl (code, S390_CC_UN, 0);
+               }
                        break;
                case OP_CHECK_THIS: {
                        /* ensure ins->sreg1 is not NULL */
                        s390_icm (code, s390_r0, 15, ins->sreg1, 0);
                }
                        break;
-               case OP_FCALL:
+               case OP_ARGLIST: {
+                       int offset = cfg->sig_cookie + cfg->stack_usage;
+
+                       if (s390_is_uimm16 (offset))
+                               s390_lhi  (code, s390_r0, offset);
+                       else {
+                               s390_basr (code, s390_r13, 0);
+                               s390_j    (code, 4);
+                               s390_word (code, offset);
+                               s390_l    (code, s390_r0, 0, s390_r13, 0);
+                       }
+                       s390_ar   (code, s390_r0, cfg->frame_reg);
+                       s390_st   (code, s390_r0, 0, ins->sreg1, 0);
+               }
+                       break;
+               case OP_FCALL: {
+                       call = (MonoCallInst*)ins;
+                       if (ins->flags & MONO_INST_HAS_METHOD)
+                               mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, 
+                                                    call->method);
+                       else
+                               mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, 
+                                                    call->fptr);
+                       s390_brasl (code, s390_r14, 0);
+                       if (call->signature->ret->type == MONO_TYPE_R4)
+                               s390_ldebr (code, s390_f0, s390_f0);
+               }
+                       break;
                case OP_LCALL:
                case OP_VCALL:
                case OP_VOIDCALL:
@@ -3663,7 +4142,14 @@ guint8 cond;
                        s390_brasl (code, s390_r14, 0);
                }
                        break;
-               case OP_FCALL_REG:
+               case OP_FCALL_REG: {
+                       call = (MonoCallInst*)ins;
+                       s390_lr   (code, s390_r1, ins->sreg1);
+                       s390_basr (code, s390_r14, s390_r1);
+                       if (call->signature->ret->type == MONO_TYPE_R4)
+                               s390_ldebr (code, s390_f0, s390_f0);
+               }
+                       break;
                case OP_LCALL_REG:
                case OP_VCALL_REG:
                case OP_VOIDCALL_REG:
@@ -3672,7 +4158,14 @@ guint8 cond;
                        s390_basr (code, s390_r14, s390_r1);
                }
                        break;
-               case OP_FCALL_MEMBASE:
+               case OP_FCALL_MEMBASE: {
+                       call = (MonoCallInst*)ins;
+                       s390_l    (code, s390_r1, 0, ins->sreg1, ins->inst_offset);
+                       s390_basr (code, s390_r14, s390_r1);
+                       if (call->signature->ret->type == MONO_TYPE_R4)
+                               s390_ldebr (code, s390_f0, s390_f0);
+               }
+                       break;
                case OP_LCALL_MEMBASE:
                case OP_VCALL_MEMBASE:
                case OP_VOIDCALL_MEMBASE:
@@ -3685,7 +4178,12 @@ guint8 cond;
                        g_assert_not_reached ();
                        break;
                case OP_LOCALLOC: {
+                       int alloca_skip = S390_MINIMAL_STACK_SIZE + cfg->param_area + 
+                                         S390_STACK_ALIGNMENT - 1;
+                       int area_offset = S390_ALIGN(alloca_skip, S390_STACK_ALIGNMENT);
                        s390_lr   (code, s390_r1, ins->sreg1);
+                       if (ins->flags & MONO_INST_INIT)
+                               s390_lr   (code, s390_r0, ins->sreg1);
                        s390_ahi  (code, s390_r1, 14);
                        s390_srl  (code, s390_r1, 0, 3);
                        s390_sll  (code, s390_r1, 0, 3);
@@ -3693,9 +4191,18 @@ guint8 cond;
                        s390_lcr  (code, s390_r1, s390_r1);
                        s390_la   (code, STK_BASE, STK_BASE, s390_r1, 0);
                        s390_st   (code, s390_r13, 0, STK_BASE, 0);
-                       s390_la   (code, ins->dreg, 0, STK_BASE, S390_MINIMAL_STACK_SIZE+7);
+                       s390_la   (code, ins->dreg, 0, STK_BASE, area_offset);
                        s390_srl  (code, ins->dreg, 0, 3);
                        s390_sll  (code, ins->dreg, 0, 3);
+                       if (ins->flags & MONO_INST_INIT) {
+                               s390_lr   (code, s390_r1, s390_r0);
+                               s390_lr   (code, s390_r0, ins->dreg);
+                               s390_lr   (code, s390_r14, s390_r12);
+                               s390_lhi  (code, s390_r13, 0);
+                               s390_mvcle(code, s390_r0, s390_r12, 0, 0);
+                               s390_jo   (code, -2);
+                               s390_lr   (code, s390_r12, s390_r14);
+                       }
                }
                        break;
                case CEE_RET: {
@@ -3709,21 +4216,57 @@ guint8 cond;
                        s390_brasl (code, s390_r14, 0);
                }
                        break;
+               case OP_RETHROW: {
+                       s390_lr (code, s390_r2, ins->sreg1);
+                       mono_add_patch_info (cfg, code-cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
+                                            (gpointer)"mono_arch_rethrow_exception");
+                       s390_brasl (code, s390_r14, 0);
+               }
+                       break;
                case OP_START_HANDLER: {
-                       s390_lr   (code, s390_r0, s390_r14);
-                       s390_st   (code, s390_r0, 0, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
+                       if (s390_is_uimm12 (ins->inst_left->inst_offset)) {
+                               s390_st   (code, s390_r14, 0, 
+                                          ins->inst_left->inst_basereg, 
+                                          ins->inst_left->inst_offset);
+                       } else {
+                               s390_basr (code, s390_r13, 0);
+                               s390_j    (code, 4);
+                               s390_word (code, ins->inst_left->inst_offset);
+                               s390_l    (code, s390_r13, 0, s390_r13, 4);
+                               s390_st   (code, s390_r14, s390_r13, 
+                                          ins->inst_left->inst_basereg, 0);
+                       }
                }
                        break;
                case OP_ENDFILTER: {
                        if (ins->sreg1 != s390_r2)
                                s390_lr (code, s390_r2, ins->sreg1);
-                       s390_l   (code, STK_BASE, 0, STK_BASE, 0);
-                       s390_lm  (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
+                       if (s390_is_uimm12 (ins->inst_left->inst_offset)) {
+                               s390_l  (code, s390_r14, 0, ins->inst_left->inst_basereg,
+                                        ins->inst_left->inst_offset);
+                       } else {
+                               s390_basr (code, s390_r13, 0);
+                               s390_j    (code, 4);
+                               s390_word (code, ins->inst_left->inst_offset);
+                               s390_l    (code, s390_r13, 0, s390_r13, 4);
+                               s390_l    (code, s390_r14, s390_r13, 
+                                          ins->inst_left->inst_basereg, 0);
+                       }
                        s390_br  (code, s390_r14);
                }
                        break;
                case CEE_ENDFINALLY: {
-                       s390_l   (code, s390_r14, 0, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
+                       if (s390_is_uimm12 (ins->inst_left->inst_offset)) {
+                               s390_l  (code, s390_r14, 0, ins->inst_left->inst_basereg,
+                                        ins->inst_left->inst_offset);
+                       } else {
+                               s390_basr (code, s390_r13, 0);
+                               s390_j    (code, 4);
+                               s390_word (code, ins->inst_left->inst_offset);
+                               s390_l    (code, s390_r13, 0, s390_r13, 4);
+                               s390_l    (code, s390_r14, s390_r13, 
+                                          ins->inst_left->inst_basereg, 0);
+                       }
                        s390_br  (code, s390_r14);
                }
                        break;
@@ -3846,14 +4389,13 @@ guint8 cond;
                        break;
                case OP_R4CONST: {
                        if (*((float *) ins->inst_p0) == 0) {
-                               s390_lzer (code, ins->dreg);
+                               s390_lzdr (code, ins->dreg);
                        } else {
                                s390_basr (code, s390_r13, 0);
                                s390_j    (code, 4);
                                s390_word (code, ins->inst_p0);
                                s390_l    (code, s390_r13, 0, s390_r13, 4);
-                               s390_le   (code, ins->dreg, 0, s390_r13, 0);
-                               s390_ldebr(code, ins->dreg, ins->dreg);
+                               s390_ldeb (code, ins->dreg, 0, s390_r13, 0);
                        }
                }
                        break;
@@ -3883,29 +4425,28 @@ guint8 cond;
                        break;
                case OP_STORER4_MEMBASE_REG: {
                        if (s390_is_uimm12(ins->inst_offset)) {
-                               s390_ledbr(code, s390_f0, ins->sreg1);
-                               s390_ste  (code, s390_f0, 0, ins->inst_destbasereg, ins->inst_offset);
+                               s390_ledbr(code, s390_f15, ins->sreg1);
+                               s390_ste  (code, s390_f15, 0, ins->inst_destbasereg, ins->inst_offset);
                        } else {
                                s390_basr (code, s390_r13, 0);
                                s390_j    (code, 4);
                                s390_word (code, ins->inst_offset);
                                s390_l    (code, s390_r13, 0, s390_r13, 4);
-                               s390_ledbr(code, s390_f0, ins->sreg1);
-                               s390_ste  (code, s390_f0, s390_r13, ins->inst_destbasereg, 0);
+                               s390_ledbr(code, s390_f15, ins->sreg1);
+                               s390_ste  (code, s390_f15, s390_r13, ins->inst_destbasereg, 0);
                        }
                }
                        break;
                case OP_LOADR4_MEMBASE: {
                        if (s390_is_uimm12(ins->inst_offset)) {
-                               s390_le   (code, ins->dreg, 0, ins->inst_basereg, ins->inst_offset);
+                               s390_ldeb (code, ins->dreg, 0, ins->inst_basereg, ins->inst_offset);
                        } else {
                                s390_basr (code, s390_r13, 0);
                                s390_j    (code, 4);
                                s390_word (code, ins->inst_offset);
                                s390_l    (code, s390_r13, 0, s390_r13, 4);
-                               s390_le   (code, ins->dreg, s390_r13, ins->inst_basereg, 0);
+                               s390_ldeb (code, ins->dreg, s390_r13, ins->inst_basereg, 0);
                        }
-                       s390_ldebr (code, ins->dreg, ins->dreg);
                }
                        break;
                case CEE_CONV_R_UN: {
@@ -3959,17 +4500,18 @@ guint8 cond;
                case OP_LCONV_TO_OVF_I: {
                        /* Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000 */
                        short int *o[5];
-                       s390_ltr  (code, ins->sreg2, ins->sreg2);
-                       s390_jnl  (code, 0); CODEPTR(code, o[0]);
                        s390_ltr  (code, ins->sreg1, ins->sreg1);
+                       s390_jnl  (code, 0); CODEPTR(code, o[0]);
+                       s390_ltr  (code, ins->sreg2, ins->sreg2);
                        s390_jnl  (code, 0); CODEPTR(code, o[1]);
                        s390_lhi  (code, s390_r13, -1);
-                       s390_cr   (code, ins->sreg1, s390_r13);
+                       s390_cr   (code, ins->sreg2, s390_r13);
                        s390_jnz  (code, 0); CODEPTR(code, o[2]);
-                       if (ins->dreg != ins->sreg2)
-                               s390_lr   (code, ins->dreg, ins->sreg2);
+                       if (ins->dreg != ins->sreg1)
+                               s390_lr   (code, ins->dreg, ins->sreg1);
                        s390_j    (code, 0); CODEPTR(code, o[3]);
                        PTRSLOT(code, o[0]);
+                       s390_ltr  (code, ins->sreg2, ins->sreg2);
                        s390_jz   (code, 0); CODEPTR(code, o[4]);
                        PTRSLOT(code, o[1]);
                        PTRSLOT(code, o[2]);
@@ -3998,12 +4540,8 @@ guint8 cond;
                }
                        break;
                case OP_FSUB: {
-                       if (ins->dreg == ins->sreg1)
-                               s390_sdbr (code, ins->dreg, ins->sreg2);
-                       else {
-                               s390_ldr  (code, ins->dreg, ins->sreg1);
-                               s390_sdbr (code, ins->dreg, ins->sreg2);
-                       }
+                       CHECK_SRCDST_NCOM_F;
+                       s390_sdbr (code, ins->dreg, src2);
                }
                        break;          
                case OP_FMUL: {
@@ -4020,12 +4558,8 @@ guint8 cond;
                }
                        break;          
                case OP_FDIV: {
-                       if (ins->dreg == ins->sreg1)
-                               s390_ddbr (code, ins->dreg, ins->sreg2);
-                       else {
-                               s390_ldr  (code, ins->dreg, ins->sreg1);
-                               s390_ddbr (code, ins->dreg, ins->sreg2);
-                       }
+                       CHECK_SRCDST_NCOM_F;
+                       s390_ddbr (code, ins->dreg, src2);
                }
                        break;          
                case OP_FNEG: {
@@ -4033,9 +4567,7 @@ guint8 cond;
                }
                        break;          
                case OP_FREM: {
-                       if (ins->dreg != ins->sreg1) {
-                               s390_ldr  (code, ins->dreg, ins->sreg1);
-                       }
+                       CHECK_SRCDST_NCOM_F;
                        s390_didbr (code, ins->dreg, ins->sreg2, 5, s390_f15);
                }
                        break;
@@ -4110,15 +4642,74 @@ guint8 cond;
                        break;
                case CEE_CKFINITE: {
                        short *o;
-                       s390_lhi  (code, s390_r13, 0xfc0);
+                       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 + 2
+                       mono_add_patch_info (cfg, code - cfg->native_code, 
                                             MONO_PATCH_INFO_EXC, "ArithmeticException");
                        s390_brasl (code, s390_r14,0);
                        PTRSLOT(code, o);
                }
                        break;
+               case OP_S390_MOVE: {
+                       if (ins->unused > 0) {
+                               if (ins->unused <= 256) {
+                                       s390_mvc  (code, ins->unused, ins->dreg, 
+                                                  ins->inst_offset, ins->sreg1, ins->inst_imm);
+                               } else {
+                                       s390_lr   (code, s390_r0, ins->dreg);
+                                       if (s390_is_uimm16 (ins->inst_offset)) {
+                                               s390_ahi  (code, s390_r0, ins->inst_offset);
+                                       } else {
+                                               s390_basr (code, s390_r13, 0);
+                                               s390_j    (code, 4);
+                                               s390_word (code, ins->inst_offset);
+                                               s390_a    (code, s390_r0, 0, s390_r13, 4);
+                                       }
+                                       s390_lr   (code, s390_r14, s390_r12);
+                                       s390_lr   (code, s390_r12, ins->sreg1);
+                                       if (s390_is_uimm16 (ins->inst_imm)) {
+                                               s390_ahi  (code, s390_r12, ins->inst_imm);
+                                       } else {
+                                               s390_basr (code, s390_r13, 0);
+                                               s390_j    (code, 4);
+                                                       s390_word (code, ins->inst_imm);
+                                               s390_a    (code, s390_r12, 0, s390_r13, 4);
+                                       }
+                                       s390_lr   (code, s390_r1, ins->sreg1);
+                                       s390_lr   (code, s390_r13, s390_r1);
+                                       s390_mvcle(code, s390_r0, s390_r12, 0, 0);
+                                       s390_jo   (code, -2);
+                                       s390_lr   (code, s390_r12, s390_r14);
+                               }
+                       }
+               }
+                       break;
+               case OP_ATOMIC_ADD_I4: {
+                       s390_lr  (code, s390_r1, ins->sreg2);
+                       s390_l   (code, s390_r0, 0, ins->inst_basereg, ins->inst_offset);
+                       s390_a   (code, s390_r1, 0, ins->inst_basereg, ins->inst_offset);
+                       s390_cs  (code, s390_r0, s390_r1, ins->inst_basereg, ins->inst_offset);
+                       s390_jnz (code, -7);
+                       s390_lr  (code, ins->dreg, s390_r1);
+               }
+                       break;  
+               case OP_ATOMIC_ADD_NEW_I4: {
+                       s390_lr  (code, s390_r1, ins->sreg2);
+                       s390_l   (code, s390_r0, 0, ins->inst_basereg, ins->inst_offset);
+                       s390_a   (code, s390_r1, 0, ins->inst_basereg, ins->inst_offset);
+                       s390_cs  (code, s390_r0, s390_r1, ins->inst_basereg, ins->inst_offset);
+                       s390_jnz (code, -7);
+                       s390_lr  (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_lr  (code, ins->dreg, s390_r0);
+               }
+                       break;  
                default:
                        g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
                        g_assert_not_reached ();
@@ -4171,146 +4762,50 @@ mono_arch_register_lowlevel_calls (void)
 /*------------------------------------------------------------------*/
 
 void
-mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
+mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, 
+                     guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
 {
        MonoJumpInfo *patch_info;
 
        for (patch_info = ji; patch_info; patch_info = patch_info->next) {
                unsigned char *ip = patch_info->ip.i + code;
-               gint32 target = 0;
+               gconstpointer target = NULL;
+
+               target = mono_resolve_patch_target (method, domain, code, 
+                                                   patch_info, run_cctors);
 
                switch (patch_info->type) {
-               case MONO_PATCH_INFO_BB:
-                       target = S390_RELATIVE((patch_info->data.bb->native_offset+code),
-                                              ip);
-                       ip    += 2;     /* Skip over op-code */
-                       break;
-               case MONO_PATCH_INFO_ABS:
-                       target = S390_RELATIVE(patch_info->data.target, ip);
-                       ip    += 2;     /* Skip over op-code */
-                       break;
-               case MONO_PATCH_INFO_LABEL:
-                       target = S390_RELATIVE((patch_info->data.inst->inst_c0+code),ip);
-                       ip    += 2;     /* Skip over op-code */
-                       break;
-               case MONO_PATCH_INFO_IP:
-                       target = ip;
-                       continue;
-               case MONO_PATCH_INFO_METHOD_REL:
-                       g_assert_not_reached ();
-                       *((gpointer *)(ip)) = code + patch_info->data.offset;
-                       continue;
-               case MONO_PATCH_INFO_INTERNAL_METHOD: {
-                       MonoJitICallInfo *mi = mono_find_jit_icall_by_name (patch_info->data.name);
-                       if (!mi) {
-                               g_warning ("unknown MONO_PATCH_INFO_INTERNAL_METHOD %s", patch_info->data.name);
+                       case MONO_PATCH_INFO_IP:
+                       case MONO_PATCH_INFO_EXC_NAME:
+                       case MONO_PATCH_INFO_LDSTR:
+                       case MONO_PATCH_INFO_TYPE_FROM_HANDLE: 
+                       case MONO_PATCH_INFO_LDTOKEN: 
+                       case MONO_PATCH_INFO_EXC:
+                               continue;
+                       case MONO_PATCH_INFO_SWITCH: 
+                               /*----------------------------------*/
+                               /* ip points at the basr r13,0/j +4 */
+                               /* instruction the vtable value     */
+                               /* follows this (i.e. ip+6)         */
+                               /*----------------------------------*/
+                               *((gconstpointer *)(ip+6)) = target;
+                               target = NULL;
+                               continue;
+                       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:
+                               target = S390_RELATIVE(target, ip);
+                               continue;
+                       case MONO_PATCH_INFO_R4:
+                       case MONO_PATCH_INFO_R8:
+                       case MONO_PATCH_INFO_METHOD_REL:
                                g_assert_not_reached ();
-                       }
-                       target = S390_RELATIVE(mono_icall_get_wrapper (mi), ip);
-                       ip    += 2;     /* Skip over op-code */
-                       break;
-               }
-               case MONO_PATCH_INFO_METHOD_JUMP:
-                       g_assert_not_reached ();
-                       break;
-               case MONO_PATCH_INFO_METHOD:
-                       if (patch_info->data.method == method) {
-                               target = S390_RELATIVE(code, ip);
-                       } else {
-                               /* get the trampoline to the method from the domain */
-                               target = S390_RELATIVE(mono_arch_create_jit_trampoline (patch_info->data.method), ip);
-                               target = mono_arch_create_jit_trampoline(patch_info->data.method);
+                               continue;
+                       default:
                                target = S390_RELATIVE(target, ip);
-                       }
-                       ip    += 2;     /* Skip over op-code */
-                       break;
-               case MONO_PATCH_INFO_SWITCH: {
-                       gpointer *table = (gpointer *)patch_info->data.target;
-                       int i;
-                       /*------------------------------------------------------*/
-                       /* ip is pointing at the basr r13,0/j +4 instruction    */
-                       /* the vtable value follows this (i.e. ip+6)            */
-                       /*------------------------------------------------------*/
-                       *((gconstpointer *)(ip+6)) = table;
-
-                       for (i = 0; i < patch_info->table_size; i++) {
-                               table [i] = (int)patch_info->data.table [i] + code;
-                       }
-                       continue;
-               }
-               case MONO_PATCH_INFO_METHODCONST:
-               case MONO_PATCH_INFO_CLASS:
-               case MONO_PATCH_INFO_IMAGE:
-               case MONO_PATCH_INFO_FIELD:
-                       target = S390_RELATIVE(patch_info->data.target, ip);
-                       continue;
-               case MONO_PATCH_INFO_R4:
-               case MONO_PATCH_INFO_R8:
-                       g_assert_not_reached ();
-                       *((gconstpointer *)(ip + 2)) = patch_info->data.target;
-                       continue;
-               case MONO_PATCH_INFO_IID:
-                       mono_class_init (patch_info->data.klass);
-                       target = S390_RELATIVE(patch_info->data.klass->interface_id, ip);
-                       continue;                       
-               case MONO_PATCH_INFO_VTABLE:
-                       target = S390_RELATIVE(mono_class_vtable (domain, patch_info->data.klass),ip);
-                       ip += 2;
-                       continue;
-               case MONO_PATCH_INFO_CLASS_INIT:
-                       target = S390_RELATIVE(mono_create_class_init_trampoline (mono_class_vtable (domain, patch_info->data.klass)), ip);
-                       ip += 2;
-                       break;
-               case MONO_PATCH_INFO_SFLDA: {
-                       MonoVTable *vtable = mono_class_vtable (domain, patch_info->data.field->parent);
-                       if (!vtable->initialized && !(vtable->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) && mono_class_needs_cctor_run (vtable->klass, method))
-                               /* Done by the generated code */
-                               ;
-                       else {
-                               if (run_cctors)
-                                       mono_runtime_class_init (vtable);
-                       }
-                       target = S390_RELATIVE((char*)vtable->data + patch_info->data.field->offset, ip);
-                       ip += 2;
-                       continue;
-               }
-               case MONO_PATCH_INFO_EXC_NAME:
-                       *((gconstpointer *)(ip)) = patch_info->data.name;
-                       continue;
-               case MONO_PATCH_INFO_LDSTR:
-                       target = mono_ldstr (domain, patch_info->data.token->image, 
-                                            mono_metadata_token_index (patch_info->data.token->token));
-                       continue;
-               case MONO_PATCH_INFO_TYPE_FROM_HANDLE: {
-                       gpointer handle;
-                       MonoClass *handle_class;
-
-                       handle = mono_ldtoken (patch_info->data.token->image, 
-                                              patch_info->data.token->token, 
-                                              &handle_class, NULL);
-                       mono_class_init (handle_class);
-                       mono_class_init (mono_class_from_mono_type (handle));
-
-                       target = handle;
-                       continue;
-               }
-               case MONO_PATCH_INFO_LDTOKEN: {
-                       gpointer handle;
-                       MonoClass *handle_class;
-
-                       handle = mono_ldtoken (patch_info->data.token->image,
-                                              patch_info->data.token->token, 
-                                              &handle_class, NULL);
-                       mono_class_init (handle_class);
-
-                       target = handle;
-                       continue;
-               }
-               case MONO_PATCH_INFO_EXC:
-                       /* everything is dealt with at epilog output time */
-                       continue;
-               default:
-                       g_assert_not_reached ();
+                               ip += 2;
                }
                s390_patch (ip, target);
        }
@@ -4318,41 +4813,6 @@ mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, Mono
 
 /*========================= End of Function ========================*/
 
-/*------------------------------------------------------------------*/
-/*                                                                  */
-/* Name                - mono_arch_max_epilog_size                         */
-/*                                                                  */
-/* Function    - Determine the maximum size of the epilog code.    */
-/*                                                                 */
-/*------------------------------------------------------------------*/
-
-int
-mono_arch_max_epilog_size (MonoCompile *cfg)
-{
-       int max_epilog_size = 96;
-       MonoJumpInfo *patch_info;
-       
-       if (cfg->method->save_lmf)
-               max_epilog_size += 128;
-       
-       if (mono_jit_trace_calls != NULL)
-               max_epilog_size += 128;
-
-       if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
-               max_epilog_size += 128;
-
-       /* count the number of exception infos */
-     
-       for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
-               if (patch_info->type == MONO_PATCH_INFO_EXC)
-                       max_epilog_size += 26;
-       }
-
-       return max_epilog_size;
-}
-
-/*========================= End of Function ========================*/
-
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* Name                - mono_arch_emit_prolog                             */
@@ -4369,42 +4829,37 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        MonoBasicBlock *bb;
        MonoMethodSignature *sig;
        MonoInst *inst;
-       int alloc_size, pos, max_offset, i, lmfOffset;
+       int alloc_size, pos, max_offset, i;
        guint8 *code;
        CallInfo *cinfo;
        size_data sz;
        int tracing = 0;
+       int lmfOffset;                                                          \
 
        if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
                tracing = 1;
 
-       cfg->code_size   = 256;
+       cfg->code_size   = 512;
        cfg->native_code = code = g_malloc (cfg->code_size);
 
-       s390_stm  (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
+       if (cfg->flags & MONO_CFG_HAS_TAIL) {
+               s390_stm (code, s390_r2, s390_r14, STK_BASE, S390_PARM_SAVE_OFFSET);
+               for (pos = 0; pos < 4; pos++)
+                       s390_std (code, pos, 0, STK_BASE, 
+                                 S390_FLOAT_SAVE_OFFSET+pos*sizeof(double));
+       } else { 
+               s390_stm  (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
+        }
 
        if (cfg->flags & MONO_CFG_HAS_ALLOCA) {
                cfg->used_int_regs |= 1 << 11;
        }
 
        alloc_size = cfg->stack_offset;
-//     if (tracing)
-//             alloc_size += S390_TRACE_STACK_SIZE;
-//     pos = 0;
-       /* reserve room to save return value */
-//     if (tracing)
-//             pos += 8;
-
-//     alloc_size += pos;
-
-//     if (method->save_lmf) 
-//             alloc_size += sizeof(MonoLMF);
-
-//     alloc_size = S390_ALIGN(alloc_size, S390_STACK_ALIGNMENT);
 
        cfg->stack_usage = alloc_size;
        s390_lr   (code, s390_r11, STK_BASE);
-       if (s390_is_imm16 (-alloc_size)) {
+       if (s390_is_uimm16 (alloc_size)) {
                s390_ahi  (code, STK_BASE, -alloc_size);
        } else { 
                int stackSize = alloc_size;
@@ -4416,7 +4871,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        }
        s390_st   (code, s390_r11, 0, STK_BASE, 0);
 
-       if (cfg->flags & MONO_CFG_HAS_ALLOCA)
+       if (cfg->frame_reg != STK_BASE)
                s390_lr (code, s390_r11, STK_BASE);
 
         /* compute max_offset in order to use short forward jumps
@@ -4438,7 +4893,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        }
 
        /* load arguments allocated to register from the stack */
-       sig = method->signature;
+       sig = mono_method_signature (method);
        pos = 0;
 
        cinfo = calculate_sizes (sig, &sz, sig->pinvoke);
@@ -4458,10 +4913,12 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                        if (ainfo->regtype == RegTypeGeneral)
                                s390_lr (code, inst->dreg, ainfo->reg);
                        else if (ainfo->regtype == RegTypeFP) {
-                               if (ainfo->size == 4) {
-                                       s390_ledbr (code, inst->dreg, ainfo->reg);
-                               } else {
-                                       s390_ldr   (code, inst->dreg, ainfo->reg);
+                               if (inst->dreg != ainfo->reg) {
+                                       if (ainfo->size == 4) {
+                                               s390_ledbr (code, inst->dreg, ainfo->reg);
+                                       } else {
+                                               s390_ldr   (code, inst->dreg, ainfo->reg);
+                                       }
                                }
                        }
                        else if (ainfo->regtype == RegTypeBase) {
@@ -4533,8 +4990,20 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                                break;
                                }
                        } else if (ainfo->regtype == RegTypeStructByAddr) {
-                               code = emit_memcpy (code, ainfo->vtsize, 
-                                                   inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
+                               if (ainfo->reg == STK_BASE) {
+                                       s390_lr  (code, s390_r13, ainfo->reg);
+                                       s390_ahi (code, s390_r13, alloc_size);
+                                       s390_l   (code, s390_r13, 0, s390_r13, 
+                                                 ainfo->offparm + S390_MINIMAL_STACK_SIZE);
+                                       code = emit_memcpy (code, abs(ainfo->vtsize), 
+                                                           inst->inst_basereg, 
+                                                           inst->inst_offset, s390_r13, 0);
+                               } else {
+                                       code = emit_memcpy (code, abs(ainfo->vtsize), 
+                                                           inst->inst_basereg, 
+                                                           inst->inst_offset, 
+                                                           ainfo->reg, 0);
+                               }
                        } else
                                g_assert_not_reached ();
                }
@@ -4561,57 +5030,61 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                /*---------------------------------------------------------------*/
                /* we build the MonoLMF structure on the stack - see mini-s390.h */
                /*---------------------------------------------------------------*/
-               lmfOffset = alloc_size - sizeof(MonoLMF);
-
-               s390_lr    (code, s390_r13, cfg->frame_reg);
-               s390_ahi   (code, s390_r13, lmfOffset);
-
-               /*---------------------------------------------------------------*/
-               /* Set lmf.lmf_addr = jit_tls->lmf                               */
-               /*---------------------------------------------------------------*/
-               s390_st    (code, s390_r2, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, lmf_addr));
-
-               /*---------------------------------------------------------------*/
-               /* Get current lmf                                               */
-               /*---------------------------------------------------------------*/
-               s390_l     (code, s390_r0, 0, s390_r2, 0);
-
-               /*---------------------------------------------------------------*/
-               /* Set our lmf as the current lmf                                */
-               /*---------------------------------------------------------------*/
-               s390_st    (code, s390_r13, 0, s390_r2, 0);
-
-               /*---------------------------------------------------------------*/
-               /* Have our lmf.previous_lmf point to the last lmf               */
-               /*---------------------------------------------------------------*/
-               s390_st    (code, s390_r0, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, previous_lmf));
-
-               /*---------------------------------------------------------------*/
-               /* save method info                                              */
-               /*---------------------------------------------------------------*/
-               s390_basr  (code, s390_r1, 0);
-               s390_j     (code, 4);
-               s390_word  (code, method);
-               s390_l     (code, s390_r1, 0, s390_r1, 4);
-               s390_st    (code, s390_r1, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, method));
-
-               /*---------------------------------------------------------------*/
-               /* save the current IP                                           */
-               /*---------------------------------------------------------------*/
+               lmfOffset = alloc_size - sizeof(MonoLMF);       
+                                                                                       
+               s390_lr    (code, s390_r13, cfg->frame_reg);            
+               s390_ahi   (code, s390_r13, lmfOffset);                                 
+                                                                                       
+               /*---------------------------------------------------------------*/     
+               /* Set lmf.lmf_addr = jit_tls->lmf                               */     
+               /*---------------------------------------------------------------*/     
+               s390_st    (code, s390_r2, 0, s390_r13,                                 
+                           G_STRUCT_OFFSET(MonoLMF, lmf_addr));                        
+                                                                                       
+               /*---------------------------------------------------------------*/     
+               /* Get current lmf                                               */     
+               /*---------------------------------------------------------------*/     
+               s390_l     (code, s390_r0, 0, s390_r2, 0);                              
+                                                                                       
+               /*---------------------------------------------------------------*/     
+               /* Set our lmf as the current lmf                                */     
+               /*---------------------------------------------------------------*/     
+               s390_st    (code, s390_r13, 0, s390_r2, 0);                             
+                                                                                       
+               /*---------------------------------------------------------------*/     
+               /* Have our lmf.previous_lmf point to the last lmf               */     
+               /*---------------------------------------------------------------*/     
+               s390_st    (code, s390_r0, 0, s390_r13,                                 
+                           G_STRUCT_OFFSET(MonoLMF, previous_lmf));                    
+                                                                                       
+               /*---------------------------------------------------------------*/     
+               /* save method info                                              */     
+               /*---------------------------------------------------------------*/     
+               s390_basr  (code, s390_r1, 0);                                          
+               s390_j     (code, 4);                                                   
+               s390_word  (code, method);                                              
+               s390_l     (code, s390_r1, 0, s390_r1, 4);                      
+               s390_st    (code, s390_r1, 0, s390_r13,                                 
+                           G_STRUCT_OFFSET(MonoLMF, method));                          
+                                                                               
+               /*---------------------------------------------------------------*/     
+               /* save the current IP                                           */     
+               /*---------------------------------------------------------------*/     
                s390_lr    (code, s390_r1, cfg->frame_reg);
-//             s390_ahi   (code, s390_r1, alloc_size);
-               s390_st    (code, s390_r1, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, ebp));
-               s390_l     (code, s390_r1, 0, s390_r1, S390_RET_ADDR_OFFSET);
-               s390_la    (code, s390_r1, 0, s390_r1, 0);
-               s390_st    (code, s390_r1, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, eip));
-
-               /*---------------------------------------------------------------*/
-               /* Save general and floating point registers                     */
-               /*---------------------------------------------------------------*/
-               s390_stm   (code, s390_r2, s390_r6, s390_r13, G_STRUCT_OFFSET(MonoLMF, gregs[2]));
-               for (i = 0; i < 16; i++) {
-                       s390_std  (code, i, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, fregs[i]));
-               }
+               s390_st    (code, s390_r1, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, ebp)); 
+               s390_basr  (code, s390_r1, 0);
+               s390_la    (code, s390_r1, 0, s390_r1, 0);                              
+               s390_st    (code, s390_r1, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, eip)); 
+                                                                                       
+               /*---------------------------------------------------------------*/     
+               /* Save general and floating point registers                     */     
+               /*---------------------------------------------------------------*/     
+               s390_stm   (code, s390_r2, s390_r12, s390_r13,                          
+                           G_STRUCT_OFFSET(MonoLMF, gregs[2]));                        
+               for (i = 0; i < 16; i++) {                                              
+                       s390_std  (code, i, 0, s390_r13,                                
+                                  G_STRUCT_OFFSET(MonoLMF, fregs[i]));                 
+               }                                                                       
 
                /*---------------------------------------------------------------*/
                /* Restore the parameter registers now that we've set up the lmf */
@@ -4646,10 +5119,24 @@ mono_arch_emit_epilog (MonoCompile *cfg)
 {
        MonoJumpInfo *patch_info;
        MonoMethod *method = cfg->method;
-       MonoMethodSignature *sig = method->signature;
-       MonoInst *inst;
-       int i, lmfOffset, tracing = 0;
+       int tracing = 0;
        guint8 *code;
+       int max_epilog_size = 96;
+       
+       if (cfg->method->save_lmf)
+               max_epilog_size += 128;
+       
+       if (mono_jit_trace_calls != NULL)
+               max_epilog_size += 128;
+
+       if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
+               max_epilog_size += 128;
+       
+       while ((cfg->code_len + max_epilog_size) > (cfg->code_size - 16)) {
+               cfg->code_size  *= 2;
+               cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
+               mono_jit_stats.code_reallocs++;
+       }
 
        code = cfg->native_code + cfg->code_len;
 
@@ -4658,71 +5145,118 @@ mono_arch_emit_epilog (MonoCompile *cfg)
                tracing = 1;
        }
        
-       if (method->save_lmf) {
-               s390_lr  (code, s390_r13, cfg->frame_reg);
-
-               lmfOffset = cfg->stack_usage -  sizeof(MonoLMF);
-
-               /*-------------------------------------------------*/
-               /* r13 = my lmf                                    */
-               /*-------------------------------------------------*/
-               s390_ahi (code, s390_r13, lmfOffset);
-
-               /*-------------------------------------------------*/
-               /* r6 = &jit_tls->lmf                              */
-               /*-------------------------------------------------*/
-               s390_l   (code, s390_r6, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, lmf_addr));
-
-               /*-------------------------------------------------*/
-               /* r0 = lmf.previous_lmf                           */
-               /*-------------------------------------------------*/
-               s390_l   (code, s390_r0, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, previous_lmf));
-
-               /*-------------------------------------------------*/
-               /* jit_tls->lmf = previous_lmf                     */
-               /*-------------------------------------------------*/
-               s390_l   (code, s390_r13, 0, s390_r6, 0);
-               s390_st  (code, s390_r0, 0, s390_r6, 0);
-       }
+       if (method->save_lmf) 
+               restoreLMF(code, cfg->frame_reg, cfg->stack_usage);
 
-       if (cfg->frame_reg != STK_BASE)
-               s390_lr  (code, STK_BASE, cfg->frame_reg);
+       if (cfg->flags & MONO_CFG_HAS_ALLOCA) 
+               s390_l   (code, STK_BASE, 0, STK_BASE, 0);
+       else
+               code = backUpStackPtr(cfg, code);
 
-       s390_ahi (code, STK_BASE, cfg->stack_usage);
        s390_lm  (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
        s390_br  (code, s390_r14);
 
-       /* add code to raise exceptions */
+       cfg->code_len = code - cfg->native_code;
+
+       g_assert (cfg->code_len < cfg->code_size);
+
+}
+
+/*========================= End of Function ========================*/
+
+/*------------------------------------------------------------------*/
+/*                                                                  */
+/* Name                - mono_arch_emit_exceptions                         */
+/*                                                                  */
+/* Function    - Emit the blocks to handle exception conditions.   */
+/*                                                                 */
+/*------------------------------------------------------------------*/
+
+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], 
+                       *exc_throw_end [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 * 26;
+
+       while ((cfg->code_len + code_size) > (cfg->code_size - 16)) {
+               cfg->code_size  *= 2;
+               cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
+               mono_jit_stats.code_reallocs++; 
+       }
+
+       code = cfg->native_code + cfg->code_len;
+
+       /*---------------------------------------------------------------------*/
+       /* 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;
+                       guint32 throw_ip;
+
                        /*-----------------------------------------------------*/
                        /* Patch the branch in epilog to come here             */
                        /*-----------------------------------------------------*/
-                       s390_patch (patch_info->ip.i+cfg->native_code+2, 
-                                   S390_RELATIVE(code,patch_info->ip.i+cfg->native_code));
-                       /*-----------------------------------------------------*/
-                       /* Patch the parameter passed to the handler           */ 
-                       /*-----------------------------------------------------*/
-                       s390_basr (code, s390_r13, 0);
-                       s390_j    (code, 4);
-                       mono_add_patch_info (cfg, code - cfg->native_code,
-                                            MONO_PATCH_INFO_EXC_NAME,
-                                            patch_info->data.target);
-                       s390_word (code, 0);
-                       /*-----------------------------------------------------*/
-                       /* Load the return address and the parameter register  */
-                       /*-----------------------------------------------------*/
-                       s390_larl (code, s390_r14, S390_RELATIVE((patch_info->ip.i +
-                                                  cfg->native_code + 8), code));
-                       s390_l    (code, s390_r2, 0, s390_r13, 4);
-                       /*-----------------------------------------------------*/
-                       /* Reuse the current patch to set the jump             */
-                       /*-----------------------------------------------------*/
-                       patch_info->type      = MONO_PATCH_INFO_INTERNAL_METHOD;
-                       patch_info->data.name = "mono_arch_throw_exception_by_name";
-                       patch_info->ip.i      = code - cfg->native_code;
-                       s390_jcl  (code, S390_CC_UN, 0);
+                       s390_patch (ip + 2, S390_RELATIVE(code,ip));
+
+                       exc_class = mono_class_from_name (mono_defaults.corlib, 
+                                                         "System", 
+                                                         patch_info->data.name);
+                       g_assert (exc_class);
+                       throw_ip = patch_info->ip.i;
+
+                       for (iExc = 0; iExc < nThrows; ++iExc)
+                               if (exc_classes [iExc] == exc_class)
+                                       break;
+               
+                       if (iExc < nThrows) {
+                               s390_jcl (code, S390_CC_UN, 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_basr (code, s390_r13, 0);
+                               s390_j    (code, 4);
+                               s390_word (code, patch_info->data.target);
+                               /*---------------------------------------------*/
+                               /* Load return address & parameter register    */
+                               /*---------------------------------------------*/
+                               s390_larl (code, s390_r14, S390_RELATIVE((patch_info->ip.i +
+                                                          cfg->native_code + 8), code));
+                               s390_l    (code, s390_r2, 0, s390_r13, 4);
+                               /*---------------------------------------------*/
+                               /* Reuse the current patch to set the jump     */
+                               /*---------------------------------------------*/
+                               patch_info->type      = MONO_PATCH_INFO_INTERNAL_METHOD;
+                               patch_info->data.name = "mono_arch_throw_exception_by_name";
+                               patch_info->ip.i      = code - cfg->native_code;
+                               s390_jcl  (code, S390_CC_UN, 0);
+                       }
                        break;
                }
                default:
@@ -4750,6 +5284,65 @@ mono_arch_emit_epilog (MonoCompile *cfg)
 void
 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
 {
+#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
+       pthread_t       self = pthread_self();
+       pthread_attr_t  attr;
+       void            *stAddr = NULL;
+       size_t          stSize  = 0;
+       struct sigaltstack sa;
+#endif
+
+       if (!tls_offset_inited) {
+               tls_offset_inited = TRUE;
+
+#if HAVE_KW_THREAD
+# if 0
+       __asm__ ("\tear\t%r1,0\n"
+                "\tlr\t%0,%3\n"
+                "\tsr\t%0,%r1\n"
+                "\tlr\t%1,%4\n"
+                "\tsr\t%1,%r1\n"
+                "\tlr\t%2,%5\n"
+                "\tsr\t%2,%r1\n"
+                : "=r" (appdomain_tls_offset),
+                  "=r" (thread_tls_offset),
+                  "=r" (lmf_tls_offset)
+                : "r" (&tls_appdomain),
+                  "r" (&tls_current_object),
+                  "r" (&mono_lmf_addr)
+                : "1", "cc");
+# endif
+#endif
+       }               
+
+#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
+
+       /*----------------------------------------------------------*/
+       /* Determine stack boundaries                               */
+       /*----------------------------------------------------------*/
+       if (!mono_running_on_valgrind ()) {
+#ifdef HAVE_PTHREAD_GETATTR_NP
+               pthread_getattr_np( self, &attr );
+#elif HAVE_PTHREAD_ATTR_GET_NP
+               pthread_attr_get_np( self, &attr );
+#endif
+               pthread_attr_getstack( &attr, &stAddr, &stSize );
+       }
+
+
+       /*----------------------------------------------------------*/
+       /* Setup an alternate signal stack                          */
+       /*----------------------------------------------------------*/
+       tls->stack_size        = stSize;
+       tls->signal_stack      = g_malloc (SIGNAL_STACK_SIZE);
+       tls->signal_stack_size = SIGNAL_STACK_SIZE;
+
+       sa.ss_sp    = tls->signal_stack;
+       sa.ss_size  = SIGNAL_STACK_SIZE;
+       sa.ss_flags = SS_ONSTACK;
+       sigaltstack (&sa, NULL);
+#endif
+
 }
 
 /*========================= End of Function ========================*/
@@ -4765,6 +5358,18 @@ mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
 void
 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
 {
+#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
+       struct sigaltstack sa;
+
+       sa.ss_sp    = tls->signal_stack;
+       sa.ss_size  = SIGNAL_STACK_SIZE;
+       sa.ss_flags = SS_DISABLE;
+       sigaltstack (&sa, NULL);
+
+       if (tls->signal_stack)
+               g_free (tls->signal_stack);
+#endif
+
 }
 
 /*========================= End of Function ========================*/
@@ -4809,21 +5414,63 @@ mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_re
 
 /*------------------------------------------------------------------*/
 /*                                                                  */
-/* Name                - mono_arch_get_opcode_for_method                   */
+/* Name                - mono_arch_get_inst_for_method                   */
 /*                                                                  */
 /* Function    - Check for opcodes we can handle directly in       */
 /*               hardware.                                         */
 /*                                                                 */
 /*------------------------------------------------------------------*/
 
-gint
-mono_arch_get_opcode_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+MonoInst*
+mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, 
+                              MonoMethodSignature *fsig, MonoInst **args)
 {
+       MonoInst *ins = NULL;
+
        if (cmethod->klass == mono_defaults.math_class) {
-               if (strcmp (cmethod->name, "Sqrt") == 0)
-                       return OP_SQRT;
+               if (strcmp (cmethod->name, "Sqrt") == 0) {
+                       MONO_INST_NEW (cfg, ins, OP_SQRT);
+                       ins->inst_i0 = args [0];
+               }
+       } else if(cmethod->klass->image == mono_defaults.corlib &&
+                          (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
+                          (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
+
+               if (strcmp (cmethod->name, "Increment") == 0 && 
+                   fsig->params [0]->type == MONO_TYPE_I4) {
+                       MonoInst *ins_iconst;
+
+                       MONO_INST_NEW (cfg, ins, OP_ATOMIC_ADD_NEW_I4);
+                       MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
+                       ins_iconst->inst_c0 = 1;
+
+                       ins->inst_i0 = args [0];
+                       ins->inst_i1 = ins_iconst;
+               } else if (strcmp (cmethod->name, "Decrement") == 0 && 
+                          fsig->params [0]->type == MONO_TYPE_I4) {
+                       MonoInst *ins_iconst;
+
+                       MONO_INST_NEW (cfg, ins, OP_ATOMIC_ADD_NEW_I4);
+                       MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
+                       ins_iconst->inst_c0 = -1;
+
+                       ins->inst_i0 = args [0];
+                       ins->inst_i1 = ins_iconst;
+               } else if (strcmp (cmethod->name, "Exchange") == 0 && 
+                          fsig->params [0]->type == MONO_TYPE_I4) {
+                       MONO_INST_NEW (cfg, ins, OP_ATOMIC_EXCHANGE_I4);
+
+                       ins->inst_i0 = args [0];
+                       ins->inst_i1 = args [1];
+               } else if (strcmp (cmethod->name, "Add") == 0 && 
+                          fsig->params [0]->type == MONO_TYPE_I4) {
+                       MONO_INST_NEW (cfg, ins, OP_ATOMIC_ADD_I4);
+
+                       ins->inst_i0 = args [0];
+                       ins->inst_i1 = args [1];
+               }
        }
-       return -1;
+       return ins;
 }
 
 /*========================= End of Function ========================*/
@@ -4847,11 +5494,33 @@ mono_arch_print_tree (MonoInst *tree, int arity)
        switch (tree->opcode) {
                case OP_S390_LOADARG:
                case OP_S390_ARGPTR:
-               case OP_S390_STKARG:
                        printf ("[0x%x(%s)]", tree->inst_offset, 
                                mono_arch_regname (tree->inst_basereg));
                        done = 1;
                        break;
+               case OP_S390_STKARG:
+                       printf ("[0x%x(previous_frame)]", 
+                               tree->inst_offset); 
+                       done = 1;
+                       break;
+               case OP_S390_MOVE:
+                       printf ("[0x%x(%d,%s),0x%x(%s)]",
+                               tree->inst_offset, tree->unused,
+                               tree->dreg, tree->inst_imm, 
+                               tree->sreg1);
+                       done = 1;
+                       break;
+               case OP_S390_SETF4RET:
+                       printf ("[f%d,f%d]", 
+                               mono_arch_regname (tree->dreg),
+                               mono_arch_regname (tree->sreg1));
+                       done = 1;
+                       break;
+               case OP_TLS_GET:
+                       printf ("[0x%x(0x%x,%s)]", tree->inst_offset,
+                               tree->inst_imm,
+                               mono_arch_regname (tree->sreg1));
+                       done = 1;
                default:
                        done = 0;
        }
@@ -4877,7 +5546,74 @@ guint32
 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
 {
        /* FIXME: */
-       return 3;
+       return 2;
+}
+
+/*========================= End of Function ========================*/
+
+/*------------------------------------------------------------------*/
+/*                                                                  */
+/* Name                - mono_arch_get_domain_intrinsic                    */
+/*                                                                  */
+/* Function    -                                                   */
+/*                                                                 */
+/* Returns     -                                                   */
+/*                                                                  */
+/*------------------------------------------------------------------*/
+
+MonoInst * 
+mono_arch_get_domain_intrinsic (MonoCompile* cfg)
+{
+       MonoInst *ins;
+
+       if (appdomain_tls_offset == -1)
+               return NULL;
+       
+       MONO_INST_NEW (cfg, ins, OP_TLS_GET);
+       ins->inst_offset = appdomain_tls_offset;
+       return (ins);
+}
+
+/*========================= End of Function ========================*/
+
+/*------------------------------------------------------------------*/
+/*                                                                  */
+/* Name                - mono_arch_get_thread_intrinsic                    */
+/*                                                                  */
+/* Function    -                                                   */
+/*                                                                 */
+/* Returns     -                                                   */
+/*                                                                  */
+/*------------------------------------------------------------------*/
+
+MonoInst * 
+mono_arch_get_thread_intrinsic (MonoCompile* cfg)
+{
+       MonoInst *ins;
+
+       if (thread_tls_offset == -1)
+               return NULL;
+       
+       MONO_INST_NEW (cfg, ins, OP_TLS_GET);
+       ins->inst_offset = thread_tls_offset;
+       return (ins);
+}
+
+/*========================= End of Function ========================*/
+
+/*------------------------------------------------------------------*/
+/*                                                                  */
+/* Name                - mono_arch_flush_register_windows                  */
+/*                                                                  */
+/* Function    -                                                   */
+/*                                                                 */
+/* Returns     -                                                   */
+/*                                                                  */
+/*------------------------------------------------------------------*/
+
+void 
+mono_arch_flush_register_windows (void)
+{
 }
 
 /*========================= End of Function ========================*/