2 * Create trampolines to invoke arbitrary functions.
4 * Copyright (C) Ximian Inc.
9 * Paolo Molaro (lupus@ximian.com)
10 * Dietmar Maurer (dietmar@ximian.com)
12 * To understand this code, one will want to the calling convention section of the ABI sepc at:
13 * http://x86-64.org/abi.pdf
14 * and the AMD64 architecture docs found at amd.com .
20 #include "amd64-codegen.h"
21 #include "mono/metadata/class.h"
22 #include "mono/metadata/tabledefs.h"
23 #include "mono/interpreter/interp.h"
24 #include "mono/metadata/appdomain.h"
25 #include "mono/metadata/marshal.h"
28 * The resulting function takes the form:
29 * void func (void (*callme)(), void *retval, void *this_obj, stackval *arguments);
31 #define FUNC_ADDR_POS 8
37 #define ARG_SIZE sizeof (stackval)
39 #define MAX_INT_ARG_REGS 6
40 #define MAX_FLOAT_ARG_REGS 8
42 // TODO get these right. They are upper bounds anyway, so it doesn't much matter.
43 #define PUSH_INT_STACK_ARG_SIZE 16
44 #define MOVE_INT_REG_ARG_SIZE 16
45 #define PUSH_FLOAT_STACK_ARG_SIZE 16
46 #define MOVE_FLOAT_REG_ARG_SIZE 16
47 #define COPY_STRUCT_STACK_ARG_SIZE 16
49 /* Maps an argument number (starting at 0) to the register it is passed in (if it fits).
50 * E.g. int foo(int bar, int quux) has the foo arg in RDI and the quux arg in RSI
51 * There is no such map for floating point args as they go in XMM0-XMM7 in order and thus the
52 * index is the register number.
54 static int int_arg_regs
[] = { AMD64_RDI
, AMD64_RSI
, AMD64_RDX
, AMD64_RCX
, AMD64_R8
, AMD64_R9
};
56 /* This next block of code resolves the ABI rules for passing structures in the argument registers.
57 * These basically amount to "Use up to two registers if they are all integer or all floating point.
58 * If the structure is bigger than two registers or would be in one integer register and one floating point,
59 * it is passed in memory instead.
61 * It is possible this code needs to be recursive to be correct in the case when one of the structure members
62 * is itself a structure.
64 * The 80-bit floating point stuff is ignored.
72 static struct_arg_type
compute_arg_type(MonoType
*type
)
74 guint32 simpletype
= type
->type
;
77 case MONO_TYPE_BOOLEAN
:
88 case MONO_TYPE_SZARRAY
:
90 case MONO_TYPE_OBJECT
:
91 case MONO_TYPE_STRING
:
93 return ARG_IN_INT_REGS
;
95 case MONO_TYPE_VALUETYPE
: {
96 if (type
->data
.klass
->enumtype
)
97 return ARG_IN_INT_REGS
;
103 return ARG_IN_FLOAT_REGS
;
106 g_error ("Can't trampoline 0x%x", type
->type
);
109 return ARG_IN_MEMORY
;
112 static struct_arg_type
value_type_info(MonoClass
*klass
, int *native_size
, int *regs_used
, int *offset1
, int *size1
, int *offset2
, int *size2
)
114 MonoMarshalType
*info
= mono_marshal_load_type_info (klass
);
116 *native_size
= info
->native_size
;
118 if (info
->native_size
> 8 || info
->num_fields
> 2)
123 return ARG_IN_MEMORY
;
126 if (info
->num_fields
== 1)
128 struct_arg_type result
= compute_arg_type(info
->fields
[0].field
->type
);
129 if (result
!= ARG_IN_MEMORY
)
132 *offset1
= info
->fields
[0].offset
;
133 *size1
= mono_marshal_type_size (info
->fields
[0].field
->type
, info
->fields
[0].mspec
, NULL
, 1, 1);
145 struct_arg_type result1
= compute_arg_type(info
->fields
[0].field
->type
);
146 struct_arg_type result2
= compute_arg_type(info
->fields
[0].field
->type
);
148 if (result1
== result2
&& result1
!= ARG_IN_MEMORY
)
151 *offset1
= info
->fields
[0].offset
;
152 *size1
= mono_marshal_type_size (info
->fields
[0].field
->type
, info
->fields
[0].mspec
, NULL
, 1, 1);
153 *offset2
= info
->fields
[1].offset
;
154 *size2
= mono_marshal_type_size (info
->fields
[1].field
->type
, info
->fields
[1].mspec
, NULL
, 1, 1);
158 return ARG_IN_MEMORY
;
162 mono_arch_create_trampoline (MonoMethodSignature
*sig
, gboolean string_ctor
)
164 unsigned char *p
, *code_buffer
;
165 guint32 stack_size
= 0, code_size
= 50;
166 guint32 arg_pos
, simpletype
;
168 static GHashTable
*cache
= NULL
;
171 guint32 int_arg_regs_used
= 0;
172 guint32 float_arg_regs_used
= 0;
173 guint32 next_int_arg_reg
= 0;
174 guint32 next_float_arg_reg
= 0;
175 /* Indicates that the return value is filled in inside the called function. */
176 int retval_implicit
= 0;
177 char *arg_in_reg_bitvector
; /* A set index by argument number saying if it is in a register
178 (integer or floating point according to type) */
181 cache
= g_hash_table_new ((GHashFunc
)mono_signature_hash
,
182 (GCompareFunc
)mono_metadata_signature_equal
);
184 if ((res
= (MonoPIFunc
)g_hash_table_lookup (cache
, sig
)))
187 if (sig
->ret
->type
== MONO_TYPE_VALUETYPE
&& !sig
->ret
->byref
&& !sig
->ret
->data
.klass
->enumtype
) {
189 code_size
+= MOVE_INT_REG_ARG_SIZE
;
194 code_size
+= MOVE_INT_REG_ARG_SIZE
;
197 /* Run through stuff to calculate code size and argument bytes that will be pushed on stack (stack_size). */
198 for (i
= 0; i
< sig
->param_count
; ++i
) {
199 if (sig
->params
[i
]->byref
)
200 simpletype
= MONO_TYPE_PTR
;
202 simpletype
= sig
->params
[i
]->type
;
204 switch (simpletype
) {
205 case MONO_TYPE_BOOLEAN
:
216 case MONO_TYPE_SZARRAY
:
217 case MONO_TYPE_CLASS
:
218 case MONO_TYPE_OBJECT
:
219 case MONO_TYPE_STRING
:
221 if (int_arg_regs_used
++ > MAX_INT_ARG_REGS
) {
223 code_size
+= PUSH_INT_STACK_ARG_SIZE
;
226 code_size
+= MOVE_INT_REG_ARG_SIZE
;
228 case MONO_TYPE_VALUETYPE
: {
237 if (sig
->params
[i
]->data
.klass
->enumtype
) {
238 simpletype
= sig
->params
[i
]->data
.klass
->enum_basetype
->type
;
242 arg_type
= value_type_info(sig
->params
[i
]->data
.klass
, &size
, ®s_used
, &offset1
, &size1
, &offset2
, &size2
);
243 if (arg_type
== ARG_IN_INT_REGS
&&
244 (int_arg_regs_used
+ regs_used
) <= MAX_INT_ARG_REGS
)
246 code_size
+= MOVE_INT_REG_ARG_SIZE
;
247 int_arg_regs_used
+= regs_used
;
251 if (arg_type
== ARG_IN_FLOAT_REGS
&&
252 (float_arg_regs_used
+ regs_used
) <= MAX_FLOAT_ARG_REGS
)
254 code_size
+= MOVE_FLOAT_REG_ARG_SIZE
;
255 float_arg_regs_used
+= regs_used
;
259 /* Else item is in memory. */
261 stack_size
+= size
+ 7;
263 code_size
+= COPY_STRUCT_STACK_ARG_SIZE
;
269 if (float_arg_regs_used
++ > MAX_FLOAT_ARG_REGS
) {
271 code_size
+= PUSH_FLOAT_STACK_ARG_SIZE
;
274 code_size
+= MOVE_FLOAT_REG_ARG_SIZE
;
277 g_error ("Can't trampoline 0x%x", sig
->params
[i
]->type
);
281 * FIXME: take into account large return values.
282 * (Comment carried over from IA32 code. Not sure what it means :-)
285 code_buffer
= p
= alloca (code_size
);
288 * Standard function prolog.
290 amd64_push_reg (p
, AMD64_RBP
);
291 amd64_mov_reg_reg (p
, AMD64_RBP
, AMD64_RSP
, 8);
293 * and align to 16 byte boundary...
296 if (sig
->ret
->type
== MONO_TYPE_VALUETYPE
&& !sig
->ret
->byref
) {
297 MonoClass
*klass
= sig
->ret
->data
.klass
;
298 if (!klass
->enumtype
) {
303 if (sig
->ret
->byref
|| string_ctor
|| !(retval_implicit
|| sig
->ret
->type
== MONO_TYPE_VOID
)) {
304 /* Push the retval register so it is saved across the call. It will be addressed via RBP later. */
305 amd64_push_reg (p
, AMD64_RSI
);
309 /* Ensure stack is 16 byte aligned when entering called function as required by calling convention.
310 * Getting this wrong results in a general protection fault on an SSE load or store somewhere in the
311 * code called under the trampoline.
313 if ((stack_size
& 15) != 0)
314 amd64_alu_reg_imm (p
, X86_SUB
, AMD64_RSP
, 16 - (stack_size
& 15));
317 * On entry to generated function:
318 * RDI has target function address
319 * RSI has return value location address
320 * RDX has this pointer address
321 * RCX has the pointer to the args array.
323 * Inside the stub function:
324 * R10 holds the pointer to the args
325 * R11 holds the target function address.
326 * The return value address is pushed on the stack.
327 * The this pointer is moved into the first arg register at the start.
329 * Optimization note: we could keep the args pointer in RCX and then
330 * load over itself at the end. Ditto the callee addres could be left in RDI in some cases.
333 /* Move args pointer to temp register. */
334 amd64_mov_reg_reg (p
, AMD64_R10
, AMD64_RCX
, 8);
335 amd64_mov_reg_reg (p
, AMD64_R11
, AMD64_RDI
, 8);
337 /* First args register gets return value pointer, if need be.
338 * Note that "byref" equal true means the called function returns a pointer.
340 if (retval_implicit
) {
341 amd64_mov_reg_reg (p
, int_arg_regs
[next_int_arg_reg
], AMD64_RSI
, 8);
345 /* this pointer goes in next args register. */
347 amd64_mov_reg_reg (p
, int_arg_regs
[next_int_arg_reg
], AMD64_RDX
, 8);
352 * Generate code to handle arguments in registers. Stack arguments will happen in a loop after this.
354 arg_in_reg_bitvector
= (char *)alloca((sig
->param_count
+ 7) / 8);
355 memset(arg_in_reg_bitvector
, 0, (sig
->param_count
+ 7) / 8);
357 /* First, load all the arguments that are passed in registers into the appropriate registers.
358 * Below there is another loop to handle arguments passed on the stack.
360 for (i
= 0; i
< sig
->param_count
; i
++) {
361 arg_pos
= ARG_SIZE
* i
;
363 if (sig
->params
[i
]->byref
)
364 simpletype
= MONO_TYPE_PTR
;
366 simpletype
= sig
->params
[i
]->type
;
368 switch (simpletype
) {
369 case MONO_TYPE_BOOLEAN
:
380 case MONO_TYPE_OBJECT
:
381 case MONO_TYPE_STRING
:
382 case MONO_TYPE_SZARRAY
:
385 case MONO_TYPE_CLASS
:
386 if (next_int_arg_reg
< MAX_INT_ARG_REGS
) {
387 amd64_mov_reg_membase (p
, int_arg_regs
[next_int_arg_reg
], AMD64_R10
, arg_pos
, 8);
389 arg_in_reg_bitvector
[i
>> 3] |= (1 << (i
& 7));
393 if (next_float_arg_reg
< MAX_FLOAT_ARG_REGS
) {
394 amd64_movss_reg_membase (p
, next_float_arg_reg
, AMD64_R10
, arg_pos
);
395 next_float_arg_reg
++;
396 arg_in_reg_bitvector
[i
>> 3] |= (1 << (i
& 7));
400 if (next_float_arg_reg
< MAX_FLOAT_ARG_REGS
) {
401 amd64_movsd_reg_membase (p
, next_float_arg_reg
, AMD64_R10
, arg_pos
);
402 next_float_arg_reg
++;
403 arg_in_reg_bitvector
[i
>> 3] |= (1 << (i
& 7));
406 case MONO_TYPE_VALUETYPE
: {
407 if (!sig
->params
[i
]->data
.klass
->enumtype
) {
416 arg_type
= value_type_info(sig
->params
[i
]->data
.klass
, &size
, ®s_used
, &offset1
, &size1
, &offset2
, &size2
);
418 if (arg_type
== ARG_IN_INT_REGS
&&
419 (next_int_arg_reg
+ regs_used
) <= MAX_INT_ARG_REGS
)
421 amd64_mov_reg_membase (p
, int_arg_regs
[next_int_arg_reg
], AMD64_R10
, arg_pos
+ offset1
, size1
);
425 amd64_mov_reg_membase (p
, int_arg_regs
[next_int_arg_reg
], AMD64_R10
, arg_pos
+ offset2
, size2
);
428 arg_in_reg_bitvector
[i
>> 3] |= (1 << (i
& 7));
432 if (arg_type
== ARG_IN_FLOAT_REGS
&&
433 (next_float_arg_reg
+ regs_used
) <= MAX_FLOAT_ARG_REGS
)
436 amd64_movss_reg_membase (p
, next_float_arg_reg
, AMD64_R10
, arg_pos
+ offset1
);
438 amd64_movsd_reg_membase (p
, next_float_arg_reg
, AMD64_R10
, arg_pos
+ offset1
);
439 next_float_arg_reg
++;
444 amd64_movss_reg_membase (p
, next_float_arg_reg
, AMD64_R10
, arg_pos
+ offset2
);
446 amd64_movsd_reg_membase (p
, next_float_arg_reg
, AMD64_R10
, arg_pos
+ offset2
);
447 next_float_arg_reg
++;
449 arg_in_reg_bitvector
[i
>> 3] |= (1 << (i
& 7));
453 /* Structs in memory are handled in the next loop. */
455 /* it's an enum value */
456 simpletype
= sig
->params
[i
]->data
.klass
->enum_basetype
->type
;
462 g_error ("Can't trampoline 0x%x", sig
->params
[i
]->type
);
466 /* Handle stack arguments, pushing the rightmost argument first. */
467 for (i
= sig
->param_count
; i
> 0; --i
) {
468 arg_pos
= ARG_SIZE
* (i
- 1);
469 if (sig
->params
[i
- 1]->byref
)
470 simpletype
= MONO_TYPE_PTR
;
472 simpletype
= sig
->params
[i
- 1]->type
;
474 switch (simpletype
) {
475 case MONO_TYPE_BOOLEAN
:
486 case MONO_TYPE_OBJECT
:
487 case MONO_TYPE_STRING
:
488 case MONO_TYPE_SZARRAY
:
491 case MONO_TYPE_CLASS
:
492 if ((arg_in_reg_bitvector
[(i
- 1) >> 3] & (1 << ((i
- 1) & 7))) == 0) {
493 amd64_push_membase (p
, AMD64_R10
, arg_pos
);
497 if ((arg_in_reg_bitvector
[(i
- 1) >> 3] & (1 << ((i
- 1) & 7))) == 0) {
498 amd64_push_membase (p
, AMD64_R10
, arg_pos
);
502 if ((arg_in_reg_bitvector
[(i
- 1) >> 3] & (1 << ((i
- 1) & 7))) == 0) {
503 amd64_push_membase (p
, AMD64_R10
, arg_pos
);
506 case MONO_TYPE_VALUETYPE
:
507 if (!sig
->params
[i
- 1]->data
.klass
->enumtype
) {
508 if ((arg_in_reg_bitvector
[(i
- 1) >> 3] & (1 << ((i
- 1) & 7))) == 0)
510 int ss
= mono_class_native_size (sig
->params
[i
- 1]->data
.klass
, NULL
);
514 amd64_alu_reg_imm(p
, X86_SUB
, AMD64_RSP
, ss
);
516 amd64_mov_reg_imm(p
, AMD64_RCX
, ss
);
517 /* Source register */
518 amd64_lea_membase(p
, AMD64_RSI
, AMD64_R10
, arg_pos
);
520 amd64_mov_reg_reg(p
, AMD64_RDI
, AMD64_RSP
, 8);
522 /* AMD64 calling convention guarantees direction flag is clear at call boundary. */
523 x86_prefix(p
, AMD64_REX(AMD64_REX_W
));
524 x86_prefix(p
, X86_REP_PREFIX
);
528 /* it's an enum value */
529 simpletype
= sig
->params
[i
- 1]->data
.klass
->enum_basetype
->type
;
534 g_error ("Can't trampoline 0x%x", sig
->params
[i
- 1]->type
);
538 /* TODO: Set RAL to number of XMM registers used in case this is a varags function? */
541 * Insert call to function
543 amd64_call_reg (p
, AMD64_R11
);
545 if (sig
->ret
->byref
|| string_ctor
|| !(retval_implicit
|| sig
->ret
->type
== MONO_TYPE_VOID
)) {
546 amd64_mov_reg_membase(p
, AMD64_RSI
, AMD64_RBP
, -8, 8);
550 * Small integer and pointer values are in EAX.
551 * Long integers are in EAX:EDX.
552 * FP values are on the FP stack.
555 if (sig
->ret
->byref
|| string_ctor
) {
556 simpletype
= MONO_TYPE_PTR
;
558 simpletype
= sig
->ret
->type
;
561 switch (simpletype
) {
562 case MONO_TYPE_BOOLEAN
:
565 amd64_mov_regp_reg (p
, AMD64_RSI
, X86_EAX
, 1);
570 amd64_mov_regp_reg (p
, AMD64_RSI
, X86_EAX
, 2);
576 case MONO_TYPE_CLASS
:
577 case MONO_TYPE_OBJECT
:
578 case MONO_TYPE_SZARRAY
:
579 case MONO_TYPE_ARRAY
:
580 case MONO_TYPE_STRING
:
582 amd64_mov_regp_reg (p
, AMD64_RSI
, X86_EAX
, 8);
585 amd64_movss_regp_reg (p
, AMD64_RSI
, AMD64_XMM0
);
588 amd64_movsd_regp_reg (p
, AMD64_RSI
, AMD64_XMM0
);
591 amd64_mov_regp_reg (p
, AMD64_RSI
, X86_EAX
, 8);
593 case MONO_TYPE_VALUETYPE
: {
602 if (sig
->ret
->data
.klass
->enumtype
) {
603 simpletype
= sig
->ret
->data
.klass
->enum_basetype
->type
;
607 arg_type
= value_type_info(sig
->params
[i
]->data
.klass
, &size
, ®s_used
, &offset1
, &size1
, &offset2
, &size2
);
609 if (arg_type
== ARG_IN_INT_REGS
)
611 amd64_mov_membase_reg (p
, AMD64_RSI
, offset1
, AMD64_RAX
, size1
);
613 amd64_mov_membase_reg (p
, AMD64_RSI
, offset2
, AMD64_RDX
, size2
);
617 if (arg_type
== ARG_IN_FLOAT_REGS
)
620 amd64_movss_membase_reg (p
, AMD64_RSI
, offset1
, AMD64_XMM0
);
622 amd64_movsd_membase_reg (p
, AMD64_RSI
, offset1
, AMD64_XMM0
);
627 amd64_movss_membase_reg (p
, AMD64_RSI
, offset2
, AMD64_XMM1
);
629 amd64_movsd_membase_reg (p
, AMD64_RSI
, offset2
, AMD64_XMM1
);
634 /* Else result should have been stored in place already. */
640 g_error ("Can't handle as return value 0x%x", sig
->ret
->type
);
649 g_assert (p
- code_buffer
< code_size
);
650 res
= (MonoPIFunc
)g_memdup (code_buffer
, p
- code_buffer
);
652 g_hash_table_insert (cache
, sig
, res
);
658 * Returns a pointer to a native function that can be used to
659 * call the specified method.
660 * The function created will receive the arguments according
661 * to the call convention specified in the method.
662 * This function works by creating a MonoInvocation structure,
663 * filling the fields in and calling ves_exec_method on it.
664 * Still need to figure out how to handle the exception stuff
665 * across the managed/unmanaged boundary.
668 mono_arch_create_method_pointer (MonoMethod
*method
)
670 MonoMethodSignature
*sig
;
672 unsigned char *p
, *code_buffer
;
676 gint32 mono_invocation_pos
;
680 int int_arg_regs_used
= 0;
681 int float_arg_regs_used
= 0;
682 int stacked_args_size
= 0; /* bytes of register passed arguments pushed on stack for safe keeping. Used to get alignment right. */
683 int next_stack_arg_rbp_offset
= 16;
684 int retval_ptr_rbp_offset
= 0;
685 int this_reg
= -1; /* Remember register this ptr is in. */
688 * If it is a static P/Invoke method, we can just return the pointer
689 * to the method implementation.
691 if (method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
&& ((MonoMethodPInvoke
*) method
)->addr
) {
692 ji
= g_new0 (MonoJitInfo
, 1);
695 ji
->code_start
= ((MonoMethodPInvoke
*) method
)->addr
;
697 mono_jit_info_table_add (mono_get_root_domain (), ji
);
698 return ((MonoMethodPInvoke
*) method
)->addr
;
701 sig
= method
->signature
;
703 code_buffer
= p
= alloca (512); /* FIXME: check for overflows... */
704 vtbuf
= alloca (sizeof(int)*sig
->param_count
);
705 rbpoffsets
= alloca (sizeof(int)*sig
->param_count
);
709 * Standard function prolog.
711 amd64_push_reg (p
, AMD64_RBP
);
712 amd64_mov_reg_reg (p
, AMD64_RBP
, AMD64_RSP
, 8);
714 /* If there is an implicit return value pointer in the first args reg, save it now so
715 * the result can be stored through the pointer at the end.
717 if (sig
->ret
->type
== MONO_TYPE_VALUETYPE
&& !sig
->ret
->byref
&& !sig
->ret
->data
.klass
->enumtype
)
719 amd64_push_reg (p
, int_arg_regs
[int_arg_regs_used
]);
721 stacked_args_size
+= 8;
722 retval_ptr_rbp_offset
= -stacked_args_size
;
726 * If there is a this pointer, remember the number of the register it is in.
729 this_reg
= int_arg_regs
[int_arg_regs_used
++];
732 /* Put all arguments passed in registers on the stack.
733 * Record offsets from RBP to each argument.
737 for (i
= 0; i
< sig
->param_count
; i
++) {
738 if (sig
->params
[i
]->byref
)
739 simpletype
= MONO_TYPE_PTR
;
741 simpletype
= sig
->params
[i
]->type
;
743 switch (simpletype
) {
744 case MONO_TYPE_BOOLEAN
:
755 case MONO_TYPE_SZARRAY
:
756 case MONO_TYPE_CLASS
:
757 case MONO_TYPE_OBJECT
:
758 case MONO_TYPE_STRING
:
760 if (int_arg_regs_used
< MAX_INT_ARG_REGS
) {
761 amd64_push_reg (p
, int_arg_regs
[int_arg_regs_used
]);
763 stacked_args_size
+= 8;
764 rbpoffsets
[i
] = -stacked_args_size
;
768 rbpoffsets
[i
] = next_stack_arg_rbp_offset
;
769 next_stack_arg_rbp_offset
+= 8;
772 case MONO_TYPE_VALUETYPE
: {
773 if (sig
->params
[i
]->data
.klass
->enumtype
) {
774 simpletype
= sig
->params
[i
]->data
.klass
->enum_basetype
->type
;
787 arg_type
= value_type_info(sig
->params
[i
]->data
.klass
, &size
, ®s_used
, &offset1
, &size1
, &offset2
, &size2
);
789 if (arg_type
== ARG_IN_INT_REGS
&&
790 (int_arg_regs_used
+ regs_used
) <= MAX_INT_ARG_REGS
)
792 amd64_alu_reg_imm (p
, X86_SUB
, AMD64_RSP
, size
);
793 stacked_args_size
+= size
;
794 rbpoffsets
[i
] = stacked_args_size
;
796 amd64_mov_reg_membase (p
, int_arg_regs
[int_arg_regs_used
], AMD64_RSP
, offset1
, size1
);
800 amd64_mov_reg_membase (p
, int_arg_regs
[int_arg_regs_used
], AMD64_RSP
, offset2
, size2
);
806 if (arg_type
== ARG_IN_FLOAT_REGS
&&
807 (float_arg_regs_used
+ regs_used
) <= MAX_FLOAT_ARG_REGS
)
809 amd64_alu_reg_imm (p
, X86_SUB
, AMD64_RSP
, size
);
810 stacked_args_size
+= size
;
811 rbpoffsets
[i
] = stacked_args_size
;
814 amd64_movss_reg_membase (p
, float_arg_regs_used
, AMD64_RSP
, offset1
);
816 amd64_movsd_reg_membase (p
, float_arg_regs_used
, AMD64_RSP
, offset1
);
817 float_arg_regs_used
++;
822 amd64_movss_reg_membase (p
, float_arg_regs_used
, AMD64_RSP
, offset2
);
824 amd64_movsd_reg_membase (p
, float_arg_regs_used
, AMD64_RSP
, offset2
);
825 float_arg_regs_used
++;
830 rbpoffsets
[i
] = next_stack_arg_rbp_offset
;
831 next_stack_arg_rbp_offset
+= size
;
836 if (float_arg_regs_used
< MAX_FLOAT_ARG_REGS
) {
837 amd64_alu_reg_imm (p
, X86_SUB
, AMD64_RSP
, 8);
838 amd64_movss_regp_reg (p
, AMD64_RSP
, float_arg_regs_used
);
839 float_arg_regs_used
++;
840 stacked_args_size
+= 8;
841 rbpoffsets
[i
] = -stacked_args_size
;
845 rbpoffsets
[i
] = next_stack_arg_rbp_offset
;
846 next_stack_arg_rbp_offset
+= 8;
850 stacked_args_size
+= 8;
851 if (float_arg_regs_used
< MAX_FLOAT_ARG_REGS
) {
852 amd64_alu_reg_imm (p
, X86_SUB
, AMD64_RSP
, 8);
853 amd64_movsd_regp_reg (p
, AMD64_RSP
, float_arg_regs_used
);
854 float_arg_regs_used
++;
855 stacked_args_size
+= 8;
856 rbpoffsets
[i
] = -stacked_args_size
;
860 rbpoffsets
[i
] = next_stack_arg_rbp_offset
;
861 next_stack_arg_rbp_offset
+= 8;
865 g_error ("Can't trampoline 0x%x", sig
->params
[i
]->type
);
869 local_size
= sizeof (MonoInvocation
) + sizeof (stackval
) * (sig
->param_count
+ 1) + stacked_args_size
;
874 stackval_pos
= -local_size
;
875 mono_invocation_pos
= stackval_pos
+ sizeof (stackval
) * (sig
->param_count
+ 1);
877 /* stacked_args_size has already been pushed onto the stack. Make room for the rest of it. */
878 amd64_alu_reg_imm (p
, X86_SUB
, AMD64_RSP
, local_size
- stacked_args_size
);
880 /* Be careful not to trash any arg regs before saving this_reg to MonoInvocation structure below. */
883 * Initialize MonoInvocation fields, first the ones known now.
885 amd64_alu_reg_reg (p
, X86_XOR
, AMD64_RAX
, AMD64_RAX
);
886 amd64_mov_membase_reg (p
, AMD64_RBP
, (mono_invocation_pos
+ G_STRUCT_OFFSET (MonoInvocation
, ex
)), AMD64_RAX
, 8);
887 amd64_mov_membase_reg (p
, AMD64_RBP
, (mono_invocation_pos
+ G_STRUCT_OFFSET (MonoInvocation
, ex_handler
)), AMD64_RAX
, 8);
888 amd64_mov_membase_reg (p
, AMD64_RBP
, (mono_invocation_pos
+ G_STRUCT_OFFSET (MonoInvocation
, parent
)), AMD64_RAX
, 8);
890 * Set the method pointer.
892 amd64_mov_membase_imm (p
, AMD64_RBP
, (mono_invocation_pos
+ G_STRUCT_OFFSET (MonoInvocation
, method
)), (long)method
, 8);
898 amd64_mov_membase_reg(p
, AMD64_RBP
, (mono_invocation_pos
+ G_STRUCT_OFFSET (MonoInvocation
, obj
)), this_reg
, 8);
901 * Handle the arguments. stackval_pos is the offset from RBP of the stackval in the MonoInvocation args array .
902 * arg_pos is the offset from RBP to the incoming arg on the stack.
903 * We just call stackval_from_data to handle all the (nasty) issues....
905 amd64_lea_membase (p
, AMD64_RAX
, AMD64_RBP
, stackval_pos
);
906 amd64_mov_membase_reg (p
, AMD64_RBP
, (mono_invocation_pos
+ G_STRUCT_OFFSET (MonoInvocation
, stack_args
)), AMD64_RAX
, 8);
907 for (i
= 0; i
< sig
->param_count
; ++i
) {
908 /* Need to call stackval_from_data (MonoType *type, stackval *result, char *data, gboolean pinvoke); */
909 amd64_mov_reg_imm (p
, AMD64_R11
, stackval_from_data
);
910 amd64_mov_reg_imm (p
, int_arg_regs
[0], sig
->params
[i
]);
911 amd64_lea_membase (p
, int_arg_regs
[1], AMD64_RBP
, stackval_pos
);
912 amd64_lea_membase (p
, int_arg_regs
[2], AMD64_RBP
, rbpoffsets
[i
]);
913 amd64_mov_reg_imm (p
, int_arg_regs
[3], sig
->pinvoke
);
914 amd64_call_reg (p
, AMD64_R11
);
915 stackval_pos
+= sizeof (stackval
);
917 /* fixme: alignment */
919 arg_pos
+= mono_type_native_stack_size (sig
->params
[i
], &align
);
921 arg_pos
+= mono_type_stack_size (sig
->params
[i
], &align
);
926 * Handle the return value storage area.
928 amd64_lea_membase (p
, AMD64_RAX
, AMD64_RBP
, stackval_pos
);
929 amd64_mov_membase_reg (p
, AMD64_RBP
, (mono_invocation_pos
+ G_STRUCT_OFFSET (MonoInvocation
, retval
)), AMD64_RAX
, 8);
930 if (sig
->ret
->type
== MONO_TYPE_VALUETYPE
&& !sig
->ret
->byref
) {
931 MonoClass
*klass
= sig
->ret
->data
.klass
;
932 if (!klass
->enumtype
) {
933 amd64_mov_reg_membase (p
, AMD64_RCX
, AMD64_RBP
, retval_ptr_rbp_offset
, 8);
934 amd64_mov_membase_reg (p
, AMD64_RBP
, stackval_pos
, AMD64_RCX
, 8);
941 amd64_lea_membase (p
, int_arg_regs
[0], AMD64_RBP
, mono_invocation_pos
);
942 amd64_mov_reg_imm (p
, AMD64_R11
, ves_exec_method
);
943 amd64_call_reg (p
, AMD64_R11
);
946 * Move the return value to the proper place.
948 amd64_lea_membase (p
, AMD64_RAX
, AMD64_RBP
, stackval_pos
);
949 if (sig
->ret
->byref
) {
950 amd64_mov_reg_membase (p
, AMD64_RAX
, AMD64_RAX
, 0, 8);
952 int simpletype
= sig
->ret
->type
;
954 switch (sig
->ret
->type
) {
957 case MONO_TYPE_BOOLEAN
:
960 amd64_movzx_reg_membase (p
, AMD64_RAX
, AMD64_RAX
, 0, 1);
965 amd64_movzx_reg_membase (p
, AMD64_RAX
, AMD64_RAX
, 0, 2);
971 case MONO_TYPE_OBJECT
:
972 case MONO_TYPE_STRING
:
973 case MONO_TYPE_CLASS
:
974 amd64_movzx_reg_membase (p
, AMD64_RAX
, AMD64_RAX
, 0, 4);
977 amd64_movzx_reg_membase (p
, AMD64_RAX
, AMD64_RAX
, 0, 8);
980 amd64_movss_regp_reg (p
, AMD64_RAX
, AMD64_XMM0
);
983 amd64_movsd_regp_reg (p
, AMD64_RAX
, AMD64_XMM0
);
985 case MONO_TYPE_VALUETYPE
: {
994 if (sig
->ret
->data
.klass
->enumtype
) {
995 simpletype
= sig
->ret
->data
.klass
->enum_basetype
->type
;
999 arg_type
= value_type_info(sig
->params
[i
]->data
.klass
, &size
, ®s_used
, &offset1
, &size1
, &offset2
, &size2
);
1001 if (arg_type
== ARG_IN_INT_REGS
)
1004 amd64_mov_membase_reg (p
, AMD64_RAX
, offset2
, AMD64_RDX
, size2
);
1005 amd64_mov_membase_reg (p
, AMD64_RAX
, offset1
, AMD64_RAX
, size1
);
1009 if (arg_type
== ARG_IN_FLOAT_REGS
)
1012 amd64_movss_membase_reg (p
, AMD64_RAX
, offset1
, AMD64_XMM0
);
1014 amd64_movsd_membase_reg (p
, AMD64_RAX
, offset1
, AMD64_XMM0
);
1019 amd64_movss_membase_reg (p
, AMD64_RAX
, offset2
, AMD64_XMM1
);
1021 amd64_movsd_membase_reg (p
, AMD64_RAX
, offset2
, AMD64_XMM1
);
1026 /* Else result should have been stored in place already. IA32 code has a stackval_to_data call here, which
1027 * looks wrong to me as the pointer in the stack val being converted is setup to point to the output area anyway.
1028 * It all looks a bit suspect anyway.
1033 g_error ("Type 0x%x not handled yet in thunk creation", sig
->ret
->type
);
1044 g_assert (p
- code_buffer
< 512);
1046 ji
= g_new0 (MonoJitInfo
, 1);
1047 ji
->method
= method
;
1048 ji
->code_size
= p
- code_buffer
;
1049 ji
->code_start
= g_memdup (code_buffer
, p
- code_buffer
);
1051 mono_jit_info_table_add (mono_get_root_domain (), ji
);
1053 return ji
->code_start
;