2 * Licensed to the .NET Foundation under one or more agreements.
3 * The .NET Foundation licenses this file to you under the MIT license.
4 * See the LICENSE file in the project root for more information.
9 #include <mono/metadata/abi-details.h>
10 #include <mono/metadata/mono-debug.h>
12 #include "interp/interp.h"
20 emit_fill_call_ctx (MonoCompile
*cfg
, MonoInst
*method
, MonoInst
*ret
)
22 cfg
->flags
|= MONO_CFG_HAS_ALLOCA
;
24 MonoInst
*alloc
, *size
;
26 EMIT_NEW_ICONST (cfg
, size
, sizeof (MonoProfilerCallContext
));
27 MONO_INST_NEW (cfg
, alloc
, OP_LOCALLOC
);
28 alloc
->dreg
= alloc_preg (cfg
);
29 alloc
->sreg1
= size
->dreg
;
30 alloc
->flags
|= MONO_INST_INIT
;
31 MONO_ADD_INS (cfg
->cbb
, alloc
);
33 MonoInst
*args_alloc
, *ins
;
34 MonoMethodSignature
*sig
;
36 sig
= mono_method_signature_internal (cfg
->method
);
38 MONO_INST_NEW (cfg
, args_alloc
, OP_LOCALLOC_IMM
);
39 args_alloc
->dreg
= alloc_preg (cfg
);
40 args_alloc
->inst_imm
= (sig
->param_count
+ sig
->hasthis
) * TARGET_SIZEOF_VOID_P
;
41 args_alloc
->flags
|= MONO_INST_INIT
;
42 MONO_ADD_INS (cfg
->cbb
, args_alloc
);
43 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, alloc
->dreg
, MONO_STRUCT_OFFSET (MonoProfilerCallContext
, args
), args_alloc
->dreg
);
45 for (int i
= 0; i
< sig
->hasthis
+ sig
->param_count
; ++i
) {
46 NEW_VARLOADA (cfg
, ins
, cfg
->args
[i
], cfg
->args
[i
]->inst_vtype
);
47 MONO_ADD_INS (cfg
->cbb
, ins
);
49 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, args_alloc
->dreg
, i
* TARGET_SIZEOF_VOID_P
, ins
->dreg
);
52 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, alloc
->dreg
, MONO_STRUCT_OFFSET (MonoProfilerCallContext
, method
), method
->dreg
);
55 MonoType
*ret_type
= mono_method_signature_internal (cfg
->method
)->ret
;
56 MonoInst
*var
= mono_compile_create_var (cfg
, ret_type
, OP_LOCAL
);
58 MonoInst
*store
, *addr
;
60 EMIT_NEW_TEMPSTORE (cfg
, store
, var
->inst_c0
, ret
);
61 EMIT_NEW_VARLOADA (cfg
, addr
, var
, NULL
);
62 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STORE_MEMBASE_REG
, alloc
->dreg
, MONO_STRUCT_OFFSET (MonoProfilerCallContext
, return_value
), addr
->dreg
);
64 /* Work around a limitation of the register allocator regarding
65 * FP stack, see https://github.com/mono/mono/pull/17251 */
66 if (cfg
->backend
->use_fpstack
&& (ret_type
->type
== MONO_TYPE_R8
|| ret_type
->type
== MONO_TYPE_R4
)) {
67 MonoInst
*move_ret_back
;
68 EMIT_NEW_VARSTORE (cfg
, move_ret_back
, ret
, ret_type
, var
);
76 can_encode_method_ref (MonoMethod
*method
)
78 /* Return whenever the AOT compiler can encode references to this method */
79 if (!method
->wrapper_type
)
81 return (method
->wrapper_type
== MONO_WRAPPER_DYNAMIC_METHOD
);
85 mini_profiler_emit_enter (MonoCompile
*cfg
)
87 gboolean trace
= mono_jit_trace_calls
!= NULL
&& mono_trace_eval (cfg
->method
);
89 if ((!MONO_CFG_PROFILE (cfg
, ENTER
) || cfg
->current_method
!= cfg
->method
|| (cfg
->compile_aot
&& !can_encode_method_ref (cfg
->method
))) && !trace
)
92 if (cfg
->current_method
!= cfg
->method
)
97 EMIT_NEW_METHODCONST (cfg
, iargs
[0], cfg
->method
);
98 EMIT_NEW_PCONST (cfg
, iargs
[1], NULL
);
100 if (MONO_CFG_PROFILE (cfg
, ENTER_CONTEXT
))
101 iargs
[2] = emit_fill_call_ctx (cfg
, iargs
[0], NULL
);
103 EMIT_NEW_PCONST (cfg
, iargs
[2], NULL
);
105 /* void mono_profiler_raise_method_enter (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */
107 mono_emit_jit_icall (cfg
, mono_trace_enter_method
, iargs
);
109 mono_emit_jit_icall (cfg
, mono_profiler_raise_method_enter
, iargs
);
113 mini_profiler_emit_leave (MonoCompile
*cfg
, MonoInst
*ret
)
115 gboolean trace
= mono_jit_trace_calls
!= NULL
&& mono_trace_eval (cfg
->method
);
117 if (!MONO_CFG_PROFILE (cfg
, LEAVE
) || cfg
->current_method
!= cfg
->method
|| (cfg
->compile_aot
&& !can_encode_method_ref (cfg
->method
)))
122 EMIT_NEW_METHODCONST (cfg
, iargs
[0], cfg
->method
);
123 EMIT_NEW_PCONST (cfg
, iargs
[1], NULL
);
125 if (MONO_CFG_PROFILE (cfg
, LEAVE_CONTEXT
))
126 iargs
[2] = emit_fill_call_ctx (cfg
, iargs
[0], ret
);
128 EMIT_NEW_PCONST (cfg
, iargs
[2], NULL
);
130 /* void mono_profiler_raise_method_leave (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */
132 mono_emit_jit_icall (cfg
, mono_trace_leave_method
, iargs
);
134 mono_emit_jit_icall (cfg
, mono_profiler_raise_method_leave
, iargs
);
138 mini_profiler_emit_tail_call (MonoCompile
*cfg
, MonoMethod
*target
)
140 gboolean trace
= mono_jit_trace_calls
!= NULL
&& mono_trace_eval (cfg
->method
);
142 if ((!MONO_CFG_PROFILE (cfg
, TAIL_CALL
) || cfg
->current_method
!= cfg
->method
) && !trace
)
145 g_assert (cfg
->current_method
== cfg
->method
);
149 EMIT_NEW_METHODCONST (cfg
, iargs
[0], cfg
->method
);
150 EMIT_NEW_PCONST (cfg
, iargs
[1], NULL
);
153 EMIT_NEW_METHODCONST (cfg
, iargs
[2], target
);
155 EMIT_NEW_PCONST (cfg
, iargs
[2], NULL
);
157 /* void mono_profiler_raise_method_tail_call (MonoMethod *method, MonoMethod *target) */
159 mono_emit_jit_icall (cfg
, mono_trace_leave_method
, iargs
);
161 mono_emit_jit_icall (cfg
, mono_profiler_raise_method_tail_call
, iargs
);
165 mini_profiler_emit_call_finally (MonoCompile
*cfg
, MonoMethodHeader
*header
, unsigned char *ip
,
166 guint32 index
, MonoExceptionClause
*clause
)
168 if (!G_UNLIKELY (mono_profiler_clauses_enabled ()))
173 NEW_BBLOCK (cfg
, ebb
);
175 MonoInst
*ins
= mini_emit_runtime_constant (cfg
, MONO_PATCH_INFO_PROFILER_CLAUSE_COUNT
, NULL
);
176 MonoInst
*count_ins
= mini_emit_memory_load (cfg
, m_class_get_byval_arg (mono_defaults
.uint32_class
), ins
, 0, 0);
177 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_ICOMPARE_IMM
, -1, count_ins
->dreg
, 0);
178 ins
->flags
|= MONO_INST_LIKELY
;
179 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_IBEQ
, ebb
);
183 EMIT_NEW_METHODCONST (cfg
, iargs
[0], cfg
->current_method
);
184 EMIT_NEW_ICONST (cfg
, iargs
[1], index
);
185 EMIT_NEW_ICONST (cfg
, iargs
[2], clause
->flags
);
187 MonoExceptionClause
*cclause
= NULL
;
189 // Are we leaving a catch clause?
190 for (guint32 i
= 0; i
< header
->num_clauses
; i
++) {
191 MonoExceptionClause
*hclause
= &header
->clauses
[i
];
192 guint32 offset
= ip
- header
->code
;
194 if (hclause
->flags
!= MONO_EXCEPTION_CLAUSE_NONE
&& hclause
->flags
!= MONO_EXCEPTION_CLAUSE_FILTER
)
197 if (!MONO_OFFSET_IN_HANDLER (hclause
, offset
))
200 if (offset
+ (*ip
== CEE_LEAVE
? 5 : 2) <= hclause
->handler_offset
+ hclause
->handler_len
) {
206 // If so, find the exception object and pass it along.
208 EMIT_NEW_TEMPLOAD (cfg
, iargs
[3], mono_find_exvar_for_offset (cfg
, cclause
->handler_offset
)->inst_c0
);
210 EMIT_NEW_PCONST (cfg
, iargs
[3], NULL
);
212 /* void mono_profiler_raise_exception_clause (MonoMethod *method, uint32_t index, MonoExceptionEnum type, MonoObject *exception) */
213 mono_emit_jit_icall (cfg
, mono_profiler_raise_exception_clause
, iargs
);
215 MONO_START_BB (cfg
, ebb
);
221 mini_profiler_context_enable (void)
223 if (!mono_debug_enabled ())
224 mono_debug_init (MONO_DEBUG_FORMAT_MONO
);
228 memdup_with_type (gpointer data
, MonoType
*t
)
232 return g_memdup (data
, mono_type_size (t
, &dummy
));
236 get_int_reg (MonoContext
*ctx
, guint32 reg
)
238 return (guint8
*)(gsize
)mono_arch_context_get_int_reg (ctx
, reg
);
242 get_variable_buffer (MonoDebugMethodJitInfo
*jit
, MonoDebugVarInfo
*var
, MonoContext
*ctx
)
244 guint32 flags
= var
->index
& MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS
;
245 guint32 reg
= var
->index
& ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS
;
248 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER
: {
250 * This is kind of a special case: All other address modes ultimately
251 * produce an address to where the actual value is located, but this
252 * address mode gets us the value itself as an host_mgreg_t value.
254 host_mgreg_t value
= (host_mgreg_t
) get_int_reg (ctx
, reg
);
256 return memdup_with_type (&value
, var
->type
);
258 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET
:
259 return memdup_with_type (get_int_reg (ctx
, reg
) + (gint32
) var
->offset
, var
->type
);
260 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR
:
261 case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR
:
262 return memdup_with_type (*(guint8
**) (get_int_reg (ctx
, reg
) + (gint32
) var
->offset
), var
->type
);
263 case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL
: {
266 MonoDebugVarInfo
*info_var
= jit
->gsharedvt_info_var
;
268 flags
= info_var
->index
& MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS
;
269 reg
= info_var
->index
& ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS
;
271 MonoGSharedVtMethodRuntimeInfo
*info
;
274 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER
:
275 info
= (MonoGSharedVtMethodRuntimeInfo
*) get_int_reg (ctx
, reg
);
277 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET
:
278 info
= *(MonoGSharedVtMethodRuntimeInfo
**) (get_int_reg (ctx
, reg
) + (gint32
) info_var
->offset
);
281 g_assert_not_reached ();
284 MonoDebugVarInfo
*locals_var
= jit
->gsharedvt_locals_var
;
286 flags
= locals_var
->index
& MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS
;
287 reg
= locals_var
->index
& ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS
;
292 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER
:
293 locals
= get_int_reg (ctx
, reg
);
295 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET
:
296 locals
= *(guint8
**) (get_int_reg (ctx
, reg
) + (gint32
) info_var
->offset
);
299 g_assert_not_reached ();
302 return memdup_with_type (locals
+ (gsize
) info
->entries
[idx
], var
->type
);
305 g_assert_not_reached ();
311 mini_profiler_context_get_this (MonoProfilerCallContext
*ctx
)
313 if (!mono_method_signature_internal (ctx
->method
)->hasthis
)
316 if (ctx
->interp_frame
)
317 return memdup_with_type (mini_get_interp_callbacks ()->frame_get_this (ctx
->interp_frame
), m_class_get_this_arg (ctx
->method
->klass
));
319 return memdup_with_type (ctx
->args
[0], m_class_get_this_arg (ctx
->method
->klass
));
323 mini_profiler_context_get_argument (MonoProfilerCallContext
*ctx
, guint32 pos
)
325 MonoMethodSignature
*sig
= mono_method_signature_internal (ctx
->method
);
327 if (pos
>= sig
->param_count
)
330 if (ctx
->interp_frame
)
331 return memdup_with_type (mini_get_interp_callbacks ()->frame_get_arg (ctx
->interp_frame
, pos
), sig
->params
[pos
]);
333 return memdup_with_type (ctx
->args
[sig
->hasthis
+ pos
], sig
->params
[pos
]);
337 mini_profiler_context_get_local (MonoProfilerCallContext
*ctx
, guint32 pos
)
340 MonoMethodHeader
*header
= mono_method_get_header_checked (ctx
->method
, error
);
341 mono_error_assert_ok (error
); // Must be a valid method at this point.
343 if (pos
>= header
->num_locals
) {
344 mono_metadata_free_mh (header
);
348 MonoType
*t
= header
->locals
[pos
];
350 mono_metadata_free_mh (header
);
352 if (ctx
->interp_frame
)
353 return memdup_with_type (mini_get_interp_callbacks ()->frame_get_local (ctx
->interp_frame
, pos
), t
);
355 MonoDebugMethodJitInfo
*info
= mono_debug_find_method (ctx
->method
, mono_domain_get ());
360 return get_variable_buffer (info
, &info
->locals
[pos
], &ctx
->context
);
364 mini_profiler_context_get_result (MonoProfilerCallContext
*ctx
)
366 MonoType
*ret
= mono_method_signature_internal (ctx
->method
)->ret
;
368 if (ctx
->interp_frame
)
369 ctx
->return_value
= mini_get_interp_callbacks ()->frame_get_res (ctx
->interp_frame
);
371 if (!ctx
->return_value
)
374 return memdup_with_type (ctx
->return_value
, ret
);
378 mini_profiler_context_free_buffer (void *buffer
)