2009-11-09 Zoltan Varga <vargaz@gmail.com>
authorZoltan Varga <vargaz@gmail.com>
Mon, 9 Nov 2009 01:44:40 +0000 (01:44 -0000)
committerZoltan Varga <vargaz@gmail.com>
Mon, 9 Nov 2009 01:44:40 +0000 (01:44 -0000)
* mini-arm.c (mono_arch_emit_prolog): Implement support for varargs.

* mini-ops.h: Add documentation for the OP_ARGLIST opcode.

svn path=/trunk/mono/; revision=145697

mono/mini/ChangeLog
mono/mini/mini-arm.c
mono/mini/mini-ops.h

index 43643ff1912e810b03572f80def696a743054a9a..09efc89f6f95fe3cd635b3be54475826e4525e81 100644 (file)
@@ -1,3 +1,9 @@
+2009-11-09  Zoltan Varga  <vargaz@gmail.com>
+
+       * mini-arm.c (mono_arch_emit_prolog): Implement support for varargs.
+
+       * mini-ops.h: Add documentation for the OP_ARGLIST opcode.
+
 2009-11-08  Zoltan Varga  <vargaz@gmail.com>
 
        * mini-arm.c: Compute the stack space used by arguments using
index f331d2961f5f317f931674cdffd8fae899ec8699..40949a3355fc5f1f55f94086b4c614d1eb0c68dd 100644 (file)
@@ -83,6 +83,27 @@ int mono_exc_esp_offset = 0;
 #define ADD_LR_PC_4 ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 25) | (1 << 23) | (ARMREG_PC << 16) | (ARMREG_LR << 12) | 4)
 #define MOV_LR_PC ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 24) | (0xa << 20) |  (ARMREG_LR << 12) | ARMREG_PC)
 #define DEBUG_IMT 0
+/* A variant of ARM_LDR_IMM which can handle large offsets */
+#define ARM_LDR_IMM_GENERAL(code, dreg, basereg, offset, scratch_reg) do { \
+       if (arm_is_imm12 ((offset))) { \
+               ARM_LDR_IMM (code, (dreg), (basereg), (offset));        \
+       } else {                                                                                                \
+               g_assert ((scratch_reg) != (basereg));                                     \
+               code = mono_arm_emit_load_imm (code, (scratch_reg), (offset));  \
+               ARM_LDR_REG_REG (code, (dreg), (basereg), (scratch_reg));               \
+       }                                                                                                                                       \
+       } while (0)
+
+#define ARM_STR_IMM_GENERAL(code, dreg, basereg, offset, scratch_reg) do {     \
+       if (arm_is_imm12 ((offset))) { \
+               ARM_STR_IMM (code, (dreg), (basereg), (offset));        \
+       } else {                                                                                                \
+               g_assert ((scratch_reg) != (basereg));                                     \
+               code = mono_arm_emit_load_imm (code, (scratch_reg), (offset));  \
+               ARM_STR_REG_REG (code, (dreg), (basereg), (scratch_reg));               \
+       }                                                                                                                                       \
+       } while (0)
 
 const char*
 mono_arch_regname (int reg)
@@ -831,13 +852,13 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke)
         DEBUG(printf("params: %d\n", sig->param_count));
        for (i = 0; i < sig->param_count; ++i) {
                if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
-                        /* Prevent implicit arguments and sig_cookie from
+                       /* Prevent implicit arguments and sig_cookie from
                           being passed in registers */
-                        gr = ARMREG_R3 + 1;
-                        /* Emit the signature cookie just before the implicit arguments */
-                        add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
-                }
-                DEBUG(printf("param %d: ", i));
+                       gr = ARMREG_R3 + 1;
+                       /* Emit the signature cookie just before the implicit arguments */
+                       add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
+               }
+               DEBUG(printf("param %d: ", i));
                if (sig->params [i]->byref) {
                         DEBUG(printf("byref\n"));
                        add_general (&gr, &stack_size, cinfo->args + n, TRUE);
@@ -949,6 +970,15 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke)
                }
        }
 
