+ emit_sig_cookie (cfg, call, cinfo);
+ }
+
+ /*
+ * Reverse the call->out_args list.
+ */
+ {
+ MonoInst *prev = NULL, *list = call->out_args, *next;
+ while (list) {
+ next = list->next;
+ list->next = prev;
+ prev = list;
+ list = next;
+ }
+ call->out_args = prev;
+ }
+ call->stack_usage = cinfo->stack_usage + extra_space;
+ call->out_ireg_args = NULL;
+ call->out_freg_args = NULL;
+ cfg->param_area = MAX (cfg->param_area, call->stack_usage);
+ cfg->flags |= MONO_CFG_HAS_CALLS;
+
+ g_free (cinfo);
+ return call;
+}
+
+/* FIXME: Remove these later */
+#define NEW_LOAD_MEMBASE(cfg,dest,op,dr,base,offset) do { \
+ MONO_INST_NEW ((cfg), (dest), (op)); \
+ (dest)->dreg = (dr); \
+ (dest)->inst_basereg = (base); \
+ (dest)->inst_offset = (offset); \
+ (dest)->type = STACK_I4; \
+ } while (0)
+
+#define EMIT_NEW_LOAD_MEMBASE(cfg,dest,op,dr,base,offset) do { NEW_LOAD_MEMBASE ((cfg), (dest), (op), (dr), (base), (offset)); MONO_ADD_INS ((cfg)->cbb, (dest)); } while (0)
+
+#undef MONO_EMIT_NEW_STORE_MEMBASE_IMM
+#define MONO_EMIT_NEW_STORE_MEMBASE_IMM(cfg,op,base,offset,imm) do { \
+ MonoInst *inst; \
+ MONO_INST_NEW ((cfg), (inst), (op)); \
+ inst->inst_destbasereg = base; \
+ inst->inst_offset = offset; \
+ inst->inst_p1 = (gpointer)(gssize)imm; \
+ MONO_ADD_INS ((cfg)->cbb, inst); \
+ } while (0)
+
+static void
+add_outarg_reg2 (MonoCompile *cfg, MonoCallInst *call, ArgStorage storage, int reg, guint32 sreg)
+{
+ MonoInst *arg;
+
+ MONO_INST_NEW (cfg, arg, 0);
+
+ arg->sreg1 = sreg;
+
+ switch (storage) {
+ case ArgInIReg:
+ arg->opcode = OP_MOVE;
+ arg->dreg = mono_alloc_ireg (cfg);
+
+ mono_call_inst_add_outarg_reg (cfg, call, arg->dreg, reg, FALSE);
+ break;
+ case ArgInFloatReg:
+ arg->opcode = OP_FMOVE;
+ arg->dreg = mono_alloc_freg (cfg);
+
+ mono_call_inst_add_outarg_reg (cfg, call, arg->dreg, reg, TRUE);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ MONO_ADD_INS (cfg->cbb, arg);
+}
+
+static void
+add_outarg_load (MonoCompile *cfg, MonoCallInst *call, int opcode, int basereg, int offset, int reg)
+{
+ MonoInst *arg;
+ int dreg = mono_alloc_ireg (cfg);
+
+ EMIT_NEW_LOAD_MEMBASE (cfg, arg, OP_LOAD_MEMBASE, dreg, sparc_sp, offset);
+ MONO_ADD_INS (cfg->cbb, arg);
+
+ mono_call_inst_add_outarg_reg (cfg, call, dreg, reg, FALSE);
+}
+
+static void
+emit_pass_long (MonoCompile *cfg, MonoCallInst *call, ArgInfo *ainfo, MonoInst *in)
+{
+ int offset = ARGS_OFFSET + ainfo->offset;
+
+ switch (ainfo->storage) {
+ case ArgInIRegPair:
+ add_outarg_reg2 (cfg, call, ArgInIReg, sparc_o0 + ainfo->reg + 1, in->dreg + 1);
+ add_outarg_reg2 (cfg, call, ArgInIReg, sparc_o0 + ainfo->reg, in->dreg + 2);
+ break;
+ case ArgOnStackPair:
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, sparc_sp, offset, in->dreg + 2);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, sparc_sp, offset + 4, in->dreg + 1);
+ break;
+ case ArgInSplitRegStack:
+ add_outarg_reg2 (cfg, call, ArgInIReg, sparc_o0 + ainfo->reg, in->dreg + 2);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, sparc_sp, offset + 4, in->dreg + 1);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+emit_pass_double (MonoCompile *cfg, MonoCallInst *call, ArgInfo *ainfo, MonoInst *in)
+{
+ int offset = ARGS_OFFSET + ainfo->offset;
+
+ switch (ainfo->storage) {
+ case ArgInIRegPair:
+ /* floating-point <-> integer transfer must go through memory */
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, sparc_sp, offset, in->dreg);
+
+ /* Load into a register pair */
+ add_outarg_load (cfg, call, OP_LOADI4_MEMBASE, sparc_sp, offset, sparc_o0 + ainfo->reg);
+ add_outarg_load (cfg, call, OP_LOADI4_MEMBASE, sparc_sp, offset + 4, sparc_o0 + ainfo->reg + 1);
+ break;
+ case ArgOnStackPair:
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, sparc_sp, offset, in->dreg);
+ break;
+ case ArgInSplitRegStack:
+ /* floating-point <-> integer transfer must go through memory */
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, sparc_sp, offset, in->dreg);
+ /* Load most significant word into register */
+ add_outarg_load (cfg, call, OP_LOADI4_MEMBASE, sparc_sp, offset, sparc_o0 + ainfo->reg);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+emit_pass_float (MonoCompile *cfg, MonoCallInst *call, ArgInfo *ainfo, MonoInst *in)
+{
+ int offset = ARGS_OFFSET + ainfo->offset;
+
+ switch (ainfo->storage) {
+ case ArgInIReg:
+ /* floating-point <-> integer transfer must go through memory */
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, sparc_sp, offset, in->dreg);
+ add_outarg_load (cfg, call, OP_LOADI4_MEMBASE, sparc_sp, offset, sparc_o0 + ainfo->reg);
+ break;
+ case ArgOnStack:
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, sparc_sp, offset, in->dreg);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+emit_pass_other (MonoCompile *cfg, MonoCallInst *call, ArgInfo *ainfo, MonoType *arg_type, MonoInst *in);
+
+static void
+emit_pass_vtype (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo, ArgInfo *ainfo, MonoType *arg_type, MonoInst *in, gboolean pinvoke)
+{
+ MonoInst *arg;
+ guint32 align, offset, pad, size;
+
+ if (arg_type->type == MONO_TYPE_TYPEDBYREF) {
+ size = sizeof (MonoTypedRef);
+ align = sizeof (gpointer);
+ }
+ else if (pinvoke)
+ size = mono_type_native_stack_size (&in->klass->byval_arg, &align);
+ else {
+ /*
+ * Other backends use mono_type_stack_size (), but that
+ * aligns the size to 8, which is larger than the size of
+ * the source, leading to reads of invalid memory if the
+ * source is at the end of address space.
+ */
+ size = mono_class_value_size (in->klass, &align);
+ }
+
+ /* The first 6 argument locations are reserved */
+ if (cinfo->stack_usage < 6 * sizeof (gpointer))
+ cinfo->stack_usage = 6 * sizeof (gpointer);
+
+ offset = ALIGN_TO ((ARGS_OFFSET - STACK_BIAS) + cinfo->stack_usage, align);
+ pad = offset - ((ARGS_OFFSET - STACK_BIAS) + cinfo->stack_usage);
+
+ cinfo->stack_usage += size;
+ cinfo->stack_usage += pad;
+
+ /*
+ * We use OP_OUTARG_VT to copy the valuetype to a stack location, then
+ * use the normal OUTARG opcodes to pass the address of the location to
+ * the callee.
+ */
+ if (size > 0) {
+ MONO_INST_NEW (cfg, arg, OP_OUTARG_VT);
+ arg->sreg1 = in->dreg;
+ arg->klass = in->klass;
+ arg->backend.size = size;
+ arg->inst_p0 = call;
+ arg->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
+ memcpy (arg->inst_p1, ainfo, sizeof (ArgInfo));
+ ((ArgInfo*)(arg->inst_p1))->offset = STACK_BIAS + offset;
+ MONO_ADD_INS (cfg->cbb, arg);
+
+ MONO_INST_NEW (cfg, arg, OP_ADD_IMM);
+ arg->dreg = mono_alloc_preg (cfg);
+ arg->sreg1 = sparc_sp;
+ arg->inst_imm = STACK_BIAS + offset;
+ MONO_ADD_INS (cfg->cbb, arg);
+
+ emit_pass_other (cfg, call, ainfo, NULL, arg);
+ }
+}
+
+static void
+emit_pass_other (MonoCompile *cfg, MonoCallInst *call, ArgInfo *ainfo, MonoType *arg_type, MonoInst *in)
+{
+ int offset = ARGS_OFFSET + ainfo->offset;
+ int opcode;
+
+ switch (ainfo->storage) {
+ case ArgInIReg:
+ add_outarg_reg2 (cfg, call, ArgInIReg, sparc_o0 + ainfo->reg, in->dreg);
+ break;
+ case ArgOnStack:
+#ifdef SPARCV9
+ NOT_IMPLEMENTED;
+#else
+ if (offset & 0x1)
+ opcode = OP_STOREI1_MEMBASE_REG;
+ else if (offset & 0x2)
+ opcode = OP_STOREI2_MEMBASE_REG;
+ else
+ opcode = OP_STOREI4_MEMBASE_REG;
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, opcode, sparc_sp, offset, in->dreg);
+#endif
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+emit_sig_cookie2 (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo)
+{
+ MonoMethodSignature *tmp_sig;
+
+ /*
+ * 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*));
+
+ /* FIXME: Add support for signature tokens to AOT */
+ cfg->disable_aot = TRUE;
+ /* We allways pass the signature on the stack for simplicity */
+ MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sparc_sp, ARGS_OFFSET + cinfo->sig_cookie.offset, tmp_sig);
+}
+
+void
+mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
+{
+ MonoInst *in;
+ MonoMethodSignature *sig;
+ int i, n;
+ CallInfo *cinfo;
+ ArgInfo *ainfo;
+ guint32 extra_space = 0;
+
+ sig = call->signature;
+ n = sig->param_count + sig->hasthis;
+
+ cinfo = get_call_info (cfg, sig, sig->pinvoke);
+
+ if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
+ /* Set the 'struct/union return pointer' location on the stack */
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, sparc_sp, 64, call->vret_var->dreg);
+ }
+
+ for (i = 0; i < n; ++i) {
+ MonoType *arg_type;
+
+ ainfo = cinfo->args + i;
+
+ if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
+ /* Emit the signature cookie just before the first implicit argument */
+ emit_sig_cookie2 (cfg, call, cinfo);
+ }
+
+ in = call->args [i];
+
+ if (sig->hasthis && (i == 0))
+ arg_type = &mono_defaults.object_class->byval_arg;
+ else
+ arg_type = sig->params [i - sig->hasthis];
+
+ if ((i >= sig->hasthis) && (MONO_TYPE_ISSTRUCT(sig->params [i - sig->hasthis])))
+ emit_pass_vtype (cfg, call, cinfo, ainfo, arg_type, in, sig->pinvoke);
+ else if (!arg_type->byref && ((arg_type->type == MONO_TYPE_I8) || (arg_type->type == MONO_TYPE_U8)))
+ emit_pass_long (cfg, call, ainfo, in);
+ else if (!arg_type->byref && (arg_type->type == MONO_TYPE_R8))
+ emit_pass_double (cfg, call, ainfo, in);
+ else if (!arg_type->byref && (arg_type->type == MONO_TYPE_R4))
+ emit_pass_float (cfg, call, ainfo, in);
+ else
+ emit_pass_other (cfg, call, ainfo, arg_type, in);
+ }
+
+ /* Handle the case where there are no implicit arguments */
+ if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (n == sig->sentinelpos)) {
+ emit_sig_cookie2 (cfg, call, cinfo);