[2019-12] [jit] Avoid passing a vtable argument to DIM methods when making calls...
[mono-project.git] / mono / mini / mini-profiler.c
blob8f7071426cd4de331c1ea1c153c5071646c54500
1 /*
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.
5 */
7 #include <config.h>
9 #include <mono/metadata/abi-details.h>
10 #include <mono/metadata/mono-debug.h>
12 #include "interp/interp.h"
13 #include "ir-emit.h"
14 #include "mini.h"
15 #include "trace.h"
17 #ifndef DISABLE_JIT
19 static MonoInst *
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);
54 if (ret) {
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);
72 return alloc;
75 static gboolean
76 can_encode_method_ref (MonoMethod *method)
78 /* Return whenever the AOT compiler can encode references to this method */
79 if (!method->wrapper_type)
80 return TRUE;
81 return (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD);
84 void
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)
90 return;
92 if (cfg->current_method != cfg->method)
93 return;
95 MonoInst *iargs [3];
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);
102 else
103 EMIT_NEW_PCONST (cfg, iargs [2], NULL);
105 /* void mono_profiler_raise_method_enter (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */
106 if (trace)
107 mono_emit_jit_icall (cfg, mono_trace_enter_method, iargs);
108 else
109 mono_emit_jit_icall (cfg, mono_profiler_raise_method_enter, iargs);
112 void
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)))
118 return;
120 MonoInst *iargs [3];
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);
127 else
128 EMIT_NEW_PCONST (cfg, iargs [2], NULL);
130 /* void mono_profiler_raise_method_leave (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */
131 if (trace)
132 mono_emit_jit_icall (cfg, mono_trace_leave_method, iargs);
133 else
134 mono_emit_jit_icall (cfg, mono_profiler_raise_method_leave, iargs);
137 void
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)
143 return;
145 g_assert (cfg->current_method == cfg->method);
147 MonoInst *iargs [3];
149 EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
150 EMIT_NEW_PCONST (cfg, iargs [1], NULL);
152 if (target)
153 EMIT_NEW_METHODCONST (cfg, iargs [2], target);
154 else
155 EMIT_NEW_PCONST (cfg, iargs [2], NULL);
157 /* void mono_profiler_raise_method_tail_call (MonoMethod *method, MonoMethod *target) */
158 if (trace)
159 mono_emit_jit_icall (cfg, mono_trace_leave_method, iargs);
160 else
161 mono_emit_jit_icall (cfg, mono_profiler_raise_method_tail_call, iargs);
164 void
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 ()))
169 return;
171 MonoBasicBlock *ebb;
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);
181 MonoInst *iargs [4];
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)
195 continue;
197 if (!MONO_OFFSET_IN_HANDLER (hclause, offset))
198 continue;
200 if (offset + (*ip == CEE_LEAVE ? 5 : 2) <= hclause->handler_offset + hclause->handler_len) {
201 cclause = hclause;
202 break;
206 // If so, find the exception object and pass it along.
207 if (cclause)
208 EMIT_NEW_TEMPLOAD (cfg, iargs [3], mono_find_exvar_for_offset (cfg, cclause->handler_offset)->inst_c0);
209 else
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);
218 #endif
220 void
221 mini_profiler_context_enable (void)
223 if (!mono_debug_enabled ())
224 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
227 static gpointer
228 memdup_with_type (gpointer data, MonoType *t)
230 int dummy;
232 return g_memdup (data, mono_type_size (t, &dummy));
235 static guint8 *
236 get_int_reg (MonoContext *ctx, guint32 reg)
238 return (guint8 *)(gsize)mono_arch_context_get_int_reg (ctx, reg);
241 static gpointer
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;
247 switch (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: {
264 guint32 idx = reg;
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;
273 switch (flags) {
274 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
275 info = (MonoGSharedVtMethodRuntimeInfo *) get_int_reg (ctx, reg);
276 break;
277 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
278 info = *(MonoGSharedVtMethodRuntimeInfo **) (get_int_reg (ctx, reg) + (gint32) info_var->offset);
279 break;
280 default:
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;
289 guint8 *locals;
291 switch (flags) {
292 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
293 locals = get_int_reg (ctx, reg);
294 break;
295 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
296 locals = *(guint8 **) (get_int_reg (ctx, reg) + (gint32) info_var->offset);
297 break;
298 default:
299 g_assert_not_reached ();
302 return memdup_with_type (locals + (gsize) info->entries [idx], var->type);
304 default:
305 g_assert_not_reached ();
306 return NULL;
310 gpointer
311 mini_profiler_context_get_this (MonoProfilerCallContext *ctx)
313 if (!mono_method_signature_internal (ctx->method)->hasthis)
314 return NULL;
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));
318 else
319 return memdup_with_type (ctx->args [0], m_class_get_this_arg (ctx->method->klass));
322 gpointer
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)
328 return NULL;
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]);
336 gpointer
337 mini_profiler_context_get_local (MonoProfilerCallContext *ctx, guint32 pos)
339 ERROR_DECL (error);
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);
345 return NULL;
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 ());
357 if (!info)
358 return NULL;
360 return get_variable_buffer (info, &info->locals [pos], &ctx->context);
363 gpointer
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)
372 return NULL;
374 return memdup_with_type (ctx->return_value, ret);
377 void
378 mini_profiler_context_free_buffer (void *buffer)
380 g_free (buffer);