+       /* Handle the case where there are no implicit arguments */
+       if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
+               /* Prevent implicit arguments and sig_cookie from
+                  being passed in registers */
+               gr = ARMREG_R3 + 1;
+               /* Emit the signature cookie just before the implicit arguments */
+               add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
+       }
+
        {
                simpletype = mini_type_get_underlying_type (NULL, sig->ret);
                switch (simpletype->type) {
@@ -1095,11 +1125,6 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                offset += 8;
 
        /* the MonoLMF structure is stored just below the stack pointer */
-
-       if (sig->call_convention == MONO_CALL_VARARG) {
-                cfg->sig_cookie = 0;
-        }
-
        if (MONO_TYPE_ISSTRUCT (sig->ret)) {
                if (cinfo->ret.storage == RegTypeStructByVal) {
                        cfg->ret->opcode = OP_REGOFFSET;
@@ -1120,8 +1145,6 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                        }
                }
                offset += sizeof(gpointer);
-               if (sig->call_convention == MONO_CALL_VARARG)
-                       cfg->sig_cookie += sizeof (gpointer);
        }
 
        curinst = cfg->locals_start;
@@ -1164,14 +1187,24 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                        offset &= ~(sizeof (gpointer) - 1);
                        inst->inst_offset = offset;
                        offset += sizeof (gpointer);
-                       if (sig->call_convention == MONO_CALL_VARARG)
-                               cfg->sig_cookie += sizeof (gpointer);
                }
                curinst++;
        }
 
+       if (sig->call_convention == MONO_CALL_VARARG) {
+               size = 4;
+               align = 4;
+
+               /* Allocate a local slot to hold the sig cookie address */
+               offset += align - 1;
+               offset &= ~(align - 1);
+               cfg->sig_cookie = offset;
+               offset += size;
+       }                       
+
        for (i = 0; i < sig->param_count; ++i) {
                inst = cfg->args [curinst];
+
                if (inst->opcode != OP_REGVAR) {
                        inst->opcode = OP_REGOFFSET;
                        inst->inst_basereg = frame_reg;
@@ -1188,8 +1221,6 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                        offset &= ~(align - 1);
                        inst->inst_offset = offset;
                        offset += size;
-                       if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos)) 
-                               cfg->sig_cookie += size;
                }
                curinst++;
        }
@@ -1237,6 +1268,39 @@ mono_arch_create_vars (MonoCompile *cfg)
        }
 }
 
+static void
+emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo)
+{
+       MonoMethodSignature *tmp_sig;
+       MonoInst *sig_arg;
+
+       if (call->tail_call)
+               NOT_IMPLEMENTED;
+
+       /* FIXME: Add support for signature tokens to AOT */
+       cfg->disable_aot = TRUE;
+
+       g_assert (cinfo->sig_cookie.storage == RegTypeBase);
+                       
+       /*
+        * mono_ArgIterator_Setup assumes the signature cookie is 
+        * passed first and all the arguments which were before it are
+        * passed on the stack after the signature. So compensate by 
+        * passing a different signature.
+        */
+       tmp_sig = mono_metadata_signature_dup (call->signature);
+       tmp_sig->param_count -= call->signature->sentinelpos;
+       tmp_sig->sentinelpos = 0;
+       memcpy (tmp_sig->params, call->signature->params + call->signature->sentinelpos, tmp_sig->param_count * sizeof (MonoType*));
+
+       MONO_INST_NEW (cfg, sig_arg, OP_ICONST);
+       sig_arg->dreg = mono_alloc_ireg (cfg);
+       sig_arg->inst_p0 = tmp_sig;
+       MONO_ADD_INS (cfg->cbb, sig_arg);
+
+       MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, cinfo->sig_cookie.offset, sig_arg->dreg);
+}
+
 void
 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
 {
@@ -1261,8 +1325,8 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                t = mini_type_get_underlying_type (NULL, t);
 
                if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
-                       /* FIXME: */
-                       NOT_IMPLEMENTED;
+                       /* Emit the signature cookie just before the implicit arguments */
+                       emit_sig_cookie (cfg, call, cinfo);
                }
 
                in = call->args [i];
@@ -1420,6 +1484,10 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                }
        }
 
+       /* Handle the case where there are no implicit arguments */
+       if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (n == sig->sentinelpos))
+               emit_sig_cookie (cfg, call, cinfo);
+
        if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
                MonoInst *vtarg;
 
@@ -3459,15 +3527,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
                        break;
                case OP_ARGLIST: {
-#if ARM_PORT
-                       if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
-                               ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
-                       } else {
-                               ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
-                               ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
-                       }
-                       ppc_stw (code, ppc_r11, 0, ins->sreg1);
-#endif
+                       g_assert (cfg->sig_cookie < 128);
+                       ARM_LDR_IMM (code, ARMREG_IP, cfg->frame_reg, cfg->sig_cookie);
+                       ARM_STR_IMM (code, ARMREG_IP, ins->sreg1, 0);
                        break;
                }
                case OP_FCALL:
@@ -4423,6 +4485,19 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                g_assert (arm_is_imm12 (inst->inst_offset));
                ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
        }
+
+       if (sig->call_convention == MONO_CALL_VARARG) {
+               ArgInfo *cookie = &cinfo->sig_cookie;
+
+               /* Save the sig cookie address */
+               g_assert (cookie->storage == RegTypeBase);
+
+               g_assert (arm_is_imm12 (prev_sp_offset + cookie->offset));
+               g_assert (arm_is_imm12 (cfg->sig_cookie));
+               ARM_ADD_REG_IMM8 (code, ARMREG_IP, cfg->frame_reg, prev_sp_offset + cookie->offset);
+               ARM_STR_IMM (code, ARMREG_IP, cfg->frame_reg, cfg->sig_cookie);
+       }
+
        for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
                ArgInfo *ainfo = cinfo->args + i;
                inst = cfg->args [pos];
index b5b0ebb142e8cff97c4d47f17d3f7b3765fd40a0..0a6a08d9bb30c79d3209247e6942e0a1b641a6f8 100644 (file)
@@ -17,7 +17,6 @@ MINI_OP(OP_ICOMPARE_IMM,      "icompare_imm", NONE, IREG, NONE)
 MINI_OP(OP_LCOMPARE_IMM,       "lcompare_imm", NONE, LREG, NONE)
 MINI_OP(OP_LOCAL,      "local", NONE, NONE, NONE)
 MINI_OP(OP_ARG,                "arg", NONE, NONE, NONE)
-MINI_OP(OP_ARGLIST,    "oparglist", NONE, IREG, NONE)
 MINI_OP(OP_OUTARG_VT,  "outarg_vt", NONE, VREG, NONE)
 MINI_OP(OP_OUTARG_VTRETADDR, "outarg_vtretaddr", IREG, NONE, NONE)
 MINI_OP(OP_SETRET,     "setret", NONE, IREG, NONE)
@@ -66,6 +65,23 @@ MINI_OP(OP_SWITCH,  "switch", NONE, IREG, NONE)
 MINI_OP(OP_THROW, "throw", NONE, IREG, NONE)
 MINI_OP(OP_RETHROW,    "rethrow", NONE, IREG, NONE)
 
+/*
+ * Vararg calls are implemented as follows:
+ * - the caller emits a hidden argument just before the varargs argument. this
+ *   'signature cookie' argument contains the signature describing the the call.
+ * - all implicit arguments are passed in memory right after the signature cookie, i.e.
+ *   the stack will look like this:
+ *   <argn>
+ *   ..
+ *   <arg1>
+ *   <sig cookie>
+ * - the OP_ARGLIST opcode in the callee computes the address of the sig cookie argument
+ *   on the stack and saves it into its sreg1.
+ * - mono_ArgIterator_Setup receives this value and uses it to find the signature and
+ *   the arguments.
+ */
+MINI_OP(OP_ARGLIST,    "oparglist", NONE, IREG, NONE)
+
 /* MONO_IS_STORE_MEMBASE depends on the order here */
 MINI_OP(OP_STORE_MEMBASE_REG,"store_membase_reg", IREG, IREG, NONE)
 MINI_OP(OP_STOREI1_MEMBASE_REG, "storei1_membase_reg", IREG, IREG, NONE)